dexie / Dexie.js

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

Cascade on delete #1932

Open tobiasBora opened 3 months ago

tobiasBora commented 3 months ago

I have some items (say Authors) and some things that link to these items (say Books), but if I delete a given Author, I'd like to delete all items that refer to this author, not to have broken links in the database (I think this corresponds for instance to cascade deletion in django). Is it possible to do that automatically with Dexie.js (either theoretically or in practice)?

dfahlander commented 3 months ago

Dexie does not have any explicit foreign keys nor automatic cascade deletion. But it has ACID consistency so you can easily create a service method or function deleteAuthor() that deletes an author consistently:

function deleteAuthor(authorId) {
  return db.transaction('rw', db.books, db.authors, () => {
    db.books.where({authorId: authorId}).delete();
    db.authors.delete(authorId);
  });
}
tobiasBora commented 3 months ago

Ok thanks. So I guess it is not planned?

My main issue with this solution is that I need to keep track of all such objects linked to a single author… (in my case there might be plenty) but I'm thinking that for my specific application it might make more sense to create a separate database for each "author", and put all objects related to this author in the same database, this way deleting a single database is trivial.

Also, sorry to hijack here, but since you mention transaction, I need to implement an undo mechanism in my app, does Dixiejs provide a simple way to keep track of all transactions to provide an efficient way to undo or redo a transaction?

dfahlander commented 3 months ago

Cascading deletes are only relevant in hierarchial structures. One useful pattern for this that I have been advocating for, is to use a parentPath property on the objects define the hierarchy. That way it is easier to move or delete entire subtrees in a single query. It's describe in the release notes of dexie@4.0.1-beta.14. That pattern is even simpler if using a single table to store all the objects and let a type property distinguish type instead of the table, but it is also possible to use with multiple tables and - just that it need multiple queries (one per table) when deleting or moving.

tobiasBora commented 3 months ago

I see, that would be great to have. Then we can maybe keep this issue and remove the question label?

Btw, I created another issue to discuss about undo here https://github.com/dexie/Dexie.js/issues/1933

NeodymiumFerBore commented 3 months ago

Was also looking for a cascade on delete feature. I stumbled upon multiple solutions:

Maybe I missed a crucial point in the documentation. I was hyped by the Dexie.use() solution, but having to include all stores in deletion queries made me doubt.

May I have some guidance for the example I gave (Boards, Lists and Items in separate tables)? Am I wrong running away from a "mono table"? Starting a new project with Dexie 4.0.1, should the parentPath pattern be preferred over Dexie.use()?

dfahlander commented 3 months ago

Traditional cascading deletes can be implemented using DBCore as follows:

  1. Create a DBCore middleware and override transaction and table
  2. In transaction make sure to always include related tables whenever a 'readwrite' transaction is requested. For example if a readwrite transaction on 'organizations' is requested, also append the 'organizationUnits' table if those belong to organizations and may be deleted as a result of deleting an organization.
  3. In table, call downlevelDatabase.table() and override mutate method before returning your clone.
  4. In your version of the mutate method, start by querying all objects from related tables that reference the objects that are about to be deleted and check if another cascading delete operation needs to take place before actually performing the deletions. This has to be done recursively.

Some type of declaration of the foreign keys' properties and tables need to be declared if this shall be implemented generically. This declaration could for example be an argument to the middleware factory function.

The add-on dexie-relationships does not implement cascading deletes but it does a trick to override Dexie's schema declaration and invents its own syntax for declaring indexes representing foreign keys. Maybe this approach could be used as an alternative to providing the foreign-key declaration as argument to middleware factory. That add-on is a bit old but the code is very small and it still works with dexie 4.