过滤

使用 Qdrant,您可以在搜索或检索点时设置条件。例如,您可以对点的 payloadid 都施加条件。

当无法在嵌入中表达对象的所有特征时,设置附加条件非常重要。例如各种业务需求:库存可用性、用户位置或期望的价格范围。

向量搜索过滤完整指南开发者关于正确使用和高级实践的建议。

过滤子句

Qdrant 允许您在子句中组合条件。子句是不同的逻辑操作,例如 ORANDNOT。子句可以递归地相互嵌套,以便您可以重现任意布尔表达式。

让我们看看 Qdrant 中实现的子句。

假设我们有一组点,其 payload 如下

[
  { "id": 1, "city": "London", "color": "green" },
  { "id": 2, "city": "London", "color": "red" },
  { "id": 3, "city": "London", "color": "blue" },
  { "id": 4, "city": "Berlin", "color": "red" },
  { "id": 5, "city": "Moscow", "color": "green" },
  { "id": 6, "city": "Moscow", "color": "blue" }
]

必须 (Must)

示例

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must": [
            { "key": "city", "match": { "value": "London" } },
            { "key": "color", "match": { "value": "red" } }
        ]
    }
    ...
}
from qdrant_client import QdrantClient, models

client = QdrantClient(url="http://localhost:6333")

client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="city",
                match=models.MatchValue(value="London"),
            ),
            models.FieldCondition(
                key="color",
                match=models.MatchValue(value="red"),
            ),
        ]
    ),
)
import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({ host: "localhost", port: 6333 });

client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        key: "city",
        match: { value: "London" },
      },
      {
        key: "color",
        match: { value: "red" },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::must([
            Condition::matches("city", "london".to_string()),
            Condition::matches("color", "red".to_string()),
        ])),
    )
    .await?;
import java.util.List;

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.Points.ScrollPoints;

QdrantClient client =
    new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addAllMust(
                        List.of(matchKeyword("city", "London"), matchKeyword("color", "red")))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

// & operator combines two conditions in an AND conjunction(must)
await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: MatchKeyword("city", "London") & MatchKeyword("color", "red")
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewMatch("city", "London"),
			qdrant.NewMatch("color", "red"),
		},
	},
})

过滤后的点将是

[{ "id": 2, "city": "London", "color": "red" }]

使用 must 时,仅当 must 中列出的每个条件都满足时,子句才为 true。从这个意义上说,must 等同于运算符 AND

应该 (Should)

示例

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "should": [
            { "key": "city", "match": { "value": "London" } },
            { "key": "color", "match": { "value": "red" } }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        should=[
            models.FieldCondition(
                key="city",
                match=models.MatchValue(value="London"),
            ),
            models.FieldCondition(
                key="color",
                match=models.MatchValue(value="red"),
            ),
        ]
    ),
)
client.scroll("{collection_name}", {
  filter: {
    should: [
      {
        key: "city",
        match: { value: "London" },
      },
      {
        key: "color",
        match: { value: "red" },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::should([
            Condition::matches("city", "london".to_string()),
            Condition::matches("color", "red".to_string()),
        ])),
    )
    .await?;
import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;
import java.util.List;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addAllShould(
                        List.of(matchKeyword("city", "London"), matchKeyword("color", "red")))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

// | operator combines two conditions in an OR disjunction(should)
await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: MatchKeyword("city", "London") | MatchKeyword("color", "red")
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Should: []*qdrant.Condition{
			qdrant.NewMatch("city", "London"),
			qdrant.NewMatch("color", "red"),
		},
	},
})

过滤后的点将是

[
  { "id": 1, "city": "London", "color": "green" },
  { "id": 2, "city": "London", "color": "red" },
  { "id": 3, "city": "London", "color": "blue" },
  { "id": 4, "city": "Berlin", "color": "red" }
]

使用 should 时,仅当 should 中列出的至少一个条件满足时,子句才为 true。从这个意义上说,should 等同于运算符 OR

必须不 (Must Not)

示例

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must_not": [
            { "key": "city", "match": { "value": "London" } },
            { "key": "color", "match": { "value": "red" } }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must_not=[
            models.FieldCondition(key="city", match=models.MatchValue(value="London")),
            models.FieldCondition(key="color", match=models.MatchValue(value="red")),
        ]
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must_not: [
      {
        key: "city",
        match: { value: "London" },
      },
      {
        key: "color",
        match: { value: "red" },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::must_not([
            Condition::matches("city", "london".to_string()),
            Condition::matches("color", "red".to_string()),
        ])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addAllMustNot(
                        List.of(matchKeyword("city", "London"), matchKeyword("color", "red")))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

// The ! operator negates the condition(must not)
await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: !(MatchKeyword("city", "London") & MatchKeyword("color", "red"))
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		MustNot: []*qdrant.Condition{
			qdrant.NewMatch("city", "London"),
			qdrant.NewMatch("color", "red"),
		},
	},
})

过滤后的点将是

[
  { "id": 5, "city": "Moscow", "color": "green" },
  { "id": 6, "city": "Moscow", "color": "blue" }
]

使用 must_not 时,仅当 should 中列出的条件都不满足时,子句才为 true。从这个意义上说,must_not 等同于表达式 (NOT A) AND (NOT B) AND (NOT C)

子句组合

也可以同时使用多个子句

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must": [
            { "key": "city", "match": { "value": "London" } }
        ],
        "must_not": [
            { "key": "color", "match": { "value": "red" } }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.FieldCondition(key="city", match=models.MatchValue(value="London")),
        ],
        must_not=[
            models.FieldCondition(key="color", match=models.MatchValue(value="red")),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        key: "city",
        match: { value: "London" },
      },
    ],
    must_not: [
      {
        key: "color",
        match: { value: "red" },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter {
            must: vec![Condition::matches("city", "London".to_string())],
            must_not: vec![Condition::matches("color", "red".to_string())],
            ..Default::default()
        }),
    )
    .await?;
import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addMust(matchKeyword("city", "London"))
                    .addMustNot(matchKeyword("color", "red"))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: MatchKeyword("city", "London") & !MatchKeyword("color", "red")
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewMatch("city", "London"),
		},
		MustNot: []*qdrant.Condition{
			qdrant.NewMatch("color", "red"),
		},
	},
})

过滤后的点将是

[
  { "id": 1, "city": "London", "color": "green" },
  { "id": 3, "city": "London", "color": "blue" }
]

在这种情况下,条件通过 AND 组合。

此外,条件可以递归嵌套。例如

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must_not": [
            {
                "must": [
                    { "key": "city", "match": { "value": "London" } },
                    { "key": "color", "match": { "value": "red" } }
                ]
            }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must_not=[
            models.Filter(
                must=[
                    models.FieldCondition(
                        key="city", match=models.MatchValue(value="London")
                    ),
                    models.FieldCondition(
                        key="color", match=models.MatchValue(value="red")
                    ),
                ],
            ),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must_not: [
      {
        must: [
          {
            key: "city",
            match: { value: "London" },
          },
          {
            key: "color",
            match: { value: "red" },
          },
        ],
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::must_not([Filter::must(
            [
                Condition::matches("city", "London".to_string()),
                Condition::matches("color", "red".to_string()),
            ],
        )
        .into()])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.filter;
import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addMustNot(
                        filter(
                            Filter.newBuilder()
                                .addAllMust(
                                    List.of(
                                        matchKeyword("city", "London"),
                                        matchKeyword("color", "red")))
                                .build()))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: new Filter { MustNot = { MatchKeyword("city", "London") & MatchKeyword("color", "red") } }
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		MustNot: []*qdrant.Condition{
			qdrant.NewFilterAsCondition(&qdrant.Filter{
				Must: []*qdrant.Condition{
					qdrant.NewMatch("city", "London"),
					qdrant.NewMatch("color", "red"),
				},
			}),
		},
	},
})

过滤后的点将是

[
  { "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" }
]

过滤条件

payload 中不同类型的值对应于我们可以应用于它们的不同的查询类型。让我们看看现有的条件变体以及它们适用于哪些数据类型。

匹配 (Match)

{
  "key": "color",
  "match": {
    "value": "red"
  }
}
models.FieldCondition(
    key="color",
    match=models.MatchValue(value="red"),
)
{
    key: 'color', 
    match: {value: 'red'}
}
Condition::matches("color", "red".to_string())
matchKeyword("color", "red");
using static Qdrant.Client.Grpc.Conditions;

MatchKeyword("color", "red");
import "github.com/qdrant/go-client/qdrant"

qdrant.NewMatch("color", "red")

对于其他类型,匹配条件看起来完全相同,只是使用的类型不同

{
  "key": "count",
  "match": {
    "value": 0
  }
}
models.FieldCondition(
    key="count",
    match=models.MatchValue(value=0),
)
{
    key: 'count',
    match: {value: 0}    
}
Condition::matches("count", 0)
import static io.qdrant.client.ConditionFactory.match;

match("count", 0);
using static Qdrant.Client.Grpc.Conditions;

Match("count", 0);
import "github.com/qdrant/go-client/qdrant"

qdrant.NewMatchInt("count", 0)

最简单的条件是检查存储的值是否等于给定值。如果存储了多个值,则其中至少一个应匹配条件。您可以将其应用于 keywordintegerbool payload。

匹配任意 (Match Any)

自 v1.1.0 版本可用

如果您想检查存储的值是否是多个值中的一个,可以使用 Match Any 条件。Match Any 对给定的值起着逻辑 OR 的作用。它也可以被描述为 IN 运算符。

您可以将其应用于 keywordinteger payload。

示例

{
  "key": "color",
  "match": {
    "any": ["black", "yellow"]
  }
}
models.FieldCondition(
    key="color",
    match=models.MatchAny(any=["black", "yellow"]),
)
{
    key: 'color',
    match: {any: ['black', 'yellow']}    
}
Condition::matches("color", vec!["black".to_string(), "yellow".to_string()])
import static io.qdrant.client.ConditionFactory.matchKeywords;

matchKeywords("color", List.of("black", "yellow"));
using static Qdrant.Client.Grpc.Conditions;

Match("color", ["black", "yellow"]);
import "github.com/qdrant/go-client/qdrant"

qdrant.NewMatchKeywords("color", "black", "yellow")

在这个示例中,如果存储的值是 blackyellow,则条件将满足。

如果存储的值是一个数组,它应该至少有一个值与任何给定的值匹配。例如,如果存储的值是 ["black", "green"],条件将满足,因为 "black"["black", "yellow"] 中。

匹配排除 (Match Except)

自 v1.2.0 版本可用

如果您想检查存储的值不是多个值中的一个,可以使用 Match Except 条件。Match Except 对给定的值起着逻辑 NOR 的作用。它也可以被描述为 NOT IN 运算符。

您可以将其应用于 keywordinteger payload。

示例

{
  "key": "color",
  "match": {
    "except": ["black", "yellow"]
  }
}
models.FieldCondition(
    key="color",
    match=models.MatchExcept(**{"except": ["black", "yellow"]}),
)
{
    key: 'color',
    match: {except: ['black', 'yellow']}
}
use qdrant_client::qdrant::r#match::MatchValue;

Condition::matches(
    "color",
    !MatchValue::from(vec!["black".to_string(), "yellow".to_string()]),
)
import static io.qdrant.client.ConditionFactory.matchExceptKeywords;

matchExceptKeywords("color", List.of("black", "yellow"));
using static Qdrant.Client.Grpc.Conditions;

Match("color", ["black", "yellow"]);
import "github.com/qdrant/go-client/qdrant"

qdrant.NewMatchExcept("color", "black", "yellow")

在这个示例中,如果存储的值既不是 black 也不是 yellow,则条件将满足。

如果存储的值是一个数组,它应该至少有一个值与任何给定的值不匹配。例如,如果存储的值是 ["black", "green"],条件将满足,因为 "green" 不匹配 "black" 也不匹配 "yellow"

嵌套键

自 v1.1.0 版本可用

Payload 是任意 JSON 对象,您很可能需要在嵌套字段上进行过滤。

为方便起见,我们使用了与 Jq 项目中类似的语法。

假设我们有一组点,其 payload 如下

[
  {
    "id": 1,
    "country": {
      "name": "Germany",
      "cities": [
        {
          "name": "Berlin",
          "population": 3.7,
          "sightseeing": ["Brandenburg Gate", "Reichstag"]
        },
        {
          "name": "Munich",
          "population": 1.5,
          "sightseeing": ["Marienplatz", "Olympiapark"]
        }
      ]
    }
  },
  {
    "id": 2,
    "country": {
      "name": "Japan",
      "cities": [
        {
          "name": "Tokyo",
          "population": 9.3,
          "sightseeing": ["Tokyo Tower", "Tokyo Skytree"]
        },
        {
          "name": "Osaka",
          "population": 2.7,
          "sightseeing": ["Osaka Castle", "Universal Studios Japan"]
        }
      ]
    }
  }
]

您可以使用点表示法搜索嵌套字段。

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "should": [
            {
                "key": "country.name",
                "match": {
                    "value": "Germany"
                }
            }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        should=[
            models.FieldCondition(
                key="country.name", match=models.MatchValue(value="Germany")
            ),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    should: [
      {
        key: "country.name",
        match: { value: "Germany" },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::should([
            Condition::matches("country.name", "Germany".to_string()),
        ])),
    )
    .await?;
import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addShould(matchKeyword("country.name", "Germany"))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(collectionName: "{collection_name}", filter: MatchKeyword("country.name", "Germany"));
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Should: []*qdrant.Condition{
			qdrant.NewMatch("country.name", "Germany"),
		},
	},
})

您也可以使用 [] 语法投影内部值来搜索数组。

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "should": [
            {
                "key": "country.cities[].population",
                "range": {
                    "gte": 9.0,
                }
            }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        should=[
            models.FieldCondition(
                key="country.cities[].population",
                range=models.Range(
                    gt=None,
                    gte=9.0,
                    lt=None,
                    lte=None,
                ),
            ),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    should: [
      {
        key: "country.cities[].population",
        range: {
          gt: null,
          gte: 9.0,
          lt: null,
          lte: null,
        },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, Range, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::should([
            Condition::range(
                "country.cities[].population",
                Range {
                    gte: Some(9.0),
                    ..Default::default()
                },
            ),
        ])),
    )
    .await?;
import static io.qdrant.client.ConditionFactory.range;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.Range;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addShould(
                        range(
                            "country.cities[].population",
                            Range.newBuilder().setGte(9.0).build()))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: Range("country.cities[].population", new Qdrant.Client.Grpc.Range { Gte = 9.0 })
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Should: []*qdrant.Condition{
			qdrant.NewRange("country.cities[].population", &qdrant.Range{
				Gte: qdrant.PtrOf(9.0),
			}),
		},
	},
})

此查询只会输出 id 为 2 的点,因为只有日本有一个人口大于 9.0 的城市。

并且叶子嵌套字段也可以是数组。

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "should": [
            {
                "key": "country.cities[].sightseeing",
                "match": {
                    "value": "Osaka Castle"
                }
            }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        should=[
            models.FieldCondition(
                key="country.cities[].sightseeing",
                match=models.MatchValue(value="Osaka Castle"),
            ),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    should: [
      {
        key: "country.cities[].sightseeing",
        match: { value: "Osaka Castle" },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::should([
            Condition::matches("country.cities[].sightseeing", "Osaka Castle".to_string()),
        ])),
    )
    .await?;
import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addShould(matchKeyword("country.cities[].sightseeing", "Germany"))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: MatchKeyword("country.cities[].sightseeing", "Germany")
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Should: []*qdrant.Condition{
			qdrant.NewMatch("country.cities[].sightseeing", "Germany"),
		},
	},
})

此查询只会输出 id 为 2 的点,因为只有日本有一个城市,其观光景点包含“大阪城”。

嵌套对象过滤

自 v1.2.0 版本可用

默认情况下,条件会考虑点的整个 payload。

例如,给定以下 payload 的两个点

[
  {
    "id": 1,
    "dinosaur": "t-rex",
    "diet": [
      { "food": "leaves", "likes": false},
      { "food": "meat", "likes": true}
    ]
  },
  {
    "id": 2,
    "dinosaur": "diplodocus",
    "diet": [
      { "food": "leaves", "likes": true},
      { "food": "meat", "likes": false}
    ]
  }
]

以下查询将匹配这两个点

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must": [
            {
                "key": "diet[].food",
                  "match": {
                    "value": "meat"
                }
            },
            {
                "key": "diet[].likes",
                  "match": {
                    "value": true
                }
            }
        ]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="diet[].food", match=models.MatchValue(value="meat")
            ),
            models.FieldCondition(
                key="diet[].likes", match=models.MatchValue(value=True)
            ),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        key: "diet[].food",
        match: { value: "meat" },
      },
      {
        key: "diet[].likes",
        match: { value: true },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::must([
            Condition::matches("diet[].food", "meat".to_string()),
            Condition::matches("diet[].likes", true),
        ])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.match;
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.Points.ScrollPoints;

QdrantClient client =
    new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addAllMust(
                        List.of(matchKeyword("diet[].food", "meat"), match("diet[].likes", true)))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: MatchKeyword("diet[].food", "meat") & Match("diet[].likes", true)
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewMatch("diet[].food", "meat"),
			qdrant.NewMatchBool("diet[].likes", true),
		},
	},
})

这是因为这两个点都匹配这两个条件

  • “t-rex”在 diet[1].food 上匹配 food=meat,在 diet[1].likes 上匹配 likes=true
  • “diplodocus”在 diet[1].food 上匹配 food=meat,在 diet[0].likes 上匹配 likes=true

要仅检索在数组元素级别匹配条件的点,即本示例中的 id 为 1 的点,您需要使用嵌套对象过滤器。

嵌套对象过滤器允许独立查询对象数组中的元素。

这是通过使用 nested 条件类型实现的,该类型由一个要关注的 payload 键和一个要应用的过滤器组成。

键应指向对象数组,并且可以使用带或不带方括号的表示法(“data”或“data[]”)。

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must": [{
            "nested": {
                "key": "diet",
                "filter":{
                    "must": [
                        {
                            "key": "food",
                            "match": {
                                "value": "meat"
                            }
                        },
                        {
                            "key": "likes",
                            "match": {
                                "value": true
                            }
                        }
                    ]
                }
            }
        }]
    }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.NestedCondition(
                nested=models.Nested(
                    key="diet",
                    filter=models.Filter(
                        must=[
                            models.FieldCondition(
                                key="food", match=models.MatchValue(value="meat")
                            ),
                            models.FieldCondition(
                                key="likes", match=models.MatchValue(value=True)
                            ),
                        ]
                    ),
                )
            )
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        nested: {
          key: "diet",
          filter: {
            must: [
              {
                key: "food",
                match: { value: "meat" },
              },
              {
                key: "likes",
                match: { value: true },
              },
            ],
          },
        },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, NestedCondition, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::must([NestedCondition {
            key: "diet".to_string(),
            filter: Some(Filter::must([
                Condition::matches("food", "meat".to_string()),
                Condition::matches("likes", true),
            ])),
        }
        .into()])),
    )
    .await?;
import java.util.List;

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.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addMust(
                        nested(
                            "diet",
                            Filter.newBuilder()
                                .addAllMust(
                                    List.of(
                                        matchKeyword("food", "meat"), match("likes", true)))
                                .build()))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: Nested("diet", MatchKeyword("food", "meat") & Match("likes", true))
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewNestedFilter("diet", &qdrant.Filter{
				Must: []*qdrant.Condition{
					qdrant.NewMatch("food", "meat"),
					qdrant.NewMatchBool("likes", true),
				},
			}),
		},
	},
})

匹配逻辑被修改,应用于 payload 中数组元素的级别。

嵌套过滤器的作用方式与将嵌套过滤器一次应用于数组的单个元素的方式相同。如果数组的至少一个元素匹配嵌套过滤器,则父文档被认为匹配条件。

限制

在嵌套对象过滤器中不支持 has_id 条件。如果需要,请将其放在相邻的 must 子句中。

POST /collections/{collection_name}/points/scroll
{
   "filter":{
      "must":[
         {
            "nested":{
               "key":"diet",
               "filter":{
                  "must":[
                     {
                        "key":"food",
                        "match":{
                           "value":"meat"
                        }
                     },
                     {
                        "key":"likes",
                        "match":{
                           "value":true
                        }
                     }
                  ]
               }
            }
         },
         {
            "has_id":[
               1
            ]
         }
      ]
   }
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.NestedCondition(
                nested=models.Nested(
                    key="diet",
                    filter=models.Filter(
                        must=[
                            models.FieldCondition(
                                key="food", match=models.MatchValue(value="meat")
                            ),
                            models.FieldCondition(
                                key="likes", match=models.MatchValue(value=True)
                            ),
                        ]
                    ),
                )
            ),
            models.HasIdCondition(has_id=[1]),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        nested: {
          key: "diet",
          filter: {
            must: [
              {
                key: "food",
                match: { value: "meat" },
              },
              {
                key: "likes",
                match: { value: true },
              },
            ],
          },
        },
      },
      {
        has_id: [1],
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, NestedCondition, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}").filter(Filter::must([
            NestedCondition {
                key: "diet".to_string(),
                filter: Some(Filter::must([
                    Condition::matches("food", "meat".to_string()),
                    Condition::matches("likes", true),
                ])),
            }
            .into(),
            Condition::has_id([1]),
        ])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.hasId;
import static io.qdrant.client.ConditionFactory.match;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.ConditionFactory.nested;
import static io.qdrant.client.PointIdFactory.id;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addMust(
                        nested(
                            "diet",
                            Filter.newBuilder()
                                .addAllMust(
                                    List.of(
                                        matchKeyword("food", "meat"), match("likes", true)))
                                .build()))
                    .addMust(hasId(id(1)))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "{collection_name}",
	filter: Nested("diet", MatchKeyword("food", "meat") & Match("likes", true)) & HasId(1)
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewNestedFilter("diet", &qdrant.Filter{
				Must: []*qdrant.Condition{
					qdrant.NewMatch("food", "meat"),
					qdrant.NewMatchBool("likes", true),
				},
			}),
			qdrant.NewHasID(qdrant.NewIDNum(1)),
		},
	},
})

全文匹配 (Full Text Match)

自 v0.10.0 版本可用

match 条件的一个特殊情况是 text 匹配条件。它允许您在文本字段中搜索特定的子字符串、标记或短语。

将匹配条件的精确文本取决于全文索引配置。配置在索引创建期间定义,并在全文索引中描述。

如果该字段没有全文索引,则条件将作为精确子字符串匹配工作。

{
  "key": "description",
  "match": {
    "text": "good cheap"
  }
}
models.FieldCondition(
    key="description",
    match=models.MatchText(text="good cheap"),
)
{
    key: 'description',
    match: {text: 'good cheap'}    
}
use qdrant_client::qdrant::Condition;

Condition::matches_text("description", "good cheap")
import static io.qdrant.client.ConditionFactory.matchText;

matchText("description", "good cheap");
using static Qdrant.Client.Grpc.Conditions;

MatchText("description", "good cheap");
import "github.com/qdrant/go-client/qdrant"

qdrant.NewMatchText("description", "good cheap")

如果查询包含多个词,则仅当所有词都出现在文本中时,条件才会满足。

范围 (Range)

{
  "key": "price",
  "range": {
    "gt": null,
    "gte": 100.0,
    "lt": null,
    "lte": 450.0
  }
}
models.FieldCondition(
    key="price",
    range=models.Range(
        gt=None,
        gte=100.0,
        lt=None,
        lte=450.0,
    ),
)
{
    key: 'price',
    range: {
        gt: null,
        gte: 100.0,
        lt: null,
        lte: 450.0    
    }    
}
use qdrant_client::qdrant::{Condition, Range};

Condition::range(
    "price",
    Range {
        gt: None,
        gte: Some(100.0),
        lt: None,
        lte: Some(450.0),
    },
)
import static io.qdrant.client.ConditionFactory.range;

import io.qdrant.client.grpc.Points.Range;

range("price", Range.newBuilder().setGte(100.0).setLte(450).build());
using static Qdrant.Client.Grpc.Conditions;

Range("price", new Qdrant.Client.Grpc.Range { Gte = 100.0, Lte = 450 });
import "github.com/qdrant/go-client/qdrant"

qdrant.NewRange("price", &qdrant.Range{
	Gte: qdrant.PtrOf(100.0),
	Lte: qdrant.PtrOf(450.0),
})

range 条件为存储的 payload 值设置可能的值范围。如果存储了多个值,则其中至少一个应匹配条件。

可用的比较

  • gt - 大于
  • gte - 大于或等于
  • lt - 小于
  • lte - 小于或等于

可以应用于 floatinteger payload。

日期时间范围 (Datetime Range)

日期时间范围是一个独特的范围条件,用于 datetime payload,它支持 RFC 3339 格式。您无需将日期转换为 UNIX 时间戳。在比较过程中,时间戳会被解析并转换为 UTC。

自 v1.8.0 版本可用

{
  "key": "date",
  "range": {
    "gt": "2023-02-08T10:49:00Z",
    "gte": null,
    "lt": null,
    "lte": "2024-01-31 10:14:31Z"
  }
}
models.FieldCondition(
    key="date",
    range=models.DatetimeRange(
        gt="2023-02-08T10:49:00Z",
        gte=None,
        lt=None,
        lte="2024-01-31T10:14:31Z",
    ),
)
{
    key: 'date',
    range: {
        gt: '2023-02-08T10:49:00Z',
        gte: null,
        lt: null,
        lte: '2024-01-31T10:14:31Z'
    }
}
use qdrant_client::qdrant::{Condition, DatetimeRange, Timestamp};

Condition::datetime_range(
    "date",
    DatetimeRange {
        gt: Some(Timestamp::date_time(2023, 2, 8, 10, 49, 0).unwrap()),
        gte: None,
        lt: None,
        lte: Some(Timestamp::date_time(2024, 1, 31, 10, 14, 31).unwrap()),
    },
)
import static io.qdrant.client.ConditionFactory.datetimeRange;

import com.google.protobuf.Timestamp;
import io.qdrant.client.grpc.Points.DatetimeRange;
import java.time.Instant;

long gt = Instant.parse("2023-02-08T10:49:00Z").getEpochSecond();
long lte = Instant.parse("2024-01-31T10:14:31Z").getEpochSecond();

datetimeRange("date",
    DatetimeRange.newBuilder()
        .setGt(Timestamp.newBuilder().setSeconds(gt))
        .setLte(Timestamp.newBuilder().setSeconds(lte))
        .build());
using Qdrant.Client.Grpc;

Conditions.DatetimeRange(
    field: "date",
    gt: new DateTime(2023, 2, 8, 10, 49, 0, DateTimeKind.Utc),
    lte: new DateTime(2024, 1, 31, 10, 14, 31, DateTimeKind.Utc)
);
import (
	"time"

	"github.com/qdrant/go-client/qdrant"
	"google.golang.org/protobuf/types/known/timestamppb"
)

qdrant.NewDatetimeRange("date", &qdrant.DatetimeRange{
	Gt:  timestamppb.New(time.Date(2023, 2, 8, 10, 49, 0, 0, time.UTC)),
	Lte: timestamppb.New(time.Date(2024, 1, 31, 10, 14, 31, 0, time.UTC)),
})

UUID 匹配 (UUID Match)

自 v1.11.0 版本可用

UUID 值的匹配方式与常规的字符串 match 条件类似。功能上,它与 keyworduuid 索引的工作方式完全相同,但 uuid 索引更节省内存。

{
  "key": "uuid",
  "match": {
    "value": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
  }
}
models.FieldCondition(
    key="uuid",
    match=models.MatchValue(value="f47ac10b-58cc-4372-a567-0e02b2c3d479"),
)
{
    key: 'uuid',
    match: {value: 'f47ac10b-58cc-4372-a567-0e02b2c3d479'}    
}
Condition::matches("uuid", "f47ac10b-58cc-4372-a567-0e02b2c3d479".to_string())
matchKeyword("uuid", "f47ac10b-58cc-4372-a567-0e02b2c3d479");
using static Qdrant.Client.Grpc.Conditions;

MatchKeyword("uuid", "f47ac10b-58cc-4372-a567-0e02b2c3d479");
import "github.com/qdrant/go-client/qdrant"

qdrant.NewMatch("uuid", "f47ac10b-58cc-4372-a567-0e02b2c3d479")

地理位置 (Geo)

地理位置边界框 (Geo Bounding Box)

{
  "key": "location",
  "geo_bounding_box": {
    "bottom_right": {
      "lon": 13.455868,
      "lat": 52.495862
    },
    "top_left": {
      "lon": 13.403683,
      "lat": 52.520711
    }
  }
}
models.FieldCondition(
    key="location",
    geo_bounding_box=models.GeoBoundingBox(
        bottom_right=models.GeoPoint(
            lon=13.455868,
            lat=52.495862,
        ),
        top_left=models.GeoPoint(
            lon=13.403683,
            lat=52.520711,
        ),
    ),
)
{
    key: 'location',
    geo_bounding_box: {
        bottom_right: {
            lon: 13.455868,
            lat: 52.495862
        },
        top_left: {
            lon: 13.403683,
            lat: 52.520711
        }
    }
}
use qdrant_client::qdrant::{Condition, GeoBoundingBox, GeoPoint};

Condition::geo_bounding_box(
    "location",
    GeoBoundingBox {
        bottom_right: Some(GeoPoint {
            lon: 13.455868,
            lat: 52.495862,
        }),
        top_left: Some(GeoPoint {
            lon: 13.403683,
            lat: 52.520711,
        }),
    },
)
import static io.qdrant.client.ConditionFactory.geoBoundingBox;

geoBoundingBox("location", 52.520711, 13.403683, 52.495862, 13.455868);
using static Qdrant.Client.Grpc.Conditions;

GeoBoundingBox("location", 52.520711, 13.403683, 52.495862, 13.455868);
import "github.com/qdrant/go-client/qdrant"

qdrant.NewGeoBoundingBox("location", 52.520711, 13.403683, 52.495862, 13.455868)

它匹配位于矩形内部的 location,矩形的左上角坐标在 bottom_right 中,右下角坐标在 top_left 中。

地理位置半径 (Geo Radius)

{
  "key": "location",
  "geo_radius": {
    "center": {
      "lon": 13.403683,
      "lat": 52.520711
    },
    "radius": 1000.0
  }
}
models.FieldCondition(
    key="location",
    geo_radius=models.GeoRadius(
        center=models.GeoPoint(
            lon=13.403683,
            lat=52.520711,
        ),
        radius=1000.0,
    ),
)
{
    key: 'location',
    geo_radius: {
        center: {
            lon: 13.403683,
            lat: 52.520711
        },
        radius: 1000.0
    }    
}
use qdrant_client::qdrant::{Condition, GeoPoint, GeoRadius};

Condition::geo_radius(
    "location",
    GeoRadius {
        center: Some(GeoPoint {
            lon: 13.403683,
            lat: 52.520711,
        }),
        radius: 1000.0,
    },
)
import static io.qdrant.client.ConditionFactory.geoRadius;

geoRadius("location", 52.520711, 13.403683, 1000.0f);
using static Qdrant.Client.Grpc.Conditions;

GeoRadius("location", 52.520711, 13.403683, 1000.0f);
import "github.com/qdrant/go-client/qdrant"

qdrant.NewGeoRadius("location", 52.520711, 13.403683, 1000.0)

它匹配位于圆形内部的 location,圆心在 center 处,半径为 radius 米。

如果存储了多个值,则其中至少一个应匹配条件。这些条件只能应用于与 geo-data format 匹配的 payload。

地理位置多边形 (Geo Polygon)

地理位置多边形搜索在您希望查找不规则形状区域内的点时非常有用,例如国家边界或森林边界。多边形总有一个外部环,并且可以可选地包含内部环。带岛屿的湖是内部环的一个例子。如果您想查找在水中但不在岛上的点,您可以为岛屿设置一个内部环。

定义环时,必须选择顺时针或逆时针的点的顺序。多边形的第一个点和最后一个点必须相同。

目前,我们仅支持非投影的全球坐标(十进制度数的经度和纬度),并且我们与任何基准无关。


{
  "key": "location",
  "geo_polygon": {
    "exterior": {
      "points": [
        { "lon": -70.0, "lat": -70.0 },
        { "lon": 60.0, "lat": -70.0 },
        { "lon": 60.0, "lat": 60.0 },
        { "lon": -70.0, "lat": 60.0 },
        { "lon": -70.0, "lat": -70.0 }
      ]
    },
    "interiors": [
      {
        "points": [
          { "lon": -65.0, "lat": -65.0 },
          { "lon": 0.0, "lat": -65.0 },
          { "lon": 0.0, "lat": 0.0 },
          { "lon": -65.0, "lat": 0.0 },
          { "lon": -65.0, "lat": -65.0 }
        ]
      }
    ]
  }
}
models.FieldCondition(
    key="location",
    geo_polygon=models.GeoPolygon(
        exterior=models.GeoLineString(
            points=[
                models.GeoPoint(
                    lon=-70.0,
                    lat=-70.0,
                ),
                models.GeoPoint(
                    lon=60.0,
                    lat=-70.0,
                ),
                models.GeoPoint(
                    lon=60.0,
                    lat=60.0,
                ),
                models.GeoPoint(
                    lon=-70.0,
                    lat=60.0,
                ),
                models.GeoPoint(
                    lon=-70.0,
                    lat=-70.0,
                ),
            ]
        ),
        interiors=[
            models.GeoLineString(
                points=[
                    models.GeoPoint(
                        lon=-65.0,
                        lat=-65.0,
                    ),
                    models.GeoPoint(
                        lon=0.0,
                        lat=-65.0,
                    ),
                    models.GeoPoint(
                        lon=0.0,
                        lat=0.0,
                    ),
                    models.GeoPoint(
                        lon=-65.0,
                        lat=0.0,
                    ),
                    models.GeoPoint(
                        lon=-65.0,
                        lat=-65.0,
                    ),
                ]
            )
        ],
    ),
)
{
  key: "location",
  geo_polygon: {
    exterior: {
      points: [
        {
          lon: -70.0,
          lat: -70.0
        },
        {
          lon: 60.0,
          lat: -70.0
        },
        {
          lon: 60.0,
          lat: 60.0
        },
        {
          lon: -70.0,
          lat: 60.0
        },
        {
          lon: -70.0,
          lat: -70.0
        }
      ]
    },
    interiors: [
      {
        points: [
          {
            lon: -65.0,
            lat: -65.0
          },
          {
            lon: 0,
            lat: -65.0
          },
          {
            lon: 0,
            lat: 0
          },
          {
            lon: -65.0,
            lat: 0
          },
          {
            lon: -65.0,
            lat: -65.0
          }
        ]
      }
    ]
  }
}
use qdrant_client::qdrant::{Condition, GeoLineString, GeoPoint, GeoPolygon};

Condition::geo_polygon(
    "location",
    GeoPolygon {
        exterior: Some(GeoLineString {
            points: vec![
                GeoPoint {
                    lon: -70.0,
                    lat: -70.0,
                },
                GeoPoint {
                    lon: 60.0,
                    lat: -70.0,
                },
                GeoPoint {
                    lon: 60.0,
                    lat: 60.0,
                },
                GeoPoint {
                    lon: -70.0,
                    lat: 60.0,
                },
                GeoPoint {
                    lon: -70.0,
                    lat: -70.0,
                },
            ],
        }),
        interiors: vec![GeoLineString {
            points: vec![
                GeoPoint {
                    lon: -65.0,
                    lat: -65.0,
                },
                GeoPoint {
                    lon: 0.0,
                    lat: -65.0,
                },
                GeoPoint { lon: 0.0, lat: 0.0 },
                GeoPoint {
                    lon: -65.0,
                    lat: 0.0,
                },
                GeoPoint {
                    lon: -65.0,
                    lat: -65.0,
                },
            ],
        }],
    },
)
import static io.qdrant.client.ConditionFactory.geoPolygon;

import io.qdrant.client.grpc.Points.GeoLineString;
import io.qdrant.client.grpc.Points.GeoPoint;

geoPolygon(
    "location",
    GeoLineString.newBuilder()
        .addAllPoints(
            List.of(
                GeoPoint.newBuilder().setLon(-70.0).setLat(-70.0).build(),
                GeoPoint.newBuilder().setLon(60.0).setLat(-70.0).build(),
                GeoPoint.newBuilder().setLon(60.0).setLat(60.0).build(),
                GeoPoint.newBuilder().setLon(-70.0).setLat(60.0).build(),
                GeoPoint.newBuilder().setLon(-70.0).setLat(-70.0).build()))
        .build(),
    List.of(
        GeoLineString.newBuilder()
            .addAllPoints(
                List.of(
                    GeoPoint.newBuilder().setLon(-65.0).setLat(-65.0).build(),
                    GeoPoint.newBuilder().setLon(0.0).setLat(-65.0).build(),
                    GeoPoint.newBuilder().setLon(0.0).setLat(0.0).build(),
                    GeoPoint.newBuilder().setLon(-65.0).setLat(0.0).build(),
                    GeoPoint.newBuilder().setLon(-65.0).setLat(-65.0).build()))
            .build()));
using Qdrant.Client.Grpc;
using static Qdrant.Client.Grpc.Conditions;

GeoPolygon(
	field: "location",
	exterior: new GeoLineString
	{
		Points =
		{
			new GeoPoint { Lat = -70.0, Lon = -70.0 },
			new GeoPoint { Lat = 60.0, Lon = -70.0 },
			new GeoPoint { Lat = 60.0, Lon = 60.0 },
			new GeoPoint { Lat = -70.0, Lon = 60.0 },
			new GeoPoint { Lat = -70.0, Lon = -70.0 }
		}
	},
	interiors: [
		new()
		{
			Points =
			{
				new GeoPoint { Lat = -65.0, Lon = -65.0 },
				new GeoPoint { Lat = 0.0, Lon = -65.0 },
				new GeoPoint { Lat = 0.0, Lon = 0.0 },
				new GeoPoint { Lat = -65.0, Lon = 0.0 },
				new GeoPoint { Lat = -65.0, Lon = -65.0 }
			}
		}
	]
);
import "github.com/qdrant/go-client/qdrant"

qdrant.NewGeoPolygon("location",
	&qdrant.GeoLineString{
		Points: []*qdrant.GeoPoint{
			{Lat: -70, Lon: -70},
			{Lat: 60, Lon: -70},
			{Lat: 60, Lon: 60},
			{Lat: -70, Lon: 60},
			{Lat: -70, Lon: -70},
		},
	}, &qdrant.GeoLineString{
		Points: []*qdrant.GeoPoint{
			{Lat: -65, Lon: -65},
			{Lat: 0, Lon: -65},
			{Lat: 0, Lon: 0},
			{Lat: -65, Lon: 0},
			{Lat: -65, Lon: -65},
		},
	})

匹配被视为位于给定多边形外部环内部或边界上但不位于任何内部环内的任何点位置。

如果为一个点存储了多个位置值,则其中任何一个匹配都会将该点包含在结果集中作为候选。这些条件只能应用于与 geo-data format 匹配的 payload。

值计数 (Values count)

除了直接比较值之外,还可以按值的数量进行过滤。

例如,给定数据

[
  { "id": 1, "name": "product A", "comments": ["Very good!", "Excellent"] },
  { "id": 2, "name": "product B", "comments": ["meh", "expected more", "ok"] }
]

我们可以在评论数量多于两个的项目中进行搜索

{
  "key": "comments",
  "values_count": {
    "gt": 2
  }
}
models.FieldCondition(
    key="comments",
    values_count=models.ValuesCount(gt=2),
)
{
    key: 'comments',
    values_count: {gt: 2}    
}
use qdrant_client::qdrant::{Condition, ValuesCount};

Condition::values_count(
    "comments",
    ValuesCount {
        gt: Some(2),
        ..Default::default()
    },
)
import static io.qdrant.client.ConditionFactory.valuesCount;

import io.qdrant.client.grpc.Points.ValuesCount;

valuesCount("comments", ValuesCount.newBuilder().setGt(2).build());
using Qdrant.Client.Grpc;
using static Qdrant.Client.Grpc.Conditions;

ValuesCount("comments", new ValuesCount { Gt = 2 });
import "github.com/qdrant/go-client/qdrant"

qdrant.NewValuesCount("comments", &qdrant.ValuesCount{
	Gt: qdrant.PtrOf(uint64(2)),
})

结果将是

[{ "id": 2, "name": "product B", "comments": ["meh", "expected more", "ok"] }]

如果存储的值不是数组 - 假定值的数量等于 1。

为空 (Is Empty)

有时过滤掉缺少某些值的记录也很有用。IsEmpty 条件可以帮助您解决这个问题

{
  "is_empty": {
    "key": "reports"
  }
}
models.IsEmptyCondition(
    is_empty=models.PayloadField(key="reports"),
)
{
  is_empty: {
    key: "reports"
  }
}
use qdrant_client::qdrant::Condition;

Condition::is_empty("reports")
import static io.qdrant.client.ConditionFactory.isEmpty;

isEmpty("reports");
using Qdrant.Client.Grpc;
using static Qdrant.Client.Grpc.Conditions;

IsEmpty("reports");
import "github.com/qdrant/go-client/qdrant"

qdrant.NewIsEmpty("reports")

此条件将匹配所有字段 reports 不存在、或为 null、或为 [] 值的记录。

为 NULL (Is Null)

无法使用 match 条件测试 NULL 值。我们必须改用 IsNull 条件

{
    "is_null": {
        "key": "reports"
    }
}
models.IsNullCondition(
    is_null=models.PayloadField(key="reports"),
)
{
  is_null: {
    key: "reports"
  }
}
use qdrant_client::qdrant::Condition;

Condition::is_null("reports")
import static io.qdrant.client.ConditionFactory.isNull;

isNull("reports");
using Qdrant.Client.Grpc;
using static Qdrant.Client.Grpc.Conditions;

IsNull("reports");
import "github.com/qdrant/go-client/qdrant"

qdrant.NewIsNull("reports")

此条件将匹配所有字段 reports 存在且值为 NULL 的记录。

具有 ID (Has id)

这种查询类型与 payload 无关,但在某些情况下非常有用。例如,用户可以将某些特定的搜索结果标记为不相关,或者我们只想在指定的点中搜索。

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must": [
            { "has_id": [1,3,5,7,9,11] }
        ]
    }
    ...
}
client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.HasIdCondition(has_id=[1, 3, 5, 7, 9, 11]),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        has_id: [1, 3, 5, 7, 9, 11],
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;
    
client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}")
            .filter(Filter::must([Condition::has_id([1, 3, 5, 7, 9, 11])])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.hasId;
import static io.qdrant.client.PointIdFactory.id;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addMust(hasId(List.of(id(1), id(3), id(5), id(7), id(9), id(11))))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(collectionName: "{collection_name}", filter: HasId([1, 3, 5, 7, 9, 11]));
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewHasID(
				qdrant.NewIDNum(1),
				qdrant.NewIDNum(3),
				qdrant.NewIDNum(5),
				qdrant.NewIDNum(7),
				qdrant.NewIDNum(9),
				qdrant.NewIDNum(11),
			),
		},
	},
})

过滤后的点将是

[
  { "id": 1, "city": "London", "color": "green" },
  { "id": 3, "city": "London", "color": "blue" },
  { "id": 5, "city": "Moscow", "color": "green" }
]

具有向量 (Has vector)

自 v1.13.0 版本可用

此条件允许通过点是否存在给定的命名向量进行过滤。

例如,如果我们的集合中有两个命名向量。

PUT /collections/{collection_name}
{
    "vectors": {
        "image": {
            "size": 4,
            "distance": "Dot"
        },
        "text": {
            "size": 8,
            "distance": "Cosine"
        }
    },
    "sparse_vectors": {
        "sparse-image": {},
        "sparse-text": {},
    },
}

集合中的某些点可能拥有所有向量,有些可能只拥有其中一部分。

这是您可以搜索定义了密集 image 向量的点的方式

POST /collections/{collection_name}/points/scroll
{
    "filter": {
        "must": [
            { "has_vector": "image" }
        ]
    }
}
from qdrant_client import QdrantClient, models

client = QdrantClient(url="http://localhost:6333")

client.scroll(
    collection_name="{collection_name}",
    scroll_filter=models.Filter(
        must=[
            models.HasVectorCondition(has_vector="image"),
        ],
    ),
)
client.scroll("{collection_name}", {
  filter: {
    must: [
      {
        has_vector: "image",
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;
    
client
    .scroll(
        ScrollPointsBuilder::new("{collection_name}")
            .filter(Filter::must([Condition::has_vector("image")])),
    )
    .await?;
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.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("{collection_name}")
            .setFilter(
                Filter.newBuilder()
                    .addMust(hasVector("image"))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(collectionName: "{collection_name}", filter: HasVector("image"));
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.Scroll(context.Background(), &qdrant.ScrollPoints{
	CollectionName: "{collection_name}",
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewHasVector(
        "image",
			),
		},
	},
})
本页面是否有用?

感谢您的反馈!🙏

很抱歉得知您觉得没帮助。😔 您可以在 GitHub 上编辑此页面,或创建一个 GitHub Issue。