Filtro
Con Qdrant, puedes establecer condiciones para buscar o recuperar puntos, lo que significa que puedes filtrar por atributo además de búsquedas de similitud para vectores, similar a establecer condiciones where
en SQL. Por ejemplo, puedes establecer condiciones para la carga útil y el id
del punto.
Es importante establecer condiciones adicionales cuando no todos los atributos de un objeto se pueden expresar en un embedding. Por ejemplo, varios requisitos comerciales como disponibilidad de inventario, ubicación del usuario o rango de precios esperado.
Condiciones de filtro
Qdrant te permite combinar condiciones en cláusulas. Las cláusulas son diferentes operaciones lógicas, como OR
, AND
y NOT
. Las cláusulas pueden anidarse recursivamente entre sí, por lo que puedes recrear cualquier expresión booleana.
Echemos un vistazo a las cláusulas implementadas en Qdrant.
Supongamos que tenemos un conjunto de puntos con cargas útiles:
[
{ "id": 1, "city": "Londres", "color": "verde" },
{ "id": 2, "city": "Londres", "color": "rojo" },
{ "id": 3, "city": "Londres", "color": "azul" },
{ "id": 4, "city": "Berlín", "color": "rojo" },
{ "id": 5, "city": "Moscú", "color": "verde" },
{ "id": 6, "city": "Moscú", "color": "azul" }
]
Debe
Ejemplo:
POST /colecciones/{nombre_de_coleccion}/puntos/desplazar
{
"filtro": {
"must": [
{ "clave": "city", "coincidencia": { "valor": "Londres" } },
{ "clave": "color", "coincidencia": { "valor": "rojo" } }
]
}
...
}
El punto filtrado será:
[{ "id": 2, "city": "Londres", "color": "rojo" }]
Cuando se usa must
, la cláusula es true
solo si se cumplen todas las condiciones enumeradas en must
. En este sentido, must
es equivalente al operador AND
.
Debería
Should
es similar al operador OR
en SQL.
Ejemplo:
POST /colecciones/{nombre_de_coleccion}/puntos/desplazar
{
"filtro": {
"should": [
{ "clave": "city", "coincidencia": { "valor": "Londres" } },
{ "clave": "color", "coincidencia": { "valor": "rojo" } }
]
}
...
}
Los puntos filtrados serán:
[
{ "id": 1, "city": "Londres", "color": "verde" },
{ "id": 2, "city": "Londres", "color": "rojo" },
{ "id": 3, "city": "Londres", "color": "azul" },
{ "id": 4, "city": "Berlín", "color": "rojo" }
]
Al usar should
, la cláusula es true
siempre que se cumpla al menos una condición enumerada en should
. En este sentido, should
es equivalente al operador OR
.
must_not
Ejemplo:
POST /colecciones/{nombre_de_coleccion}/puntos/desplazar
{
"filtro": {
"must_not": [
{ "clave": "city", "coincidencia": { "valor": "Londres" } },
{ "clave": "color", "coincidencia": { "valor": "rojo" } }
]
}
...
}
Los puntos filtrados serán:
[
{ "id": 5, "city": "Moscú", "color": "verde" },
{ "id": 6, "city": "Moscú", "color": "azul" }
]
Al usar must_not
, la subcláusula es true
solo si ninguna de las condiciones enumeradas en must_not
se cumple. En este sentido, must_not
es equivalente a la expresión (NO A) Y (NO B) Y (NO C)
.
Combinación de Condiciones
Es posible usar múltiples condiciones simultáneamente:
POST /colecciones/{nombre_coleccion}/puntos/desplazar
{
"filtro": {
"debe": [
{ "clave": "ciudad", "coincidencia": { "valor": "Londres" } }
],
"no_debe": [
{ "clave": "color", "coincidencia": { "valor": "rojo" } }
]
}
...
}
Los puntos filtrados serán:
[
{ "id": 1, "ciudad": "Londres", "color": "verde" },
{ "id": 3, "ciudad": "Londres", "color": "azul" }
]
En este caso, las condiciones están combinadas usando AND
.
Además, las condiciones pueden estar anidadas de forma recursiva. Por ejemplo:
POST /colecciones/{nombre_coleccion}/puntos/desplazar
{
"filtro": {
"no_debe": [
{
"debe": [
{ "clave": "ciudad", "coincidencia": { "valor": "Londres" } },
{ "clave": "color", "coincidencia": { "valor": "rojo" } }
]
}
]
}
...
}
Los puntos filtrados serán:
[
{ "id": 1, "ciudad": "Londres", "color": "verde" },
{ "id": 3, "ciudad": "Londres", "color": "azul" },
{ "id": 4, "ciudad": "Berlín", "color": "rojo" },
{ "id": 5, "ciudad": "Moscú", "color": "verde" },
{ "id": 6, "ciudad": "Moscú", "color": "azul" }
]
Filtrado de Condiciones
En la carga útil, diferentes tipos de valores corresponden a diferentes tipos de consultas que se les pueden aplicar. Echemos un vistazo a las variantes de condiciones existentes y a los tipos de datos a los que se aplican.
Coincidencia
{
"clave": "color",
"coincidencia": {
"valor": "rojo"
}
}
Para otros tipos, las condiciones de coincidencia se ven exactamente iguales, solo que se utilizan diferentes tipos:
{
"clave": "conteo",
"coincidencia": {
"valor": 0
}
}
La condición más simple verifica si el valor almacenado es igual al valor dado. Si se almacenan varios valores, al menos uno de ellos debe satisfacer la condición. Esto se puede aplicar a cargas útiles de palabras clave, enteros y booleanos.
Coincidencia de Cualquier
Disponible desde v1.1.0
Si desea verificar si el valor almacenado es uno de varios valores, puede usar la condición de coincidencia de cualquier. La coincidencia de cualquier trata el valor dado como una operación lógica OR. También se puede describir como el operador IN
.
Se puede aplicar a cargas útiles de palabras clave y enteros.
Ejemplo:
{
"clave": "color",
"coincidencia": {
"cualquiera": ["negro", "amarillo"]
}
}
En este ejemplo, si el valor almacenado es negro
o amarillo
, entonces se cumplirá la condición.
Si el valor almacenado es un array, debe tener al menos un valor que coincida con cualquiera de los valores dados. Por ejemplo, si el valor almacenado es ["negro", "verde"]
, entonces se cumplirá la condición porque "negro"
está en ["negro", "amarillo"]
.
Coincidencia de Exclusión
Disponible desde v1.2.0
Si desea verificar si el valor almacenado no es ninguno de varios valores, puede usar la condición de coincidencia de exclusión. La coincidencia de exclusión trata el valor dado como una operación lógica NOR. También se puede describir como el operador NOT IN
.
Se puede aplicar a cargas útiles de palabras clave y enteros.
Ejemplo:
{
"clave": "color",
"coincidencia": {
"excepto": ["negro", "amarillo"]
}
}
En este ejemplo, si el valor almacenado no es ni negro
ni amarillo
, entonces se cumplirá la condición.
Si el valor almacenado es un array, debe tener al menos un valor que no coincida con ninguno de los valores dados. Por ejemplo, si el valor almacenado es ["negro", "verde"]
, entonces se cumplirá la condición porque "verde"
no coincide con "negro"
ni "amarillo"
.
Llaves Anidadas
Disponible a partir de la versión v1.1.0 en adelante
Dado que la carga útil es un objeto JSON arbitrario, es posible que necesite filtrar campos anidados.
Para su conveniencia, utilizamos una sintaxis similar al proyecto Jq.
Supongamos que tenemos un conjunto de puntos con la siguiente carga útil:
[
{
"id": 1,
"country": {
"name": "Alemania",
"cities": [
{
"name": "Berlín",
"population": 3.7,
"sightseeing": ["Puerta de Brandeburgo", "Reichstag"]
},
{
"name": "Múnich",
"population": 1.5,
"sightseeing": ["Marienplatz", "Parque Olímpico"]
}
]
}
},
{
"id": 2,
"country": {
"name": "Japón",
"cities": [
{
"name": "Tokio",
"population": 9.3,
"sightseeing": ["Torre de Tokio", "Tokyo Skytree"]
},
{
"name": "Osaka",
"population": 2.7,
"sightseeing": ["Castillo de Osaka", "Universal Studios Japan"]
}
]
}
}
]
Puede usar la notación de punto para buscar campos anidados.
POST /collections/{collection_name}/points/scroll
{
"filter": {
"should": [
{
"key": "country.name",
"match": {
"value": "Alemania"
}
}
]
}
}
También puede usar la sintaxis [ ]
para buscar en el array proyectando valores internos.
POST /collections/{collection_name}/points/scroll
{
"filter": {
"should": [
{
"key": "country.cities[].population",
"range": {
"gte": 9.0,
}
}
]
}
}
Esta consulta solo muestra el punto con id 2, ya que solo Japón tiene una ciudad con una población mayor a 9.0.
Los campos anidados también pueden ser un array.
POST /collections/{collection_name}/points/scroll
{
"filter": {
"should": [
{
"key": "country.cities[].sightseeing",
"match": {
"value": "Castillo de Osaka"
}
}
]
}
}
Esta consulta solo muestra el punto con id 2, ya que solo Japón tiene una ciudad con un lugar turístico que incluye "Castillo de Osaka".
Filtrado de Objetos Anidados
Disponible desde la versión 1.2.0
De forma predeterminada, las condiciones consideran el payload completo de un punto.
Por ejemplo, dado los dos puntos en el payload a continuación:
[
{
"id": 1,
"dinosaurio": "t-rex",
"dieta": [
{ "comida": "hojas", "gusta": false},
{ "comida": "carne", "gusta": true}
]
},
{
"id": 2,
"dinosaurio": "diplodocus",
"dieta": [
{ "comida": "hojas", "gusta": true},
{ "comida": "carne", "gusta": false}
]
}
]
La siguiente consulta coincidiría con estos dos puntos:
POST /colecciones/{nombre_coleccion}/puntos/scroll
{
"filtro": {
"debe": [
{
"clave": "dieta[].comida",
"coincidencia": {
"valor": "carne"
}
},
{
"clave": "dieta[].gusta",
"coincidencia": {
"valor": true
}
}
]
}
}
La razón por la que los dos puntos anteriores coinciden es porque ambos satisfacen estas dos condiciones:
- "t-rex" satisface
dieta[1].comida
con comida = carne ydieta[1].gusta
con gusta = true - "diplodocus" satisface
dieta[1].comida
con comida = carne ydieta[0].gusta
con gusta = true
Para obtener solo los puntos que coinciden con las condiciones de los elementos del array, por ejemplo, el punto con id 1 en este ejemplo, es necesario utilizar filtros de objetos anidados.
Los filtros de objetos anidados permiten consultar matrices de objetos de forma independiente.
Esto se puede lograr utilizando el tipo de condición anidado
, que consiste en la clave del payload de interés y el filtro a aplicar.
La clave debe apuntar a una matriz de objetos y opcionalmente puede utilizar la notación de corchetes ("datos" o "datos[]").
POST /colecciones/{nombre_coleccion}/puntos/scroll
{
"filtro": {
"debe": [
"anidado": {
{
"clave": "dieta",
"filtro": {
"debe": [
{
"clave": "comida",
"coincidencia": {
"valor": "carne"
}
},
{
"clave": "gusta",
"coincidencia": {
"valor": true
}
}
]
}
}
}
]
}
}
La lógica de coincidencia se modifica para aplicarse a nivel de elemento de matriz dentro del payload.
El filtro anidado funciona de la misma manera que al aplicar un filtro anidado a un solo elemento de una matriz. Si al menos un elemento de la matriz coincide con el filtro anidado, se considera que el documento principal coincide con la condición.
Limitación
Los filtros de objetos anidados no admiten la condición tiene_id
. Si es necesario utilizarla, colóquela en una cláusula debe
adyacente.
POST /colecciones/{nombre_coleccion}/puntos/scroll
{
"filtro": {
"debe": [
"anidado": {
{
"clave": "dieta",
"filtro": {
"debe": [
{
"clave": "comida",
"coincidencia": {
"valor": "carne"
}
},
{
"clave": "gusta",
"coincidencia": {
"valor": true
}
}
]
}
}
},
{ "tiene_id": [1] }
]
}
}
Coincidencia de Texto Exacta
Disponible desde la versión 0.10.0
Un caso especial de la condición de coincidencia
es la condición de coincidencia de texto
. Permite buscar subcadenas específicas, tokens o frases dentro del campo de texto.
Los textos exactos que satisfacen esta condición dependen de la configuración del índice de texto completo. La configuración se define cuando se crea el índice y se describe dentro del índice de texto completo.
Si el campo no tiene un índice de texto completo, esta condición funcionará en base a la coincidencia exacta de subcadenas.
{
"clave": "descripción",
"coincidencia": {
"texto": "bueno y barato"
}
}
Si la consulta tiene varias palabras, esta condición solo se satisfará cuando todas las palabras aparezcan en el texto.
Rango
{
"key": "precio",
"rango": {
"gt": null,
"gte": 100.0,
"lt": null,
"lte": 450.0
}
}
La condición de rango
establece el rango posible de valores para la carga almacenada. Si se almacenan varios valores, al menos un valor debe cumplir con la condición.
Las operaciones de comparación disponibles incluyen:
-
gt
- mayor que -
gte
- mayor o igual que -
lt
- menor que -
lte
- menor o igual que
Se puede aplicar a números de punto flotante y cargas enteras.
Recuadro Geográfico de Límites
{
"key": "ubicación",
"geo_bounding_box": {
"bottom_right": {
"lat": 52.495862,
"lon": 13.455868
},
"top_left": {
"lat": 52.520711,
"lon": 13.403683
}
}
}
Coincide con la ubicación
dentro del rectángulo con coordenadas en la esquina inferior derecha como bottom_right
y coordenadas en la esquina superior izquierda como top_left
.
Radio Geográfico
{
"key": "ubicación",
"geo_radio": {
"centro": {
"lat": 52.520711,
"lon": 13.403683
},
"radio": 1000.0
}
}
Coincide con la ubicación
dentro del círculo con el centro en centro
y un radio de radio
metros.
Si se almacenan varios valores, al menos un valor debe cumplir con la condición. Estas condiciones solo se pueden aplicar a cargas que coincidan con el formato de datos geográficos.
Conteo de Valores
Además de la comparación directa de valores, el filtrado también puede basarse en el número de valores.
Por ejemplo, dados los siguientes datos:
[
{ "id": 1, "nombre": "Producto A", "comentarios": ["¡Muy bueno!", "Excelente"] },
{ "id": 2, "nombre": "Producto B", "comentarios": ["Justo", "Esperando más", "Bueno"] }
]
Podemos buscar solo los elementos con más de dos comentarios:
{
"key": "comentarios",
"conteo_valores": {
"gt": 2
}
}
El resultado será:
[{ "id": 2, "nombre": "Producto B", "comentarios": ["Justo", "Esperando más", "Bueno"] }]
Si el valor almacenado no es un array, se asume que el conteo de valores es igual a 1.
Está Vacío
A veces, es útil filtrar registros que carecen de ciertos valores. La condición Está Vacío
puede ayudarte a lograr esto:
{
"esta_vacío": {
"key": "informes"
}
}
Esta condición coincidirá con todos los registros donde el campo informes
no existe o tiene un valor de null
o []
.
Está Vacío a menudo es muy útil cuando se usa en conjunto con la negación lógica must_not. En este caso, seleccionará todos los valores no vacíos.
Es Nulo
La condición igual no puede probar los valores NULL
. Debemos usar la condición Es Nulo
:
{
"es_nulo": {
"key": "informes"
}
}
Esta condición coincidirá con todos los registros donde el campo informes
existe y tiene un valor de NULL
.
Tiene ID
Este tipo de consulta no está relacionada con las cargas, pero es muy útil en ciertas situaciones. Por ejemplo, los usuarios pueden querer etiquetar ciertos resultados de búsqueda como irrelevantes, o solo queremos buscar entre puntos específicos.
POST /colecciones/{nombre_colección}/puntos/desplazar
{
"filtro": {
"debe": [
{ "tiene_id": [1,3,5,7,9,11] }
]
}
...
}
Los puntos filtrados serán:
[
{ "id": 1, "ciudad": "Londres", "color": "verde" },
{ "id": 3, "ciudad": "Londres", "color": "azul" },
{ "id": 5, "ciudad": "Moscú", "color": "verde" }
]