How to aggregate $lookup with MongoDB C# driver? - c#

I am using below lookup to get all required documents & then apply LINQ query to find specific documents & it is working fine as expected.
new BsonDocument("$lookup", new BsonDocument()
.Add("from", "JournalInstructionReplication")
.Add("localField", "_id")
.Add("foreignField", "_eid")
.Add("as", "Replicated"))
var cursor = await collection.AggregateAsync(pipeline, options);
List<BsonDocument> list = cursor.ToList();
var failedDocs = list.Where(d => d["Replicated"][0]["lastReplicationStatus"] != "success" ||
d["Replicated"][0]["eSeq"] != d["Replicated"][0]["lastReplicationSequence"])
.ToList();
I want to merge above LINQ query with existing lookup query.
Any idea how to achieve with lookup?

To avoid running [0] you can run $unwind since there's clearly 1:1 relationship between joined collections (you join them by _id). The in-memory part has to be converted into below $match:
{
$match: {
$expr: {
$or: [
{ $ne: [ "$Replicated.lastReplicationStatus", "success" ] },
{ $ne: [ "$Replicated.eSeq", "$Replicated.lastReplicationSequence" ] },
]
}
}
}
which would look like below in C#:
var q = _channels.Aggregate()
.Lookup(
foreignCollectionName: "JournalInstructionReplication",
localField: "_id",
foreignField:"_id",
#as: "Replicated")
.Unwind("Replicated")
.Match(new BsonDocument()
{
{ "$expr", new BsonDocument()
{
{ "$or", new BsonArray()
{
new BsonDocument(){{ "$ne", new BsonArray(){ "$Replicated.lastReplicationStatus", "success" } }},
new BsonDocument(){{ "$ne", new BsonArray(){ "$Replicated.eSeq", "$Replicated.lastReplicationSequence" } }
} }
} }
}});
var result = q.ToList();

Thanks a lot. It worked as expected.Final working code as below:
new BsonDocument("$lookup", new BsonDocument()
.Add("from", "JournalInstructionReplication")
.Add("localField", "_id")
.Add("foreignField", "_eid")
.Add("as", "Replicated")),
new BsonDocument("$unwind", new BsonDocument()
.Add("path", "$Replicated")),
new BsonDocument("$match", new BsonDocument()
.Add("$expr", new BsonDocument()
.Add("$or", new BsonArray()
.Add(new BsonDocument()
.Add("$ne", new BsonArray()
.Add("$Replicated.lastReplicationStatus")
.Add("success")
)
)
.Add(new BsonDocument()
.Add("$ne", new BsonArray()
.Add("$Replicated.eSeq")
.Add("$Replicated.lastReplicationSequence")
)
)
)
))
};
var cursor = await collection.AggregateAsync(pipeline, options);
List<BsonDocument> list = cursor.ToList();

Related

Multi phrase search in C# LINQ Cannot be translated to SQL

I'm trying to write a semi-advanced LINQ to SQL query to search through my entities in a .NET 6 project. I want to give the user the ability to enter something like "user search a OR alernative lookup b OR seperate query from joe" into a search. After handling the user input, my LINQ query is set up below:
bool isNameSearch = true;
bool isNoteSearch = true;
List<List<string>> _multiSearchList = new()
{
new List<string>() { "user", "search", "a" },
new List<string>() { "alternative", "lookup", "b" },
new List<string>() { "seperate", "query", "from", "joe" }
};
var _query = (from tblHeader in _DbContext.batches
where tblHeader.isDeleted != true
select tblHeader)
_query = _query.Where(x =>
_multiSearchList.Any(y =>
y.All(z =>
(isNameSearch && x.Name.Contains(z)) ||
(isNoteSearch && x.Note.Contains(z))
)
)
);
var _results = await _query.ToListAsync();
var _totalCount = await _query.CountAsync();
The problem is that although this query works with a local collection, it can't be translated to sql. Is there a way to accomplish this?
Since EF Core do not support queries with local collections (exception is Contains), I would suggest using LINQKit which has PredicateBuilder.
Then you can build predicate for your specific case:
bool isNameSearch = true;
bool isNoteSearch = true;
List<List<string>> _multiSearchList = new()
{
new List<string>() { "user", "search", "a" },
new List<string>() { "alternative", "lookup", "b" },
new List<string>() { "seperate", "query", "from", "joe" }
};
var _query = (from tblHeader in _DbContext.batches
where tblHeader.isDeleted != true
select tblHeader)
var mainPredicate = PredicateBuilder.New(_query);
foreach (var sentense in _multiSearchList)
[
var predicate = PredicateBuilder.New(_query);
foreach(var word in sentense)
[
var subPredicate = PredicateBuilder.New(_query);
if (isNameSearch)
subPredicate = subPredicate.Or(x => x.Name.Contains(word));
if (isNoteSearch)
subPredicate = subPredicate.Or(x => x.Note.Contains(word));
predicate = predicate.And(subPredicate)
]
mainPredicate = mainPredicate.Or(predicate);
]
_query = _query.Where(mainPredicate);

How to transform the following query in Mongo using C#

I can run it in Robomongo, but I do not know how to do this in C #
db.mycollection.aggregate([
{$match : { "Partner._id": {$in: ["acb123","def456"]}}},
{$group : {_id: {status: "$Status"}, count: {$sum: 1}}}
])
You can use fluent aggregation and simply parse BsonDocuments from JSON strings:
var result = await mycollection.Aggregate()
.Match(BsonDocument.Parse("{ 'Partner._id': {$in: ['acb123','def456']} }"))
.Group(BsonDocument.Parse("{ _id: '$Status', count: {$sum: 1} }"))
.ToListAsync();
Or you can build BsonDocuments manually:
var result = await mycollection.Aggregate()
.Match(
new BsonDocument {
{"Partner._id", new BsonDocument("$in", new BsonArray(new []{"acb123","def456"}))}
}
).Group(
new BsonDocument {
{ "_id", "$Status" }, // thus you group by single field
{ "count", new BsonDocument("$sum", 1) }
}
).ToListAsync();

How to use "Or" statement in MongoDB C# Driver?

I am facing problem while Querying the following in MongoDB C#. My code in mongo client is
db.collection.find( { $or: [ { quantity: { $lt: 20 } }, { price: 10 } ,{price:100},{name:"x"}] } )
but how to query the same in C#.
I was able to query the following mongo client code statement
db.collection.find({type:"food"},{name:1,quantity:1})
as
var match = new BsonDocument() { { "$match", new BsonDocument { {"type":"food" } } } };
var project = new BsonDocument(){ { "$project", new BsonDocument{ { "name", 1 } { "quantity", 1 } } } };
AggregateArgs AggregationPipeline = new AggregateArgs() { Pipeline = new[] { match, project } };
var aggregate = Collection.Aggregate(AggregationPipeline);
I am using Mongo C Sharp Driver 1.9.2.
Thanks.
First, add a builder:
var builder = Builders<BsonDocument>.Filter;
Then a filter like this:
var filter = builder.Lt("quantity", 20) | builder.Eq("price", 10) | other stuff)
And finally something like:
db.collection.Find(filter).ToList();

MongoDB Aggregate function in C#

I am trying to display/list data after using aggregation function but it isn't happening.
This code works absolutely fine.
var connectionstring = "mongodb://localhost:27017";
var client = new MongoClient(connectionstring);
var db = client.GetDatabase("school");
var col = db.GetCollection<BsonDocument>("students");
var filter = new BsonDocument("type", "homework");
var filter2 = Builders<BsonDocument>.Filter.Eq("scores.type", "homework");
var myresults = await col.Find(filter2)
.Limit(2)
.Project("{name:1,scores:1,_id:0}")
.Sort("{score:1}")
.ToListAsync();
foreach (var result in myresults)
{
Console.WriteLine(result);
}
This code fetches document as it should however when I replace
var myresults = await col.Find(filter2)
.Limit(2)
.Project("{name:1,scores:1,_id:0}")
.Sort("{score:1}")
.ToListAsync();
with this
var myresults = await col.Aggregate()
.Unwind("{$scores}")
.Group(new BsonDocument { { "_id", "$_id" }, { "lowscore", new BsonDocument("$min", "$scores.score") } })
//.Group("{_id:'$_id',lowscore:{$min:'$scores.score'}}")
.ToListAsync();
No record is being pulled.
I do not want to use Pipeline method. I simply want to display the result obtained via aggregate function.
This is my Mongo Query (I want the same result as this in C#)-
db.students.aggregate([{$sort:{_id:-1}},{$unwind:"$scores"},{$group:{_id:"$_id", lowscore:{"$min":"$scores.score"}}}])
Building aggregation pipeline is bit tricky.
Try:
var pipeline = new BsonDocument[] {
new BsonDocument{ { "$sort", new BsonDocument("_id", 1) }},
new BsonDocument{{"$unwind", "$scores"}},
new BsonDocument{{"$group", new BsonDocument{
{"_id", "$_id"},
{"lowscore",new BsonDocument{
{"$min","$scores.score"}}
}}
}}
};
var result = collection.Aggregate<BsonDocument> (pipeline).ToListAsync();
If you do pipeline.ToJson(), you'll get following JSON equivalent string which is same as of your original and tested MongoShell query.
[
{
"$sort": {
"_id": 1
}
},
{
"$unwind": "$scores"
},
{
"$group": {
"_id": "$_id",
"lowscore": {
"$min": "$scores.score"
}
}
}
]
This is wrong... {$scores} isn't even valid json. Remove the curly braces and the dollar sign from the $unwind directive.
The parameter name is field, so you need to provide a field name to it.
Try with writing only $score instead of #scores.score. may be it helpful.
db.students.aggregate([{$sort:{_id:-1}},{$unwind:"$scores"},{$group:{_id:"$_id", lowscore:{"$min":"$score"}}}])

how to update embeded document in mongodb using C#

i have one collection in which i am doing insert/update operation. for insert i use the code:
MongoCollection<BsonDocument> tblCity = mydb.GetCollection<BsonDocument>("tblCity");
BsonDocument CollectionCity = new BsonDocument {
{ "CityCode", cityCode },
{ "CityName", cityName },
{ "stamps" , new BsonDocument {
{"ins", DateTime.Now},
{"upd", ""},
{"createUsr", UserId},
{"updUsr", ""},
{"Ins_Ip", ""},
{"Upd_IP", GetIP()}
}
}
};
tblCity.Insert(CollectionCity);
it is working fine. but while i am updating i am using code:
MongoCollection <BsonDocument> tblCity = mydb.GetCollection<BsonDocument>("tblCity");
var query = new QueryDocument { { "City_strCode", cityCode } };
var update = new UpdateDocument {
{ "$set", new BsonDocument("City_strName", cityName) },
{ "stamps" , new BsonDocument{
{"upd", DateTime.Now},
{"updUsr", ""},
{"Upd_IP", GetIP()
}}
}};
tblCity.Update(query, update);
But problem is that with out changing the ins date i want to update upd field. But it is removing the ins field and updating the upd field. I am trying a lot of ways but not able to get any solution. Please suggest something....Even I got some links based on this and tried.. but none of them workout.
You'll need to fix your use of $set and the query.
Your query doesn't match with the field name in the inserted document.
var query = new QueryDocument { { "CityCode", cityCode } };
If you're using $set, then pass all of the fields you want to change as part of the BsonDocument:
var query = new QueryDocument { { "CityCode", cityCode } };
var update = new UpdateDocument {
{ "$set", new BsonDocument {
{ "CityName", "San Fran"},
{ "stamps.upd" , DateTime.Now()},
{ "stamps.updUsr", ""},
{ "stamps.Upd_IP", "10.0.0.1" }
}}};

Categories