MongoDB C# 2.x Driver ElemMatch with Dictionaries - c#

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());

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);

How to query on the type of a nested object in RavenDB

Here's the setup, I've got an object that contains a list of events like so:
public class Order
{
public string Name {get; set;}
public List<OrderEvent> OrderEvents {get; set;}
public bool IsComplete() => OrderEvents.Any(e => e.GetType() == typeof(OrderComplete));
}
public class OrderEvent
{
public DateTime TimeStamp {get; set;}
}
public class OrderSubmitted : OrderEvent {...quantity ect...}
public class OrderPaidFor : OrderEvent {...amounts...}
public class OrderComplete : OrderEvent {...more stuff...}
Now I can dump this data into the database and pull it out and all is good, but how do I write a query to just get the completed orders without pulling all the orders client side and filtering there?
I've tried the following query but I've been told that I can't translate GetType like that.
Session.Query<Order>()
.Where(o => o.Events.Any(e => e.GetType() == typeof(OrderComplete)))
.ToList();
I'm pretty sure there's a good way to do this using JObjects and querying the $type property, but google and my efforts haven't come up with anything good yet.
Thanks.
What you can do is make the IsComplete() function a read only property instead. That way it would serialize down to IsComplete: true/false in the stored document.
Then you should be able to query like this:
Session.Query<Order>()
.Where(o => o.IsComplete)
.ToList();
Hope this helps!

Include_in_parent using Nest elastic search library

Is the IncludeInParent elastic property available in NEST C# like they have it for IncludeInAll.
If yes then how we add the IncludeInParent property to the objects.
I don't want to use fluent mapping as my index creating class is dynamic for an e.g:
public ESClient CreateIndex<T>() where T : class
{
if (!Client.IndexExists(f => f.Index(ESIndexName)).Exists)
{
Client.CreateIndex(ESIndexName, c => c
.NumberOfReplicas(1)
.NumberOfShards(4)
.Settings(s => s
.Add("merge.policy.merge_factor", "10")
.Add("search.slowlog.threshold.fetch.warn", "1s")
)
.AddMapping<T>(m => m.MapFromAttributes())
);
}
return this;
}
Is there a way I can specify IncludeInParent like IncludeInAll as below:
[ElasticProperty(IncludeInAll = true)]
public List<Cars> Cars { get; set; }
If not I think its nice to add IncludeInParent elastic property in NEST library.
What you ask can only be done using the fluent api.
The reply in this post Include_In_Parent option for ElasticSearch and NEST library may help you.

MongoDB Query C# Driver

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);

MongoDb BsonClassMap

I'm new to MongoDb and I'm currently using the CSharp drivers (version 1.2). My problems occur when using BsonClass map. Below is the code I'm tring to execute. I've simply defined a custom type I'd like to map to a BsonDocument.
In order to use this I'm taking advantage of BsonClassMap.RegisterClassMap(). When I hit the foreach statement (trying to access the first entry in the FinAs() results) I get the following error:
'Cannot deserialize Guid from BsonType ObjectId.'
From what I understand BsonClassMap uses GuidGenerator for objects of type Guid. Why am I getting this error?
Please note that the insertion is performed without any errors...and after performing the insert, newEmployee has an EmployeeId that's been automatically generated for it.
Here's the code I'm trying to run:
class Program{
static void Main(string[] args){
MongoServer server = MongoServer.Create();
MongoDatabase dataBase = server.GetDatabase("test");
MongoCollection<Employee>employees = dataBase.GetCollection<Employee>("employees");
BsonClassMap.RegisterClassMap<Employee>(cm =>
{
cm.MapProperty(c => c.Name);
cm.MapProperty(c => c.Email);
cm.MapIdProperty(c => c.EmployeeId);
});
var newEmployee = new Employee{ Name="Test", Email="test#test.com"};
employees.Insert(newEmployee);
foreach(Employee e in employees.FindAs<Employee>(Query.EQ("Name","Test")){
Console.Writeline(e.Name);
}
}
}
public class Employee
{
public Guid EmployeeId {get;set;}
public string Name {get;set;}
public string Email {get;set;}
}
I suspect you need to use SetIdMember to identify your id field. Any particular reason you aren't using ObjectId values instead of Guids?
Turns out I had some additional documents stored in my collection before I started using BsonClassMap. Their ObjectId was stored in a format native to MongoDB....unfortunately this format couldn't be parsed to a Guid. To fix the error I ended up wiping out all old entries.

Categories

Resources