⬆️ ⬇️

MongoDB and C # driver from 10gen, unobvious moments



Under the cat, partial loading of objects, search for an object by an element of an array nested in it, and some identifiers. Things that somehow took away my time to investigate how it works, and sometimes to dig into the source code of the driver when using MongoDB in a real project.



After the first post about mongodb, I switched to f # for a few weeks and when I returned, I realized that everything was forgotten. I had to re-read my own post, and apparently not one has to disassemble the Mongo, since even Dmitry Nesteruk stuck to me like. Lyrical digression ended. Immediately make a reservation, in this article, as in the previous discussion, we will focus on the driver, the link to which is posted on the Mongo website, this is the driver from 10gen.



Partial loading of objects



Starting to translate one of the real projects on Mongo, I immediately faced the need to load objects without nested collections that it contains. It should be noted that the driver in question works very simply. Through classes of helpers and wizards, he simply adds lines to get a working request in mongo javascript. In order not to load some kind of field, it is enough to specify field_name: 0 in the request



db.users.find({}, {thumbnail:0});<br/>



In the driver, everything is not so obvious, but after some digging we managed to get this code:

')

MongoCollection coll = GetCollection();<br/>

FieldsBuilder fbExclude = Fields.Exclude( new string []{“thumbnail”});<br/>

//can be FindAllAs<TEntity>()

MongoCursor result = coll.FindAll().SetFields(fbExclude);<br/>



All results in the cursor will be without a field thumbnail, checked, works great. All unloaded fields will be initialized with default values, for collections null. There is one subtlety here, if you don’t load any field, and then you record this object, it will completely legally grind the old one into the database and this test will drop on the last Assert:



public class Data<br/>

{<br/>

[BsonId]<br/>

public ObjectId Id {get;set;}<br/>

public int Area {get;set;}<br/>

}<br/>

Data x = new Data();<br/>

x.Area = 20;<br/>

var db = GetDb();<br/>

var coll = db.GetCollection<Data>( typeof (Data).FullName);<br/>

db.ClearColl<Data>();<br/>

coll.Save(x);<br/>

Data y = coll.FindAllAs<Data>().SetFields(Fields.Exclude( new string []{ "Area" })).FirstOrDefault();<br/>

Assert.AreEqual(0, y.Area);<br/>

coll.Save(y);<br/>

Data z = coll.FindAllAs<Data>().SetFields(

Fields.Exclude( new string [] { "Area" })

).FirstOrDefault();<br/>

//fail of course

Assert.AreEqual(20, z.Area);<br/>



I think this is somehow solved through Find and modify, but so far such a need is not worth it and all that needs to be changed is loaded completely.



Select by nested array element



In my case, there was a task to choose a region based on the postal code of one European country. Because for historical reasons, the general logic did not work in all cases, the region should be determined by the following algorithm: if there is an exact match, then select the region found, if there is no match, then leave the first 2 digits and make an assumption about the region based on the range of postal codes in region, and the ranges themselves may be many. The object looks something like this:



public class HighLevelCodeInterval <br/>

{<br/>

public HighLevelCodeInterval() { }<br/>

<br/>

public HighLevelCodeInterval( int mn, int mx) <br/>

{<br/>

Min = mn; Max = mx;<br/>

}<br/>

public int Min { get; set; }<br/>

public int Max { get; set; }<br/>

}<br/>

public class RegionObject <br/>

{<br/>

public RegionObject() <br/>

{<br/>

PostalCodes = new List< int >();<br/>

}<br/>

<br/>

[BsonId]<br/>

public int Id { get; set; }<br/>

public string Name { get; set; }<br/>

public List<HighLevelCodeInterval> HighLevelCodes { get; set; }<br/>

public List< int > PostalCodes { get; set; }<br/>

}<br/>



Then the query of the sample for a specific code will be:



QueryComplete q = Query.EQ( "PostalCodes" , postCode);<br/>

MongoCollection coll = GetCollection();<br/>

RegionObject result = coll.FindOneAs<RegionObject>();<br/>



I would say unexpectedly simple. For a range search, I’ll quote only the query itself:



int nCodeValue = …;<br/>

Query.And( <br/>

Query.LTE( "HighLevelCodes.Min" , nCodeValue),<br/>

Query.GTE( "HighLevelCodes.Max" , nCodeValue)<br/>

)<br/>



It must be admitted that the search by nested arrays in Mongo is very worthy.



Identifiers



The driver supports 2 built-in generator of identifiers: for Guid and ObjectId types, i.e. it is enough to decorate the properties of one of these types with the BsonId attribute and then everything will be done by itself. But the identifier can be any, my integer identifiers work fine for me, but their uniqueness is monitored by a program external to Mongo. It is also claimed that there is an opportunity to write the generator itself:



public class EmployeeIdGenerator : IIdGenerator <br/>

{<br/>

object GenerateId(){ â‹® }<br/>

bool IsEmpty( object id) { â‹® }<br/>

}<br/>

public class Employee <br/>

{ <br/>

[BsonId(IdGenerator = typeof (EmployeeIdGenerator)]<br/>

public int Id { get; set; }<br/>

// other fields or properties

}<br/>



I myself really did not try.



Conclusion



Document oriented databases are not suitable for all projects. If it does not work out beautifully, then DDD may not be applicable. I found that one of my projects fits perfectly into DDD and is incredibly pleased with the disappearance of a huge number of tables, if everything goes well, there will soon be performance tests in the spirit of MondoDB vs MSSQL on a really working application.



PS: I want to mention a utility that really helps to see what is written into the database as a result of the operations performed - this is Mongo Vue

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



All Articles