<commit_before>#!/usr/bin/env python
# Copyright (c) 2015 IBM. All rights reserved.
#
# 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 a
#
#     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 module that maps to a Cloudant or CouchDB database instance.
"""
import json
import contextlib
import posixpath

from requests.exceptions import HTTPError

from ._2to3 import url_quote_plus, iteritems_
from ._common_util import (
    JSON_INDEX_TYPE,
    SEARCH_INDEX_ARGS,
    SPECIAL_INDEX_TYPE,
    TEXT_INDEX_TYPE,
    get_docs)
from .document import Document
from .design_document import DesignDocument
from .view import View
from .index import Index, TextIndex, SpecialIndex
from .query import Query
from .error import CloudantException, CloudantArgumentError
from .result import Result, QueryResult
from .feed import Feed, InfiniteFeed

class CouchDatabase(dict):
    """
    Encapsulates a CouchDB database.  A CouchDatabase object is
    instantiated with a reference to a client/session.
    It supports accessing the documents, and various database
    features such as the document indexes, changes feed, design documents, etc.

    :param CouchDB client: Client instance used by the database.
    :param str database_name: Database name used to reference the database.
    :param int fetch_limit: Optional fetch limit used to set the max number of
        documents to fetch per query during iteration cycles.  Defaults to 100.
    """
    def __init__(self, client, database_name, fetch_limit=100):
        super(CouchDatabase, self).__init__()
        self.client = client
        self._database_host = client.server_url
        self.database_name = database_name
        self.r_session = client.r_session
        self._fetch_limit = fetch_limit
        self.result = Result(self.all_docs)

    @property
    def admin_party(self):
        """
        Returns the CouchDB Admin Party status.  ``True`` if using Admin Party
        ``False`` otherwise.

        :returns: CouchDB Admin Party mode status
        """
        return self.client.admin_party

    @property
    def database_url(self):
        """
        Constructs and returns the database URL.

        :returns: Database URL
        """
        return posixpath.join(
            self._database_host,
            url_quote_plus(self.database_name)
        )

    @property
    def creds(self):
        """
        Retrieves a dictionary of useful authentication information
        that can be used to authenticate against this database.

        :returns: Dictionary containing authentication information
        """
        if self.admin_party:
            return None
        return {
            "basic_auth": self.client.basic_auth_str(),
            "user_ctx": self.client.session()['userCtx']
        }

    def exists(self):
        """
        Performs an existence check on the remote database.

        :returns: Boolean True if the database exists, False otherwise
        """
        resp = self.r_session.get(self.database_url)
        return resp.status_code == 200

    def metadata(self):
        """
        Retrieves the remote database metadata dictionary.

        :returns: Dictionary containing database metadata details
        """
        resp = self.r_session.get(self.database_url)
        resp.raise_for_status()
        return resp.json()

    def doc_count(self):
        """
        Retrieves the number of documents in the remote database

        :returns: Database document count
        """
        return self.metadata().get('doc_count')

    def create_document(self, data, throw_on_exists=False):
        """
        Creates a new document in the remote and locally cached database, using
        the data provided.  If an _id is included in the data then depending on
        that _id either a :class:`~cloudant.document.Document` or a
        :class:`~cloudant.design_document.DesignDocument`
        object will be added to the locally cached database and returned by this
        method.

        :param dict data: Dictionary of document JSON data, containing _id.
        :param bool throw_on_exists: Optional flag dictating whether to raise
            an exception if the document already exists in the database.

        :returns: A :class:`~cloudant.document.Document` or
            :class:`~cloudant.design_document.DesignDocument` instance
            corresponding to the new document in the database.
        """
        docid = data.get('_id', None)
        doc = None
        if docid and docid.startswith('_design/'):
            doc = DesignDocument(self, docid)
        else:
            doc = Document(self, docid)
        if throw_on_exists and doc.exists():
            raise CloudantException(
                'Error - Document with id {0} already exists.'.format(docid)
            )
        doc.update(data)
        doc.create()
        super(CouchDatabase, self).__setitem__(doc['_id'], doc)
        return doc

    def new_document(self):
        """
        Creates a new, empty document in the remote and locally cached database,
        auto-generating the _id.

        :returns: Document instance corresponding to the new document in the
            database
        """
        doc = Document(self, None)
        doc.create()
        super(CouchDatabase, self).__setitem__(doc['_id'], doc)
        return doc

    def design_documents(self):
        """
        Retrieve the JSON content for all design documents in this database.
        Performs a remote call to retrieve the content.

        :returns: All design documents found in this database in JSON format
        """
        url = posixpath.join(self.database_url, '_all_docs')
        query = "startkey=\"_design\"&endkey=\"_design0\"&include_docs=true"
        resp = self.r_session.get(url, params=query)
        resp.raise_for_status()
        data = resp.json()
        return data['rows']

    def list_design_documents(self):
        """
        Retrieves a list of design document names in this database.
        Performs a remote call to retrieve the content.

        :returns: List of names for all design documents in this database
        """
        url = posixpath.join(self.database_url, '_all_docs')
        query = "startkey=\"_design\"&endkey=\"_design0\""
        resp = self.r_session.get(url, params=query)
        resp.raise_for_status()
        data = resp.json()
        return [x.get('key') for x in data.get('rows', [])]

    def get_design_document(self, ddoc_id):
        """
        Retrieves a design document.  If a design document exists remotely
        then that content is wrapped in a DesignDocument object and returned
        to the caller.  Otherwise a "shell" DesignDocument object is returned.

        :param str ddoc_id: Design document id

        :returns: A DesignDocument instance, if exists remotely then it will
            be populated accordingly
        """
        ddoc = DesignDocument(self, ddoc_id)
        try:
            ddoc.fetch()
        except HTTPError as error:
            if error.response.status_code != 404:
                raise

        return ddoc

    def get_view_result(self, ddoc_id, view_name, raw_result=False, **kwargs):
        """
        Retrieves the view result based on the design document and view name.
        By default the result is returned as a
        :class:`~cloudant.result.Result` object which provides a key
        accessible, sliceable, and iterable interface to the result collection.
        Depending on how you are accessing, slicing or iterating through your
        result collection certain query parameters are not permitted.  See
        :class:`~cloudant.result.Result` for additional details.

        However, by setting ``raw_result=True``, the result will be returned as
        the raw JSON response content for the view requested.  With this setting
        there are no restrictions on the query parameters used but it also
        means that the result collection key access, slicing, and iteration is
        the responsibility of the developer.

        For example:

        .. code-block:: python

            # get Result based on a design document view
            result = db.get_view_result('_design/ddoc_id_001', 'view_001')

            # get a customized Result based on a design document view
            result = db.get_view_result('_design/ddoc_id_001', 'view_001',
                include_docs=True, reduce=False)

            # get raw response content based on a design document view
            result = db.get_view_result('_design/ddoc_id_001', 'view_001',
                raw_result=True)

            # get customized raw response content for a design document view
            db.get_view_result('_design/ddoc_id_001', 'view_001',
                raw_result=True, include_docs=True, skip=100, limit=100)

        For more detail on key access, slicing and iteration, refer to the
        :class:`~cloudant.result.Result` documentation.

        :param str ddoc_id: Design document id used to get result.
        :param str view_name: Name of the view used to get result.
        :param bool raw_result: Dictates whether the view result is returned
            as a default Result object or a raw JSON response.
            Defaults to False.
        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool group: Using the reduce function, group the results to a
            group or single row.
        :param group_level: Only applicable if the view uses complex keys: keys
            that are lists. Groups reduce results for the specified number
            of list fields.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param list keys: Return only documents that match the specified keys.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param int limit: Limit the number of returned documents to the
            specified count.  Not valid when used with
            :class:`~cloudant.result.Result` iteration.
        :param int page_size: Sets the page size for result iteration.
            Only valid if used with ``raw_result=False``.
        :param bool reduce: True to use the reduce function, false otherwise.
        :param int skip: Skip this number of rows from the start.
            Not valid when used with :class:`~cloudant.result.Result` iteration.
        :param str stale: Allow the results from a stale view to be used. This
            makes the request return immediately, even if the view has not been
            completely built yet. If this parameter is not given, a response is
            returned only after the view has been built.
        :param startkey: Return records starting with the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        :returns: The result content either wrapped in a QueryResult or
            as the raw response JSON content
        """
        view = View(DesignDocument(self, ddoc_id), view_name)
        if raw_result:
            return view(**kwargs)
        elif kwargs:
            return Result(view, **kwargs)
        else:
            return view.result

    def create(self):
        """
        Creates a database defined by the current database object, if it
        does not already exist and raises a CloudantException if the operation
        fails.  If the database already exists then this method call is a no-op.

        :returns: The database object
        """
        if self.exists():
            return self

        resp = self.r_session.put(self.database_url)
        if resp.status_code == 201 or resp.status_code == 202:
            return self

        raise CloudantException(
            "Unable to create database {0}: Reason: {1}".format(
                self.database_url, resp.text
            ),
            code=resp.status_code
        )

    def delete(self):
        """
        Deletes the current database from the remote instance.
        """
        resp = self.r_session.delete(self.database_url)
        resp.raise_for_status()

    def all_docs(self, **kwargs):
        """
        Wraps the _all_docs primary index on the database, and returns the
        results by value. This can be used as a direct query to the _all_docs
        endpoint.  More convenient/efficient access using keys, slicing
        and iteration can be done through the ``result`` attribute.

        Keyword arguments supported are those of the view/index access API.

        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
        :param list keys: Return only documents that match the specified keys.
        :param int limit: Limit the number of returned documents to the
            specified count.
        :param int skip: Skip this number of rows from the start.
        :param startkey: Return records starting with the specified key.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        :returns: Raw JSON response content from ``_all_docs`` endpoint

        """
        resp = get_docs(self.r_session,
                        '/'.join([self.database_url, '_all_docs']),
                        self.client.encoder,
                        **kwargs)
        return resp.json()

    @contextlib.contextmanager
    def custom_result(self, **options):
        """
        Provides a context manager that can be used to customize the
        ``_all_docs`` behavior and wrap the output as a
        :class:`~cloudant.result.Result`.

        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param list keys: Return only documents that match the specified keys.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param int page_size: Sets the page size for result iteration.
        :param startkey: Return records starting with the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        For example:

        .. code-block:: python

            with database.custom_result(include_docs=True) as rslt:
                data = rslt[100: 200]
        """
        rslt = Result(self.all_docs, **options)
        yield rslt
        del rslt

    def keys(self, remote=False):
        """
        Retrieves the list of document ids in the database.  Default is
        to return only the locally cached document ids, specify remote=True
        to make a remote request to include all document ids from the remote
        database instance.

        :param bool remote: Dictates whether the list of locally cached
            document ids are returned or a remote request is made to include
            an up to date list of document ids from the server.
            Defaults to False.

        :returns: List of document ids
        """
        if not remote:
            return list(super(CouchDatabase, self).keys())
        docs = self.all_docs()
        return [row['id'] for row in docs.get('rows', [])]

    def changes(self, raw_data=False, **kwargs):
        """
        Returns the ``_changes`` feed iterator.  The ``_changes`` feed can be
        iterated over and once complete can also provide the last sequence
        identifier of the feed.  If necessary, the iteration can be stopped by
        issuing a call to the ``stop()`` method on the returned iterator object.

        For example:

        .. code-block:: python

            # Iterate over a "normal" _changes feed
            changes = db.changes()
            for change in changes:
                print(change)
            print(changes.last_seq)

            # Iterate over a "continuous" _changes feed with additional options
            changes = db.changes(feed='continuous', since='now', descending=True)
            for change in changes:
                if some_condition:
                    db_updates.stop()
                print(change)

        :param bool raw_data: If set to True then the raw response data will be
            streamed otherwise if set to False then JSON formatted data will be
            streamed.  Default is False.
        :param bool conflicts: Can only be set if include_docs is True. Adds
            information about conflicts to each document.  Default is False.
        :param bool descending: Changes appear in sequential order.  Default is
            False.
        :param list doc_ids: To be used only when ``filter`` is set to
            ``_doc_ids``. Filters the feed so that only changes to the
            specified documents are sent.
        :param str feed: Type of feed.  Valid values are ``continuous``,
            ``longpoll``, and ``normal``.  Default is ``normal``.
        :param str filter: Name of filter function from a design document to get
            updates.  Default is no filter.
        :param int heartbeat: Time in milliseconds after which an empty line is
            sent during ``longpoll`` or ``continuous`` if there have been no
            changes.  Must be a positive number.  Default is no heartbeat.
        :param bool include_docs: Include the document with the result.  The
            document will not be returned as a
            :class:`~cloudant.document.Document` but instead will be returned as
            either formated JSON or as raw response content.  Default is False.
        :param int limit: Maximum number of rows to return.  Must be a positive
            number.  Default is no limit.
        :param since: Start the results from changes after the specified
            sequence identifier. In other words, using since excludes from the
            list all changes up to and including the specified sequence
            identifier. If since is 0 (the default), or omitted, the request<commit_msg>Corrected changes example doc<commit_after>#!/usr/bin/env python
# Copyright (c) 2015 IBM. All rights reserved.
#
# 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 a
#
#     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 module that maps to a Cloudant or CouchDB database instance.
"""
import json
import contextlib
import posixpath

from requests.exceptions import HTTPError

from ._2to3 import url_quote_plus, iteritems_
from ._common_util import (
    JSON_INDEX_TYPE,
    SEARCH_INDEX_ARGS,
    SPECIAL_INDEX_TYPE,
    TEXT_INDEX_TYPE,
    get_docs)
from .document import Document
from .design_document import DesignDocument
from .view import View
from .index import Index, TextIndex, SpecialIndex
from .query import Query
from .error import CloudantException, CloudantArgumentError
from .result import Result, QueryResult
from .feed import Feed, InfiniteFeed

class CouchDatabase(dict):
    """
    Encapsulates a CouchDB database.  A CouchDatabase object is
    instantiated with a reference to a client/session.
    It supports accessing the documents, and various database
    features such as the document indexes, changes feed, design documents, etc.

    :param CouchDB client: Client instance used by the database.
    :param str database_name: Database name used to reference the database.
    :param int fetch_limit: Optional fetch limit used to set the max number of
        documents to fetch per query during iteration cycles.  Defaults to 100.
    """
    def __init__(self, client, database_name, fetch_limit=100):
        super(CouchDatabase, self).__init__()
        self.client = client
        self._database_host = client.server_url
        self.database_name = database_name
        self.r_session = client.r_session
        self._fetch_limit = fetch_limit
        self.result = Result(self.all_docs)

    @property
    def admin_party(self):
        """
        Returns the CouchDB Admin Party status.  ``True`` if using Admin Party
        ``False`` otherwise.

        :returns: CouchDB Admin Party mode status
        """
        return self.client.admin_party

    @property
    def database_url(self):
        """
        Constructs and returns the database URL.

        :returns: Database URL
        """
        return posixpath.join(
            self._database_host,
            url_quote_plus(self.database_name)
        )

    @property
    def creds(self):
        """
        Retrieves a dictionary of useful authentication information
        that can be used to authenticate against this database.

        :returns: Dictionary containing authentication information
        """
        if self.admin_party:
            return None
        return {
            "basic_auth": self.client.basic_auth_str(),
            "user_ctx": self.client.session()['userCtx']
        }

    def exists(self):
        """
        Performs an existence check on the remote database.

        :returns: Boolean True if the database exists, False otherwise
        """
        resp = self.r_session.get(self.database_url)
        return resp.status_code == 200

    def metadata(self):
        """
        Retrieves the remote database metadata dictionary.

        :returns: Dictionary containing database metadata details
        """
        resp = self.r_session.get(self.database_url)
        resp.raise_for_status()
        return resp.json()

    def doc_count(self):
        """
        Retrieves the number of documents in the remote database

        :returns: Database document count
        """
        return self.metadata().get('doc_count')

    def create_document(self, data, throw_on_exists=False):
        """
        Creates a new document in the remote and locally cached database, using
        the data provided.  If an _id is included in the data then depending on
        that _id either a :class:`~cloudant.document.Document` or a
        :class:`~cloudant.design_document.DesignDocument`
        object will be added to the locally cached database and returned by this
        method.

        :param dict data: Dictionary of document JSON data, containing _id.
        :param bool throw_on_exists: Optional flag dictating whether to raise
            an exception if the document already exists in the database.

        :returns: A :class:`~cloudant.document.Document` or
            :class:`~cloudant.design_document.DesignDocument` instance
            corresponding to the new document in the database.
        """
        docid = data.get('_id', None)
        doc = None
        if docid and docid.startswith('_design/'):
            doc = DesignDocument(self, docid)
        else:
            doc = Document(self, docid)
        if throw_on_exists and doc.exists():
            raise CloudantException(
                'Error - Document with id {0} already exists.'.format(docid)
            )
        doc.update(data)
        doc.create()
        super(CouchDatabase, self).__setitem__(doc['_id'], doc)
        return doc

    def new_document(self):
        """
        Creates a new, empty document in the remote and locally cached database,
        auto-generating the _id.

        :returns: Document instance corresponding to the new document in the
            database
        """
        doc = Document(self, None)
        doc.create()
        super(CouchDatabase, self).__setitem__(doc['_id'], doc)
        return doc

    def design_documents(self):
        """
        Retrieve the JSON content for all design documents in this database.
        Performs a remote call to retrieve the content.

        :returns: All design documents found in this database in JSON format
        """
        url = posixpath.join(self.database_url, '_all_docs')
        query = "startkey=\"_design\"&endkey=\"_design0\"&include_docs=true"
        resp = self.r_session.get(url, params=query)
        resp.raise_for_status()
        data = resp.json()
        return data['rows']

    def list_design_documents(self):
        """
        Retrieves a list of design document names in this database.
        Performs a remote call to retrieve the content.

        :returns: List of names for all design documents in this database
        """
        url = posixpath.join(self.database_url, '_all_docs')
        query = "startkey=\"_design\"&endkey=\"_design0\""
        resp = self.r_session.get(url, params=query)
        resp.raise_for_status()
        data = resp.json()
        return [x.get('key') for x in data.get('rows', [])]

    def get_design_document(self, ddoc_id):
        """
        Retrieves a design document.  If a design document exists remotely
        then that content is wrapped in a DesignDocument object and returned
        to the caller.  Otherwise a "shell" DesignDocument object is returned.

        :param str ddoc_id: Design document id

        :returns: A DesignDocument instance, if exists remotely then it will
            be populated accordingly
        """
        ddoc = DesignDocument(self, ddoc_id)
        try:
            ddoc.fetch()
        except HTTPError as error:
            if error.response.status_code != 404:
                raise

        return ddoc

    def get_view_result(self, ddoc_id, view_name, raw_result=False, **kwargs):
        """
        Retrieves the view result based on the design document and view name.
        By default the result is returned as a
        :class:`~cloudant.result.Result` object which provides a key
        accessible, sliceable, and iterable interface to the result collection.
        Depending on how you are accessing, slicing or iterating through your
        result collection certain query parameters are not permitted.  See
        :class:`~cloudant.result.Result` for additional details.

        However, by setting ``raw_result=True``, the result will be returned as
        the raw JSON response content for the view requested.  With this setting
        there are no restrictions on the query parameters used but it also
        means that the result collection key access, slicing, and iteration is
        the responsibility of the developer.

        For example:

        .. code-block:: python

            # get Result based on a design document view
            result = db.get_view_result('_design/ddoc_id_001', 'view_001')

            # get a customized Result based on a design document view
            result = db.get_view_result('_design/ddoc_id_001', 'view_001',
                include_docs=True, reduce=False)

            # get raw response content based on a design document view
            result = db.get_view_result('_design/ddoc_id_001', 'view_001',
                raw_result=True)

            # get customized raw response content for a design document view
            db.get_view_result('_design/ddoc_id_001', 'view_001',
                raw_result=True, include_docs=True, skip=100, limit=100)

        For more detail on key access, slicing and iteration, refer to the
        :class:`~cloudant.result.Result` documentation.

        :param str ddoc_id: Design document id used to get result.
        :param str view_name: Name of the view used to get result.
        :param bool raw_result: Dictates whether the view result is returned
            as a default Result object or a raw JSON response.
            Defaults to False.
        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool group: Using the reduce function, group the results to a
            group or single row.
        :param group_level: Only applicable if the view uses complex keys: keys
            that are lists. Groups reduce results for the specified number
            of list fields.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param list keys: Return only documents that match the specified keys.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param int limit: Limit the number of returned documents to the
            specified count.  Not valid when used with
            :class:`~cloudant.result.Result` iteration.
        :param int page_size: Sets the page size for result iteration.
            Only valid if used with ``raw_result=False``.
        :param bool reduce: True to use the reduce function, false otherwise.
        :param int skip: Skip this number of rows from the start.
            Not valid when used with :class:`~cloudant.result.Result` iteration.
        :param str stale: Allow the results from a stale view to be used. This
            makes the request return immediately, even if the view has not been
            completely built yet. If this parameter is not given, a response is
            returned only after the view has been built.
        :param startkey: Return records starting with the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        :returns: The result content either wrapped in a QueryResult or
            as the raw response JSON content
        """
        view = View(DesignDocument(self, ddoc_id), view_name)
        if raw_result:
            return view(**kwargs)
        elif kwargs:
            return Result(view, **kwargs)
        else:
            return view.result

    def create(self):
        """
        Creates a database defined by the current database object, if it
        does not already exist and raises a CloudantException if the operation
        fails.  If the database already exists then this method call is a no-op.

        :returns: The database object
        """
        if self.exists():
            return self

        resp = self.r_session.put(self.database_url)
        if resp.status_code == 201 or resp.status_code == 202:
            return self

        raise CloudantException(
            "Unable to create database {0}: Reason: {1}".format(
                self.database_url, resp.text
            ),
            code=resp.status_code
        )

    def delete(self):
        """
        Deletes the current database from the remote instance.
        """
        resp = self.r_session.delete(self.database_url)
        resp.raise_for_status()

    def all_docs(self, **kwargs):
        """
        Wraps the _all_docs primary index on the database, and returns the
        results by value. This can be used as a direct query to the _all_docs
        endpoint.  More convenient/efficient access using keys, slicing
        and iteration can be done through the ``result`` attribute.

        Keyword arguments supported are those of the view/index access API.

        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
        :param list keys: Return only documents that match the specified keys.
        :param int limit: Limit the number of returned documents to the
            specified count.
        :param int skip: Skip this number of rows from the start.
        :param startkey: Return records starting with the specified key.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        :returns: Raw JSON response content from ``_all_docs`` endpoint

        """
        resp = get_docs(self.r_session,
                        '/'.join([self.database_url, '_all_docs']),
                        self.client.encoder,
                        **kwargs)
        return resp.json()

    @contextlib.contextmanager
    def custom_result(self, **options):
        """
        Provides a context manager that can be used to customize the
        ``_all_docs`` behavior and wrap the output as a
        :class:`~cloudant.result.Result`.

        :param bool descending: Return documents in descending key order.
        :param endkey: Stop returning records at this specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str endkey_docid: Stop returning records when the specified
            document id is reached.
        :param bool include_docs: Include the full content of the documents.
        :param bool inclusive_end: Include rows with the specified endkey.
        :param key: Return only documents that match the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param list keys: Return only documents that match the specified keys.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param int page_size: Sets the page size for result iteration.
        :param startkey: Return records starting with the specified key.
            Not valid when used with :class:`~cloudant.result.Result` key
            access and key slicing.
        :param str startkey_docid: Return records starting with the specified
            document ID.

        For example:

        .. code-block:: python

            with database.custom_result(include_docs=True) as rslt:
                data = rslt[100: 200]
        """
        rslt = Result(self.all_docs, **options)
        yield rslt
        del rslt

    def keys(self, remote=False):
        """
        Retrieves the list of document ids in the database.  Default is
        to return only the locally cached document ids, specify remote=True
        to make a remote request to include all document ids from the remote
        database instance.

        :param bool remote: Dictates whether the list of locally cached
            document ids are returned or a remote request is made to include
            an up to date list of document ids from the server.
            Defaults to False.

        :returns: List of document ids
        """
        if not remote:
            return list(super(CouchDatabase, self).keys())
        docs = self.all_docs()
        return [row['id'] for row in docs.get('rows', [])]

    def changes(self, raw_data=False, **kwargs):
        """
        Returns the ``_changes`` feed iterator.  The ``_changes`` feed can be
        iterated over and once complete can also provide the last sequence
        identifier of the feed.  If necessary, the iteration can be stopped by
        issuing a call to the ``stop()`` method on the returned iterator object.

        For example:

        .. code-block:: python

            # Iterate over a "normal" _changes feed
            changes = db.changes()
            for change in changes:
                print(change)
            print(changes.last_seq)

            # Iterate over a "continuous" _changes feed with additional options
            changes = db.changes(feed='continuous', since='now', descending=True)
            for change in changes:
                if some_condition:
                    changes.stop()
                print(change)

        :param bool raw_data: If set to True then the raw response data will be
            streamed otherwise if set to False then JSON formatted data will be
            streamed.  Default is False.
        :param bool conflicts: Can only be set if include_docs is True. Adds
            information about conflicts to each document.  Default is False.
        :param bool descending: Changes appear in sequential order.  Default is
            False.
        :param list doc_ids: To be used only when ``filter`` is set to
            ``_doc_ids``. Filters the feed so that only changes to the
            specified documents are sent.
        :param str feed: Type of feed.  Valid values are ``continuous``,
            ``longpoll``, and ``normal``.  Default is ``normal``.
        :param str filter: Name of filter function from a design document to get
            updates.  Default is no filter.
        :param int heartbeat: Time in milliseconds after which an empty line is
            sent during ``longpoll`` or ``continuous`` if there have been no
            changes.  Must be a positive number.  Default is no heartbeat.
        :param bool include_docs: Include the document with the result.  The
            document will not be returned as a
            :class:`~cloudant.document.Document` but instead will be returned as
            either formated JSON or as raw response content.  Default is False.
        :param int limit: Maximum number of rows to return.  Must be a positive
            number.  Default is no limit.
        :param since: Start the results from changes after the specified
            sequence identifier. In other words, using since excludes from the
            list all changes up to and including the specified sequence
            identifier. If since is 0 (the default), or omitted, the request
{'fraction_non_alphanumeric': 0.06523728268664837, 'fraction_numerical': 0.0034621757301481317, 'mean_word_length': 2.3059189012426424, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6579217', 'n_tokens_mistral': 10085, 'n_tokens_neox': 9749, 'n_words': 4304}