Example (output abridged and indented for clarity):
    >>> admin_for_my_db.find_embedding_providers()
    FindEmbeddingProvidersResult(embedding_providers=..., openai, ...)
    >>> admin_for_my_db.find_embedding_providers().embedding_providers
    {
        'openai': EmbeddingProvider(
            display_name='OpenAI',
            models=[
                EmbeddingProviderModel(name='text-embedding-3-small'),
                ...
            ]
        ),
        ...
    }

A `FindEmbeddingProvidersResult` object with the complete information

returned by the API about available embedding providers


a timeout, in milliseconds, for the DevOps API request.


Convert a vector of strings to a vector of floats.

vector

A vector of objects.

list

A vector of floats.


Safely determine if it's a list of floats.
Assumption: if list, and first item is float, then all items are.


The path helps determining special treatments


Normalize a payload for API calls.
This includes e.g. ensuring values for "$vector" key
are made into plain lists of floats.

payload

A dict expressing a payload for an API call

Dict[str, Any]: a "normalized" payload dict


Process a dictionary just returned from the API.
This is the place where e.g. `{"$date": 123}` is
converted back into a datetime object.


Return a shortened-if-necessary version of a 'secret' string (with ellipsis).

secret

a secret string to redact

if the secret and the fixed ending exceed this size,
shortening takes place.

this controls what to do when the input secret is
shorter, i.e. when no shortening takes place.
if False, the secret is returned as-is;
If True, a masked string is returned of the same length as secret.

a 'redacted' form of the secret string as per the rules outlined above.


Abstract base class for a token provider.
The relevant method in this interface is returning a string to use as token.

The __str__ / __repr__ methods are NOT to be used as source of tokens:
use get_token instead.

Note that equality (__eq__) checks if the generated tokens match
under all circumstances (e.g. a literal passthrough matches a
different-encoding token provider that yields the same token).
If a token provider comes that encodes a recipe for nondeterministic
periodic renewal, its __eq__ method will have to override the one in this class.


Produce a string for direct use as token in a subsequent API request,
or None for no token.


A "pass-through" provider that wraps a supplied literal token.

StaticTokenProvider

token

an access token for subsequent use in the client.

>>> from astrapy import DataAPIClient
>>> from astrapy.authentication import StaticTokenProvider
>>> token_provider = StaticTokenProvider("AstraCS:xyz...")
>>> database = DataAPIClient().get_database(
...     "https://01234567-...-eu-west1.apps.datastax.com",
...     token=token_provider,
... )


A token provider encoding username/password-based authentication,
as used e.g. for DSE and HCD. These are base64-encoded and concatenated
by colons, with a prepended suffix 'Cassandra', as required by
this authentication scheme.

UsernamePasswordTokenProvider

username

the username for accessing the database.

password

the corresponding password.

>>> from astrapy import DataAPIClient
>>> from astrapy.constants imort Environment
>>> from astrapy.authentication import UsernamePasswordTokenProvider
>>> token_provider = UsernamePasswordTokenProvider("username", "password")
>>> database = DataAPIClient(environment=Environment.HCD).get_database(
...     "http://localhost:8181",
...     token=token_provider,
... )


Abstract base class for a provider of embedding-related headers (such as API Keys).
The relevant method in this interface is returning a dict to use as
(part of the) headers in Data API requests for a collection.

This class captures the fact that, depending on the embedding provider for
the collection, there may be zero, one *or more* headers to be passed
if relying on the HEADERS auth method for Vectorize.


Produce a dictionary for use as (part of) the headers in HTTP requests
to the Data API.


A "pass-through" header provider representing the single-header
(typically "X-Embedding-Api-Key") auth scheme, in use by most of the
embedding models in Vectorize.

EmbeddingAPIKeyHeaderProvider

a string that will be the value for the header.
If None is passed, this results in a no-headers provider (such
as the one used for non-Vectorize collections).

>>> from astrapy import DataAPIClient
>>> from astrapy.authentication import (
    CollectionVectorServiceOptions,
    EmbeddingAPIKeyHeaderProvider,
)
>>> my_emb_api_key = EmbeddingAPIKeyHeaderProvider("abc012...")
>>> service_options = CollectionVectorServiceOptions(
...     provider="a-certain-provider",
...     model_name="some-embedding-model",
... )
>>>
>>> database = DataAPIClient().get_database(
...     "https://01234567-...-eu-west1.apps.datastax.com",
...     token="AstraCS:...",
... )
>>> collection = database.create_collection(
...     "vectorize_collection",
...     service=service_options,
...     embedding_api_key=my_emb_api_key,
... )
>>> # likewise:
>>> collection_b = database.get_collection(
...     "vectorize_collection",
...     embedding_api_key=my_emb_api_key,
... )


A header provider representing the two-header auth scheme in use
by the Amazon Web Services (e.g. AWS Bedrock) when using header-based
authentication.

AWSEmbeddingHeadersProvider

value of the "Access ID" secret. This will become
the value for the corresponding header.

value of the "Secret ID" secret. This will become
the value for the corresponding header.

>>> from astrapy import DataAPIClient
>>> from astrapy.authentication import (
    CollectionVectorServiceOptions,
    AWSEmbeddingHeadersProvider,
)
>>> my_aws_emb_api_key = AWSEmbeddingHeadersProvider(
    embedding_access_id="my-access-id-012...",
    embedding_secret_id="my-secret-id-abc...",
)
>>> service_options = CollectionVectorServiceOptions(
...     provider="bedrock",
...     model_name="some-aws-bedrock-model",
... )
>>>
>>> database = DataAPIClient().get_database(
...     "https://01234567-...-eu-west1.apps.datastax.com",
...     token="AstraCS:...",
... )
>>> collection = database.create_collection(
...     "vectorize_aws_collection",
...     service=service_options,
...     embedding_api_key=my_aws_emb_api_key,
... )
>>> # likewise:
>>> collection_b = database.get_collection(
...     "vectorize_aws_collection",
...     embedding_api_key=my_aws_emb_api_key,
... )


A Data API collection, the main object to interact with the Data API,
especially for DDL operations.
This class has a synchronous interface.

A Collection is spawned from a Database object, from which it inherits
the details on how to reach the API server (endpoint, authentication token).

Collection

database

a Database object, instantiated earlier. This represents
the database the collection belongs to.

name

the collection name. This parameter should match an existing
collection on the database.

keyspace

this is the keyspace to which the collection belongs.
If not specified, the database's working keyspace is used.

namespace

an alias for `keyspace`. *DEPRECATED*, removal in 2.0.

An instance of `astrapy.api_options.CollectionAPIOptions`
providing the general settings for interacting with the Data API.

callers

a list of caller identities, i.e. applications, or frameworks,
on behalf of which the Data API calls are performed. These end up
in the request user-agent.
Each caller identity is a ("caller_name", "caller_version") pair.

*DEPRECATED*, use `callers`. Removal 2.0. Name of the
application, or framework, on behalf of which the Data API calls
are performed. This ends up in the request user-agent.

version of the caller. *DEPRECATED*, use `callers`. Removal 2.0.

>>> from astrapy import DataAPIClient, Collection
>>> my_client = astrapy.DataAPIClient("AstraCS:...")
>>> my_db = my_client.get_database(
...    "https://01234567-....apps.astra.datastax.com"
... )
>>> my_coll_1 = Collection(database=my_db, name="my_collection")
>>> my_coll_2 = my_db.create_collection(
...     "my_v_collection",
...     dimension=3,
...     metric="cosine",
... )
>>> my_coll_3a = my_db.get_collection("my_already_existing_collection")
>>> my_coll_3b = my_db.my_already_existing_collection
>>> my_coll_3c = my_db["my_already_existing_collection"]

creating an instance of Collection does not trigger actual creation
of the collection on the database. The latter should have been created
beforehand, e.g. through the `create_collection` method of a Database.


Create a clone of this collection with some changed attributes.

name

the name of the collection. This parameter is useful to
quickly spawn Collection instances each pointing to a different
collection existing in the same keyspace.

optional API key(s) for interacting with the collection.
If an embedding service is configured, and this parameter is not None,
each Data API call will include the necessary embedding-related headers
as specified by this parameter. If a string is passed, it translates
into the one "embedding api key" header
(i.e. `astrapy.authentication.EmbeddingAPIKeyHeaderProvider`).
For some vectorize providers/models, if using header-based authentication,
specialized subclasses of `astrapy.authentication.EmbeddingHeadersProvider`
should be supplied.

a default timeout, in millisecond, for the duration of each
operation on the collection. Individual timeouts can be provided to
each collection method call and will take precedence, with this value
being an overall default.
Note that for some methods involving multiple API calls (such as
`find`, `delete_many`, `insert_many` and so on), it is strongly suggested
to provide a specific timeout as the default one likely wouldn't make
much sense.

version of the caller. *DEPRECATED*, use `callers`.
Removal 2.0.

a new Collection instance.

>>> my_other_coll = my_coll.with_options(
...     name="the_other_coll",
...     callers=[("caller_identity", "0.1.2")],
... )


Create an AsyncCollection from this one. Save for the arguments
explicitly provided as overrides, everything else is kept identical
to this collection in the copy (the database is converted into
an async object).

database

an AsyncDatabase object, instantiated earlier.
This represents the database the new collection belongs to.

the new copy, an AsyncCollection instance.


Set a new identity for the application/framework on behalf of which
the Data API calls are performed (the "caller").

name of the application, or framework, on behalf of which
the Data API calls are performed. This ends up in the request user-agent.

version of the caller.


Get the collection options, i.e. its configuration as read from the database.

The method issues a request to the Data API each time is invoked,
without caching mechanisms: this ensures up-to-date information
for usages such as real-time collection validation by the application.

options

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

a CollectionOptions instance describing the collection.

(See also the database `list_collections` method.)

>>> my_coll.options()
CollectionOptions(vector=CollectionVectorOptions(dimension=3, metric='cosine'))


Information on the collection (name, location, database), in the
form of a CollectionInfo object.

Not to be confused with the collection `options` method (related
to the collection internal configuration).

info

>>> my_coll.info().database_info.region
'eu-west-1'
>>> my_coll.info().full_name
'default_keyspace.my_v_collection'

the returned CollectionInfo wraps, among other things,
the database information: as such, calling this method
triggers the same-named method of a Database object (which, in turn,
performs a HTTP request to the DevOps API).
See the documentation for `Database.info()` for more details.


a Database object, the database this collection belongs to.


The namespace this collection is in.

*DEPRECATED* (removal in 2.0). Switch to the "keyspace" property.**


The keyspace this collection is in.


The name of this collection.


The fully-qualified collection name within the database,
in the form "keyspace.collection_name".


Insert a single document in the collection in an atomic operation.

document

the dictionary expressing the document to insert.
The `_id` field of the document can be left out, in which
case it will be created automatically.

vector

a vector (a list of numbers appropriate for the collection)
for the document. Passing this parameter is equivalent to
providing a `$vector` field within the document itself,
however the two are mutually exclusive.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the document instead.

vectorize

a string to be made into a vector, if such a service
is configured for the collection. Passing this parameter is
equivalent to providing a `$vectorize` field in the document itself,
however the two are mutually exclusive.
Moreover, this parameter cannot coexist with `vector`.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the document instead.

an InsertOneResult object.

>>> my_coll.count_documents({}, upper_bound=10)
0
>>> my_coll.insert_one(
...     {
...         "age": 30,
...         "name": "Smith",
...         "food": ["pear", "peach"],
...         "likes_fruit": True,
...     },
... )
InsertOneResult(raw_results=..., inserted_id='ed4587a4-...-...-...')
>>> my_coll.insert_one({"_id": "user-123", "age": 50, "name": "Maccio"})
InsertOneResult(raw_results=..., inserted_id='user-123')
>>> my_coll.count_documents({}, upper_bound=10)
2/n/n/n>>> my_coll.insert_one({"tag": "v", "$vector": [10, 11]})
InsertOneResult(...)

If an `_id` is explicitly provided, which corresponds to a document
that exists already in the collection, an error is raised and
the insertion fails.


Insert a list of documents into the collection.
This is not an atomic operation.

documents

an iterable of dictionaries, each a document to insert.
Documents may specify their `_id` field or leave it out, in which
case it will be added automatically.

vectors

an optional list of vectors (as many vectors as the provided
documents) to associate to the documents when inserting.
Passing vectors this way is indeed equivalent to the "$vector" field
of the documents, however the two are mutually exclusive.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the documents instead.

vectorize

an optional list of strings to be made into as many vectors
(one per document), if such a service is configured for the collection.
Passing this parameter is equivalent to providing a `$vectorize`
field in the documents themselves, however the two are mutually exclusive.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the documents instead.

ordered

if False (default), the insertions can occur in arbitrary order
and possibly concurrently. If True, they are processed sequentially.
If there are no specific reasons against it, unordered insertions are to
be preferred as they complete much faster.

how many documents to include in a single API request.
Exceeding the server maximum allowed value results in an error.
Leave it unspecified (recommended) to use the system default.

concurrency

maximum number of concurrent requests to the API at
a given time. It cannot be more than one for ordered insertions.

a timeout, in milliseconds, for the operation.
If not passed, the collection-level setting is used instead:
If many documents are being inserted, this method corresponds
to several HTTP requests: in such cases one may want to specify
a more tolerant timeout here.

an InsertManyResult object.

>>> my_coll.count_documents({}, upper_bound=10)
0
>>> my_coll.insert_many(
...     [{"a": 10}, {"a": 5}, {"b": [True, False, False]}],
...     ordered=True,
... )
InsertManyResult(raw_results=..., inserted_ids=['184bb06f-...', '...', '...'])
>>> my_coll.count_documents({}, upper_bound=100)
3
>>> my_coll.insert_many(
...     [{"seq": i} for i in range(50)],
...     concurrency=5,
... )
InsertManyResult(raw_results=..., inserted_ids=[... ...])
>>> my_coll.count_documents({}, upper_bound=100)
53
>>> my_coll.insert_many(
...     [
...         {"tag": "a", "$vector": [1, 2]},
...         {"tag": "b", "$vector": [3, 4]},
...     ]
... )
InsertManyResult(...)

A failure mode for this command is related to certain faulty documents
found among those to insert: a document may have the an `_id` already
present on the collection, or its vector dimension may not
match the collection setting.

For an ordered insertion, the method will raise an exception at
the first such faulty document -- nevertheless, all documents processed
until then will end up being written to the database.

For unordered insertions, if the error stems from faulty documents
the insertion proceeds until exhausting the input documents: then,
an exception is raised -- and all insertable documents will have been
written to the database, including those "after" the troublesome ones.

If, on the other hand, there are errors not related to individual
documents (such as a network connectivity error), the whole
`insert_many` operation will stop in mid-way, an exception will be raised,
and only a certain amount of the input documents will
have made their way to the database.


Find documents on the collection, matching a certain provided filter.

The method returns a Cursor that can then be iterated over. Depending
on the method call pattern, the iteration over all documents can reflect
collection mutations occurred since the `find` method was called, or not.
In cases where the cursor reflects mutations in real-time, it will iterate
over cursors in an approximate way (i.e. exhibiting occasional skipped
or duplicate documents). This happens when making use of the `sort`
option in a non-vector-search manner.

find

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

projection

it controls which parts of the document are returned.
It can be an allow-list: `{"f1": True, "f2": True}`,
or a deny-list: `{"fx": False, "fy": False}`, but not a mixture
(except for the `_id` and other special fields, which can be
associated to both True or False independently of the rest
of the specification).
The special star-projections `{"*": True}` and `{"*": False}`
have the effect of returning the whole document and `{}` respectively.
For lists in documents, slice directives can be passed to select
portions of the list: for instance, `{"array": {"$slice": 2}}`,
`{"array": {"$slice": -2}}`, `{"array": {"$slice": [4, 2]}}` or
`{"array": {"$slice": [-4, 2]}}`.
An iterable over strings will be treated implicitly as an allow-list.
The default projection (used if this parameter is not passed) does not
necessarily include "special" fields such as `$vector` or `$vectorize`.
See the Data API documentation for more on projections.

skip

with this integer parameter, what would be the first `skip`
documents returned by the query are discarded, and the results
start from the (skip+1)-th document.
This parameter can be used only in conjunction with an explicit
`sort` criterion of the ascending/descending type (i.e. it cannot
be used when not sorting, nor with vector-based ANN search).

limit

this (integer) parameter sets a limit over how many documents
are returned. Once `limit` is reached (or the cursor is exhausted
for lack of matching documents), nothing more is returned.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to perform vector search (i.e. ANN,
or "approximate nearest-neighbours" search).
When running similarity search on a collection, no other sorting
criteria can be specified. Moreover, there is an upper bound
to the number of documents that can be returned. For details,
see the Note about upper bounds and the Data API documentation.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
This can be supplied in (exclusive) alternative to `vector`,
provided such a service is configured for the collection,
and achieves the same effect.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

a boolean to request the numeric value of the
similarity to be returned as an added "$similarity" key in each
returned document. Can only be used for vector ANN search, i.e.
when either `vector` is supplied or the `sort` parameter has the
shape {"$vector": ...}.

a boolean to request query vector used in this search.
If set to True (and if the invocation is a vector search), calling
the `get_sort_vector` method on the returned cursor will yield
the vector used for the ANN search.

sort

with this dictionary parameter one can control the order
the documents are returned. See the Note about sorting, as well as
the one about upper bounds, for details.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

a timeout, in milliseconds, for each single one
of the underlying HTTP requests used to fetch documents as the
cursor is iterated over.
If not passed, the collection-level setting is used instead.

a Cursor object representing iterations over the matching documents

(see the Cursor object for how to use it. The simplest thing is to

run a for loop: `for document in collection.sort(...):`).

>>> filter = {"seq": {"$exists": True}}
>>> for doc in my_coll.find(filter, projection={"seq": True}, limit=5):
...     print(doc["seq"])
...
37
35
10
36
27
>>> cursor1 = my_coll.find(
...     {},
...     limit=4,
...     sort={"seq": astrapy.constants.SortDocuments.DESCENDING},
... )
>>> [doc["_id"] for doc in cursor1]
['97e85f81-...', '1581efe4-...', '...', '...']
>>> cursor2 = my_coll.find({}, limit=3)
>>> cursor2.distinct("seq")
[37, 35, 10]/n/n/n>>> my_coll.insert_many([
...     {"tag": "A", "$vector": [4, 5]},
...     {"tag": "B", "$vector": [3, 4]},
...     {"tag": "C", "$vector": [3, 2]},
...     {"tag": "D", "$vector": [4, 1]},
...     {"tag": "E", "$vector": [2, 5]},
... ])
>>> ann_tags = [
...     document["tag"]
...     for document in my_coll.find(
...         {},
...         sort={"$vector": [3, 3]},
...         limit=3,
...     )
... ]
>>> ann_tags
['A', 'B', 'C']
>>> # (assuming the collection has metric VectorMetric.COSINE)/n/n/n>>> cursor = my_coll.find(
...     sort={"$vector": [3, 3]},
...     limit=3,
...     include_sort_vector=True,
... )
>>> cursor.get_sort_vector()
[3.0, 3.0]
>>> matches = list(cursor)
>>> cursor.get_sort_vector()
[3.0, 3.0]

When not specifying sorting criteria at all (by vector or otherwise),
the cursor can scroll through an arbitrary number of documents as
the Data API and the client periodically exchange new chunks of documents.
It should be noted that the behavior of the cursor in the case documents
have been added/removed after the `find` was started depends on database
internals and it is not guaranteed, nor excluded, that such "real-time"
changes in the data would be picked up by the cursor.


Run a search, returning the first document in the collection that matches
provided filters, if any is found.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to perform vector search (i.e. ANN,
or "approximate nearest-neighbours" search), extracting the most
similar document in the collection matching the filter.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
Using vectorize assumes a suitable service is configured for the collection.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

a boolean to request the numeric value of the
similarity to be returned as an added "$similarity" key in the
returned document. Can only be used for vector ANN search, i.e.
when either `vector` is supplied or the `sort` parameter has the
shape {"$vector": ...}.

sort

with this dictionary parameter one can control the order
the documents are returned. See the Note about sorting for details.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

a dictionary expressing the required document, otherwise None.

See the `find` method for more details on the accepted parameters
(whereas `skip` and `limit` are not valid parameters for `find_one`).


Return a list of the unique values of `key` across the documents
in the collection that match the provided filter.

distinct

key

the name of the field whose value is inspected across documents.
Keys can use dot-notation to descend to deeper document levels.
Example of acceptable `key` values:
    "field"
    "field.subfield"
    "field.3"
    "field.3.subfield"
If lists are encountered and no numeric index is specified,
all items in the list are visited.

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

a timeout, in milliseconds, with the same meaning as for `find`.
If not passed, the collection-level setting is used instead.

a list of all different values for `key` found across the documents

that match the filter. The result list has no repeated items.

>>> my_coll.insert_many(
...     [
...         {"name": "Marco", "food": ["apple", "orange"], "city": "Helsinki"},
...         {"name": "Emma", "food": {"likes_fruit": True, "allergies": []}},
...     ]
... )
InsertManyResult(raw_results=..., inserted_ids=['c5b99f37-...', 'd6416321-...'])
>>> my_coll.distinct("name")
['Marco', 'Emma']
>>> my_coll.distinct("city")
['Helsinki']
>>> my_coll.distinct("food")
['apple', 'orange', {'likes_fruit': True, 'allergies': []}]
>>> my_coll.distinct("food.1")
['orange']
>>> my_coll.distinct("food.allergies")
[]
>>> my_coll.distinct("food.likes_fruit")
[True]

For details on the behaviour of "distinct" in conjunction with
real-time changes in the collection contents, see the
Note of the `find` command.


Count the documents in the collection matching the specified filter.

a required ceiling on the result of the count operation.
If the actual number of documents exceeds this value,
an exception will be raised.
Furthermore, if the actual number of documents exceeds the maximum
count that the Data API can reach (regardless of upper_bound),
an exception will be raised.

the exact count of matching documents.

>>> my_coll.insert_many([{"seq": i} for i in range(20)])
InsertManyResult(...)
>>> my_coll.count_documents({}, upper_bound=100)
20
>>> my_coll.count_documents({"seq":{"$gt": 15}}, upper_bound=100)
4
>>> my_coll.count_documents({}, upper_bound=10)
Traceback (most recent call last):
    ... ...
astrapy.exceptions.TooManyDocumentsToCountException

Count operations are expensive: for this reason, the best practice
is to provide a reasonable `upper_bound` according to the caller
expectations. Moreover, indiscriminate usage of count operations
for sizeable amounts of documents (i.e. in the thousands and more)
is discouraged in favor of alternative application-specific solutions.
Keep in mind that the Data API has a hard upper limit on the amount
of documents it will count, and that an exception will be thrown
by this method if this limit is encountered.


Query the API server for an estimate of the document count in the collection.

Contrary to `count_documents`, this method has no filtering parameters.

a server-provided estimate count of the documents in the collection.


Find a document on the collection and replace it entirely with a new one,
optionally inserting a new one if no match is found.

replacement

the new document to write into the collection.

projection

it controls which parts of the document are returned.
It can be an allow-list: `{"f1": True, "f2": True}`,
or a deny-list: `{"fx": False, "fy": False}`, but not a mixture
(except for the `_id` and other special fields, which can be
associated to both True or False independently of the rest
of the specification).
The special star-projections `{"*": True}` and `{"*": False}`
have the effect of returning the whole document and `{}` respectively.
For lists in documents, slice directives can be passed to select
portions of the list: for instance, `{"array": {"$slice": 2}}`,
`{"array": {"$slice": -2}}`, `{"array": {"$slice": [4, 2]}}` or
`{"array": {"$slice": [-4, 2]}}`.
An iterable over strings will be treated implicitly as an allow-list.
The default projection (used if this parameter is not passed) does not
necessarily include "special" fields such as `$vector` or `$vectorize`.
See the Data API documentation for more on projections.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to use vector search (i.e. ANN,
or "approximate nearest-neighbours" search), as the sorting criterion.
In this way, the matched document (if any) will be the one
that is most similar to the provided vector.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

sort

with this dictionary parameter one can control the sorting
order of the documents matching the filter, effectively
determining what document will come first and hence be the
replaced one. See the `find` method for more on sorting.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

upsert

this parameter controls the behavior in absence of matches.
If True, `replacement` is inserted as a new document
if no matches are found on the collection. If False,
the operation silently does nothing in case of no matches.

a flag controlling what document is returned:
if set to `ReturnDocument.BEFORE`, or the string "before",
the document found on database is returned; if set to
`ReturnDocument.AFTER`, or the string "after", the new
document is returned. The default is "before".

A document (or a projection thereof, as required), either the one

before the replace operation or the one after that.

Alternatively, the method returns None to represent

that no matching document was found, or that no replacement

was inserted (depending on the `return_document` parameter).

>>> my_coll.insert_one({"_id": "rule1", "text": "all animals are equal"})
InsertOneResult(...)
>>> my_coll.find_one_and_replace(
...     {"_id": "rule1"},
...     {"text": "some animals are more equal!"},
... )
{'_id': 'rule1', 'text': 'all animals are equal'}
>>> my_coll.find_one_and_replace(
...     {"text": "some animals are more equal!"},
...     {"text": "and the pigs are the rulers"},
...     return_document=astrapy.constants.ReturnDocument.AFTER,
... )
{'_id': 'rule1', 'text': 'and the pigs are the rulers'}
>>> my_coll.find_one_and_replace(
...     {"_id": "rule2"},
...     {"text": "F=ma^2"},
...     return_document=astrapy.constants.ReturnDocument.AFTER,
... )
>>> # (returns None for no matches)
>>> my_coll.find_one_and_replace(
...     {"_id": "rule2"},
...     {"text": "F=ma"},
...     upsert=True,
...     return_document=astrapy.constants.ReturnDocument.AFTER,
...     projection={"_id": False},
... )
{'text': 'F=ma'}


Replace a single document on the collection with a new one,
optionally inserting a new one if no match is found.

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

vectorize

a string to be made into a vector to perform vector search.
Using vectorize assumes a suitable service is configured for the collection.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

an UpdateResult object summarizing the outcome of the replace operation.

>>> my_coll.insert_one({"Marco": "Polo"})
InsertOneResult(...)
>>> my_coll.replace_one({"Marco": {"$exists": True}}, {"Buda": "Pest"})
UpdateResult(raw_results=..., update_info={'n': 1, 'updatedExisting': True, 'ok': 1.0, 'nModified': 1})
>>> my_coll.find_one({"Buda": "Pest"})
{'_id': '8424905a-...', 'Buda': 'Pest'}
>>> my_coll.replace_one({"Mirco": {"$exists": True}}, {"Oh": "yeah?"})
UpdateResult(raw_results=..., update_info={'n': 0, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0})
>>> my_coll.replace_one({"Mirco": {"$exists": True}}, {"Oh": "yeah?"}, upsert=True)
UpdateResult(raw_results=..., update_info={'n': 1, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0, 'upserted': '931b47d6-...'})


Find a document on the collection and update it as requested,
optionally inserting a new one if no match is found.

update

the update prescription to apply to the document, expressed
as a dictionary as per Data API syntax. Examples are:
    {"$set": {"field": "value}}
    {"$inc": {"counter": 10}}
    {"$unset": {"field": ""}}
See the Data API documentation for the full syntax.

upsert

this parameter controls the behavior in absence of matches.
If True, a new document (resulting from applying the `update`
to an empty document) is inserted if no matches are found on
the collection. If False, the operation silently does nothing
in case of no matches.

that no matching document was found, or that no update

was applied (depending on the `return_document` parameter).

>>> my_coll.insert_one({"Marco": "Polo"})
InsertOneResult(...)
>>> my_coll.find_one_and_update(
...     {"Marco": {"$exists": True}},
...     {"$set": {"title": "Mr."}},
... )
{'_id': 'a80106f2-...', 'Marco': 'Polo'}
>>> my_coll.find_one_and_update(
...     {"title": "Mr."},
...     {"$inc": {"rank": 3}},
...     projection=["title", "rank"],
...     return_document=astrapy.constants.ReturnDocument.AFTER,
... )
{'_id': 'a80106f2-...', 'title': 'Mr.', 'rank': 3}
>>> my_coll.find_one_and_update(
...     {"name": "Johnny"},
...     {"$set": {"rank": 0}},
...     return_document=astrapy.constants.ReturnDocument.AFTER,
... )
>>> # (returns None for no matches)
>>> my_coll.find_one_and_update(
...     {"name": "Johnny"},
...     {"$set": {"rank": 0}},
...     upsert=True,
...     return_document=astrapy.constants.ReturnDocument.AFTER,
... )
{'_id': 'cb4ef2ab-...', 'name': 'Johnny', 'rank': 0}


Update a single document on the collection as requested,
optionally inserting a new one if no match is found.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to use vector search (i.e. ANN,
or "approximate nearest-neighbours" search), as the sorting criterion.
In this way, the matched document (if any) will be the one
that is most similar to the provided vector.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

sort

with this dictionary parameter one can control the sorting
order of the documents matching the filter, effectively
determining what document will come first and hence be the
replaced one. See the `find` method for more on sorting.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

an UpdateResult object summarizing the outcome of the update operation.

>>> my_coll.insert_one({"Marco": "Polo"})
InsertOneResult(...)
>>> my_coll.update_one({"Marco": {"$exists": True}}, {"$inc": {"rank": 3}})
UpdateResult(raw_results=..., update_info={'n': 1, 'updatedExisting': True, 'ok': 1.0, 'nModified': 1})
>>> my_coll.update_one({"Mirko": {"$exists": True}}, {"$inc": {"rank": 3}})
UpdateResult(raw_results=..., update_info={'n': 0, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0})
>>> my_coll.update_one({"Mirko": {"$exists": True}}, {"$inc": {"rank": 3}}, upsert=True)
UpdateResult(raw_results=..., update_info={'n': 1, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0, 'upserted': '2a45ff60-...'})


Apply an update operations to all documents matching a condition,
optionally inserting one documents in absence of matches.

update

the update prescription to apply to the documents, expressed
as a dictionary as per Data API syntax. Examples are:
    {"$set": {"field": "value}}
    {"$inc": {"counter": 10}}
    {"$unset": {"field": ""}}
See the Data API documentation for the full syntax.

upsert

this parameter controls the behavior in absence of matches.
If True, a single new document (resulting from applying `update`
to an empty document) is inserted if no matches are found on
the collection. If False, the operation silently does nothing
in case of no matches.

a timeout, in milliseconds, for the operation.
If not passed, the collection-level setting is used instead:
if a large number of document updates is anticipated, it is suggested
to specify a larger timeout than in most other operations as the
update will span several HTTP calls to the API in sequence.

>>> my_coll.insert_many([{"c": "red"}, {"c": "green"}, {"c": "blue"}])
InsertManyResult(...)
>>> my_coll.update_many({"c": {"$ne": "green"}}, {"$set": {"nongreen": True}})
UpdateResult(raw_results=..., update_info={'n': 2, 'updatedExisting': True, 'ok': 1.0, 'nModified': 2})
>>> my_coll.update_many({"c": "orange"}, {"$set": {"is_also_fruit": True}})
UpdateResult(raw_results=..., update_info={'n': 0, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0})
>>> my_coll.update_many(
...     {"c": "orange"},
...     {"$set": {"is_also_fruit": True}},
...     upsert=True,
... )
UpdateResult(raw_results=..., update_info={'n': 1, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0, 'upserted': '46643050-...'})

Similarly to the case of `find` (see its docstring for more details),
running this command while, at the same time, another process is
inserting new documents which match the filter of the `update_many`
can result in an unpredictable fraction of these documents being updated.
In other words, it cannot be easily predicted whether a given
newly-inserted document will be picked up by the update_many command or not.


Find a document in the collection and delete it. The deleted document,
however, is the return value of the method.

projection

it controls which parts of the document are returned.
It can be an allow-list: `{"f1": True, "f2": True}`,
or a deny-list: `{"fx": False, "fy": False}`, but not a mixture
(except for the `_id` and other special fields, which can be
associated to both True or False independently of the rest
of the specification).
The special star-projections `{"*": True}` and `{"*": False}`
have the effect of returning the whole document and `{}` respectively.
For lists in documents, slice directives can be passed to select
portions of the list: for instance, `{"array": {"$slice": 2}}`,
`{"array": {"$slice": -2}}`, `{"array": {"$slice": [4, 2]}}` or
`{"array": {"$slice": [-4, 2]}}`.
An iterable over strings will be treated implicitly as an allow-list.
The default projection (used if this parameter is not passed) does not
necessarily include "special" fields such as `$vector` or `$vectorize`.
See the Data API documentation for more on projections.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to use vector search (i.e. ANN,
or "approximate nearest-neighbours" search), as the sorting criterion.
In this way, the matched document (if any) will be the one
that is most similar to the provided vector.
This parameter cannot be used together with `sort`.
See the `find` method for more details on this parameter.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
This can be supplied in (exclusive) alternative to `vector`,
provided such a service is configured for the collection,
and achieves the same effect.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

sort

with this dictionary parameter one can control the sorting
order of the documents matching the filter, effectively
determining what document will come first and hence be the
deleted one. See the `find` method for more on sorting.

Either the document (or a projection thereof, as requested), or None

if no matches were found in the first place.

>>> my_coll.insert_many(
...     [
...         {"species": "swan", "class": "Aves"},
...         {"species": "frog", "class": "Amphibia"},
...     ],
... )
InsertManyResult(...)
>>> my_coll.find_one_and_delete(
...     {"species": {"$ne": "frog"}},
...     projection=["species"],
... )
{'_id': '5997fb48-...', 'species': 'swan'}
>>> my_coll.find_one_and_delete({"species": {"$ne": "frog"}})
>>> # (returns None for no matches)


Delete one document matching a provided filter.
This method never deletes more than a single document, regardless
of the number of matches to the provided filters.

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

a DeleteResult object summarizing the outcome of the delete operation.

>>> my_coll.insert_many([{"seq": 1}, {"seq": 0}, {"seq": 2}])
InsertManyResult(...)
>>> my_coll.delete_one({"seq": 1})
DeleteResult(raw_results=..., deleted_count=1)
>>> my_coll.distinct("seq")
[0, 2]
>>> my_coll.delete_one(
...     {"seq": {"$exists": True}},
...     sort={"seq": astrapy.constants.SortDocuments.DESCENDING},
... )
DeleteResult(raw_results=..., deleted_count=1)
>>> my_coll.distinct("seq")
[0]
>>> my_coll.delete_one({"seq": 2})
DeleteResult(raw_results=..., deleted_count=0)


Delete all documents matching a provided filter.

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.
Passing an empty filter, `{}`, completely erases all contents
of the collection.

a timeout, in milliseconds, for the operation.
If not passed, the collection-level setting is used instead:
keep in mind that this method entails successive HTTP requests
to the API, depending on how many documents are to be deleted.
For this reason, in most cases it is suggested to relax the
timeout compared to other method calls.

>>> my_coll.insert_many([{"seq": 1}, {"seq": 0}, {"seq": 2}])
InsertManyResult(...)
>>> my_coll.delete_many({"seq": {"$lte": 1}})
DeleteResult(raw_results=..., deleted_count=2)
>>> my_coll.distinct("seq")
[2]
>>> my_coll.delete_many({"seq": {"$lte": 1}})
DeleteResult(raw_results=..., deleted_count=0)

This operation is in general not atomic. Depending on the amount
of matching documents, it can keep running (in a blocking way)
for a macroscopic time. In that case, new documents that are
meanwhile inserted (e.g. from another process/application) will be
deleted during the execution of this method call until the
collection is devoid of matches.
An exception is the `filter={}` case, whereby the operation is atomic.


Delete all documents in a collection.

a dictionary of the form {"ok": 1} to signal successful deletion.

>>> my_coll.distinct("seq")
[2, 1, 0]
>>> my_coll.count_documents({}, upper_bound=100)
4
>>> my_coll.delete_all()
{'ok': 1}
>>> my_coll.count_documents({}, upper_bound=100)
0


Execute an arbitrary amount of operations such as inserts, updates, deletes
either sequentially or concurrently.

This method does not execute atomically, i.e. individual operations are
each performed in the same way as the corresponding collection method,
and each one is a different and unrelated database mutation.

requests

an iterable over concrete subclasses of `BaseOperation`,
such as `InsertMany` or `ReplaceOne`. Each such object
represents an operation ready to be executed on a collection,
and is instantiated by passing the same parameters as one
would the corresponding collection method.

ordered

whether to launch the `requests` one after the other or
in arbitrary order, possibly in a concurrent fashion. For
performance reasons, False (default) should be preferred
when compatible with the needs of the application flow.

concurrency

maximum number of concurrent operations executing at
a given time. It cannot be more than one for ordered bulk writes.

a timeout, in milliseconds, for the whole bulk write.
Remember that, if the method call times out, then there's no
guarantee about what portion of the bulk write has been received
and successfully executed by the Data API.
If not passed, the collection-level setting is used instead:
in most cases, however, one should pass a relaxed timeout
if longer sequences of operations are to be executed in bulk.

A single BulkWriteResult summarizing the whole list of requested

operations. The keys in the map attributes of BulkWriteResult

(when present) are the integer indices of the corresponding operation

in the `requests` iterable.

>>> from astrapy.operations import InsertMany, ReplaceOne
>>> op1 = InsertMany([{"a": 1}, {"a": 2}])
>>> op2 = ReplaceOne({"z": 9}, replacement={"z": 9, "replaced": True}, upsert=True)
>>> my_coll.bulk_write([op1, op2])
BulkWriteResult(bulk_api_results={0: ..., 1: ...}, deleted_count=0, inserted_count=3, matched_count=0, modified_count=0, upserted_count=1, upserted_ids={1: '2addd676-...'})
>>> my_coll.count_documents({}, upper_bound=100)
3
>>> my_coll.distinct("replaced")
[True]


Drop the collection, i.e. delete it from the database along with
all the documents it contains.

drop

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.
Remember there is not guarantee that a request that has
timed out us not in fact honored.

>>> my_coll.find_one({})
{'_id': '...', 'a': 100}
>>> my_coll.drop()
{'ok': 1}
>>> my_coll.find_one({})
Traceback (most recent call last):
    ... ...
astrapy.exceptions.DataAPIResponseException: Collection does not exist, collection name: my_collection

Once the method succeeds, methods on this object can still be invoked:
however, this hardly makes sense as the underlying actual collection
is no more.
It is responsibility of the developer to design a correct flow
which avoids using a deceased collection any further.


Send a POST request to the Data API for this collection with
an arbitrary, caller-provided payload.

command

body

a JSON-serializable dictionary, the payload of the request.

if True, responses with a nonempty 'errors' field
result in an astrapy exception being raised.

a dictionary with the response of the HTTP request.

>>> my_coll.command({"countDocuments": {}})
{'status': {'count': 123}}


A Data API collection, the main object to interact with the Data API,
especially for DDL operations.
This class has an asynchronous interface for use with asyncio.

An AsyncCollection is spawned from a Database object, from which it inherits
the details on how to reach the API server (endpoint, authentication token).

AsyncCollection

database

a Database object, instantiated earlier. This represents
the database the collection belongs to.

name

the collection name. This parameter should match an existing
collection on the database.

keyspace

this is the keyspace to which the collection belongs.
If not specified, the database's working keyspace is used.

namespace

an alias for `keyspace`. *DEPRECATED*, removal in 2.0.

An instance of `astrapy.api_options.CollectionAPIOptions`
providing the general settings for interacting with the Data API.

callers

a list of caller identities, i.e. applications, or frameworks,
on behalf of which the Data API calls are performed. These end up
in the request user-agent.
Each caller identity is a ("caller_name", "caller_version") pair.

*DEPRECATED*, use `callers`. Removal 2.0. Name of the
application, or framework, on behalf of which the Data API calls
are performed. This ends up in the request user-agent.

version of the caller. *DEPRECATED*, use `callers`. Removal 2.0.

>>> from astrapy import DataAPIClient, AsyncCollection
>>> my_client = astrapy.DataAPIClient("AstraCS:...")
>>> my_async_db = my_client.get_async_database(
...    "https://01234567-....apps.astra.datastax.com"
... )
>>> my_async_coll_1 = AsyncCollection(database=my_async_db, name="my_collection")
>>> my_async coll_2 = asyncio.run(my_async_db.create_collection(
...     "my_v_collection",
...     dimension=3,
...     metric="cosine",
... ))
>>> my_async_coll_3a = asyncio.run(my_async_db.get_collection(
...     "my_already_existing_collection",
... ))
>>> my_async_coll_3b = my_async_db.my_already_existing_collection
>>> my_async_coll_3c = my_async_db["my_already_existing_collection"]

creating an instance of AsyncCollection does not trigger actual creation
of the collection on the database. The latter should have been created
beforehand, e.g. through the `create_collection` method of an AsyncDatabase.


Create a clone of this collection with some changed attributes.

name

the name of the collection. This parameter is useful to
quickly spawn AsyncCollection instances each pointing to a different
collection existing in the same keyspace.

optional API key(s) for interacting with the collection.
If an embedding service is configured, and this parameter is not None,
each Data API call will include the necessary embedding-related headers
as specified by this parameter. If a string is passed, it translates
into the one "embedding api key" header
(i.e. `astrapy.authentication.EmbeddingAPIKeyHeaderProvider`).
For some vectorize providers/models, if using header-based authentication,
specialized subclasses of `astrapy.authentication.EmbeddingHeadersProvider`
should be supplied.

a default timeout, in millisecond, for the duration of each
operation on the collection. Individual timeouts can be provided to
each collection method call and will take precedence, with this value
being an overall default.
Note that for some methods involving multiple API calls (such as
`find`, `delete_many`, `insert_many` and so on), it is strongly suggested
to provide a specific timeout as the default one likely wouldn't make
much sense.

version of the caller. *DEPRECATED*, use `callers`.
Removal 2.0.

a new AsyncCollection instance.

>>> my_other_async_coll = my_async_coll.with_options(
...     name="the_other_coll",
...     callers=[("caller_identity", "0.1.2")],
... )


Create a Collection from this one. Save for the arguments
explicitly provided as overrides, everything else is kept identical
to this collection in the copy (the database is converted into
a sync object).

database

a Database object, instantiated earlier.
This represents the database the new collection belongs to.

the new copy, a Collection instance.

>>> my_async_coll.to_sync().count_documents({}, upper_bound=100)
77


Set a new identity for the application/framework on behalf of which
the Data API calls are performed (the "caller").

name of the application, or framework, on behalf of which
the Data API calls are performed. This ends up in the request user-agent.

version of the caller.


Get the collection options, i.e. its configuration as read from the database.

The method issues a request to the Data API each time is invoked,
without caching mechanisms: this ensures up-to-date information
for usages such as real-time collection validation by the application.

options

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

a CollectionOptions instance describing the collection.

(See also the database `list_collections` method.)

>>> asyncio.run(my_async_coll.options())
CollectionOptions(vector=CollectionVectorOptions(dimension=3, metric='cosine'))


Information on the collection (name, location, database), in the
form of a CollectionInfo object.

Not to be confused with the collection `options` method (related
to the collection internal configuration).

info

>>> my_async_coll.info().database_info.region
'us-east1'
>>> my_async_coll.info().full_name
'default_keyspace.my_v_collection'


a Database object, the database this collection belongs to.


The namespace this collection is in.

*DEPRECATED* (removal in 2.0). Switch to the "keyspace" property.**


The keyspace this collection is in.


The name of this collection.


The fully-qualified collection name within the database,
in the form "keyspace.collection_name".


Insert a single document in the collection in an atomic operation.

document

the dictionary expressing the document to insert.
The `_id` field of the document can be left out, in which
case it will be created automatically.

vector

a vector (a list of numbers appropriate for the collection)
for the document. Passing this parameter is equivalent to
providing a `$vector` field within the document itself,
however the two are mutually exclusive.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the document instead.

vectorize

a string to be made into a vector, if such a service
is configured for the collection. Passing this parameter is
equivalent to providing a `$vectorize` field in the document itself,
however the two are mutually exclusive.
Moreover, this parameter cannot coexist with `vector`.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the document instead.

an InsertOneResult object.

>>> async def write_and_count(acol: AsyncCollection) -> None:
...     count0 = await acol.count_documents({}, upper_bound=10)
...     print("count0", count0)
...     await acol.insert_one(
...         {
...             "age": 30,
...             "name": "Smith",
...             "food": ["pear", "peach"],
...             "likes_fruit": True,
...         },
...     )
...     await acol.insert_one({"_id": "user-123", "age": 50, "name": "Maccio"})
...     count1 = await acol.count_documents({}, upper_bound=10)
...     print("count1", count1)
...
>>> asyncio.run(write_and_count(my_async_coll))
count0 0
count1 2

>>> asyncio.run(my_async_coll.insert_one({"tag": v", "$vector": [10, 11]}))
InsertOneResult(...)


Returns:
    an InsertManyResult object.

documents

an iterable of dictionaries, each a document to insert.
Documents may specify their `_id` field or leave it out, in which
case it will be added automatically.

vectors

an optional list of vectors (as many vectors as the provided
documents) to associate to the documents when inserting.
Passing vectors this way is indeed equivalent to the "$vector" field
of the documents, however the two are mutually exclusive.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the documents instead.

vectorize

an optional list of strings to be made into as many vectors
(one per document), if such a service is configured for the collection.
Passing this parameter is equivalent to providing a `$vectorize`
field in the documents themselves, however the two are mutually exclusive.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the documents instead.

ordered

if False (default), the insertions can occur in arbitrary order
and possibly concurrently. If True, they are processed sequentially.
If there are no specific reasons against it, unordered insertions are to
be preferred as they complete much faster.

how many documents to include in a single API request.
Exceeding the server maximum allowed value results in an error.
Leave it unspecified (recommended) to use the system default.

concurrency

maximum number of concurrent requests to the API at
a given time. It cannot be more than one for ordered insertions.

a timeout, in milliseconds, for the operation.
If not passed, the collection-level setting is used instead:
If many documents are being inserted, this method corresponds
to several HTTP requests: in such cases one may want to specify
a more tolerant timeout here.

>>> async def write_and_count(acol: AsyncCollection) -> None:
...             count0 = await acol.count_documents({}, upper_bound=10)
...             print("count0", count0)
...             im_result1 = await acol.insert_many(
...                 [
...                     {"a": 10},
...                     {"a": 5},
...                     {"b": [True, False, False]},
...                 ],
...                 ordered=True,
...             )
...             print("inserted1", im_result1.inserted_ids)
...             count1 = await acol.count_documents({}, upper_bound=100)
...             print("count1", count1)
...             await acol.insert_many(
...                 [{"seq": i} for i in range(50)],
...                 concurrency=5,
...             )
...             count2 = await acol.count_documents({}, upper_bound=100)
...             print("count2", count2)
...
>>> asyncio.run(write_and_count(my_async_coll))
count0 0
inserted1 ['e3c2a684-...', '1de4949f-...', '167dacc3-...']
count1 3
count2 53
>>> asyncio.run(my_async_coll.insert_many(
...     [
...         {"tag": "a", "$vector": [1, 2]},
...         {"tag": "b", "$vector": [3, 4]},
...     ]
... ))
InsertManyResult(...)


Find documents on the collection, matching a certain provided filter.

The method returns a Cursor that can then be iterated over. Depending
on the method call pattern, the iteration over all documents can reflect
collection mutations occurred since the `find` method was called, or not.
In cases where the cursor reflects mutations in real-time, it will iterate
over cursors in an approximate way (i.e. exhibiting occasional skipped
or duplicate documents). This happens when making use of the `sort`
option in a non-vector-search manner.

find

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

projection

it controls which parts of the document are returned.
It can be an allow-list: `{"f1": True, "f2": True}`,
or a deny-list: `{"fx": False, "fy": False}`, but not a mixture
(except for the `_id` and other special fields, which can be
associated to both True or False independently of the rest
of the specification).
The special star-projections `{"*": True}` and `{"*": False}`
have the effect of returning the whole document and `{}` respectively.
For lists in documents, slice directives can be passed to select
portions of the list: for instance, `{"array": {"$slice": 2}}`,
`{"array": {"$slice": -2}}`, `{"array": {"$slice": [4, 2]}}` or
`{"array": {"$slice": [-4, 2]}}`.
An iterable over strings will be treated implicitly as an allow-list.
The default projection (used if this parameter is not passed) does not
necessarily include "special" fields such as `$vector` or `$vectorize`.
See the Data API documentation for more on projections.

skip

with this integer parameter, what would be the first `skip`
documents returned by the query are discarded, and the results
start from the (skip+1)-th document.
This parameter can be used only in conjunction with an explicit
`sort` criterion of the ascending/descending type (i.e. it cannot
be used when not sorting, nor with vector-based ANN search).

limit

this (integer) parameter sets a limit over how many documents
are returned. Once `limit` is reached (or the cursor is exhausted
for lack of matching documents), nothing more is returned.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to perform vector search (i.e. ANN,
or "approximate nearest-neighbours" search).
When running similarity search on a collection, no other sorting
criteria can be specified. Moreover, there is an upper bound
to the number of documents that can be returned. For details,
see the Note about upper bounds and the Data API documentation.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
This can be supplied in (exclusive) alternative to `vector`,
provided such a service is configured for the collection,
and achieves the same effect.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

a boolean to request the numeric value of the
similarity to be returned as an added "$similarity" key in each
returned document. Can only be used for vector ANN search, i.e.
when either `vector` is supplied or the `sort` parameter has the
shape {"$vector": ...}.

a boolean to request query vector used in this search.
If set to True (and if the invocation is a vector search), calling
the `get_sort_vector` method on the returned cursor will yield
the vector used for the ANN search.

sort

with this dictionary parameter one can control the order
the documents are returned. See the Note about sorting, as well as
the one about upper bounds, for details.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

a timeout, in milliseconds, for each single one
of the underlying HTTP requests used to fetch documents as the
cursor is iterated over.
If not passed, the collection-level setting is used instead.

an AsyncCursor object representing iterations over the matching documents

(see the AsyncCursor object for how to use it. The simplest thing is to

run a for loop: `for document in collection.sort(...):`).

>>> async def run_finds(acol: AsyncCollection) -> None:
...             filter = {"seq": {"$exists": True}}
...             print("find results 1:")
...             async for doc in acol.find(filter, projection={"seq": True}, limit=5):
...                 print(doc["seq"])
...             async_cursor1 = acol.find(
...                 {},
...                 limit=4,
...                 sort={"seq": astrapy.constants.SortDocuments.DESCENDING},
...             )
...             ids = [doc["_id"] async for doc in async_cursor1]
...             print("find results 2:", ids)
...             async_cursor2 = acol.find({}, limit=3)
...             seqs = await async_cursor2.distinct("seq")
...             print("distinct results 3:", seqs)
...
>>> asyncio.run(run_finds(my_async_coll))
find results 1:
48
35
7
11
13
find results 2: ['d656cd9d-...', '479c7ce8-...', '96dc87fd-...', '83f0a21f-...']
distinct results 3: [48, 35, 7]/n/n/n>>> async def run_vector_finds(acol: AsyncCollection) -> None:
...     await acol.insert_many([
...         {"tag": "A", "$vector": [4, 5]},
...         {"tag": "B", "$vector": [3, 4]},
...         {"tag": "C", "$vector": [3, 2]},
...         {"tag": "D", "$vector": [4, 1]},
...         {"tag": "E", "$vector": [2, 5]},
...     ])
...     ann_tags = [
...         document["tag"]
...         async for document in acol.find(
...             {},
...             sort={"$vector": [3, 3]},
...             limit=3,
...         )
...     ]
...     return ann_tags
...
>>> asyncio.run(run_vector_finds(my_async_coll))
['A', 'B', 'C']
>>> # (assuming the collection has metric VectorMetric.COSINE)/n/n/n>>> async_cursor = my_async_coll.find(
...     sort={"$vector": [3, 3]},
...     limit=3,
...     include_sort_vector=True,
... )
>>> asyncio.run(async_cursor.get_sort_vector())
[3.0, 3.0]
>>> asyncio.run(async_cursor.__anext__())
{'_id': 'b13ce177-738e-47ec-bce1-77738ee7ec93', 'tag': 'A'}
>>> asyncio.run(async_cursor.get_sort_vector())
[3.0, 3.0]


Run a search, returning the first document in the collection that matches
provided filters, if any is found.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to perform vector search (i.e. ANN,
or "approximate nearest-neighbours" search), extracting the most
similar document in the collection matching the filter.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
Using vectorize assumes a suitable service is configured for the collection.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

a boolean to request the numeric value of the
similarity to be returned as an added "$similarity" key in the
returned document. Can only be used for vector ANN search, i.e.
when either `vector` is supplied or the `sort` parameter has the
shape {"$vector": ...}.

sort

with this dictionary parameter one can control the order
the documents are returned. See the Note about sorting for details.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

a dictionary expressing the required document, otherwise None.

>>> async def demo_find_one(acol: AsyncCollection) -> None:
....    print("Count:", await acol.count_documents({}, upper_bound=100))
...     result0 = await acol.find_one({})
...     print("result0", result0)
...     result1 = await acol.find_one({"seq": 10})
...     print("result1", result1)
...     result2 = await acol.find_one({"seq": 1011})
...     print("result2", result2)
...     result3 = await acol.find_one({}, projection={"seq": False})
...     print("result3", result3)
...     result4 = await acol.find_one(
...         {},
...         sort={"seq": astrapy.constants.SortDocuments.DESCENDING},
...     )
...     print("result4", result4)
...
>>>
>>> asyncio.run(demo_find_one(my_async_coll))
Count: 50
result0 {'_id': '479c7ce8-...', 'seq': 48}
result1 {'_id': '93e992c4-...', 'seq': 10}
result2 None
result3 {'_id': '479c7ce8-...'}
result4 {'_id': 'd656cd9d-...', 'seq': 49}

>>> asyncio.run(my_async_coll.find_one(
...     {},
...     sort={"$vector": [1, 0]},
...     projection={"*": True},
... ))
{'_id': '...', 'tag': 'D', '$vector': [4.0, 1.0]}


Return a list of the unique values of `key` across the documents
in the collection that match the provided filter.

distinct

key

the name of the field whose value is inspected across documents.
Keys can use dot-notation to descend to deeper document levels.
Example of acceptable `key` values:
    "field"
    "field.subfield"
    "field.3"
    "field.3.subfield"
If lists are encountered and no numeric index is specified,
all items in the list are visited.

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

a timeout, in milliseconds, with the same meaning as for `find`.
If not passed, the collection-level setting is used instead.

a list of all different values for `key` found across the documents

that match the filter. The result list has no repeated items.

>>> async def run_distinct(acol: AsyncCollection) -> None:
...     await acol.insert_many(
...         [
...             {"name": "Marco", "food": ["apple", "orange"], "city": "Helsinki"},
...             {"name": "Emma", "food": {"likes_fruit": True, "allergies": []}},
...         ]
...     )
...     distinct0 = await acol.distinct("name")
...     print("distinct('name')", distinct0)
...     distinct1 = await acol.distinct("city")
...     print("distinct('city')", distinct1)
...     distinct2 = await acol.distinct("food")
...     print("distinct('food')", distinct2)
...     distinct3 = await acol.distinct("food.1")
...     print("distinct('food.1')", distinct3)
...     distinct4 = await acol.distinct("food.allergies")
...     print("distinct('food.allergies')", distinct4)
...     distinct5 = await acol.distinct("food.likes_fruit")
...     print("distinct('food.likes_fruit')", distinct5)
...
>>> asyncio.run(run_distinct(my_async_coll))
distinct('name') ['Emma', 'Marco']
distinct('city') ['Helsinki']
distinct('food') [{'likes_fruit': True, 'allergies': []}, 'apple', 'orange']
distinct('food.1') ['orange']
distinct('food.allergies') []
distinct('food.likes_fruit') [True]


Count the documents in the collection matching the specified filter.

a required ceiling on the result of the count operation.
If the actual number of documents exceeds this value,
an exception will be raised.
Furthermore, if the actual number of documents exceeds the maximum
count that the Data API can reach (regardless of upper_bound),
an exception will be raised.

the exact count of matching documents.

>>> async def do_count_docs(acol: AsyncCollection) -> None:
...     await acol.insert_many([{"seq": i} for i in range(20)])
...     count0 = await acol.count_documents({}, upper_bound=100)
...     print("count0", count0)
...     count1 = await acol.count_documents({"seq":{"$gt": 15}}, upper_bound=100)
...     print("count1", count1)
...     count2 = await acol.count_documents({}, upper_bound=10)
...     print("count2", count2)
...
>>> asyncio.run(do_count_docs(my_async_coll))
count0 20
count1 4
Traceback (most recent call last):
    ... ...
astrapy.exceptions.TooManyDocumentsToCountException


Query the API server for an estimate of the document count in the collection.

Contrary to `count_documents`, this method has no filtering parameters.

a server-provided estimate count of the documents in the collection.


Find a document on the collection and replace it entirely with a new one,
optionally inserting a new one if no match is found.

Args:

    filter: a predicate expressed as a dictionary according to the
        Data API filter syntax. Examples are:
            {}
            {"name": "John"}
            {"price": {"$lt": 100}}
            {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
        See the Data API documentation for the full set of operators.
    replacement: the new document to write into the collection.
    projection: it controls which parts of the document are returned.
        It can be an allow-list: `{"f1": True, "f2": True}`,
        or a deny-list: `{"fx": False, "fy": False}`, but not a mixture
        (except for the `_id` and other special fields, which can be
        associated to both True or False independently of the rest
        of the specification).
        The special star-projections `{"*": True}` and `{"*": False}`
        have the effect of returning the whole document and `{}` respectively.
        For lists in documents, slice directives can be passed to select
        portions of the list: for instance, `{"array": {"$slice": 2}}`,
        `{"array": {"$slice": -2}}`, `{"array": {"$slice": [4, 2]}}` or
        `{"array": {"$slice": [-4, 2]}}`.
        An iterable over strings will be treated implicitly as an allow-list.
        The default projection (used if this parameter is not passed) does not
        necessarily include "special" fields such as `$vector` or `$vectorize`.
        See the Data API documentation for more on projections.
    vector: a suitable vector, i.e. a list of float numbers of the appropriate
        dimensionality, to use vector search (i.e. ANN,
        or "approximate nearest-neighbours" search), as the sorting criterion.
        In this way, the matched document (if any) will be the one
        that is most similar to the provided vector.
        *DEPRECATED* (removal in 2.0). Use a `$vector` key in the
        sort clause dict instead.
    vectorize: a string to be made into a vector to perform vector search.
        Using vectorize assumes a suitable service is configured for the collection.
        *DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
        sort clause dict instead.
    sort: with this dictionary parameter one can control the sorting
        order of the documents matching the filter, effectively
        determining what document will come first and hence be the
        replaced one. See the `find` method for more on sorting.
        Vector-based ANN sorting is achieved by providing a "$vector"
        or a "$vectorize" key in `sort`.
    upsert: this parameter controls the behavior in absence of matches.
        If True, `replacement` is inserted as a new document
        if no matches are found on the collection. If False,
        the operation silently does nothing in case of no matches.
    return_document: a flag controlling what document is returned:
        if set to `ReturnDocument.BEFORE`, or the string "before",
        the document found on database is returned; if set to
        `ReturnDocument.AFTER`, or the string "after", the new
        document is returned. The default is "before".
    max_time_ms: a timeout, in milliseconds, for the underlying HTTP request.
        If not passed, the collection-level setting is used instead.

A document, either the one before the replace operation or the

one after that. Alternatively, the method returns None to represent

that no matching document was found, or that no replacement

was inserted (depending on the `return_document` parameter).

>>> async def do_find_one_and_replace(acol: AsyncCollection) -> None:
...             await acol.insert_one({"_id": "rule1", "text": "all animals are equal"})
...             result0 = await acol.find_one_and_replace(
...                 {"_id": "rule1"},
...                 {"text": "some animals are more equal!"},
...             )
...             print("result0", result0)
...             result1 = await acol.find_one_and_replace(
...                 {"text": "some animals are more equal!"},
...                 {"text": "and the pigs are the rulers"},
...                 return_document=astrapy.constants.ReturnDocument.AFTER,
...             )
...             print("result1", result1)
...             result2 = await acol.find_one_and_replace(
...                 {"_id": "rule2"},
...                 {"text": "F=ma^2"},
...                 return_document=astrapy.constants.ReturnDocument.AFTER,
...             )
...             print("result2", result2)
...             result3 = await acol.find_one_and_replace(
...                 {"_id": "rule2"},
...                 {"text": "F=ma"},
...                 upsert=True,
...                 return_document=astrapy.constants.ReturnDocument.AFTER,
...                 projection={"_id": False},
...             )
...             print("result3", result3)
...
>>> asyncio.run(do_find_one_and_replace(my_async_coll))
result0 {'_id': 'rule1', 'text': 'all animals are equal'}
result1 {'_id': 'rule1', 'text': 'and the pigs are the rulers'}
result2 None
result3 {'text': 'F=ma'}


Replace a single document on the collection with a new one,
optionally inserting a new one if no match is found.

replacement

the new document to write into the collection.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to use vector search (i.e. ANN,
or "approximate nearest-neighbours" search), as the sorting criterion.
In this way, the matched document (if any) will be the one
that is most similar to the provided vector.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
Using vectorize assumes a suitable service is configured for the collection.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

sort

with this dictionary parameter one can control the sorting
order of the documents matching the filter, effectively
determining what document will come first and hence be the
replaced one. See the `find` method for more on sorting.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

upsert

this parameter controls the behavior in absence of matches.
If True, `replacement` is inserted as a new document
if no matches are found on the collection. If False,
the operation silently does nothing in case of no matches.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

an UpdateResult object summarizing the outcome of the replace operation.

>>> async def do_replace_one(acol: AsyncCollection) -> None:
...     await acol.insert_one({"Marco": "Polo"})
...     result0 = await acol.replace_one(
...         {"Marco": {"$exists": True}},
...         {"Buda": "Pest"},
...     )
...     print("result0.update_info", result0.update_info)
...     doc1 = await acol.find_one({"Buda": "Pest"})
...     print("doc1", doc1)
...     result1 = await acol.replace_one(
...         {"Mirco": {"$exists": True}},
...         {"Oh": "yeah?"},
...     )
...     print("result1.update_info", result1.update_info)
...     result2 = await acol.replace_one(
...         {"Mirco": {"$exists": True}},
...         {"Oh": "yeah?"},
...         upsert=True,
...     )
...     print("result2.update_info", result2.update_info)
...
>>> asyncio.run(do_replace_one(my_async_coll))
result0.update_info {'n': 1, 'updatedExisting': True, 'ok': 1.0, 'nModified': 1}
doc1 {'_id': '6e669a5a-...', 'Buda': 'Pest'}
result1.update_info {'n': 0, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0}
result2.update_info {'n': 1, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0, 'upserted': '30e34e00-...'}


Find a document on the collection and update it as requested,
optionally inserting a new one if no match is found.

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

update

the update prescription to apply to the document, expressed
as a dictionary as per Data API syntax. Examples are:
    {"$set": {"field": "value}}
    {"$inc": {"counter": 10}}
    {"$unset": {"field": ""}}
See the Data API documentation for the full syntax.

projection

it controls which parts of the document are returned.
It can be an allow-list: `{"f1": True, "f2": True}`,
or a deny-list: `{"fx": False, "fy": False}`, but not a mixture
(except for the `_id` and other special fields, which can be
associated to both True or False independently of the rest
of the specification).
The special star-projections `{"*": True}` and `{"*": False}`
have the effect of returning the whole document and `{}` respectively.
For lists in documents, slice directives can be passed to select
portions of the list: for instance, `{"array": {"$slice": 2}}`,
`{"array": {"$slice": -2}}`, `{"array": {"$slice": [4, 2]}}` or
`{"array": {"$slice": [-4, 2]}}`.
An iterable over strings will be treated implicitly as an allow-list.
The default projection (used if this parameter is not passed) does not
necessarily include "special" fields such as `$vector` or `$vectorize`.
See the Data API documentation for more on projections.

upsert

this parameter controls the behavior in absence of matches.
If True, a new document (resulting from applying the `update`
to an empty document) is inserted if no matches are found on
the collection. If False, the operation silently does nothing
in case of no matches.

a flag controlling what document is returned:
if set to `ReturnDocument.BEFORE`, or the string "before",
the document found on database is returned; if set to
`ReturnDocument.AFTER`, or the string "after", the new
document is returned. The default is "before".

A document (or a projection thereof, as required), either the one

before the replace operation or the one after that.

Alternatively, the method returns None to represent

that no matching document was found, or that no update

was applied (depending on the `return_document` parameter).

>>> async def do_find_one_and_update(acol: AsyncCollection) -> None:
...     await acol.insert_one({"Marco": "Polo"})
...     result0 = await acol.find_one_and_update(
...         {"Marco": {"$exists": True}},
...         {"$set": {"title": "Mr."}},
...     )
...     print("result0", result0)
...     result1 = await acol.find_one_and_update(
...         {"title": "Mr."},
...         {"$inc": {"rank": 3}},
...         projection=["title", "rank"],
...         return_document=astrapy.constants.ReturnDocument.AFTER,
...     )
...     print("result1", result1)
...     result2 = await acol.find_one_and_update(
...         {"name": "Johnny"},
...         {"$set": {"rank": 0}},
...         return_document=astrapy.constants.ReturnDocument.AFTER,
...     )
...     print("result2", result2)
...     result3 = await acol.find_one_and_update(
...         {"name": "Johnny"},
...         {"$set": {"rank": 0}},
...         upsert=True,
...         return_document=astrapy.constants.ReturnDocument.AFTER,
...     )
...     print("result3", result3)
...
>>> asyncio.run(do_find_one_and_update(my_async_coll))
result0 {'_id': 'f7c936d3-b0a0-45eb-a676-e2829662a57c', 'Marco': 'Polo'}
result1 {'_id': 'f7c936d3-b0a0-45eb-a676-e2829662a57c', 'title': 'Mr.', 'rank': 3}
result2 None
result3 {'_id': 'db3d678d-14d4-4caa-82d2-d5fb77dab7ec', 'name': 'Johnny', 'rank': 0}


Update a single document on the collection as requested,
optionally inserting a new one if no match is found.

an UpdateResult object summarizing the outcome of the update operation.

>>> async def do_update_one(acol: AsyncCollection) -> None:
...     await acol.insert_one({"Marco": "Polo"})
...     result0 = await acol.update_one(
...         {"Marco": {"$exists": True}},
...         {"$inc": {"rank": 3}},
...     )
...     print("result0.update_info", result0.update_info)
...     result1 = await acol.update_one(
...         {"Mirko": {"$exists": True}},
...         {"$inc": {"rank": 3}},
...     )
...     print("result1.update_info", result1.update_info)
...     result2 = await acol.update_one(
...         {"Mirko": {"$exists": True}},
...         {"$inc": {"rank": 3}},
...         upsert=True,
...     )
...     print("result2.update_info", result2.update_info)
...
>>> asyncio.run(do_update_one(my_async_coll))
result0.update_info {'n': 1, 'updatedExisting': True, 'ok': 1.0, 'nModified': 1})
result1.update_info {'n': 0, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0})
result2.update_info {'n': 1, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0, 'upserted': '75748092-...'}


Apply an update operations to all documents matching a condition,
optionally inserting one documents in absence of matches.

update

the update prescription to apply to the documents, expressed
as a dictionary as per Data API syntax. Examples are:
    {"$set": {"field": "value}}
    {"$inc": {"counter": 10}}
    {"$unset": {"field": ""}}
See the Data API documentation for the full syntax.

upsert

this parameter controls the behavior in absence of matches.
If True, a single new document (resulting from applying `update`
to an empty document) is inserted if no matches are found on
the collection. If False, the operation silently does nothing
in case of no matches.

a timeout, in milliseconds, for the operation.
If not passed, the collection-level setting is used instead:
if a large number of document updates is anticipated, it is suggested
to specify a larger timeout than in most other operations as the
update will span several HTTP calls to the API in sequence.

>>> async def do_update_many(acol: AsyncCollection) -> None:
...     await acol.insert_many([{"c": "red"}, {"c": "green"}, {"c": "blue"}])
...     result0 = await acol.update_many(
...         {"c": {"$ne": "green"}},
...         {"$set": {"nongreen": True}},
...     )
...     print("result0.update_info", result0.update_info)
...     result1 = await acol.update_many(
...         {"c": "orange"},
...         {"$set": {"is_also_fruit": True}},
...     )
...     print("result1.update_info", result1.update_info)
...     result2 = await acol.update_many(
...         {"c": "orange"},
...         {"$set": {"is_also_fruit": True}},
...         upsert=True,
...     )
...     print("result2.update_info", result2.update_info)
...
>>> asyncio.run(do_update_many(my_async_coll))
result0.update_info {'n': 2, 'updatedExisting': True, 'ok': 1.0, 'nModified': 2}
result1.update_info {'n': 0, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0}
result2.update_info {'n': 1, 'updatedExisting': False, 'ok': 1.0, 'nModified': 0, 'upserted': '79ffd5a3-ab99-4dff-a2a5-4aaa0e59e854'}


Find a document in the collection and delete it. The deleted document,
however, is the return value of the method.

vector

a suitable vector, i.e. a list of float numbers of the appropriate
dimensionality, to use vector search (i.e. ANN,
or "approximate nearest-neighbours" search), as the sorting criterion.
In this way, the matched document (if any) will be the one
that is most similar to the provided vector.
*DEPRECATED* (removal in 2.0). Use a `$vector` key in the
sort clause dict instead.

vectorize

a string to be made into a vector to perform vector search.
Using vectorize assumes a suitable service is configured for the collection.
*DEPRECATED* (removal in 2.0). Use a `$vectorize` key in the
sort clause dict instead.

sort

with this dictionary parameter one can control the sorting
order of the documents matching the filter, effectively
determining what document will come first and hence be the
replaced one. See the `find` method for more on sorting.
Vector-based ANN sorting is achieved by providing a "$vector"
or a "$vectorize" key in `sort`.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

Either the document (or a projection thereof, as requested), or None

if no matches were found in the first place.

>>> async def do_find_one_and_delete(acol: AsyncCollection) -> None:
...     await acol.insert_many(
...         [
...             {"species": "swan", "class": "Aves"},
...             {"species": "frog", "class": "Amphibia"},
...         ],
...     )
...     delete_result0 = await acol.find_one_and_delete(
...         {"species": {"$ne": "frog"}},
...         projection=["species"],
...     )
...     print("delete_result0", delete_result0)
...     delete_result1 = await acol.find_one_and_delete(
...         {"species": {"$ne": "frog"}},
...     )
...     print("delete_result1", delete_result1)
...
>>> asyncio.run(do_find_one_and_delete(my_async_coll))
delete_result0 {'_id': 'f335cd0f-...', 'species': 'swan'}
delete_result1 None


Delete one document matching a provided filter.
This method never deletes more than a single document, regardless
of the number of matches to the provided filters.

filter

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.

a DeleteResult object summarizing the outcome of the delete operation.

>>> my_coll.insert_many([{"seq": 1}, {"seq": 0}, {"seq": 2}])
InsertManyResult(...)
>>> my_coll.delete_one({"seq": 1})
DeleteResult(raw_results=..., deleted_count=1)
>>> my_coll.distinct("seq")
[0, 2]
>>> my_coll.delete_one(
...     {"seq": {"$exists": True}},
...     sort={"seq": astrapy.constants.SortDocuments.DESCENDING},
... )
DeleteResult(raw_results=..., deleted_count=1)
>>> my_coll.distinct("seq")
[0]
>>> my_coll.delete_one({"seq": 2})
DeleteResult(raw_results=..., deleted_count=0)


Delete all documents matching a provided filter.

a predicate expressed as a dictionary according to the
Data API filter syntax. Examples are:
    {}
    {"name": "John"}
    {"price": {"$lt": 100}}
    {"$and": [{"name": "John"}, {"price": {"$lt": 100}}]}
See the Data API documentation for the full set of operators.
Passing an empty filter, `{}`, completely erases all contents
of the collection.

a timeout, in milliseconds, for the operation.
If not passed, the collection-level setting is used instead:
keep in mind that this method entails successive HTTP requests
to the API, depending on how many documents are to be deleted.
For this reason, in most cases it is suggested to relax the
timeout compared to other method calls.

>>> async def do_delete_many(acol: AsyncCollection) -> None:
...     await acol.insert_many([{"seq": 1}, {"seq": 0}, {"seq": 2}])
...     delete_result0 = await acol.delete_many({"seq": {"$lte": 1}})
...     print("delete_result0.deleted_count", delete_result0.deleted_count)
...     distinct1 = await acol.distinct("seq")
...     print("distinct1", distinct1)
...     delete_result2 = await acol.delete_many({"seq": {"$lte": 1}})
...     print("delete_result2.deleted_count", delete_result2.deleted_count)
...
>>> asyncio.run(do_delete_many(my_async_coll))
delete_result0.deleted_count 2
distinct1 [2]
delete_result2.deleted_count 0


Delete all documents in a collection.

a dictionary of the form {"ok": 1} to signal successful deletion.

>>> async def do_delete_all(acol: AsyncCollection) -> None:
...     distinct0 = await acol.distinct("seq")
...     print("distinct0", distinct0)
...     count1 = await acol.count_documents({}, upper_bound=100)
...     print("count1", count1)
...     delete_result2 = await acol.delete_all()
...     print("delete_result2", delete_result2)
...     count3 = await acol.count_documents({}, upper_bound=100)
...     print("count3", count3)
...
>>> asyncio.run(do_delete_all(my_async_coll))
distinct0 [4, 2, 3, 0, 1]
count1 5
delete_result2 {'ok': 1}
count3 0


Execute an arbitrary amount of operations such as inserts, updates, deletes
either sequentially or concurrently.

This method does not execute atomically, i.e. individual operations are
each performed in the same way as the corresponding collection method,
and each one is a different and unrelated database mutation.

requests

an iterable over concrete subclasses of `BaseOperation`,
such as `AsyncInsertMany` or `AsyncReplaceOne`. Each such object
represents an operation ready to be executed on a collection,
and is instantiated by passing the same parameters as one
would the corresponding collection method.

ordered

whether to launch the `requests` one after the other or
in arbitrary order, possibly in a concurrent fashion. For
performance reasons, False (default) should be preferred
when compatible with the needs of the application flow.

concurrency

maximum number of concurrent operations executing at
a given time. It cannot be more than one for ordered bulk writes.

a timeout, in milliseconds, for the whole bulk write.
Remember that, if the method call times out, then there's no
guarantee about what portion of the bulk write has been received
and successfully executed by the Data API.
If not passed, the collection-level setting is used instead:
in most cases, however, one should pass a relaxed timeout
if longer sequences of operations are to be executed in bulk.

A single BulkWriteResult summarizing the whole list of requested

operations. The keys in the map attributes of BulkWriteResult

(when present) are the integer indices of the corresponding operation

in the `requests` iterable.

>>> from astrapy.operations import AsyncInsertMany, AsyncReplaceOne, AsyncOperation
>>> from astrapy.results import BulkWriteResult
>>>
>>> async def do_bulk_write(
...     acol: AsyncCollection,
...     async_operations: List[AsyncOperation],
... ) -> BulkWriteResult:
...     bw_result = await acol.bulk_write(async_operations)
...     count0 = await acol.count_documents({}, upper_bound=100)
...     print("count0", count0)
...     distinct0 = await acol.distinct("replaced")
...     print("distinct0", distinct0)
...     return bw_result
...
>>> op1 = AsyncInsertMany([{"a": 1}, {"a": 2}])
>>> op2 = AsyncReplaceOne(
...     {"z": 9},
...     replacement={"z": 9, "replaced": True},
...     upsert=True,
... )
>>> result = asyncio.run(do_bulk_write(my_async_coll, [op1, op2]))
count0 3
distinct0 [True]
>>> print("result", result)
result BulkWriteResult(bulk_api_results={0: ..., 1: ...}, deleted_count=0, inserted_count=3, matched_count=0, modified_count=0, upserted_count=1, upserted_ids={1: 'ccd0a800-...'})


Drop the collection, i.e. delete it from the database along with
all the documents it contains.

drop

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.
Remember there is not guarantee that a request that has
timed out us not in fact honored.

>>> async def drop_and_check(acol: AsyncCollection) -> None:
...     doc0 = await acol.find_one({})
...     print("doc0", doc0)
...     drop_result = await acol.drop()
...     print("drop_result", drop_result)
...     doc1 = await acol.find_one({})
...
>>> asyncio.run(drop_and_check(my_async_coll))
doc0 {'_id': '...', 'z': -10}
drop_result {'ok': 1}
Traceback (most recent call last):
    ... ...
astrapy.exceptions.DataAPIResponseException: Collection does not exist, collection name: my_collection


Send a POST request to the Data API for this collection with
an arbitrary, caller-provided payload.

command

body

a JSON-serializable dictionary, the payload of the request.

if True, responses with a nonempty 'errors' field
result in an astrapy exception being raised.

a timeout, in milliseconds, for the underlying HTTP request.
If not passed, the collection-level setting is used instead.

a dictionary with the response of the HTTP request.

>>> asyncio.await(my_async_coll.command({"countDocuments": {}}))
{'status': {'count': 123}}


An exception specific to issuing requests to the DevOps API.


A request to the DevOps API resulted in an HTTP 4xx or 5xx response.

Though the DevOps API seldom enriches such errors with a response text,
this class acts as the DevOps counterpart to DataAPIHttpException
to facilitate a symmetryc handling of errors at application lebel.

DevOpsAPIHttpException

text

a text message about the exception.

a list of all DevOpsAPIErrorDescriptor objects
found in the response.


error_descriptors: list[DevOpsAPIErrorDescriptor] = error_descriptors


Parse a httpx status error into this exception.


A DevOps API operation timed out.

DevOpsAPITimeoutException

text

a textual description of the error

this denotes the phase of the HTTP request when the event
occurred ("connect", "read", "write", "pool") or "generic" if there is
not a specific request associated to the exception.

endpoint

if the timeout is tied to a specific request, this is the
URL that the request was targeting.

if the timeout is tied to a specific request, this is the
associated payload (as a string).


An object representing a single error returned from the DevOps API,
typically with an error code and a text message.

A single response from the Devops API may return zero, one or more of these.

DevOpsAPIErrorDescriptor

id

a numeric code as found in the API "ID" item.

message

the text found in the API "error" item.

attributes

a dict with any further key-value pairs returned by the API.


message

message: str | None = error_dict.get('message')


attributes

attributes: dict[str, Any] = {k: vfor (k, v) in error_dict.items() if k not in {'ID', 'message'}}


The DevOps API response is malformed in that it does not have
expected field(s), or they are of the wrong type.

DevOpsAPIFaultyResponseException

text

a text message about the exception.

the response returned by the API in the form of a dict.


raw_response: dict[str, Any] | None = raw_response


A request to the DevOps API returned with a non-success return code
and one of more errors in the HTTP response.

DevOpsAPIResponseException

text

a text message about the exception.

command

the raw payload that was sent to the DevOps API.

a list of all DevOpsAPIErrorDescriptor objects
returned by the API in the response.


command

command: dict[str, Any] | None = command


Parse a raw response from the API into this exception.


An object representing a single error returned from the Data API,
typically with an error code and a text message.
An API request would return with an HTTP 200 success error code,
but contain a nonzero amount of these.

A single response from the Data API may return zero, one or more of these.
Moreover, some operations, such as an insert_many, may partally succeed
yet return these errors about the rest of the operation (such as,
some of the input documents could not be inserted).

DataAPIErrorDescriptor

a string code as found in the API "error" item.

message

the text found in the API "error" item.


title

title: str | None = error_dict.get('title')


error_code: str | None = error_dict.get('errorCode')


family

family: str | None = error_dict.get('family')


scope

scope: str | None = error_dict.get('scope')


attributes

attributes: dict[str, Any] = {k: vfor (k, v) in error_dict.items() if k not in self._known_dict_fields}


An object representing an errorful response from the Data API.
Errors specific to the Data API (as opposed to e.g. network failures)
would result in an HTTP 200 success response code but coming with
one or more DataAPIErrorDescriptor objects.

This object corresponds to one response, and as such its attributes
are a single request payload, a single response, but a list of
DataAPIErrorDescriptor instances.

DataAPIDetailedErrorDescriptor

a list of DataAPIErrorDescriptor objects.

command

the raw payload of the API request.

the full API response in the form of a dict.


Any exception occurred while issuing requests to the Data API
and specific to it, such as:
  - a collection is found not to exist when gettings its metadata,
  - the API return a response with an error,
but not, for instance,
  - a network error while sending an HTTP request to the API.


A request to the Data API resulted in an HTTP 4xx or 5xx response.

In most cases this comes with additional information: the purpose
of this class is to present such information in a structured way,
akin to what happens for the DataAPIResponseException, while
still raising (a subclass of) `httpx.HTTPStatusError`.

DataAPIHttpException

text

a text message about the exception.

a list of all DataAPIErrorDescriptor objects
found in the response.


error_descriptors: list[DataAPIErrorDescriptor] = error_descriptors


A Data API operation timed out. This can be a request timeout occurring
during a specific HTTP request, or can happen over the course of a method
involving several requests in a row, such as a paginated find.

DataAPITimeoutException

text

a textual description of the error


The cursor operation cannot be invoked if a cursor is not in its pristine
state (i.e. is already being consumed or is exhausted altogether).

CursorIsStartedException

text

a text message about the exception.

a string description of the current state
of the cursor. See the documentation for Cursor.


A collection is found non-existing and the requested operation
cannot be performed.

CollectionNotFoundException

text

a text message about the exception.

keyspace

the keyspace where the collection was supposed to be.

namespace

an alias for 'keyspace'. *DEPRECATED*, removal in 2.0

the name of the expected collection.


An operation expected a collection not to exist, yet it has
been detected as pre-existing.

CollectionAlreadyExistsException

text

a text message about the exception.

keyspace

the keyspace where the collection was expected not to exist.

the name of the collection.