More on my Mongo repository pattern

OK, so it turns out I missed something yesterday. It’s somewhat subtle, but it likely would’ve turned into a headache if the system was under any load.

The problem was I was doing a .GetAll() whenever I needed to apply a filter. So, I would do a .GetAll().Where(..). The thinking was that this was like most of the LINQ world works, where it doesn’t matter because none of this code is run until you assemble the complete statement.

Well, when working with MongoDB, it has it’s own way to do filtering. The developers of this API account for this. They have MongoDB analogs for .FInd() for example. Well, that to me, meant that I would need to add a member onto my repository signature of:

public interface IMongoRepository<TEntity>
where TEntity : class, IMongoItem, new()
{
TEntity GetById(ObjectId id);

IEnumerable<TEntity> GetAll();

void Insert(TEntity item);

void Update(TEntity item);

void Delete(ObjectId id);
}

I first thought, why not add a .GetByFilter(..) and then figure out what the underlying MongoDB API needs. Well, it needs a IMongoQuery. That’s kind of ugly, because if I created a .GetByFilter(IMongoQuery query) – then the consumer would have to create an instance of this QueryBuilder class and generate a where clause. BUT, when I looked into what that querybuilder uses, I realized that I could wrap that functionality and expose, what would appear to be a normal “where” clause.

For example, imagine if I now had a signature like this:

public interface IMongoRepository<TEntity>
where TEntity : class, IMongoItem, new()
{
TEntity GetById(ObjectId id);

IEnumerable<TEntity> GetAll();

IEnumerable<TEntity> GetByFilter(Expression<Func<TEntity, Boolean>> query);

void Insert(TEntity item);

void Update(TEntity item);

void Delete(ObjectId id);
}

Note the new “GetByFilter” – that would be pretty sweet! That would mean the consumer could do things like:

repository.GetByFilter(customer=>customer.StateCode="CA");

So, what does that method look like? Here’s what I did to abstract away that internal IMongoQuery so that MongoDB is happy – and the end-developer is happy:

public IEnumerable<T> GetByFilter(Expression<Func<T,Boolean>> query)
{
if (query == null)
throw new ArgumentNullException("query");

MongoServer connection = GetConnection();

MongoDatabase database = connection.GetDatabase(DatabaseName);
MongoCollection<T> collection = database.GetCollection<T>(CollectionName);

// Translate our lambda into a MongoQuery
IMongoQuery mongoQuery = new QueryBuilder<T>().Where(query);

// Execute the query in it's native form, on Mongo
IEnumerable<T> items = collection.FindAs<T>(mongoQuery);

return items;
}

“Why did you go through all of this?”, you ask. Well, yesterday, I created a bunch of dummy rows in the database for authentication. Afterwards, I created a new account (which went at the end of that collection). When I log in as my first account, I got sub-second response time. When I log in as that new account, I was seeing 4-5 second response time!

That is what led into my looking into this more today. Now that I’ve changed the approach, I now get sub-second response time for all users no matter where you are in the collection. Put another way, I basically switched from doing:

repository.GetAll().Where(item=>item.Criteria=true)

to

repository.GetByFilter(item=>item.Criteria=true)

And internally, this works differently. The first approach got a Mongo cursor for ALL records, and then did client-side filtering. The second approach asked Mongo to do the filtering, and then got a cursor to just those results.

Posted in Linq, MongoDB, NoSQL, SQL, Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 5 other followers

%d bloggers like this: