Back to Couchdb

``/{db}/_changes``

src/docs/src/api/database/changes.rst

3.5.141.6 KB
Original Source

.. Licensed under the Apache License, Version 2.0 (the "License"); you may not .. use this file except in compliance with the License. You may obtain a copy of .. the License at .. .. http://www.apache.org/licenses/LICENSE-2.0 .. .. Unless required by applicable law or agreed to in writing, software .. distributed under the License is distributed on an "AS IS" BASIS, WITHOUT .. WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the .. License for the specific language governing permissions and limitations under .. the License.

.. _api/db/changes:

================== /{db}/_changes

.. http:get:: /{db}/_changes :synopsis: Returns changes for the given database

Returns a sorted list of changes made to documents in the database, in time
order of application. Only the most recent change for a given document is
guaranteed to be provided, for example if a document has had fields added,
and then deleted, an API client checking for changes will not necessarily
receive the intermediate state of added documents.

This can be used to listen for update and modifications to the database for
post processing or synchronization, and for practical purposes,
a continuously connected ``_changes`` feed is a reasonable approach for
generating a real-time log for most applications.

:param db: Database name
:<header Accept: - :mimetype:`application/json`
                 - :mimetype:`text/event-stream`
                 - :mimetype:`text/plain`
:<header Last-Event-ID: ID of the last events received by the server on a
    previous connection. Overrides ``since`` query parameter.
:query array doc_ids: List of document IDs to filter the changes feed as
    valid JSON array. Used with :ref:`_doc_ids <changes/filter/doc_ids>`
    filter. Since `length of URL is limited`_, it is better to use
    :post:`/{db}/_changes` instead.
:query boolean conflicts: Includes `conflicts` information in response.
    Ignored if `include_docs` isn't ``true``. Default is ``false``.
:query boolean descending: Return the change results in descending sequence
    order (most recent change first). Default is ``false``.
:query string feed: - **normal** Specifies :ref:`Normal Polling Mode
                      <changes/normal>`. All past changes are returned
                      immediately. *Default.*
                    - **longpoll** Specifies :ref:`Long Polling Mode
                      <changes/longpoll>`. Waits until at least one change
                      has occurred, sends the change, then closes the
                      connection. Most commonly used in conjunction with
                      ``since=now``, to wait for the next change.
                    - **continuous** Sets :ref:`Continuous Mode
                      <changes/continuous>`. Sends a line of JSON per
                      event. Keeps the socket open until ``timeout``.
                    - **eventsource** Sets :ref:`Event Source Mode
                      <changes/eventsource>`. Works the same as Continuous
                      Mode, but sends the events in `EventSource
                      <http://dev.w3.org/html5/eventsource/>`_ format.
:query string filter: - **design_doc/filter_name** Reference to a
                        :ref:`filter function <filterfun>` from a design
                        document that will filter whole stream emitting
                        only filtered events. See the section `Change
                        Notifications in the book CouchDB The Definitive
                        Guide`_ for more information.
                      - **_doc_ids** :ref:`doc_ids filter
                        <changes/filter/doc_ids>`
                      - **_view** :ref:`view filter <changes/filter/view>`
                      - **_design** :ref:`design filter
                        <changes/filter/design>`
:query number heartbeat: Period in *milliseconds* after which an empty
    line is sent in the results. Only applicable for :ref:`longpoll
    <changes/longpoll>`, :ref:`continuous <changes/continuous>`, and
    :ref:`eventsource <changes/eventsource>` feeds. Overrides any timeout
    to keep the feed alive indefinitely. Default is ``60000``. May be
    ``true`` to use default value.
:query boolean include_docs: Include the associated document with each
    result. If there are conflicts, only the winning revision is returned.
    Default is ``false``. When used with ``all_docs`` style and a filter,
    return the document body even if does not pass the filtering criteria.
    In other words, filtering applies only to the list of ``"changes"``
    revision list not the returned document body in the ``"doc"`` field.
:query boolean attachments: Include the Base64-encoded content of
    :ref:`attachments <api/doc/attachments>` in the documents that
    are included if ``include_docs`` is ``true``. Ignored if ``include_docs``
    isn't ``true``. Default is ``false``.
:query boolean att_encoding_info: Include encoding information in attachment
    stubs if ``include_docs`` is ``true`` and the particular attachment is
    compressed. Ignored if ``include_docs`` isn't ``true``.
    Default is ``false``.
:query number last-event-id: Alias of `Last-Event-ID` header.
:query number limit: Limit number of result rows to the specified value
    (note that using ``0`` here has the same effect as ``1``).
:query since: Start the results from the change immediately after the given
    update sequence. Default is ``0``. Other values can be:

    - A valid update sequence, for example, from a ``_changes`` feed response.
    - ``now``
    - ``0``
    - A timestamp string matching the ``YYYY-MM-DDTHH:MM:SSZ``
      format. The results returned will depend on the time-sequence
      intervals recorded by the time-seq data structure. To inspect or reset
      it use the :ref:`_time_seq <api/db/time_seq>` endpoint. For additional
      details see the :ref:`changes/timeseq` section below.
:query string 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). When using a filter
    with ``all_docs`` style, if none of the revisions match the filter, the
    changes row is skipped. If at least one revision matches, the changes
    row is returned with all matching revision. If ``all_docs`` style is
    used with ``include_docs=true`` and at least one revision matches the
    filter, the winning doc body is returned, even if it doesn't not pass
    the filtering criteria.
:query number timeout: Maximum period in *milliseconds* to wait for a change
    before the response is sent, even if there are no results.
    Only applicable for :ref:`longpoll <changes/longpoll>`,
    :ref:`continuous <changes/continuous>` or
    :ref:`eventsource <changes/eventsource>` feeds.
    Default value is specified by :config:option:`chttpd/changes_timeout`
    configuration option. Note that ``60000`` value is also the default
    maximum timeout to prevent undetected dead connections.
:query string view: Allows to use view functions as filters. Documents
    counted as "passed" for view filter in case if map function emits
    at least one record for them.
    See :ref:`changes/filter/view` for more info.
:query number seq_interval: When fetching changes in a batch, setting the
    *seq_interval* parameter tells CouchDB to only calculate the update seq
    with every Nth result returned. By setting **seq_interval=<batch size>**
    , where ``<batch size>`` is the number of results requested per batch,
    load can be reduced on the source CouchDB database; computing the seq
    value across many shards (esp. in highly-sharded databases) is expensive
    in a heavily loaded CouchDB cluster.
:>header Cache-Control: ``no-cache`` if changes feed is
    :ref:`eventsource <changes/eventsource>`
:>header Content-Type: - :mimetype:`application/json`
                       - :mimetype:`text/event-stream`
                       - :mimetype:`text/plain; charset=utf-8`
:>header ETag: Response hash if changes feed is `normal`
:>header Transfer-Encoding: ``chunked``
:>json json last_seq: Last change update sequence
:>json number pending: Count of remaining items in the feed
:>json array results: Changes made to a database
:code 200: Request completed successfully
:code 400: Bad request
:code 401: Unauthorized request to a protected API
:code 403: Insufficient permissions / :ref:`Too many requests with invalid credentials<error/403>`

The ``results`` field of database changes:

:json array changes: List of document's leaves with single field ``rev``.
:json string id: Document ID.
:json json seq: Update sequence.
:json bool deleted: ``true`` if the document is deleted.

**Request**:

.. code-block:: http

    GET /db/_changes?style=all_docs HTTP/1.1
    Accept: application/json
    Host: localhost:5984

**Response**:

.. code-block:: http

    HTTP/1.1 200 OK
    Cache-Control: must-revalidate
    Content-Type: application/json
    Date: Mon, 12 Aug 2013 00:54:58 GMT
    ETag: "6ASLEKEMSRABT0O5XY9UPO9Z"
    Server: CouchDB (Erlang/OTP)
    Transfer-Encoding: chunked

    {
        "last_seq": "5-g1AAAAIreJyVkEsKwjAURZ-toI5cgq5A0sQ0OrI70XyppcaRY92J7kR3ojupaSPUUgotgRd4yTlwbw4A0zRUMLdnpaMkwmyF3Ily9xBwEIuiKLI05KOTW0wkV4rruP29UyGWbordzwKVxWBNOGMKZhertDlarbr5pOT3DV4gudUC9-MPJX9tpEAYx4TQASns2E24ucuJ7rXJSL1BbEgf3vTwpmedCZkYa7Pulck7Xt7x_usFU2aIHOD4eEfVTVA5KMGUkqhNZV-8_o5i",
        "pending": 0,
        "results": [
            {
                "changes": [
                    {
                        "rev": "2-7051cbe5c8faecd085a3fa619e6e6337"
                    }
                ],
                "id": "6478c2ae800dfc387396d14e1fc39626",
                "seq": "3-g1AAAAG3eJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MSGXAqSVIAkkn2IFUZzIkMuUAee5pRqnGiuXkKA2dpXkpqWmZeagpu_Q4g_fGEbEkAqaqH2sIItsXAyMjM2NgUUwdOU_JYgCRDA5ACGjQfn30QlQsgKvcjfGaQZmaUmmZClM8gZhyAmHGfsG0PICrBPmQC22ZqbGRqamyIqSsLAAArcXo"
            },
            {
                "changes": [
                    {
                        "rev": "3-7379b9e515b161226c6559d90c4dc49f"
                    }
                ],
                "deleted": true,
                "id": "5bbc9ca465f1b0fcd62362168a7c8831",
                "seq": "4-g1AAAAHXeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBMZc4EC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HqQ_kQG3qgSQqnoUtxoYGZkZG5uS4NY8FiDJ0ACkgAbNx2cfROUCiMr9CJ8ZpJkZpaaZEOUziBkHIGbcJ2zbA4hKsA-ZwLaZGhuZmhobYurKAgCz33kh"
            },
            {
                "changes": [
                    {
                        "rev": "6-460637e73a6288cb24d532bf91f32969"
                    },
                    {
                        "rev": "5-eeaa298781f60b7bcae0c91bdedd1b87"
                    }
                ],
                "id": "729eb57437745e506b333068fff665ae",
                "seq": "5-g1AAAAIReJyVkE0OgjAQRkcwUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloRid3MMkEUoJHbXbOxVy6arc_SxQWQzRVHCuYHaxSpuj1aqbj0t-3-AlSrZakn78oeSvjRSIkIhSNiCFHbsKN3c50b02mURvEB-yD296eNOzzoRMRLRZ98rkHS_veGcC_nR-fGe1gaCaxihhjOI2lX0BhniHaA"
            }
        ]
    }

.. _length of URL is limited: http://stackoverflow.com/a/417184/965635

.. versionchanged:: 0.11.0 added include_docs parameter .. versionchanged:: 1.2.0 added view parameter and special value _view for filter one .. versionchanged:: 1.3.0 since parameter could take now value to start listen changes since current seq number. .. versionchanged:: 1.3.0 eventsource feed type added. .. versionchanged:: 1.4.0 Support Last-Event-ID header. .. versionchanged:: 1.6.0 added attachments and att_encoding_info parameters .. versionchanged:: 2.0.0 update sequences can be any valid json object, added seq_interval

.. note:: If the specified replicas of the shards in any given since value are unavailable, alternative replicas are selected, and the last known checkpoint between them is used. If this happens, you might see changes again that you have previously seen. Therefore, an application making use of the _changes feed should be ‘idempotent’, that is, able to receive the same data multiple times, safely.

.. note:: Cloudant Sync and PouchDB already optimize the replication process by setting seq_interval parameter to the number of results expected per batch. This parameter increases throughput by reducing latency between sequential requests in bulk document transfers. This has resulted in up to a 20% replication performance improvement in highly-sharded databases.

.. warning:: Using the attachments parameter to include attachments in the changes feed is not recommended for large attachment sizes. Also note that the Base64-encoding that is used leads to a 33% overhead (i.e. one third) in transfer size for attachments.

.. warning:: The results returned by _changes are partially ordered. In other words, the order is not guaranteed to be preserved for multiple calls.

.. http:post:: /{db}/_changes :synopsis: Returns changes for the given database for certain document IDs or selector filter.

Requests the database changes feed in the same way as :get:`/{db}/_changes`
does, but is widely used with ``?filter=_doc_ids`` or ``?filter=_selector``
query parameters and allows one to pass a larger list of document IDs or
the body of the selector to filter.

:param db: Database name
:query string filter: - **_doc_ids** :ref:`doc_ids filter <changes/filter/doc_ids>`
                      - **_selector** :ref:`selector filter <changes/filter/selector>`

**Request**:

.. code-block:: http

    POST /recipes/_changes?filter=_doc_ids HTTP/1.1
    Accept: application/json
    Content-Length: 40
    Content-Type: application/json
    Host: localhost:5984

    {
        "doc_ids": [
            "SpaghettiWithMeatballs"
        ]
    }

**Response**:

.. code-block:: http

    HTTP/1.1 200 OK
    Cache-Control: must-revalidate
    Content-Type: application/json
    Date: Sat, 28 Sep 2013 07:23:09 GMT
    ETag: "ARIHFWL3I7PIS0SPVTFU6TLR2"
    Server: CouchDB (Erlang OTP)
    Transfer-Encoding: chunked

    {
        "last_seq": "5-g1AAAAIreJyVkEsKwjAURZ-toI5cgq5A0sQ0OrI70XyppcaRY92J7kR3ojupaSPUUgotgRd4yTlwbw4A0zRUMLdnpaMkwmyF3Ily9xBwEIuiKLI05KOTW0wkV4rruP29UyGWbordzwKVxWBNOGMKZhertDlarbr5pOT3DV4gudUC9-MPJX9tpEAYx4TQASns2E24ucuJ7rXJSL1BbEgf3vTwpmedCZkYa7Pulck7Xt7x_usFU2aIHOD4eEfVTVA5KMGUkqhNZV8_o5i",
        "pending": 0,
        "results": [
            {
                "changes": [
                    {
                        "rev": "13-bcb9d6388b60fd1e960d9ec4e8e3f29e"
                    }
                ],
                "id": "SpaghettiWithMeatballs",
                "seq":  "5-g1AAAAIReJyVkE0OgjAQRkcwUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloRid3MMkEUoJHbXbOxVy6arc_SxQWQzRVHCuYHaxSpuj1aqbj0t-3-AlSrZakn78oeSvjRSIkIhSNiCFHbsKN3c50b02mURvEB-yD296eNOzzoRMRLRZ98rkHS_veGcC_nR-fGe1gaCaxihhjOI2lX0BhniHaA"
            }
        ]
    }

**Request**:

.. code-block:: http

    POST /db/_changes?filter=_selector HTTP/1.1
    Accept: application/json
    Accept-Encoding: gzip, deflate
    Content-Length: 25
    Content-Type: application/json
    Host: 127.0.0.1:5984

    {
        "selector": {
            "data": 1
        }
    }

**Response**:

.. code-block:: http

    HTTP/1.1 200 OK
    Cache-Control: must-revalidate
    Content-Type: application/json
    Date: Fri, 05 Jan 2024 18:08:46 GMT
    ETag: "9UTJJV90GMV3XQKBM9RNAS0IK"
    Server: CouchDB/3.3.3-42c2484 (Erlang OTP/24)
    Transfer-Encoding: chunked

    {
        "last_seq": "4-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE5lzgQLshqkGSWmGyZjKcRqRxwIkGRqA1H-oSYxgk0ySLSxSEi0wdWUBAGlCJKQ",
        "pending": 0,
        "results": [
            {
                "changes": [
                    {
                        "rev": "3-fc9d7a5cf38c9f062aa246cb072eae68"
                    }
                ],
                "id": "d1",
                "seq": "4-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE5lzgQLshqkGSWmGyZjKcRqRxwIkGRqA1H-oSYxgk0ySLSxSEi0wdWUBAGlCJKQ"
            }
        ]
    }

.. _changes:

Changes Feeds

.. _changes/normal:

Polling

By default all changes are immediately returned within the JSON body::

GET /somedatabase/_changes HTTP/1.1

.. code-block:: javascript

{"results":[
{"seq":"1-g1AAAAF9eJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P__7MSGXAqSVIAkkn2IFUZzIkMuUAee5pRqnGiuXkKA2dpXkpqWmZeagpu_Q4g_fGEbEkAqaqH2sIItsXAyMjM2NgUUwdOU_JYgCRDA5ACGjQfn30QlQsgKvcTVnkAovI-YZUPICpBvs0CAN1eY_c","id":"fresh","changes":[{"rev":"1-967a00dff5e02add41819138abb3284d"}]},
{"seq":"3-g1AAAAG3eJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MSGXAqSVIAkkn2IFUZzIkMuUAee5pRqnGiuXkKA2dpXkpqWmZeagpu_Q4g_fGEbEkAqaqH2sIItsXAyMjM2NgUUwdOU_JYgCRDA5ACGjQfn30QlQsgKvcjfGaQZmaUmmZClM8gZhyAmHGfsG0PICrBPmQC22ZqbGRqamyIqSsLAAArcXo","id":"updated","changes":[{"rev":"2-7051cbe5c8faecd085a3fa619e6e6337CFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloRid3MMkEUoJHbXbOxVy6arc_SxQWQzRVHCuYHaxSpuj1aqbj0t-3-AlSrZakn78oeSvjRSIkIhSNiCFHbsKN3c50b02mURvEB-yD296eNOzzoRMRLRZ98rkHS_veGcC_nR-fGe1gaCaxihhjOI2lX0BhniHaA","id":"deleted","changes":[{"rev":"2-eec205a9d413992850a6e32678485900"}],"deleted":true}
],
"last_seq":"5-g1AAAAIreJyVkEsKwjAURZ-toI5cgq5A0sQ0OrI70XyppcaRY92J7kR3ojupaSPUUgotgRd4yTlwbw4A0zRUMLdnpaMkwmyF3Ily9xBwEIuiKLI05KOTW0wkV4rruP29UyGWbordzwKVxWBNOGMKZhertDlarbr5pOT3DV4gudUC9-MPJX9tpEAYx4TQASns2E24ucuJ7rXJSL1BbEgf3vTwpmedCZkYa7Pulck7Xt7x_usFU2aIHOD4eEfVTVA5KMGUkqhNZV-8_o5i",
"pending": 0}

results is the list of changes in sequential order. New and changed documents only differ in the value of the rev; deleted documents include the "deleted": true attribute. (In the style=all_docs mode, deleted applies only to the current/winning revision. The other revisions listed might be deleted even if there is no deleted property; you have to GET them individually to make sure.)

last_seq is the update sequence of the last update returned (Equivalent to the last item in the results).

Sending a since param in the query string skips all changes up to and including the given update sequence:

.. code-block:: http

GET /somedatabase/_changes?since=4-g1AAAAHXeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBMZc4EC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HqQ_kQG3qgSQqnoUtxoYGZkZG5uS4NY8FiDJ0ACkgAbNx2cfROUCiMr9CJ8ZpJkZpaaZEOUziBkHIGbcJ2zbA4hKsA-ZwLaZGhuZmhobYurKAgCz33kh HTTP/1.1

The return structure for normal and longpoll modes is a JSON array of changes objects, and the last update sequence.

In the return format for continuous mode, the server sends a CRLF (carriage-return, linefeed) delimited line for each change. Each line contains the JSON object described above.

You can also request the full contents of each document change (instead of just the change notification) by using the include_docs parameter.

.. code-block:: javascript

{
    "last_seq": "5-g1AAAAIreJyVkEsKwjAURZ-toI5cgq5A0sQ0OrI70XyppcaRY92J7kR3ojupaSPUUgotgRd4yTlwbw4A0zRUMLdnpaMkwmyF3Ily9xBwEIuiKLI05KOTW0wkV4rruP29UyGWbordzwKVxWBNOGMKZhertDlarbr5pOT3DV4gudUC9-MPJX9tpEAYx4TQASns2E24ucuJ7rXJSL1BbEgf3vTwpmedCZkYa7Pulck7Xt7x_usFU2aIHOD4eEfVTVA5KMGUkqhNZV-8_o5i",
    "pending": 0,
    "results": [
        {
            "changes": [
                {
                    "rev": "2-eec205a9d413992850a6e32678485900"
                }
            ],
            "deleted": true,
            "id": "deleted",
            "seq":  "5-g1AAAAIReJyVkE0OgjAQRkcwUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloRid3MMkEUoJHbXbOxVy6arc_SxQWQzRVHCuYHaxSpuj1aqbj0t-3-AlSrZakn78oeSvjRSIkIhSNiCFHbsKN3c50b02mURvEByD296eNOzzoRMRLRZ98rkHS_veGcC_nR-fGe1gaCaxihhjOI2lX0BhniHaA",
        }
    ]
}

.. _changes/longpoll:

Long Polling

The longpoll feed, probably most applicable for a browser, is a more efficient form of polling that waits for a change to occur before the response is sent. longpoll avoids the need to frequently poll CouchDB to discover nothing has changed!

The request to the server will remain open until a change is made on the database and is subsequently transferred, and then the connection will close. This is low load for both server and client.

The response is basically the same JSON as is sent for the normal feed.

Because the wait for a change can be significant you can set a timeout before the connection is automatically closed (the timeout argument). You can also set a heartbeat interval (using the heartbeat query argument), which sends a newline to keep the connection active.

Keep in mind that heartbeat means "Send a linefeed every x ms if no change arrives, and hold the connection indefinitely" while timeout means "Hold this connection open for x ms, and if no change arrives in that time, close the socket." heartbeat overrides timeout.

.. _changes/continuous:

Continuous

Continually polling the CouchDB server is not ideal - setting up new HTTP connections just to tell the client that nothing happened puts unnecessary strain on CouchDB.

A continuous feed stays open and connected to the database until explicitly closed and changes are sent to the client as they happen, i.e. in near real-time.

As with the longpoll feed type you can set both the timeout and heartbeat intervals to ensure that the connection is kept open for new changes and updates.

Keep in mind that heartbeat means "Send a linefeed every x ms if no change arrives, and hold the connection indefinitely" while timeout means "Hold this connection open for x ms, and if no change arrives in that time, close the socket." heartbeat overrides timeout.

The continuous feed's response is a little different than the other feed types to simplify the job of the client - each line of the response is either empty or a JSON object representing a single change, as found in the normal feed's results.

If limit has been specified the feed will end with a { last_seq } object.

.. code-block:: http

GET /somedatabase/_changes?feed=continuous HTTP/1.1

.. code-block:: javascript

{"seq":"1-g1AAAAF9eJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MSGXAqSVIAkkn2IFUZzIkMuUAee5pRqnGiuXkKA2dpXkpqWmZeagpu_Q4g_fGEbEkAqaqH2sIItsXAyMjM2NgUUwdOU_JYgCRDA5ACGjQfn30QlQsgKvcTVnkAovI-YZUPICpBvs0CAN1eY_c","id":"fresh","changes":[{"rev":"5-g1AAAAHxeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBOZcoEC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HkV_kkGyZWqSEXH6E0D666H6GcH6DYyMzIyNTUnwRR4LkGRoAFJAg-YjwiMtOdXCwJyU8ICYtABi0n6EnwzSzIxS00yI8hPEjAMQM-5nJTIQUPkAovI_UGUWAA0SgOI","id":"updated","changes":[{"rev":"2-7051cbe5c8faecd085a3fa619e6e6337"}]}
{"seq":"3-g1AAAAHReJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBOZcoEC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HkV_kkGyZWqSEXH6E0D660H6ExlwqspjAZIMDUAKqHA-yCZGiEuTUy0MzEnxL8SkBRCT9iPcbJBmZpSaZkKUmyFmHICYcZ-wux9AVIJ8mAUABgp6XQ","id":"deleted","changes":[{"rev":"2-eec205a9d413992850a6e32678485900"}],"deleted":true}
... tum tee tum ...
{"seq":"6-g1AAAAIreJyVkEsKwjAURWMrqCOXoCuQ9MU0OrI70XyppcaRY92J7kR3ojupaVNopRQsgRd4yTlwb44QmqahQnN7VjpKImAr7E6Uu4eAI7EoiiJLQx6c3GIiuVJcx93vvQqxdFPsaguqLAY04YwpNLtYpc3RatXPJyW__-EFllst4D_-UPLXmh9VPAaICaEDUtixm-jmLie6N30YqTeYDenDmx7e9GwyYRODNuu_MnnHyzverV6AMkPkAMfHO1rdUAKUkqhLZV-_0o5j","id":"updated","changes":[{"rev":"3-825cb35de44c433bfb2df415563a19de"}]}

Obviously, ... tum tee tum ... does not appear in the actual response, but represents a long pause before the change with seq 6 occurred.

.. _Change Notifications in the book CouchDB The Definitive Guide: http://guide.couchdb.org/draft/notifications.html

.. _changes/eventsource:

Event Source

The eventsource feed provides push notifications that can be consumed in the form of DOM events in the browser. Refer to the W3C eventsource specification_ for further details. CouchDB also honours the Last-Event-ID parameter.

.. code-block:: http

GET /somedatabase/_changes?feed=eventsource HTTP/1.1

.. code-block:: javascript

// define the event handling function
if (window.EventSource) {

    var source = new EventSource("/somedatabase/_changes?feed=eventsource");
    source.onerror = function(e) {
        alert('EventSource failed.');
    };

    var results = [];
    var sourceListener = function(e) {
        var data = JSON.parse(e.data);
        results.push(data);
    };

    // start listening for events
    source.addEventListener('message', sourceListener, false);

    // stop listening for events
    source.removeEventListener('message', sourceListener, false);

}

If you set a heartbeat interval (using the heartbeat query argument), CouchDB will send a hearbeat event that you can subscribe to with:

.. code-block:: javascript

source.addEventListener('heartbeat', function () {}, false);

This can be monitored by the client application to restart the EventSource connection if needed (i.e. if the TCP connection gets stuck in a half-open state).

.. note:: EventSource connections are subject to cross-origin resource sharing restrictions. You might need to configure :ref:CORS support <cors> to get the EventSource to work in your application.

.. _W3C eventsource specification: http://www.w3.org/TR/eventsource/

.. _changes/filter:

Filtering

You can filter the contents of the changes feed in a number of ways. The most basic way is to specify one or more document IDs to the query. This causes the returned structure value to only contain changes for the specified IDs. Note that the value of this query argument should be a JSON formatted array.

You can also filter the _changes feed by defining a filter function within a design document. The specification for the filter is the same as for replication filters. You specify the name of the filter function to the filter parameter, specifying the design document name and :ref:filter name <filterfun>. For example:

.. code-block:: http

GET /db/_changes?filter=design_doc/filtername HTTP/1.1

Additionally, a couple of built-in filters are available and described below.

.. _changes/filter/doc_ids:

_doc_ids

This filter accepts only changes for documents which ID in specified in doc_ids query parameter or payload's object array. See :post:/{db}/_changes for an example.

.. _changes/filter/selector:

_selector

.. versionadded:: 2.0

This filter accepts only changes for documents which match a specified selector, defined using the same :ref:selector syntax <find/selectors> used for :ref:_find <api/db/_find>.

This is significantly more efficient than using a JavaScript filter function and is the recommended option if filtering on document attributes only.

Note that, unlike JavaScript filters, selectors do not have access to the request object.

Request:

.. code-block:: http

POST /recipes/_changes?filter=_selector HTTP/1.1
Content-Type: application/json
Host: localhost:5984

{
    "selector": { "_id": { "$regex": "^_design/" } }
}

Response:

.. code-block:: http

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Tue, 06 Sep 2016 20:03:23 GMT
Etag: "1H8RGBCK3ABY6ACDM7ZSC30QK"
Server: CouchDB (Erlang OTP/18)
Transfer-Encoding: chunked

{
    "last_seq": "11-g1AAAAIreJyVkEEKwjAQRUOrqCuPoCeQZGIaXdmbaNIk1FLjyrXeRG-iN9Gb1LQRaimFlsAEJnkP_s8RQtM0VGhuz0qTmABfYXdI7h4CgeSiKIosDUVwcotJIpQSOmp_71TIpZty97OgymJAU8G5QrOLVdocrVbdfFzy-wYvcbLVEvrxh5K_NlJggIhSNiCFHbmJbu5yonttMoneYD6kD296eNOzzoRNBNqse2Xyjpd3vP96AcYNTQY4Pt5RdTOuHIwCY5S0qewLwY6OaA",
    "pending": 0,
    "results": [
        {
            "changes": [
                {
                    "rev": "10-304cae84fd862832ea9814f02920d4b2"
                }
            ],
            "id": "_design/ingredients",
            "seq": "8-g1AAAAHxeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBOZcoEC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HkV_kkGyZWqSEXH6E0D666H6GcH6DYyMzIyNTUnwRR4LkGRoAFJAg-ZnJTIQULkAonI_ws0GaWZGqWkmRLkZYsYBiBn3Cdv2AKIS7ENWsG2mxkampsaGmLqyAOYpgEo"
        },
        {
            "changes": [
                {
                    "rev": "123-6f7c1b7c97a9e4f0d22bdf130e8fd817"
                }
            ],
            "deleted": true,
            "id": "_design/cookbook",
            "seq": "9-g1AAAAHxeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBOZcoEC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HkV_kkGyZWqSEXH6E0D661F8YWBkZGZsbEqCL_JYgCRDA5ACGjQ_K5GBgMoFEJX7EW42SDMzSk0zIcrNEDMOQMy4T9i2BxCVYB-ygm0zNTYyNTU2xNSVBQDnK4BL"
        },
        {
            "changes": [
                {
                    "rev": "6-5b8a52c22580e922e792047cff3618f3"
                }
            ],
            "deleted": true,
            "id": "_design/meta",
            "seq": "11-g1AAAAIReJyVkE0OgjAQRiegUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloQhO7mGSCKWEjtrtnQq5dFXufhaoLIZoKjhXMLtYpc3RatXNxyW_b_ASJVstST_-UPLXRgpESEQpG5DCjlyFm7uc6F6bTKI3iA_Zhzc9vOlZZ0ImItqse2Xyjpd3vDMBfzo_vrPawLiaxihhjOI2lX0BirqHbg"
        }
    ]
}

.. _changes/filter/selector/missing:

Missing selector ################

If the selector object is missing from the request body, the error message is similar to the following example:

.. code-block:: json

{ "error": "bad request", "reason": "Selector must be specified in POST payload" }

.. _changes/filter/selector/invalidjson:

Not a valid JSON object #######################

If the selector object is not a well-formed JSON object, the error message is similar to the following example:

.. code-block:: json

{ "error": "bad request", "reason": "Selector error: expected a JSON object" }

.. _changes/filter/selector/invalidselector:

Not a valid selector ####################

If the selector object does not contain a valid selection expression, the error message is similar to the following example:

.. code-block:: json

{ "error": "bad request", "reason": "Selector error: expected a JSON object" }

.. _changes/filter/design:

_design

The _design filter accepts only changes for any design document within the requested database.

Request:

.. code-block:: http

GET /recipes/_changes?filter=_design HTTP/1.1
Accept: application/json
Host: localhost:5984

Response:

.. code-block:: http

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Tue, 06 Sep 2016 12:55:12 GMT
ETag: "ARIHFWL3I7PIS0SPVTFU6TLR2"
Server: CouchDB (Erlang OTP)
Transfer-Encoding: chunked

{
    "last_seq": "11-g1AAAAIreJyVkEEKwjAQRUOrqCuPoCeQZGIaXdmbaNIk1FLjyrXeRG-iN9Gb1LQRaimFlsAEJnkP_s8RQtM0VGhuz0qTmABfYXdI7h4CgeSiKIosDUVwcotJIpQSOmp_71TIpZty97OgymJAU8G5QrOLVdocrVbdfFzy-wYvcbLVEvrxh5K_NlJggIhSNiCFHbmJbu5yonttMoneYD6kD296eNOzzoRNBNqse2Xyjpd3vP96AcYNTQY4Pt5RdTOuHIwCY5S0qewLwY6OaA",
    "pending": 0,
    "results": [
        {
            "changes": [
                {
                    "rev": "10-304cae84fd862832ea9814f02920d4b2"
                }
            ],
            "id": "_design/ingredients",
            "seq": "8-g1AAAAHxeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBOZcoEC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HkV_kkGyZWqSEXH6E0D666H6GcH6DYyMzIyNTUnwRR4LkGRoAFJAg-ZnJTIQULkAonI_ws0GaWZGqWkmRLkZYsYBiBn3Cdv2AKIS7ENWsG2mxkampsaGmLqyAOYpgEo"
        },
        {
            "changes": [
                {
                    "rev": "123-6f7c1b7c97a9e4f0d22bdf130e8fd817"
                }
            ],
            "deleted": true,
            "id": "_design/cookbook",
            "seq": "9-g1AAAAHxeJzLYWBg4MhgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkOS_P___7MymBOZcoEC7MmJKSmJqWaYynEakaQAJJPsoaYwgE1JM0o1TjQ3T2HgLM1LSU3LzEtNwa3fAaQ_HkV_kkGyZWqSEXH6E0D661F8YWBkZGZsbEqCL_JYgCRDA5ACGjQ_K5GBgMoFEJX7EW42SDMzSk0zIcrNEDMOQMy4T9i2BxCVYB-ygm0zNTYyNTU2xNSVBQDnK4BL"
        },
        {
            "changes": [
                {
                    "rev": "6-5b8a52c22580e922e792047cff3618f3"
                }
            ],
            "deleted": true,
            "id": "_design/meta",
            "seq": "11-g1AAAAIReJyVkE0OgjAQRiegUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloQhO7mGSCKWEjtrtnQq5dFXufhaoLIZoKjhXMLtYpc3RatXNxyW_b_ASJVstST_-UPLXRgpESEQpG5DCjlyFm7uc6F6bTKI3iA_Zhzc9vOlZZ0ImItqse2Xyjpd3vDMBfzo_vrPawLiaxihhjOI2lX0BirqHbg"
        }
    ]
}

.. _changes/filter/view:

_view

.. versionadded:: 1.2

The special filter _view allows to use existing :ref:map function <mapfun> as the :ref:filter <filterfun>. If the map function emits anything for the processed document it counts as accepted and the changes event emits to the feed. For most use-practice cases filter functions are very similar to map ones, so this feature helps to reduce amount of duplicated code.

.. warning:: While :ref:map functions <mapfun> doesn't process the design documents, using _view filter forces them to do this. You need to be sure, that they are ready to handle documents with alien structure without panic.

.. note:: Using _view filter doesn't query the view index files, so you cannot use common :ref:view query parameters <api/ddoc/view> to additionally filter the changes feed by index key. Also, CouchDB doesn't returns the result instantly as it does for views - it really uses the specified map function as filter.

Moreover, you cannot make such filters dynamic e.g. process the request
query parameters or handle the :ref:`userctx_object` - the map function
only operates with the document.

Request:

.. code-block:: http

GET /recipes/_changes?filter=_view&view=ingredients/by_recipe HTTP/1.1
Accept: application/json
Host: localhost:5984

Response:

.. code-block:: http

HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Type: application/json
Date: Tue, 06 Sep 2016 12:57:56 GMT
ETag: "ARIHFWL3I7PIS0SPVTFU6TLR2"
Server: CouchDB (Erlang OTP)
Transfer-Encoding: chunked

{
    "last_seq": "11-g1AAAAIreJyVkEEKwjAQRUOrqCuPoCeQZGIaXdmbaNIk1FLjyrXeRG-iN9Gb1LQRaimFlsAEJnkP_s8RQtM0VGhuz0qTmABfYXdI7h4CgeSiKIosDUVwcotJIpQSOmp_71TIpZty97OgymJAU8G5QrOLVdocrVbdfFzy-wYvcbLVEvrxh5K_NlJggIhSNiCFHbmJbu5yonttMoneYD6kD296eNOzzoRNBNqse2Xyjpd3vP96AcYNTQY4Pt5RdTOuHIwCY5S0qewLwY6OaA",
    "results": [
        {
            "changes": [
                {
                    "rev": "13-bcb9d6388b60fd1e960d9ec4e8e3f29e"
                }
            ],
            "id": "SpaghettiWithMeatballs",
            "seq": "11-g1AAAAIReJyVkE0OgjAQRiegUVceQU9g-mOpruQm2tI2SLCuXOtN9CZ6E70JFmpCCCFCmkyTdt6bfJMDwDQNFcztWWkcY8JXyB2cu49AgFwURZGloQhO7mGSCKWEjtrtnQq5dFXufhaoLIZoKjhXMLtYpc3RatXNxyW_b_ASJVstST_-UPLXRgpESEQpG5DCjlyFm7uc6F6bTKI3iA_Zhzc9vOlZZ0ImItqse2Xyjpd3vDMBfzo_vrPawLiaxihhjOI2lX0BirqHbg"
        }
    ]
}

.. _changes/timeseq:

Time-based changes feeds

.. versionadded:: 3.6

Starting with version 3.6 :ref:api/db/changes API can emit rows starting from a point in time. To use this feature set the since parameter value to a timestamp in the YYYY-MM-DDTHH:MM:SSZ format. That's a standard format in both the ISO 8601 and RFC 3339 standards.

Request:

.. code-block:: http

GET db/_changes?since=2026-01-09T19:01:02Z HTTP/1.1
Accept: application/json
Host: localhost:5984

Response:

.. code-block:: http

HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked

{
    "last_seq": "112-g1AAAABLeJzLYWBgYMxgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkMeC8N_IMjKYE4syAUKsZumWJgZGVhgasgCAKx6Erk",
    "pending": 0,
    "results": [
        {
            "changes": [{"rev": "1-967a00dff5e02add41819138abb3284d"}],
            "id": "019ba3f4f9b77e06900cf8b631821a3a",
            "seq": "111-g1AAAABLeJzLYWBgYMxgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkMeC8N_IMjKYE7MzwUKsZumWJgZGVhgasgCAKxaErg"
        },
        {
            "changes": [{"rev": "1-967a00dff5e02add41819138abb3284d"}],
            "id": "019ba3f51e077f0c82ca136136de2d7f",
            "seq": "112-g1AAAABLeJzLYWBgYMxgTmHgz8tPSTV0MDQy1zMAQsMcoARTIkMeC8N_IMjKYE4syAUKsZumWJgZGVhgasgCAKx6Erk"
        }
    ]
}

Implementation Details

CouchDB automatically maps time intervals to db update sequences as documents get updated. This is implemented with a histogram data structure which looks like [{t3, seq3}, {t2, seq2}, {t1, seq1}, ...].

In order to avoid any performance impact, and provide constant update and read algorithmic complexity, there is a maximum number (60) of these histogram bins per shard file. As a trade-off, however, it means not having exact timestamps for individual changes; a single bin may represent a group of updates.

Bin sizes increase exponentially the further back in time they go. This means, for example, that for the recent 24 hours it's possible to target individual hour blocks; a week back it's only possible to target individual days; and a few years back can only target individual months.

How Time Bins Are Updated Over Time

The smallest time granularity is 3 hours. It's not possible to target time intervals smaller than that.

A new database starts with an empty list of time bins ([]). On the first update, the first 3 hour bin will be created.

.. code-block:: text

[3h]

If updates continue, after 3 hours there might be a few more 3h bins added

.. code-block:: text

[3h, 3h, 3h]

After about 60 * 3hs ~= 1 week, all 60 bins would be filled with 3h bins.

.. code-block:: text

[3h, 3h, ..., 3h, 3h, 3h] <--------- 60 ---------->

Next, some of the oldest bins will start to be merged together to form 6 hour bins:

.. code-block:: text

[6h, 6h, ..., 3h, 3h, 3h] <--------- 60 ---------->

And after a few years it might look like:

.. code-block:: text

[4y, 2y, ... 12h, 6h, 3h] <--------- 60 ---------->

_time_seq Endpoint

The new :ref:_time_seq <api/db/time_seq> API endpoint can help inspect the time histograms for each db shard file.

Request:

.. code-block:: http

GET db/_time_seq HTTP/1.1
Accept: application/json
Host: localhost:5984

Response:

.. code-block:: http

HTTP/1.1 200 OK
Content-Type: application/json

{
    "time_seq": {
        "00000000-ffffffff": {
            "[email protected]": [
                ["2026-01-08T03:00:00Z", 14],
                ["2026-01-08T09:00:00Z", 5],
                ["2026-01-08T15:00:00Z", 22],
                ["2026-01-08T18:00:00Z", 8],
                ["2026-01-08T21:00:00Z", 16],
                ["2026-01-09T03:00:00Z", 20],
                ["2026-01-09T06:00:00Z", 5],
                ["2026-01-09T09:00:00Z", 5],
                ["2026-01-09T12:00:00Z", 5],
                ["2026-01-09T15:00:00Z", 10],
                ["2026-01-09T18:00:00Z", 2]
            ]
        }
    }
}

The times are the start times of each bin, and the values are the number of changes which occurred in that time interval. For example, in the time bin started at 2026-01-08T09:00:00Z there were 5 changes.

Since this is a histogram we can pipe it into a Python script with matlotlib and visualise it.

.. figure:: ../../../images/time_seq.png :align: center

 Time seq histogram.

Times are not exact

If the since time instant falls in the middle of a time bin interval, then all the rows from that time bine are returned. In other words, there is a chance to get some older rows than the provided since timestamp. If there is a need to get exact time bounded rows, use a custom time value per document and a selector filter with the since timestamp parameter.

This can be seen in the example above -- the passed in since value 2026-01-09T19:01:02Z doesn't fall on a bin start. So we got all the rows from the start of the 2026-01-09T18:00:00Z bin. If we zoom in on the rendered histogram to the right this might look like this:

.. figure:: ../../../images/time_seq_zoom.png :align: center

 Zoom in to last three bins.