automerge / automerge-classic

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically.
http://automerge.org/
MIT License
14.75k stars 467 forks source link

If I do lots of change by a single assignment, automerge won't perform a diff. #79

Closed linonetwo closed 6 years ago

linonetwo commented 6 years ago

See https://github.com/ianstormtaylor/slate/issues/259#issuecomment-377482517 If I do something like this:

const doc2c = Automerge.change(doc2b, 'Adding some text', doc => {
  // from https://docs.slatejs.org/walkthroughs/saving-to-a-database
  doc.note = {
    document: {
      nodes: [
        {
          object: 'block',
          type: 'paragraph',
          nodes: [
            {
              object: 'text',
              leaves: [
                {
                  text: 'A line of text in a paragraph.',
                },
              ],
            },
          ],
        },
      ],
    },
  };
});
// sent change to doc1's client
changes = Automerge.getChanges(doc2b, doc2c);
const doc1c = Automerge.applyChanges(doc1b, changes);

// change some text deep inside document
const doc1d = Automerge.change(doc1c, 'Rewrite line', doc => {
  doc.note = {
    document: {
      nodes: [
        {
          object: 'block',
          type: 'paragraph',
          nodes: [
            {
              object: 'text',
              leaves: [
                {
                  text: 'Rewrite this line.',
                },
              ],
            },
          ],
        },
      ],
    },
  };
});

changes = Automerge.getChanges(doc1c, doc1d);

Those changes looks like this:

[ { actor: 'fca6a267-8317-4471-a8ed-71267e7d5779',
    seq: 2,
    deps: { '46ede060-927c-4ddb-9224-a9c654c3d42c': 1 },
    message: 'Rewrite line',
    ops:
     [ [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object],
       [Object] ] } ]

There are lots of ops, though I'm only change a single line.

Will this result in a huge history, just like somebody commits a movie into the git repo?

j-f1 commented 6 years ago

I’m curious, why can’t you just say doc.note.document.nodes[0].leaves[0].text = "..."?

linonetwo commented 6 years ago

The reason is that this json is serialized from slate.js editor, and each save can generate a different complex json.

j-f1 commented 6 years ago

You might be able to use something like assign-deep to make only the required changes (assignDeep(doc.note, newContent))

pvh commented 6 years ago

What's happening here is that automerge is not a diff-based system but an identity one. This is important because when we merge changes across a distributed system we want to guarantee that if two people create, for example, empty TODO items and then subsequently edit the titles after merging we would have two different TODO items with different titles, not one TODO item with one title or the other.

I suspect something like assign-deep would do the job you're looking for.

Further, for a text editor you might look into collecting more granular operations as they occur and updating the document accordingly. I'm afraid I am not familiar with slate.js, but if you're hoping to do something akin to real-time collaboration large infrequent updates are much more likely to generate potential conflicts than regularly synchronized and smaller ones.