Introduction to CouchDB with .NET part 7: viewing changes made in the database

Introduction

In the previous post we looked at batch insertions and updates in CouchDB. Batch operations are very useful if we intend to insert or update multiple documents at once. We can mix insertions, updates and deletions in the same batch operation. The individual modifications in the batch are treated in isolation. This implies that if one modification fails then it won’t affect the others. In other words batch operations are non-atomic in CouchDB.

In this post we’ll look at a way to check what changes have been made to a CouchDB database.

List of changes made to a database

The CouchDB HTTP API has a special endpoint /db-name/_changes to view the changes made to the selected database. E.g. to view the changes made to the persons database we would issue the following request:

GET http://localhost:5984/persons/_changes

The API responds with a list of changes sorted by date in an ascending order. Here’s what I got on my request:

{
	"results": [{
		"seq": "1-g1AAAAF1eJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rkQGPoiQFIJlkT1idA0hdPGF1CSB19QTV5bEASYYGIAVUOj8rgzmRMRcowG5qYWxgaGGMTR9B0xZATNtPjM0HIGrvE6P2AUQtyEdZAEzoeNA",
		"id": "3559d9c81c785b6bfc27a3490401b892",
		"changes": [{
			"rev": "1-de9b80faccbc3bcd0dc04a8e2eee51a0"
		}]
	},
	{
		"seq": "3-g1AAAAGXeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPcikRAZ86hxA6uIJq0sAqasnqC6PBUgyNAApoNL5IF8wgn1hamFsYGhhTKIvIKYtgJi2nxibD0DU3idG7QOIWpCPsgCO2YLR",
		"id": "3559d9c81c785b6bfc27a34904019fa5",
		"changes": [{
			"rev": "2-53da1176c8605ee7c21b7db5a1be0164"
		}]
	},
	{
		"seq": "10-g1AAAAG5eJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPcikRAZ86hxA6uIJq0sAqasnqC6PBUgyNAApoNL5IF8wgn1hamFsYGhhTKIvIKYtgJi2nxibD0DU3gfZzA62OdnQ0sQgLYksmx9ATAP5OQsAStiNBg",
		"id": "3559d9c81c785b6bfc27a3490401af41",
		"changes": [{
			"rev": "4-0ab70230331b8c0cb6b084e789795cae"
		}]
	},
	{
		"seq": "14-g1AAAAHbeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPdQkFrBJqSYWxkmmKaSa5AAyKR5kUiIDPnUJIHX1BNXlsQBJhgYgBVQ6H-Q6RrDrTC2MDQwtjEl0HcS0BRDT9hNj8wGI2vsgm9nBNicbWpoYpCWRZfMDiGkgP2cBAK34lz4",
		"id": "3559d9c81c785b6bfc27a3490401b00f",
		"changes": [{
			"rev": "4-b191581a2cd889f62b696d3c0b549448"
		}]
	},
	{
		"seq": "15-g1AAAAHbeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPdQkFrBJqSYWxkmmKaSa5AAyKR5kUiIDPnUJIHX1BNXlsQBJhgYgBVQ6H-Q6RrDrTC2MDQwtjEl0HcS0BRDT9hNj8wGI2vsgmznANicbWpoYpCWRZfMDiGkgP2cBAK5Glz8",
		"id": "3559d9c81c785b6bfc27a349040177b0",
		"changes": [{
			"rev": "4-05a41ea3b37ea0dea540fc34890c9369"
		}],
		"deleted": true
	},
	{
		"seq": "18-g1AAAAH9eJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPdQkFrBJqSYWxkmmKaSa5AAyKR5kUiIDPnUJIHX1BNXlsQBJhgYgBVQ6H-Q6RrDrTC2MDQwtjEl0HcS0BRDT9hNj8wGI2vsgmznANicbWpoYpCWRZfMDiGngUGYGm5ZmmGyWaoDVtCwAWQahng",
		"id": "3559d9c81c785b6bfc27a34904018a42",
		"changes": [{
			"rev": "3-af434f7f64b7304ae495bb58f05d1521"
		}],
		"deleted": true
	},
	{
		"seq": "21-g1AAAAJTeJydz0sOgjAQBuAKJOLSE-gRWiilruQmSh-kEoSFstab6E30JnoTbCmJMSFE2MwkM5kv_xQAAF-5Aix5VXMlWFJUPC1UdToXeuWkgK2apsmVmzpHPZhDEgsc8L6DAYatdWXbTvJaSWIaskiMlRIj7ToJtBIhaYSRzrSoSyGzQynFkLA3wuVHgIzGRIo_hdLTFVx108jNKLNWiWgIEQ1HfmS1u9UeRnOtFlCCMjxJe1rtZTS_1TjaYJixSdrbas03W4Y4kbBXyz95mrk8",
		"id": "3559d9c81c785b6bfc27a3490401990e",
		"changes": [{
			"rev": "3-03208d7c4416be70af857a2b8ab22fbe"
		}],
		"deleted": true
	}],
	"last_seq": "21-g1AAAAJTeJydz0sOgjAQBuAKJOLSE-gRWiilruQmSh-kEoSFstab6E30JnoTbCmJMSFE2MwkM5kv_xQAAF-5Aix5VXMlWFJUPC1UdToXeuWkgK2apsmVmzpHPZhDEgsc8L6DAYatdWXbTvJaSWIaskiMlRIj7ToJtBIhaYSRzrSoSyGzQynFkLA3wuVHgIzGRIo_hdLTFVx108jNKLNWiWgIEQ1HfmS1u9UeRnOtFlCCMjxJe1rtZTS_1TjaYJixSdrbas03W4Y4kbBXyz95mrk8",
	"pending": 0
}

That’s quite a long JSON but most of the space is take up by the “seq” property.

Here’s what the above JSON is telling us:

  • results: the array of changes made to the documents in the database
  • seq: each modification gets a unique sequence ID.
  • id: the id of the document where the changes were made
  • changes: an array of the revision numbers of the changes. The response will only contain the change with the most recent revision number for each document.
  • deleted: only included if the document has been deleted
  • last_seq: the sequence ID of the most recent modification. This can be useful if you’re planning to periodically check the changes since the previous time you called for the list of changes. We’ll see the filtering options later on.
  • pending: the number of items remaining in the response feed

We can also retrieve the properties of the documents with the include_docs flag:

GET http://localhost:5984/persons/_changes?include_docs=true

The response will include a property called “doc” with the document details. Here’s an example:

"doc": {
        "_id": "3559d9c81c785b6bfc27a3490401b892",
        "_rev": "1-de9b80faccbc3bcd0dc04a8e2eee51a0",
        "first-name": "Freddie",
        "last-name": "Mercury",
        "age": 80
      }

If you need to view the list of changes in the reverse order then attach the descending flag as follows:

http://localhost:5984/persons/_changes?descending=true

This will put the most recent change on top of the results array.

Limit the results

The “limit” and “since” query parameters help us reduce the size of the response. “since” is set to the sequence ID from which we want to view the list of changes. The “limit” is the number of changes we’d like to get from the sequence ID referenced in the “since” parameter. These two parameters can be used independently, i.e. the URL doesn’t need to include limit and since for the request to be valid. Here’s an example:

GET http://localhost:5984/persons/_changes?since=10-g1AAAAG5eJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPcikRAZ86hxA6uIJq0sAqasnqC6PBUgyNAApoNL5IF8wgn1hamFsYGhhTKIvIKYtgJi2nxibD0DU3gfZzA62OdnQ0sQgLYksmx9ATAP5OQsAStiNBg&limit=2

This will return 2 changes made after the sequence ID “10-g1AAA…” :

{
	"results": [{
		"seq": "14-g1AAAAHbeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPdQkFrBJqSYWxkmmKaSa5AAyKR5kUiIDPnUJIHX1BNXlsQBJhgYgBVQ6H-Q6RrDrTC2MDQwtjEl0HcS0BRDT9hNj8wGI2vsgm9nBNicbWpoYpCWRZfMDiGkgP2cBAK34lz4",
		"id": "3559d9c81c785b6bfc27a3490401b00f",
		"changes": [{
			"rev": "4-b191581a2cd889f62b696d3c0b549448"
		}]
	},
	{
		"seq": "17-g1AAAAIxeJyd0EsOgjAQBuARSNClJ9AjtAIFV3IT7YtUgrBQ1noTvYneRG-CpWVjQlDcTJN2-v3TFgAwVa6AOa9qrgRLi4rTQlXHU6GPHAps0TRNrlzqHPSGj0gswhXvuzDAsKWubNNJnpFkmAQsEmOltJW2nQRGIoRGIdYzzepSyGxfSjEk7Frh_CEglsREih-F0tMVLnrRyLVVJkaJkgDhJBj5IqvdrHbPKXztfdjeZ5vsm2SO1yHK2F_JL6uZ33CNlmFOJOrV8jdEDa9j",
		"id": "3559d9c81c785b6bfc27a34904018a42",
		"changes": [{
			"rev": "3-af434f7f64b7304ae495bb58f05d1521"
		}],
		"deleted": true
	}],
	"last_seq": "17-g1AAAAIxeJyd0EsOgjAQBuARSNClJ9AjtAIFV3IT7YtUgrBQ1noTvYneRG-CpWVjQlDcTJN2-v3TFgAwVa6AOa9qrgRLi4rTQlXHU6GPHAps0TRNrlzqHPSGj0gswhXvuzDAsKWubNNJnpFkmAQsEmOltJW2nQRGIoRGIdYzzepSyGxfSjEk7Frh_CEglsREih-F0tMVLnrRyLVVJkaJkgDhJBj5IqvdrHbPKXztfdjeZ5vsm2SO1yHK2F_JL6uZ33CNlmFOJOrV8jdEDa9j",
	"pending": 2
}

Filtering on document IDs

We can also get the changes for specific document IDs using a POST request:

POST http://localhost:5984/persons/_changes?filter=_doc_ids

I’ll include two document IDs in the request body:

{
	"doc_ids": ["3559d9c81c785b6bfc27a34904019fa5", "3559d9c81c785b6bfc27a3490401af41"
		]
}

I now only get the most recent changes made to those two document IDs:

{
  "results": [
    {
      "seq": "2-g1AAAAF1eJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPcikRAZ86hxA6uIJq0sAqasnqC6PBUgyNAApoNL5xKhdAFG7nxi1ByBq7xOj9gFELci9WQDCI3kv",
      "id": "3559d9c81c785b6bfc27a34904019fa5",
      "changes": [
        {
          "rev": "2-53da1176c8605ee7c21b7db5a1be0164"
        }
      ]
    },
    {
      "seq": "10-g1AAAAHTeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rgzmRKRcowG5gZp5iYpSMTQMeY5IUgGSSPcikRAZ86hxA6uKhNjKAbTQzSzQ1MQTayFmal5KalpmXmoLPhASQCfUEbcpjAZIMDUAKqHQ-yDZGsG2mFsYGhhbGJPoPYtoCiGn7ibH5AETtfZDN7GCbkw0tTQzSksiy-QHENJCfswAv35PO",
      "id": "3559d9c81c785b6bfc27a3490401af41",
      "changes": [
        {
          "rev": "4-0ab70230331b8c0cb6b084e789795cae"
        }
      ]
    }
  ],
  "last_seq": "21-g1AAAAJTeJydz0sOgjAQBuAKJOLSE-gRWiilruQmSh-kEoSFstab6E30JnoTbCmJMSFE2MwkM5kv_xQAAF-5Aix5VXMlWFJUPC1UdToXeuWkgK2apsmVmzpHPZhDEgsc8L6DAYatdWXbTvJaSWIaskiMlRIj7ToJtBIhaYSRzrSoSyGzQynFkLA3wuVHgIzGRIo_hdLTFVx108jNKLNWiWgIEQ1HfmS1u9UeRnOtFlCCMjxJe1rtZTS_1TjaYJixSdrbas03W4Y4kbBXyz95mrk8",
  "pending": 0
}

Read the next post here.

You can view all posts related to data storage on this blog here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: