GitHub

Working with documents

PouchDB is a NoSQL database, meaning that you store unstructured documents rather than explicitly specifying a schema with rows, tables, and all that jazz.

A document might look like this:

{
  "_id": "mittens",
  "name": "Mittens",
  "occupation": "kitten",
  "age": 3,
  "hobbies": [
    "playing with balls of yarn",
    "chasing laser pointers",
    "lookin' hella cute"
  ]
}

If you come from a SQL background, this handy conversion chart may help:

SQL concept PouchDB concept
table no equivalent
row document
column field
primary key primary key (_id)
index view

We'll discuss these concepts later on.

To store a document, you simply put it:

const doc = {
  "_id": "mittens",
  "name": "Mittens",
  "occupation": "kitten",
  "age": 3,
  "hobbies": [
    "playing with balls of yarn",
    "chasing laser pointers",
    "lookin' hella cute"
  ]
};
db.put(doc);

Whenever you put() a document, it must have an _id field so that you can retrieve it later.

So now let's get() the document by using its _id:

db.get('mittens').then(function (doc) {
  console.log(doc);
});

You should see:

{
  "name": "Mittens",
  "occupation": "kitten",
  "age": 3,
  "hobbies": [
    "playing with balls of yarn",
    "chasing laser pointers",
    "lookin' hella cute"
  ],
  "_id": "mittens",
  "_rev": "1-bea5fa18e06522d12026f4aee6b15ee4"
}

You can see a live example of this code.

The document looks exactly the same as when we put it, except... aha! What is this? There is a new field, _rev, that contains what looks like garbage. PouchDB gots some 'splainin' to do.

The new field, _rev is the revision marker. It is a randomly-generated ID that changes whenever a document is created or updated.

Unlike most other databases, whenever you update a document in PouchDB or CouchDB, you must present the entire document along with its current revision marker.

For instance, to increment Mittens' age to 4, we would do:

doc.age = 4;
doc._rev = "1-bea5fa18e06522d12026f4aee6b15ee4";
db.put(doc);

If you fail to include the correct _rev, you will get the following sad error:

{
  "status": 409,
  "name": "conflict",
  "message": "Document update conflict"
}

HTTP 409 is a standard HTTP error message that indicates a conflict.

So to update Mittens' age, we will first need to fetch Mittens from the database, to ensure that we have the correct _rev before we put them back. We don't need to manually assign the _rev value here (like we did above), as it is already in the doc we're fetching.

// fetch mittens
db.get('mittens').then(function (doc) {
  // update their age
  doc.age = 4;
  // put them back
  return db.put(doc);
}).then(function () {
  // fetch mittens again
  return db.get('mittens');
}).then(function (doc) {
  console.log(doc);
});

You can see a live example of this code.

Don't worry if the structure of this code seems strange! It's using promises, which will be discussed in the next chapter.

Now you should see the following:

{
  "name": "Mittens",
  "occupation": "kitten",
  "age": 4,
  "hobbies": [
    "playing with balls of yarn",
    "chasing laser pointers",
    "lookin' hella cute"
  ],
  "_id": "mittens",
  "_rev": "2-3e3fd988b331193beeeea2d4221b57e7"
}

As you can see, we have successfully updated Mittens' age to 4 (they grow up so fast!), and their revision marker has also changed to "2-3e3fd988b331193beeeea2d4221b57e7". If we wanted to increment their age to 5, we would need to supply this new revision marker.

Now that you understand a bit about how to create and update documents, let's take a small detour to talk about asynchronous code.