How to build mongo aggregation pipeline with nested and/or conditions? - c#

I'm trying to build an aggregation pipeline that that filters out documents using a AND(OR(AND) condition.
What I need is something like
if (A == "A" && B == "B" && (C == NULL || (C != NULL && C == "C")))
My query looks like below.
The inner part:
var x = new BsonDocument
{
{
"$or",
new BsonArray
{
new BsonDocument {{"C", BsonNull.Value}},
new BsonDocument
{
{
"$and",
new BsonArray
{
new BsonDocument
{
{
"C",
new BsonDocument {{"$ne", BsonNull.Value}}
}
},
new BsonDocument {{"C", C}}
}
}
}
}
}
};
Then the full filter is
var filter = new BsonDocument
{
{
"$and",
new BsonArray
{
new BsonDocument {{"A", A}},
new BsonDocument {{"B", B}},
x
}
}
};
Then I call this like:
var y = await collection.Aggregate().Match(filter).ToListAsync();
However, it not returning any results though it should.
In my debugger, final Bson looks like:
{
{
"$and": [
{
"A": "A"
},
{
"B": "B"
},
{
"$or": [
{
"C": null
},
{
"$and": [
{
"C": {
"$ne": null
}
},
{
"C": "C"
}
]
}
]
}
]
}
}

Managed to figure it out. I changed the query to build it as a match as well since it's easier to test with mongo shell:
var match = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
"$and",
new BsonArray
{
new BsonDocument {{"A", A}},
new BsonDocument {{"B", B}},
new BsonDocument
{
{
"$or",
new BsonArray
{
new BsonDocument {{C, BsonNull.Value}},
new BsonDocument
{
{
"$and",
new BsonArray
{
new BsonDocument
{
{
"C",
new BsonDocument {{"$ne", BsonNull.Value}}
}
},
new BsonDocument {{"C", C}}
}
}
}
}
}
}
}
}
}
}
};
Hope this helps anyone running into a problem with similar query.

Related

C# - Finding a nested object value by dot separated key [duplicate]

I have json text like this:
{
"course_editions": {
"2014/SL": [
{
"grades": {
"course_units_grades": {
"159715": {
"1": {
"value_symbol": "4",
"exam_session_number": 1,
"exam_id": 198172,
"value_description": {
"en": "good",
}
}
}
},
"course_grades": {}
}
},
{
"grades": {
"course_units_grades": {
"159796": {
"1": {
"value_symbol": "5",
"exam_session_number": 1,
"exam_id": 198259,
"value_description": {
"en": "very good",
}
}
}
},
"course_grades": {}
}
},
I would like to use JToken.SelectTokens Method from Namespace: Newtonsoft.Json.Linq
I've tried like this:
string json_response = GetResponse(sign(url_courses));
var courses_tokens = JObject.Parse(json_response).SelectTokens("['course_editions'].['2014/SL'].[*].['grades'].*")
It doesn't work. I would like to get only these numbers after course_unit_grades and before "1". So in this example only: "159715" and "159796" to be able to use all of them, one by one in
foreach(var lp in courses_tokens) {
}
This is one possible way :
var jobj = JObject.Parse(json);
var coursesTokens = jobj.SelectTokens("course_editions.2014/SL[*].grades.course_units_grades")
.Select(o => o.First) //get the first child of `course_units_grades`
.Cast<JProperty>() //cast to JProperty
.Select(o => o.Name); //get the name of the property
foreach (string coursesToken in coursesTokens)
{
Console.WriteLine(coursesToken);
}
Dotnetfiddle Demo
given json sample at the bottom, the output is as follow :
159715
159796
json sample :
var json = #"{
'course_editions': {
'2014/SL': [
{
'grades': {
'course_units_grades': {
'159715': {
'1': {
'value_symbol': '4',
'exam_session_number': 1,
'exam_id': 198172,
'value_description': {
'en': 'good'
}
}
}
},
'course_grades': {}
}
},
{
'grades': {
'course_units_grades': {
'159796': {
'1': {
'value_symbol': '5',
'exam_session_number': 1,
'exam_id': 198259,
'value_description': {
'en': 'very good'
}
}
}
},
'course_grades': {}
}
}
]
}
}";

MongoDB Aggregate function to C#

this has been bugging me all night.
I have the following MongoDb function that returns the top searches in a collection including there count
db.search.aggregate([
{
$match: {
keywords: { $not: {$size: 0} }
}
},
{ $unwind: "$term" },
{
$group: {
_id: {$toLower: '$term'},
count: { $sum: 1 }
}
},
{
$match: {
count: { $gte: 2 }
}
},
{ $sort : { count : -1} },
{ $limit : 100 }
]);
I am trying to move this to a C# function and I am getting the error :
MongoDB.Driver.MongoCommandException: 'Command aggregate failed: A
pipeline stage specification object must contain exactly one field..'
Here is my C# version, can anyone see what I am missing?
var pipeline = new BsonDocument[] {
new BsonDocument
{
{
"$match",
new BsonDocument {{"keywords", new BsonDocument {{"$not", new BsonDocument {{"$size", 0}}}}}}
}
},
new BsonDocument {{"$unwind", "$term"}},
new BsonDocument
{
{
"$group", new BsonDocument
{
{"_id", new BsonDocument {{"$toLower", "$term"}}},
{
"count", new BsonDocument
{
{"$sum", 1}
}
}
}
}
},
new BsonDocument
{
{
"$match", new BsonDocument
{
{"count", new BsonDocument {{"$gte", 2}}}
}
},
{
"$sort", new BsonDocument
{
{"count", -1}
}
},
{"$limit", 100}
}
};
var result = collection.Aggregate<BsonDocument>(pipeline).ToListAsync();
Console.WriteLine(result);
$match, $sort and $limit should be separate Aggregation Pipeline stages. In your case they have one root BsonDocument, try:
...
new BsonDocument
{
{
"$match", new BsonDocument
{
{"count", new BsonDocument {{"$gte", 2}}}
}
}
},
new BsonDocument()
{
{ "$sort", new BsonDocument
{
{"count", -1}
}
}
},
new BsonDocument()
{
{ "$limit", 100 }
}

Aggregating By match date $or with other field in Mongodb in C#

I am using mongo aggregation with match/group. Below is my mongo query & similar i am trying to convert to c#. Able to get date part but struggling for $or part for "ev" field. Need to apply below match/or/group/sort on one collection & lookup on other collection.
db.getCollection("customer").aggregate(
[
{
"$match" : {
"eventTs" : {
"$gte" : ISODate("2020-02-27T00:00:00.000-0500"),
"$lte" : ISODate("2020-02-28T00:00:00.000-0500")
},
$or: [
{ "ev": "JournalAdded" },
{ "ev": "JournalProcessed" },
{ "ev": "JournalModified" },
{ "ev": "JournalVoided" },
{ "ev": "JournalApproved" },
{ "ev": "JournalCancelled" }
]
}
},
{
"$sort" : {
"eid" : 1.0,
"eseq" : 1.0
}
},
{
"$group" : {
"_id" : "$eid",
lastSeq: {$last: "$eseq"},
eventNames:{$push: "$ev"}
}
},
{
"$lookup":
{
from: "xyz",
localField: "_id",
foreignField: "_eid",
as: "Replicated"
}
}
]
);
working c# code:
var match = new BsonDocument
{
{
"$match",
new BsonDocument
{
{ "eventTs", new BsonDocument
{
{ "$gte", startTs },
{ "$lte", endTs }
}
},
{ "ev", "JournalAdded" },
//{ "ev", "JournalProcessed" },
//{ "ev", "JournalModified" },
//{ "ev", "JournalVoided" },
//{ "ev", "JournalApproved" },
//{ "ev", "JournalCancelled" }
}
}
};
var pipeline = new[] { match };
var result = instructionEventsDocs.Aggregate<BsonDocument>(pipeline);
List<BsonDocument> list = result.ToList();
Need c# equivalent of above mongo query. Can anyone help me with this ?
You can also add the BsonArray after the BsonDocument for $or part for "ev" field,look into the answers in this link Adding BSON array to BsonDocument in MongoDB
var match = new BsonDocument
{
{
"$match",
new BsonDocument
{
{ "eventTs", new BsonDocument
{
{ "$gte", startTs },
{ "$lte", endTs }
}
},
{
"$or", new BsonArray {
new BsonDocument
{
{ "ev", "JournalAdded" }
},
new BsonDocument
{
{ "ev", "JournalProcessed" }
}
}
},
}
}
};
Equivalent mongo query
{{ "$match" : { "eventTs" : { "$gte" : ISODate("2020-02-27T05:00:00Z"), "$lte" : ISODate("2020-02-28T05:00:00Z") }, "$or" : [{ "ev" : "JournalAdded" }, { "ev" : "JournalProcessed" }] } }}

C# JToken.SelectTokens Method - what JPath expression?

I have json text like this:
{
"course_editions": {
"2014/SL": [
{
"grades": {
"course_units_grades": {
"159715": {
"1": {
"value_symbol": "4",
"exam_session_number": 1,
"exam_id": 198172,
"value_description": {
"en": "good",
}
}
}
},
"course_grades": {}
}
},
{
"grades": {
"course_units_grades": {
"159796": {
"1": {
"value_symbol": "5",
"exam_session_number": 1,
"exam_id": 198259,
"value_description": {
"en": "very good",
}
}
}
},
"course_grades": {}
}
},
I would like to use JToken.SelectTokens Method from Namespace: Newtonsoft.Json.Linq
I've tried like this:
string json_response = GetResponse(sign(url_courses));
var courses_tokens = JObject.Parse(json_response).SelectTokens("['course_editions'].['2014/SL'].[*].['grades'].*")
It doesn't work. I would like to get only these numbers after course_unit_grades and before "1". So in this example only: "159715" and "159796" to be able to use all of them, one by one in
foreach(var lp in courses_tokens) {
}
This is one possible way :
var jobj = JObject.Parse(json);
var coursesTokens = jobj.SelectTokens("course_editions.2014/SL[*].grades.course_units_grades")
.Select(o => o.First) //get the first child of `course_units_grades`
.Cast<JProperty>() //cast to JProperty
.Select(o => o.Name); //get the name of the property
foreach (string coursesToken in coursesTokens)
{
Console.WriteLine(coursesToken);
}
Dotnetfiddle Demo
given json sample at the bottom, the output is as follow :
159715
159796
json sample :
var json = #"{
'course_editions': {
'2014/SL': [
{
'grades': {
'course_units_grades': {
'159715': {
'1': {
'value_symbol': '4',
'exam_session_number': 1,
'exam_id': 198172,
'value_description': {
'en': 'good'
}
}
}
},
'course_grades': {}
}
},
{
'grades': {
'course_units_grades': {
'159796': {
'1': {
'value_symbol': '5',
'exam_session_number': 1,
'exam_id': 198259,
'value_description': {
'en': 'very good'
}
}
}
},
'course_grades': {}
}
}
]
}
}";

Adding BSON array to BsonDocument in MongoDB

How can I add BsonArray to BsonDocument in MongoDB using a C# driver? I want a result something like this
{
author: 'joe',
title : 'Yet another blog post',
text : 'Here is the text...',
tags : [ 'example', 'joe' ],
comments : [ { author: 'jim', comment: 'I disagree' },
{ author: 'nancy', comment: 'Good post' }
]
}
You can create the above document in C# with the following statement:
var document = new BsonDocument {
{ "author", "joe" },
{ "title", "yet another blog post" },
{ "text", "here is the text..." },
{ "tags", new BsonArray { "example", "joe" } },
{ "comments", new BsonArray {
new BsonDocument { { "author", "jim" }, { "comment", "I disagree" } },
new BsonDocument { { "author", "nancy" }, { "comment", "Good post" } }
}}
};
You can test whether you produced the correct result with:
var json = document.ToJson();
you can also add the array after the BsonDocument already exists, like this:
BsonDocument doc = new BsonDocument {
{ "author", "joe" },
{ "title", "yet another blog post" },
{ "text", "here is the text..." }
};
BsonArray array1 = new BsonArray {
"example", "joe"
};
BsonArray array2 = new BsonArray {
new BsonDocument { { "author", "jim" }, { "comment", "I disagree" } },
new BsonDocument { { "author", "nancy" }, { "comment", "Good post" } }
};
doc.Add("tags", array1);
doc.Add("comments", array2);

Categories

Resources