views:

233

answers:

2

Let me state a simple example: You have an Order and a Shopping Cart. One way I envision persisting this is to save an Order document and a Cart document. The Order document could have a field called "shopping-cart" whose value is the UUID of the relevant Cart document. Another way I can imagine doing this is to save an Order document with the "shopping-cart" field containing an associative array of the entire Cart. In other words, instead of saving the Cart explicitly as an independent document, I embed the Cart document in the Order document.

What if we decide later that a Cart should be persistent, so a returning user will find his half-finished Cart waiting for him across sessions? I imagine we could then combine both methods, keeping the Cart separate while it's incomplete and embedding it in the Order document when it's finalized/purchased.

Both methods would work, though I worry about CouchDB not having foreign key constraints; in the first method the Cart document could be deleted, leaving you with a corrupt data set.

How do you decide which method to use? Is one of these methods more idiomatic to CouchDB? Are there any methods I missed?

I'm new to CouchDB so it's difficult for me to see the advantages/disadvantages to having a more or less normalized structure.

A: 

If the Order and Cart objects have a one to one relationship (which is how it sounds), then the second approach makes the most sense ... just keep the associated objects contained in one. That gives you the data integrity you're looking for and makes it simple ;-)

Joel Martinez
+1  A: 

If on the other hand they aren't one to one then using a complex key you can use view collation to do a join.

function(doc) {
  if (doc.type = 'order') {
    emit([doc.cartid, 1], doc);
  } else if (doc.type = 'cart') {
    emit([doc.id, 0], doc);
  }
}

the docs will be collated by cartid with the orders coming after the cart. Your application code can easily join this stream and you can query by a particular cartid using startkey and endkey.

see: View Collation for the collation rules.

You can also use a reduce to join them together as well.

Just change the map function to this:

function(doc) {
  if (doc.type = 'order') {
    emit([doc.cartid, 1], {cartid: doc.cartid, orders: [doc]});
  } else if (doc.type = 'cart') {
    emit([doc.id, 0], {cartid: doc.id, orders: [], cart: doc);
  }
}

and add a reduce function like this:

function(keys, values) {
  var out = {cartid: null, orders: [], cart: null};
  for (idx in values) {
    var doc = values[idx];
    out['cartid'] = doc.cartid;
    if (doc.cart) { out['cart'] = doc.cart };
    for (idx2 in doc.orders) {
      out.orders.push(doc.orders[idx2]);
    }
  }
  return out;
}

This will return a single document per cart which will a cartid, an array of order documents and a cart document.

Apologies if there are errors in the above code but I don't have a couchdb test instance handy to try them out. You should get the general idea though and the CouchDB wiki has more details.

Jeremy Wall