问题
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 EmpID
s 100, but it will contain both of the MobileNumber
s 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