Jun 30, 2015

The monolithic approach to CouchDB views

The monolithic approach to CouchDB views

Imagine you have an app that created one document for each day. The docs ids are easily "2015-02-05", "2015-02-06" and so on. Nothing could be more simple. Let's say each day you record "sales", "expenses" and "events", so this a document for a typical day for the retail management Couchapp for an orchid shop:

{
  "_id": "2015-02-04",
  "sales": [{
    "what": "A blue orchid",
    "price": 50000
  }, {
    "what": "A red orchid",
    "price": 3500
  }, {
    "what": "A yellow orchid",
    "price": 11500
  }],
  "expenses": [{
    "what": "A new bucket",
    "how much": 300
  },{
    "what": "The afternoon snack",
    "how much": "1200"
  }],
  "events": [
    "Bob opened the store",
    "Lisa arrived",
    "Bob went home",
    "Lisa closed the store"
  ]
}

Now when you want to know what happened in a specific day, you know where to look at.

But you don't want only that, you want profit reports, cash flows, day profitability, a complete log of the events et cetera. Then you create one view to turn this mess into something more useful:

function (doc) {
  var spldate = doc._id.split("-")
  var year = parseInt(spldate[0])
  var month = parseInt(spldate[1])
  var day = parseInt(spldate[2])

  doc.sales.forEach(function (sale, i) {
    emit(["sale", sale.what], sale.price)
    emit(["cashflow", year, month, day, i], sale.price)
  })
  doc.expenses.forEach(function (exp, i) {
    emit(["expense", exp.what], exp.price)
    emit(["cashflow", year, month, day, i], -exp.price)
  })
  doc.events.forEach(function (ev, i) {
    emit(["log", year, month, day, i], ev)
  })
}

Then you add a reduce function with the value of _sum and you get a bunch of useful query endpoints. For example, you can request

/_design/orchids/_view/main?startkey=["cashflow", "2014", "12"]&endkey=["cashflow", "2014", "12", {}]