mirror of
https://github.com/qdrant/landing_page.git
synced 2026-10-01 08:58:31 +02:00
Merge remote-tracking branch 'upstream/master' into v1.16-release-blog
This commit is contained in:
@@ -369,7 +369,7 @@ import static io.qdrant.client.ConditionFactory.matchKeyword;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
import io.qdrant.client.grpc.Points.ScrollPoints;
|
||||
|
||||
QdrantClient client =
|
||||
@@ -515,7 +515,7 @@ import static io.qdrant.client.ConditionFactory.match;
|
||||
import static io.qdrant.client.ConditionFactory.matchKeyword;
|
||||
import static io.qdrant.client.ConditionFactory.nested;
|
||||
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
import io.qdrant.client.grpc.Points.ScrollPoints;
|
||||
|
||||
client
|
||||
|
||||
@@ -323,7 +323,7 @@ import java.util.List;
|
||||
import static io.qdrant.client.ConditionFactory.hasVector;
|
||||
import static io.qdrant.client.PointIdFactory.id;
|
||||
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
import io.qdrant.client.grpc.Points.ScrollPoints;
|
||||
|
||||
client
|
||||
|
||||
@@ -334,6 +334,42 @@ created and `indexed_vectors_count` might be equal to `0`.
|
||||
|
||||
It is possible to reduce the `indexing_threshold` for an existing collection by [updating collection parameters](#update-collection-parameters).
|
||||
|
||||
### Collection metadata
|
||||
|
||||
*Available as of v1.16.0*
|
||||
|
||||
For convenience and better data organization, Qdrant allows attaching custom metadata to collections in the form of key-value pairs.
|
||||
Adding metadata is treated as a part of collection configuration and synchronized across all nodes in a cluster with consensus protocol.
|
||||
|
||||
Collection metadata can be specified during collection creation:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-collection/with-metadata/" >}}
|
||||
|
||||
as well as updated later:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/update-collection/with-metadata/" >}}
|
||||
|
||||
Note, that update operation only modifies the specified metadata fields, leaving other fields unchanged.
|
||||
|
||||
When specified, metadata is returned as part of collection info:
|
||||
|
||||
``` json
|
||||
{
|
||||
"result": {
|
||||
"config": {
|
||||
"metadata": {
|
||||
"my-metadata-field": {
|
||||
"key-a": "value-a",
|
||||
"key-b": 42
|
||||
},
|
||||
"another-field": 123
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Collection aliases
|
||||
|
||||
In a production environment, it is sometimes necessary to switch different versions of vectors seamlessly.
|
||||
@@ -372,3 +408,4 @@ For example, you can switch underlying collection with the following command:
|
||||
### List all collections
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/list-all-collections/simple/" >}}
|
||||
|
||||
|
||||
@@ -319,6 +319,16 @@ If there is no full-text index for the field, the condition will work as exact s
|
||||
|
||||
If the query has several words, then the condition will be satisfied only if all of them are present in the text.
|
||||
|
||||
### Full Text Any
|
||||
|
||||
*Available as of v1.16.0*
|
||||
|
||||
The `text_any` full-text match condition is similar to the `text` condition, but with a key difference: while `text` only matches text fields that contain *all* the query terms, `text_any` matches fields that contain *any* of the query terms. In other words, even if a text field contains just one of the query terms, it is considered a match.
|
||||
|
||||
For example, a query for `good cheap` matches `cheap hardware` as well as `good performance`.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/filter-condition/full-text-match-any/" >}}
|
||||
|
||||
### Phrase Match
|
||||
|
||||
*Available as of v1.15.0*
|
||||
|
||||
@@ -10,7 +10,7 @@ hideInSidebar: false # Optional. If true, the page will not be shown in the side
|
||||
|
||||
_Available as of v1.10.0_
|
||||
|
||||
With the introduction of [many named vectors per point](/documentation/concepts/vectors/#named-vectors), there are use-cases when the best search is obtained by combining multiple queries,
|
||||
With the introduction of [multiple named vectors per point](/documentation/concepts/vectors/#named-vectors), there are use-cases when the best search is obtained by combining multiple queries,
|
||||
or by performing the search in more than one stage.
|
||||
|
||||
Qdrant has a flexible and universal interface to make this possible, called `Query API` ([API reference](https://api.qdrant.tech/api-reference/search/query-points)).
|
||||
@@ -35,33 +35,46 @@ One of the most common problems when you have different representations of the s
|
||||
For example, in text search, it is often useful to combine dense and sparse vectors get the best of semantics,
|
||||
plus the best of matching specific words.
|
||||
|
||||
Qdrant currently has two ways of combining the results from different queries:
|
||||
Qdrant has a few ways of fusing the results from different queries: `rrf` and `dbsf`
|
||||
|
||||
- `rrf` -
|
||||
<a href=https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf target="_blank">
|
||||
Reciprocal Rank Fusion
|
||||
</a>
|
||||
### Reciprocal Rank Fusion (RRF)
|
||||
<a href=https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf target="_blank">
|
||||
RRF</a> considers the positions of results within each query, and boosts the ones that appear closer to the top in multiple sets of results.
|
||||
|
||||
The formula is simple, but needs access to the rank of each result in each query.
|
||||
|
||||
Considers the positions of results within each query, and boosts the ones that appear closer to the top in multiple of them.
|
||||
$$ score(d\in D) = \sum_{r_d\in R(d)} \frac{1}{k + r_d} $$
|
||||
|
||||
- `dbsf` -
|
||||
<a href=https://medium.com/plain-simple-software/distribution-based-score-fusion-dbsf-a-new-approach-to-vector-search-ranking-f87c37488b18 target="_blank">
|
||||
Distribution-Based Score Fusion
|
||||
</a> _(available as of v1.11.0)_
|
||||
Where $D$ the set of points across all results, $R(d)$ is the set of rankings for a particular document, and $k$ is a constant (set to 2 by default).
|
||||
|
||||
Normalizes the scores of the points in each query, using the mean +/- the 3rd standard deviation as limits, and then sums the scores of the same point across different queries.
|
||||
Here is an example of RRF for a query containing two prefetches against different named vectors configured to hold sparse and dense vectors, respectively.
|
||||
|
||||
<aside role="status"><code>dbsf</code> is stateless and calculates the normalization limits only based on the results of each query, not on all the scores that it has seen.</aside>
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/hybrid-rrf/" >}}
|
||||
|
||||
Here is an example of Reciprocal Rank Fusion for a query containing two prefetches against different named vectors configured to respectively hold sparse and dense vectors.
|
||||
#### Parametrized RRF
|
||||
_Available as of v1.16.0_
|
||||
|
||||
To change the value of constant $k$ in the formula, use the dedicated `rrf` query variant.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/hybrid-rrf-k/" >}}
|
||||
|
||||
|
||||
### Distribution-Based Score Fusion (DBSF)
|
||||
|
||||
_Available as of v1.11.0_
|
||||
|
||||
<a href=https://medium.com/plain-simple-software/distribution-based-score-fusion-dbsf-a-new-approach-to-vector-search-ranking-f87c37488b18 target="_blank">
|
||||
DBSF</a>
|
||||
normalizes the scores of the points in each query, using the mean +/- the 3rd standard deviation as limits, and then sums the scores of the same point across different queries.
|
||||
|
||||
<aside role="status"><code>dbsf</code> is stateless and calculates the normalization limits only based on the results of each query, not on all the scores that it has seen.</aside>
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/hybrid-basic/" >}}
|
||||
|
||||
## Multi-stage queries
|
||||
|
||||
In many cases, the usage of a larger vector representation gives more accurate search results, but it is also more expensive to compute.
|
||||
In general, larger vector representations give more accurate search results, but makes them more expensive to compute.
|
||||
|
||||
Splitting the search into two stages is a known technique:
|
||||
Splitting the search into two stages is a known technique to mitigate this effect:
|
||||
|
||||
- First, use a smaller and cheaper representation to get a large list of candidates.
|
||||
- Then, re-score the candidates using the larger and more accurate representation.
|
||||
|
||||
@@ -182,6 +182,26 @@ Available tokenizers are:
|
||||
* `prefix` - splits the string into words, separated by spaces, punctuation marks, and special characters, and then creates a prefix index for each word. For example: `hello` will be indexed as `h`, `he`, `hel`, `hell`, `hello`.
|
||||
* `multilingual` - a special type of tokenizer based on multiple packages like [charabia](https://github.com/meilisearch/charabia) and [vaporetto](https://github.com/daac-tools/vaporetto) to deliver fast and accurate tokenization for a large variety of languages. It allows proper tokenization and lemmatization for multiple languages, including those with non-Latin alphabets and non-space delimiters. See the [charabia documentation](https://github.com/meilisearch/charabia) for a full list of supported languages and normalization options. Note: For the Japanese language, Qdrant relies on the `vaporetto` project, which has much less overhead compared to `charabia`, while maintaining comparable performance.
|
||||
|
||||
### Lowercasing
|
||||
|
||||
By default, full-text search in Qdrant is case-insensitive. For example, users can search for the lowercase term `tv` and find text fields containing the uppercase word `TV`. Case-insensitivity is achieved by converting both the words in the index and the query terms to lowercase.
|
||||
|
||||
Lowercasing is enabled by default. To use case-sensitive full-text search, configure a full-text index with `lowercase` set to `false`.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-payload-index/lowercase-full-text/" >}}
|
||||
|
||||
### ASCII Folding
|
||||
|
||||
*Available as of v1.16.0*
|
||||
|
||||
When enabled, ASCII folding converts Unicode characters into their corresponding ASCII equivalents, for example, by removing diacritics. For instance, the character `ã` is changed into `a`, `ç` becomes `c`, and `é` is converted to `e`.
|
||||
|
||||
Because ASCII folding is applied to both the words in the index and the query terms, it increases recall. For example, users can search for `cafe` and also find text fields containing the word `café`.
|
||||
|
||||
ASCII folding is not enabled by default. To enable it, configure a full-text index with `ascii_folding` set to `true`.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-payload-index/asciifolding-full-text/" >}}
|
||||
|
||||
### Stemmer
|
||||
|
||||
A **stemmer** is an algorithm used in text processing to reduce words to their root or base form, known as the "stem." For example, the words "running", "runner and "runs" can all be reduced to the stem "run."
|
||||
@@ -324,18 +344,23 @@ Where:
|
||||
|
||||
Separately, a payload index and a vector index cannot solve the problem of search using the filter completely.
|
||||
|
||||
In the case of weak filters, you can use the HNSW index as it is. In the case of stringent filters, you can use the payload index and complete rescore.
|
||||
However, for cases in the middle, this approach does not work well.
|
||||
In the case of high-selectivity (weak) filters, you can use the HNSW index as it is.
|
||||
In the case of low-selectivity (strict) filters, you can use the payload index and complete rescore.
|
||||
|
||||
On the one hand, we cannot apply a full scan on too many vectors. On the other hand, the HNSW graph starts to fall apart when using too strict filters.
|
||||
However, for cases in the middle, this approach does not work well.
|
||||
On the one hand, we cannot apply a full scan on too many vectors.
|
||||
On the other hand, the HNSW graph starts to fall apart when using too strict filters.
|
||||
|
||||

|
||||
|
||||

|
||||
<!--  -->
|
||||
|
||||
You can find more information on why this happens in our [blog post](https://blog.vasnetsov.com/posts/categorical-hnsw/).
|
||||
Qdrant solves this problem by extending the HNSW graph with additional edges based on the stored payload values.
|
||||
|
||||
Extra edges allow you to efficiently search for nearby vectors using the HNSW index and apply filters as you search in the graph.
|
||||
You can find more information on this approach in our [article](/articles/filtrable-hnsw/).
|
||||
|
||||
This approach minimizes the overhead on condition checks since you only need to calculate the conditions for a small fraction of the points involved in the search.
|
||||
However, in some cases, these additional edges might not be enough.
|
||||
These extra edges are added per each payload index separately, but not per each possible combination of them.
|
||||
So, a combination of two or more strict filters still might lead to disconnected graph components.
|
||||
The same may happen when having a large number of soft-deleted points in the graph.
|
||||
In such cases, the [ACORN Search Algorithm](/documentation/concepts/search/#acorn-search-algorithm) can be used.
|
||||
|
||||
@@ -265,6 +265,36 @@ Alternative way to specify which points to remove is to use filter.
|
||||
|
||||
This example removes all points with `{ "color": "red" }` from the collection.
|
||||
|
||||
## Conditional updates
|
||||
|
||||
_Available as of v1.16.0_
|
||||
|
||||
All update operations (including point insertion, vector updates, payload updates, and deletions) support configurable pre-conditions based on filters.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/insert-points/with-condition/" >}}
|
||||
|
||||
While conditional payload modification and deletion covers the use-case of mass data modification, conditional point insertion and vector updates are particularly useful for implementing optimistic concurrency control in distributed systems.
|
||||
|
||||
A common scenario for such mechanism is when multiple clients try to update the same point independently.
|
||||
Consider the following sequence of events:
|
||||
|
||||
- Client A reads point P.
|
||||
- Client B reads point P.
|
||||
- Client A modifies point P and writes it back to Qdrant.
|
||||
- Client B modifies point P (based on the stale data) and writes it back to Qdrant, unintentionally overwriting changes made by Client A.
|
||||
|
||||
To prevent such situations, Client B can use conditional updates.
|
||||
For this, we would need to introduce an additional field in the payload, e.g. `version`, which would be incremented on each update.
|
||||
|
||||
When Client A writes back the modified point P, it would set the condition that the `version` field must be equal to the value it read initially.
|
||||
If Client B tries to write back its changes later, the condition would fail (as the `version` has been incremented by Client A), and Qdrant would reject the update, preventing accidental overwrites.
|
||||
|
||||
Instead of `version`, applications can use timestamps (assuming synchronized clocks) or any other monotonically increasing value that fits their data model.
|
||||
|
||||
This mechanism is especially useful in the scenarios of embedding model migration, where we need to resolve conflicts between regular application updates and background re-embedding tasks.
|
||||
|
||||
{{< figure src="/docs/embedding-model-migration.png" caption="Embedding model migration in blue-green deployment" width="80%" >}}
|
||||
|
||||
## Retrieve points
|
||||
|
||||
There is a method for retrieving points by their ids.
|
||||
|
||||
@@ -87,6 +87,8 @@ Currently, it could be:
|
||||
* `hnsw_ef` - value that specifies `ef` parameter of the HNSW algorithm.
|
||||
* `exact` - option to not use the approximate search (ANN). If set to true, the search may run for a long as it performs a full scan to retrieve exact results.
|
||||
* `indexed_only` - With this option you can disable the search in those segments where vector index is not built yet. This may be useful if you want to minimize the impact to the search performance whilst the collection is also being updated. Using this option may lead to a partial result if the collection is not fully indexed yet, consider using it only if eventual consistency is acceptable for your use case.
|
||||
* `quantization` - parameters related to quantization. See [Searching with Quantization](/documentation/guides/quantization/#searching-with-quantization) guide.
|
||||
* `acorn` - parameters related to the [ACORN search algorithm](#acorn-search-algorithm).
|
||||
|
||||
Since the `filter` parameter is specified, the search is performed only among those points that satisfy the filter condition.
|
||||
See details of possible filters and their work in the [filtering](/documentation/concepts/filtering/) section.
|
||||
@@ -168,6 +170,34 @@ It is possible to target nested fields using a dot notation:
|
||||
|
||||
Accessing array elements by index is currently not supported.
|
||||
|
||||
### ACORN Search Algorithm
|
||||
|
||||
*Available as of v1.16.0*
|
||||
|
||||
For filtered vector search, you are recommended to create a [payload index](/documentation/concepts/indexing/#payload-index) for the fields you want to filter by.
|
||||
During the search, Qdrant will use a combined [filterable index](/documentation/concepts/indexing/#filtrable-index).
|
||||
However, when combining multiple strict payload filters, this mechanism might not provide sufficient accuracy.
|
||||
In such cases, you can use the ACORN search algorithm.
|
||||
|
||||
It is an extension to the regular HNSW search algorithm, based on the ACORN-1 algorithm described in the paper [ACORN: Performant and Predicate-Agnostic Search Over Vector Embeddings and Structured Data](https://arxiv.org/abs/2403.04871).
|
||||
During graph traversal, it explores not just direct neighbors (first hop), but also neighbors of neighbors (second hop) when direct neighbors are filtered out.
|
||||
This improves search accuracy at the cost of performance.
|
||||
|
||||
Enable it as follows:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/with-acorn/" >}}
|
||||
|
||||
ACORN is disabled by default.
|
||||
Once enabled via the `enable` flag, it activates conditionally when estimated filter selectivity is below the threshold.
|
||||
The optional `max_selectivity` value controls this threshold;
|
||||
`0.0` means ACORN will never be used, `1.0` means it will always be used. The default value is `0.4`.
|
||||
Selectivity is estimated as:
|
||||
$$ \text{Estimated filter selectivity} =
|
||||
\frac{\text{Estimated number of points satisfying the filters}}
|
||||
{\text{Total number of points}}
|
||||
$$
|
||||
Since ACORN is significantly slower (approximately 2-10x in typical scenarios) but improves recall for restrictive filters, tuning this parameter is about deciding when the accuracy improvement justifies the performance cost.
|
||||
|
||||
## Batch search API
|
||||
|
||||
The batch search API enables to perform multiple search requests via a single request.
|
||||
@@ -453,5 +483,6 @@ However, the general principles are:
|
||||
* estimate the cardinality of a filtered result before selecting a strategy
|
||||
* retrieve points using payload index (see [indexing](/documentation/concepts/indexing/)) if cardinality is below threshold
|
||||
* use filterable vector index if the cardinality is above a threshold
|
||||
* use ACORN when the selectivity (ratio) is low, but the cardinality (an amount) is still high
|
||||
|
||||
You can adjust the threshold using a [configuration file](https://github.com/qdrant/qdrant/blob/master/config/config.yaml), as well as independently for each collection.
|
||||
|
||||
@@ -62,340 +62,106 @@ message in an environment variable, such as
|
||||
|
||||
*Available as of v1.13.0*
|
||||
|
||||
Strict mode is a feature to restrict certain type of operations on the collection in order to protect it.
|
||||
Strict mode is a feature to restrict certain type of operations on a collection in order to protect the Qdrant cluster.
|
||||
|
||||
The goal is to prevent inefficient usage patterns that could overload the collections.
|
||||
The goal is to prevent inefficient usage patterns that could overload the system.
|
||||
|
||||
This configuration ensures a more predictible and responsive service when you do not have control over the queries that are being executed.
|
||||
|
||||
Here is a non exhaustive list of operations that can be restricted using strict mode:
|
||||
|
||||
- Preventing querying non indexed payload which can be very slow
|
||||
- Maximum number of filtering conditions in a query
|
||||
- Maximum batch size when inserting vectors
|
||||
- Maximum collection size (in terms of vectors or payload size)
|
||||
|
||||
See [schema definitions](https://api.qdrant.tech/api-reference/collections/create-collection#request.body.strict_mode_config) for all the `strict_mode_config` parameters.
|
||||
Strict mode ensures a more predictable and responsive service when you do not have control over the queries that are being executed.
|
||||
|
||||
Upon crossing a limit, the server will return a client side error with the information about the limit that was crossed.
|
||||
|
||||
The `strict_mode_config` can be enabled when [creating](#create-a-collection) a new collection, see [schema definitions](https://api.qdrant.tech/api-reference/collections/create-collection#request.body.strict_mode_config) for all the available `strict_mode_config` parameters.
|
||||
|
||||
As part of the config, the `enabled` field act as a toggle to enable or disable the strict mode dynamically.
|
||||
|
||||
The `strict_mode_config` can be enabled when [creating](#create-a-collection) a collection, for instance below to activate the `unindexed_filtering_retrieve` limit.
|
||||
It is possible to raise the default limits and/or disable strict mode entirely. Though, in order to ensure a stable cluster we strongly recommend to keep strict mode enabled using its default configuration. For disabling strict mode on an existing collection use:
|
||||
|
||||
Setting `unindexed_filtering_retrieve` to false prevents the usage of filtering on a non indexed payload key.
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/disable/" >}}
|
||||
|
||||
```http
|
||||
PUT /collections/{collection_name}
|
||||
{
|
||||
"strict_mode_config": {
|
||||
"enabled": true,
|
||||
"unindexed_filtering_retrieve": false
|
||||
}
|
||||
}
|
||||
```
|
||||
### Disable retrieving via non indexed payload
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:6333/collections/{collection_name} \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"strict_mode_config": {
|
||||
"enabled":" true,
|
||||
"unindexed_filtering_retrieve": false
|
||||
}
|
||||
}'
|
||||
```
|
||||
Setting `unindexed_filtering_retrieve` to false prevents retrieving points by filtering on a non indexed payload key which can be very slow.
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/unindexed-filtering-retrieve/" >}}
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
Or turn it off later on an existing collection through the [collection update](#update-collection-parameters) API.
|
||||
|
||||
client.create_collection(
|
||||
collection_name="{collection_name}",
|
||||
strict_mode_config=models.StrictModeConfig(enabled=True, unindexed_filtering_retrieve=false),
|
||||
)
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/unindexed-filtering-retrieve-off/" >}}
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
### Disable updating via non indexed payload
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
Setting `unindexed_filtering_update` to false prevents updating points by filtering on a non indexed payload key which can be very slow.
|
||||
|
||||
client.createCollection("{collection_name}", {
|
||||
strict_mode_config: {
|
||||
enabled: true,
|
||||
unindexed_filtering_retrieve: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/unindexed-filtering-update/" >}}
|
||||
|
||||
```rust
|
||||
use qdrant_client::Qdrant;
|
||||
use qdrant_client::qdrant::{CreateCollectionBuilder, StrictModeConfigBuilder};
|
||||
### Maximum number of payload index count
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
Setting `max_payload_index_count` caps the maximum number of payload index that can exist on a collection.
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("{collection_name}")
|
||||
.strict_config_mode(StrictModeConfigBuilder::default().enabled(true).unindexed_filtering_retrieve(false)),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/max-payload-index-count/" >}}
|
||||
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.StrictModeCOnfig;
|
||||
### Maximum query `limit` parameter
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
Retrieving large result set is expensive.
|
||||
|
||||
client
|
||||
.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setStrictModeConfig(
|
||||
StrictModeConfig.newBuilder().setEnabled(true).setUnindexedFilteringRetrieve(false).build())
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
Setting `max_query_limit` caps the maximum number of points that can be retrieved in a single query.
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/max-query-limit/" >}}
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
### Maximum `timeout` parameter
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
strictModeConfig: new StrictModeConfig { enabled = true, unindexed_filtering_retrieve = false }
|
||||
);
|
||||
```
|
||||
Long running operations are often symptomatic of a deeper issue.
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
Setting `max_timeout` caps the maximum value in seconds for the `timeout` parameter in all API operations.
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/max-timeout/" >}}
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
### Maximum size of a filtering condition
|
||||
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
StrictModeConfig: &qdrant.StrictModeConfig{
|
||||
Enabled: qdrant.PtrOf(true),
|
||||
IndexingThreshold: qdrant.PtrOf(false),
|
||||
},
|
||||
})
|
||||
```
|
||||
Large filtering conditions are expensive to evaluate.
|
||||
|
||||
Or activate it later on an existing collection through the [collection update](#update-collection-parameters) API:
|
||||
Setting `condition_max_size` caps the maximum number of element a filtering condition can have.
|
||||
|
||||
```http
|
||||
PATCH /collections/{collection_name}
|
||||
{
|
||||
"strict_mode_config": {
|
||||
"enabled": true,
|
||||
"unindexed_filtering_retrieve": false
|
||||
}
|
||||
}
|
||||
```
|
||||
e.g. the number of elements in `MatchAny`
|
||||
|
||||
```bash
|
||||
curl -X PATCH http://localhost:6333/collections/{collection_name} \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"strict_mode_config": {
|
||||
"enabled": true,
|
||||
"unindexed_filtering_retrieve": false
|
||||
}
|
||||
}'
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/condition-max-size/" >}}
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
### Maximum number of conditions in a filter
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
A large number of filtering conditions are expensive to evaluate.
|
||||
|
||||
client.update_collection(
|
||||
collection_name="{collection_name}",
|
||||
strict_mode_config=models.StrictModeConfig(enabled=True, unindexed_filtering_retrieve=False),
|
||||
)
|
||||
```
|
||||
Setting `filter_max_conditions` caps the maximum number of conditions filters can have.
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/filter-max-conditions/" >}}
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
### Maximum batch size when inserting vectors
|
||||
|
||||
client.updateCollection("{collection_name}", {
|
||||
strict_mode_config: {
|
||||
enabled: true,
|
||||
unindexed_filtering_retrieve: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
Sending very large batch upserts can create internal congestion.
|
||||
|
||||
```rust
|
||||
use qdrant_client::qdrant::{StrictModeConfigBuilder, UpdateCollectionBuilder};
|
||||
Setting `upsert_max_batchsize` caps the maximum size in bytes of a batch during vector upserts.
|
||||
|
||||
client
|
||||
.update_collection(
|
||||
UpdateCollectionBuilder::new("{collection_name}").strict_mode_config(
|
||||
StrictModeConfigBuilder::default().enabled(true).unindexed_filtering_retrieve(false),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/upsert-max-batchsize/" >}}
|
||||
|
||||
```java
|
||||
import io.qdrant.client.grpc.Collections.StrictModeConfigBuilder;
|
||||
import io.qdrant.client.grpc.Collections.UpdateCollection;
|
||||
### Maximum collection storage size
|
||||
|
||||
client.updateCollectionAsync(
|
||||
UpdateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setStrictModeConfig(
|
||||
StrictModeConfig.newBuilder().setEnabled(true).setUnindexedFilteringRetrieve(false).build())
|
||||
.build());
|
||||
```
|
||||
It is possible to set the maximum size of a collection in terms of vectors and/or payload storage size.
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
Setting `max_collection_vector_size_bytes` and/or `max_collection_payload_size_bytes` caps the maximum byte size of a collection.
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/max-collection-storage-size-bytes/" >}}
|
||||
|
||||
await client.UpdateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
strictModeConfig: new StrictModeConfig { Enabled = true, UnindexedFilteringRetrieve = false }
|
||||
);
|
||||
```
|
||||
### Maximum points count
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
Setting `max_points_count` caps the maximum number of points for a collection.
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/max-points-count/" >}}
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
### Rate limiting
|
||||
|
||||
client.UpdateCollection(context.Background(), &qdrant.UpdateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
StrictModeConfig: &qdrant.StrictModeConfig{
|
||||
Enabled: qdrant.PtrOf(true),
|
||||
UnindexedFilteringRetrieve: qdrant.PtrOf(false),
|
||||
},
|
||||
})
|
||||
```
|
||||
An extremely high rate of incoming requests can have a negative impact on the latency.
|
||||
|
||||
To disable completely strict mode on an existing collection use:
|
||||
Setting `read_rate_limit` and/or `write_rate_limit` to cap the maximum number of operations per minute per replica.
|
||||
|
||||
```http
|
||||
PATCH /collections/{collection_name}
|
||||
{
|
||||
"strict_mode_config": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
When exceeding the maximum number of operations, the client will receive an HTTP 429 error code with a suggested delay before retrying.
|
||||
|
||||
```bash
|
||||
curl -X PATCH http://localhost:6333/collections/{collection_name} \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"strict_mode_config": {
|
||||
"enabled": false,
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.update_collection(
|
||||
collection_name="{collection_name}",
|
||||
strict_mode_config=models.StrictModeConfig(enabled=False),
|
||||
)
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.updateCollection("{collection_name}", {
|
||||
strict_mode_config: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```rust
|
||||
use qdrant_client::qdrant::{StrictModeConfigBuilder, UpdateCollectionBuilder};
|
||||
|
||||
client
|
||||
.update_collection(
|
||||
UpdateCollectionBuilder::new("{collection_name}").strict_mode_config(
|
||||
StrictModeConfigBuilder::default().enabled(false),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
|
||||
```java
|
||||
import io.qdrant.client.grpc.Collections.StrictModeConfigBuilder;
|
||||
import io.qdrant.client.grpc.Collections.UpdateCollection;
|
||||
|
||||
client.updateCollectionAsync(
|
||||
UpdateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setStrictModeConfig(
|
||||
StrictModeConfig.newBuilder().setEnabled(false).build())
|
||||
.build());
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.UpdateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
strictModeConfig: new StrictModeConfig { Enabled = false }
|
||||
);
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.UpdateCollection(context.Background(), &qdrant.UpdateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
StrictModeConfig: &qdrant.StrictModeConfig{
|
||||
Enabled: qdrant.PtrOf(false),
|
||||
},
|
||||
})
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/strict-mode/rate-limiting/" >}}
|
||||
@@ -328,30 +328,30 @@ storage:
|
||||
quantization: null
|
||||
|
||||
# Default strict mode parameters for newly created collections.
|
||||
strict_mode:
|
||||
#strict_mode:
|
||||
# Whether strict mode is enabled for a collection or not.
|
||||
enabled: false
|
||||
#enabled: false
|
||||
|
||||
# Max allowed `limit` parameter for all APIs that don't have their own max limit.
|
||||
max_query_limit: null
|
||||
#max_query_limit: null
|
||||
|
||||
# Max allowed `timeout` parameter.
|
||||
max_timeout: null
|
||||
#max_timeout: null
|
||||
|
||||
# Allow usage of unindexed fields in retrieval based (eg. search) filters.
|
||||
unindexed_filtering_retrieve: null
|
||||
#unindexed_filtering_retrieve: null
|
||||
|
||||
# Allow usage of unindexed fields in filtered updates (eg. delete by payload).
|
||||
unindexed_filtering_update: null
|
||||
#unindexed_filtering_update: null
|
||||
|
||||
# Max HNSW value allowed in search parameters.
|
||||
search_max_hnsw_ef: null
|
||||
#search_max_hnsw_ef: null
|
||||
|
||||
# Whether exact search is allowed or not.
|
||||
search_allow_exact: null
|
||||
#search_allow_exact: null
|
||||
|
||||
# Max oversampling value allowed in search.
|
||||
search_max_oversampling: null
|
||||
#search_max_oversampling: null
|
||||
|
||||
# Maximum number of collections allowed to be created
|
||||
# If null - no limit.
|
||||
@@ -423,6 +423,10 @@ service:
|
||||
#
|
||||
# Uncomment to enable.
|
||||
# hardware_reporting: true
|
||||
#
|
||||
# Uncomment to enable.
|
||||
# Prefix for the names of metrics in the /metrics API.
|
||||
# metrics_prefix: qdrant_
|
||||
|
||||
cluster:
|
||||
# Use `enabled: true` to run Qdrant in distributed deployment mode
|
||||
|
||||
@@ -361,113 +361,7 @@ A clear use-case for this feature is managing a multi-tenant collection, where e
|
||||
|
||||
To enable user-defined sharding, set `sharding_method` to `custom` during collection creation:
|
||||
|
||||
```http
|
||||
PUT /collections/{collection_name}
|
||||
{
|
||||
"shard_number": 1,
|
||||
"sharding_method": "custom"
|
||||
// ... other collection parameters
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_collection(
|
||||
collection_name="{collection_name}",
|
||||
shard_number=1,
|
||||
sharding_method=models.ShardingMethod.CUSTOM,
|
||||
# ... other collection parameters
|
||||
)
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createCollection("{collection_name}", {
|
||||
shard_number: 1,
|
||||
sharding_method: "custom",
|
||||
// ... other collection parameters
|
||||
});
|
||||
```
|
||||
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateCollectionBuilder, Distance, ShardingMethod, VectorParamsBuilder,
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("{collection_name}")
|
||||
.vectors_config(VectorParamsBuilder::new(300, Distance::Cosine))
|
||||
.shard_number(1)
|
||||
.sharding_method(ShardingMethod::Custom.into()),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
|
||||
```java
|
||||
import static io.qdrant.client.ShardKeyFactory.shardKey;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.ShardingMethod;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
// ... other collection parameters
|
||||
.setShardNumber(1)
|
||||
.setShardingMethod(ShardingMethod.Custom)
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
// ... other collection parameters
|
||||
shardNumber: 1,
|
||||
shardingMethod: ShardingMethod.Custom
|
||||
);
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
// ... other collection parameters
|
||||
ShardNumber: qdrant.PtrOf(uint32(1)),
|
||||
ShardingMethod: qdrant.ShardingMethod_Custom.Enum(),
|
||||
})
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-collection/with-custom-sharding/" >}}
|
||||
|
||||
In this mode, the `shard_number` means the number of shards per shard key, where points will be distributed evenly. For example, if you have 10 shard keys and a collection config with these settings:
|
||||
|
||||
@@ -487,235 +381,11 @@ For large cardinality keys, it is recommended to use [partition by payload](/doc
|
||||
|
||||
Now you need to create custom shards ([API reference](https://api.qdrant.tech/api-reference/distributed/create-shard-key#request)):
|
||||
|
||||
```http
|
||||
PUT /collections/{collection_name}/shards
|
||||
{
|
||||
"shard_key": "{shard_key}"
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_shard_key("{collection_name}", "{shard_key}")
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createShardKey("{collection_name}", {
|
||||
shard_key: "{shard_key}"
|
||||
});
|
||||
```
|
||||
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateShardKeyBuilder, CreateShardKeyRequestBuilder
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_shard_key(
|
||||
CreateShardKeyRequestBuilder::new("{collection_name}")
|
||||
.request(CreateShardKeyBuilder::default().shard_key("{shard_key".to_string())),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
|
||||
```java
|
||||
import static io.qdrant.client.ShardKeyFactory.shardKey;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKey;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKeyRequest;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.createShardKeyAsync(CreateShardKeyRequest.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setRequest(CreateShardKey.newBuilder()
|
||||
.setShardKey(shardKey("{shard_key}"))
|
||||
.build())
|
||||
.build()).get();
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateShardKeyAsync(
|
||||
"{collection_name}",
|
||||
new CreateShardKey { ShardKey = new ShardKey { Keyword = "{shard_key}", } }
|
||||
);
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateShardKey(context.Background(), "{collection_name}", &qdrant.CreateShardKey{
|
||||
ShardKey: qdrant.NewShardKey("{shard_key}"),
|
||||
})
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-shard/create-named-shard/" >}}
|
||||
|
||||
To specify the shard for each point, you need to provide the `shard_key` field in the upsert request:
|
||||
|
||||
```http
|
||||
PUT /collections/{collection_name}/points
|
||||
{
|
||||
"points": [
|
||||
{
|
||||
"id": 1111,
|
||||
"vector": [0.1, 0.2, 0.3]
|
||||
},
|
||||
]
|
||||
"shard_key": "user_1"
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.upsert(
|
||||
collection_name="{collection_name}",
|
||||
points=[
|
||||
models.PointStruct(
|
||||
id=1111,
|
||||
vector=[0.1, 0.2, 0.3],
|
||||
),
|
||||
],
|
||||
shard_key_selector="user_1",
|
||||
)
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.upsert("{collection_name}", {
|
||||
points: [
|
||||
{
|
||||
id: 1111,
|
||||
vector: [0.1, 0.2, 0.3],
|
||||
},
|
||||
],
|
||||
shard_key: "user_1",
|
||||
});
|
||||
```
|
||||
|
||||
```rust
|
||||
use qdrant_client::qdrant::{PointStruct, UpsertPointsBuilder};
|
||||
use qdrant_client::Payload;
|
||||
|
||||
client
|
||||
.upsert_points(
|
||||
UpsertPointsBuilder::new(
|
||||
"{collection_name}",
|
||||
vec![PointStruct::new(
|
||||
111,
|
||||
vec![0.1, 0.2, 0.3],
|
||||
Payload::default(),
|
||||
)],
|
||||
)
|
||||
.shard_key_selector("user_1".to_string()),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
|
||||
```java
|
||||
import java.util.List;
|
||||
|
||||
import static io.qdrant.client.PointIdFactory.id;
|
||||
import static io.qdrant.client.ShardKeySelectorFactory.shardKeySelector;
|
||||
import static io.qdrant.client.VectorsFactory.vectors;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.PointStruct;
|
||||
import io.qdrant.client.grpc.Points.UpsertPoints;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.upsertAsync(
|
||||
UpsertPoints.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.addAllPoints(
|
||||
List.of(
|
||||
PointStruct.newBuilder()
|
||||
.setId(id(111))
|
||||
.setVectors(vectors(0.1f, 0.2f, 0.3f))
|
||||
.build()))
|
||||
.setShardKeySelector(shardKeySelector("user_1"))
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "{collection_name}",
|
||||
points: new List<PointStruct>
|
||||
{
|
||||
new() { Id = 111, Vectors = new[] { 0.1f, 0.2f, 0.3f } }
|
||||
},
|
||||
shardKeySelector: new ShardKeySelector { ShardKeys = { new List<ShardKey> { "user_1" } } }
|
||||
);
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.Upsert(context.Background(), &qdrant.UpsertPoints{
|
||||
CollectionName: "{collection_name}",
|
||||
Points: []*qdrant.PointStruct{
|
||||
{
|
||||
Id: qdrant.NewIDNum(111),
|
||||
Vectors: qdrant.NewVectors(0.1, 0.2, 0.3),
|
||||
},
|
||||
},
|
||||
ShardKeySelector: &qdrant.ShardKeySelector{
|
||||
ShardKeys: []*qdrant.ShardKey{
|
||||
qdrant.NewShardKey("user_1"),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
{{< code-snippet path="/documentation/headless/snippets/insert-points/with-custom-shard/" >}}
|
||||
|
||||
<aside role="alert">
|
||||
Using the same point ID across multiple shard keys is <strong>not supported<sup>*</sup></strong> and should be avoided.
|
||||
@@ -1353,7 +1023,7 @@ client
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
import io.qdrant.client.grpc.Points.QueryPoints;
|
||||
import io.qdrant.client.grpc.Points.ReadConsistency;
|
||||
import io.qdrant.client.grpc.Points.ReadConsistencyType;
|
||||
|
||||
@@ -17,65 +17,127 @@ The integration with Qdrant is easy to
|
||||
[configure](https://prometheus.io/docs/prometheus/latest/getting_started/#configure-prometheus-to-monitor-the-sample-targets)
|
||||
with Prometheus and Grafana.
|
||||
|
||||
## Monitoring multi-node clusters
|
||||
## Metrics
|
||||
|
||||
When scraping metrics from multi-node Qdrant clusters, it is important to scrape from
|
||||
each node individually instead of using a load-balanced URL. Otherwise, your metrics will appear inconsistent after each scrape.
|
||||
Qdrant exposes various metrics in Prometheus/OpenMetrics format, commonly used together with Grafana for monitoring.
|
||||
|
||||
## Monitoring in Qdrant Cloud
|
||||
Two endpoints are available:
|
||||
|
||||
Qdrant Cloud offers additional metrics and telemetry that are not available in the open-source version. For more information, see [Qdrant Cloud Monitoring](/documentation/cloud/cluster-monitoring/).
|
||||
- `/metrics` for metrics of a Qdrant node/peer, see [all metrics](#node-metrics-metrics).
|
||||
|
||||
## Exposed metrics
|
||||
|
||||
There are two endpoints avaliable:
|
||||
|
||||
- `/metrics` is the direct endpoint of the underlying Qdrant database node.
|
||||
|
||||
- `/sys_metrics` is a Qdrant cloud-only endpoint that provides additional operational and infrastructure metrics about your cluster, like CPU, memory and disk utilisation, collection metrics and load balancer telemetry. For more information, see [Qdrant Cloud Monitoring](/documentation/cloud/cluster-monitoring/).
|
||||
- `/sys_metrics` (Qdrant Cloud only) for metrics about your cluster, like CPU, memory, disk utilisation, collection metrics and load balancer telemetry. For more information, see [Qdrant Cloud Monitoring](/documentation/cloud/cluster-monitoring/).
|
||||
|
||||
Note that `/metrics` only reports metrics for the peer connected to. It is therefore important to scrape from each peer individually, even if a load balancer is involved.
|
||||
|
||||
### Node metrics `/metrics`
|
||||
|
||||
Each Qdrant server will expose the following metrics.
|
||||
Each Qdrant node will expose the following metrics.
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| ----------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| app_info | gauge | Information about Qdrant server |
|
||||
| app_status_recovery_mode | gauge | If Qdrant is currently started in recovery mode |
|
||||
| collections_total | gauge | Number of collections |
|
||||
| collections_vector_total | gauge | Total number of vectors in all collections |
|
||||
| collections_full_total | gauge | Number of full collections |
|
||||
| collections_aggregated_total | gauge | Number of aggregated collections |
|
||||
| rest_responses_total | counter | Total number of responses through REST API |
|
||||
| rest_responses_fail_total | counter | Total number of failed responses through REST API |
|
||||
| rest_responses_avg_duration_seconds | gauge | Average response duration in REST API |
|
||||
| rest_responses_min_duration_seconds | gauge | Minimum response duration in REST API |
|
||||
| rest_responses_max_duration_seconds | gauge | Maximum response duration in REST API |
|
||||
| grpc_responses_total | counter | Total number of responses through gRPC API |
|
||||
| grpc_responses_fail_total | counter | Total number of failed responses through REST API |
|
||||
| grpc_responses_avg_duration_seconds | gauge | Average response duration in gRPC API |
|
||||
| grpc_responses_min_duration_seconds | gauge | Minimum response duration in gRPC API |
|
||||
| grpc_responses_max_duration_seconds | gauge | Maximum response duration in gRPC API |
|
||||
| cluster_enabled | gauge | Whether the cluster support is enabled. 1 - YES |
|
||||
| memory_active_bytes | gauge | Total number of bytes in active pages allocated by the application. [Reference](https://jemalloc.net/jemalloc.3.html#stats.active) |
|
||||
| memory_allocated_bytes | gauge | Total number of bytes allocated by the application. [Reference](https://jemalloc.net/jemalloc.3.html#stats.allocated) |
|
||||
| memory_metadata_bytes | gauge | Total number of bytes dedicated to allocator metadata. [Reference](https://jemalloc.net/jemalloc.3.html#stats.metadata) |
|
||||
| memory_resident_bytes | gauge | Maximum number of bytes in physically resident data pages mapped. [Reference](https://jemalloc.net/jemalloc.3.html#stats.resident) |
|
||||
| memory_retained_bytes | gauge | Total number of bytes in virtual memory mappings. [Reference](https://jemalloc.net/jemalloc.3.html#stats.retained) |
|
||||
| collection_hardware_metric_cpu | gauge | CPU measurements of a collection (Experimental) |
|
||||
Counters - such as the number of created snapshots - are reset when the node is restarted.
|
||||
|
||||
**Cluster-related metrics**
|
||||
**Application metrics**
|
||||
|
||||
There are also some metrics which are exposed in distributed mode only.
|
||||
| Name | Type | Meaning |
|
||||
| ----------------------------------- | ------- | ------------------------------ |
|
||||
| app_info | gauge | Qdrant server name and version |
|
||||
| app_status_recovery_mode | gauge | If started in recovery mode |
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| -------------------------------- | ------- | ---------------------------------------------------------------------- |
|
||||
| cluster_peers_total | gauge | Total number of cluster peers |
|
||||
| cluster_term | counter | Current cluster term |
|
||||
| cluster_commit | counter | Index of last committed (finalized) operation cluster peer is aware of |
|
||||
| cluster_pending_operations_total | gauge | Total number of pending operations for cluster peer |
|
||||
| cluster_voter | gauge | Whether the cluster peer is a voter or learner. 1 - VOTER |
|
||||
**Collection metrics**
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| ------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| collections_total | gauge | Number of collections |
|
||||
| collection_points | gauge | Number of points, per collection <sup>(v1.16+)</sup> |
|
||||
| collection_vectors | gauge | Number of vectors, per collection and vector name <sup>(v1.16+)</sup> |
|
||||
| collections_vector_total | gauge | Number of vectors in all collections |
|
||||
| collection_indexed_only_excluded_points | gauge | Number of points excluded in [`indexed_only`](/documentation/concepts/search/#search-api) search, per collection and vector name <sup>(v1.16+)</sup> |
|
||||
| collection_active_replicas_min | gauge | Minimum number of active replicas across all collections and shards <sup>(v1.16+)</sup> |
|
||||
| collection_active_replicas_max | gauge | Maximum number of active replicas across all collections and shards <sup>(v1.16+)</sup> |
|
||||
| collection_dead_replicas | gauge | Number of non-active replicas across all collections and shards <sup>(v1.16+)</sup> |
|
||||
| collection_running_optimizations | gauge | Number of running optimization tasks, per collection <sup>(v1.16+)</sup> |
|
||||
| collection_hardware_metric_cpu | counter | CPU measurements of a collection, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
| collection_hardware_metric_payload_io_read | counter | Payload IO read operations measurement, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
| collection_hardware_metric_payload_io_write | counter | Payload IO write operations measurement, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
| collection_hardware_metric_payload_index_io_read | counter | Payload index read operations measurement, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
| collection_hardware_metric_payload_index_io_write | counter | Payload index write operations measurement, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
| collection_hardware_metric_vector_io_read | counter | Vector IO read operations measurement, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
| collection_hardware_metric_vector_io_write | counter | Vector IO write operations measurement, per collection <sup>(v1.13+)</sup> [^metrics-hwreporting] |
|
||||
|
||||
[^metrics-hwreporting]: Only reported if hardware metrics are enabled in the configuration. See `service.hardware_reporting` in the [configuration](/documentation/guides/configuration/).
|
||||
|
||||
**Snapshot metrics**
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| --------------------------------------- | ------- | --------------------------------------------------------------------------- |
|
||||
| snapshot_creation_running | gauge | Number of snapshots being created, per collection <sup>(v1.16+)</sup> |
|
||||
| snapshot_recovery_running | gauge | Number of snapshots being recovered, per collection <sup>(v1.16+)</sup> |
|
||||
| snapshot_created_total | counter | Number of created snapshots since start, per collection <sup>(v1.16+)</sup> |
|
||||
|
||||
**API response metrics**
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| ----------------------------------- | --------- | ------------------------------------------------------------------ |
|
||||
| rest_responses_total | counter | Number of responses through REST API |
|
||||
| rest_responses_fail_total | counter | Number of failed responses through REST API |
|
||||
| rest_responses_avg_duration_seconds | gauge | Average response duration in REST API |
|
||||
| rest_responses_min_duration_seconds | gauge | Minimum response duration in REST API |
|
||||
| rest_responses_max_duration_seconds | gauge | Maximum response duration in REST API |
|
||||
| rest_responses_duration_seconds | histogram | Histogram of response durations in the REST API <sup>(v1.8+)</sup> |
|
||||
| grpc_responses_total | counter | Number of responses through gRPC API |
|
||||
| grpc_responses_fail_total | counter | Number of failed responses through REST API |
|
||||
| grpc_responses_avg_duration_seconds | gauge | Average response duration in gRPC API |
|
||||
| grpc_responses_min_duration_seconds | gauge | Minimum response duration in gRPC API |
|
||||
| grpc_responses_max_duration_seconds | gauge | Maximum response duration in gRPC API |
|
||||
| grpc_responses_duration_seconds | histogram | Histogram of response durations in the gRPC API <sup>(v1.8+)</sup> |
|
||||
|
||||
**Process metrics**
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| ----------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| memory_active_bytes | gauge | Total number of bytes in active pages allocated by the application ([ref](https://jemalloc.net/jemalloc.3.html#stats.active)) |
|
||||
| memory_allocated_bytes | gauge | Total number of bytes allocated by the application ([ref](https://jemalloc.net/jemalloc.3.html#stats.allocated)) |
|
||||
| memory_metadata_bytes | gauge | Total number of bytes dedicated to allocator metadata ([ref](https://jemalloc.net/jemalloc.3.html#stats.metadata)) |
|
||||
| memory_resident_bytes | gauge | Maximum number of bytes in physically resident data pages mapped ([ref](https://jemalloc.net/jemalloc.3.html#stats.resident)) |
|
||||
| memory_retained_bytes | gauge | Total number of bytes in virtual memory mappings ([ref](https://jemalloc.net/jemalloc.3.html#stats.retained)) |
|
||||
| process_threads | gauge | Number of used system threads <sup>(v1.16+)</sup> |
|
||||
| process_open_mmaps | gauge | Number of open memory maps <sup>(v1.16+)</sup> |
|
||||
| system_max_mmaps | gauge | System wide maximum number of open memory maps <sup>(v1.16+)</sup> |
|
||||
| process_open_fds | gauge | Number of open file descriptors <sup>(v1.16+)</sup> |
|
||||
| process_max_fds | gauge | Maximum number of open file descriptors <sup>(v1.16+)</sup> |
|
||||
| process_minor_page_faults_total | counter | Number of minor page faults encountered by the process <sup>(v1.16+)</sup> |
|
||||
| process_major_page_faults_total | counter | Number of major page faults encountered by the process <sup>(v1.16+)</sup> |
|
||||
|
||||
**Cluster metrics (consensus)**
|
||||
|
||||
Metrics reporting the current cluster consensus state of the node. Exposed only
|
||||
when distributed mode is enabled.
|
||||
|
||||
| Name | Type | Meaning |
|
||||
| -------------------------------- | ------- | ----------------------------------------------------------------------- |
|
||||
| cluster_enabled | gauge | If distributed mode is enabled [^metrics-distributed] |
|
||||
| cluster_peers_total | gauge | Number of cluster peers [^metrics-distributed] |
|
||||
| cluster_term | counter | Raft consensus term [^metrics-distributed] |
|
||||
| cluster_commit | counter | Raft consensus commit - last committed operation [^metrics-distributed] |
|
||||
| cluster_pending_operations_total | gauge | Number of pending consensus operations [^metrics-distributed] |
|
||||
| cluster_voter | gauge | If a consensus voter (`1`) or learner (`0`) [^metrics-distributed] |
|
||||
|
||||
[^metrics-distributed]: Only reported if distributed mode (cluster mode) is enabled. Enabled by default in all Qdrant Cloud environments. See `cluster.enabled` in the [configuration](/documentation/guides/configuration/).
|
||||
|
||||
### Metrics configuration
|
||||
|
||||
*Available as of v1.16.0*
|
||||
|
||||
In self-hosted environments you have further configuration options for metrics.
|
||||
|
||||
By default, all Qdrant metrics have no application namespace prefix. You may set
|
||||
a prefix with `service.metrics_prefix` in the
|
||||
[configuration](/documentation/guides/configuration/).
|
||||
|
||||
To achieve this you may use the following environment variable for example:
|
||||
|
||||
```bash
|
||||
QDRANT__SERVICE__METRICS_PREFIX="qdrant_"
|
||||
```
|
||||
|
||||
## Telemetry endpoint
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
title: Multitenancy
|
||||
weight: 12
|
||||
aliases:
|
||||
- ../tutorials/multiple-partitions
|
||||
- /tutorials/multiple-partitions/
|
||||
---
|
||||
# Configure Multitenancy
|
||||
|
||||
<aside role="alert">
|
||||
It is not recommended to create hundreds and thousands of collections per cluster as it increases resource overhead unsustainably. Eventually this will lead to increased costs and at some point performance degradation and cluster instability. In Qdrant Cloud, we limit the amount of collections per cluster to 1000.
|
||||
</aside>
|
||||
|
||||
**How many collections should you create?** In most cases, a single collection per embedding model with payload-based partitioning for different tenants and use cases. This approach is called multitenancy. It is efficient for most users, but requires additional configuration. This document will show you how to set it up.
|
||||
|
||||
**When should you create multiple collections?** When you have a limited number of users and you need isolation. This approach is flexible, but it may be more costly, since creating numerous collections may result in resource overhead. Also, you need to ensure that they do not affect each other in any way, including performance-wise.
|
||||
|
||||
## Partition by payload
|
||||
|
||||
When an instance is shared between multiple users, you may need to partition vectors by user. This is done so that each user can only access their own vectors and can't see the vectors of other users.
|
||||
|
||||
|
||||
<aside role="alert">
|
||||
Note: The key doesn't necessarily need to be named <code>group_id</code>. You can choose a name that best suits your data structure and naming conventions.
|
||||
</aside>
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/insert-points/with-tenant-group-id/" >}}
|
||||
|
||||
2. Use a filter along with `group_id` to filter vectors for each user.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/with-filter-by-group-id/" >}}
|
||||
|
||||
## Calibrate performance
|
||||
|
||||
The speed of indexation may become a bottleneck in this case, as each user's vector will be indexed into the same collection. To avoid this bottleneck, consider _bypassing the construction of a global vector index_ for the entire collection and building it only for individual groups instead.
|
||||
|
||||
By adopting this strategy, Qdrant will index vectors for each user independently, significantly accelerating the process.
|
||||
|
||||
To implement this approach, you should:
|
||||
|
||||
1. Set `payload_m` in the HNSW configuration to a non-zero value, such as 16.
|
||||
2. Set `m` in hnsw config to 0. This will disable building global index for the whole collection.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-collection/with-disabled-global-hnsw/" >}}
|
||||
|
||||
3. Create keyword payload index for `group_id` field.
|
||||
|
||||
<aside role="alert">
|
||||
<code>is_tenant</code> parameter is available as of v1.11.0. Previous versions should use default options for keyword index creation.
|
||||
</aside>
|
||||
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-payload-index/with-group-id-as-tenant/" >}}
|
||||
|
||||
`is_tenant=true` parameter is optional, but specifying it provides storage with additional information about the usage patterns the collection is going to use.
|
||||
When specified, storage structure will be organized in a way to co-locate vectors of the same tenant together, which can significantly improve performance in some cases.
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
One downside to this approach is that global requests (without the `group_id` filter) will be slower since they will necessitate scanning all groups to identify the nearest neighbors.
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Multitenancy
|
||||
weight: 12
|
||||
aliases:
|
||||
- ../tutorials/multiple-partitions
|
||||
- /tutorials/multiple-partitions/
|
||||
- /documentation/guides/multiple-partitions/
|
||||
---
|
||||
# Configure Multitenancy
|
||||
|
||||
<aside role="alert">
|
||||
It is not recommended to create hundreds and thousands of collections per cluster as it increases resource overhead unsustainably. Eventually this will lead to increased costs and at some point performance degradation and cluster instability. In Qdrant Cloud, we limit the amount of collections per cluster to 1000.
|
||||
</aside>
|
||||
|
||||
**How many collections should you create?** In most cases, a single collection per embedding model with payload-based partitioning for different tenants and use cases. This approach is called multitenancy. It is efficient for most users, but requires additional configuration. This document will show you how to set it up.
|
||||
|
||||
**When should you create multiple collections?** When you have a limited number of users and you need isolation. This approach is flexible, but it may be more costly, since creating numerous collections may result in resource overhead. Also, you need to ensure that they do not affect each other in any way, including performance-wise.
|
||||
|
||||
## Partition by payload
|
||||
|
||||
When an instance is shared between multiple users, you may need to partition vectors by user. This is done so that each user can only access their own vectors and can't see the vectors of other users.
|
||||
|
||||
|
||||
<aside role="alert">
|
||||
Note: The key doesn't necessarily need to be named <code>group_id</code>. You can choose a name that best suits your data structure and naming conventions.
|
||||
</aside>
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/insert-points/with-tenant-group-id/" >}}
|
||||
|
||||
2. Use a filter along with `group_id` to filter vectors for each user.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/query-points/with-filter-by-group-id/" >}}
|
||||
|
||||
## Calibrate performance
|
||||
|
||||
The speed of indexation may become a bottleneck in this case, as each user's vector will be indexed into the same collection. To avoid this bottleneck, consider _bypassing the construction of a global vector index_ for the entire collection and building it only for individual groups instead.
|
||||
|
||||
By adopting this strategy, Qdrant will index vectors for each user independently, significantly accelerating the process.
|
||||
|
||||
To implement this approach, you should:
|
||||
|
||||
1. Set `payload_m` in the HNSW configuration to a non-zero value, such as 16.
|
||||
2. Set `m` in hnsw config to 0. This will disable building global index for the whole collection.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-collection/with-disabled-global-hnsw/" >}}
|
||||
|
||||
3. Create keyword payload index for `group_id` field.
|
||||
|
||||
<aside role="alert">
|
||||
<code>is_tenant</code> parameter is available as of v1.11.0. Previous versions should use default options for keyword index creation.
|
||||
</aside>
|
||||
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-payload-index/with-group-id-as-tenant/" >}}
|
||||
|
||||
`is_tenant=true` parameter is optional, but specifying it provides storage with additional information about the usage patterns the collection is going to use.
|
||||
When specified, storage structure will be organized in a way to co-locate vectors of the same tenant together, which can significantly improve performance by utilizing sequential reads during queries.
|
||||
|
||||
|
||||
{{< figure src="/docs/defragmentation.png" alt="Tenants defragmentation with is_tenant" caption="Grouping tenants together by tenant ID, if `is_tenant=true` is used" width="90%" >}}
|
||||
|
||||
|
||||
|
||||
### Limitations
|
||||
|
||||
One downside to this approach is that global requests (without the `group_id` filter) will be slower since they will necessitate scanning all groups to identify the nearest neighbors.
|
||||
|
||||
|
||||
## Tiered multitenancy
|
||||
|
||||
In some real-world applications, tenants might not be equally distributed. For example, a SaaS application might have a few large customers and many small ones.
|
||||
Large tenants might require extended resources and isolation, while small tenants should not create too much overhead.
|
||||
|
||||
One solution to this problem might be to introduce application-level logic to separate tenants into different collections based on their size or resource requirements.
|
||||
There is, however, a downside to this approach: we might not know in advance which tenants will be large and which stay small.
|
||||
In addition, application-level logic increases complexity of the system and requires additional source of truth for tenant placement management.
|
||||
|
||||
To address this problem, in v1.16.0 Qdrant provides a built-in mechanism for tiered multitenancy.
|
||||
|
||||
With tiered multitenancy, you can implement two levels of tenant isolation within a single collection, keeping small tenants together inside a shared Shard, while isolating large tenants into their own dedicated Shards.
|
||||
There are 3 components in Qdrant, that allows you to implement tiered multitenancy:
|
||||
|
||||
- [**User-defined Sharding**](/documentation/guides/distributed_deployment/#user-defined-sharding) allows you to create named Shards within a collection. It allows to isolate large tenants into their own Shards.
|
||||
- **Fallback shards** - a special routing mechanism that allows to route request to either a dedicated Shard (if it exists) or to a shared Fallback Shard. It allows to keep requests unified, without the need to know whether a tenant is dedicated or shared.
|
||||
- **Tenant promotion** - a mechanism that allows to move tenants from the shared Fallback Shard to their own dedicated Shard when they grow large enough. This process is based on Qdrant's internal shard transfer mechanism, which makes promotion completely transparent for the application. Both read and write requests are supported during the promotion process.
|
||||
|
||||
|
||||
{{< figure src="/docs/tenant-promotion.png" alt="Tiered multitenancy with tenant promotion" caption="Tiered multitenancy with tenant promotion" width="90%" >}}
|
||||
|
||||
### Configure tiered multitenancy
|
||||
|
||||
To take advantage of tiered multitenancy, you need to create a collection with user-defined (aka `custom`) sharding and create a Fallback Shard in it.
|
||||
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-collection/with-custom-sharding/" >}}
|
||||
|
||||
Start with creating a fallback Shard, which will be used to store small tenants.
|
||||
Let's name it `default`.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-shard/create-named-shard-default/" >}}
|
||||
|
||||
Since the collection will allow both dedicated and shared tenants, we need still need to configure payload-based tenancy for this collection the same way as described in the [Partition by payload](#partition-by-payload) section above. Namely, we need to create a payload index for the `group_id` field with `is_tenant=true`.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-payload-index/with-group-id-as-tenant/" >}}
|
||||
|
||||
### Query tiered multitenant collection
|
||||
|
||||
Now we can start uploading data into the collection. One important difference from the simple payload-based multitenancy is that now we need to specify the **Shard Key Selector** in each request to route requests to the correct Shard.
|
||||
|
||||
Shard Key Selector will specify two keys:
|
||||
|
||||
- `target` shard - name of the tenant's dedicated Shard (which may or may not exist).
|
||||
- `fallback` shard - name of the shared Fallback Shard (in our case, `default`).
|
||||
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/insert-points/with-tenant-group-id-and-fallback-shard-key/" >}}
|
||||
|
||||
The routing logic will work as follows:
|
||||
|
||||
- If the `target` Shard exists and active, the request will be routed to it.
|
||||
- If the `target` Shard does not exist, the request will be routed to the `fallback` Shard.
|
||||
|
||||
Similarly, when querying points, we need to specify the Shard Key Selector and filter by `group_id`.
|
||||
Note, that filter match value should always match the `target` Shard Key.
|
||||
|
||||
|
||||
### Promote tenant to dedicated Shard
|
||||
|
||||
When a tenant grows large enough, you can promote it to its own dedicated Shard.
|
||||
In order to do that, you first need to create a new Shard for the tenant:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-shard/create-named-shard-for-promotion/" >}}
|
||||
|
||||
Note, that we create a Shard in `Partial` state, since it would still need to transfer data into it.
|
||||
|
||||
To initiate data transfer, there is another API method called `replicate_points`:
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/shard-transfer/with-filter/" >}}
|
||||
|
||||
Once transfer is completed, target Shard will become `Active`, and all requests for the tenant will be routed to it automatically.
|
||||
At this point it is safe to delete the tenant's data from the shared Fallback Shard to free up space.
|
||||
|
||||
|
||||
### Limitations
|
||||
|
||||
- Currently, `fallback` Shard may only contain a single shard ID on its own. That means all small tenants must fit a single peer of the cluser. This restriction will be improved in future releases.
|
||||
- Similar to collections, dedicated Shards introduce some resource overhead. It is not recommended to create more than a thousand dedicated Shards per cluster. Recommended threshold of promoting a tenant is the same as the indexing threshold for a single collection, which is around 20K points.
|
||||
|
||||
@@ -60,6 +60,17 @@ Increase the `ef` and `m` parameters of the HNSW index to improve precision, eve
|
||||
**Note:** The speed of this setup depends on the disk’s IOPS (Input/Output Operations Per Second).</br>
|
||||
You can use [fio](https://gist.github.com/superboum/aaa45d305700a7873a8ebbab1abddf2b) to measure disk IOPS.
|
||||
|
||||
### Inline Storage in HNSW Index
|
||||
|
||||
*Available as of v1.16.0*
|
||||
|
||||
When storing vectors and the HNSW index on disk, you can improve search performance by enabling the `inline_storage` option in the `hnsw_config`.
|
||||
With inline storage, Qdrant stores copies of vectors directly within the HNSW index file.
|
||||
It makes searches faster by reducing the number of IO operations, at the cost of 3-4x increased storage usage.
|
||||
It requires quantization to be enabled.
|
||||
|
||||
{{< code-snippet path="/documentation/headless/snippets/create-collection/with-inline-storage/" >}}
|
||||
|
||||
## 3. High Precision with High-Speed Search
|
||||
|
||||
For scenarios requiring both high speed and high precision, keep as much data in RAM as possible. Apply quantization with re-scoring for tunable accuracy.
|
||||
@@ -112,4 +123,4 @@ By adjusting configurations like vector storage, quantization, and search parame
|
||||
- **High Precision + High Speed:** Keep data in RAM, use quantization with re-scoring.
|
||||
- **Latency vs. Throughput:** Adjust segment numbers based on the priority.
|
||||
|
||||
Choose the strategy that best fits your use case to get the most out of Qdrant’s performance capabilities.
|
||||
Choose the strategy that best fits your use case to get the most out of Qdrant’s performance capabilities.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
```java
|
||||
import static io.qdrant.client.ConditionFactory.matchKeyword;
|
||||
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
|
||||
client
|
||||
.countAsync(
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
This code snippet is used to create a collection with a User Defined Sharding (aka custom sharding) configured.
|
||||
Unlike default auto-sharding, that uses hash-based sharding strategy, user-defined sharding allows to create named Shards and route requests to specific Shards based on application-level logic.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
// ... other collection parameters
|
||||
shardNumber: 1,
|
||||
shardingMethod: ShardingMethod.Custom
|
||||
);
|
||||
```
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
// ... other collection parameters
|
||||
ShardNumber: qdrant.PtrOf(uint32(1)),
|
||||
ShardingMethod: qdrant.ShardingMethod_Custom.Enum(),
|
||||
})
|
||||
```
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}
|
||||
{
|
||||
"shard_number": 1,
|
||||
"sharding_method": "custom"
|
||||
// ... other collection parameters
|
||||
}
|
||||
```
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
```java
|
||||
import static io.qdrant.client.ShardKeyFactory.shardKey;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.ShardingMethod;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
// ... other collection parameters
|
||||
.setShardNumber(1)
|
||||
.setShardingMethod(ShardingMethod.Custom)
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_collection(
|
||||
collection_name="{collection_name}",
|
||||
shard_number=1,
|
||||
sharding_method=models.ShardingMethod.CUSTOM,
|
||||
# ... other collection parameters
|
||||
)
|
||||
```
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateCollectionBuilder, Distance, ShardingMethod, VectorParamsBuilder,
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("{collection_name}")
|
||||
.vectors_config(VectorParamsBuilder::new(300, Distance::Cosine))
|
||||
.shard_number(1)
|
||||
.sharding_method(ShardingMethod::Custom.into()),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createCollection("{collection_name}", {
|
||||
shard_number: 1,
|
||||
sharding_method: "custom",
|
||||
// ... other collection parameters
|
||||
});
|
||||
```
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
When creating a collection with inline storage enabled, the HNSW index stores copies of both the original vectors and quantized vectors within the index file itself.
|
||||
This reduces random disk seeks during search, trading disk space for improved search speed.
|
||||
The `inline_storage` option requires quantization to be enabled and does not support multi-vectors.
|
||||
Set `hnsw_config.inline_storage` to `true` and configure quantization to use this feature.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine, OnDisk = true },
|
||||
quantizationConfig: new QuantizationConfig
|
||||
{
|
||||
Binary = new BinaryQuantization { AlwaysRam = false }
|
||||
},
|
||||
hnswConfig: new HnswConfigDiff { OnDisk = true, InlineStorage = true }
|
||||
);
|
||||
```
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
|
||||
Size: 768,
|
||||
Distance: qdrant.Distance_Cosine,
|
||||
OnDisk: qdrant.PtrOf(true),
|
||||
}),
|
||||
QuantizationConfig: qdrant.NewQuantizationBinary(
|
||||
&qdrant.BinaryQuantization{
|
||||
AlwaysRam: qdrant.PtrOf(false),
|
||||
},
|
||||
),
|
||||
HnswConfig: &qdrant.HnswConfigDiff{
|
||||
OnDisk: qdrant.PtrOf(true),
|
||||
InlineStorage: qdrant.PtrOf(true),
|
||||
},
|
||||
})
|
||||
```
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}
|
||||
{
|
||||
"vectors": {
|
||||
"size": 768,
|
||||
"distance": "Cosine",
|
||||
"on_disk": true
|
||||
},
|
||||
"quantization_config": {
|
||||
"binary": {
|
||||
"always_ram": false
|
||||
}
|
||||
},
|
||||
"hnsw_config": {
|
||||
"on_disk": true,
|
||||
"inline_storage": true
|
||||
}
|
||||
}
|
||||
```
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.BinaryQuantization;
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.Distance;
|
||||
import io.qdrant.client.grpc.Collections.HnswConfigDiff;
|
||||
import io.qdrant.client.grpc.Collections.QuantizationConfig;
|
||||
import io.qdrant.client.grpc.Collections.VectorParams;
|
||||
import io.qdrant.client.grpc.Collections.VectorsConfig;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setVectorsConfig(
|
||||
VectorsConfig.newBuilder()
|
||||
.setParams(
|
||||
VectorParams.newBuilder()
|
||||
.setSize(768)
|
||||
.setDistance(Distance.Cosine)
|
||||
.setOnDisk(true)
|
||||
.build())
|
||||
.build())
|
||||
.setQuantizationConfig(
|
||||
QuantizationConfig.newBuilder()
|
||||
.setBinary(BinaryQuantization.newBuilder().setAlwaysRam(false).build())
|
||||
.build())
|
||||
.setHnswConfig(HnswConfigDiff.newBuilder().setOnDisk(true).setInlineStorage(true).build())
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_collection(
|
||||
collection_name="{collection_name}",
|
||||
vectors_config=models.VectorParams(
|
||||
size=768, distance=models.Distance.COSINE, on_disk=True
|
||||
),
|
||||
quantization_config=models.BinaryQuantization(
|
||||
binary=models.BinaryQuantizationConfig(always_ram=False),
|
||||
),
|
||||
hnsw_config=models.HnswConfigDiff(on_disk=True, inline_storage=True),
|
||||
)
|
||||
```
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
BinaryQuantizationBuilder, CreateCollectionBuilder, Distance, HnswConfigDiffBuilder,
|
||||
VectorParamsBuilder,
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("{collection_name}")
|
||||
.vectors_config(VectorParamsBuilder::new(768, Distance::Cosine).on_disk(true))
|
||||
.quantization_config(BinaryQuantizationBuilder::new(false))
|
||||
.hnsw_config(
|
||||
HnswConfigDiffBuilder::default()
|
||||
.on_disk(true)
|
||||
.inline_storage(true),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createCollection("{collection_name}", {
|
||||
vectors: {
|
||||
size: 768,
|
||||
distance: "Cosine",
|
||||
on_disk: true,
|
||||
},
|
||||
quantization_config: {
|
||||
binary: {
|
||||
always_ram: false,
|
||||
},
|
||||
},
|
||||
hnsw_config: {
|
||||
on_disk: true,
|
||||
inline_storage: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
+1
@@ -0,0 +1 @@
|
||||
This code snippet is used to create a collection with a specific vector configuration and additional metadata. The metadata is provided as a JSON object, allowing you to store custom information about the collection. In this example, we add two metadata fields.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
```bash
|
||||
curl -X PUT http://localhost:6333/collections/{collection_name} \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"vectors": {
|
||||
"size": 300,
|
||||
"distance": "Cosine"
|
||||
},
|
||||
"metadata": {
|
||||
"my-metadata-field": "value-1",
|
||||
"another-field": 123
|
||||
}
|
||||
}'
|
||||
```
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateCollectionAsync(
|
||||
collectionName: "{collection_name}",
|
||||
vectorsConfig: new VectorParams { Size = 100, Distance = Distance.Cosine },
|
||||
metadata: new()
|
||||
{
|
||||
["my-metadata-field"] = "value-1",
|
||||
["another-field"] = 123
|
||||
}
|
||||
);
|
||||
```
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
|
||||
Size: 100,
|
||||
Distance: qdrant.Distance_Cosine,
|
||||
}),
|
||||
Metadata: qdrant.NewValueMap(map[string]any{
|
||||
"my-metadata-field": "value-1",
|
||||
"another-field": 123,
|
||||
}),
|
||||
})
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}
|
||||
{
|
||||
"vectors": {
|
||||
"size": 300,
|
||||
"distance": "Cosine"
|
||||
},
|
||||
"metadata": {
|
||||
"my-metadata-field": "value-1",
|
||||
"another-field": 123
|
||||
}
|
||||
}
|
||||
```
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
```java
|
||||
import java.util.Map;
|
||||
|
||||
import static io.qdrant.client.ValueFactory.value;
|
||||
|
||||
import io.qdrant.client.grpc.Collections.CreateCollection;
|
||||
import io.qdrant.client.grpc.Collections.Distance;
|
||||
import io.qdrant.client.grpc.Collections.VectorParams;
|
||||
import io.qdrant.client.grpc.Collections.VectorsConfig;
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
|
||||
QdrantClient client = new QdrantClient(
|
||||
QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.createCollectionAsync(
|
||||
CreateCollection.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setVectorsConfig(
|
||||
VectorsConfig.newBuilder()
|
||||
.setParams(
|
||||
VectorParams.newBuilder()
|
||||
.setDistance(Distance.Cosine)
|
||||
.setSize(100)
|
||||
.build())
|
||||
.build())
|
||||
.putAllMetadata(
|
||||
Map.of(
|
||||
"my-metadata-field", value("value-1"),
|
||||
"another-field", value(123)))
|
||||
.build())
|
||||
.get();
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_collection(
|
||||
collection_name="{collection_name}",
|
||||
metadata={
|
||||
"my-metadata-field": "value-1",
|
||||
"another-field": 123
|
||||
},
|
||||
)
|
||||
```
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{CreateCollectionBuilder, Distance, VectorParamsBuilder};
|
||||
use qdrant_client::Qdrant;
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
|
||||
let mut metadata: HashMap<String, Value> = HashMap::new();
|
||||
metadata.insert("my-metadata-field".to_string(), json!("value-1"));
|
||||
metadata.insert("another-field".to_string(), json!(123));
|
||||
|
||||
|
||||
client
|
||||
.create_collection(
|
||||
CreateCollectionBuilder::new("{collection_name}")
|
||||
.vectors_config(VectorParamsBuilder::new(100, Distance::Cosine))
|
||||
.metadata(metadata),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createCollection("{collection_name}", {
|
||||
vectors: { size: 100, distance: "Cosine" },
|
||||
metadata: {
|
||||
"my-metadata-field": "value-1",
|
||||
"another-field": 123
|
||||
}
|
||||
});
|
||||
```
|
||||
+1
@@ -0,0 +1 @@
|
||||
This code snippet demonstrates how to create a full-text index with ASCII folding support for a specified field in a collection. The index configuration includes details such as the field name, type (text), tokenizer (word), and whether to ASCII-fold tokens. This setup enables filtering points based on the presence of specific words while ignoring diacritics in the field, allowing for efficient full-text search functionality within the payload.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreatePayloadIndexAsync(
|
||||
collectionName: "{collection_name}",
|
||||
fieldName: "name_of_the_field_to_index",
|
||||
schemaType: PayloadSchemaType.Text,
|
||||
indexParams: new PayloadIndexParams
|
||||
{
|
||||
TextIndexParams = new TextIndexParams
|
||||
{
|
||||
Tokenizer = TokenizerType.Word,
|
||||
Lowercase = true,
|
||||
AsciiFolding = true,
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
FieldName: "name_of_the_field_to_index",
|
||||
FieldType: qdrant.FieldType_FieldTypeText.Enum(),
|
||||
FieldIndexParams: qdrant.NewPayloadIndexParamsText(
|
||||
&qdrant.TextIndexParams{
|
||||
Tokenizer: qdrant.TokenizerType_Word,
|
||||
Lowercase: qdrant.PtrOf(true),
|
||||
AsciiFolding: qdrant.PtrOf(true),
|
||||
}),
|
||||
})
|
||||
```
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}/index
|
||||
{
|
||||
"field_name": "name_of_the_field_to_index",
|
||||
"field_schema": {
|
||||
"type": "text",
|
||||
"tokenizer": "word",
|
||||
"ascii_folding": true
|
||||
}
|
||||
}
|
||||
```
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.PayloadIndexParams;
|
||||
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
|
||||
import io.qdrant.client.grpc.Collections.TextIndexParams;
|
||||
import io.qdrant.client.grpc.Collections.TokenizerType;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.createPayloadIndexAsync(
|
||||
"{collection_name}",
|
||||
"name_of_the_field_to_index",
|
||||
PayloadSchemaType.Text,
|
||||
PayloadIndexParams.newBuilder()
|
||||
.setTextIndexParams(
|
||||
TextIndexParams.newBuilder()
|
||||
.setTokenizer(TokenizerType.Word)
|
||||
.setLowercase(true)
|
||||
.setAsciiFolding(true)
|
||||
.build())
|
||||
.build(),
|
||||
null,
|
||||
null,
|
||||
null)
|
||||
.get();
|
||||
```
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_payload_index(
|
||||
collection_name="{collection_name}",
|
||||
field_name="name_of_the_field_to_index",
|
||||
field_schema=models.TextIndexParams(
|
||||
type="text",
|
||||
tokenizer=models.TokenizerType.WORD,
|
||||
ascii_folding=True,
|
||||
),
|
||||
)
|
||||
```
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateFieldIndexCollectionBuilder,
|
||||
TextIndexParamsBuilder,
|
||||
FieldType,
|
||||
TokenizerType,
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
let text_index_params = TextIndexParamsBuilder::new(TokenizerType::Word)
|
||||
.ascii_folding(true);
|
||||
|
||||
client
|
||||
.create_field_index(
|
||||
CreateFieldIndexCollectionBuilder::new(
|
||||
"{collection_name}",
|
||||
"name_of_the_field_to_index",
|
||||
FieldType::Text,
|
||||
).field_index_params(text_index_params.build()),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createPayloadIndex("{collection_name}", {
|
||||
field_name: "name_of_the_field_to_index",
|
||||
field_schema: {
|
||||
type: "text",
|
||||
tokenizer: "word",
|
||||
ascii_folding: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
+1
@@ -0,0 +1 @@
|
||||
This code snippet demonstrates how to configure a full-text index for case-sensitive match support for a specified field in a collection. The index configuration includes details such as the field name, type (text), tokenizer (word), and whether to convert tokens to lowercase. Lowercasing is enabled by default, enabling case-sensitive matching. This setup disables lowercasing, enabling the filtering og points based on the presence of exact words in the field.
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreatePayloadIndexAsync(
|
||||
collectionName: "{collection_name}",
|
||||
fieldName: "name_of_the_field_to_index",
|
||||
schemaType: PayloadSchemaType.Text,
|
||||
indexParams: new PayloadIndexParams
|
||||
{
|
||||
TextIndexParams = new TextIndexParams
|
||||
{
|
||||
Tokenizer = TokenizerType.Word,
|
||||
Lowercase = true,
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateFieldIndex(context.Background(), &qdrant.CreateFieldIndexCollection{
|
||||
CollectionName: "{collection_name}",
|
||||
FieldName: "name_of_the_field_to_index",
|
||||
FieldType: qdrant.FieldType_FieldTypeText.Enum(),
|
||||
FieldIndexParams: qdrant.NewPayloadIndexParamsText(
|
||||
&qdrant.TextIndexParams{
|
||||
Tokenizer: qdrant.TokenizerType_Word,
|
||||
Lowercase: qdrant.PtrOf(true),
|
||||
}),
|
||||
})
|
||||
```
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}/index
|
||||
{
|
||||
"field_name": "name_of_the_field_to_index",
|
||||
"field_schema": {
|
||||
"type": "text",
|
||||
"tokenizer": "word",
|
||||
"lowercase": false
|
||||
}
|
||||
}
|
||||
```
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
```java
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.PayloadIndexParams;
|
||||
import io.qdrant.client.grpc.Collections.PayloadSchemaType;
|
||||
import io.qdrant.client.grpc.Collections.TextIndexParams;
|
||||
import io.qdrant.client.grpc.Collections.TokenizerType;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client
|
||||
.createPayloadIndexAsync(
|
||||
"{collection_name}",
|
||||
"name_of_the_field_to_index",
|
||||
PayloadSchemaType.Text,
|
||||
PayloadIndexParams.newBuilder()
|
||||
.setTextIndexParams(
|
||||
TextIndexParams.newBuilder()
|
||||
.setTokenizer(TokenizerType.Word)
|
||||
.setLowercase(true)
|
||||
.build())
|
||||
.build(),
|
||||
null,
|
||||
null,
|
||||
null)
|
||||
.get();
|
||||
```
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_payload_index(
|
||||
collection_name="{collection_name}",
|
||||
field_name="name_of_the_field_to_index",
|
||||
field_schema=models.TextIndexParams(
|
||||
type="text",
|
||||
tokenizer=models.TokenizerType.WORD,
|
||||
lowercase=False,
|
||||
),
|
||||
)
|
||||
```
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateFieldIndexCollectionBuilder,
|
||||
TextIndexParamsBuilder,
|
||||
FieldType,
|
||||
TokenizerType,
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
let text_index_params = TextIndexParamsBuilder::new(TokenizerType::Word)
|
||||
.lowercase(false);
|
||||
|
||||
client
|
||||
.create_field_index(
|
||||
CreateFieldIndexCollectionBuilder::new(
|
||||
"{collection_name}",
|
||||
"name_of_the_field_to_index",
|
||||
FieldType::Text,
|
||||
).field_index_params(text_index_params.build()),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createPayloadIndex("{collection_name}", {
|
||||
field_name: "name_of_the_field_to_index",
|
||||
field_schema: {
|
||||
type: "text",
|
||||
tokenizer: "word",
|
||||
lowercase: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
This code snippet creates a named shard with the name "default" in a Qdrant collection.
|
||||
Collection is required to be configured with `custom` sharding method to support named shards.
|
||||
Once created, named shard will receive all requests that specify its name in the shard key selector.
|
||||
|
||||
If no named shard is specified in the request, request will be broadcasted to all shards in the collection.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateShardKeyAsync(
|
||||
"{collection_name}",
|
||||
new CreateShardKey { ShardKey = new ShardKey { Keyword = "default", } }
|
||||
);
|
||||
```
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateShardKey(context.Background(), "{collection_name}", &qdrant.CreateShardKey{
|
||||
ShardKey: qdrant.NewShardKey("default"),
|
||||
})
|
||||
```
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}/shards
|
||||
{
|
||||
"shard_key": "default"
|
||||
}
|
||||
```
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
```java
|
||||
import static io.qdrant.client.ShardKeyFactory.shardKey;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKey;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKeyRequest;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.createShardKeyAsync(CreateShardKeyRequest.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setRequest(CreateShardKey.newBuilder()
|
||||
.setShardKey(shardKey("default"))
|
||||
.build())
|
||||
.build()).get();
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_shard_key("{collection_name}", "default")
|
||||
```
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateShardKeyBuilder, CreateShardKeyRequestBuilder
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_shard_key(
|
||||
CreateShardKeyRequestBuilder::new("{collection_name}")
|
||||
.request(CreateShardKeyBuilder::default().shard_key("default".to_string())),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createShardKey("{collection_name}", {
|
||||
shard_key: "default"
|
||||
});
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
This code snippet creates a named shard with the name "user_1" in a Qdrant collection.
|
||||
This shard is intended to be used as a dedicated shard for a specific tenant or user, allowing for better data isolation and management. Creation of the shard specifies initial state as `Partial`, as it needs to be populated with data before it can serve requests.
|
||||
|
||||
Collection is required to be configured with `custom` sharding method to support named shards.
|
||||
Once created, named shard will receive all requests that specify its name in the shard key selector.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateShardKeyAsync(
|
||||
"{collection_name}",
|
||||
new CreateShardKey {
|
||||
ShardKey = new ShardKey { Keyword = "default" },
|
||||
InitialState = ReplicaState.Partial
|
||||
}
|
||||
);
|
||||
```
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateShardKey(
|
||||
context.Background(),
|
||||
"{collection_name}",
|
||||
&qdrant.CreateShardKey{
|
||||
ShardKey: qdrant.NewShardKey("default"),
|
||||
InitialState: qdrant.ReplicaState_PARTIAL,
|
||||
}
|
||||
)
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}/shards
|
||||
{
|
||||
"shard_key": "user_1",
|
||||
"initial_state": "Partial"
|
||||
}
|
||||
```
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
```java
|
||||
import static io.qdrant.client.ShardKeyFactory.shardKey;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKey;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKeyRequest;
|
||||
import io.qdrant.client.grpc.Collections.ReplicaState;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.createShardKeyAsync(CreateShardKeyRequest.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setRequest(CreateShardKey.newBuilder()
|
||||
.setShardKey(shardKey("default"))
|
||||
.setInitialState(ReplicaState.PARTIAL)
|
||||
.build())
|
||||
.build()).get();
|
||||
```
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_shard_key(
|
||||
"{collection_name}",
|
||||
shard_key="user_1",
|
||||
initial_state=models.ReplicaState.PARTIAL
|
||||
)
|
||||
```
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateShardKeyBuilder, CreateShardKeyRequestBuilder
|
||||
};
|
||||
use qdrant_client::qdrant::ReplicaState;
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_shard_key(
|
||||
CreateShardKeyRequestBuilder::new("{collection_name}")
|
||||
.request(
|
||||
CreateShardKeyBuilder::default()
|
||||
.shard_key("user_1".to_string())
|
||||
.initial_state(ReplicaState::Partial)
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createShardKey("{collection_name}", {
|
||||
shard_key: "default",
|
||||
initial_state: "Partial"
|
||||
});
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
This code snippet creates a named shard in a Qdrant collection.
|
||||
Collection is required to be configured with `custom` sharding method to support named shards.
|
||||
Once created, named shard will receive all requests that specify its name in the shard key selector.
|
||||
|
||||
If no named shard is specified in the request, request will be broadcasted to all shards in the collection.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.CreateShardKeyAsync(
|
||||
"{collection_name}",
|
||||
new CreateShardKey { ShardKey = new ShardKey { Keyword = "{shard_key}", } }
|
||||
);
|
||||
```
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.CreateShardKey(context.Background(), "{collection_name}", &qdrant.CreateShardKey{
|
||||
ShardKey: qdrant.NewShardKey("{shard_key}"),
|
||||
})
|
||||
```
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}/shards
|
||||
{
|
||||
"shard_key": "{shard_key}"
|
||||
}
|
||||
```
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
```java
|
||||
import static io.qdrant.client.ShardKeyFactory.shardKey;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKey;
|
||||
import io.qdrant.client.grpc.Collections.CreateShardKeyRequest;
|
||||
|
||||
QdrantClient client =
|
||||
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
|
||||
|
||||
client.createShardKeyAsync(CreateShardKeyRequest.newBuilder()
|
||||
.setCollectionName("{collection_name}")
|
||||
.setRequest(CreateShardKey.newBuilder()
|
||||
.setShardKey(shardKey("{shard_key}"))
|
||||
.build())
|
||||
.build()).get();
|
||||
```
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
```python
|
||||
from qdrant_client import QdrantClient, models
|
||||
|
||||
client = QdrantClient(url="http://localhost:6333")
|
||||
|
||||
client.create_shard_key("{collection_name}", "{shard_key}")
|
||||
```
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::{
|
||||
CreateShardKeyBuilder, CreateShardKeyRequestBuilder
|
||||
};
|
||||
use qdrant_client::Qdrant;
|
||||
|
||||
let client = Qdrant::from_url("http://localhost:6334").build()?;
|
||||
|
||||
client
|
||||
.create_shard_key(
|
||||
CreateShardKeyRequestBuilder::new("{collection_name}")
|
||||
.request(CreateShardKeyBuilder::default().shard_key("{shard_key}".to_string())),
|
||||
)
|
||||
.await?;
|
||||
```
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
```typescript
|
||||
import { QdrantClient } from "@qdrant/js-client-rest";
|
||||
|
||||
const client = new QdrantClient({ host: "localhost", port: 6333 });
|
||||
|
||||
client.createShardKey("{collection_name}", {
|
||||
shard_key: "{shard_key}"
|
||||
});
|
||||
```
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
```java
|
||||
import static io.qdrant.client.ConditionFactory.matchKeyword;
|
||||
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
|
||||
client
|
||||
.deleteAsync(
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import static io.qdrant.client.ConditionFactory.matchKeyword;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
import io.qdrant.client.grpc.Points.SearchMatrixPoints;
|
||||
|
||||
QdrantClient client =
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import static io.qdrant.client.ConditionFactory.matchKeyword;
|
||||
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import io.qdrant.client.grpc.Points.Filter;
|
||||
import io.qdrant.client.grpc.Common.Filter;
|
||||
import io.qdrant.client.grpc.Points.SearchMatrixPoints;
|
||||
|
||||
QdrantClient client =
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
import static io.qdrant.client.ConditionFactory.datetimeRange;
|
||||
|
||||
import com.google.protobuf.Timestamp;
|
||||
import io.qdrant.client.grpc.Points.DatetimeRange;
|
||||
import io.qdrant.client.grpc.Common.DatetimeRange;
|
||||
import java.time.Instant;
|
||||
|
||||
long gt = Instant.parse("2023-02-08T10:49:00Z").getEpochSecond();
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
This code snippet sets up a field condition to search for one or more query terms within a text field. In this case, a special `text_any` match is defined with the target text being "good cheap". A text field is considered a match if it contains any of the search terms. The behavior may vary depending on the configuration of the full-text index for the field. If there is no full-text index configured for the field, the condition will work as an exact substring match.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
```csharp
|
||||
using static Qdrant.Client.Grpc.Conditions;
|
||||
|
||||
MatchTextAny("description", "good cheap");
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
```go
|
||||
import "github.com/qdrant/go-client/qdrant"
|
||||
|
||||
qdrant.NewMatchTextAny("description", "good cheap")
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
```java
|
||||
import static io.qdrant.client.ConditionFactory.matchTextAny;
|
||||
|
||||
matchTextAny("description", "good cheap");
|
||||
```
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
```json
|
||||
{
|
||||
"key": "description",
|
||||
"match": {
|
||||
"text_any": "good cheap"
|
||||
}
|
||||
}
|
||||
```
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
```python
|
||||
models.FieldCondition(
|
||||
key="description",
|
||||
match=models.MatchTextAny(text_any="good cheap"),
|
||||
)
|
||||
```
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
```rust
|
||||
use qdrant_client::qdrant::Condition;
|
||||
|
||||
Condition::matches_text_any("description", "good cheap")
|
||||
```
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
```typescript
|
||||
{
|
||||
key: 'description',
|
||||
match: {text_any: 'good cheap'}
|
||||
}
|
||||
```
|
||||
+1
-1
@@ -1 +1 @@
|
||||
This code snippet sets up a field condition to search for a specific substring or phrase within a text field. In this case, a special `text` match is defined with the target text being "good cheap". The behavior may vary depending on the configuration of the full-text index for the field. If there is no full-text index configured for the field, the condition will work as an exact substring match.
|
||||
This code snippet sets up a field condition to search for multiple query terms within a text field. In this case, a special `text` match is defined with the target text being "good cheap". A text field is only considered a match if it contains all of the search terms. The behavior may vary depending on the configuration of the full-text index for the field. If there is no full-text index configured for the field, the condition will work as an exact substring match.
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
```java
|
||||
import static io.qdrant.client.ConditionFactory.geoPolygon;
|
||||
|
||||
import io.qdrant.client.grpc.Points.GeoLineString;
|
||||
import io.qdrant.client.grpc.Points.GeoPoint;
|
||||
import io.qdrant.client.grpc.Common.GeoPoint;
|
||||
import io.qdrant.client.grpc.Common.GeoLineString;
|
||||
|
||||
geoPolygon(
|
||||
"location",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
```java
|
||||
import static io.qdrant.client.ConditionFactory.valuesCount;
|
||||
|
||||
import io.qdrant.client.grpc.Points.ValuesCount;
|
||||
import io.qdrant.client.grpc.Common.ValuesCount;
|
||||
|
||||
valuesCount("comments", ValuesCount.newBuilder().setGt(2).build());
|
||||
```
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
This code snippet demonstrates how to insert a point with conditionally. Update condition is represented by a filter, if the filter matches existing points, the point will be updated. If condition doesn't match the point, the upsert operation will be ignored. If the point doesn't exist, it will be inserted as a new point.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
```csharp
|
||||
using Qdrant.Client;
|
||||
using Qdrant.Client.Grpc;
|
||||
using static Qdrant.Client.Grpc.Conditions;
|
||||
|
||||
var client = new QdrantClient("localhost", 6334);
|
||||
|
||||
await client.UpsertAsync(
|
||||
collectionName: "{collection_name}",
|
||||
points: new List<PointStruct>
|
||||
{
|
||||
new PointStruct
|
||||
{
|
||||
Id = 1,
|
||||
Vectors = new[] { 0.05f, 0.61f, 0.76f, 0.74f },
|
||||
Payload = {
|
||||
["city"] = "Berlin",
|
||||
["price"] = 1.99,
|
||||
["version"] = 3
|
||||
}
|
||||
}
|
||||
},
|
||||
updateFilter: Match("version", 2)
|
||||
);
|
||||
```
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qdrant/go-client/qdrant"
|
||||
)
|
||||
|
||||
client, err := qdrant.NewClient(&qdrant.Config{
|
||||
Host: "localhost",
|
||||
Port: 6334,
|
||||
})
|
||||
|
||||
client.Upsert(context.Background(), &qdrant.UpsertPoints{
|
||||
CollectionName: "{collection_name}",
|
||||
Points: []*qdrant.PointStruct{
|
||||
{
|
||||
Id: qdrant.NewIDNum(1),
|
||||
Vectors: qdrant.NewVectors(0.05, 0.61, 0.76, 0.74),
|
||||
Payload: qdrant.NewValueMap(map[string]any{
|
||||
"city": "Berlin", "price": 1.99, "version": 3}),
|
||||
},
|
||||
},
|
||||
UpdateFilter: &qdrant.Filter{
|
||||
Must: []*qdrant.Condition{
|
||||
qdrant.NewMatchInt("version", 2),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
```http
|
||||
PUT /collections/{collection_name}/points
|
||||
{
|
||||
"points": [
|
||||
{
|
||||
"id": 1,
|
||||
"vector": [0.05, 0.61, 0.76, 0.74],
|
||||
"payload": {
|
||||
"city": "Berlin",
|
||||
"price": 1.99,
|
||||
"version": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"update_filter": {
|
||||
"must": [
|
||||
{
|
||||
"key": "version",
|
||||
"match": {
|
||||
"value": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user