automerge-classic icon indicating copy to clipboard operation
automerge-classic copied to clipboard

Cannot move objects in arrays since 0.14.1

Open viktorkalajo opened this issue 4 years ago • 8 comments

The error Cannot assign an object that already belongs to an Automerge document seems to trigger when trying to move an item in an array since the last patch. Here is an example to reproduce:

const automerge = require('automerge');

const s1 = automerge.init();

const s2 = automerge.change(s1, 'create array', doc => {
  doc.myArray = [{foo: '1'}, {bar: '2'}];
});

const s3 = automerge.change(s2, 'move item in array', doc => {
  const removedItem = doc.myArray.splice(0, 1);
  doc.myArray.insertAt(1, removedItem);
});

If this is an expected error, what is the recommended way of moving things in arrays?

viktorkalajo avatar Jun 18 '20 09:06 viktorkalajo

Thanks @viktorkalajo for the bug report. This should be possible, sorry to hear that it's not working at the moment. I will look into it, but in the meantime, as a hacky workaround you can deep-clone the removed object by serialising and deserialising to JSON:

const s3 = automerge.change(s2, 'move item in array', doc => {
  const removedItems = doc.myArray.splice(0, 1);
  doc.myArray.insertAt(1, ...JSON.parse(JSON.stringify(removedItems)));
})

ept avatar Jun 24 '20 12:06 ept

The workaround has an issue

Assume

  • Document

    {
      sourceItemsArray: [{
        objectId: 1,
        value: { a: 1, b: 2 }
      }],
      destinationItemsArray: [{
        objectId: 2,
        value: { a: 3, b: 4 }
      }]
    }
    
    
  • and two concurrent actors are editing the document

Event sequence

  1. The first one changes at T1 the value of the item in sourceItemsArray, to {a: 101}
  2. The second one then moves at T2 the same item from sourceItemsArray to the destination
  3. On merge, the result document is
    {
      sourceItemsArray: [],
      destinationItemsArray: [{
        objectId: 2,
        value: { a: 3, b: 4 }
      },{
        objectId: 3,
        value: { a: 1, b: 2 }
      }]
    }
    
  4. Expected document is
    {
      sourceItemsArray: [],
      destinationItemsArray: [{
        objectId: 2,
        value: { a: 3, b: 4 }
      },{
        objectId: 1,
        value: { a: 101, b: 2 }
      }]
    }
    
    

So the change at T1 is lost. Note the differences are:

  1. objectId (from 1 to 3)
  2. value.a == 1 but it should be value.a == 101

mkhanal avatar Aug 20 '20 05:08 mkhanal

Yes, that is unfortunately an expected behaviour of the workaround, since it removes the objects from the old location and creates fresh objects (with the old contents) in the new location.

A proper move operation is planned, but unfortunately it's going to be a bit of work.

ept avatar Aug 20 '20 11:08 ept

Not sure if it helps, but it's also impossible to assign objects removed from arrays or other objects to other object properties as well. Any thought on how to work around this without de-soulifying the object?

const automerge = require('automerge')
const s1 = automerge.init()

const s2 = automerge.change(s1, 'create array', doc => {
  doc.myArray = [{ foo: '1' }, { bar: '2' }]
})
const s3 = automerge.change(s2, 'move item out of array', doc => {
  const removedItem = doc.myArray.splice(0, 1)
  doc.someProp = removedItem
})

and

...
const s2 = automerge.change(s1, 'create object', doc => {
  doc.myProp = { a: { foo: '1' }, b: { bar: '2' } }
})
const s3 = automerge.change(s2, 'move item out of object', doc => {
  const removedItem = doc.myProp.a
  delete doc.myProp.a
  doc.removedItem = removedItem
})

both result in:

TypeError: Cannot assign an object that already belongs to an Automerge document. See https://github.com/automerge/automerge#making-fine-grained-changes

beorn avatar Sep 01 '20 17:09 beorn

@beorn Sorry, the deep cloning workaround is the only solution for now, until the proper move operation lands.

ept avatar Sep 04 '20 11:09 ept

Closing this issue as there is nothing to be done for now. Support for a proper move operation is planned, which will address this issue.

ept avatar Feb 11 '21 16:02 ept

@ept is this a duplicate of another issue? if not, might it be possible to re-open until the move lands as a means to notify folks who are interested to know?

hcientist avatar Feb 11 '21 17:02 hcientist

Okay @hcientist, makes sense. Reopening the issue to serve as a kind of documentation.

ept avatar Feb 11 '21 17:02 ept