{% 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](#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](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors). **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()](#replication), 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 `_id`s `['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 %}