forked from dexie/Dexie.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow advanced modifications of properties in Collection.modify (dexi…
…e#1910) Add a new class PropModification with capabilities of modifying properties into computed values based on the previous value, to improve the Operation-based CRDTs support that is already possible with where clauses and `Collection.modify()` but with more precise operations than just replacing a property to another constant value. The first operation to support is `replacePrefix` that replaces the leading string of a string. This PR also exports a helper function `replacePrefix()` to use in modify operations that returns an instance of PropModification configured to do this operation. Future operations could include `increment` (for numbers), `push` (for arrays), `add` (for unique arrays / sets), `remove` for arrays, and `updateArrayItem` for arrays with object items. The `replacePrefix` operation could seem a bit special-casey, but the reality is that it solves an important pattern when working with tree structures and letting an indexed property represent the tree path. Using this new operation, it becomes possible to move an entire sub tree in a single modify operation: ```js db.files .where('path') .startsWith('old/path') .modify({ path: replacePrefix('old/path', 'new/other/path') }); ``` Even though this can already be accomplished using a JS callback though doing the equivalent operation, it would not fully propagate the operation to DBCore in order to implement CRDT capabilities: ```js db.files .where('path') .startsWith('old/path') .modify(file => { file.path = 'new/other/path' + file.path.substring('old/path'.length); }); ``` Using this JS function would not propagate the operation to DBCore because a JS function cannot be interpreted safely for several reasons (security but also missing closures) - so sync implementations would only get the resulting individual put() operations that resulted locally from the modify operation, and would therefore not be able to consistently sync the tree move operation with the server in a truly consistent manner that would resolve correctly across multiple clients in case other clients have added, moved or deleted nodes while being temporarily offline. Adding this new operation will allow moving trees and propagate the operation consistently in synced environments that supports and propagate the `criteria` and `changeSpec` properties in the resulting put operation to DBCore and further down into sync implementations. Currently only Dexie Cloud Server will respect this and handle it consistently, but the information is available in DBCore to implement in other endpoints.
- Loading branch information
1 parent
6a2810b
commit 48e76a3
Showing
12 changed files
with
143 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { PropModification } from "../helpers/prop-modification"; | ||
|
||
export function replacePrefix(a: string, b:string) { | ||
return new PropModification({replacePrefix: [a, b]}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { PropModSpec } from "../public/types/prop-modification"; | ||
|
||
export const PropModSymbol: unique symbol = Symbol(); | ||
|
||
export class PropModification implements PropModSpec { | ||
[PropModSymbol]?: true; | ||
replacePrefix?: [string, string]; | ||
|
||
execute(value: any) { | ||
const prefixToReplace = this.replacePrefix?.[0]; | ||
if (prefixToReplace && typeof value === 'string' && value.startsWith(prefixToReplace)) { | ||
return this.replacePrefix[1] + value.substring(prefixToReplace.length); | ||
} | ||
return value; | ||
} | ||
|
||
constructor(spec: PropModSpec) { | ||
Object.assign(this, spec); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export declare const PropModSymbol: unique symbol; | ||
|
||
export type PropModSpec = { | ||
replacePrefix?: [string, string]; | ||
} | ||
|
||
export class PropModification implements PropModSpec { | ||
[PropModSymbol]?: true; | ||
replacePrefix?: [string, string]; | ||
|
||
constructor(spec: PropModSpec); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import { KeyPaths, KeyPathValue } from "./keypaths"; | ||
import { PropModification } from "./prop-modification"; | ||
|
||
export type UpdateSpec<T> = { [KP in KeyPaths<T>]?: KeyPathValue<T, KP> }; | ||
export type UpdateSpec<T> = { [KP in KeyPaths<T>]?: KeyPathValue<T, KP> | PropModification }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { equal } from 'QUnit'; | ||
import sortedJSON from "sorted-json"; | ||
import { deepClone } from '../src/functions/utils'; | ||
|
||
export function deepEqual(actual, expected, description) { | ||
actual = JSON.parse(JSON.stringify(actual)); | ||
expected = JSON.parse(JSON.stringify(expected)); | ||
actual = sortedJSON.sortify(actual, { sortArray: false }); | ||
expected = sortedJSON.sortify(expected, { sortArray: false }); | ||
equal(JSON.stringify(actual, null, 2), JSON.stringify(expected, null, 2), description); | ||
} | ||
export function isDeepEqual(actual, expected, allowedExtra, prevActual) { | ||
actual = deepClone(actual); | ||
expected = deepClone(expected); | ||
if (allowedExtra) Array.isArray(allowedExtra) ? allowedExtra.forEach(key => { | ||
if (actual[key]) expected[key] = deepClone(prevActual[key]); | ||
}) : Object.keys(allowedExtra).forEach(key => { | ||
if (actual[key]) expected[key] = deepClone(allowedExtra[key]); | ||
}); | ||
|
||
actual = sortedJSON.sortify(actual, { sortArray: false }); | ||
expected = sortedJSON.sortify(expected, { sortArray: false }); | ||
return JSON.stringify(actual, null, 2) === JSON.stringify(expected, null, 2); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,6 @@ | |
"downlevelIteration": true | ||
}, | ||
"files": [ | ||
"tests-all.js" | ||
"tests-all.js", "./deepEqual.js" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters