Last time we looked at building manual secondary indexes in Couchbase: in effect, using Couchbase more for its key-value store properties than as a document store.
Continuing that key-value thread takes us to one of the most important questions in non-relational database modelling: how do we name our keys?
Three ways to build a key
There are, broadly, three ways to name a key used in a key-value store:
- deterministic: e.g. the email address someone uses to log into your system
- computer generated: e.g. a UUID
- some combination of the two: e.g. a UUID with a deterministic portion.
Which of these we choose depends largely on how we plan to access the data.
Let's say we're storing a user profile. Assuming no cookies, what are we guaranteed to know about our user after they've logged in?
Well, one thing would be their login name.
So, if we want to make life easy for ourselves in retrieving our user profile then we can key it with that person's login name. Everything else we need to know about that person could be derived from their user profile, in one way or another.
Pretty quickly we'd bump up against a problem: for a user to change their login name we now have to either create a new user profile under a new key or create a look-up document. We could insist that our users can never change their login names but it's unreasonable to make our users suffer unnecessarily.
We could just decided it's not that much effort to copy the profile data over to a new document under a new key. Alternatively, we could use something unrelated to the user's data for our key.
The main downside of a determinstic key is that, usually, it'll be an element of the data that we're storing.
If we use something like a UUID our users can update their email address, or whatever else we're using as a login name, without us having to recreate their profile document under a fresh key.
We can even get Couchbase to generate the key for us using a counter.
Here's how it works:
- Someone fills out the new account form and clicks “Submit”.
- We increment our counter document and it returns the next number up (e.g. 1001).
- We create a new user profile document keyed with 1001.
- We then create look-up documents for things such as their login name, enabling us to do a simple look-up on the data we hold at login time.
We also get some side benefits from this pattern, such as a counter telling us how many user profiles we've created during the application's lifetime.
It's when we combine both these methods that we can start to do really interesting things with key names.
We've looked before at when to embed data in one large document and when it's best to refer to other documents. When we choose to refer to data held in separate documents, we can build predictable key names from components that tell us something about what the document holds.
Let's look at our user profile again. The main document is stored under the key 1001. We're working on an ecommerce site so we also want to know all of the orders our customer has made. Simple: we store the list of orders under the key 1001::orders.
Similarly, our system might judge what sort of marketing emails to send to customers based on their total spend with the site. Rather than have the system calculate that afresh each time, we instead do it the NoSQL way: we calculate it once and then store it for later retrieval under the key 1001::orders::value.
Next time I'll be looking at how to model data most effectively for use with Couchbase views.