Filtrare
Con Qdrant, puoi impostare condizioni per cercare o recuperare punti, il che significa che puoi filtrare per attributo oltre alle ricerche di similarità per vettori, simile all'impostazione delle condizioni SQL where
. Ad esempio, puoi impostare condizioni per il payload e l'id
del punto.
È importante impostare condizioni aggiuntive quando non tutte le caratteristiche di un oggetto possono essere espresse in un embedding. Ad esempio, vari requisiti aziendali come disponibilità di inventario, posizione dell'utente o intervallo di prezzo previsto.
Condizioni di Filtraggio
Qdrant consente di combinare condizioni in clausole. Le clausole sono diverse operazioni logiche, come OR
, AND
e NOT
. Le clausole possono essere annidate in modo ricorsivo l'una dentro l'altra, in modo da poter ricreare qualsiasi espressione booleana.
Diamo un'occhiata alle clausole implementate in Qdrant.
Supponiamo di avere un insieme di punti con payload:
[
{ "id": 1, "city": "Londra", "color": "verde" },
{ "id": 2, "city": "Londra", "color": "rosso" },
{ "id": 3, "city": "Londra", "color": "blu" },
{ "id": 4, "city": "Berlino", "color": "rosso" },
{ "id": 5, "city": "Mosca", "color": "verde" },
{ "id": 6, "city": "Mosca", "color": "blu" }
]
Deve
Esempio:
POST /collections/{nome_collezione}/points/scroll
{
"filter": {
"must": [
{ "key": "city", "match": { "value": "Londra" } },
{ "key": "color", "match": { "value": "rosso" } }
]
}
...
}
Il punto filtrato sarà:
[{ "id": 2, "city": "Londra", "color": "rosso" }]
Utilizzando must
, la clausola è true
solo se ciascuna condizione elencata in must
è soddisfatta. In questo senso, must
è equivalente all'operatore AND
.
Dovrebbe
Should
è simile all'operatore OR
in SQL.
Esempio:
POST /collections/{nome_collezione}/points/scroll
{
"filter": {
"should": [
{ "key": "city", "match": { "value": "Londra" } },
{ "key": "color", "match": { "value": "rosso" } }
]
}
...
}
I punti filtrati saranno:
[
{ "id": 1, "city": "Londra", "color": "verde" },
{ "id": 2, "city": "Londra", "color": "rosso" },
{ "id": 3, "city": "Londra", "color": "blu" },
{ "id": 4, "city": "Berlino", "color": "rosso" }
]
Utilizzando should
, la clausola è true
finché almeno una delle condizioni elencate in should
è soddisfatta. In questo senso, should
è equivalente all'operatore OR
.
non_deve
Esempio:
POST /collections/{nome_collezione}/points/scroll
{
"filter": {
"must_not": [
{ "key": "city", "match": { "value": "Londra" } },
{ "key": "color", "match": { "value": "rosso" } }
]
}
...
}
I punti filtrati saranno:
[
{ "id": 5, "city": "Mosca", "color": "verde" },
{ "id": 6, "city": "Mosca", "color": "blu" }
]
Utilizzando must_not
, la sotto-clausola è true
solo se nessuna delle condizioni elencate in must_not
è soddisfatta. In questo senso, must_not
è equivalente all'espressione (NOT A) AND (NOT B) AND (NOT C)
.
Combinazione di Condizioni
È possibile utilizzare contemporaneamente più condizioni:
POST /collections/{collection_name}/points/scroll
{
"filter": {
"must": [
{ "key": "city", "match": { "value": "London" } }
],
"must_not": [
{ "key": "color", "match": { "value": "red" } }
]
}
...
}
I punti filtrati saranno:
[
{ "id": 1, "city": "London", "color": "green" },
{ "id": 3, "city": "London", "color": "blue" }
]
In questo caso, le condizioni sono combinate utilizzando AND
.
Inoltre, le condizioni possono essere annidate in modo ricorsivo. Ad esempio:
POST /collections/{collection_name}/points/scroll
{
"filter": {
"must_not": [
{
"must": [
{ "key": "city", "match": { "value": "London" } },
{ "key": "color", "match": { "value": "red" } }
]
}
]
}
...
}
I punti filtrati saranno:
[
{ "id": 1, "city": "London", "color": "green" },
{ "id": 3, "city": "London", "color": "blue" },
{ "id": 4, "city": "Berlin", "color": "red" },
{ "id": 5, "city": "Moscow", "color": "green" },
{ "id": 6, "city": "Moscow", "color": "blue" }
]
Filtraggio delle Condizioni
Nella richiesta, diversi tipi di valori corrispondono a diversi tipi di interrogazioni che possono essere applicate ad essi. Esaminiamo le varianti di condizioni esistenti e i tipi di dati a cui si applicano.
Corrispondenza
{
"key": "color",
"match": {
"value": "red"
}
}
Per altri tipi, le condizioni di corrispondenza sono esattamente le stesse, solo con tipi diversi utilizzati:
{
"key": "count",
"match": {
"value": 0
}
}
La condizione più semplice controlla se il valore memorizzato è uguale al valore fornito. Se sono memorizzati più valori, almeno uno di essi dovrebbe soddisfare la condizione. Ciò può essere applicato a payload di parole chiave, interi e booleani.
Qualsiasi Corrispondenza
Disponibile dalla versione v1.1.0
Se si desidera verificare se il valore memorizzato è uno tra più valori, è possibile utilizzare la condizione di qualsiasi corrispondenza. La corrispondenza di qualsiasi tratta il valore fornito come un'operazione logica OR. Può anche essere descritta come l'operatore IN
.
Si può applicare ai payload di parole chiave e interi.
Esempio:
{
"key": "color",
"match": {
"any": ["black", "yellow"]
}
}
In questo esempio, se il valore memorizzato è black
o yellow
, allora la condizione sarà soddisfatta.
Se il valore memorizzato è un array, dovrebbe avere almeno un valore che corrisponda ad uno dei valori forniti. Ad esempio, se il valore memorizzato è ["black", "green"]
, allora la condizione sarà soddisfatta perché "black"
è in ["black", "yellow"]
.
Esclusione della Corrispondenza
Disponibile dalla versione v1.2.0
Se si desidera verificare se il valore memorizzato non è nessuno dei valori specificati, è possibile utilizzare la condizione di esclusione della corrispondenza. La corrispondenza di esclusione tratta il valore fornito come un'operazione logica NOR. Può anche essere descritta come l'operatore NOT IN
.
Si può applicare ai payload di parole chiave e interi.
Esempio:
{
"key": "color",
"match": {
"except": ["black", "yellow"]
}
}
In questo esempio, se il valore memorizzato non è né black
né yellow
, allora la condizione sarà soddisfatta.
Se il valore memorizzato è un array, dovrebbe avere almeno un valore che non corrisponda ad uno dei valori forniti. Ad esempio, se il valore memorizzato è ["black", "green"]
, allora la condizione sarà soddisfatta perché "green"
non corrisponde a "black"
o "yellow"
.
Chiavi nidificate
Disponibile dalla versione v1.1.0 in poi
Poiché il payload è un oggetto JSON arbitrario, potresti avere bisogno di filtrare campi nidificati.
Per comodità, utilizziamo una sintassi simile al progetto Jq.
Supponiamo di avere un insieme di punti con il seguente payload:
[
{
"id": 1,
"country": {
"name": "Germania",
"cities": [
{
"name": "Berlino",
"population": 3.7,
"sightseeing": ["Porta di Brandeburgo", "Reichstag"]
},
{
"name": "Monaco",
"population": 1.5,
"sightseeing": ["Marienplatz", "Olympiapark"]
}
]
}
},
{
"id": 2,
"country": {
"name": "Giappone",
"cities": [
{
"name": "Tokyo",
"population": 9.3,
"sightseeing": ["Torre di Tokyo", "Tokyo Skytree"]
},
{
"name": "Osaka",
"population": 2.7,
"sightseeing": ["Castello di Osaka", "Universal Studios Japan"]
}
]
}
}
]
Puoi utilizzare la notazione puntata per cercare campi nidificati.
POST /collections/{collection_name}/points/scroll
{
"filter": {
"should": [
{
"key": "country.name",
"match": {
"value": "Germania"
}
}
]
}
}
Puoi anche utilizzare la sintassi [ ]
per cercare l'array proiettando i valori interni.
POST /collections/{collection_name}/points/scroll
{
"filter": {
"should": [
{
"key": "country.cities[].population",
"range": {
"gte": 9.0,
}
}
]
}
}
Questa query restituisce solo il punto con id 2, poiché solo il Giappone ha una città con una popolazione maggiore di 9.0.
I campi nidificati possono anche essere un array.
POST /collections/{collection_name}/points/scroll
{
"filter": {
"should": [
{
"key": "country.cities[].sightseeing",
"match": {
"value": "Castello di Osaka"
}
}
]
}
}
Questa query restituisce solo il punto con id 2, poiché solo il Giappone ha una città con un luogo di interesse che include "Castello di Osaka".
Filtraggio dell'oggetto nidificato
Disponibile dalla versione 1.2.0
Per impostazione predefinita, le condizioni prendono in considerazione l'intero payload di un punto.
Ad esempio, dati i due punti nel payload di seguito:
[
{
"id": 1,
"dinosaur": "t-rex",
"diet": [
{ "food": "foglie", "likes": false},
{ "food": "carne", "likes": true}
]
},
{
"id": 2,
"dinosaur": "diplodocus",
"diet": [
{ "food": "foglie", "likes": true},
{ "food": "carne", "likes": false}
]
}
]
La query seguente corrisponderebbe a questi due punti:
POST /collections/{nome_collezione}/points/scroll
{
"filter": {
"must": [
{
"key": "diet[].food",
"match": {
"value": "carne"
}
},
{
"key": "diet[].likes",
"match": {
"value": true
}
}
]
}
}
I due punti sopra corrispondono perché entrambi soddisfano queste due condizioni:
- "t-rex" soddisfa
diet[1].food
con food = carne ediet[1].likes
con likes = true - "diplodocus" soddisfa
diet[1].food
con food = carne ediet[0].likes
con likes = true
Per ottenere solo i punti che corrispondono alle condizioni degli elementi dell'array, ad esempio, il punto con id 1 in questo esempio, è necessario utilizzare filtri per oggetti nidificati.
I filtri per oggetti nidificati consentono di interrogare gli array di oggetti in modo indipendente.
Ciò può essere ottenuto utilizzando il tipo di condizione nested
, che consiste nella chiave del payload di interesse e nel filtro da applicare.
La chiave dovrebbe puntare a un array di oggetti e opzionalmente utilizzare la notazione a parentesi quadre ("data" o "data[]").
POST /collections/{nome_collezione}/points/scroll
{
"filter": {
"must": [
"nested": {
{
"key": "diet",
"filter": {
"must": [
{
"key": "food",
"match": {
"value": "carne"
}
},
{
"key": "likes",
"match": {
"value": true
}
}
]
}
}
}
]
}
}
La logica di corrispondenza è modificata per applicarsi al livello degli elementi dell'array all'interno del payload.
Il filtro nidificato funziona allo stesso modo di quando si applica un filtro nidificato a un singolo elemento di un array. Finché almeno un elemento dell'array corrisponde al filtro nidificato, il documento padre è considerato corrispondente alla condizione.
Limitazione
I filtri per oggetti nidificati non supportano la condizione has_id
. Se è necessario utilizzarla, inserirla in una clausola must
adiacente.
POST /collections/{nome_collezione}/points/scroll
{
"filter": {
"must": [
"nested": {
{
"key": "diet",
"filter": {
"must": [
{
"key": "food",
"match": {
"value": "carne"
}
},
{
"key": "likes",
"match": {
"value": true
}
}
]
}
}
},
{ "has_id": [1] }
]
}
}
Corrispondenza esatta del testo
Disponibile dalla versione 0.10.0
Un caso speciale della condizione match
è la condizione di corrispondenza del testo
. Consente di cercare sottostringhe, token o frasi specifiche all'interno del campo di testo.
I testi esatti che soddisfano questa condizione dipendono dalla configurazione dell'indice full-text. La configurazione è definita al momento della creazione dell'indice ed è descritta all'interno dell'indice full-text.
Se il campo non ha un indice full-text, questa condizione funzionerà in base alla corrispondenza esatta della sottostringa.
{
"key": "descrizione",
"match": {
"text": "buono ed economico"
}
}
Se la query ha diverse parole, questa condizione sarà soddisfatta solo quando tutte le parole appaiono nel testo.
Intervallo
{
"key": "price",
"range": {
"gt": null,
"gte": 100.0,
"lt": null,
"lte": 450.0
}
}
La condizione range
imposta l'intervallo possibile di valori per il payload memorizzato. Se sono memorizzati più valori, almeno uno deve corrispondere alla condizione.
Le operazioni di confronto disponibili includono:
-
gt
- maggiore di -
gte
- maggiore o uguale a -
lt
- minore di -
lte
- minore o uguale a
Può essere applicata a numeri in virgola mobile e payload interi.
Rettangolo di confine geografico
{
"key": "location",
"geo_bounding_box": {
"bottom_right": {
"lat": 52.495862,
"lon": 13.455868
},
"top_left": {
"lat": 52.520711,
"lon": 13.403683
}
}
}
Corrisponde alla location
all'interno del rettangolo con le coordinate in basso a destra come bottom_right
e le coordinate in alto a sinistra come top_left
.
Raggio geografico
{
"key": "location",
"geo_radius": {
"center": {
"lat": 52.520711,
"lon": 13.403683
},
"radius": 1000.0
}
}
Corrisponde alla location
all'interno del cerchio con il centro in center
e un raggio di radius
metri.
Se sono memorizzati più valori, almeno uno deve corrispondere alla condizione. Queste condizioni possono essere applicate solo ai payload che corrispondono al formato dati geografici.
Conteggio dei valori
Oltre al confronto diretto dei valori, il filtraggio può anche basarsi sul numero di valori.
Ad esempio, dati i seguenti dati:
[
{ "id": 1, "name": "Prodotto A", "commenti": ["Molto buono!", "Eccellente"] },
{ "id": 2, "name": "Prodotto B", "commenti": ["Discreto", "Mi aspettavo di più", "Buono"] }
]
Possiamo cercare solo gli elementi con più di due commenti:
{
"key": "commenti",
"values_count": {
"gt": 2
}
}
Il risultato sarà:
[{ "id": 2, "name": "Prodotto B", "commenti": ["Discreto", "Mi aspettavo di più", "Buono"] }]
Se il valore memorizzato non è un array, si presume che il conteggio dei valori sia uguale a 1.
È vuoto
A volte è utile filtrare i record che mancano di determinati valori. La condizione IsEmpty
può aiutarti a ottenere questo:
{
"is_empty": {
"key": "report"
}
}
Questa condizione corrisponderà a tutti i record in cui il campo report
non esiste o ha un valore di null
o []
.
IsEmpty è spesso molto utile quando usato insieme alla negazione logica must_not. In questo caso, selezionerà tutti i valori non vuoti.
È nullo
La condizione match non può testare i valori NULL
. Dobbiamo usare la condizione IsNull
:
{
"is_null": {
"key": "report"
}
}
Questa condizione corrisponderà a tutti i record in cui il campo report
esiste e ha un valore di NULL
.
Ha ID
Questo tipo di query non è correlato ai payload, ma è molto utile in certe situazioni. Ad esempio, gli utenti potrebbero voler contrassegnare alcuni risultati di ricerca come non pertinenti, oppure potremmo voler cercare solo tra punti specifici.
POST /collezioni/{nome_collezione}/punti/scroll
{
"filtro": {
"must": [
{ "ha_id": [1,3,5,7,9,11] }
]
}
...
}
I punti filtrati saranno:
[
{ "id": 1, "città": "Londra", "colore": "verde" },
{ "id": 3, "città": "Londra", "colore": "blu" },
{ "id": 5, "città": "Mosca", "colore": "verde" }
]