ember-indexeddb icon indicating copy to clipboard operation
ember-indexeddb copied to clipboard

Multi index querying doesn't seem to be supported.

Open joegaudet opened this issue 5 years ago • 7 comments

Setup like so:

export default IndexedDbConfigurationService.extend({
  currentVersion: 1,

  version1: computed(function () {
    return {
      stores: {
        'restaurant': '&id,*areas'
      }
    };
  }),

  mapTable: computed(function () {
    return {
      'restaurant': (item) => ({
        id: item.id,
        json: this._cleanObject(item),
        areas: get(item, 'relationships.areas.data').map(_ => _.id)
      })
    }
  })
});

Can see that the db is loaded as expected:

Screen Shot 2020-03-22 at 12 35 17 PM

However, the store does not return anything:

const r = await this.store.query('restaurant', {areas: ["1"]});
// r === []

What am I missing.

joegaudet avatar Mar 22 '20 19:03 joegaudet

This here seems to be the issue:

https://github.com/mydea/ember-indexeddb/blob/master/addon/services/indexed-db.js#L567

should have a predicate index.multi

joegaudet avatar Mar 22 '20 19:03 joegaudet

Hmm, the line you quoted is about Compound Index, but I believe you are talking about MultiEntry Index.

I have not added any special support for MultiEntry indices so far. However, looking through the Dexie docs, it appears you should be able to solve this specific usecase by simply querying for {areas: "1"}. Basically, you can, as of now, only query for a single item in the array (= includes).

To be able to have and behavior natively, we'd need to add something like this here (multi index does not work together with compound index): https://github.com/mydea/ember-indexeddb/blob/master/addon/services/indexed-db.js#L548

if (keys.length === 1) {
  let key = keys[0];
 
  if (typeOf(query[key]) === 'array') {
    let values = query[key];
    let result = db[type].where(key).equals(values.shift());
    while(values.length > 0) { 
      result.and(key).equals(values.shift());
    }
    return result.distinct();
   }

  return db[type].where(key).equals(query[key]);
}

That seems like it should work, but I've just written that from the top of my head. I'd love to accept a PR with that change, ideally tested - if you want to tackle that, I'd love to guide you if necessary!

mydea avatar Mar 23 '20 07:03 mydea

Hmm, I was thinking it more like an SQL in a statement like so:

select * from foo where id in (1,2,3)

So it's more like an or.

joegaudet avatar Mar 23 '20 16:03 joegaudet

This stuff here:

https://github.com/mydea/ember-indexeddb/blob/master/addon/services/indexed-db.js#L610

Not intended for multi indexes?

joegaudet avatar Mar 23 '20 16:03 joegaudet

Taking an approach like this so far:

    // Else, filter manually
    return Object.entries(query)
      .reduce(
        (query, [queryKey, queryValue]) => {
          let index = indexMap[queryKey];
          let isMulti = index.multi && isArray(queryValue);

          if (!query) {
            return isMulti
              ? db[type].where(queryKey).anyOf(...queryValue)
              : db[type].where(queryKey).equals(queryValue);
          } else {
            let predicate = isMulti
              ? (item) => get(item, queryKey).any(_ => queryValue.includes(_))
              : (item) => get(item, queryKey) === queryValue;

            return query.and(predicate);
          }
        }, null);

Seems to be working, curious, have you had any success with relationships between objects? Do they need to be async false?

joegaudet avatar Mar 23 '20 18:03 joegaudet

Opened a PR for initial comment.

joegaudet avatar Mar 24 '20 02:03 joegaudet

Will think about how to test.

joegaudet avatar Mar 24 '20 02:03 joegaudet