Skip to content
josephg edited this page May 9, 2011 · 18 revisions

OT for JSON Draft Spec

NOTE: This is a draft spec. It is likely to change before OT for JSON objects is implemented.

OT for JSON allows multiple users to collaborate on arbitrary documents described by JSON. This document specifies what operations are valid and how those operations behave when multiple users are editing at the same time.

The document is stored by a plain JSON object. When the document is first created, the document is null.

Operations

Like text operations, JSON operations are lists of components. The operation is a grouping of these components, applied in order.

Each operation component is an object with a p:PATH component. The path is a list of keys to reach the target element in the document. For example, given the following document:

{'a':[100, 200, 300], 'b': 'hi'}

An operation to delete the first array element (100) would be the following:

[{p:['a', 0], ld:100}]

The path (['a', 0]) describes how to reach the target element from the root. The first element is a key in the containing object and the second is an index into the array.


Number operations

The only operation you can perform on a number is to add to it. Remember, you can always replace the number with another number by operating on the number's container.

Are there any other ways the format should support modifying numbers? Ideas:

  • Linear multiple as well (Ie, x = Bx + C)
  • MAX, MIN, etc? That would let you do timestamps...

I can't think of any good use cases for those operations...

Add

Usage:

{p:PATH, na:X}

Adds X to the number at PATH. If you want to subtract, add a negative number.


String operations

The semantics of string operations is identical to the semantics of plain text operations. See Text Operations for specifics of how strings function under transformation.

For string operations, the path is augmented with an offset in the string. Eg, given the following object:

{'key':[100,'abcde']}

If you wanted to delete the 'd' from the string 'abcde', you would use the following operation:

{p:['key',1,3],sd:'d'}

Note the path. The components, in order, are the key to the list, the index to the 'abcde' string, and then the offset to the 'd' character in the string.

Insert into a string

Usage:

{p:PATH, si:TEXT}

Insert TEXT at the location specified by PATH. The path must specify an offset in a string.

Delete from a string

Usage:

{p:PATH, sd:TEXT}

Delete TEXT at the location specified by PATH. The path must specify an offset in a string. TEXT must be contained at the location specified.


Lists and Objects

Lists and objects have the same set of operations (Insert, Delete, Replace, Move) but their semantics are different. To avoid ambiguity inside the transform function, list operations and object operations are named differently. (li, ld, lm for lists and oi, od and om for objects).

Inserting, Deleting and Replacing in a list

Usage:

Insert:  {p:PATH, li:NEWVALUE}
Delete:  {p:PATH, ld:OLDVALUE}
Replace: {p:PATH, ld:OLDVALUE, li:NEWVALUE}

Inserts, deletes, or replaces the element at PATH.

The last element in the path specifies an index in the list where elements will be deleted, inserted or replaced. The index must be valid (0 >= new index >= list length). The indexes of existing list elements may change when new list elements are added or removed.

The replace operation:

{p:PATH, ld:OLDVALUE, li:NEWVALUE}

is equivalent to a delete followed by an insert:

{p:PATH, ld:OLDVALUE}
{p:PATH, li:NEWVALUE}

Given the following list:

[100, 300, 400]

applying the following operation:

[{p:[1], li:{'yo':'hi there'}}, {p:[3], ld:400}]

would result in the following new list:

[100, {'yo':'hi there'}, 300]

Inserting, Deleting and Replacing in an object

Usage:

Insert:  {p:PATH, oi:NEWVALUE}
Delete:  {p:PATH, od:OLDVALUE}
Replace: {p:PATH, od:OLDVALUE, oi:NEWVALUE}

Set the element indicated by PATH from OLDVALUE to NEWVALUE. The last element of the path must be the key of the element to be inserted, deleted or replaced.

When inserting, the key must not already be used. When deleting or replacing a value, OLDVALUE must be equal to the current value the object has at the specified key.

As with lists, the replace operation:

{p:PATH, od:OLDVALUE, oi:NEWVALUE}

is equivalent to a delete followed by an insert:

{p:PATH, od:OLDVALUE}
{p:PATH, oi:NEWVALUE}

Moving list elements

Usage:

{p:PATH, lm:NEWINDEX}

Moves the list element specified by PATH to a different place in the list, with index NEWINDEX. Any elements between the old index and the new index will get new indicies, as appropriate.

The new index must be 0 <= index < list length. The new index will be interpreted after the element has been removed from its current position. Given the following data:

['a', 'b', 'c']

the following operation:

[{p:[1], lm:2}]

will result in the following data:

['a', 'c', 'b']

Moving object elements / Renaming keys

Usage:

{p:PATH, om:NEWKEY}

Changes the key of the element specified by PATH to NEWKEY. There must not currently be an element in the object with a key of NEWKEY.

NOTE: These move operations are currently pretty limited. They allows you to move data around inside a list or inside an object, but not to move data between separate objects or lists. I could definitely support that, but I can't think of any use cases where that would actually be useful.

You can always move by deleting the element & inserting it back elsewhere, but if you do that, any operations on the deleted element will be lost.