MongoDB + NoRM- Concurrency and collections
Lets say we have the following document structure:
class BlogPost
{
[MongoIdentifier]
public Guid Id{get;set;}
public string Body{get;set;}
....
}
class Comment
{
[MongoIdentifier]
public Guid Id{get;set;}
public string Body {get;set;}
}
If we assume that multiple users may post comments for the same post,开发者_StackOverflow中文版 what would be the best way to model the relation between these?
if Post have a collection of comments, I might get concurrency problems, won't I ?
And placing a FK like attribute on Comment seems too relational , or?
You basically have two options: 1. Aggregate comments in the post document, or 2. Model post and comment as documents.
If you aggregate the comments, you should either a) implement a revision number on the post, allowing you to detect race conditions and implement handling of optimistic concurreny, or b) add new comments with a MongoDB modifier - e.g. something like
var posts = mongo.GetCollection<Post>();
var crit = new { Id = postId };
var mod = new { Comments = M.Push(new Comment(...)) };
posts.Update(crit, mod, false, false);
If you model post and comment as separate documents, handling concurrency is probably easier, but you lose the ability to load a post and its comments with a single findOne
command.
In my opinion, (1) is by far the most interesting option because it models the post as an aggregate object, which is exactly what it is when you put your OO glasses on :). It's definitely the document-oriented approach, whereas (2) resembles the flat structure of a relational database.
This is one of the canonical NoSQL examples. The standard method for doing this is to store the Comments
as an array of objects inside of the BlogPost
.
To avoid concurrency problems MongoDB provides several atomic operations. In particular there are several update modifiers that work well with "sub-documents" or "sub-arrays".
For something like "add this comment to the post", you would typically use the $push
command which will append the comment to the Post.
I see that you're using the "NoRM" drivers. It looks like they have support for atomic commands, as evidenced by their tests. In fact, their tests perform a "push this comment to the blog post".
They sort of give an example of how you'd model it over on the MongoDB page on inserting - I think you'd want a collection of comments exposed as a property on your post. You'd add comments to a given Post entity and this would do away with tying a Comment entity back to its parent Post entity which, as you are right to question, is something that makes sense in a RDBMS but not so much in a NoSQL solution.
As far as concurrency goes, if you don't trust Mongo to handle that for you, it's probably a big hint that you shouldn't be building an application on top of it.
I've created a test app that spawns 1000 concurrent threads adding "Comments" to the same "Post", the result is that alot of comments are lost.
So MongoDB treats child collections as a single value, it does not merge changes by default.
If I have a Comments collection on post, then I get concurrency problems when two or more users are adding comments at the exact same time (unlikely but possible)
So is it possible to add a comment to the post.comments collection without updating the entire post object?
精彩评论