/**
* Copyright (C) 2015 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/db/exec/sort_key_generator.h"
#include "mongo/db/json.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/db/query/query_test_service_context.h"
#include "mongo/stdx/memory.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
namespace {
/**
* Test function to verify that the SortKeyGenerator can generate a sortKey from a fetched document.
*
* sortSpec - The JSON representation of the sort spec BSONObj.
* doc - The JSON representation of the BSON document.
* queryObj - The JSON representation of the query predicate. Used when generating the sort key from
* an array.
* collator - The collation for the sort that we are are generating keys for.
*
* Returns the BSON representation of the sort key, to be checked against the expected sort key.
*/
BSONObj extractSortKey(const char* sortSpec,
const char* doc,
const char* query,
const CollatorInterface* collator) {
QueryTestServiceContext serviceContext;
auto txn = serviceContext.makeOperationContext();
WorkingSetMember wsm;
wsm.obj = Snapshotted(SnapshotId(), fromjson(doc));
wsm.transitionToOwnedObj();
BSONObj sortKey;
auto sortKeyGen = stdx::make_unique(
txn.get(), fromjson(sortSpec), fromjson(query), collator);
ASSERT_OK(sortKeyGen->getSortKey(wsm, &sortKey));
return sortKey;
}
/**
* Test function to verify that the SortKeyGenerator can generate a sortKey while using only index
* data (that is, the document will not be fetched). For SERVER-20117.
*
* sortSpec - The JSON representation of the sort spec BSONObj.
* ikd - The data stored in the index.
* collator - The collation for the sort that we are are generating keys for.
*
* Returns the BSON representation of the sort key, to be checked against the expected sort key.
*/
BSONObj extractSortKeyCovered(const char* sortSpec,
const IndexKeyDatum& ikd,
const CollatorInterface* collator) {
QueryTestServiceContext serviceContext;
auto txn = serviceContext.makeOperationContext();
WorkingSet ws;
WorkingSetID wsid = ws.allocate();
WorkingSetMember* wsm = ws.get(wsid);
wsm->keyData.push_back(ikd);
ws.transitionToRecordIdAndIdx(wsid);
BSONObj sortKey;
auto sortKeyGen =
stdx::make_unique(txn.get(), fromjson(sortSpec), BSONObj(), collator);
ASSERT_OK(sortKeyGen->getSortKey(*wsm, &sortKey));
return sortKey;
}
TEST(SortKeyGeneratorTest, SortKeyNormal) {
BSONObj actualOut = extractSortKey("{a: 1}", "{_id: 0, a: 5}", "", nullptr);
BSONObj expectedOut = BSON("" << 5);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyNormal2) {
BSONObj actualOut = extractSortKey("{a: 1}", "{_id: 0, z: 10, a: 6, b: 16}", "", nullptr);
BSONObj expectedOut = BSON("" << 6);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyString) {
BSONObj actualOut =
extractSortKey("{a: 1}", "{_id: 0, z: 'thing1', a: 'thing2', b: 16}", "", nullptr);
BSONObj expectedOut = BSON(""
<< "thing2");
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyCompound) {
BSONObj actualOut = extractSortKey(
"{a: 1, b: 1}", "{_id: 0, z: 'thing1', a: 99, c: {a: 4}, b: 16}", "", nullptr);
BSONObj expectedOut = BSON("" << 99 << "" << 16);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyEmbedded) {
BSONObj actualOut = extractSortKey(
"{'c.a': 1, b: 1}", "{_id: 0, z: 'thing1', a: 99, c: {a: 4}, b: 16}", "", nullptr);
BSONObj expectedOut = BSON("" << 4 << "" << 16);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyArray) {
BSONObj actualOut = extractSortKey(
"{'c': 1, b: 1}", "{_id: 0, z: 'thing1', a: 99, c: [2, 4, 1], b: 16}", "", nullptr);
BSONObj expectedOut = BSON("" << 1 << "" << 16);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyCoveredNormal) {
CollatorInterface* collator = nullptr;
BSONObj actualOut = extractSortKeyCovered(
"{a: 1}", IndexKeyDatum(BSON("a" << 1), BSON("" << 5), nullptr), collator);
BSONObj expectedOut = BSON("" << 5);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyCoveredEmbedded) {
CollatorInterface* collator = nullptr;
BSONObj actualOut = extractSortKeyCovered(
"{'a.c': 1}",
IndexKeyDatum(BSON("a.c" << 1 << "c" << 1), BSON("" << 5 << "" << 6), nullptr),
collator);
BSONObj expectedOut = BSON("" << 5);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyCoveredCompound) {
CollatorInterface* collator = nullptr;
BSONObj actualOut = extractSortKeyCovered(
"{a: 1, c: 1}",
IndexKeyDatum(BSON("a" << 1 << "c" << 1), BSON("" << 5 << "" << 6), nullptr),
collator);
BSONObj expectedOut = BSON("" << 5 << "" << 6);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyCoveredCompound2) {
CollatorInterface* collator = nullptr;
BSONObj actualOut = extractSortKeyCovered("{a: 1, b: 1}",
IndexKeyDatum(BSON("a" << 1 << "b" << 1 << "c" << 1),
BSON("" << 5 << "" << 6 << "" << 4),
nullptr),
collator);
BSONObj expectedOut = BSON("" << 5 << "" << 6);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyCoveredCompound3) {
CollatorInterface* collator = nullptr;
BSONObj actualOut =
extractSortKeyCovered("{b: 1, c: 1}",
IndexKeyDatum(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1),
BSON("" << 5 << "" << 6 << "" << 4 << "" << 9000),
nullptr),
collator);
BSONObj expectedOut = BSON("" << 6 << "" << 4);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, ExtractStringSortKeyWithCollatorUsesComparisonKey) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
BSONObj actualOut =
extractSortKey("{a: 1}", "{_id: 0, z: 'thing1', a: 'thing2', b: 16}", "", &collator);
BSONObj expectedOut = BSON(""
<< "2gniht");
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, CollatorHasNoEffectWhenExtractingNonStringSortKey) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
BSONObj actualOut = extractSortKey("{a: 1}", "{_id: 0, z: 10, a: 6, b: 16}", "", &collator);
BSONObj expectedOut = BSON("" << 6);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, CollatorHasNoAffectWhenExtractingCoveredSortKey) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
BSONObj actualOut = extractSortKeyCovered("{b: 1}",
IndexKeyDatum(BSON("a" << 1 << "b" << 1),
BSON("" << 4 << ""
<< "foo"),
nullptr),
&collator);
BSONObj expectedOut = BSON(""
<< "foo");
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, SortKeyGenerationForArraysUsesTheQueryPredicate) {
BSONObj actualOut =
extractSortKey("{a: -1}", "{_id: 0, a: [1, 2, 3, 4]}", "{a: {$lt: 3}}", nullptr);
BSONObj expectedOut = BSON("" << 2);
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, EnsureSortKeyGenerationForArraysRespectsCollation) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
BSONObj actualOut =
extractSortKey("{a: 1}", "{_id: 0, a: ['aaz', 'zza', 'yya', 'zzb']}", "", &collator);
BSONObj expectedOut = BSON(""
<< "ayy");
ASSERT_EQ(actualOut, expectedOut);
}
TEST(SortKeyGeneratorTest, EnsureSortKeyGenerationForArraysWithPredicateRespectsCollation) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
BSONObj actualOut = extractSortKey(
"{a: 1}", "{_id: 0, a: ['aaz', 'zza', 'yya', 'zzb']}", "{a: {$gt: 'yya'}}", &collator);
BSONObj expectedOut = BSON(""
<< "azz");
ASSERT_EQ(actualOut, expectedOut);
}
} // namespace
} // namespace mongo