How to create MongoDB MultiKey index on attribute of items in an array .NET Driver

Ivan picture Ivan · May 19, 2017 · Viewed 7.8k times · Source

I have a MongoDB collection "foos" containing items which each have an array of "bars". That is, "foo" has the following schema:

{
    "id": UUID
    "name": string
    ...
    "bars": [
        "id": UUID
        "key": string
        ...
    ]
}

I need to create an index on name and bar.key using the MongoDB C# .NET Mongo driver.

I presumed I could use a Linq Select function to do this as follows:

Indexes.Add(Context.Collection<FooDocument>().Indexes.CreateOne(
    Builders<FooDocument>.IndexKeys
        .Descending(x => x.Bars.Select(y => y.Key))));

However this results in an InvalidOperationException:

System.InvalidOperationException: 'Unable to determine the serialization information for x => x.Bars.Select(y => y.Id).'

The Mongo documentation on MultiKey indexes shows how to create such an index using simple dot notation, i.e.

db.foos.createIndex( { "name": 1, "bars.key": 1 } )

However the MongoDB driver documentation seems to suggest that using a Linq function as I'm doing is correct.

How can I create a multikey index on my collection using the MongoDB .NET Driver, preferably using a Linq function?

Answer

Artyom picture Artyom · May 19, 2017

This is an example how to do it with C#

var indexDefinition = Builders<FooDocument>.IndexKeys.Combine(
    Builders<FooDocument>.IndexKeys.Ascending(f => f.Key1),
    Builders<FooDocument>.IndexKeys.Ascending(f => f.Key2));

await collection.Indexes.CreateOneAsync(indexDefinition); 

UPDATE

Regarding index within the array, closest what i was able to find is to use "-1" as index whene you building your index key. As i understand from github source code is is a valid option in case of building queries.

var indexDefinition = Builders<FooDocument>.IndexKeys.Combine(
    Builders<FooDocument>.IndexKeys.Ascending(f => f.Key1),
    Builders<FooDocument>.IndexKeys.Ascending(f => f.Key2[-1].Key));

await collection.Indexes.CreateOneAsync(indexDefinition); 

"-1" is a hardcoded constant in side mongodb C# drivers which means "$" (proof). So this code would try to create index:

{ "Key1": 1, "Key2.$.Key": 1 }

which is fine for querying info from database, but not allowed (will throw an exception "Index key contains an illegal field name: field name starts with '$'") to use in indexes. So i assume it should be changed in mongodb drivers to make it work. Something like "-2" means empty operator. In that case we could use

var indexDefinition = Builders<FooDocument>.IndexKeys.Combine(
    Builders<FooDocument>.IndexKeys.Ascending(f => f.Key1),
    Builders<FooDocument>.IndexKeys.Ascending(f => f.Key2[-2].Key));

await collection.Indexes.CreateOneAsync(indexDefinition); 

which would generate index like:

{ "Key1": 1, "Key2.Key": 1 }

So basically i don't think it is possible right now to build index you want with pure Linq without changing mongo C# drivers.

So i think your only option do like this, still C# but without Linq

await collection.Indexes.CreateOneAsync(new BsonDocument {{"name", 1}, {"bars.key", 1}});