dexie / Dexie.js

A Minimalistic Wrapper for IndexedDB
https://dexie.org
Apache License 2.0
11.62k stars 642 forks source link

liveQuery() with dynamic parameters #1738

Open ndegheselle opened 1 year ago

ndegheselle commented 1 year ago

Hello,

I am using Dexie with Svelte, I make my requests with liveQuery() I have several queries that need dynamic parameters that can be changed by the user, for exemple :

const products = liveQuery(() => db.products.where('date').above(filterDate)).toArray()

I have seen a lot of solution using rxjs but I don't think Dexie is using it, I was wondering if thre was a method to do that. ChatGPT is conveniently inventing products.refilter() or products.reEvaluate() which would be convenient.

I also haven't find anything on the Observable specification something that would help me (like joining two observable in one).

Any help would be appreciated, thanks !

ndegheselle commented 1 year ago

One possible solution i found is to just use the svelte reactive statement, for exemple :

$: products = liveQuery(() => db.products.where('date').above(filterDate)).toArray())

While automatically refresh then filterDate is updated (in other words svelte will recreate the liveQuery whenever the args in it will changes). However with this you can't easily make a store that is shared between several components since you can use the svelte reactive syntaxe only in svelte components.

One other solution I have is to modify the liveQuery definition to expose the internal method doQuery :

...
        doQuery();
        observable.refresh = doQuery;
        return subscription;
});
...

So that you can do : products.refresh() whenever the args of the liveQuery changed, I am not to sure if calling doQuery multiple times have effects on perf or something else but it still seems like a better solution than hacking with svelte reactive $.

fev4 commented 1 year ago

The best way I found to handle this is just to use db queries inside functions and call those depending on specific actions or life cycle hooks, either onMount, onDestroy, or what-ever other user event you define.

liyuan1125 commented 1 year ago

I also want to know how to implement

<template>
  <select v-model='limit'>
    <el-option :value='10'>10</el-option>
    <el-option :value='20'>20</el-option>
  </select>
</template>

<script setup lang="ts">
  import { from, useObservable } from '@vueuse/rxjs'
  import { liveQuery } from 'dexie'
  import db from '@/db/db.ts'
  import { Message } from '@/types/message.ts'

  const messages = useObservable<Message[]>(
    from(liveQuery(async () => await db.messages.limit(limit.value).toArray()))
  )
</script>
ndegheselle commented 1 year ago

@fev4 Can't do that since I am updating the indexedDB table from different components, so I have to keep watching the database in my stores (which is really easy with liveQuery). I tried to do what you said nonetheless but I can't found a good way to get an update event from a table with Dexie, so I am stuck with updating the query then my params changes but no updates from DB or having static params but updates from the DB.

@liyuan1125 this stackoverlow answer is probably what you are looking for.

Finnaly settled with forking Dexie and exposed doQuery : forked diff. With this I can use it like so :

const projectsFilters = {
    date: null,
};
export function setProjectsDateFilter(_date)
{
    projectsFilters.date = _date;
    currentProjects.doQuery();
}

export const currentProjects = liveQuery(() => getProjectsByAccount(currentAccountId, projectsFilters.date));
liyuan1125 commented 1 year ago

@fev4不能这样做,因为我正在从不同的组件更新 indexedDB 表,所以我必须继续观察我商店中的数据库(使用 liveQuery 真的很容易)。尽管如此,我还是尝试按照您所说的去做,但是我找不到使用 Dexie 从表中获取更新事件的好方法,所以我一直坚持更新查询,然后我的参数发生了变化,但没有来自数据库的更新或具有静态参数但是来自数据库的更新。

@liyuan1125 这个 stackoverlow 答案可能就是你要找的。

Finnaly 解决了 forking Dexie 并公开了 doQuery:forked diff。有了这个我可以像这样使用它:

const projectsFilters = {
    date: null,
};
export function setProjectsDateFilter(_date)
{
    projectsFilters.date = _date;
    currentProjects.doQuery();
}

export const currentProjects = liveQuery(() => getProjectsByAccount(currentAccountId, projectsFilters.date));

I can't understand your example, can you provide a more detailed code? Thank you so much

ndegheselle commented 1 year ago

@liyuan1125 Here is a complete example of how I would do it with Vuejs, keep in mind that I can call doQuery() because I forked Dexie and I am using my own version of dexie.