How to deserialize query with LUUID value to BsonDocument - c#

I'm trying to deserialize filter with LUUID (or NUUID in this example) to BsonDocument:
var tmpQry = "{'ValueId': NUUID('ca7ac84f-18bf-42f0-b028-333ed144c549')";
var tmpBson = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(tmpQry);
and getting an error:
System.FormatException: 'JSON reader was expecting a value but found 'NUUID'.'
I understand, that LUUID is not valid for JSON, but is it somehow possible to get BsonDocument from my string?
In my particular case i can't implement MongoDB nested $elemMatch query, like in this issue.
But my query contains identifiers:
db.getCollection('my_db').aggregate([
{
$match: {
'Event.key_attributes': {
$all: [
{ '$elemMatch': { 'Type.Code': 'code1', 'ValueId': LUUID('00000000-0000-0000-0000-000000000001') } },
{ '$elemMatch': { 'Type.Code': 'code2', 'ValueId': LUUID("00000000-0000-0000-0000-000000000002") } },
{ '$elemMatch': { 'Type.Code': 'code3', 'ValueId': LUUID("00000000-0000-0000-0000-000000000003") } }
]
}
}
},
{
$group: {
'_id': '$$CURRENT.Event.type._id',
'code': { '$last': '$$CURRENT.Event.type.Code' },
'value': { '$sum': '$$CURRENT.Event.value' }
}
}
]);
, and I even can't deserialize it into a BsonDocument.
Does my problem have any solution?
Thanks a lot.

Finally, I solved this issue. Instead of trying to serialize the query from a string, I created BsonDocument pipeline:
var filter = new BsonDocument {{
"$match", new BsonDocument {{
"Event.key_attributes", new BsonDocument {{
"$all", new BsonArray().AddRange(limit.KeyAttributes.Select(ka => new BsonDocument(
"$elemMatch", new BsonDocument().AddRange(new List<BsonElement>{
new BsonElement("Type.Code", ka.Type.Code),
new BsonElement("ValueId", ka.ValueId)
})
)).ToList())
}}
}}
}};
var group = new BsonDocument {{
"$group", new BsonDocument().AddRange(new List<BsonElement>{
new BsonElement("_id", "$$CURRENT.Event.type._id"),
new BsonElement("code", new BsonDocument{{
"$last", "$$CURRENT.Event.type.Code" }}),
new BsonElement("value", new BsonDocument{{
"$sum", "$$CURRENT.Event.value" }})
})
}};
var pipeline = new BsonDocument[]
{
filter,
group
};
var result = collection.Aggregate<MyOutputClass>(pipeline).ToListAsync();
There were no problems with Guid.

The poster has worked around his problem, but for those happening up this question later: Use CSUUID('guid-string-here') for the CSharp legacy format.

Related

How to get two collections output in a single document in MongoDb in c#?

Lets say I am having two collections named CollectionA and CollectionB, Both collection have different fields.
CollectionA will have multiple documents with Same field,
CollectionB contain only one document
Example
CollectionA
{
"UniqeId" :1,
"Hobbies" : "Eating"
},
{
"UniqeId" :2,
"Hobbies" : "Sleeping"
},
{
"UniqeId" :3,
"Hobbies" : "Walking"
}
CollectionB
{
"UserName" :"Sukuna",
"UserType" : "Villan"
}
I want output like this
{
"UniqeId" :1,
"Hobbies" : "Eating",
"UserName" :"Sukuna",
"UserType" : "Villan"
}
Consider All the documents in a CollectionA will contain same fields
And you can see there is no Unique fields between the two collection, and you can see we need to apply filter in CollectionA
ie) UniqeId=1
I am using C#, and I can able do two DB request to get those collection details (One req for CollectionA output and another one for CollectionB output) and manage to combine both in API level to get the desired output, but I want to do in DB level itself,
I don't want two DB calls, that is eating the API performance, so is there anyway to achieve this in a single DB call or by using any aggregate pipeline?.
Thanks in Advance
lookup with localField 1 and foreignField 1
db.a.aggregate([
{
$lookup: {
from: "b",
localField: "1",
foreignField: "1",
as: "docs"
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
{ $first: "$docs" }
]
}
}
},
{
$unset: [ "docs", "_id" ]
},
{
$group: {
_id: "$UserName",
doc: { $first: "$$ROOT" }
}
},
{
$replaceWith: "$doc"
}
])
mongoplayground
Finally after lots of trail and error and playing with pipeline, I can able to do that in aggregate pipeline, and I am using unionWith and group.
Here is the C# code
var pipeline1= new BsonDocument("$unionWith",
new BsonDocument
{
{ "coll", "CollectionB" },
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("UniqeId", 1))
} }
});
var pipeline2 = new BsonDocument("$group",
new BsonDocument
{
{ "_id", 0 },
{ "merged",
new BsonDocument("$push", "$$ROOT") }
});
var pipeline3 = new BsonDocument("$replaceRoot",
new BsonDocument("newRoot",
new BsonDocument("$mergeObjects", "$merged")));
var pipeline4 = new BsonDocument("$project",
new BsonDocument("_id", 0));
BsonDocument[] pipeline = new BsonDocument[] { pipeline1, pipeline2, pipeline3, pipeline4 };
var dbResponse = await collection.Aggregate<BsonDocument>(pipeline).ToListAsync();

How to use MongoDB Search Index / Indexes on ASP.NET

I have a collection of 10 000 documents, (~ 6 Mb)
When I'm using a simple find to retrieve all the data, it takes like more than a minute to retrieve all the documents.
I created a search Index on MongoDb to speed up what I want to achieve.
I have also a search Index :
Here is the Query Syntax of this Search Index :
new BsonArray {
new BsonDocument("$search", new BsonDocument {
{
"index",
"searchProd"
},
{
"text",
new BsonDocument {
{
"query",
application // DYNAMIC Value that I want to pass
},
{
"path",
new BsonDocument("wildcard", "*")
}
}
}
})
}
Couldn't find a way how to implement this on my function.
public async Task<List<Produit>> ListAllProducts(string application)
{
var sw = Stopwatch.StartNew();
var allProducts = await _produits.Find(new BsonDocument("$search", new BsonDocument
{
{
"index",
"searchProd"
},
{
"text",
new BsonDocument
{
{
"query",
application
},
{
"path",
new BsonDocument("wildcard", "*")
}
}
}
})).ToList();
return allProducts;
}
By doing this I'm getting
MongoDB.Driver.MongoCommandException: Command find failed: unknown top
level operator: $search. If you have a field name that starts with a
'$' symbol, consider using $getField or $setField..
Thanks.
atlas $search is an aggregation stage, not find argument. It should look similar to:
_produits.Aggregate().AppendStage<BsonDocument>("your $search stage in string or BsonDocument form").ToList();

How to $lookup with pipeline & let parameters in C# (MongoDB.Driver 2.7.2)

I need to run the following query in MongoDB in my C# code. I use MongoDB.Driver 2.7.2 and MongoDB 4.0.
I would like to use $lookup rather than $project / $unwind in order to prevent naming each one of the collection's possible fields.
db.getCollection('Collection1').aggregate([
{ "$match" : { "Id" : 1 } },
{ "$lookup": {
"from": "Collection2",
"let": { collection1Id : "$Id" },
"pipeline": [
{ $match:
{ $expr:
{ $and:
[
{ $eq : [ "$Collection1Id", "$$collection2Id" ] },
{ $in : [ "$_id" , [1, 2, 3]] }
]
}
}
}
],
"as": "item"
}
}
])
I expect the output of Collection1 joined with Collection2 under multiple join conditions.
As you didn't specify a particular class mapping and example documents, below answer will use BsonDocument type and example data from the manual $lookup: specify multiple join conditions with lookup
BsonArray subpipeline = new BsonArray();
subpipeline.Add(
new BsonDocument("$match",new BsonDocument(
"$expr", new BsonDocument(
"$and", new BsonArray {
new BsonDocument("$eq", new BsonArray{"$stock_item", "$$order_item"} ),
new BsonDocument("$gte", new BsonArray{"$instock", "$$order_qty"} )
}
)
))
);
var lookup = new BsonDocument("$lookup",
new BsonDocument("from", "warehouses")
.Add("let",
new BsonDocument("order_item", "$item")
.Add("order_qty", "$ordered"))
.Add("pipeline", subpipeline)
.Add("as", "stockdata")
);
var results = collection.Aggregate()
.Match(new BsonDocument("_id", 1))
.AppendStage<BsonDocument>(lookup).ToEnumerable();
foreach (var x in results)
{
Console.WriteLine(x.ToJson());
}
Please note that support for more expressive $lookup using PipelineDefinitionBuilder is coming for version 2.8.x. For more information see CSHARP-2013

MongoDB filtered lookup in C#

this question refers to the example in How to project specific fields in array on filtered lookup
where a filter was applied on the 'joined' table.
Now I want to translate this into a query in C#, but I don't manage to add the filter to the projection. This is as far as I got:
db.GetCollection("meta")
.Aggregate()
.Match(new BsonDocument { { "test", "OK" }})
.Lookup("merge","Exp","Exp","kin")
.Project(Builders<BsonDocument>.Projection
.Include("Exp")
.Include("test")
.Include("kin")
)
Any idea?
It seems to work the hard way:
db.GetCollection("meta")
.Aggregate()
.Match(new BsonDocument { { "test", "OK" }})
.Lookup("merge","Exp","Exp","kin")
.Project(new BsonDocument {
{ "Exp" , 1},
{ "test" , 1},
{ "kin", new BsonDocument {
{ "$filter", new BsonDocument {
{ "input", "$kin"},
{ "as", , "kin"},
{ "cond", new BsonDocument {
{ "$eq", new BsonArray { "$$kin.M2", "val"}}}
}}
}}
}})
.Project(new BsonDocument {
{ "Exp", 1 },
{"test", 1},
{"date", 1},
{"kin.M1",1},
{"kin.M2",1},
{"kin.T",1 }})
but of course it would be nice to use the API of the Mongo Driver.

Executing Aggregate framework query in c#

I am trying to create a console application which will read through a Mongo db and filter out some data based on some business logic and display the results on console screen.
Till now I am able to write the following query and execute it successfully on Mongo shell. Here is the query:
{db.Collection.aggregate([
{$unwind: { path: "$Pages"}},
{$group : {
_id :{UrlPath: "$Pages.Url.Path", I_Id : "$_id", Date: { $dateToString: { format: "%Y-%m-%d",date: "$Pages.DateTime"}}, CId: "$CId",
x: {$sum:1},
y : {$sum:"$V"}
}},
{$group : {
_id : {Date: "$_id.Date",CId: "$_id.CId", PageUrl: "$_id.UrlPath"},
p: {$sum:1},
q : {$sum:"$x"},
TotalCount: {$sum:"$y"}
}},
{$sort:{p:-1}},
],{allowDiskUse: true}).pretty();
};
The problem I am facing is replicating the same query logic in C# code. Till now I able to connect to mongo db collections and able to basic CRUD operations.
But replicating this aggregate logic is really blowing my mind. I have tried using PipelineDefinition option but can't manage to get correct output out of it.
Can some one please guide me in the correct direction here.
Thanks in advance
Finally I was able to achieve it using collection.Aggregate() method.
Code looks like this:
collection.Aggregate(new AggregateOptions { AllowDiskUse = true})
.Unwind(i => i.Pages)
.Group(new BsonDocument
{
{
"_id", new BsonDocument
{
{ "Date", new BsonDocument("$dateToString", new BsonDocument {{"format", "%Y-%m-%d"}, {"date", "$Pages.DateTime"}})},
{"CId","$_id"}
}
},
{
"x", new BsonDocument
{
{"$sum", "$Value"}
}
}
}
)
.Group(new BsonDocument
{
{
"_id", new BsonDocument
{
{"Date", "$_id.Date"},
{"CId", "$_id.CId"}
}
},
{
"p", new BsonDocument
{
{"$sum", 1}
}
},
{
"q", new BsonDocument
{
{"$sum", "$x"}
}
}
}
)
.Sort(new BsonDocument("q", -1))
.ToList();

Categories

Resources