Skip to content
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

Added position option to array push & pop #603

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions lib/datastore.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ Datastore.prototype.getCandidates = function (query, dontExpireStaleDocs, callba
dontExpireStaleDocs = false;
}


async.waterfall([
// STEP 1: get candidates list by checking indexes from most to least frequent usecase
function (cb) {
Expand Down Expand Up @@ -560,15 +559,14 @@ Datastore.prototype._update = function (query, updateQuery, options, cb) {
var callback
, self = this
, numReplaced = 0
, multi, upsert
, multi, upsert, posnum
, i
;

if (typeof options === 'function') { cb = options; options = {}; }
callback = cb || function () {};
multi = options.multi !== undefined ? options.multi : false;
upsert = options.upsert !== undefined ? options.upsert : false;

posnum = options.position !== undefined ? options.position : false;
async.waterfall([
function (cb) { // If upsert option is set, check whether we need to insert the doc
if (!upsert) { return cb(); }
Expand All @@ -586,11 +584,13 @@ Datastore.prototype._update = function (query, updateQuery, options, cb) {
model.checkObject(updateQuery);
// updateQuery is a simple object with no modifier, use it as the document to insert
toBeInserted = updateQuery;

} catch (e) {
// updateQuery contains modifiers, use the find query as the base,
// strip it from all operators and update it according to updateQuery
try {
toBeInserted = model.modify(model.deepCopy(query, true), updateQuery);
toBeInserted = model.modify(model.deepCopy(query, true), updateQuery, posnum);

} catch (err) {
return callback(err);
}
Expand All @@ -616,7 +616,7 @@ Datastore.prototype._update = function (query, updateQuery, options, cb) {
if (model.match(candidates[i], query) && (multi || numReplaced === 0)) {
numReplaced += 1;
if (self.timestampData) { createdAt = candidates[i].createdAt; }
modifiedDoc = model.modify(candidates[i], updateQuery);
modifiedDoc = model.modify(candidates[i], updateQuery, posnum);
if (self.timestampData) {
modifiedDoc.createdAt = createdAt;
modifiedDoc.updatedAt = new Date();
Expand Down Expand Up @@ -653,7 +653,7 @@ Datastore.prototype._update = function (query, updateQuery, options, cb) {
};

Datastore.prototype.update = function () {
this.executor.push({ this: this, fn: this._update, arguments: arguments });
this.executor.push({ this: this, fn: this._update, arguments: arguments});
};


Expand Down
43 changes: 26 additions & 17 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var util = require('util')
;


/**
/*
* Check a key, throw an error if the key is non valid
* @param {String} k key
* @param {Model} v value, needed to treat the Date edge case
Expand Down Expand Up @@ -268,16 +268,18 @@ lastStepModifierFunctions.$unset = function (obj, field, value) {
* Optional modifier $slice to slice the resulting array, see https://docs.mongodb.org/manual/reference/operator/update/slice/
* Différeence with MongoDB: if $slice is specified and not $each, we act as if value is an empty array
*/
lastStepModifierFunctions.$push = function (obj, field, value) {
lastStepModifierFunctions.$push = function (obj, field, value, posnum) {
// Create the array if it doesn't exist
if (!obj.hasOwnProperty(field)) { obj[field] = []; }

if (!util.isArray(obj[field])) { throw new Error("Can't $push an element on non-array values"); }

if (value !== null && typeof value === 'object' && value.$slice && value.$each === undefined) {
if (value !== null && typeof value === 'object' && value.$slice && value.$each && value.position === undefined) {
value.$each = [];
}



if (value !== null && typeof value === 'object' && value.$each) {
if (Object.keys(value).length >= 3 || (Object.keys(value).length === 2 && value.$slice === undefined)) { throw new Error("Can only use $slice in cunjunction with $each when $push to array"); }
if (!util.isArray(value.$each)) { throw new Error("$each requires an array value"); }
Expand All @@ -302,11 +304,14 @@ lastStepModifierFunctions.$push = function (obj, field, value) {
obj[field] = obj[field].slice(start, end);
}
} else {
obj[field].push(value);
}
};

if (posnum === undefined){
obj[field].push(value);

}
if (posnum !== undefined){
obj[field].splice(posnum, 0, value);
}}
};
/**
* Add an element to an array field only if it is not already in it
* No modification if the element is already in the array
Expand Down Expand Up @@ -339,16 +344,18 @@ lastStepModifierFunctions.$addToSet = function (obj, field, value) {
/**
* Remove the first or last element of an array
*/
lastStepModifierFunctions.$pop = function (obj, field, value) {
lastStepModifierFunctions.$pop = function (obj, field, value, posnum) {
if (!util.isArray(obj[field])) { throw new Error("Can't $pop an element from non-array values"); }
if (typeof value !== 'number') { throw new Error(value + " isn't an integer, can't use it with $pop"); }
if (value === 0) { return; }

if (posnum !== undefined) {
obj[field] = obj[field].splice(posnum,1);
} else {
if (value > 0) {
obj[field] = obj[field].slice(0, obj[field].length - 1);
} else {
obj[field] = obj[field].slice(1);
}
}}
};


Expand Down Expand Up @@ -401,7 +408,7 @@ lastStepModifierFunctions.$max = function (obj, field, value) {
* Updates the value of the field, only if specified field is smaller than the current value of the field
*/
lastStepModifierFunctions.$min = function (obj, field, value) {
if (typeof obj[field] === 'undefined') { 
if (typeof obj[field] === 'undefined') {
obj[field] = value;
} else if (value < obj[field]) {
obj[field] = value;
Expand All @@ -410,22 +417,23 @@ lastStepModifierFunctions.$min = function (obj, field, value) {

// Given its name, create the complete modifier function
function createModifierFunction (modifier) {
return function (obj, field, value) {
return function (obj, field, value, posnum) {
var fieldParts = typeof field === 'string' ? field.split('.') : field;

if (fieldParts.length === 1) {
lastStepModifierFunctions[modifier](obj, field, value);
lastStepModifierFunctions[modifier](obj, field, value, posnum);
} else {
if (obj[fieldParts[0]] === undefined) {
if (modifier === '$unset') { return; } // Bad looking specific fix, needs to be generalized modifiers that behave like $unset are implemented
obj[fieldParts[0]] = {};
}
modifierFunctions[modifier](obj[fieldParts[0]], fieldParts.slice(1), value);
modifierFunctions[modifier](obj[fieldParts[0]], fieldParts.slice(1), value, posnum);
}
};
}

// Actually create all modifier functions

Object.keys(lastStepModifierFunctions).forEach(function (modifier) {
modifierFunctions[modifier] = createModifierFunction(modifier);
});
Expand All @@ -434,7 +442,8 @@ Object.keys(lastStepModifierFunctions).forEach(function (modifier) {
/**
* Modify a DB object according to an update query
*/
function modify (obj, updateQuery) {
function modify (obj, updateQuery, posnum) {

var keys = Object.keys(updateQuery)
, firstChars = _.map(keys, function (item) { return item[0]; })
, dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; })
Expand All @@ -458,7 +467,7 @@ function modify (obj, updateQuery) {
modifiers.forEach(function (m) {
var keys;

if (!modifierFunctions[m]) { throw new Error("Unknown modifier " + m); }
if (!modifierFunctions[m]) { throw new Error("Unknown modifier hehe " + m); }

// Can't rely on Object.keys throwing on non objects since ES6
// Not 100% satisfying as non objects can be interpreted as objects but no false negatives so we can live with it
Expand All @@ -468,7 +477,7 @@ function modify (obj, updateQuery) {

keys = Object.keys(updateQuery[m]);
keys.forEach(function (k) {
modifierFunctions[m](newDoc, k, updateQuery[m][k]);
modifierFunctions[m](newDoc, k, updateQuery[m][k], posnum);
});
});
}
Expand Down