summaryrefslogtreecommitdiff
path: root/db/commands/find_and_modify.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/commands/find_and_modify.cpp')
-rw-r--r--db/commands/find_and_modify.cpp153
1 files changed, 153 insertions, 0 deletions
diff --git a/db/commands/find_and_modify.cpp b/db/commands/find_and_modify.cpp
new file mode 100644
index 0000000..0cf766f
--- /dev/null
+++ b/db/commands/find_and_modify.cpp
@@ -0,0 +1,153 @@
+// find_and_modify.cpp
+
+/**
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+#include "../commands.h"
+#include "../instance.h"
+#include "../clientcursor.h"
+
+namespace mongo {
+
+ /* Find and Modify an object returning either the old (default) or new value*/
+ class CmdFindAndModify : public Command {
+ public:
+ virtual void help( stringstream &help ) const {
+ help <<
+ "{ findAndModify: \"collection\", query: {processed:false}, update: {$set: {processed:true}}, new: true}\n"
+ "{ findAndModify: \"collection\", query: {processed:false}, remove: true, sort: {priority:-1}}\n"
+ "Either update or remove is required, all other fields have default values.\n"
+ "Output is in the \"value\" field\n";
+ }
+
+ CmdFindAndModify() : Command("findAndModify", false, "findandmodify") { }
+ virtual bool logTheOp() { return false; } // the modifications will be logged directly
+ virtual bool slaveOk() const { return false; }
+ virtual LockType locktype() const { return WRITE; }
+ virtual bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
+ static DBDirectClient db;
+
+ string ns = dbname + '.' + cmdObj.firstElement().valuestr();
+
+ BSONObj origQuery = cmdObj.getObjectField("query"); // defaults to {}
+ Query q (origQuery);
+ BSONElement sort = cmdObj["sort"];
+ if (!sort.eoo())
+ q.sort(sort.embeddedObjectUserCheck());
+
+ bool upsert = cmdObj["upsert"].trueValue();
+
+ BSONObj fieldsHolder (cmdObj.getObjectField("fields"));
+ const BSONObj* fields = (fieldsHolder.isEmpty() ? NULL : &fieldsHolder);
+
+ Projection projection;
+ if (fields) {
+ projection.init(fieldsHolder);
+ if (!projection.includeID())
+ fields = NULL; // do projection in post-processing
+ }
+
+ BSONObj out = db.findOne(ns, q, fields);
+ if (out.isEmpty()) {
+ if (!upsert) {
+ result.appendNull("value");
+ return true;
+ }
+
+ BSONElement update = cmdObj["update"];
+ uassert(13329, "upsert mode requires update field", !update.eoo());
+ uassert(13330, "upsert mode requires query field", !origQuery.isEmpty());
+ db.update(ns, origQuery, update.embeddedObjectUserCheck(), true);
+
+ BSONObj gle = db.getLastErrorDetailed();
+ result.append("lastErrorObject", gle);
+ if (gle["err"].type() == String) {
+ errmsg = gle["err"].String();
+ return false;
+ }
+
+ if (cmdObj["new"].trueValue()) {
+ BSONElement _id = gle["upserted"];
+ if (_id.eoo())
+ _id = origQuery["_id"];
+
+ out = db.findOne(ns, QUERY("_id" << _id), fields);
+ }
+
+ }
+ else {
+
+ if (cmdObj["remove"].trueValue()) {
+ uassert(12515, "can't remove and update", cmdObj["update"].eoo());
+ db.remove(ns, QUERY("_id" << out["_id"]), 1);
+
+ BSONObj gle = db.getLastErrorDetailed();
+ result.append("lastErrorObject", gle);
+ if (gle["err"].type() == String) {
+ errmsg = gle["err"].String();
+ return false;
+ }
+
+ }
+ else { // update
+
+ BSONElement queryId = origQuery["_id"];
+ if (queryId.eoo() || getGtLtOp(queryId) != BSONObj::Equality) {
+ // need to include original query for $ positional operator
+
+ BSONObjBuilder b;
+ b.append(out["_id"]);
+ BSONObjIterator it(origQuery);
+ while (it.more()) {
+ BSONElement e = it.next();
+ if (strcmp(e.fieldName(), "_id"))
+ b.append(e);
+ }
+ q = Query(b.obj());
+ }
+
+ if (q.isComplex()) // update doesn't work with complex queries
+ q = Query(q.getFilter().getOwned());
+
+ BSONElement update = cmdObj["update"];
+ uassert(12516, "must specify remove or update", !update.eoo());
+ db.update(ns, q, update.embeddedObjectUserCheck());
+
+ BSONObj gle = db.getLastErrorDetailed();
+ result.append("lastErrorObject", gle);
+ if (gle["err"].type() == String) {
+ errmsg = gle["err"].String();
+ return false;
+ }
+
+ if (cmdObj["new"].trueValue())
+ out = db.findOne(ns, QUERY("_id" << out["_id"]), fields);
+ }
+ }
+
+ if (!fieldsHolder.isEmpty() && !fields){
+ // we need to run projection but haven't yet
+ out = projection.transform(out);
+ }
+
+ result.append("value", out);
+
+ return true;
+ }
+ } cmdFindAndModify;
+
+
+}