// 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.writeOK(bulk.execute());

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

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

        if (i == 0) {
            // Unique index exists, but not the right one.
            coll.ensureIndex({num: 1}, {unique: true});
            coll.ensureIndex({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.ensureIndex({x: 1});
            coll.ensureIndex({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.ensureIndex({x: 1});
            coll.ensureIndex({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.ensureIndex({num: 1}, {unique: true});
            coll.ensureIndex({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.ensureIndex({_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.ensureIndex({_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.ensureIndex({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.ensureIndex({num: 1}, {unique: true});
            coll.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({x: "hashed"});
            coll.ensureIndex({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.ensureIndex({num: "hashed"});
            coll.ensureIndex({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.ensureIndex({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.ensureIndex({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.ensureIndex({x: 1, num: 1}, {filter: {num: {$gt: 1}}});
            coll.ensureIndex({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();

})();
