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

FindAndModify + upsert #940

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ed0265e
Allow objects that implement toDate to be cast
connor4312 Dec 19, 2014
852c566
WIP: Added first code for findAndModify.
Globegitter Apr 7, 2015
44588d3
WIP: Added first code for findAndModify.
Globegitter Apr 7, 2015
3bb74a9
Rebased to latest master.
Globegitter Apr 8, 2015
ec1bee4
WIP: Added first code for findAndModify.
Globegitter Apr 7, 2015
25e8d52
Fixed findAndModify. Added unit tests.
Globegitter Apr 8, 2015
202a84f
Fixed unit tests.
Globegitter Apr 8, 2015
c6ab08a
Manual merge.
Globegitter Apr 8, 2015
2afa428
Removed consoe.log
Globegitter Apr 8, 2015
e420e6c
Return error on update. Fixed some criteria checks.
Globegitter Apr 8, 2015
27c6e9e
Merge pull request #764 from MCProHosting/toDate
particlebanana Apr 8, 2015
32d4126
Get first working implementation of mergeArrays.
Globegitter Apr 9, 2015
def9b0f
Slightly optimised and simplified mergeArrays implementation.
Globegitter Apr 9, 2015
bf7f206
Forgot to rename to valueKeys.
Globegitter Apr 9, 2015
6e7a3dd
Of course also forgot in compoundQueries.
Globegitter Apr 9, 2015
43a096f
Take union of arrays rather than simply concatenating them.
Globegitter Apr 9, 2015
b3d56bc
Merge pull request #942 from Globegitter/patch-5
devinivy Apr 10, 2015
a1b4cf2
WIP: Added first code for findAndModify.
Globegitter Apr 7, 2015
6df2f24
Fixed findAndModify. Added unit tests.
Globegitter Apr 8, 2015
c517bb5
Fixed unit tests.
Globegitter Apr 8, 2015
2a47c9b
Removed consoe.log
Globegitter Apr 8, 2015
a63c7a8
Return error on update. Fixed some criteria checks.
Globegitter Apr 8, 2015
1767213
Get first working implementation of mergeArrays.
Globegitter Apr 9, 2015
3ad6a82
Slightly optimised and simplified mergeArrays implementation.
Globegitter Apr 9, 2015
aeb1dd1
Forgot to rename to valueKeys.
Globegitter Apr 9, 2015
941042e
Of course also forgot in compoundQueries.
Globegitter Apr 9, 2015
5817a94
Take union of arrays rather than simply concatenating them.
Globegitter Apr 9, 2015
abbc41f
Rebased to latest master.
Globegitter Apr 10, 2015
cb1e337
Removed remaining HEAD in aggregatQueries
Globegitter Apr 10, 2015
2b5b403
Fix findAndModifyEach.
Globegitter Apr 20, 2015
d9a3bd3
Push array element, not the whole array.
Globegitter Apr 20, 2015
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
91 changes: 91 additions & 0 deletions lib/waterline/adapter/aggregateQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,97 @@ module.exports = {
if(err) return cb(err);
cb(null, models);
});
},

// If an optimized findAndModify exists, use it, otherwise use an asynchronous loop with create()
findAndModifyEach: function(attributesToCheck, valuesList, options, cb) {
var self = this;
var connName;
var adapter;

if(typeof options === 'function') {
cb = options;
// set default values
options = {
upsert: false,
new: true,
mergeArrays: false
};
}

var isObjectArray = false;
if (_.isObject(attributesToCheck[0])) {
if (attributesToCheck.length > 1 &&
attributesToCheck.length !== valuesList.length) {
return cb(new Error('findAndModifyEach: The two passed arrays have to be of the same length.'));
}
isObjectArray = true;
}

// Normalize Arguments
cb = normalize.callback(cb);

// Clone sensitive data
attributesToCheck = _.clone(attributesToCheck);
valuesList = _.clone(valuesList);

// Custom user adapter behavior
if(hasOwnProperty(this.dictionary, 'findAndModifyEach')) {
connName = this.dictionary.findAndModifyEach;
adapter = this.connections[connName]._adapter;

if(hasOwnProperty(adapter, 'findAndModifyEach')) {
return adapter.findAndModifyEach(connName, this.collection, valuesList, options, cb);
}
}

// Build a list of models
var models = [];
var i = 0;

async.eachSeries(valuesList, function (values, cb) {
if (!_.isObject(values)) return cb(new Error('findAndModifyEach: Unexpected value in valuesList.'));

// Check that each of the criteria keys match:
// build a criteria query
var criteria = {};

if (isObjectArray) {
if (_.isObject(attributesToCheck[i])) {
Object.keys(attributesToCheck[i]).forEach(function (attrName) {
criteria[attrName] = values[attrName];
});
if (attributesToCheck.length > 1) {
i++;
}
} else {
return cb(new Error('findAndModifyEach: Element ' + i + ' in attributesToCheck is not an object.' ));
}
} else {
attributesToCheck.forEach(function (attrName) {
criteria[attrName] = values[attrName];
});
}

return self.findAndModify.call(self, criteria, values, options, function (err, model) {
if(err) return cb(err);

// if returned models are an array push each result
if (Array.isArray(model)) {
for (var i = 0; i < model.length; i++) {
models.push(model[i]);
};
} else if (model) {
// Add model to list
models.push(model);
}

cb(null, model);
});
}, function (err) {
if(err) return cb(err);
cb(null, models);
});
}

};
100 changes: 100 additions & 0 deletions lib/waterline/adapter/compoundQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,106 @@ module.exports = {

self.create(values, cb);
});
},

findAndModify: function(criteria, values, options, cb) {
var self = this;
var connName;
var adapter;

if(typeof options === 'function') {
cb = options;
// set default values
options = {
upsert: false,
new: false,
mergeArrays: false
};
}

if (typeof values === 'function') {
cb = values;
values = null
}

options = options || { };

//new: true is the default value
if (!('new' in options)) {
options.new = true;
}

// // If no values were specified, use criteria
// if (!values) values = criteria.where ? criteria.where : criteria;

// Normalize Arguments
criteria = normalize.criteria(criteria);
cb = normalize.callback(cb);

// Build Default Error Message
var err = "No find() or create() method defined in adapter!";

// Custom user adapter behavior
if(hasOwnProperty(this.dictionary, 'findAndModify')) {
connName = this.dictionary.findOrCreate;
adapter = this.connections[connName]._adapter;

if(hasOwnProperty(adapter, 'findAndModify')) {
return adapter.findAndModify(connName, this.collection, values, options, cb);
}
}

// Default behavior
// WARNING: Not transactional! (unless your data adapter is)
this.findOne(criteria, function(err, result) {
if(err) return cb(err);
if(result) {

//merging any arrays in the result with any matching arrays in the values
// Note: At this point values should just be an object
if (options.mergeArrays) {
var valueKeys = Object.keys(values);
// Loop over all the results to see if it contains an array
for (var i = 0; i < valueKeys.length; i++) {
// Check if both properties are an array, if one isn't just ignore
// Note: We do not have to explicitly check if the property exists in the result
// because isArray then just returns false
if (Array.isArray(values[valueKeys[i]]) && Array.isArray(result[valueKeys[i]])) {
//now take the union of the arrays
values[valueKeys[i]] = _.union(result[valueKeys[i]], values[[valueKeys[i]]]);
}
}
}

self.update(criteria, values, function(err, updatedResults) {
if (err) {
return cb(err);
}
// if new is given return the model after it has been updated
if (options.new) {
return cb(null, updatedResults);
} else {
// Unserialize values
return cb(null, result);
}

});
} else if (options.upsert) {
// Create a new record if nothing is found and upsert is true.
//Note(globegitter): This might now ignore the 'options.new' flag
//so need to find a proper way to test/verify that.
self.create(values, function(err, result) {
if(err) return cb(err);
if (options.new) {
return cb(null, result);
} else {
return cb(null, []);
}
});
} else {
return cb(null, []);
}
});
}

};
3 changes: 3 additions & 0 deletions lib/waterline/core/typecast.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ Cast.prototype.date = function date(value) {
if (value.__proto__ == Date.prototype) {
_value = new Date(value.getTime());
}
else if (typeof value.toDate === 'function') {
_value = value.toDate();
}
else {
_value = new Date(Date.parse(value));
}
Expand Down
Loading