I'm currently looking at putting together a .NET role provider for couchbase for use in a project. I'm looking to model this in 2 document types.
The first document type is a simple list of all of the roles available in the application. This makes it easy to support Add, Delete, GetAllRoles etc. This document would have a fixed key per application, so "applicationname_roles" so it is well known from the codes point of view and quickly retrievable.
The second document maps a user to a role, so for example
{ "type": "roleprovider.user-role", "user": "user1", "role": "role1", "application": "app1" }
The key for this document type would be of the format "applicationname_roles_username_rolename", making the most common operation of testing if a user is in a particular role trivial and quick.
To support the GetRolesForUser or GetUsersInRole methods of the .NET role provider I'm looking at using a view of the form.
function (doc, meta) {
if(meta.type != 'json')
{
return;
}
if (doc.type == "roleprovider.user-role")
{
if(doc.application && doc.user && doc.role)
{
emit([doc.application, "user:" + doc.user, doc.role]);
emit([doc.application, "role:" + doc.role, doc.user]);
}
}}
So for every user to role mapping we get 2 rows emitted into the view. The first allows us to query the view for what roles a user is in. The second for which users are in a role. The .NET provider simply needs to prefix either "user:" or "role:" based on wether or not its querying GetRolesForUser or GetUsersInRole to filter down on what it needs.
So now to the question, this all seems reasonably trivial and logical, however its the first time I've worked with Couchbase and wondered if I was falling into any traps with this? An obvious alternative approach would be to use 2 views, but in my reading I've seen it mentioned that its best to keep the number of design documents down and the number of views within those down as well, see Perry Krug's reply in couchbase views per bucket discussion, in this he mentions trying to 'generate multiple different queries off of one index'. So basically I'm wondering if the approach I've described above is prescribing to what Perry is saying, or if I'm just tricking myself and going to cause myself pain down the line.
Thanks for any pointers.
(Note: resurrecting an ancient question because it's not been answered yet, and it might interest someone else.)
Your approach in general is sound. But, unless you're actually experiencing performance issues, I would stick to one view per query type in this case. While combining multiple queries into a single view will reduce the amount of work Couchbase needs to do to build the views, it will increase the cost of each query, as it will have to scan an index that's twice as large. If you're not using many other views at the same time, I would keep the views separate. In fact, I would even put them in different design documents, so that Couchbase will process them concurrently by different indexer threads. There's no need to prematurely optimize for a performance problem that doesn't exist yet; go with the straightforward solution first, then optimize if necessary.
If you do run into a performance problem with query reads, you may need to consider moving from views to a key/value based approach. Specifically, storing a separate document for every application-user and application-role pair, and appending a list of roles to the former and users to the latter. This means that you essentially end up maintaining the indexes yourself, but it will give you an order of magnitude improvement in read latency. Take a look at this blog about maintaining sets with append operations.
Yes, I do see the irony in advocating against premature optimization in one paragraph and suggesting a performance optimization in the next. So I'd like to emphasize that you should first test whether the view approach gives you acceptable performance - and for most reasonable applications it will - and if it does, stick to that because it's much more straightforward. If you discover that you need better performance after all, then start looking into the second alternative.