SitePen / dstore

A data infrastructure framework, providing the tools for modelling and interacting with data collections and objects.
http://dstorejs.io/
Other
281 stars 83 forks source link

Query abstraction missing #34

Closed stemey closed 10 years ago

stemey commented 10 years ago

I implemented a server-side dojo/store which connects to a MongoDB. The most difficult part was actually transforming the query passed to query() into a mongoDB query. In my opinion this is difficult because of the lack of a query abstraction in dojo/store.

Actually I had to adapt my store implementation to the clients using the store. Specifically diji/form/FilteringSelect and gridx. Both have different ways to build queries. Looking at the comments in dijit/form/FilteringSelect the lack of abstraction becomes evident. The comment mentions two Stores that were specifically handled (JsonRest and Memory). The problem arises because a like-query is not standardized. Similary Gridx creates very complex queries with logical operators which cannot be expressed by simple http-parameters

I suggest you introduce a query abstraction like rql or MongoDb's query in dstore. That makes store and client implementors' lifes easier.

neonstalwart commented 10 years ago

@stemey i use rql with the Rest store already...

var Query = require('rql/query').Query,
    query = '' + new Query().eq(['user', 'id'], id);

store.filter(query).fetch().then( ... );

you can also use the provided rql queryEngine (https://github.com/SitePen/dstore/blob/master/extensions/rqlQueryEngine.js) with a Memory store.

stemey commented 10 years ago

@neonstalwart that does not seem to solve my problem with gridx and FilteringSelect dijits that don't use RQL. I want to use existing dijits and create a new store. If I consume my own store I am fine without a query abstraction.

neonstalwart commented 10 years ago

i don't know about gridx at all but i was able to previously have FilteringSelect produce an rql query by instantiating the FilteringSelect with queryExpr: "glob:${0}*". at that time i was using a dojo/store JsonRest store that was wrapped to change query so that it produced an rql formatted string as the query. the wrapper would update store.query and store.queryEngine so that they understood rql and also translated the usual object queries into rql

e.g.

if (typeof query === 'object') {
    for (prop in query) {
        if (query.hasOwnProperty(prop)) {
            q.push(prop + '=' + query[prop]);
        }
    }
    q = q.join('&');
}

i guess the equivalent place to do this in dstore would be _renderQueryParams

neonstalwart commented 10 years ago

that does not seem to solve my problem with gridx and FilteringSelect dijits that don't use RQL

also, how does dstore fix this since the issue is that these consumers are not using a consistent query format? i agree that a unified query format across all of dojo would be good. i think rql is a really good starting point for that and dstore embraces rql - the next step would seem to be to adjust the consumers to produce queries that are rql.

stemey commented 10 years ago

Surely I can fix every dijit to work with every other store. But that is not the idea of the store api, is it?

I created a generic application whose underlying store should be easily exchangeable - turns out that is harder than I thought. As you said, a query abstraction would be great. Unfortunatly not even the provided standard stores Memory and JsonRest agree on a query format.

I think that dstore is a chance to address this shortcoming.

kriszyp commented 10 years ago

dstore does include RQL support, but it is not included by default in the base Memory store, to avoid potentially unnecessary modules from being loaded. However, as you point out, this is problem for achieving any type of consistent way of querying beyond simple property equality filtering. Perhaps another idea might be to allow for defining your query format/engine in your call to filter(). What if we allowed this:

require(['dstore/extensions/rqlQueryEngine'], function (rqlQueryEngine) {
   var filtered = store.filter('some query', {queryEngine: rqlQueryEngine});

I don't know that this is terribly elegant, it is kind of an uncomfortable tight coupling with a dstore module, but perhaps it is a feasible solution for a more consistent means of advanced store querying.

stemey commented 10 years ago

@kriszyp I don't really understand your idea. My concern is to make it easier to implement a store by making the clients behave in a standard way.

I see the following ways to address this issue:

kriszyp commented 10 years ago

I am not sure I understand, I thought the goal was to unify querying so that widgets didn't have to worry about different formats, and having two query formats seems to defeat that purpose. Also, I am curious if something like an IndexedDB store would use 'client' or 'server'. It is clearly on the client, but the query mechanism, using indices, is much similar to server stores than the Memory store.

stemey commented 10 years ago

Well, it seems to be unrealistic to have a standard query format. Distinguishing client and server is also not a satisfactory solution.

The idea of a queryBuilder still seems to be a good one. Using a queryBuilder which is retrieved from the store is better than a standard query format because a store does not need to parse the standard query and then create a new query native to its query engine (IndexedDb, InMemory, MongoDb etc.).

The dijit implementors also don't need to worry about different store implementations.

var query = this.store.queryBuilder().like("name","Will*").create();
var results = this.store.filter(query);
neonstalwart commented 10 years ago

rql (almost) already provides what you're looking for.

require([ 'rql/Query' ], function (rqlQuery) {
    var Query = rqlQuery.Query,
        query = new Query().match('name', 'glob:Will*'),
        results = store.filter(query);
});

in order for this to happen i see 3 main points to be addressed

  1. find a dsl that is sufficient to express complex queries that can be serialized to a string
  2. have dstore support querying with that syntax
  3. encourage consumers of the stores to use that syntax

for point 1, rql is a syntax that can express complex queries and has a dsl (via rql/query.Query) to support building those queries. the one thing that may need to be addressed here is to have more people look at rql and provide their feedback about how the dsl (and the rql syntax) might be improved, add more tests, etc.

for point 2, dstore provides dstore/rqlQueryEngine to support the ability to use rql as the queryEngine. of course this doesn't address server-backed stores but those simply depend on having a parser on the server that will parse the serialized query. if you're using JavaScript on your server, rql already provides rql/parser. i think i might be coming around to the idea of associating the Query constructor with the queryEngine so that the dsl can be translated to any query syntax (e.g rql, key-value pairs, mongo, etc) without the consumer needing to know the difference.

the big missing piece is to get store consumers to use a dsl for creating queries. for this you should probably approach @wkeese to see if it's something that could be worked into https://github.com/ibm-js/delite. if he's not happy with the rql dsl then let's find something that everyone would be happy with but i really think that the key to getting what you're looking for lies in finding consensus among the consumers of the store API to agree on a dsl to construct queries - i'm suggesting the rql/query.Query dsl as a good place to start.

stemey commented 10 years ago

That sounds promising. Can you direct me to the docs (or source code) of the RQL-Querybuilder API (unsuccessfully searched the persrvr/rql repo).

Still the client should retrieve the queryBuilder instance from the store instead of instantiating rql/Query himself. So there needs to be a function on the store like store.queryBuilder().

neonstalwart commented 10 years ago

the readme for rql lists all the operators (https://github.com/persvr/rql)

I think store.Query would be a good option but the name is a bikeshed.

neonstalwart commented 10 years ago

https://github.com/persvr/rql/blob/master/query.js#L34-L36 also lists all the possible operators

anatoliyarkhipov commented 10 years ago

Sorry for my english, guys, but i must comment this.

Here is several facts:

  1. Not every store can support a complex query that can be generated with rql (it may be limited functionality of the server for example).
  2. The other case is when the store partially supports rql. I.e. it supports advanced conditions ("in", "between", "lte") but not nested expressions. Example (nested or not supported): and(or(eq(foo,3),eq(foo,bar)),lt(price,10)))
  3. Every store can support simple query { field: 'value' }
  4. Filtering by functions may be very useful for inmemory stores

So, it's classical problem of object-oriented programming, i think. Here is pseudocode:

// It's about third case (with simple queries). 
interface IStore {
  function filter(query: SimpleQuery): ExpectedDataFormat;
}
class SimpleStore implements IStore {
  function filter(query: SimpleQuery): ExpectedDataFormat {
    // do some simple filtering
  }
}
// So, all simple widgets (like FilteringSelect) will declare that they expect IStore
class FileringSelect {
  store: IStore;
}

// This interface supports second case (advanced conditions but not nested expressions):
interface ISupportAdvancedConditions extends IStore {
  // JS allow us to declare "query: AdvancedQuery|SimpleQuery", it's just convention, hehe
  // Exptected data format does not change
  function filter(query: AdvancedQuery|SimpleQuery): ExpectedDataFormat; 
}
// Pseudo implementation
class AdvancedStore extends SimpleStore implements ISupportAdvancedConditions {
  function filter(query: AdvancedQuery|SimpleQuery): ExpectedDataFormat {
    if (isAdvancedQuery(query)) { 
      // do some advanced filtering
    } else {
       return this.inherited(arguments) // SimpleStore#filter
    }
  }
}
// Or
class AdvancedStore implements ISupportAdvancedConditions {
  function filter(query: AdvancedQuery|SimpleQuery): ExpectedDataFormat {
    if (isAdvancedQuery(query)) { 
       // do some advanced filtering
    } else {
       // do my own simple filtering
    }
  }
}
// And we can create the "AdvancedStore" and put it to FileringSelect. It will still work

// Similarly with full-rql store
interface ISupportRql extends ISupportAdvancedConditions { /* ... */ }
class RqlStore extends AdvancedStore implements ISupportRql { /* ... */ }
// or with custom implementation
class RqlStore implements ISupportRql { /* ... */ }

// For inmemory store
interface ISupportFunctions { /* ... */ }
class MemoryStore extends SimpleStore implements ISupportFunctions { /* ... */ }

// So, simple widgets does not have to worry about different formats (any store 
// can implement third case) but advanced widgets expect advanced stores:
class FileringSelect {
  store: IStore;
}
class CoolGridWithConditionalFiltering {
  store: IAdvancedStore;
}
class CoolSearchStringWithFullRqlSupport {
  store: IRqlStore;
}

// We can now create store for our app
class CrmStore implements ISupportRql {
  function filter(query: SimpleQuery|AdvancedQuery|FullRqlQuery): ExpectedDataFormat {
    // it may be the same code for all types of query 
    var queryParams = this.generateQueryParams(query)
    return api.call('someMethod', queryParams).then(toExpectedDataFormat)
  }
}

var store = new CrmStore(),
    filteringSelect = new FilterinSelect({ store: store }),
    coolGrid = new CoolGridWithConditionalFiltering({ store: store }),
    searchString = new CoolSearchStringWithFullRqlSupport({ store: store })
stemey commented 10 years ago

@anatoliyarkhipov You are right, but type declarations on properties don't exist in javascript. In order to check that the store supports the necessary features the client might check the features one by one as in "duck typing": if (!store.supports('like', 'greaterThan') {throw "wrong store";}

@neonstalwart we just need to confirm the name for the method and a better documentation of the queryBuilder api.

How can I help?

anatoliyarkhipov commented 10 years ago

@stemey Yes, here is no type declarations, I talk about conventions. My convention is "every advanced store must support query format of previous store". This is not necessary if (!store.supports('like', 'greaterThan') {throw "wrong store";}. You do not check that the store has a "query" method, you just use it. So, why you are going to check supported query type? If you expect a simple store then just use it as a simple store. If your widget will receive an advanced store it will continue to use it as a simple store.

kriszyp commented 10 years ago

The other approach, which may be more in-line with the current spirit of the dstore API, would be to simply start adding more query methods on the collections. We could add support for things like (and even align it with RQL):

collection.gt('propertyName', minimumValue).lt('propertyName', maxValue).forEach(...);

and:

collection.filter({'priority':'high'}).or(collection.filter({'priority':'critical'})).forEach(...);

This seems like it would be the simplest approach from an API perspective, no need to go through a separate query builder, and detecting support for features is easy (check for the presence of a method). And our query, lazy-execution model would work well with this as well. However, I think the downside (in contrast to the previous suggestion), is that the base memory store has to load a potentially much larger query engine, to support all of this. We could leverage the RQL query engine, since it already has all these query capabilities, but do we want to always load that? Do we want users to opt-in to more sophisticated query capabilities, or provide it in the base stores?

neonstalwart commented 10 years ago

this is an interesting idea.

i don't think that the Memory store would need to have the full support of rql by default. a limited subset that translates to the current capabilities of dstore/objectQueryEngine should be ok and hopefully not increase the size too much.

i want to say that users should opt-in to more sophisticated query capabilities but i know that in my case i'll probably tend to pull in rqlQueryEngine most times since i get a lot of benefit from it so making it opt-in is probably more of a theoretical benefit but i suppose it's worth being conservative in this regard if it helps get support for the idea.

@kriszyp since the possible querying capabilities depend on the queryEngine being used, how would the store expose the capability of the queryEngine?

currently a Memory store can be made to understand rql via

// declare store
var RqlMemoryStore = declare(Memory, {
  queryEngine: rqlQueryEngine
});

// how can i know the query capabilities of this store?
var store = new RqlMemoryStore();

this doesn't expose the capabilities of the queryEngine through the store. i tend to think that either a queryEngine needs to become a mixin

var RqlQueryEngine = declare(null, {
    queryEngine: ..., // this would be what is currently dstore/extensions/rqlQueryEngine
    Query: ...  // this would be rql/query.Query
});

var RqlMemoryStore = declare([ Memory, RqlQueryEngine ]);
var store = new RqlMemoryStore();

// capabilities of the queryEngine can now be determined by methods available
var query = new store.Query();

store.filter(query.eq('foo', 'bar'));

a variation - the queryEngine becomes a mixin that directly exposes all its capabilities to be mixed into the store

var RqlQueryEngine = declare(null, {
    queryEngine: ..., // this would be what is currently dstore/extensions/rqlQueryEngine
    eq: ...,
    // etc
});

var RqlMemoryStore = declare([ Memory, RqlQueryEngine ]);
var store = new RqlMemoryStore();

// capabilities of the queryEngine can now be determined by methods available on the store
store.filter(store.eq('foo', 'bar'));

or extend the current queryEngine API to include a Query property

// declare store
var RqlMemoryStore = declare(Memory, {
  queryEngine: rqlQueryEngine // includes a Query property for construction queries
});

var store = new RqlMemoryStore();

var query = new store.queryEngine.Query()

my thinking up to this point had been along the lines of the first option - making queryEngine a mixin with a Query property exposed on the store. each have some trade-offs in discoverability and how to compose stores and queries so i'm open to considering the options. i like the idea of seeing if we can go with the current spirit of the dstore API.

stemey commented 10 years ago

Adding the query building method to the collection seems like a good idea.

The 'or'-syntax seems cumbersome in that the original collection needs to be referenced again. Maybe it could be more like this:

collection.filter({'priority':'high'}).or().filter({'priority':'critical'})).forEach(...);

I assume that a query is being constructed until a result-processing method (like forEach) is called. Is the query reset after that or can I reset the query programmatically to get all data in the collection?

anatoliyarkhipov commented 10 years ago

I agree that variant .or(collection.filter(...)).forEach(...) is not intuitively. I expect an lazy array from filter method but not query-like object that can be used as argument for or method.

But @stemey how you intend to implement nested conditions in your variant? or(and(...), filter) In a flat style it must be something like this: .or().and().filter().closeAnd().filter().closeOr().forEach(...) or shorter: .or().and().filter()._and().filter()._or().forEach(...) It looks very strange.

Why we cannot just store a Query constructor in collection?

var q = new collection.Query
collection.filter(q.or(q.gt(...), q.lt(...))).forEach(...)
stemey commented 10 years ago

Hm, you are probably right, it is not feasible to create every possible query with a 'train wreck' :)

As far as I understand Collection.filter returns a new Collection. Using the proposed method would create a lot of Subcollections. That might be a waste that should be prevented by a queryBuilder as a separate instance.

kriszyp commented 10 years ago

I think that providing a store.Query() builder/constructor is a reasonable path forward. Any suggestions on what functionality should be included in the base stores (Memory and Rest (which would consist of serialization to query strings))?

kriszyp commented 10 years ago

Another thought. If we are to truly define a "Query" builder, should Query be defined to include things like sorting (which is certainly part of the resulting query results)? And if so, it seems like we would want to try to make unary query operations symmetrical between the collection and the Query build (available on both), and then the Query should be executed with a query() method. However, if we are actually always passing this constructed thing into filter(), than we should have a "Filter()" builder instead, which I think implies only operations that help create a subset of objects (no mapping, aggregation, etc., as those would belong on the collection, I presume). So are we wanting a "Query" or "Filter" builder?

stemey commented 10 years ago

We should probably have a "Filter" builder then. I think, the sorting can be done via the available sort method.

anatoliyarkhipov commented 10 years ago

I agree, we need a "Filter" instead of "Query". It must have the logical and comparison operators, no more. If we will provide a Query builder with other methods (like "sort", "group_by", etc.) then:

  1. People will start to implement a custom builders instead of custom stores for specific API in their apps;
  2. Also it does not allow us to change a collection in different places:
function getClosedTasks() {
    var f = new tasks.Filter
    return tasks.filter(f.eq('status', 'closed'))
}

function getLastClosedTasks() {
    return getClosedTasks().sort('-date')
}

getLastClosedTasks().fetchRange(0, 10).then( /* ... */ )

// I can not find a clear way to do something like this if the 
// "sort" method will be in an builder. 
// It might be .query(...).query(...), but it is confusing me:

// ...

function getLastClosedTasks() {
    var q = new Tasks.Query
    return getClosedTasks()
         .query(q.sort('-date')) // what? why query? i just want sort the tasks
}

// ...
neonstalwart commented 10 years ago

i'm going to say we should still be doing Query. the reason is because of serialization of the query. abstracting the complete serialization of the query seems necessary to gain all the benefits. if we just do Filter then how should we allow for different representations/serializations of sorting and paging? i think that whatever the answer is, it's going to effectively just be a way to implement the difference between Filter and Query so let's just go for Query.

kriszyp commented 10 years ago

The serialization isn't necessarily the concern of the the filter builder though, is it? Isn't it only the Request store that needs to be responsible for serializing queries (and it can already do this for sorting and paging)?

stemey commented 10 years ago

The original Idea is to provide an api to build queries, that go beyond simple comparisons. I don't see any ambiguities in the sort API. So really we are only talking about filtering.

Reusing the store and exchanging the Filter/Query Builder is not a design goal that I feel is necessary. The builder is provided by the store and an integral part of it.

The way the responsibilities are split between queryBuilder and store is up to the store developer.

neonstalwart commented 10 years ago

it seems we are close to moving on this idea so i'm supportive of going for just Filter in order to move forward. i think that if it becomes evident that there is some reason that it needs to be Query instead then we can consider the options at that time.

stemey commented 10 years ago

I suggest that we also add a "like" expression. That directly maps to queries in SQL, Lucene and others. This is different from "match" which takes a regex pattern as argument. The wildcard should be asteriks or can be defined as another argument. The escaping of the wildcard pattern should probably just be a duplication (e.g. "Will**" matches anything that starts with "Will".)

stemey commented 10 years ago

Also a like expression can be translated to regex but not vice versa.

cjolif commented 10 years ago

I guess the only drawback I see with the suggested API in 38bb92e is that currently you can have components (such as the delite(ful) ones) leveraging dstore API without actually requiring dstore to be loaded (for example users that have their own store implementation, as soon as they comply with the dstore API all will be fine).

If the components have to build their queries using a Filter object, then this forces the components to actually loaded dstore/Filter irrespective of whether the user is coming up with is own store not requiring the dstore project. If the syntax was available on the store itself, then the user's store could implement it itself and obviously dstore implementation would just leverage dstore/Filter.

stemey commented 10 years ago

@cjolif I understand the api differently. A dstore implementation provides a Filter constructor in the property Filter. So there can be an individual Filter class for every store class.

  // this widget has a store property provided by client code
  var filter = new this.store.Filter().eq("name","william");
  this.store.filter(filter).forEach(...)
cjolif commented 10 years ago

ok, in that case it should be fine. I guess I was fooled by this test case:

https://github.com/SitePen/dstore/blob/38bb92ee9f0dd653d801549b8f0f3e779cfc951a/tests/Memory.js#L58

where the filter is instantiated directly from the dstore/Filter import.

kriszyp commented 10 years ago

Yes, that test case should be fixed, and I think I did in the query-mixin branch I was considering, but we will get it fixed when we merge. On Jul 8, 2014 7:20 AM, "Christophe Jolif" notifications@github.com wrote:

ok, in that case it should be fine. I guess I was fooled by this test case:

https://github.com/SitePen/dstore/blob/38bb92ee9f0dd653d801549b8f0f3e779cfc951a/tests/Memory.js#L58

where the filter is instantiated directly from the dstore/Filter import.

— Reply to this email directly or view it on GitHub https://github.com/SitePen/dstore/issues/34#issuecomment-48342253.

kriszyp commented 10 years ago

And back to the like operator, I would like to avoid having multiple like-ish operators. One can certainly translate a subset of regex to the like format (that roughly corresponds to the subset of the regex functionality that is supported by like).

mercmobily commented 10 years ago

Hi,

I re-read this thread a few times while sick. Here is my 2c -- and that's how I 'fixed" the issue on my end.

I frankly think that asking the clients to do anything more than simple URL fields (the way it is now) will basically lead to very complex code on the client side. The beauty of stores is that they can be consumed pretty much by anybody; the fact that you can only pass key/value to them for filtering, to me, is a plus.

What a bout complex queries?

This is the way I deal with this on my application (which isn't even released yet); the server defines stores as ever, using JsonRestStores (which uses callbacks rather than promises, wooops :D ). Say that you have a table with:

name: string(30)
surname: string(60)

Now: the "default" search fields for this, server side, are "name" and "surname", and they need to match 100%. This means that if you call the store /some/path/to/store?name=tony, it will only return records where the name is exactly Tony.

However, in your server, you can define a searchSchema:

var UsersInfo = declare( [ HotStore, MultiHomePermsMixin, PrivateUserDataMixin ], {

     schema: new HotSchema({

aredValidator: 'email', trim: 70, min: 4 }, /surname : { type: 'string', required: true, default: "Your surname", notEmpty: true}, /name : { type: 'string', required: true, default: "Your name", notEmpty: true }, }),

     onlineSearchSchema: new Schema({
       name             : { type: 'string', trim: 20, searchable: true, searchOptions: { type: 'is' } },
       surname          : { type: 'string', trim: 20, searchable: true, searchOptions: { type: 'is' } },
       nameContains     : { type: 'string', trim: 4, searchable: true, searchOptions: { type: 'contains',   field: 'name' } },
       surnameContains  : { type: 'string', trim: 4, searchable: true, searchOptions: { type: 'contains',   field: 'surname' } },
       surnameStartsWith: { type: 'string', trim: 4, searchable: true, searchOptions: { type: 'startsWith', field: 'surname' } },
       nameOrSurnameStartsWith: { type: 'string', trim: 4, searchable: true, searchOptions: [ { field: 'surname', type: 'startsWith', condition: 'or' }, { field: 'name', type: 'startsWith', condition: 'or' } ] },
     }),
     handlePut: true,
     handleGet: true,

     storeName:  'usersInfo',

     publicURL: '/config/users/:userId',
   });

Basically, I define a set of "custom" search fields, each one with a specific meaning in terms of what it searches.

The painful part is the one where you need memory's queryEngine to implement these client-side. However, I honestly think that when using Memory as Cache to Json Rest, you nearly always:

Please note that while the server side is already done and dusted, the client side isn't. I was about to get into it a few weeks back, and then I got derailed by the whole beforeId thing in stores, as well as this very thread which I was observing.

My point is that the client side would benefit by being easy... super easy. It should never pass a complex query over, and expect the server to always deal with it. Instead, it should simply pass simple simple key/value fields -- and those keys can have a very loaded meaning, and even triggered very complex queries on the server -- with the client living in blissful ignorance of how complex things are.

Sorry about the overly long message -- just my 2c.

mercmobily commented 10 years ago

Having said all this, I might be completely full of crap and my solution might be completely ass. How ready is the query-mixin branch, Kris? Does it at least "kind of" work? (If it does, and if that's the direction dstore is going, I will follow right through and scrap the code on JsonRestStores for the "fancy filtering" fields, and embrace query-mixin now that I am in dstore-land...)

kriszyp commented 10 years ago

Here is the branch with the filtering building functionality if anyone wants to take a look before I potentially merge it: https://github.com/SitePen/dstore/tree/filter-builder

mercmobily commented 10 years ago

I really, really wants to see how this works before I make any (hopefully meaningful) comments. But I need to know how to use the thing! So...

Sorry, this is probably something I should know already...

On 29 August 2014 23:55, Kris Zyp notifications@github.com wrote:

Here is the branch with the filtering building functionality if anyone wants to take a look before I potentially merge it: https://github.com/SitePen/dstore/tree/filter-builder

— Reply to this email directly or view it on GitHub https://github.com/SitePen/dstore/issues/34#issuecomment-53894747.

wkeese commented 10 years ago

@mercmobily - comparing actually hard since the branch isn't rebased against the latest master, but https://github.com/Sitepen/dstore/compare/f34284a...filter-builder seems to work.

mercmobily commented 10 years ago

Thanks Kris. Two questions:

1) With this implementation, how do .and() and .or() actually work in terms of syntax? It's been discussed here, but I can't figure it out from the code.

2) At this point in my JsonRestStores server module I need to implement RQL, and also need to get specific layers (MongoDB, MySQL) to translate RQL to DB-specific queries. Is that right?

On 30 August 2014 06:58, Bill Keese notifications@github.com wrote:

@mercmobily https://github.com/mercmobily - comparing actually hard since the branch isn't rebased against the latest master, but f34284a...filter-builder https://github.com/SitePen/dstore/compare/f34284a...filter-builder seems to work.

— Reply to this email directly or view it on GitHub https://github.com/SitePen/dstore/issues/34#issuecomment-53938885.

anatoliyarkhipov commented 10 years ago

@mercmobily

  1. You can see it here (file tests/Request.js): https://github.com/SitePen/dstore/commit/a53180ce7477db4c42a8cb754f544aab2a468d66 filter.ne('id', 2).or(filter.eq('foo', true), filter.eq('foo'));
  2. I think, you can translate RQL query to your server's format in the client module, if I understand the question
kriszyp commented 10 years ago

As @anatoliyarkhipov pointed out, translation can occur client, side. The serialization of the filter can be customized by overriding _rendreFilterParams (from dstore/Request).