// 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 . */ #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; }