summaryrefslogtreecommitdiff
path: root/jstests/core/views/views_distinct.js
blob: 0a0b6002b150e550d7e643456724e9e7bddbfd96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Test the distinct command with views.
(function() {
    "use strict";

    // For arrayEq. We don't use array.eq as it does an ordered comparison on arrays but we don't
    // care about order in the distinct response.
    load("jstests/aggregation/extras/utils.js");

    var viewsDB = db.getSiblingDB("views_distinct");
    assert.commandWorked(viewsDB.dropDatabase());

    // Populate a collection with some test data.
    let allDocuments = [];
    allDocuments.push({_id: "New York", state: "NY", pop: 7});
    allDocuments.push({_id: "Newark", state: "NJ", pop: 3});
    allDocuments.push({_id: "Palo Alto", state: "CA", pop: 10});
    allDocuments.push({_id: "San Francisco", state: "CA", pop: 4});
    allDocuments.push({_id: "Trenton", state: "NJ", pop: 5});

    let coll = viewsDB.getCollection("coll");
    let bulk = coll.initializeUnorderedBulkOp();
    allDocuments.forEach(function(doc) {
        bulk.insert(doc);
    });
    assert.writeOK(bulk.execute());

    // Create views on the data.
    assert.commandWorked(viewsDB.runCommand({create: "identityView", viewOn: "coll"}));
    assert.commandWorked(viewsDB.runCommand(
        {create: "largePopView", viewOn: "identityView", pipeline: [{$match: {pop: {$gt: 5}}}]}));
    let identityView = viewsDB.getCollection("identityView");
    let largePopView = viewsDB.getCollection("largePopView");

    function assertIdentityViewDistinctMatchesCollection(key, query) {
        query = (query === undefined) ? {} : query;
        const collDistinct = coll.distinct(key, query);
        const viewDistinct = identityView.distinct(key, query);
        assert(arrayEq(collDistinct, viewDistinct),
               "Distinct on a collection did not match distinct on its identity view; got " +
                   tojson(viewDistinct) + " but expected " + tojson(collDistinct));
    }

    // Test basic distinct requests on known fields without a query.
    assertIdentityViewDistinctMatchesCollection("pop");
    assertIdentityViewDistinctMatchesCollection("_id");
    assert(arrayEq([7, 10], largePopView.distinct("pop")));
    assert(arrayEq(["New York", "Palo Alto"], largePopView.distinct("_id")));

    // Test distinct with the presence of a query.
    assertIdentityViewDistinctMatchesCollection("state", {});
    assertIdentityViewDistinctMatchesCollection("pop", {pop: {$exists: true}});
    assertIdentityViewDistinctMatchesCollection("state", {pop: {$gt: 3}});
    assertIdentityViewDistinctMatchesCollection("_id", {state: "CA"});
    assert(arrayEq(["CA"], largePopView.distinct("state", {pop: {$gte: 8}})));
    assert(arrayEq([7], largePopView.distinct("pop", {state: "NY"})));

    // Test distinct where we expect an empty set response.
    assertIdentityViewDistinctMatchesCollection("nonexistent");
    assertIdentityViewDistinctMatchesCollection("pop", {pop: {$gt: 1000}});
    assert.eq([], largePopView.distinct("nonexistent"));
    assert.eq([], largePopView.distinct("_id", {state: "FL"}));

    // Explain works with distinct.
    assert.commandWorked(identityView.explain().distinct("_id"));
    assert.commandWorked(largePopView.explain().distinct("pop", {state: "CA"}));
    let explainPlan = largePopView.explain().count({foo: "bar"});
    assert.commandWorked(explainPlan);
    assert.eq(explainPlan["stages"][0]["$cursor"]["queryPlanner"]["namespace"],
              "views_distinct.coll");

    // Distinct with explicit explain modes works on a view.
    explainPlan = assert.commandWorked(largePopView.explain("queryPlanner").distinct("pop"));
    assert.eq(explainPlan.stages[0].$cursor.queryPlanner.namespace, "views_distinct.coll");
    assert(!explainPlan.stages[0].$cursor.hasOwnProperty("executionStats"));

    explainPlan = assert.commandWorked(largePopView.explain("executionStats").distinct("pop"));
    assert.eq(explainPlan.stages[0].$cursor.queryPlanner.namespace, "views_distinct.coll");
    assert(explainPlan.stages[0].$cursor.hasOwnProperty("executionStats"));
    assert.eq(explainPlan.stages[0].$cursor.executionStats.nReturned, 2);
    assert(!explainPlan.stages[0].$cursor.executionStats.hasOwnProperty("allPlansExecution"));

    explainPlan = assert.commandWorked(largePopView.explain("allPlansExecution").distinct("pop"));
    assert.eq(explainPlan.stages[0].$cursor.queryPlanner.namespace, "views_distinct.coll");
    assert(explainPlan.stages[0].$cursor.hasOwnProperty("executionStats"));
    assert.eq(explainPlan.stages[0].$cursor.executionStats.nReturned, 2);
    assert(explainPlan.stages[0].$cursor.executionStats.hasOwnProperty("allPlansExecution"));

    // Distinct commands fail when they try to change the collation of a view.
    assert.commandFailedWithCode(
        viewsDB.runCommand({distinct: "identityView", key: "state", collation: {locale: "en_US"}}),
        ErrorCodes.OptionNotSupportedOnView);

    // Test distinct on nested objects, nested arrays and nullish values.
    coll.drop();
    allDocuments = [];
    allDocuments.push({a: 1, b: [2, 3, [4, 5], {c: 6}], d: {e: [1, 2]}});
    allDocuments.push({a: [1], b: [2, 3, 4, [5]], c: 6, d: {e: 1}});
    allDocuments.push({a: [1, 2], b: 3, c: [6], d: [{e: 1}, {e: [1, 2]}]});
    allDocuments.push({a: [1, 2], b: [4, 5], c: [undefined], d: [1]});
    allDocuments.push({a: null, b: [4, 5, null, undefined], c: [], d: {e: null}});
    allDocuments.push({a: undefined, b: null, c: [null], d: {e: undefined}});

    bulk = coll.initializeUnorderedBulkOp();
    allDocuments.forEach(function(doc) {
        bulk.insert(doc);
    });
    assert.writeOK(bulk.execute());

    assertIdentityViewDistinctMatchesCollection("a");
    assertIdentityViewDistinctMatchesCollection("b");
    assertIdentityViewDistinctMatchesCollection("c");
    assertIdentityViewDistinctMatchesCollection("d");
    assertIdentityViewDistinctMatchesCollection("e");
}());