MongoDB Query C# Driver - c#

I am having trouble with expressing this query in C# MongoDB, I want it to return all the results of an objectID where it does not equal to "000000000000000000000000" which works in MongoVue; But I can't get it work in my program.
{"ProfilePictureId" : {$ne: new ObjectId ("000000000000000000000000")}}
I am using official C# driver:
var query = new QueryDocument();
foreach (BsonDocument book in col.Find(query))
{
...
}

You can build your query as follows:
var query = Query.NE("ProfilePictureId", ObjectId.Empty);
ObjectId.Empty returns an ObjectId composed of all zeroes.

Assuming that you are querying for documents of a class looking something like:
public class Profile {
public ObjectId ProfilePictureId { get; set; }
//... other attributes, construcotrs, methods etc...
}
You can also write your query using expression lambdas like this:
var query = Query<Profile>.NE(s => s.ProfilePictureId, ObjectId.Empty);

Related

How to 'Find And Push' element in an array inside of another array in a document using LINQ to MongoDb

I want to push an element to an array inside another array inside a document using LINQ with latest MongoDb driver
here is the code:
public class Contract : BaseDocument
{
public ObjectId Id {get;set;}
...
public List<Payment> Payments {get;set;}
}
public class Payment : BaseDocument
{
public ObjectId Id {get;set;}
public double TotalPaymentAmount {get;set;}
public DateTime PaymentWorthDate {get;set;}
...
public List<PaymentTransaction> PaymentTransactions {get;set;}
}
public class PaymentTransaction
{
public double AmountPaid {get;set;}
public DateTime TransactionDateTime {get;set;}
}
So how to push new PaymentTransaction to a specific Payment in a particular Contract using 'LINQ Expression' ?
Thanks!
LINQ stands for Language-Intergrated Query while you're trying to update the document so actually you need UpdateOne method. Since you have more than one nested array you can leverage regular query to identify Contract along with the $ positional operator to indicate which Payment (nested object) should be modified.
.NET MongoDB driver offers a special syntax where you can pass -1 as an index to indicate that this item will be identified based on filtering condition. Try:
var filterBuilder = Builders<Contract>.Filter;
var filter = filterBuilder.Eq(x => x.Id, contractId) &
filterBuilder.ElemMatch(doc => doc.Payments, el => el.Id == paymentId);
var updateBuilder = Builders<Contract>.Update;
var update = updateBuilder.Push(doc => doc.Payments[-1].PaymentTransactions, new PaymentTransaction());
col.UpdateOne(filter, update);

Mongo C# driver find multiple and delete

I'm using collection.FindOneAndDeleteAsync, but this uses a ton of cpu when used to get many documents. What's the best way to go about finding multiple documents(anywhere from 100 to 50k) and delete, using the c# mongo driver?
Thanks
You need to Find the docs you want to delete, and then delete them using DeleteMany with a filter of _id: {$in: ids}, where ids is an enumerable of the _id values of those documents.
C# example:
public class Entity
{
public ObjectId id { get; set; }
public string name { get; set; }
}
// Find the documents to delete
var test = db.GetCollection<Entity>("test");
var filter = new BsonDocument();
var docs = test.Find(filter).ToList();
// Get the _id values of the found documents
var ids = docs.Select(d => d.id);
// Create an $in filter for those ids
var idsFilter = Builders<Entity>.Filter.In(d => d.id, ids);
// Delete the documents using the $in filter
var result = test.DeleteMany(idsFilter);

How to query collection using generic query builder

I am using the mongocsharpdriver nuget package (version 1.11.0) to run queries against a mongo database.
When creating a query object in c# I can do this:
var query = Query.EQ("RootValue", "foo");
I can use the nicer generic query Builders instead to do this:
var query = Query<RootObject>.EQ(x=>x.RootValue, "foo");
Now consider this query::
var query = Query.EQ("Things.Value", "bar");
Here Things is a collection of objects that have a string (Value) on them. In this case the query will return any object which has a match in any of the Values of Things.
How do I write this query using the generic Query builder?
I can't work out what expression I need that will get correctly translated to what I want...
In case it makes it clearer here are the classes for my example:
public class RootObject
{
[BsonId]
public ObjectId Id {get; set;}
public IEnumerable<RepeatedObject> Things {get; set;}
public string RootValue {get; set;}
}
public class RepeatedObject
{
public string Value {get; set;}
}
Using this version of the driver, the following query
var query = Query<RootObject>.ElemMatch(x => x.Things, x => x.EQ(y => y.Value, "bar"));
will be translated into the desired MongoDB query:
{ Things: { $elemMatch: { Value: "bar" } } }

MongoDB C# 2.x Driver ElemMatch with Dictionaries

I am trying to use ElemMatch to find a document in MongoDB using the 2.2 driver with no success. I am receiving an exception such as:
System.InvalidOperationException : The serializer for field
'EnabledForProduct' must implement IBsonArraySerializer and provide
item serialization info.
Here's how my class looks like:
public class Document
{
public string Id {get; set;}
public Dictionary<Product, bool> EnabledForProduct { get; set; }
}
public enum Product {Product1,Product2};
My ClassMap looks lke this:
BsonClassMap.RegisterClassMap<Document>(cm =>
{
cm.AutoMap();
cm.MapMember(c => c.EnabledForProduct)
.SetSerializer(new DictionaryInterfaceImplementerSerializer<Dictionary<Product, bool>>(DictionaryRepresentation.ArrayOfDocuments,
BsonSerializer.LookupSerializer<int>(),
BsonSerializer.LookupSerializer<bool>()));
});
The exception occurs when trying to use a Filter such as:
Builders<Document>.Filter.ElemMatch(f => f.EnabledForProduct,
x => x.Key == Product1 && x.Value))
This used to work flawlessly in the 1.x Driver.
Does anyone know what I am doing wrong?
Well, after some trial and error implementations, I figured out a way to do what I needed. Instead of directly using my model class, I ended up using a BsonDocument collection just for my ElemMatch filter like this:
var bsonCollection = database.GetCollection<BsonDocument>("testcollection");
The filter gets created like this:
var filter = Builders<BsonDocument>.Filter.ElemMatch("EnabledForProduct", Builders<BsonDocument>.Filter.And(Builders<BsonDocument>.Filter.Eq("k",(int)Product.Product1),Builders<BsonDocument>.Filter.Eq("v",true)));
And the generic BsonDocument can be deserialized back to my model class using BsonSerializer:
var foundDoc = BsonSerializer.Deserialize<Document>(bsonCollection.Find(filter).Limit(1).FirstOrDefault());

Azure Document DB UpdateDoc

I am starting off with azure document db. I was trying to update an existing document. When I use the following query everything works:
dynamic Team2Doc = client.CreateDocumentQuery<Document>(documentCollection.DocumentsLink).Where(d => d.Id == "t002").AsEnumerable().FirstOrDefault();
Team2Doc.TeamName = "UPDATED_TEAM_2";
await client.ReplaceDocumentAsync(Team2Doc);
but if use the below code:
dynamic Team2Doc = client.CreateDocumentQuery<Document>(documentCollection.DocumentsLink).Where(d => d.TeamName== "team1").AsEnumerable().FirstOrDefault();
Team2Doc.TeamName = "UPDATED_TEAM_2";
await client.ReplaceDocumentAsync(Team2Doc);
I get this error:
"The best overloaded method match for
'Microsoft.Azure.Documents.Client.DocumentClient.ReplaceDocumentAsync(Microsoft.Azure.Documents.Document,
Microsoft.Azure.Documents.Client.RequestOptions)' has some invalid
arguments"
Is there anyway to retrieve a document by one of the properties and update the document?
The where clause is trying to query the property TeamName which does not exist in Document class.
Changing the type of the queryable to your data model should fix it.
For example, say you have the following data model:
public class EmployeeDocument : Document
{
// Other properties that you may have similarly defined ....
public class string TeamName
{
get
{
return this.GetValue<string>("TeamName");
}
set
{
this.SetValue("TeamName", value);
}
}
}
Then you can modify your query like this:
var team2Doc = client.CreateDocumentQuery<EmployeeDocument>(documentCollection.DocumentsLink).Where(d => d.TeamName== "team1").AsEnumerable().FirstOrDefault();
team2Doc.TeamName = "UPDATED_TEAM_2";
await client.ReplaceDocumentAsync(team2Doc);
Note that you have to use the EmployeeDocument, instead of the Document class, while creating the document queryable. That will let you query on EmployeeDocument properties.
SQL Version
Creating a document model for each of your existing data models may not be feasible if you have a large number of data models. In that case you may want to try out the SQL query syntax.
Refer to Aravind's answer in this post. The example he uses is for deleting documents, but it can be easily modified to update them too.
You can also create a model with Id:
public class Employee
{
[JsonPropery("id")]
public class string Id { get; set; }
public class string TeamName { get; set; }
}
And then replace the document using it's Id:
var employee= client
.CreateDocumentQuery<Employee>(documentCollection.DocumentsLink)
.Where(d => d.TeamName== "team1")
.AsEnumerable()
.FirstOrDefault();
employee.TeamName = "team2";
var documentUri = UriFactory.CreateDocumentUri(databaseName, collectionName, employee.Id);
await client.ReplaceDocumentAsync(employee);

Categories

Resources