It does complicate the type, and it breaks my nice abstraction. Also remember that any funny logic will need to be on the server and the client - and I'd really like the server to be application-agnostic if possible. That said, the server will probably need application-specific schema validation & access control code anyway, so maybe its not a big deal.
Another option is to just do the dangerous thing for objects, but actively encourage people to use lists instead. If you're inserting into a list instead of inserting into an object, the semantics are safe & easy. The final list will just contains both items. For hierarchal to-do lists, you probably want a children:[] list on your nodes anyway. Using object-move when the key is either a GUID or a hash of the data would work fine as well because your keys won't conflict. This won't work if you're trying to map a directory structure of files though. But thats the only bad use case I can think of. Maybe something as simple as a flag on the move operation saying "in the case of conflicts, rewrite the destination key to {key}_n". Or we could just force the IDE to use a list of files, and leave deduping hacks there. -J On Sat, Oct 18, 2014 at 4:44 PM, Ali Lown <a...@lown.me.uk> wrote: > Hi Joseph, > > I think that the only sensible option is to delegate the resolution of > this action in the case of conflict back into the application, so some > sort of extension of (4) that allows some arbitrary lambda expression > to be passed as the onconflict method. (Depending on the situation, > they might want to 'merge' the two items (if possible), rather than > moving one into a 'backup' location). > > This does complicate the OT type, and does make it more difficult to > analyse how long certain actions will take to resolve though... > > How does this sound? > > Ali > > On 19 October 2014 00:33, Joseph Gentle <jose...@gmail.com> wrote: >> I'm (finally!) taking a serious look at making a better version of the >> JSON OT type. I'm cross-posting this here because it directly effects >> sharejs & derby users, and I think any serious rewrite of wave will >> use something like this at the top level to store waves. >> >> I have two questions that I would love some input on from the wider >> community. >> >> We want to add a 'move' action which will let you transplant arbitrary >> parts of the JSON structure to other places in the tree. This is >> really useful if, for example, you want a hierarchal to-do list, a >> tree of source files for your IDE or a tree of blips. >> >> But I can't figure out what should happen if two users move different >> objects to the same place in the tree at the same time. >> >> 1. The simplest option is to simply delete one of them. This is really >> convenient from a programming pov, but I think that would be the worst >> kind of surprise. {x:1, y:2} -> {z:2} >> 2. We could try and make one of the moves fail - {x:1, y:2} -> {x:1, >> z:2}. On the face of it this sounds great, but this is a cascading >> failure. If (locally) I move x->z then insert some new data at x, what >> happens to the new data? Does the new data get deleted? Does x get >> deleted? I can't think of a good answer here which isn't just >> dangerous in a more complicated way. Making both the moves fail has >> the same problem. >> 3. We could pick a winner and then move the loser to some special >> lost&found bucket or something. So, {x:1, y:2} -> {__recovered_z:1, >> z:2} But that could play havoc with data bindings, and we'd have to >> introduce some reserved keys. The other way to do this is to introduce >> a new top-level structure which contains the data, so it'd be >> {data:{x:1, y:2}} -> {data:{z:2}, lost:[{z:1}]}. >> 4. Or we could add the backup location into the move operation itself, >> so whoever's building on top of this API can make the choice. (They >> can make a better decision because they know what the data structure >> looks like). So instead of (move x->z) we'd have (move x->z, >> onconflict:(move x->__x)) or something. >> >> Are there any other choices I'm missing here? I'm edging toward option >> 4, although it might increase the type's complexity by 50% unless I >> can think of a clean way to do it. >> >> Thanks >> -J