Execute complex mongo JSON query using C# mongo driver - c#

I have following mongo shell query:
{'field':'FieldOne','value':'FieldOne','category':'categoryOne'} , { 'Color' : { '$elemMatch' : { 'Value' : 'Green' } }}
Is there any way to execute same query using C# mongo driver?. I have tried using below C# code, but only the first one is getting executed:
BsonDocument query = BsonDocument.Parse("{'field':'Overall','value':'Overall','category':'LoggedIncidents'} , { 'Priority' : { '$elemMatch' : { 'Value' : 'P1' } }}");
QueryDocument queryDoc = new QueryDocument(query);
var result = collection.Find(queryDoc).ToListAsync().Result;
The first item ({'field':'FieldOne','value':'FieldOne','category':'categoryOne'}) is being executed but not the second one ({ 'Color' : { '$elemMatch' : { 'Value' : 'Green' } }}).

You are using it in find command if i'm not wrong.
the first "{}" is what the filter condition for find is. next set of parameters are essentially to show/hide the fields you need.
so if you want to filter by value of array "green" you can do this.
db.objects.find({"color":{$elemMatch:{value:"green"}}})
should give you the results you are looking for. but if you want to specifically show/hide fields, you can do
db.objects.find({"color":{$elemMatch:{value:"green"}}},{"field":1})
is this what you want to attain?

Related

in mongodb, what is the most efficient way to get the first and last document

I have documents like this:
class A
{
DateTime T;
...
}
and I would like to find the earliest and the newest document.
Is it better to do this:
var First = db.Collection.AsQueryable().OrderBy(_ => _.t).FirstOrDefault();
var Last = db.Collection.AsQueryable().OrderByDescending(_ => _.t).FirstOrDefault();
or,
var First = db.Collection.AsQueryable().OrderBy(_ => _.t).FirstOrDefault();
var Last = db.Collection.AsQueryable().OrderBy(_ => _.t).LastOrDefault();
or
var C = db.Collection.AsQueryable().OrderBy(_ => _.t);
var First = C.FirstOrDefault();
var Last = C.LastOrDefault();
I am wondering if there is anything with the underlying implementation that would change the speed between these options?
I am wondering if the sort has been done once already, is it possible that the result is cached and getting the first and the last elements would be faster?
Profiler becomes your friend when you're not sure about driver syntax. To enable logging for all queries you need to run on your database below statement:
db.setProfilingLevel(2)
Then to check last query executed on the database you need to run:
db.system.profile.find().limit(1).sort( { ts : -1 } ).pretty()
So for the first code snippet you will get:
"pipeline" : [ { "$sort" : { "t" : 1 } },
{ "$limit" : 1 }
]
"pipeline" : [ { "$sort" : { "t" : -1 } },
{ "$limit" : 1 }
]
For the second pair it prints
"pipeline" : [ { "$sort" : { "t" : 1 } },
{ "$limit" : 1 }
]
and throws NotSupportedException for LastOrDefault on my machine, if it works on your MongoDB driver's version you can check generated MongoDB statement using profiler
For the last one when you hover on c in your Visual Studio it prints
{aggregate([{ "$sort" : { "t" : 1 } }])}
but since it's of type IOrderedQueryable<T> it is only not materialized query so it will get executed on the database when you run FirstOrDefault generating the same aggregation body as previous statements. I'm getting NotSupportedException here as well. Here you can find a list of supported LINQ operators and both Last and LastOrDefault are not implemented so you need to sort descending.

MongoDB updateOne

I am trying to update an existing Mongo record, but am getting an "Additional information: Element name 'ID' is not valid'." error
I have a a BsonDocument "document" containing data that I retrieve from another source that looks like this:
{ "ID" : "ABCecdcf9851efbf0ef66953", ListingKey : "234534345345", "Created" : ISODate("2017-08-04T00:31:23.357Z"), "Modified" : ISODate("2017-08-04T00:31:23.358Z"), "Field1" : 1, "Field2" : "0.09", "Field3" : "1.10", "Field4" : "1", "Field5" : "1" }
Here is the C# code that I have written:
var collection = db.GetCollection<BsonDocument>("MyCollection");
//Hard coded for testing
var filter = Builders<BsonDocument>.Filter.Eq("ListingKey", "234534345345");
collection.UpdateOne(filter, document);
Is this related to the BsonDocument that I am trying to use to update? I found this documentation, which causes me to think that this is the cause. If so, is there a way to do an update with the format I have been provided?
https://docs.mongodb.com/getting-started/csharp/update/
I had a process working where it would delete the document and then add a new document, but for efficiency's sake I need this to update. Ideally it will only update the fields that are present in the BsonDocument and keep the existing fields in the Mongo document as is.
My problem was because I did not have the correct value when trying to update. My code works with this:
var collection = db.GetCollection<BsonDocument>("MyCollection");
//Hard coded for testing
var filter = Builders<BsonDocument>.Filter.Eq("ListingKey", "234534345345");
var update = Builders<BsonDocument>.Update.Set("Created", DateTime.UtcNow);
foreach (BsonElement item in document)
{
update = update.Set(item.Name, item.Value);
}
var result = collection.UpdateOne(filter, update);
I had to convert my string into an update BsonDocument.

How to implement MongoDB nested $elemMatch Query in C#

I have a MongoDB collection in the following format.
{
"_id" : ObjectId("56c6f03ffd07dc1de805e84f"),
"Details" : {
"a" : [
[ {
"DeviceID" : "log0",
"DeviceName" : "Dev0"
},
{
"DeviceID" : "log1",
"DeviceName" : "Dev1"
}
],
[ {
"DeviceID" : "Model0",
"DeviceName" : "ModelName0"
},
{
"DeviceID" : "Model1",
"DeviceName" : "ModelName1"
}
]
]
}
}
And I am trying to fetch all the documents where the DeviceName in array "a" contains a particular value, say "Name0". However I could get the desired result while using below Mongo query:
db.test_collection.find({"Details.a":{$elemMatch:{$elemMatch:{DeviceName : /.*Name0.*/}}}});
Now I am struggling to implement the above query in C#. Can anyone guide me with that?
so far I have tried the below code and it was not working as expected
query = Query.And(Query.ElemMatch("Details.a", Query.And(Query.ElemMatch("DeviceName", Query.Matches("DeviceName", new BsonRegularExpression("Name0"))))));
Thanks in advance
Well, honestly writing queries in C# are bit tricky but you can always play a trick.
var bsonQuery = "{'Details.a':{$elemMatch:{$elemMatch:{DeviceName : /.*Name0.*/}}}}";
var filter = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(bsonQuery);
var result = col.FindSync (filter).ToList();
I'm deserializing a plain MongoDB queries into a BsonDocument which in return I'm passing to FindAsync as filter.
In the end, you'll have desired outcome in variable result.
Note: I'm assuming MongoDB connection has been established and variable col holds reference to MongoDB collection.
EDIT: Please see following link https://groups.google.com/forum/#!topic/mongodb-csharp/0dcoVlbFR2A. Now it's confirmed that C# driver doesn't support nameless filters so writing above query using Buidlers<BsonDocument>.Filter at moment is not supported.
Long story short, you are left with only one choice and that is to query as I mentioned above in my solution.

MongoDB/C# Update Collection entries

Hello I have a mongoDB Collection called Nodes whose structure is as follows:
{
"_id" : new BinData(3, "Ljot2wCBskWG9VobsLA0zQ=="),
"logicalname" : "Test Node",
"host" : "eh5tmb054pc",
"port" : 104,
"appendtimestamp" : false,
"studies" : ["1.3.12.2.1107"],
"tests" : [],
"mainentries" : [{
"_id" : "1.3.12.2.1107",
"Index" : 0,
"Type" : "Study"
}]
}
I created a new key called "mainentries" which is now storing the "studies" and "tests". So in order to support my new versions without hassle, I now want to write a method in my Setup Helper, which would enable me to read this collection - Check if studies,tests exists , If yes add the key "mainentries" and remove the studies/tests key.
My question is: What kind of query must I use to reach each collection of Nodes to check for the fields and update. I am using the MongoDB-CSharp community driver.
Would appreciate any help and pointers.
You can simply check whether the field(s) still exist(s):
var collection = db.GetCollection<Node>("nodes");
var nodes = collection.Find(Query.And( // might want Query.Or instead?
Query<Node>.Exists(p => p.Tests),
Query<Node>.Exists(p => p.Studies)).SetSnapshot();
foreach(var node in nodes) {
// maybe you want to move the Tests and Studies to MainEntries here?
node.MainEntries = new List<MainEntries>();
node.Test = null;
node.Studies = null;
collection.Update(node);
}
If you don't want to migrate the data, but just remove the fields and create new ones, you can also do in a simple batch update using $exists, $set and $remove

Update field from c# driver

I have a document collection like this:
{
"_id" : ObjectId("4fb21439f31dfd122ce39c4a"),
"Name" : "Freelander 2",
"Manufacture" : "Landrover"
}
I am using the C# driver, and want to check whether the "Name" field exists; if it does not, I need to update it from the code. Can someone help on this? Please share sample code because I am a newbie to mongoDB.
You can use the exists operator:
db.myCollection.find( { Name: { $exists: true } } );
Using the C# driver, the equivalent would be something like this:
IMongoQuery query = Query.Exists("Name", true)
var results = myCollection.Find(query);
To set a value if it doesn't exist, you can use an Update statement like this:
IMongoQuery query = Query.Exists("name", true);
IMongoUpdate update = Update.Set("Name", "newvalue");
myCollection.Update(query, update);
I'm assuming you want to update all documents that don't have a Name field to the same value. You can do that in the mongo shell like this:
> db.test.update({Name:{$exists:false}}, {$set:{Name:"xyz"}}, false, true)
You can do the same thing in C# like this:
var query = Query.Exists("Name", false);
var update = Update.Set("Name", "xyz");
collection.Update(query, update, UpdateFlags.Multi);

Categories