/* Copyright 2016 MongoDB Inc.
*
* 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/client/query.h"
#include "mongo/base/status.h"
#include "mongo/base/status_with.h"
#include "mongo/bson/util/builder.h"
namespace mongo {
using std::string;
const BSONField Query::ReadPrefField("$readPreference");
const BSONField Query::ReadPrefModeField("mode");
const BSONField Query::ReadPrefTagsField("tags");
Query::Query(const string& json) : obj(fromjson(json)) {}
Query::Query(const char* json) : obj(fromjson(json)) {}
Query& Query::hint(const string& jsonKeyPatt) {
return hint(fromjson(jsonKeyPatt));
}
Query& Query::where(const string& jscode, BSONObj scope) {
/* use where() before sort() and hint() and explain(), else this will assert. */
verify(!isComplex());
BSONObjBuilder b(std::move(obj));
b.appendWhere(jscode, scope);
obj = b.obj();
return *this;
}
void Query::makeComplex() {
if (isComplex())
return;
BSONObjBuilder b;
b.append("query", obj);
obj = b.obj();
}
Query& Query::sort(const BSONObj& s) {
appendComplex("orderby", s);
return *this;
}
Query& Query::hint(BSONObj keyPattern) {
appendComplex("$hint", keyPattern);
return *this;
}
Query& Query::explain() {
appendComplex("$explain", true);
return *this;
}
Query& Query::snapshot() {
appendComplex("$snapshot", true);
return *this;
}
Query& Query::minKey(const BSONObj& val) {
appendComplex("$min", val);
return *this;
}
Query& Query::maxKey(const BSONObj& val) {
appendComplex("$max", val);
return *this;
}
bool Query::isComplex(const BSONObj& obj, bool* hasDollar) {
if (obj.hasElement("query")) {
if (hasDollar)
*hasDollar = false;
return true;
}
if (obj.hasElement("$query")) {
if (hasDollar)
*hasDollar = true;
return true;
}
return false;
}
Query& Query::readPref(ReadPreference pref, const BSONArray& tags) {
appendComplex(ReadPrefField.name().c_str(),
ReadPreferenceSetting(pref, TagSet(tags)).toInnerBSON());
return *this;
}
bool Query::isComplex(bool* hasDollar) const {
return isComplex(obj, hasDollar);
}
bool Query::hasReadPreference(const BSONObj& queryObj) {
const bool hasReadPrefOption = queryObj["$queryOptions"].isABSONObj() &&
queryObj["$queryOptions"].Obj().hasField(ReadPrefField.name());
bool canHaveReadPrefField = Query::isComplex(queryObj) ||
// The find command has a '$readPreference' option.
queryObj.firstElementFieldName() == StringData("find");
return (canHaveReadPrefField && queryObj.hasField(ReadPrefField.name())) || hasReadPrefOption;
}
BSONObj Query::getFilter() const {
bool hasDollar;
if (!isComplex(&hasDollar))
return obj;
return obj.getObjectField(hasDollar ? "$query" : "query");
}
BSONObj Query::getSort() const {
if (!isComplex())
return BSONObj();
BSONObj ret = obj.getObjectField("orderby");
if (ret.isEmpty())
ret = obj.getObjectField("$orderby");
return ret;
}
BSONObj Query::getHint() const {
if (!isComplex())
return BSONObj();
return obj.getObjectField("$hint");
}
bool Query::isExplain() const {
return isComplex() && obj.getBoolField("$explain");
}
string Query::toString() const {
return obj.toString();
}
void assembleQueryRequest(const string& ns,
BSONObj query,
int nToReturn,
int nToSkip,
const BSONObj* fieldsToReturn,
int queryOptions,
Message& toSend) {
if (kDebugBuild) {
massert(10337, (string) "object not valid assembleRequest query", query.isValid());
}
// see query.h for the protocol we are using here.
BufBuilder b;
int opts = queryOptions;
b.appendNum(opts);
b.appendStr(ns);
b.appendNum(nToSkip);
b.appendNum(nToReturn);
query.appendSelfToBufBuilder(b);
if (fieldsToReturn)
fieldsToReturn->appendSelfToBufBuilder(b);
toSend.setData(dbQuery, b.buf(), b.len());
}
} // namespace mongo