Skip to content

Commit 57dbc7c

Browse files
authored
Merge pull request #969 from streamich/diff-obj-key
Ability to shallow merge "obj" node keys
2 parents 44475e5 + 58d4adb commit 57dbc7c

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

packages/json-joy/src/json-crdt-diff/JsonCrdtDiff.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,31 @@ export class JsonCrdtDiff {
248248
return this.builder.flush();
249249
}
250250

251+
/** Diffs only keys present in the destination object. */
252+
public diffDstKeys(src: ObjNode, dst: Record<string, unknown>): Patch {
253+
const builder = this.builder;
254+
const inserts: [key: string, value: ITimestampStruct][] = [];
255+
const keys: string[] = Object.keys(dst);
256+
const keyLength = keys.length;
257+
for (let i = 0; i < keyLength; i++) {
258+
const key = keys[i];
259+
const child = src.get(key);
260+
const dstValue = dst[key];
261+
if (!child) {
262+
inserts.push([key, this.buildConView(dstValue)]);
263+
continue;
264+
}
265+
try {
266+
this.diffAny(child, dstValue);
267+
} catch (error) {
268+
if (error instanceof DiffError) inserts.push([key, this.buildConView(dstValue)]);
269+
else throw error;
270+
}
271+
}
272+
if (inserts.length) builder.insObj(src.id, inserts);
273+
return this.builder.flush();
274+
}
275+
251276
protected buildView(dst: unknown): ITimestampStruct {
252277
const builder = this.builder;
253278
if (dst instanceof Timestamp) return builder.con(dst);

packages/json-joy/src/json-crdt-diff/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ export const diff = (src: NodeApi<any>, dst: unknown): Patch | undefined => {
1010

1111
export const merge = (src: NodeApi<any>, dst: unknown): Patch | undefined => {
1212
const patch = diff(src, dst);
13-
if (patch) src.api.model.applyPatch(patch);
13+
if (patch) src.api.model.applyLocalPatch(patch);
1414
return patch;
1515
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {Model} from '../../Model';
2+
3+
describe('merge', () => {
4+
test('can subscribe and un-subscribe to "view" events', async () => {
5+
const doc = Model.create({
6+
obj: {
7+
foo: new Uint8Array([1, 2, 3]),
8+
bar: '123',
9+
},
10+
});
11+
const api = doc.api;
12+
const obj = api.obj(['obj']);
13+
obj.merge({
14+
foo: new Uint8Array([4, 5, 6]),
15+
baz: 'new',
16+
});
17+
expect(obj.view()).toEqual({
18+
foo: new Uint8Array([4, 5, 6]),
19+
baz: 'new',
20+
});
21+
});
22+
23+
test('can merge specific key', async () => {
24+
const doc = Model.create({
25+
obj: {
26+
foo: new Uint8Array([1, 2, 3]),
27+
bar: '123',
28+
},
29+
});
30+
const api = doc.api;
31+
const obj = api.obj(['obj']);
32+
obj.get('foo').merge(new Uint8Array([4, 5, 6]));
33+
expect(obj.view()).toEqual({
34+
foo: new Uint8Array([4, 5, 6]),
35+
bar: '123',
36+
});
37+
obj.mergeKeys({foo: {aha: 123}});
38+
expect(obj.view()).toEqual({
39+
foo: {aha: 123},
40+
bar: '123',
41+
});
42+
obj.mergeKeys({foo: '123', x: 'y', bar: '1234'});
43+
expect(obj.view()).toEqual({
44+
foo: '123',
45+
bar: '1234',
46+
x: 'y',
47+
});
48+
});
49+
});

packages/json-joy/src/json-crdt/model/api/nodes.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder';
1010
import {MergeFanOut, MicrotaskBufferFanOut} from './fanout';
1111
import {ExtNode} from '../../extensions/ExtNode';
1212
import * as diff from '../../../json-crdt-diff';
13+
import {JsonCrdtDiff} from '../../../json-crdt-diff/JsonCrdtDiff';
1314
import type {Path} from '@jsonjoy.com/json-pointer';
1415
import type {Extension} from '../../extensions/Extension';
1516
import type {ExtApi} from '../../extensions/types';
@@ -540,6 +541,20 @@ export class ObjApi<N extends ObjNode<any> = ObjNode<any>> extends NodeApi<N> {
540541
return this.node.keys.has(key);
541542
}
542543

544+
/** Diffs only keys present in `dst` object. */
545+
diffKeys(dst: Record<string, unknown>): Patch | undefined {
546+
const diff = new JsonCrdtDiff(this.api.model);
547+
const patch = diff.diffDstKeys(this.node, dst);
548+
return patch.ops.length ? patch : void 0;
549+
}
550+
551+
/** Merges only keys present in `dst` object. */
552+
mergeKeys(dst: Record<string, unknown>): Patch | undefined {
553+
const patch = this.diffKeys(dst);
554+
if (patch) this.api.model.applyLocalPatch(patch);
555+
return patch;
556+
}
557+
543558
/**
544559
* Returns a proxy object for this node. Allows to access object properties
545560
* by key.

0 commit comments

Comments
 (0)