<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>MongoDB | Jacob Aloysious</title><link>https://jacobaloysious.in/category/mongodb/</link><atom:link href="https://jacobaloysious.in/category/mongodb/index.xml" rel="self" type="application/rss+xml"/><description>MongoDB</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><lastBuildDate>Sun, 14 Mar 2021 00:00:00 +0000</lastBuildDate><image><url>https://jacobaloysious.in/images/icon_hu4591c05f594249c11c1e99a3a8f1f246_3759739_512x512_fill_lanczos_center_2.png</url><title>MongoDB</title><link>https://jacobaloysious.in/category/mongodb/</link></image><item><title>MongoDB - Hybrid Schema</title><link>https://jacobaloysious.in/post/tech_mogodb-hybridschemadesign/</link><pubDate>Sun, 14 Mar 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_mogodb-hybridschemadesign/</guid><description>&lt;p>I recently ran into a situation where in we have a session and each Session produces a lot of messages (sync) and each message has an associated timestamp and order.
This is similar to a blog post (session) and its comments (messages).&lt;/p>
&lt;p>Since the messages are unbounded (not sure how many would be produced). In my initial design - I had a separate collection for Messages. Each Session would have one corresponding MessageW (wrapper) document and each MesssageW document would have an array of messages (acutal messages).&lt;/p>
&lt;p>With all the available data I had - the size of the MessageW document never went above 1MB - so we were all good. But unfortunately, we had a new use case which broke this assumption 😟. The session was running for hours and there was tons of messages getting generated and yes - we hit the 16MB ceiling 😓.&lt;/p>
&lt;p>Obvious solution was to create to create a new one doucment for every new message. So, that we never hit the size limit (16MB) of a mongoDB document. But, since we are looking at unbounded message - there could huge (million message docs) number of documents.&lt;/p>
&lt;p>So, we choose to go with the &lt;strong>Hybrid Approach&lt;/strong>&lt;/p>
&lt;p>In the hybrid approach, we go with our initial option - where we have a MessageW (wrapper) document - which has an array of Messages. Then we add a constrain on the document i.e. the size of the array should not be more than a given arbitary number. For every new message we increment the &lt;em>count&lt;/em>. If the number count crossed the constraint - then create a new MessageW document, inc the page number and also update the Session (&lt;em>NumberOfMsgPages&lt;/em>). The reader would get the &lt;em>NumberOfMsgPages&lt;/em> and read backwards - until he hit page 0. Voila!! 😍&lt;/p>
&lt;p>&lt;strong>Important Items to note:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Session Object : will have a &lt;em>NumberOfMsgPages&lt;/em> property&lt;/li>
&lt;li>MessageW Object: would have a &lt;em>Page&lt;/em> and &lt;em>Count&lt;/em> property.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Operators:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/update/inc/" target="_blank" rel="noopener">$inc&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/update/set/" target="_blank" rel="noopener">$set&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/query/lt/" target="_blank" rel="noopener">$lt&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-upsert" target="_blank" rel="noopener">upsert&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Since: A code is worth a thousand words. Here is the code 😊&lt;/p>
&lt;pre>&lt;code> public void InsertMessage(Message message, ObjectId parentId)
{
var builder = Builders&amp;lt;Message&amp;gt;.Filter;
// Find MessageObject: given parentId and CurrentPageNo
// And: Constraint: Number of messages is less than 1000.
var filter = builder.Eq(&amp;quot;parentId&amp;quot;, parentId)
&amp;amp; builder.Lt(&amp;quot;Count&amp;quot;, 1000)
&amp;amp; builder.Eq(&amp;quot;Page&amp;quot;, CurrentMessagePageCount);
// Try and Insert into existing Message Document array And Increment the Count.
var update = Builders&amp;lt;Message&amp;gt;.Update
.AddToSet(x =&amp;gt; x.Messages, message)
.Inc(&amp;quot;Count&amp;quot;, 1);
// If the constraint with &amp;quot;Count&amp;quot; matches.
// ModifiedCount would be greater than Zero
var result = MsgCollection.UpdateOne(filter, update);
if (result.IsAcknowledged)
{
if(result.ModifiedCount== 0)
{
// Increment the Page count
CurrentMessagePageCount += 1;
// Update: NumberOfMsgPages in Session Collection
var sessionFilter = Builders&amp;lt;MySession&amp;gt;.Filter.Eq(&amp;quot;_id&amp;quot;, parentId);
var updateSession = Builders&amp;lt;MySession&amp;gt;.Update
.Inc(&amp;quot;NumberOfMsgPages&amp;quot;, 1);
SessionsCollection.UpdateOne(sessionFilter, updateSession);
// Find MessageObject: given parentId and CurrentPageNo-which is obviously not found.
// So, we will use &amp;quot;IsUpsert=true&amp;quot; to add a new document.
// And: Number of messages is less than 20.
var filter2 = builder.Eq(&amp;quot;parentId&amp;quot;, parentId)
&amp;amp; builder.Eq(&amp;quot;Page&amp;quot;, CurrentMessagePageCount);
// Try: Insert into existing Message And Increment the Count
var update2 = Builders&amp;lt;Message&amp;gt;.Update
.AddToSet(x =&amp;gt; x.Messages, message)
.Set(&amp;quot;Page&amp;quot;, CurrentMessagePageCount)
.Inc(&amp;quot;Count&amp;quot;, 1);
MsgCollection.UpdateOne(filter2, update2, new UpdateOptions() { IsUpsert = true });
}
}
}
&lt;/code>&lt;/pre>
&lt;p>A obvious reaction after reading the code is - Ok, whats the big deal here?&lt;/p>
&lt;p>Lets take a code walk through: (think how would you achive this in your traditional SQL DB&amp;rsquo;s)&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Code never check if the MessageW document &lt;strong>exits&lt;/strong> before Update.&lt;/p>
&lt;ul>
&lt;li>How did it work: &lt;strong>&lt;em>&amp;ldquo;$IsUpsert=true&amp;rdquo;&lt;/em>&lt;/strong> is the magic word.&lt;/li>
&lt;li>&lt;em>$IsUpsert=True&lt;/em>: MongoDB would internally insert a &lt;strong>new&lt;/strong> document - if the document doesn&amp;rsquo;t exists.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Updating &lt;strong>counter&lt;/strong> is happening inside MongoDB:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;em>$inc&lt;/em>&lt;/strong> mongoDB operator - increments a specific field - in our case &lt;em>count&lt;/em>&lt;/li>
&lt;li>Hence, Client doesn&amp;rsquo;t have to read the document to know the actual count and then increment/update.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Constraint Check:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;em>$lt&lt;/em>&lt;/strong> MongoDB operator would handle the constraints.&lt;/li>
&lt;li>Update goes through if the constraint is met.&lt;/li>
&lt;li>As part of the result, we could check if any doc was modified&lt;/li>
&lt;li>Cool part - the second half of the code runs only once in 1000 inserts&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>How did we come up with the arbitary constraint number? well, I dont think we have a thumb rule here. Since we don&amp;rsquo;t track the actual document size - you would have to look at your data and come up with the sizing.&lt;/p>
&lt;p>BTW, I am new to MongoDB and learning. If there are better approaches - feel free to write back to me. Thanks.&lt;/p>
&lt;p>Reference:
&lt;a href="https://www.oreilly.com/library/view/mongodb-applied-design/9781449340056/" target="_blank" rel="noopener">MongoDB Applied Design Patterns, by Rick Copeland&lt;/a>&lt;/p></description></item><item><title>MongoDB Tit Bits</title><link>https://jacobaloysious.in/post/tech_mogodb-titbits/</link><pubDate>Sun, 07 Mar 2021 00:00:00 +0000</pubDate><guid>https://jacobaloysious.in/post/tech_mogodb-titbits/</guid><description>&lt;p>While I am getting started with MongoDB for my new project. Here are the few quick notes - based on my readings so far:&lt;/p>
&lt;p>One Key Takeaway:&lt;/p>
&lt;blockquote>
&lt;p>Data accessed together should be stored together&lt;/p>
&lt;/blockquote>
&lt;h4 id="wiredtiger">WiredTiger:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/core/wiredtiger/" target="_blank" rel="noopener">WiredTiger&lt;/a> is the default storage engine for mongo db&lt;/li>
&lt;li>It stores documents and indexes on disk&lt;/li>
&lt;li>In memory cache stores some doc and frequency used index - working set
&lt;ul>
&lt;li>50% of (RAM -1 GB) Or, 256MB&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="massive-arrays">Massive Arrays:&lt;/h4>
&lt;ul>
&lt;li>Max Document size is 16MB&lt;/li>
&lt;li>Index performance on arrays decreases as array size increases&lt;/li>
&lt;li>
&lt;a href="https://www.mongodb.com/blog/post/building-with-patterns-the-extended-reference-pattern" target="_blank" rel="noopener">Extended Reference pattern&lt;/a>: where we duplicate some and not all data&lt;/li>
&lt;/ul>
&lt;h4 id="index">Index:&lt;/h4>
&lt;ul>
&lt;li>Each index is atleast 8KB&lt;/li>
&lt;li>Index take up storage - One File for each collection and one file for each Index (WiredTiger impl)&lt;/li>
&lt;li>Write performance as index needs to updated&lt;/li>
&lt;li>Limit each collection to 50 Index max&lt;/li>
&lt;li>Do: Add index for frequently supported queries - improves read performance&lt;/li>
&lt;li>Don’t: Create unnecessary indexes - reduce performance and takes up space&lt;/li>
&lt;/ul>
&lt;h4 id="bloated-documents">Bloated Documents:&lt;/h4>
&lt;ul>
&lt;li>Do: Data accessed together should be stored together&lt;/li>
&lt;li>Don’t: bloat your document with related data that is not accessed to gether&lt;/li>
&lt;li>Data that is related to each other should NOT necessarily stored together&lt;/li>
&lt;li>Remove bloat from frequently used documents - it can be inmemory wire tiger cache&lt;/li>
&lt;li>Data Duplication is OK (depends!)
&lt;ul>
&lt;li>Summary document and Details document - ref via links&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="case-incentive-query">Case Incentive Query:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/query/regex/" target="_blank" rel="noopener">$regex&lt;/a> queries are case &lt;em>insensitive&lt;/em> but not performant&lt;/li>
&lt;li>Non-$regex queries are case &lt;em>sensitive&lt;/em>&lt;/li>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/collation/" target="_blank" rel="noopener">Collation&lt;/a>:
&lt;ul>
&lt;li>Language specific rules for MongoDB for string comparison&lt;/li>
&lt;li>Strength ranges from 1-5&lt;/li>
&lt;li>Strength 1-2 will give you case insensitive&lt;/li>
&lt;li>Query: {&amp;ldquo;first_name&amp;rdquo;: {$regex: /Jacob/i }} - case insensitive&lt;/li>
&lt;li>Query: {&amp;ldquo;first_name&amp;rdquo;: &amp;ldquo;Jacob&amp;rdquo;} - case sensitive&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="accessing---separate-data-together">Accessing - Separate data together:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/" target="_blank" rel="noopener">$lookup&lt;/a>:
&lt;ul>
&lt;li>Is used to join data from more than one collection&lt;/li>
&lt;li>Great for rarely used queries or analytical queries (batch run overnight)&lt;/li>
&lt;li>Very slow and resource intensive&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h4 id="upserthttpswwwmongodbtutorialorgmongodb-crudmongodb-upsert">
&lt;a href="https://www.mongodbtutorial.org/mongodb-crud/mongodb-upsert/" target="_blank" rel="noopener">Upsert&lt;/a>:&lt;/h4>
&lt;ul>
&lt;li>Upsert is a combination of update and insert. Upsert performs two functions:
&lt;ul>
&lt;li>Update data if there is a matching document.&lt;/li>
&lt;li>Insert a new document in case there is no document matches the query criteria.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Personally: I found this very interesting, as for my use cases - I dont need to track if the document already exist.&lt;/li>
&lt;/ul>
&lt;p>I understand - the blog looks a bit navive (read, nothing interseting), sry I am just getting started. BTW, I plan to write a follow blog - deep dive on my specific use cases and data modelling.&lt;/p>
&lt;h4 id="reference">Reference:&lt;/h4>
&lt;ul>
&lt;li>
&lt;a href="https://www.youtube.com/channel/UCK_m2976Yvbx-TyDLw7n1WA" target="_blank" rel="noopener">MongoDB Channel&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.mongodbtutorial.org/" target="_blank" rel="noopener">MongoDB Tutorial&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>