-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add skipPoll support for json1 ops, instead of throwing errors for them #137
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1044,10 +1044,44 @@ function getInnerFields(params, fields) { | |
function opContainsAnyField(op, fields) { | ||
for (var i = 0; i < op.length; i++) { | ||
var component = op[i]; | ||
if (component.p.length === 0) { | ||
return true; | ||
} else if (fields[component.p[0]]) { | ||
if (Array.isArray(component.p)) { | ||
// json0 op component: | ||
// | ||
// Each op component has its own array of `p` path segments from doc root. | ||
if (component.p.length === 0) { | ||
return true; | ||
} else if (fields[component.p[0]]) { | ||
return true; | ||
} | ||
} else if (typeof component === 'string') { | ||
// json1 field descent from root: | ||
// | ||
// First string in top-level array means all subsequent operations will be | ||
// underneath that top-level field, no need to continue iterating. | ||
return fields[component]; | ||
} else if (hasOwnProperty(component, 'p') || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general, I wonder if we're trying to do too much with heuristics here? It's really hard to determine if an op is a given type, and we may do a bunch of work we don't need to if a certain collection never submits JSON ops (this is the mainline case for our organisation!). I wonder if this belongs as some sort of helper function plugin exported by this library, which consumers need to enable using ShareDB's So given a helper function const backend = new Backend({
skipPoll: (collection, id, op, query) => {
// Let's assume this is easy to determine for a consumer. Each
// collection probably has a set type that never changes,
// especially if we're querying!
if (!isJson1Collection(collection)) return false;
return shouldSkipJson1Poll(op, query);
},
}); The advantages of this:
Disadvantages:
|
||
hasOwnProperty(component, 'r') || | ||
hasOwnProperty(component, 'd') || | ||
hasOwnProperty(component, 'i') || | ||
hasOwnProperty(component, 'e')) { | ||
// json1 root-level operation: | ||
// | ||
// If we encounter an operation at top level prior to encountering a string, | ||
// (field descent) then the operation affects the entire document. | ||
return true; | ||
} else if (Array.isArray(component)) { | ||
// json1 child operation list: | ||
// | ||
// In a canonical json1 op, if we encounter a child op prior to encountering | ||
// a string (field descent), then the child should start with a field descent. | ||
// If that weren't the case, the op would be pulled up to the top-level array. | ||
var descendant = component; | ||
while (typeof descendant[0] !== 'string') { | ||
descendant = descendant[0]; | ||
} | ||
if (fields[descendant[0]]) { | ||
return true; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add an |
||
} | ||
} | ||
return false; | ||
|
@@ -1457,6 +1491,10 @@ function isPlainObject(value) { | |
); | ||
} | ||
|
||
function hasOwnProperty(obj, prop) { | ||
return Object.prototype.hasOwnProperty.call(obj, prop); | ||
} | ||
|
||
// Convert a simple map of fields that we want into a mongo projection. This | ||
// depends on the data being stored at the top level of the document. It will | ||
// only work properly for json documents--which are the only types for which | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
var expect = require('chai').expect; | ||
var json1 = require('ot-json1'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a test for |
||
var ShareDbMongo = require('../index'); | ||
|
||
describe('skipPoll', function() { | ||
|
@@ -111,6 +112,51 @@ describe('skipPoll', function() { | |
assertNotSkips({op: [{p: ['a'], dummyOp: 1}, {p: [], dummyOp: 1}]}, query); | ||
assertNotSkips({op: [{p: [], dummyOp: 1}, {p: ['x'], dummyOp: 1}]}, query); | ||
}); | ||
|
||
it('json1 root-level changes', function() { | ||
// Root-level doc changes should always cause a query poll. | ||
assertNotSkips({op: json1.insertOp('', {brandNew: 'value'})}, query); | ||
assertNotSkips({op: json1.removeOp('')}, query); | ||
}); | ||
|
||
it('json1 ops not affecting queried fields', function() { | ||
assertSkips({op: json1.insertOp(['notQueried'], 'hello')}, query); | ||
assertSkips({op: json1.moveOp(['notQueried'], ['alsoNotQueried'])}, query); | ||
assertSkips({op: json1.removeOp(['notQueried'], 'hello')}, query); | ||
}); | ||
|
||
it('json1 insert ops', function() { | ||
assertIfSkips({op: json1.insertOp(['a'], 'hello')}, query, !has(fields, 'a')); | ||
assertIfSkips({op: json1.insertOp(['a', 'b'], 'hello')}, query, !has(fields, 'a')); | ||
assertIfSkips({op: json1.insertOp(['a', 0], 'hello')}, query, !has(fields, 'a')); | ||
}); | ||
|
||
it('json1 remove ops', function() { | ||
assertIfSkips({op: json1.removeOp(['a'], 'hello')}, query, !has(fields, 'a')); | ||
assertIfSkips({op: json1.removeOp(['a', 'b'], 'hello')}, query, !has(fields, 'a')); | ||
assertIfSkips({op: json1.removeOp(['a', 0], 'hello')}, query, !has(fields, 'a')); | ||
}); | ||
|
||
it('json1 move ops', function() { | ||
assertIfSkips({op: json1.moveOp(['a'], ['x'])}, query, !has(fields, 'a') && !has(fields, 'x')); | ||
assertIfSkips({op: json1.moveOp(['x'], ['a'])}, query, !has(fields, 'x') && !has(fields, 'a')); | ||
assertIfSkips({op: json1.moveOp(['a'], ['notQueried'])}, query, !has(fields, 'a')); | ||
assertIfSkips({op: json1.moveOp(['notQueried'], ['a'])}, query, !has(fields, 'a')); | ||
assertSkips({op: json1.moveOp(['notQueried'], ['alsoNotQueried'])}, query); | ||
}); | ||
|
||
it('json1 composed ops', function() { | ||
var compositeNotQueried = json1.type.compose( | ||
json1.insertOp(['notQueried1'], 'hello'), | ||
json1.moveOp(['notQueried2'], ['notQueried3']) | ||
); | ||
assertSkips({op: compositeNotQueried}, query); | ||
var compositeQueried = json1.type.compose( | ||
json1.insertOp(['notQueried1'], 'hello'), | ||
json1.moveOp(['notQueried2'], ['a']) | ||
); | ||
assertIfSkips({op: compositeQueried}, query, !has(fields, 'a')); | ||
}); | ||
}); | ||
} | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check would break with eg
text-unicode
with a query set up on the root-level_data
field (the default field if your snapshot isn't an object)