Comparing version 0.3.5 to 0.3.6
@@ -69,4 +69,4 @@ /** | ||
if (typeof v === undefined) { return null; } | ||
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) { return v; } | ||
//if (v && v.constructor && v.constructor.name === 'Date') { console.log("==============="); return { $$date: v.toString() }; } | ||
@@ -235,21 +235,68 @@ return v; | ||
/** | ||
* Test for field equality | ||
* @param {Object} obj The model to check | ||
* @param {String} field Can contain dots, in that case that means we will set a subfield recursively | ||
* @param {Model} value | ||
* Check whether 'things' are equal | ||
* Things are defined as any native types (string, number, boolean, null, date) and objects | ||
* In the case of object, we check deep equality | ||
* Returns true if they are, false otherwise | ||
*/ | ||
matcherFunctions.$eq = function (obj, field, value) { | ||
function areThingsEqual (a, b) { | ||
var aKeys , bKeys , i; | ||
// Strings, booleans, numbers, null | ||
if (a === null || typeof a === 'string' || typeof a === 'boolean' || typeof a === 'number' || | ||
b === null || typeof b === 'string' || typeof b === 'boolean' || typeof b === 'number') { return a === b; } | ||
// Dates | ||
if (util.isDate(a) || util.isDate(b)) { return util.isDate(a) && util.isDate(b) && a.getTime() === b.getTime(); } | ||
// Arrays (no match since arrays are used as a $in) | ||
// undefined (no match since they mean field doesn't exist and can't be serialized) | ||
if (util.isArray(a) || util.isArray(b) || a === undefined || b === undefined) { return false; } | ||
// Objects (check for deep equality) | ||
// a and b should be objects at this point | ||
try { | ||
aKeys = Object.keys(a); | ||
bKeys = Object.keys(b); | ||
} catch (e) { | ||
return false; | ||
} | ||
if (aKeys.length !== bKeys.length) { return false; } | ||
for (i = 0; i < aKeys.length; i += 1) { | ||
if (bKeys.indexOf(aKeys[i]) === -1) { return false; } | ||
if (!areThingsEqual(a[aKeys[i]], b[aKeys[i]])) { return false; } | ||
} | ||
return true; | ||
} | ||
/** | ||
* Get a value from object with dot notation | ||
* @param {Object} obj | ||
* @param {String} field | ||
*/ | ||
function getDotValue (obj, field) { | ||
var fieldParts = typeof field === 'string' ? field.split('.') : field; | ||
if (!obj) { return false; } // field cannot be empty here so that means there is no match | ||
if (!obj) { return undefined; } // field cannot be empty so that means we should return undefined so that nothing can match | ||
if (fieldParts.length === 1) { | ||
return obj[fieldParts[0]] === value; // TODO: check for real equality, incl. deep object equality | ||
return obj[fieldParts[0]]; | ||
} else { | ||
return matcherFunctions.$eq(obj[fieldParts[0]], fieldParts.slice(1), value); | ||
return getDotValue(obj[fieldParts[0]], fieldParts.slice(1)); | ||
} | ||
} | ||
/** | ||
* Test for field equality | ||
* @param {Object} obj The model to check | ||
* @param {String} field Can contain dots, in that case that means we will set a subfield recursively | ||
* @param {Model} value | ||
*/ | ||
matcherFunctions.$eq = function (obj, field, value) { | ||
return areThingsEqual(getDotValue(obj, field), value); | ||
}; | ||
/** | ||
@@ -276,2 +323,4 @@ * Tell if a given document matches a query | ||
module.exports.modify = modify; | ||
module.exports.getDotValue = getDotValue; | ||
module.exports.match = match; | ||
module.exports.areThingsEqual = areThingsEqual; |
{ | ||
"name": "nedb", | ||
"version": "0.3.5", | ||
"version": "0.3.6", | ||
"author": { | ||
@@ -5,0 +5,0 @@ "name": "tldr.io", |
@@ -42,7 +42,11 @@ # NeDB (Node embedded database) | ||
### Inserting documents | ||
The native types are String, Number, Boolean and Date. You can also use | ||
arrays and subdocuments (objects). If you specify an `_id` field, it | ||
will be used as the document's _id, otherwise nedb will generate one. | ||
Note that the generated `_id` is a simple string, not an ObjectId. Field names cannot begin by '$' or contain a '.'. | ||
The native types are `String`, `Number`, `Boolean`, `Date` and `null`. You can also use | ||
arrays and subdocuments (objects). If a field is `undefined`, it will not be saved (this is different from | ||
MongoDB which transforms `undefined` in `null`, something I find counter-intuitive). | ||
If you specify an `_id` field, it will be used as the document's id, otherwise nedb will generate one randomly. | ||
Note that the generated `_id` is a simple string, not an `ObjectId`. | ||
Field names cannot begin by '$' or contain a '.'. | ||
```javascript | ||
@@ -53,2 +57,4 @@ var document = { hello: 'world' | ||
, nedbIsAwesome: true | ||
, notthere: null | ||
, notToBeSaved: undefined // Will not be saved | ||
, fruits: [ 'apple', 'orange', 'pear' ] | ||
@@ -60,2 +66,3 @@ , infos: { name: 'nedb' } | ||
// newDoc is the newly inserted document, including its _id | ||
// newDoc has no key called notToBeSaved since its value was undefined | ||
}); | ||
@@ -70,3 +77,3 @@ ``` | ||
// { _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false } | ||
// { _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true, humans: { genders: 2 } } | ||
// { _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true, humans: { genders: 2, eyes: true } } | ||
// { _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false } | ||
@@ -91,2 +98,7 @@ // { _id: 'id4', planet: 'Omicron Persia 8', system: 'futurama', inhabited: true } | ||
// You can also deep-compare objects. Don't confuse this with dot-notation! | ||
db.find({ humans: { genders: 2 } }, function (err, docs) { | ||
// docs is empty, because { genders: 2 } is not equal to { genders: 2, eyes: true } | ||
}); | ||
db.find({}, function (err, docs) { | ||
@@ -93,0 +105,0 @@ // docs contains all documents in the collection |
@@ -158,3 +158,3 @@ var Datastore = require('../lib/datastore') | ||
it('Can find all documents an empty query is used', function (done) { | ||
it('Can find all documents if an empty query is used', function (done) { | ||
async.waterfall([ | ||
@@ -237,8 +237,49 @@ function (cb) { | ||
it('Can find dates and objects (non JS-native types)', function (done) { | ||
var date1 = new Date(1234543) | ||
, date2 = new Date(9999) | ||
; | ||
d.insert({ now: date1, sth: { name: 'nedb' } }, function () { | ||
d.findOne({ now: date1 }, function (err, doc) { | ||
assert.isNull(err); | ||
doc.sth.name.should.equal('nedb'); | ||
d.findOne({ now: date2 }, function (err, doc) { | ||
assert.isNull(err); | ||
assert.isNull(doc); | ||
d.findOne({ sth: { name: 'nedb' } }, function (err, doc) { | ||
assert.isNull(err); | ||
doc.sth.name.should.equal('nedb'); | ||
d.findOne({ sth: { name: 'other' } }, function (err, doc) { | ||
assert.isNull(err); | ||
assert.isNull(doc); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('Can use dot-notation to query subfields', function (done) { | ||
d.insert({ greeting: { english: 'hello' } }, function () { | ||
d.findOne({ "greeting.english": 'hello' }, function (err, doc) { | ||
assert.isNull(err); | ||
doc.greeting.english.should.equal('hello'); | ||
done(); | ||
d.findOne({ "greeting.english": 'hellooo' }, function (err, doc) { | ||
assert.isNull(err); | ||
assert.isNull(doc); | ||
d.findOne({ "greeting.englis": 'hello' }, function (err, doc) { | ||
assert.isNull(err); | ||
assert.isNull(doc); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -245,0 +286,0 @@ }); |
@@ -13,3 +13,3 @@ var model = require('../lib/model') | ||
it('Can serialize and deserialize strings', function (done) { | ||
it('Can serialize and deserialize strings', function () { | ||
var a, b, c; | ||
@@ -32,7 +32,5 @@ | ||
c.test.indexOf('\n').should.not.equal(-1); | ||
done(); | ||
}); | ||
it('Can serialize and deserialize booleans', function (done) { | ||
it('Can serialize and deserialize booleans', function () { | ||
var a, b, c; | ||
@@ -45,7 +43,5 @@ | ||
c.test.should.equal(true); | ||
done(); | ||
}); | ||
it('Can serialize and deserialize numbers', function (done) { | ||
it('Can serialize and deserialize numbers', function () { | ||
var a, b, c; | ||
@@ -58,7 +54,5 @@ | ||
c.test.should.equal(5); | ||
done(); | ||
}); | ||
it('Can serialize and deserialize null', function (done) { | ||
it('Can serialize and deserialize null', function () { | ||
var a, b, c; | ||
@@ -71,7 +65,16 @@ | ||
assert.isNull(a.test); | ||
}); | ||
done(); | ||
it('undefined fields are removed when serialized', function() { | ||
var a = { bloup: undefined, hello: 'world' } | ||
, b = model.serialize(a) | ||
, c = model.deserialize(b) | ||
; | ||
Object.keys(c).length.should.equal(1); | ||
c.hello.should.equal('world'); | ||
assert.isUndefined(c.bloup); | ||
}); | ||
it('Can serialize and deserialize a date', function (done) { | ||
it('Can serialize and deserialize a date', function () { | ||
var a, b, c | ||
@@ -86,7 +89,5 @@ , d = new Date(); | ||
c.test.getTime().should.equal(d.getTime()); | ||
done(); | ||
}); | ||
it('Can serialize and deserialize sub objects', function (done) { | ||
it('Can serialize and deserialize sub objects', function () { | ||
var a, b, c | ||
@@ -102,7 +103,5 @@ , d = new Date(); | ||
c.test.yes.again.should.equal('yes'); | ||
done(); | ||
}); | ||
it('Can serialize and deserialize sub arrays', function (done) { | ||
it('Can serialize and deserialize sub arrays', function () { | ||
var a, b, c | ||
@@ -118,4 +117,2 @@ , d = new Date(); | ||
c.test[2].again.should.equal('yes'); | ||
done(); | ||
}); | ||
@@ -341,2 +338,67 @@ | ||
describe('Comparing things', function () { | ||
it('Two things of different types cannot be equal, two identical native things are equal', function () { | ||
var toTest = [null, 'somestring', 42, true, new Date(72998322), { hello: 'world' }] | ||
, toTestAgainst = [null, 'somestring', 42, true, new Date(72998322), { hello: 'world' }] // Use another array so that we don't test pointer equality | ||
, i, j | ||
; | ||
for (i = 0; i < toTest.length; i += 1) { | ||
for (j = 0; j < toTestAgainst.length; j += 1) { | ||
model.areThingsEqual(toTest[i], toTestAgainst[j]).should.equal(i === j); | ||
} | ||
} | ||
}); | ||
it('Can test native types null undefined string number boolean date equality', function () { | ||
var toTest = [null, undefined, 'somestring', 42, true, new Date(72998322), { hello: 'world' }] | ||
, toTestAgainst = [undefined, null, 'someotherstring', 5, false, new Date(111111), { hello: 'mars' }] | ||
, i | ||
; | ||
for (i = 0; i < toTest.length; i += 1) { | ||
model.areThingsEqual(toTest[i], toTestAgainst[i]).should.equal(false); | ||
} | ||
}); | ||
it('If one side is an array or undefined, comparison fails', function () { | ||
var toTestAgainst = [null, undefined, 'somestring', 42, true, new Date(72998322), { hello: 'world' }] | ||
, i | ||
; | ||
for (i = 0; i < toTestAgainst.length; i += 1) { | ||
model.areThingsEqual([1, 2, 3], toTestAgainst[i]).should.equal(false); | ||
model.areThingsEqual(toTestAgainst[i], []).should.equal(false); | ||
model.areThingsEqual(undefined, toTestAgainst[i]).should.equal(false); | ||
model.areThingsEqual(toTestAgainst[i], undefined).should.equal(false); | ||
} | ||
}); | ||
it('Can test objects equality', function () { | ||
model.areThingsEqual({ hello: 'world' }, {}).should.equal(false); | ||
model.areThingsEqual({ hello: 'world' }, { hello: 'mars' }).should.equal(false); | ||
model.areThingsEqual({ hello: 'world' }, { hello: 'world', temperature: 42 }).should.equal(false); | ||
model.areThingsEqual({ hello: 'world', other: { temperature: 42 }}, { hello: 'world', other: { temperature: 42 }}).should.equal(true); | ||
}); | ||
}); | ||
describe('Getting a fields value in dot notation', function () { | ||
it('Return first-level and nested values', function () { | ||
model.getDotValue({ hello: 'world' }, 'hello').should.equal('world'); | ||
model.getDotValue({ hello: 'world', type: { planet: true, blue: true } }, 'type.planet').should.equal(true); | ||
}); | ||
it('Return undefined if the field cannot be found in the object', function () { | ||
assert.isUndefined(model.getDotValue({ hello: 'world' }, 'helloo')); | ||
assert.isUndefined(model.getDotValue({ hello: 'world', type: { planet: true } }, 'type.plane')); | ||
}); | ||
}); | ||
describe('$eq', function () { | ||
@@ -357,5 +419,5 @@ | ||
it('Can find undefined in first level or dot notation', function () { | ||
model.match({ test: undefined }, { test: undefined }).should.equal(true); | ||
model.match({ test: { pp: undefined } }, { "test.pp": undefined }).should.equal(true); | ||
it('Cannot find undefined', function () { | ||
model.match({ test: undefined }, { test: undefined }).should.equal(false); | ||
model.match({ test: { pp: undefined } }, { "test.pp": undefined }).should.equal(false); | ||
}); | ||
@@ -362,0 +424,0 @@ |
77855
1745
252