Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support secondary indices in findAll #7

Open
jmdobry opened this issue Mar 26, 2015 · 9 comments
Open

Support secondary indices in findAll #7

jmdobry opened this issue Mar 26, 2015 · 9 comments

Comments

@jmdobry
Copy link
Member

jmdobry commented Mar 26, 2015

Example:

adapter.findAll(User, {
  where: {
    age: { '>': 30 },
    status: 'unknown'
  }
});

Let's say there is a secondary index setup on the "status" field. In order to tell the query to take advantage of this, I propose the following:

adapter.findAll(User, {
  where: {
    age: { '>': 30 },
    status: { '==': 'unknown' }
  }
}, {
  keys: ['status']
});

Which will optimize the query by first doing a getAll('unknown', { index: 'status' }) and then adding the filter calls.

@jmdobry
Copy link
Member Author

jmdobry commented Mar 26, 2015

I also might be worth it to add configuration to resources, such as a keys option, that specifies which fields of the resource are a secondary index, so you don't have to specify keys during the method call.

@gtarcea
Copy link

gtarcea commented Mar 26, 2015

I like this idea. Would this impact the REST interface? For example, lets say I want to get all projects by user. Right now it appears that /projects gets all projects, and /projects/id looks up by the primary key. Would the interface change to something like:
/projects/by/:key
It looks like this change would impact all the other adapters. Is there something I could do in the short term?

@gtarcea
Copy link

gtarcea commented Mar 26, 2015

Another suggestion - the filterQuery which creates the rql starts by setting up the table to query on. If filterQuery was changed to take rql and spit it back out, then it could be used in the application to build up a query to execute. For example, if I did the following:
let rql = r.table('files').getAll(owner, {index: 'user'});
rql = User.filterQuery(rql);
let results = yield rql.run();

Then I could use the query syntax in my application but work extend the interface in ways that may be specific to my application needs. This would also let me work around the need for a secondary index.

@jmdobry
Copy link
Member Author

jmdobry commented Mar 26, 2015

Let me expand on my example (still a very simplified example).

Let's assume I'm using js-data + js-data-angular on the frontend and js-data + js-data-rethinkdb on the backend.

Frontend:

angular.module('myApp', ['ngRoute', 'js-data'])
  .config(function ($routeProvider) {
    $routeProvider.when('/projects', {
      // ...
      resolve: {
        projects: function (User, Project) {
          return User.getLoggedInUser.then(function (user) {
            // assuming user.id is 5 then:
            // GET /projects?userId=5
            return Project.findAll({
              userId: user.id
            });
          });
        }
      }
    });
  });

Backend:

let Project = store.defineResource({
  // ...
  keys: ['userId'],
  // ...
});

export default Project;
app.get('/projects', function (req, res) {

  // right now this will not use "getAll" or any secondary indices
  // but if we change js-data-rethinkdb to look at that "keys"
  // option that I put on the Project resource definition above
  // then the "filterQuery" method can use that to optimize 
  // itself to use "getAll" and the secondary index
  Project.findAll(req.query).then(function (projects) {
    res.status(200).send(projects);
  });
});

This approach will make things work more like "magic", with the adapter automatically using secondary indexes if it has been told about them.

Regarding your suggestion of filterQuery taking reql and spitting it back out, I see the usefulness there, and I will think about how I would want that to work.

@jmdobry
Copy link
Member Author

jmdobry commented Mar 27, 2015

@gtarcea In 1.2.0 the adapter now has a filterSequence(sequence, params) method. This will take a given sequence, such as r.table('files').getAll(owner, {index: 'user'}) and then apply the proper RQL filter, orderBy, skip, limit clauses according to what is described in params. This allows you to do what you suggested in your last comment:

let rql = r.table('files').getAll(owner, {index: 'user'});
let params = { type: 'pdf' };
rql = rethinkdbAdapter.filterSequence(rql, params);
let results = yield rql.run();

so you might do something like:

let store = new JSData.DS();
let rethinkdbAdapter = new DSRethinkDBAdapter(...);
store.registerAdapter('rethink', rethinkdbAdapter, { default: true });
let File = store.defineResource({
  name: 'file',
  table: 'files',
  relations: {
    belongsTo: {
      user: {
        localField: 'owner',
        localKey: 'user'
      }
    }
  }
});

File.findAllByIndex = (key, index, params) => {
  let rql = rethinkdbAdapter.r.table('files').getAll(key, { index: index });
  rql = rethinkdbAdapter.filterSequence(rql, params);
  return yield rql.run();
};

@gtarcea
Copy link

gtarcea commented Mar 27, 2015

This is great. Thank you so much. I will let you know how it works out.

@internalfx
Copy link
Member

wow, didn't know this was here.

@Yevgeniuz
Copy link

Is it possible to filter by null?
smth like:

where: {
    type: { 'in': 30 },
    subtype: null
  }

@jmdobry
Copy link
Member Author

jmdobry commented Mar 31, 2016

Should be able to yeah

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants