js-data-rethinkdb
js-data-rethinkdb copied to clipboard
Support secondary indices in findAll
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.
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.
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?
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.
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.
@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();
};
This is great. Thank you so much. I will let you know how it works out.
wow, didn't know this was here.
Is it possible to filter by null? smth like:
where: {
type: { 'in': 30 },
subtype: null
}
Should be able to yeah