I want to be able to project to another type when querying MongoDB using the C# SDK.
For example, below I want to query the collection using builder filters (or LINQ expressions) for MyType
, but I want to project the result to MySubType
.
var mySubTypes = Database
.GetCollection<MyType>("MyCollection")
.Find(Builders<MyType>.Filter.AnyIn(x => x.Documents, documentId))
.ProjectTo<MySubType>() // Project to another type??
.ToList();
One could imagine MySubType
to be a subset of MyType
, represented using inheritance:
public class MyType : MySubType
{
[BsonElement("documents")]
public List<string> Documents { get; set; }
}
public class MySubType
{
[BsonElement("name")]
public string Name { get; set; }
}
Why do I want to do this?
Because the Documents
array is very large and used purely during querying (i.e. to filter) within the database engine. Retrieving and serializing this array will be an unnecessary cost.
I have found the way to execute mapping you want:
collection
.Find(Builders<MyType>.Filter.AnyIn(x => x.Documents, new[] { "c" }))
.Project(Builders<MyType>.Projection.Exclude(c => c.Documents))
.As<MySubType>()
.ToList();
But first you should register mapping for your SubType with ignoring extra element. I don't understand it 100%, seems to be a bug of the driver, it doesn't get Documents
from mongo, but knows, that MyType
has such property. Note, you should register your class mapping, before you first create collection of this type.
if (!BsonClassMap.IsClassMapRegistered(typeof(MySubType)))
{
BsonClassMap.RegisterClassMap<MySubType>(cm =>
{
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
}
I did it with sample data :
var toInsert = new List<MyType>
{
new MyType {Id = 1, Name = "bla", Documents =new List<string> {"a", "b", "v"}},
new MyType {Id = 2, Name = "ada", Documents =new List<string> {"c", "d", "r"}},
};
And could get the expected output:
collection
.Find(Builders<MyType>.Filter.AnyIn(x => x.Documents, new[] { "c" }))
.Project(Builders<MyType>.Projection.Exclude(c => c.Documents))
.As<MySubType>()
.ToList()
.Dump();