// SERVER-2326 - make sure that sharding only works with unique indices
(function() {

var s = new ShardingTest({name: "shard_index", shards: 2, mongos: 1});

// Regenerate fully because of SERVER-2782
for (var i = 0; i < 22; i++) {
    var coll = s.admin._mongo.getDB("test").getCollection("foo" + i);
    coll.drop();

    var bulk = coll.initializeUnorderedBulkOp();
    for (var j = 0; j < 300; j++) {
        bulk.insert({num: j, x: 1});
    }
    assert.commandWorked(bulk.execute());

    if (i == 0) {
        s.adminCommand({enablesharding: "" + coll._db});
        s.ensurePrimaryShard(coll.getDB().getName(), s.shard1.shardName);
    }

    print("\n\n\n\n\nTest # " + i);

    if (i == 0) {
        // Unique index exists, but not the right one.
        coll.createIndex({num: 1}, {unique: true});
        coll.createIndex({x: 1});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not shard collection when another unique index exists!");
    }
    if (i == 1) {
        // Unique index exists as prefix, also index exists
        coll.createIndex({x: 1});
        coll.createIndex({x: 1, num: 1}, {unique: true});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard non-unique index without unique option.");
        }
    }
    if (i == 2) {
        // Non-unique index exists as prefix, also index exists.  No unique index.
        coll.createIndex({x: 1});
        coll.createIndex({x: 1, num: 1});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
            passed = true;

        } catch (e) {
            print(e);
            assert(
                !passed,
                "Should be able to shard collection with no unique index if unique not specified.");
        }
    }
    if (i == 3) {
        // Unique index exists as prefix, also unique index exists
        coll.createIndex({num: 1}, {unique: true});
        coll.createIndex({num: 1, x: 1}, {unique: true});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}, unique: true});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard collection with unique prefix index.");
        }
    }
    if (i == 4) {
        // Unique index exists as id, also unique prefix index exists
        coll.createIndex({_id: 1, num: 1}, {unique: true});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {_id: 1}, unique: true});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard collection with unique id index.");
        }
    }
    if (i == 5) {
        // Unique index exists as id, also unique prefix index exists
        coll.createIndex({_id: 1, num: 1}, {unique: true});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {_id: 1, num: 1}, unique: true});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard collection with unique combination id index.");
        }
    }
    if (i == 6) {
        coll.remove({});

        // Unique index does not exist, also unique prefix index exists
        coll.createIndex({num: 1, _id: 1}, {unique: true});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}, unique: true});
        } catch (e) {
            print(e);
            assert(
                false,
                "Should be able to shard collection with no unique index but with a unique prefix index.");
        }

        printjson(coll.getIndexes());

        // Make sure the index created is unique!
        assert.eq(1,
                  coll.getIndexes()
                      .filter(function(z) {
                          return friendlyEqual(z.key, {num: 1}) && z.unique;
                      })
                      .length);
    }
    if (i == 7) {
        coll.remove({});

        // No index exists

        try {
            assert.eq(coll.find().itcount(), 0);
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard collection with no index on shard key.");
        }
    }
    if (i == 8) {
        coll.remove({});

        // No index exists

        passed = false;
        try {
            assert.eq(coll.find().itcount(), 0);
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}, unique: true});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(
            passed,
            "Should be able to shard collection with unique flag but with no unique index on shard key, if coll empty.");

        printjson(coll.getIndexes());

        // Make sure the index created is unique!
        assert.eq(1,
                  coll.getIndexes()
                      .filter(function(z) {
                          return friendlyEqual(z.key, {num: 1}) && z.unique;
                      })
                      .length);
    }
    if (i == 9) {
        // Unique index exists on a different field as well
        coll.createIndex({num: 1}, {unique: true});
        coll.createIndex({x: 1});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not shard collection when another unique index exists!");
    }
    if (i == 10) {
        // try sharding non-empty collection without any index
        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard without index");

        // now add containing index and try sharding by prefix
        coll.createIndex({num: 1, x: 1});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(passed, "Should be able to shard collection with prefix of existing index");

        printjson(coll.getIndexes());

        // make sure no extra index is created
        assert.eq(2, coll.getIndexes().length);
    }
    if (i == 11) {
        coll.remove({});

        // empty collection with useful index. check new index not created
        coll.createIndex({num: 1, x: 1});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(passed, "Should be able to shard collection with prefix of existing index");

        printjson(coll.getIndexes());

        // make sure no extra index is created
        assert.eq(2, coll.getIndexes().length);
    }
    if (i == 12) {
        // check multikey values for x make index unusable for shard key
        coll.save({num: 100, x: [2, 3]});
        coll.createIndex({num: 1, x: 1});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard collection with mulikey index");
    }
    if (i == 13) {
        coll.save({num: [100, 200], x: 10});
        coll.createIndex({num: 1, x: 1});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard collection with mulikey index");
    }
    if (i == 14) {
        coll.save({num: 100, x: 10, y: [1, 2]});
        coll.createIndex({num: 1, x: 1, y: 1});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard collection with mulikey index");
    }
    if (i == 15) {
        // try sharding with a hashed index
        coll.createIndex({num: "hashed"});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: "hashed"}});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard collection with hashed index.");
        }
    }
    if (i == 16) {
        // create hashed index, but try to declare it unique when sharding
        coll.createIndex({num: "hashed"});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: "hashed"}, unique: true});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to declare hashed shard key unique.");
    }
    if (i == 17) {
        // create hashed index, but unrelated unique index present
        coll.createIndex({x: "hashed"});
        coll.createIndex({num: 1}, {unique: true});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: "hashed"}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard on hashed index with another unique index");
    }
    if (i == 18) {
        // create hashed index, and a regular unique index exists on same field
        coll.createIndex({num: "hashed"});
        coll.createIndex({num: 1}, {unique: true});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {num: "hashed"}});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard coll with hashed and regular unique index");
        }
    }
    if (i == 19) {
        // Create sparse index.
        coll.createIndex({x: 1}, {sparse: true});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard coll with sparse index");
    }
    if (i == 20) {
        // Create partial index.
        coll.createIndex({x: 1}, {filter: {num: {$gt: 1}}});

        passed = false;
        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
            passed = true;
        } catch (e) {
            print(e);
        }
        assert(!passed, "Should not be able to shard coll with partial index");
    }
    if (i == 21) {
        // Ensure that a collection with a normal index and a partial index can be sharded,
        // where
        // both are prefixed by the shard key.

        coll.createIndex({x: 1, num: 1}, {filter: {num: {$gt: 1}}});
        coll.createIndex({x: 1, num: -1});

        try {
            s.adminCommand({shardcollection: "" + coll, key: {x: 1}});
        } catch (e) {
            print(e);
            assert(false, "Should be able to shard coll with regular and partial index");
        }
    }
}

s.stop();
})();
