/**
 * Tests that a $** index can support queries which test for equality to empty nested objects.
 *
 * @tags: [
 *   does_not_support_stepdowns,
 * ]
 */
import {FeatureFlagUtil} from "jstests/libs/feature_flag_util.js";

const coll = db.wildcard_index_equality_to_empty_obj;
coll.drop();

assert.commandWorked(coll.insert([
    {_id: 0},
    {_id: 1, a: null},
    {_id: 2, a: []},
    {_id: 3, a: {}},
    {_id: 4, a: [{}]},
    {_id: 5, a: [[{}]]},
    {_id: 6, a: [1, 2, {}]},
    {_id: 7, a: {b: 1}},
    {_id: 8, a: 3},
    {_id: 9, a: {b: {}}},
    {_id: 10, a: [0, {b: {}}]},
]));

const wildcardIndexes =
    [{keyPattern: {"$**": 1}}, {keyPattern: {"$**": 1, b: 1}, wildcardProjection: {b: 0}}];
for (const indexSpec of wildcardIndexes) {
    const option = {};
    if (indexSpec.wildcardProjection) {
        option['wildcardProjection'] = indexSpec.wildcardProjection;
    }
    assert.commandWorked(coll.createIndex(indexSpec.keyPattern, option));

    // Test that a comparison to empty object query returns the expected results when the $** index
    // is hinted.
    let results = coll.find({a: {}}, {_id: 1}).sort({_id: 1}).hint(indexSpec.keyPattern).toArray();
    assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}]);

    // Result set should be the same as when hinting a COLLSCAN and with no hint.
    assert.eq(results, coll.find({a: {}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray());
    assert.eq(results, coll.find({a: {}}, {_id: 1}).sort({_id: 1}).toArray());

    // Repeat the above query, but express it using $lte:{}, which is a synonym for $eq:{}.
    results =
        coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).hint(indexSpec.keyPattern).toArray();
    assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}]);
    assert.eq(results,
              coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray());
    assert.eq(results, coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).toArray());

    // Test that an inequality to empty object query results in an error when the $** index is
    // hinted.
    assert.throws(() => coll.find({a: {$gte: {}}}, {_id: 1})
                            .sort({_id: 1})
                            .hint(indexSpec.keyPattern)
                            .toArray());

    // Test that an inequality to empty object query returns the expected results in the presence of
    // the $** index.
    results = coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).toArray();
    assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}, {_id: 7}, {_id: 9}, {_id: 10}]);

    // Result set should be the same as when hinting a COLLSCAN and with no hint.
    assert.eq(results,
              coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray());
    assert.eq(results, coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).toArray());

    // Test that an $in with an empty object returns the expected results when the $** index is
    // hinted.
    results = coll.find({a: {$in: [3, {}]}}, {_id: 1})
                  .sort({_id: 1})
                  .hint(indexSpec.keyPattern)
                  .toArray();
    assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}, {_id: 8}]);

    // Result set should be the same as when hinting a COLLSCAN and with no hint.
    assert.eq(
        results,
        coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray());
    assert.eq(results, coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).toArray());

    // Test that a wildcard index can support equality to an empty object on a dotted field.
    results =
        coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).hint(indexSpec.keyPattern).toArray();
    assert.eq(results, [{_id: 9}, {_id: 10}]);

    // Result set should be the same as when hinting a COLLSCAN and with no hint.
    assert.eq(results,
              coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray());
    assert.eq(results, coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).toArray());
}
