0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 01:21:03 +01:00
mongodb/jstests/sharding/collation_lookup.js
2019-02-07 18:41:36 -05:00

460 lines
18 KiB
JavaScript

/**
* Tests that the $lookup stage respects the collation when the local and/or foreign collections
* are sharded.
*
* The comparison of string values between the 'localField' and 'foreignField' should use the
* collation either explicitly set on the aggregation operation, or the collation inherited from the
* collection the "aggregate" command was performed on.
*/
(function() {
"use strict";
load("jstests/aggregation/extras/utils.js"); // for arrayEq
load("jstests/noPassthrough/libs/server_parameter_helpers.js"); // For setParameterOnAllHosts.
load("jstests/libs/discover_topology.js"); // For findDataBearingNodes.
function runTests(withDefaultCollationColl, withoutDefaultCollationColl, collation) {
// Test that the $lookup stage respects the inherited collation.
let res = withDefaultCollationColl
.aggregate([{
$lookup: {
from: withoutDefaultCollationColl.getName(),
localField: "str",
foreignField: "str",
as: "matched",
},
}])
.toArray();
assert.eq(1, res.length, tojson(res));
let expected = [{_id: "lowercase", str: "abc"}, {_id: "uppercase", str: "ABC"}];
assert(arrayEq(expected, res[0].matched),
"Expected " + tojson(expected) + " to equal " + tojson(res[0].matched) +
" up to ordering");
res = withDefaultCollationColl
.aggregate([{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str1: "$str"},
pipeline: [
{$match: {$expr: {$eq: ["$str", "$$str1"]}}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str2: "$str"},
pipeline: [{$match: {$expr: {$eq: ["$str", "$$str1"]}}}],
as: "matched2"
}
}
],
as: "matched1",
},
}])
.toArray();
assert.eq(1, res.length, tojson(res));
expected = [
{
"_id": "lowercase",
"str": "abc",
"matched2": [{"_id": "lowercase", "str": "abc"}, {"_id": "uppercase", "str": "ABC"}]
},
{
"_id": "uppercase",
"str": "ABC",
"matched2":
[{"_id": "lowercase", "str": "abc"}, {"_id": "uppercase", "str": "ABC"}]
}
];
assert(arrayEq(expected, res[0].matched1),
"Expected " + tojson(expected) + " to equal " + tojson(res[0].matched1) +
" up to ordering. " + tojson(res));
// Test that the $lookup stage respects the inherited collation when it optimizes with an
// $unwind stage.
res = withDefaultCollationColl
.aggregate([
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
localField: "str",
foreignField: "str",
as: "matched",
},
},
{$unwind: "$matched"},
])
.toArray();
assert.eq(2, res.length, tojson(res));
expected = [
{_id: "lowercase", str: "abc", matched: {_id: "lowercase", str: "abc"}},
{_id: "lowercase", str: "abc", matched: {_id: "uppercase", str: "ABC"}}
];
assert(arrayEq(expected, res),
"Expected " + tojson(expected) + " to equal " + tojson(res) + " up to ordering");
res = withDefaultCollationColl
.aggregate([
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str1: "$str"},
pipeline: [
{$match: {$expr: {$eq: ["$str", "$$str1"]}}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str2: "$str"},
pipeline: [{$match: {$expr: {$eq: ["$str", "$$str1"]}}}],
as: "matched2"
}
},
{$unwind: "$matched2"},
],
as: "matched1",
},
},
{$unwind: "$matched1"},
])
.toArray();
assert.eq(4, res.length, tojson(res));
expected = [
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "lowercase",
"str": "abc",
"matched2": {"_id": "lowercase", "str": "abc"}
}
},
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "lowercase",
"str": "abc",
"matched2": {"_id": "uppercase", "str": "ABC"}
}
},
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "uppercase",
"str": "ABC",
"matched2": {"_id": "lowercase", "str": "abc"}
}
},
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "uppercase",
"str": "ABC",
"matched2": {"_id": "uppercase", "str": "ABC"}
}
}
];
assert(arrayEq(expected, res),
"Expected " + tojson(expected) + " to equal " + tojson(res) + " up to ordering");
// Test that the $lookup stage respects an explicit collation on the aggregation operation.
res = withoutDefaultCollationColl
.aggregate(
[
{$match: {_id: "lowercase"}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
localField: "str",
foreignField: "str",
as: "matched",
},
},
],
collation)
.toArray();
assert.eq(1, res.length, tojson(res));
expected = [{_id: "lowercase", str: "abc"}, {_id: "uppercase", str: "ABC"}];
assert(arrayEq(expected, res[0].matched),
"Expected " + tojson(expected) + " to equal " + tojson(res[0].matched) +
" up to ordering");
res = withoutDefaultCollationColl
.aggregate(
[
{$match: {_id: "lowercase"}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str1: "$str"},
pipeline: [
{$match: {$expr: {$eq: ["$str", "$$str1"]}}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str2: "$str"},
pipeline: [{$match: {$expr: {$eq: ["$str", "$$str1"]}}}],
as: "matched2"
}
}
],
as: "matched1",
},
}
],
collation)
.toArray();
assert.eq(1, res.length, tojson(res));
expected = [
{
"_id": "lowercase",
"str": "abc",
"matched2": [{"_id": "lowercase", "str": "abc"}, {"_id": "uppercase", "str": "ABC"}]
},
{
"_id": "uppercase",
"str": "ABC",
"matched2":
[{"_id": "lowercase", "str": "abc"}, {"_id": "uppercase", "str": "ABC"}]
}
];
assert(arrayEq(expected, res[0].matched1),
"Expected " + tojson(expected) + " to equal " + tojson(res[0].matched1) +
" up to ordering");
// Test that the $lookup stage respects an explicit collation on the aggregation operation
// when
// it optimizes with an $unwind stage.
res = withoutDefaultCollationColl
.aggregate(
[
{$match: {_id: "lowercase"}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
localField: "str",
foreignField: "str",
as: "matched",
},
},
{$unwind: "$matched"},
],
collation)
.toArray();
assert.eq(2, res.length, tojson(res));
expected = [
{_id: "lowercase", str: "abc", matched: {_id: "lowercase", str: "abc"}},
{_id: "lowercase", str: "abc", matched: {_id: "uppercase", str: "ABC"}}
];
assert(arrayEq(expected, res),
"Expected " + tojson(expected) + " to equal " + tojson(res) + " up to ordering");
res = withoutDefaultCollationColl
.aggregate(
[
{$match: {_id: "lowercase"}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str1: "$str"},
pipeline: [
{$match: {$expr: {$eq: ["$str", "$$str1"]}}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str2: "$str"},
pipeline: [{$match: {$expr: {$eq: ["$str", "$$str1"]}}}],
as: "matched2"
}
},
{$unwind: "$matched2"},
],
as: "matched1",
},
},
{$unwind: "$matched1"},
],
collation)
.toArray();
assert.eq(4, res.length, tojson(res));
expected = [
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "lowercase",
"str": "abc",
"matched2": {"_id": "lowercase", "str": "abc"}
}
},
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "lowercase",
"str": "abc",
"matched2": {"_id": "uppercase", "str": "ABC"}
}
},
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "uppercase",
"str": "ABC",
"matched2": {"_id": "lowercase", "str": "abc"}
}
},
{
"_id": "lowercase",
"str": "abc",
"matched1": {
"_id": "uppercase",
"str": "ABC",
"matched2": {"_id": "uppercase", "str": "ABC"}
}
}
];
assert(arrayEq(expected, res),
"Expected " + tojson(expected) + " to equal " + tojson(res) + " up to ordering");
// Test that the $lookup stage uses the "simple" collation if a collation isn't set on the
// collection or the aggregation operation.
res = withoutDefaultCollationColl
.aggregate([
{$match: {_id: "lowercase"}},
{
$lookup: {
from: withDefaultCollationColl.getName(),
localField: "str",
foreignField: "str",
as: "matched",
},
},
])
.toArray();
assert.eq([{_id: "lowercase", str: "abc", matched: [{_id: "lowercase", str: "abc"}]}], res);
res = withoutDefaultCollationColl
.aggregate([
{$match: {_id: "lowercase"}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str1: "$str"},
pipeline: [
{$match: {$expr: {$eq: ["$str", "$$str1"]}}},
{
$lookup: {
from: withoutDefaultCollationColl.getName(),
let : {str2: "$str"},
pipeline: [{$match: {$expr: {$eq: ["$str", "$$str1"]}}}],
as: "matched2"
}
},
{$unwind: "$matched2"},
],
as: "matched1",
},
},
])
.toArray();
assert.eq([{
"_id": "lowercase",
"str": "abc",
"matched1": [{
"_id": "lowercase",
"str": "abc",
"matched2": {"_id": "lowercase", "str": "abc"}
}]
}],
res);
}
const st = new ShardingTest({shards: 2, config: 1});
setParameterOnAllHosts(
DiscoverTopology.findNonConfigNodes(st.s), "internalQueryAllowShardedLookup", true);
const testName = "collation_lookup";
const caseInsensitive = {collation: {locale: "en_US", strength: 2}};
const mongosDB = st.s0.getDB(testName);
const withDefaultCollationColl = mongosDB[testName + "_with_default"];
const withoutDefaultCollationColl = mongosDB[testName + "_without_default"];
assert.commandWorked(
mongosDB.createCollection(withDefaultCollationColl.getName(), caseInsensitive));
assert.writeOK(withDefaultCollationColl.insert({_id: "lowercase", str: "abc"}));
assert.writeOK(withoutDefaultCollationColl.insert({_id: "lowercase", str: "abc"}));
assert.writeOK(withoutDefaultCollationColl.insert({_id: "uppercase", str: "ABC"}));
assert.writeOK(withoutDefaultCollationColl.insert({_id: "unmatched", str: "def"}));
//
// Sharded collection with default collation and unsharded collection without a default
// collation.
//
assert.commandWorked(
withDefaultCollationColl.createIndex({str: 1}, {collation: {locale: "simple"}}));
// Enable sharding on the test DB and ensure its primary is shard0000.
assert.commandWorked(mongosDB.adminCommand({enableSharding: mongosDB.getName()}));
st.ensurePrimaryShard(mongosDB.getName(), st.shard0.shardName);
// Shard the collection with a default collation.
assert.commandWorked(mongosDB.adminCommand({
shardCollection: withDefaultCollationColl.getFullName(),
key: {str: 1},
collation: {locale: "simple"}
}));
// Split the collection into 2 chunks.
assert.commandWorked(mongosDB.adminCommand(
{split: withDefaultCollationColl.getFullName(), middle: {str: "abc"}}));
// Move the chunk containing {str: "abc"} to shard0001.
assert.commandWorked(mongosDB.adminCommand({
moveChunk: withDefaultCollationColl.getFullName(),
find: {str: "abc"},
to: st.shard1.shardName
}));
runTests(withDefaultCollationColl, withoutDefaultCollationColl, caseInsensitive);
// TODO: Enable the following tests once SERVER-32536 is fixed.
//
// Sharded collection with default collation and sharded collection without a default
// collation.
//
// Shard the collection without a default collation.
// assert.commandWorked(mongosDB.adminCommand({
// shardCollection: withoutDefaultCollationColl.getFullName(),
// key: {_id: 1},
// }));
// // Split the collection into 2 chunks.
// assert.commandWorked(mongosDB.adminCommand(
// {split: withoutDefaultCollationColl.getFullName(), middle: {_id: "unmatched"}}));
// // Move the chunk containing {_id: "lowercase"} to shard0001.
// assert.commandWorked(mongosDB.adminCommand({
// moveChunk: withoutDefaultCollationColl.getFullName(),
// find: {_id: "lowercase"},
// to: st.shard1.shardName
// }));
// runTests(withDefaultCollationColl, withoutDefaultCollationColl, caseInsensitive);
st.stop();
})();