summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline/document_source_sort.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/pipeline/document_source_sort.cpp')
-rw-r--r--src/mongo/db/pipeline/document_source_sort.cpp515
1 files changed, 255 insertions, 260 deletions
diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp
index f4e57d5c8ae..1b7396b8513 100644
--- a/src/mongo/db/pipeline/document_source_sort.cpp
+++ b/src/mongo/db/pipeline/document_source_sort.cpp
@@ -39,329 +39,324 @@
namespace mongo {
- using boost::intrusive_ptr;
- using std::unique_ptr;
- using std::make_pair;
- using std::string;
- using std::vector;
+using boost::intrusive_ptr;
+using std::unique_ptr;
+using std::make_pair;
+using std::string;
+using std::vector;
- const char DocumentSourceSort::sortName[] = "$sort";
+const char DocumentSourceSort::sortName[] = "$sort";
- const char *DocumentSourceSort::getSourceName() const {
- return sortName;
- }
+const char* DocumentSourceSort::getSourceName() const {
+ return sortName;
+}
- boost::optional<Document> DocumentSourceSort::getNext() {
- pExpCtx->checkForInterrupt();
+boost::optional<Document> DocumentSourceSort::getNext() {
+ pExpCtx->checkForInterrupt();
- if (!populated)
- populate();
+ if (!populated)
+ populate();
- if (!_output || !_output->more())
- return boost::none;
+ if (!_output || !_output->more())
+ return boost::none;
- return _output->next().second;
- }
+ return _output->next().second;
+}
- void DocumentSourceSort::serializeToArray(vector<Value>& array, bool explain) const {
- if (explain) { // always one Value for combined $sort + $limit
- array.push_back(Value(DOC(getSourceName() <<
- DOC("sortKey" << serializeSortKey(explain)
- << "mergePresorted" << (_mergingPresorted ? Value(true) : Value())
- << "limit" << (limitSrc ? Value(limitSrc->getLimit()) : Value())))));
- }
- else { // one Value for $sort and maybe a Value for $limit
- MutableDocument inner (serializeSortKey(explain));
- if (_mergingPresorted)
- inner["$mergePresorted"] = Value(true);
- array.push_back(Value(DOC(getSourceName() << inner.freeze())));
-
- if (limitSrc) {
- limitSrc->serializeToArray(array);
- }
+void DocumentSourceSort::serializeToArray(vector<Value>& array, bool explain) const {
+ if (explain) { // always one Value for combined $sort + $limit
+ array.push_back(
+ Value(DOC(getSourceName()
+ << DOC("sortKey" << serializeSortKey(explain) << "mergePresorted"
+ << (_mergingPresorted ? Value(true) : Value()) << "limit"
+ << (limitSrc ? Value(limitSrc->getLimit()) : Value())))));
+ } else { // one Value for $sort and maybe a Value for $limit
+ MutableDocument inner(serializeSortKey(explain));
+ if (_mergingPresorted)
+ inner["$mergePresorted"] = Value(true);
+ array.push_back(Value(DOC(getSourceName() << inner.freeze())));
+
+ if (limitSrc) {
+ limitSrc->serializeToArray(array);
}
}
+}
- void DocumentSourceSort::dispose() {
- _output.reset();
- pSource->dispose();
- }
+void DocumentSourceSort::dispose() {
+ _output.reset();
+ pSource->dispose();
+}
- DocumentSourceSort::DocumentSourceSort(const intrusive_ptr<ExpressionContext> &pExpCtx)
- : DocumentSource(pExpCtx)
- , populated(false)
- , _mergingPresorted(false)
- {}
+DocumentSourceSort::DocumentSourceSort(const intrusive_ptr<ExpressionContext>& pExpCtx)
+ : DocumentSource(pExpCtx), populated(false), _mergingPresorted(false) {}
- long long DocumentSourceSort::getLimit() const {
- return limitSrc ? limitSrc->getLimit() : -1;
- }
+long long DocumentSourceSort::getLimit() const {
+ return limitSrc ? limitSrc->getLimit() : -1;
+}
- bool DocumentSourceSort::coalesce(const intrusive_ptr<DocumentSource> &pNextSource) {
- if (!limitSrc) {
- limitSrc = dynamic_cast<DocumentSourceLimit*>(pNextSource.get());
- return limitSrc.get(); // false if next is not a $limit
- }
- else {
- return limitSrc->coalesce(pNextSource);
- }
+bool DocumentSourceSort::coalesce(const intrusive_ptr<DocumentSource>& pNextSource) {
+ if (!limitSrc) {
+ limitSrc = dynamic_cast<DocumentSourceLimit*>(pNextSource.get());
+ return limitSrc.get(); // false if next is not a $limit
+ } else {
+ return limitSrc->coalesce(pNextSource);
}
+}
- void DocumentSourceSort::addKey(const string& fieldPath, bool ascending) {
- VariablesIdGenerator idGenerator;
- VariablesParseState vps(&idGenerator);
- vSortKey.push_back(ExpressionFieldPath::parse("$$ROOT." + fieldPath, vps));
- vAscending.push_back(ascending);
- }
+void DocumentSourceSort::addKey(const string& fieldPath, bool ascending) {
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ vSortKey.push_back(ExpressionFieldPath::parse("$$ROOT." + fieldPath, vps));
+ vAscending.push_back(ascending);
+}
- Document DocumentSourceSort::serializeSortKey(bool explain) const {
- MutableDocument keyObj;
- // add the key fields
- const size_t n = vSortKey.size();
- for(size_t i = 0; i < n; ++i) {
- if (ExpressionFieldPath* efp = dynamic_cast<ExpressionFieldPath*>(vSortKey[i].get())) {
- // ExpressionFieldPath gets special syntax that includes direction
- const FieldPath& withVariable = efp->getFieldPath();
- verify(withVariable.getPathLength() > 1);
- verify(withVariable.getFieldName(0) == "ROOT");
- const string fieldPath = withVariable.tail().getPath(false);
-
- // append a named integer based on the sort order
- keyObj.setField(fieldPath, Value(vAscending[i] ? 1 : -1));
- }
- else {
- // other expressions use a made-up field name
- keyObj[string(str::stream() << "$computed" << i)] = vSortKey[i]->serialize(explain);
- }
+Document DocumentSourceSort::serializeSortKey(bool explain) const {
+ MutableDocument keyObj;
+ // add the key fields
+ const size_t n = vSortKey.size();
+ for (size_t i = 0; i < n; ++i) {
+ if (ExpressionFieldPath* efp = dynamic_cast<ExpressionFieldPath*>(vSortKey[i].get())) {
+ // ExpressionFieldPath gets special syntax that includes direction
+ const FieldPath& withVariable = efp->getFieldPath();
+ verify(withVariable.getPathLength() > 1);
+ verify(withVariable.getFieldName(0) == "ROOT");
+ const string fieldPath = withVariable.tail().getPath(false);
+
+ // append a named integer based on the sort order
+ keyObj.setField(fieldPath, Value(vAscending[i] ? 1 : -1));
+ } else {
+ // other expressions use a made-up field name
+ keyObj[string(str::stream() << "$computed" << i)] = vSortKey[i]->serialize(explain);
}
- return keyObj.freeze();
}
+ return keyObj.freeze();
+}
- DocumentSource::GetDepsReturn DocumentSourceSort::getDependencies(DepsTracker* deps) const {
- for(size_t i = 0; i < vSortKey.size(); ++i) {
- vSortKey[i]->addDependencies(deps);
- }
-
- return SEE_NEXT;
+DocumentSource::GetDepsReturn DocumentSourceSort::getDependencies(DepsTracker* deps) const {
+ for (size_t i = 0; i < vSortKey.size(); ++i) {
+ vSortKey[i]->addDependencies(deps);
}
+ return SEE_NEXT;
+}
- intrusive_ptr<DocumentSource> DocumentSourceSort::createFromBson(
- BSONElement elem,
- const intrusive_ptr<ExpressionContext> &pExpCtx) {
- uassert(15973, str::stream() << " the " <<
- sortName << " key specification must be an object",
- elem.type() == Object);
- return create(pExpCtx, elem.embeddedObject());
- }
+intrusive_ptr<DocumentSource> DocumentSourceSort::createFromBson(
+ BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) {
+ uassert(15973,
+ str::stream() << " the " << sortName << " key specification must be an object",
+ elem.type() == Object);
- intrusive_ptr<DocumentSourceSort> DocumentSourceSort::create(
- const intrusive_ptr<ExpressionContext> &pExpCtx,
- BSONObj sortOrder,
- long long limit) {
+ return create(pExpCtx, elem.embeddedObject());
+}
- intrusive_ptr<DocumentSourceSort> pSort = new DocumentSourceSort(pExpCtx);
+intrusive_ptr<DocumentSourceSort> DocumentSourceSort::create(
+ const intrusive_ptr<ExpressionContext>& pExpCtx, BSONObj sortOrder, long long limit) {
+ intrusive_ptr<DocumentSourceSort> pSort = new DocumentSourceSort(pExpCtx);
- /* check for then iterate over the sort object */
- BSONForEach(keyField, sortOrder) {
- const char* fieldName = keyField.fieldName();
+ /* check for then iterate over the sort object */
+ BSONForEach(keyField, sortOrder) {
+ const char* fieldName = keyField.fieldName();
- if (str::equals(fieldName, "$mergePresorted")) {
- verify(keyField.Bool());
- pSort->_mergingPresorted = true;
- continue;
- }
+ if (str::equals(fieldName, "$mergePresorted")) {
+ verify(keyField.Bool());
+ pSort->_mergingPresorted = true;
+ continue;
+ }
- if (keyField.type() == Object) {
- // this restriction is due to needing to figure out sort direction
- uassert(17312,
- "the only expression supported by $sort right now is {$meta: 'textScore'}",
- keyField.Obj() == BSON("$meta" << "textScore"));
+ if (keyField.type() == Object) {
+ // this restriction is due to needing to figure out sort direction
+ uassert(17312,
+ "the only expression supported by $sort right now is {$meta: 'textScore'}",
+ keyField.Obj() == BSON("$meta"
+ << "textScore"));
- pSort->vSortKey.push_back(new ExpressionMeta());
- pSort->vAscending.push_back(false); // best scoring documents first
- continue;
- }
-
- uassert(15974,
- "$sort key ordering must be specified using a number or {$meta: 'textScore'}",
- keyField.isNumber());
+ pSort->vSortKey.push_back(new ExpressionMeta());
+ pSort->vAscending.push_back(false); // best scoring documents first
+ continue;
+ }
- int sortOrder = keyField.numberInt();
+ uassert(15974,
+ "$sort key ordering must be specified using a number or {$meta: 'textScore'}",
+ keyField.isNumber());
- uassert(15975, "$sort key ordering must be 1 (for ascending) or -1 (for descending)",
- ((sortOrder == 1) || (sortOrder == -1)));
+ int sortOrder = keyField.numberInt();
- pSort->addKey(fieldName, (sortOrder > 0));
- }
+ uassert(15975,
+ "$sort key ordering must be 1 (for ascending) or -1 (for descending)",
+ ((sortOrder == 1) || (sortOrder == -1)));
- uassert(15976, str::stream() << sortName << " must have at least one sort key",
- !pSort->vSortKey.empty());
+ pSort->addKey(fieldName, (sortOrder > 0));
+ }
- if (limit > 0) {
- bool coalesced = pSort->coalesce(DocumentSourceLimit::create(pExpCtx, limit));
- verify(coalesced); // should always coalesce
- verify(pSort->getLimit() == limit);
- }
+ uassert(15976,
+ str::stream() << sortName << " must have at least one sort key",
+ !pSort->vSortKey.empty());
- return pSort;
+ if (limit > 0) {
+ bool coalesced = pSort->coalesce(DocumentSourceLimit::create(pExpCtx, limit));
+ verify(coalesced); // should always coalesce
+ verify(pSort->getLimit() == limit);
}
- SortOptions DocumentSourceSort::makeSortOptions() const {
- /* make sure we've got a sort key */
- verify(vSortKey.size());
+ return pSort;
+}
- SortOptions opts;
- if (limitSrc)
- opts.limit = limitSrc->getLimit();
+SortOptions DocumentSourceSort::makeSortOptions() const {
+ /* make sure we've got a sort key */
+ verify(vSortKey.size());
- opts.maxMemoryUsageBytes = 100*1024*1024;
- if (pExpCtx->extSortAllowed && !pExpCtx->inRouter) {
- opts.extSortAllowed = true;
- opts.tempDir = pExpCtx->tempDir;
- }
+ SortOptions opts;
+ if (limitSrc)
+ opts.limit = limitSrc->getLimit();
- return opts;
+ opts.maxMemoryUsageBytes = 100 * 1024 * 1024;
+ if (pExpCtx->extSortAllowed && !pExpCtx->inRouter) {
+ opts.extSortAllowed = true;
+ opts.tempDir = pExpCtx->tempDir;
}
- void DocumentSourceSort::populate() {
- if (_mergingPresorted) {
- typedef DocumentSourceMergeCursors DSCursors;
- typedef DocumentSourceCommandShards DSCommands;
- if (DSCursors* castedSource = dynamic_cast<DSCursors*>(pSource)) {
- populateFromCursors(castedSource->getCursors());
- } else if (DSCommands* castedSource = dynamic_cast<DSCommands*>(pSource)) {
- populateFromBsonArrays(castedSource->getArrays());
- } else {
- msgasserted(17196, "can only mergePresorted from MergeCursors and CommandShards");
- }
+ return opts;
+}
+
+void DocumentSourceSort::populate() {
+ if (_mergingPresorted) {
+ typedef DocumentSourceMergeCursors DSCursors;
+ typedef DocumentSourceCommandShards DSCommands;
+ if (DSCursors* castedSource = dynamic_cast<DSCursors*>(pSource)) {
+ populateFromCursors(castedSource->getCursors());
+ } else if (DSCommands* castedSource = dynamic_cast<DSCommands*>(pSource)) {
+ populateFromBsonArrays(castedSource->getArrays());
} else {
- unique_ptr<MySorter> sorter (MySorter::make(makeSortOptions(), Comparator(*this)));
- while (boost::optional<Document> next = pSource->getNext()) {
- sorter->add(extractKey(*next), *next);
- }
- _output.reset(sorter->done());
+ msgasserted(17196, "can only mergePresorted from MergeCursors and CommandShards");
}
- populated = true;
+ } else {
+ unique_ptr<MySorter> sorter(MySorter::make(makeSortOptions(), Comparator(*this)));
+ while (boost::optional<Document> next = pSource->getNext()) {
+ sorter->add(extractKey(*next), *next);
+ }
+ _output.reset(sorter->done());
}
+ populated = true;
+}
- class DocumentSourceSort::IteratorFromCursor : public MySorter::Iterator {
- public:
- IteratorFromCursor(DocumentSourceSort* sorter, DBClientCursor* cursor)
- : _sorter(sorter)
- , _cursor(cursor)
- {}
-
- bool more() { return _cursor->more(); }
- Data next() {
- const Document doc = DocumentSourceMergeCursors::nextSafeFrom(_cursor);
- return make_pair(_sorter->extractKey(doc), doc);
- }
- private:
- DocumentSourceSort* _sorter;
- DBClientCursor* _cursor;
- };
-
- void DocumentSourceSort::populateFromCursors(const vector<DBClientCursor*>& cursors) {
- vector<std::shared_ptr<MySorter::Iterator> > iterators;
- for (size_t i = 0; i < cursors.size(); i++) {
- iterators.push_back(std::make_shared<IteratorFromCursor>(this, cursors[i]));
- }
+class DocumentSourceSort::IteratorFromCursor : public MySorter::Iterator {
+public:
+ IteratorFromCursor(DocumentSourceSort* sorter, DBClientCursor* cursor)
+ : _sorter(sorter), _cursor(cursor) {}
- _output.reset(MySorter::Iterator::merge(iterators, makeSortOptions(), Comparator(*this)));
+ bool more() {
+ return _cursor->more();
+ }
+ Data next() {
+ const Document doc = DocumentSourceMergeCursors::nextSafeFrom(_cursor);
+ return make_pair(_sorter->extractKey(doc), doc);
}
- class DocumentSourceSort::IteratorFromBsonArray : public MySorter::Iterator {
- public:
- IteratorFromBsonArray(DocumentSourceSort* sorter, const BSONArray& array)
- : _sorter(sorter)
- , _iterator(array)
- {}
-
- bool more() { return _iterator.more(); }
- Data next() {
- Document doc(_iterator.next().Obj());
- return make_pair(_sorter->extractKey(doc), doc);
- }
- private:
- DocumentSourceSort* _sorter;
- BSONObjIterator _iterator;
- };
-
- void DocumentSourceSort::populateFromBsonArrays(const vector<BSONArray>& arrays) {
- vector<std::shared_ptr<MySorter::Iterator> > iterators;
- for (size_t i = 0; i < arrays.size(); i++) {
- iterators.push_back(std::make_shared<IteratorFromBsonArray>(this, arrays[i]));
- }
+private:
+ DocumentSourceSort* _sorter;
+ DBClientCursor* _cursor;
+};
- _output.reset(MySorter::Iterator::merge(iterators, makeSortOptions(), Comparator(*this)));
+void DocumentSourceSort::populateFromCursors(const vector<DBClientCursor*>& cursors) {
+ vector<std::shared_ptr<MySorter::Iterator>> iterators;
+ for (size_t i = 0; i < cursors.size(); i++) {
+ iterators.push_back(std::make_shared<IteratorFromCursor>(this, cursors[i]));
}
- Value DocumentSourceSort::extractKey(const Document& d) const {
- Variables vars(0, d);
- if (vSortKey.size() == 1) {
- return vSortKey[0]->evaluate(&vars);
- }
+ _output.reset(MySorter::Iterator::merge(iterators, makeSortOptions(), Comparator(*this)));
+}
- vector<Value> keys;
- keys.reserve(vSortKey.size());
- for (size_t i=0; i < vSortKey.size(); i++) {
- keys.push_back(vSortKey[i]->evaluate(&vars));
- }
- return Value(std::move(keys));
+class DocumentSourceSort::IteratorFromBsonArray : public MySorter::Iterator {
+public:
+ IteratorFromBsonArray(DocumentSourceSort* sorter, const BSONArray& array)
+ : _sorter(sorter), _iterator(array) {}
+
+ bool more() {
+ return _iterator.more();
+ }
+ Data next() {
+ Document doc(_iterator.next().Obj());
+ return make_pair(_sorter->extractKey(doc), doc);
}
- int DocumentSourceSort::compare(const Value& lhs, const Value& rhs) const {
-
- /*
- populate() already checked that there is a non-empty sort key,
- so we shouldn't have to worry about that here.
-
- However, the tricky part is what to do is none of the sort keys are
- present. In this case, consider the document less.
- */
- const size_t n = vSortKey.size();
- if (n == 1) { // simple fast case
- if (vAscending[0])
- return Value::compare(lhs, rhs);
- else
- return -Value::compare(lhs, rhs);
- }
+private:
+ DocumentSourceSort* _sorter;
+ BSONObjIterator _iterator;
+};
- // compound sort
- for (size_t i = 0; i < n; i++) {
- int cmp = Value::compare(lhs[i], rhs[i]);
- if (cmp) {
- /* if necessary, adjust the return value by the key ordering */
- if (!vAscending[i])
- cmp = -cmp;
+void DocumentSourceSort::populateFromBsonArrays(const vector<BSONArray>& arrays) {
+ vector<std::shared_ptr<MySorter::Iterator>> iterators;
+ for (size_t i = 0; i < arrays.size(); i++) {
+ iterators.push_back(std::make_shared<IteratorFromBsonArray>(this, arrays[i]));
+ }
- return cmp;
- }
- }
+ _output.reset(MySorter::Iterator::merge(iterators, makeSortOptions(), Comparator(*this)));
+}
- /*
- If we got here, everything matched (or didn't exist), so we'll
- consider the documents equal for purposes of this sort.
- */
- return 0;
+Value DocumentSourceSort::extractKey(const Document& d) const {
+ Variables vars(0, d);
+ if (vSortKey.size() == 1) {
+ return vSortKey[0]->evaluate(&vars);
}
- intrusive_ptr<DocumentSource> DocumentSourceSort::getShardSource() {
- verify(!_mergingPresorted);
- return this;
+ vector<Value> keys;
+ keys.reserve(vSortKey.size());
+ for (size_t i = 0; i < vSortKey.size(); i++) {
+ keys.push_back(vSortKey[i]->evaluate(&vars));
}
+ return Value(std::move(keys));
+}
+
+int DocumentSourceSort::compare(const Value& lhs, const Value& rhs) const {
+ /*
+ populate() already checked that there is a non-empty sort key,
+ so we shouldn't have to worry about that here.
+
+ However, the tricky part is what to do is none of the sort keys are
+ present. In this case, consider the document less.
+ */
+ const size_t n = vSortKey.size();
+ if (n == 1) { // simple fast case
+ if (vAscending[0])
+ return Value::compare(lhs, rhs);
+ else
+ return -Value::compare(lhs, rhs);
+ }
+
+ // compound sort
+ for (size_t i = 0; i < n; i++) {
+ int cmp = Value::compare(lhs[i], rhs[i]);
+ if (cmp) {
+ /* if necessary, adjust the return value by the key ordering */
+ if (!vAscending[i])
+ cmp = -cmp;
- intrusive_ptr<DocumentSource> DocumentSourceSort::getMergeSource() {
- verify(!_mergingPresorted);
- intrusive_ptr<DocumentSourceSort> other = new DocumentSourceSort(pExpCtx);
- other->vAscending = vAscending;
- other->vSortKey = vSortKey;
- other->limitSrc = limitSrc;
- other->_mergingPresorted = true;
- return other;
+ return cmp;
+ }
}
+
+ /*
+ If we got here, everything matched (or didn't exist), so we'll
+ consider the documents equal for purposes of this sort.
+ */
+ return 0;
+}
+
+intrusive_ptr<DocumentSource> DocumentSourceSort::getShardSource() {
+ verify(!_mergingPresorted);
+ return this;
+}
+
+intrusive_ptr<DocumentSource> DocumentSourceSort::getMergeSource() {
+ verify(!_mergingPresorted);
+ intrusive_ptr<DocumentSourceSort> other = new DocumentSourceSort(pExpCtx);
+ other->vAscending = vAscending;
+ other->vSortKey = vSortKey;
+ other->limitSrc = limitSrc;
+ other->_mergingPresorted = true;
+ return other;
+}
}
#include "mongo/db/sorter/sorter.cpp"