📜 ⬆️ ⬇️

MongoDB and C #. New features and non-obvious problems

Introduction


In early July, the next version (1.5) of the official MongoDB driver for C # was released. Among the innovations worth noting support typed queries. Now it is possible to use lambda functions in conjunction with Expression.
In this article, I will show examples of new syntax, which I really like (and I generally like Expression in C #), as well as demonstrate examples of queries, where, alas, Expression will not help us and will have to return to the usual lines. I also speculate why it is so, and whether everything will ever be great in C # when working with MongoDB.

Oh good


Yes, now instead of:

ObjectId articleId = new ObjectId("dgdfg343ddfg"); IMongoQuery query = Query.EQ("_id", articleId); 

You can write this:

 ObjectId articleId = new ObjectId("dgdfg343ddfg"); IMongoQuery query = Query<Article>.EQ(item => item.Id, articleId); 

in the presence, of course, of the Article class representing the schema of the document. It is worth noting that all the methods associated with sampling in QueryBuilder <T> are completely analogous to those in QueryBuilder. True, you still need to use Query.And or Query.Or to combine queries. Well, this is understandable, since all methods return the same QueryBuilder. In fact, they can be combined as you like.
UpdateBuilder <T> also appeared for UpdateBuilder with appropriate methods.
As it was before:
')
 Article article = new Article(); IMongoUpdate update = Update.PushWrapped("Articles", article); 

How can now:

 Article article = new Article(); IMongoUpdate update = Update<Article>.Push(item => item.Articles, article); 

In my opinion, much better (if we talk about beauty and control). In general, expression trees are a very powerful and beautiful thing. Here, only the very tip of the iceberg, but I have used them a lot where. In many ways, this is a Reflection with a human face.
Anyway. Here everything seems to be simple. We turn to less beautiful things.

About sad


Simple queries like searching by value pass with a bang. A little more complicated, by the way, too. For example, searching for an element in an array:

 IMongoQuery query = Query<Article>.ElemMatch<Comment>(item => item.Comments, builder => builder.EQ(item => item.Id, comment.Id)); 

But the tension is already felt.
Now let's throw some coal. There is a document of this content:

 _id : "s3d4f5d6sf", array1 : [{ _id : "cv434lfgd45", array2 : [{ _id : "df4gd45g43f4", name : "Logic" }, { ... }] }, { ... }] 

And here we have the task to add one more element to array2. In a programme:

1) the main document will be described by the Doc model class
2) turn array1 into List <Item1>, where Item1 is the model class for each element of array1
3) turn array2 into List <Item2>, where Item2 is the model class for each element of array2

(I deliberately depersonalized the document so that there were no complaints about the data structure)

There are no problems with finding problems:

 IMongoQuery query = Query.And( Query<Doc>.EQ(item => item.Id, new ObjectId("s3d4f5d6sf")), Query<Doc>.ElemMatch<Item1>(item => item.Array2, builder => builder.EQ(item => item.Id, new ObjectId("df4gd45g43f4"))); 

But with the request to update the complexity arose (I deliberately did not consider the option of sampling the document, adding data to it, and then writing to the database. For the option is too non-optimal. I would also like to update the data with an atomic operation rather than a cascade of samples / records). You need to get to the nested array array2. If you use standard MongoDB tools, you can do this:

 IMongoUpdate update = Update.PushWrapped<Item2>("array1.$.array2", new Item2()); 

I could not figure out how to get around the magic lines, and the longer I looked at the expression "array1. $. Array2", the more suspicious thoughts I had.

Oh high


Let's start from afar.

There are "static" languages. For example, C #. In them, the structures of elements are known before compilation (most often). And it is with these structures that we operate. More specifically, we operate with classes, such schemes. Well, on the data side, we have objects - instances of classes.

There are “dynamic” languages. For example, Javascript. In them, it is common practice to create a data structure in the course of execution. In general, we operate with an “empty” object and add / remove methods and fields. Structures as such do not exist at all. There is only a starting point (prototype) from which we make a start.

In the organization of the data, you can also draw some kind of analogy.
In relational databases there are hard data schemes and dependencies between them.
In document-oriented there are only documents, nesting and lists.

And now I will fantasize and bring coarser analogies.
The problem in composing a query using Expression for the case of “array1. $. Array2” is that in C # we operate with structures (classes) to work with documents (objects). The "power" of the query adds fuel to the fire. If we leave only the first condition in the sample request, then if there is a desire (setting the flag in multiupdate) we will be able to add an element to each of the array2 array. After all, in fact, our beautiful query (even if we can write it) will still be converted to "array1. $. Array2". For a more extensive document with a deep level of nesting, the situation will only get worse.

I'm not saying that it is impossible to come up with an Expression syntax for such a task, it just seems to me that this syntax will lose clarity. That is, the connection between the syntactic and semantic parts (intuitive, understandably) will melt. I'm not sure what exactly I want in my projects.
For me, the conclusions are pretty obvious: a beautiful syntax in relatively simple cases is great and cool, but in more complex situations it can be concluded that the Expression does not simplify the work, but only multiplies the ambiguities.

Conclusion


I do not have much experience with MongoDB, and I will gladly include in the article a solution to the described query problem, as well as reasoned arguments / controversy on building architecture and code in C # for MongoDB.

Source: https://habr.com/ru/post/148623/


All Articles