Comparing version 0.8.8 to 0.8.9
@@ -135,2 +135,16 @@ /** | ||
/** | ||
* Tells if an object is a primitive type or a "real" object | ||
* Arrays are considered primitive | ||
*/ | ||
function isPrimitiveType (obj) { | ||
return ( typeof obj === 'boolean' || | ||
typeof obj === 'number' || | ||
typeof obj === 'string' || | ||
obj === null || | ||
util.isDate(obj) || | ||
util.isArray(obj)); | ||
} | ||
/** | ||
* Utility functions for comparing things | ||
@@ -311,2 +325,19 @@ * Assumes type checking was already done (a and b already have the same type) | ||
/** | ||
* Removes all instances of a value from an existing array | ||
*/ | ||
lastStepModifierFunctions.$pull = function (obj, field, value) { | ||
var arr, i; | ||
if (!util.isArray(obj[field])) { throw "Can't $pull an element from non-array values"; } | ||
arr = obj[field]; | ||
for (i = arr.length - 1; i >= 0; i -= 1) { | ||
if (match(arr[i], value)) { | ||
arr.splice(i, 1); | ||
} | ||
} | ||
}; | ||
/** | ||
* Increment a numeric field's value | ||
@@ -588,3 +619,3 @@ */ | ||
/** | ||
* Tell if a given document matches a query | ||
* Tell if a given document matches a query | ||
* @param {Object} obj Document to check | ||
@@ -594,8 +625,23 @@ * @param {Object} query | ||
function match (obj, query) { | ||
var queryKeys = Object.keys(query) | ||
, i | ||
; | ||
var queryKeys, queryKey, queryValue, i; | ||
// Primitive query against a primitive type | ||
// This is a bit of a hack since we construct an object with an arbitrary key only to dereference it later | ||
// But I don't have time for a cleaner implementation now | ||
if (isPrimitiveType(obj) || isPrimitiveType(query)) { | ||
return matchQueryPart({ needAKey: obj }, 'needAKey', query); | ||
} | ||
// Normal query | ||
queryKeys = Object.keys(query); | ||
for (i = 0; i < queryKeys.length; i += 1) { | ||
if (!matchQueryPart(obj, queryKeys[i], query[queryKeys[i]])) { return false; } | ||
queryKey = queryKeys[i]; | ||
queryValue = query[queryKey]; | ||
if (queryKey[0] === '$') { | ||
if (!logicalOperators[queryKey]) { throw "Unknown logical operator " + queryKey; } | ||
if (!logicalOperators[queryKey](obj, queryValue)) { return false; } | ||
} else { | ||
if (!matchQueryPart(obj, queryKey, queryValue)) { return false; } | ||
} | ||
} | ||
@@ -612,13 +658,4 @@ | ||
var objValue = getDotValue(obj, queryKey) | ||
, i | ||
, keys, firstChars, dollarFirstChars | ||
; | ||
// Query part begins with a logical operator: apply it | ||
if (queryKey[0] === '$') { | ||
if (!logicalOperators[queryKey]) { throw "Unknown logical operator " + queryKey; } | ||
return logicalOperators[queryKey](obj, queryValue); | ||
} | ||
, i, keys, firstChars, dollarFirstChars; | ||
// Check if the object value is an array treat it as an array of { obj, query } | ||
@@ -671,2 +708,3 @@ // Where there needs to be at least one match | ||
module.exports.checkObject = checkObject; | ||
module.exports.isPrimitiveType = isPrimitiveType; | ||
module.exports.modify = modify; | ||
@@ -673,0 +711,0 @@ module.exports.getDotValue = getDotValue; |
{ | ||
"name": "nedb", | ||
"version": "0.8.8", | ||
"version": "0.8.9", | ||
"author": { | ||
@@ -5,0 +5,0 @@ "name": "tldr.io", |
@@ -267,3 +267,3 @@ # NeDB (Node embedded database) | ||
* A new document will replace the matched docs | ||
* The modifiers create the fields they need to modify if they don't exist, and you can apply them to subdocs. Available field modifiers are `$set` to change a field's value, `$unset` to delete a field and `$inc` to increment a field's value. To work on arrays, you have `$push`, `$pop`, `$addToSet`, and the special `$each`. See examples below for the syntax. | ||
* The modifiers create the fields they need to modify if they don't exist, and you can apply them to subdocs. Available field modifiers are `$set` to change a field's value, `$unset` to delete a field and `$inc` to increment a field's value. To work on arrays, you have `$push`, `$pop`, `$addToSet`, `$pull`, and the special `$each`. See examples below for the syntax. | ||
* `options` is an object with two possible parameters | ||
@@ -354,2 +354,12 @@ * `multi` (defaults to `false`) which allows the modification of several documents if set to true | ||
// $pull removes all values matching a value or even any NeDB query from the array | ||
db.update({ _id: 'id6' }, { $pull: { fruits: 'apple' } }, {}, function () { | ||
// Now the fruits array is ['orange', 'pear'] | ||
}); | ||
db.update({ _id: 'id6' }, { $pull: { fruits: $in: ['apple', 'pear'] } }, {}, function () { | ||
// Now the fruits array is ['orange'] | ||
}); | ||
// $each can be used to $push or $addToSet multiple values at once | ||
@@ -356,0 +366,0 @@ // This example works the same way with $addToSet |
@@ -168,3 +168,18 @@ var model = require('../lib/model') | ||
}); | ||
it('Can check if an object is a primitive or not', function () { | ||
model.isPrimitiveType(5).should.equal(true); | ||
model.isPrimitiveType('sdsfdfs').should.equal(true); | ||
model.isPrimitiveType(0).should.equal(true); | ||
model.isPrimitiveType(true).should.equal(true); | ||
model.isPrimitiveType(false).should.equal(true); | ||
model.isPrimitiveType(new Date()).should.equal(true); | ||
model.isPrimitiveType([]).should.equal(true); | ||
model.isPrimitiveType([3, 'try']).should.equal(true); | ||
model.isPrimitiveType(null).should.equal(true); | ||
model.isPrimitiveType({}).should.equal(false); | ||
model.isPrimitiveType({ a: 42 }).should.equal(false); | ||
}); | ||
}); // ==== End of 'Object checking' ==== // | ||
@@ -204,4 +219,4 @@ | ||
}); // ==== End of 'Deep copying' ==== // | ||
describe('Modifying documents', function () { | ||
@@ -301,12 +316,12 @@ | ||
}); // End of '$set modifier' | ||
describe('$unset modifier', function () { | ||
it('Can delete a field, not throwing an error if the field doesnt exist', function () { | ||
var obj, updateQuery, modified; | ||
describe('$unset modifier', function () { | ||
it('Can delete a field, not throwing an error if the field doesnt exist', function () { | ||
var obj, updateQuery, modified; | ||
obj = { yup: 'yes', other: 'also' } | ||
updateQuery = { $unset: { yup: true } } | ||
modified = model.modify(obj, updateQuery); | ||
assert.deepEqual(modified, { other: 'also' }); | ||
assert.deepEqual(modified, { other: 'also' }); | ||
@@ -316,30 +331,30 @@ obj = { yup: 'yes', other: 'also' } | ||
modified = model.modify(obj, updateQuery); | ||
assert.deepEqual(modified, obj); | ||
assert.deepEqual(modified, obj); | ||
obj = { yup: 'yes', other: 'also' } | ||
updateQuery = { $unset: { nope: true, other: true } } | ||
modified = model.modify(obj, updateQuery); | ||
assert.deepEqual(modified, { yup: 'yes' }); | ||
}); | ||
assert.deepEqual(modified, { yup: 'yes' }); | ||
}); | ||
it('Can unset sub-fields and entire nested documents', function () { | ||
var obj, updateQuery, modified; | ||
var obj, updateQuery, modified; | ||
obj = { yup: 'yes', nested: { a: 'also', b: 'yeah' } } | ||
updateQuery = { $unset: { nested: true } } | ||
modified = model.modify(obj, updateQuery); | ||
assert.deepEqual(modified, { yup: 'yes' }); | ||
assert.deepEqual(modified, { yup: 'yes' }); | ||
obj = { yup: 'yes', nested: { a: 'also', b: 'yeah' } } | ||
updateQuery = { $unset: { 'nested.a': true } } | ||
modified = model.modify(obj, updateQuery); | ||
assert.deepEqual(modified, { yup: 'yes', nested: { b: 'yeah' } }); | ||
assert.deepEqual(modified, { yup: 'yes', nested: { b: 'yeah' } }); | ||
obj = { yup: 'yes', nested: { a: 'also', b: 'yeah' } } | ||
updateQuery = { $unset: { 'nested.a': true, 'nested.b': true } } | ||
modified = model.modify(obj, updateQuery); | ||
assert.deepEqual(modified, { yup: 'yes', nested: {} }); | ||
assert.deepEqual(modified, { yup: 'yes', nested: {} }); | ||
}); | ||
}); // End of '$unset modifier' | ||
}); // End of '$unset modifier' | ||
@@ -548,2 +563,60 @@ describe('$inc modifier', function () { | ||
describe('$pull modifier', function () { | ||
it('Can remove an element from a set', function () { | ||
var obj = { arr: ['hello', 'world'] } | ||
, modified; | ||
modified = model.modify(obj, { $pull: { arr: 'world' } }); | ||
assert.deepEqual(modified, { arr: ['hello'] }); | ||
obj = { arr: ['hello'] }; | ||
modified = model.modify(obj, { $pull: { arr: 'world' } }); | ||
assert.deepEqual(modified, { arr: ['hello'] }); | ||
}); | ||
it('Can remove multiple matching elements', function () { | ||
var obj = { arr: ['hello', 'world', 'hello', 'world'] } | ||
, modified; | ||
modified = model.modify(obj, { $pull: { arr: 'world' } }); | ||
assert.deepEqual(modified, { arr: ['hello', 'hello'] }); | ||
}); | ||
it('Throw if we try to pull from a non-array', function () { | ||
var obj = { arr: 'hello' } | ||
, modified; | ||
(function () { | ||
modified = model.modify(obj, { $pull: { arr: 'world' } }); | ||
}).should.throw(); | ||
}); | ||
it('Use deep-equality to check whether we can remove a value from a set', function () { | ||
var obj = { arr: [{ b: 2 }, { b: 3 }] } | ||
, modified; | ||
modified = model.modify(obj, { $pull: { arr: { b: 3 } } }); | ||
assert.deepEqual(modified, { arr: [ { b: 2 } ] }); | ||
obj = { arr: [ { b: 2 } ] } | ||
modified = model.modify(obj, { $pull: { arr: { b: 3 } } }); | ||
assert.deepEqual(modified, { arr: [{ b: 2 }] }); | ||
}); | ||
it('Can use any kind of nedb query with $pull', function () { | ||
var obj = { arr: [4, 7, 12, 2], other: 'yup' } | ||
, modified | ||
; | ||
modified = model.modify(obj, { $pull: { arr: { $gte: 5 } } }); | ||
assert.deepEqual(modified, { arr: [4, 2], other: 'yup' }); | ||
obj = { arr: [{ b: 4 }, { b: 7 }, { b: 1 }], other: 'yeah' }; | ||
modified = model.modify(obj, { $pull: { arr: { b: { $gte: 5} } } }); | ||
assert.deepEqual(modified, { arr: [{ b: 4 }, { b: 1 }], other: 'yeah' }); | ||
}); | ||
}); // End of '$pull modifier' | ||
}); // ==== End of 'Modifying documents' ==== // | ||
@@ -550,0 +623,0 @@ |
594575
14312
508