Dexie.js icon indicating copy to clipboard operation
Dexie.js copied to clipboard

Return only one field?

Open tobiasBora opened this issue 1 year ago • 6 comments

If I am only interested on the field foo of an object, doing:

const firstFriend = await db.friends.get(1);

would lead to a re-render of the component each time firstFriend changes. Would it be possible to add a way to select only a subfield, such that it gets re-rendered only when firstFriend.nickname is changed for instance?

tobiasBora avatar May 12 '24 16:05 tobiasBora

An update of a property will trigger all live querys that have requested that object no matter which properties they did access. This would be possible to track using Proxies but I'm not sure if that would be worth it in the general case.

dfahlander avatar Jun 25 '24 23:06 dfahlander

In a previous project of mine this was an important bottleneck as I had a large list and I was printing in each cell a dropdown menu with the title of all other elements in the list. Title were rarely changing, but the content of the list were changing very frequently, basically at each key stroke, causing the whole list dom to re-render at each key stroke, causing a really laggy interface. One can combine this with rxjs to avoid re-render when the titles does not change but this seems a bit dirty and not sure how it compares in term of efficiency since the rxjs function would still run O(n) comparisons at every key stoke…

tobiasBora avatar Jun 26 '24 07:06 tobiasBora

I think that Dexie's responsibility should be restricted to not re-launch the querier too often and the frontend components responsibility would be to avoid unnescessary re-rendering based on comparing individal input properties (in React it would be by using pure / memo components, in Angular probably by using an Rx distinct operator or similar). It would be tempting to try to solve this in dexie because it's possible in this particular case but I think it would come with a too great cost in code size and performance hit on other use cases.

dfahlander avatar Jun 26 '24 07:06 dfahlander

Thanks. I'm not too familiar with the internals of dexie.js, would this theoretically require Dexie to also do O(n) operations for every key stroke, or could Dexie.js solve this using only a constant number of operations for every key stroke? If Dexie.js also requires O(n) comparisons, then it makes sense to use indeed Rx/memo… but if it can do it in O(1) operations, then there would be some advantages to do it in Dexie.js directly I guess.

It would be tempting to try to solve this in dexie because it's possible in this particular case but I think it would come with a too great cost in code size and performance hit on other use cases.

I understand. To avoid performance hit on other use cases, would it be possible to create a special .getField method to apply this possibly costly overhead only when needed?

tobiasBora avatar Jun 26 '24 09:06 tobiasBora

Thanks. I'm not too familiar with the internals of dexie.js, would this theoretically require Dexie to also do O(n) operations for every key stroke, or could Dexie.js solve this using only a constant number of operations for every key stroke? If Dexie.js also requires O(n) comparisons, then it makes sense to use indeed Rx/memo… but if it can do it in O(1) operations, then there would be some advantages to do it in Dexie.js directly I guess.

Dexie's cache in version 4 makes it reuse a single request even if there are multiple identical liveQueries going on. The querier callback will be re-executed for every update but the dexie queries will resolve from the cache so the performance impact would be low as long as the querier callback doesn't do heavy computations. Now the caching framework only supports a subset of queries, namely pure range-based queries with (Table/Collection).toArray(): db.table.where({key: value}).toArray(), db.table.where('key').between(a,b).toArray(), above/below/equals/startsWith() etc). Support for Table.get() as well as offset/limit will be added though in an upcoming version.

It would be tempting to try to solve this in dexie because it's possible in this particular case but I think it would come with a too great cost in code size and performance hit on other use cases.

I understand. To avoid performance hit on other use cases, would it be possible to create a special .getField method to apply this possibly costly overhead only when needed?

There could be a cost in code complexity and code size also of the dexie library, but it could be worth to consider in future.

dfahlander avatar Jun 26 '24 09:06 dfahlander

Ok thanks, interesting. Is there any doc to explain in details how the cache works exactly, and the efficiency of the different functions, when a re-render is triggered etc?

In my case, I do a query like:

  let foo = liveQuery(
    () => db.foo.orderBy('position').toArray();
  );

and in a svelte component I do:

foo.map(d => ({value: {fooId: d.id}, name: d.name}))

to create a Select dropdown… if there is a way to make this use case more efficient to avoid re-renders, that would be great, otherwise I can certainly use Rxjs at the cost of a map + array comparison (so O(n)).

There could be a cost in code complexity and code size also of the dexie library, but it could be worth to consider in future.

I understand, do the best given your constraints ;-) Thanks!

tobiasBora avatar Jun 27 '24 08:06 tobiasBora