Projection in Where Clause Query of a Embedded document in MongoDB Collection using C#

故事扮演 提交于 2019-12-02 18:22:32

问题


Filter the Collection in DB instead of Memory

I'm having a Model Class, Save it in a MongoDB Collection then Query the same as per my expectation mentioned below.

My Model Class:

public Class Employee
{
    public ObjectId Id { get; set; }
    public string EmpID { get; set; }
    public string EmpName { get; set; }
    public List<Mobile> EmpMobile { get; set; }
    public bool IsLive { get; set; }
}

public Class Mobile
{
    public string MobID { get; set; }
    public string MobNumber { get; set; }
    public bool IsPreferred { get; set; }
    public bool IsLive { get; set; }
}

The Values are

List<Employee> EmpInfo = new List<Employee>() {
new Employee()
{
    EmpID = "100",
    EmpName = "John",
    EmpMobile = new List<Mobile>()
    {
        new Mobile() { MobNumber = "55566610", IsPreferred = true, IsLive = false },
        new Mobile() { MobNumber = "55566611", IsPreferred = false, IsLive = true },
    },
    IsLive = true
},

new Employee()
{
    EmpID = "101",
    EmpName = "Peter",
    EmpMobile = new List<Mobile>()
    {
        new Mobile() { MobNumber = "55566610", IsPreferred = true, IsLive = false },
        new Mobile() { MobNumber = "55566611", IsPreferred = false, IsLive = false },
    },
    IsLive = true
},

new Employee()
{
    EmpID = "102",
    EmpName = "Jack",
    EmpMobile = new List<Mobile>()
    {
        new Mobile() { MobNumber = "55566610", IsPreferred = true, IsLive = true },
        new Mobile() { MobNumber = "55566611", IsPreferred = false, IsLive = true },
    },
    IsLive = false
}

}

collectionEmpInfo.InsertMany(EmpInfo);
var empList = collectionEmpInfo.Find(new BsonDocument()).ToList();

Now I wish to Select Only EmpInfo.IsLive == true inside the embedded document I need only EmpInfo.EmpMobile.IsLive == true satisfied Mobile documents

My Expected Output:

List<Employee> EmpInfo = new List<Employee>() {
new Employee()
{
    EmpID = "100",
    EmpName = "John",
    EmpMobile = new List<Mobile>()
    {
        new Mobile() { MobNumber = "55566611", IsPreferred = false, IsLive = true }
    },
    IsLive = true
},

new Employee()
{
    EmpID = "101",
    EmpName = "Peter",
    EmpMobile = new List<Mobile>()
    {

    },
    IsLive = true
}

}

Kindly assist me how to write a Where Clause Query for my expected output using c# MongoDB.

Note: Filter the Collection in DB instead of Memory

My MongoDB Libraries and Connections are

IMongoClient _client = new MongoClient();
IMongoDatabase _database = _client.GetDatabase("Test");

回答1:


EDIT

Added projection - so selected array contains only documents where IsLive==true

I think it is easier to use typed queries as c# is strongly typed language. I used ElemMatch as this is designed to scan an array and looks for a matching element.

var filterDef = new FilterDefinitionBuilder<Employee>();
var filter = filterDef.Eq(x => x.IsLive, true);

var projectDef = new ProjectionDefinitionBuilder<Employee>();
var projection = projectDef.ElemMatch<Mobile>("EmpMobile", "{IsLive:true}");            

var empList = collectionEmpInfo.Find(filter).Project<Employee>(projection).ToList();



回答2:


I think this would solve the problem that you have:

var collection = _database.GetCollection<Employee>("employee"); // (1)

var filterBuilder = Builders<BsonDocument>.Filter;
var filter = filterBuilder.Eq("IsLive", true) & filterBuilder.Eq("EmpMobile.IsLive", true); // (2)

var results = await collection.FindAsync(filter).ToListAsync(); // (3)

(1): You will need to change the collection name with the collection name that has the data you want to query. Also, notice that it requests a TDocument as the generic parameter. It seems that Employee does not inherit from TDocument, but you can do so or you can create another DTO class for this purpose.

(2): You can combine conditions by using the bit AND operator (&).

Also, you can query directly against internal values of an array (which you have in your classes as a list). As it turns out, it will return the document if any of the array values satisfies the condition. In your example, this should return the document for EmpIDs 100, but it will contain both of the MobileNumbers in it. You retrieved the document that satisfied the condition, but you retrieved the document in its entirety.

(3) Finally, rendering the results to a list so you have them in memory. Alternatively, you can walk through the results using cursor.MoveNextAsync(), but this will keep your connection to MongoDB open longer.

You can find most of the information with examples in the Find or Query Data with C# Driver of the MongoDB docs.




回答3:


You have several int for your approach.

first you are using collectionEmpInfo.InsertOne(EmpInfo); i am assuming you want to use InsertMany instead.

as for filtering over the collection you have to know that your filter will affect if the whole object is retrieved, so adding a filter on an embedded array within an entity will not filter the array but determine if the whole object is retrieved or not based on whether it matches the query condition on the embedded array. My suggestion is to only apply the filter over the employees in the query and filter the result set in memory.

var filter = filterBuilder.Eq("IsLive", true);

var results = await collection.FindAsync(filter).ToListAsync();

now filter the results collection in memory as in

var filteredResults = results.ForEach(employee => employee.EmpMobile = employee.EmpMobile.Where(mob => mob.isLive).ToList());


来源:https://stackoverflow.com/questions/37801269/projection-in-where-clause-query-of-a-embedded-document-in-mongodb-collection-us

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!