docs/_includes/api/changes.html
{% include anchor.html edit="true" title="Listen to database changes" hash="changes" %} {% highlight "js" %} db.changes(options) {% endhighlight %} A list of changes made to documents in the database, in the order they were made. It returns an object with the method cancel(), which you call if you don't want to listen to new changes anymore. It is an [event emitter][event emitter] and will emit a 'change' event on each document change, a 'complete' event when all the changes have been processed, and an 'error' event when an error occurs. Calling cancel() will unsubscribe all event listeners automatically. ### Options All options default to false unless otherwise specified. * options.live: Will emit change events for all future changes until cancelled. * options.include_docs: Include the associated document with each change. * options.conflicts: Include conflicts. * options.attachments: Include attachments. - options.binary: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings. * options.descending: Reverse the order of the output documents. * options.since: Start the results from the change immediately after the given sequence number. You can also pass 'now' if you want only new changes (when live is true). Ignored if descending is true. * options.limit: Limit the number of results to this number. * options.timeout: Request timeout (in milliseconds), use false to disable. * options.heartbeat: For http adapter only, time in milliseconds for server to give a heartbeat to keep long connections open. Defaults to 10000 (10 seconds), use false to disable the default. **Filtering Options:** * options.filter: Reference a filter function from a design document to selectively get updates. To use a view function, pass _view here and provide a reference to the view function in options.view. See filtered changes for details. * options.doc_ids: Only show changes for docs with these ids (array of strings). * options.query_params: Object containing properties that are passed to the filter function, e.g. {"foo:"bar"}, where "bar" will be available in the filter function as params.query.foo. To access the params, define your filter function like function (doc, params). * options.view: Specify a view function (e.g. 'design_doc_name/view_name' or 'view_name' as shorthand for 'view_name/view_name') to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: options.filter must be set to '_view' for this option to work. * options.selector: Filter using a query/pouchdb-find selector. **Note**: Cannot be used in combination with the filter option. **Advanced Options:** * options.return_docs: Defaults to true except when options.live = true then it defaults to false. Passing false prevents the changes feed from keeping all the documents in memory – in other words complete always has an empty results array, and the change event is the only way to get the event. Useful for large change sets where otherwise you would run out of memory. * options.batch_size: Only available for http databases, this configures how many changes to fetch at a time. Increasing this can reduce the number of requests made. Default is 25. * options.style: Specifies how many revisions are returned in the changes array. The default, 'main_only', will only return the current "winning" revision; 'all_docs' will return all leaf revisions (including conflicts and deleted former conflicts). Most likely you won't need this unless you're writing a replicator. * options.seq_interval: Only available for http databases. Specifies that seq information only be generated every N changes. Larger values can improve changes throughput with CouchDB 2.0 and later. Note that last_seq is always populated regardless. #### Example Usage: {% highlight "js" %} const changes = db.changes({ since: 'now', live: true, include_docs: true }).on('change', function(change) { // handle change }).on('complete', function(info) { // changes() was canceled }).on('error', function (err) { console.log(err); }); changes.cancel(); // whenever you want to cancel {% endhighlight %} #### Example Response: {% highlight "js" %} { "id":"somestuff", "seq":21, "changes":[{ "rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8" }], "doc":{ "title":"Ch-Ch-Ch-Ch-Changes", "_id":"someDocId", "_rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8" } } {% endhighlight %} #### Change events * __change__ (info) - This event fires when a change has been found. info will contain details about the change, such as whether it was deleted and what the new _rev is. info.doc will contain the doc if you set include_docs to true. See below for an example response. * __complete__ (info) - This event fires when all changes have been read. In live changes, only cancelling the changes should trigger this event. info.results will contain the list of changes. See below for an example. * __error__ (err) - This event is fired when the changes feed is stopped due to an unrecoverable failure. #### Example response Example response in the 'change' listener (using {include_docs: true}): {% highlight "js" %} { id: 'doc1', changes: [{ rev: '1-9152679630cc461b9477792d93b83eae' }], doc: { _id: 'doc1', _rev: '1-9152679630cc461b9477792d93b83eae' }, seq: 1 } {% endhighlight %} Example response in the 'change' listener when a doc was deleted: {% highlight "js" %} { id: 'doc2', changes: [{ rev: '2-9b50a4b63008378e8d0718a9ad05c7af' }], doc: { _id: 'doc2', _rev: '2-9b50a4b63008378e8d0718a9ad05c7af', _deleted: true }, deleted: true, seq: 3 } {% endhighlight %} Example response in the 'complete' listener: {% highlight "js" %} { "results": [{ "id": "doc1", "changes": [ { "rev": "1-9152679630cc461b9477792d93b83eae" }], "doc": { "_id": "doc1", "_rev": "1-9152679630cc461b9477792d93b83eae" }, "seq": 1 }, { "id": "doc2", "changes": [{ "rev": "2-9b50a4b63008378e8d0718a9ad05c7af" }], "doc": { "_id": "doc2", "_rev": "2-9b50a4b63008378e8d0718a9ad05c7af", "_deleted": true }, "deleted": true, "seq": 3 } ], "last_seq": 3 } {% endhighlight %} seq and last_seq correspond to the overall sequence number of the entire database, and it's what is passed in when using since (except for the special 'now'). It is the primary key for the changes feed, and is also used as a checkpointer by the replication algorithm. #### Single-shot If you don't specify {live: true}, then you can also use changes() in the standard callback/promise style, and it will be treated as a single-shot request, which returns a list of the changes (i.e. what the 'complete' event emits): {% include code/start.html id="changes1" type="callback" %} {% highlight "js" %} db.changes({ limit: 10, since: 0 }, function (err, response) { if (err) { return console.log(err); } // handle result }); {% endhighlight %} {% include code/end.html %} {% include code/start.html id="changes1" type="async" %} {% highlight "js" %} try { const result = await db.changes({ limit: 10, since: 0 }); } catch (err) { console.log(err); } {% endhighlight %} {% include code/end.html %} {% include code/start.html id="changes1" type="promise" %} {% highlight "js" %} db.changes({ limit: 10, since: 0 }).then(function (result) { // handle result }).catch(function (err) { console.log(err); }); {% endhighlight %} {% include code/end.html %} #### Example Response: {% highlight "js" %} { "results": [{ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD", "seq": 1, "changes": [{ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393" }] }, { "id": "mydoc", "seq": 2, "changes": [{ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F" }] }, { "id": "otherdoc", "seq": 3, "changes": [{ "rev": "1-3753476B70A49EA4D8C9039E7B04254C" }] }, { "id": "828124B9-3973-4AF3-9DFD-A94CE4544005", "seq": 4, "changes": [{ "rev": "1-A8BC08745E62E58830CA066D99E5F457" }] }], "last_seq": 4 } {% endhighlight %} When live is false, the returned object is also an event emitter as well as a promise, and will fire the 'complete' event when the results are ready. Note that this 'complete' event only fires when you aren't doing live changes. #### Filtered changes As with replicate(), you can filter using: * an ad-hoc filter function * an array of doc_ids * a filter function inside of a design document * a filter function inside of a design document, with query_params * a view function inside of a design document If you are running changes() on a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible. If you are running changes() on a local PouchDB, then obviously all five methods will run client-side. There are also no performance benefits to using any of the five, so can also just filter yourself, in your own on('change') handler. These methods are implemented in PouchDB purely for consistency with CouchDB. The named functions can either be specified with 'designdoc_id/function_name' or (if both design doc id and function name are equal) as 'fname' as shorthand for 'fname/fname'. #### Filtering examples In these examples, we'll work with some mammals. Let's imagine our docs are: {% highlight "js" %} [{_id: 'a', name: 'Kangaroo', type: 'marsupial'}, {_id: 'b', name: 'Koala', type: 'marsupial'}, {_id: 'c', name: 'Platypus', type: 'monotreme'}] {% endhighlight %} Here are 5 examples using the 5 different systems. **Example 1: Ad-hoc filter function** *Warning*: this runs client-side, if the database is remote. Filter by type === 'marsupial': {% highlight "js" %} db.changes({ filter: function (doc) { return doc.type === 'marsupial'; } }); {% endhighlight %} **Example 2: Array of doc_ids** Filter documents with _ids ['a', 'c']. {% highlight "js" %} db.changes({ doc_ids: ['a', 'c'] }); {% endhighlight %} **Example 3: filter function inside of a design document** First put() a design document: {% highlight "js" %} { _id: '_design/mydesign', filters: { myfilter: function (doc) { return doc.type === 'marsupial'; }.toString() } } {% endhighlight %} Then filter by type === 'marsupial': {% highlight "js" %} db.changes({ filter: 'mydesign/myfilter' }); {% endhighlight %} **Example 4: filter function inside of a design document, with query_params** This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function. First put() a design document: {% highlight "js" %} { _id: '_design/myfilter', filters: { myfilter: function (doc, req) { return doc.type === req.query.type; }.toString() } } {% endhighlight %} Then filter by type === 'marsupial': {% highlight "js" %} db.changes({ filter: 'myfilter', query_params: {type: 'marsupial'} }); {% endhighlight %} Since both the design document and the filter function have the same name, we can shorten the function name to 'myfilter'. **Example 5: view function inside of a design document** This doesn't really offer any advantages compared to the previous two methods, unless you are already using a view for map/reduce queries, and you want to reuse it. Any documents that emit() anything will be considered to have passed this filter method. First put() a design document: {% highlight "js" %} { _id: '_design/mydesign', views: { myview: function (doc) { if (doc.type === 'marsupial') { emit(doc._id); } }.toString() } } {% endhighlight %} Then filter by type === 'marsupial': {% highlight "js" %} db.changes({ filter: '_view', view: 'mydesign/myview' }); {% endhighlight %}