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 #939

Closed
wants to merge 8 commits into from
Closed
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
84 changes: 84 additions & 0 deletions lib/waterline/adapter/aggregateQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,90 @@ 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('findOrCreateEach: 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);

// Add model to list
if(model) models.push(model);

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

};
81 changes: 81 additions & 0 deletions lib/waterline/adapter/compoundQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,87 @@ 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) {

self.update(criteria, values, function(err, updatedResults) {
// 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, []);
}
});
}

};
Loading