过滤

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

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

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

过滤子句

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

让我们来看一下在Qdrant中实现的条款。

假设我们有一组具有以下有效载荷的点:

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

必须

示例:

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

应该

示例:

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

禁止

示例:

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

筛选条件

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

匹配

{
  "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)

最简单的条件类型是检查存储的值是否等于给定的值。 如果存储了多个值,其中至少有一个应该符合条件。 您可以将其应用于关键词整数bool负载。

匹配任意

自 v1.1.0 起可用

如果你想检查存储的值是否是多个值中的一个,你可以使用“匹配任意”条件。 “匹配任意”对于给定的值起到逻辑OR的作用。它也可以被描述为IN操作符。

您可以将其应用于关键词整数负载。

示例:

{
  "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"]中。

匹配排除

自 v1.2.0 版本起可用

如果你想检查存储的值是否不是多个值之一,你可以使用Match Except条件。 Match Except对于给定的值起到逻辑NOR的作用。 它也可以被描述为NOT IN操作符。

您可以将其应用于关键字整数负载。

示例:

{
  "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 起可用

由于有效载荷是任意的JSON对象,您很可能需要在一个嵌套字段上进行过滤。

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

假设我们有一组具有以下有效载荷的点:

[
  {
    "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 版本起可用

默认情况下,条件会考虑点的整个有效负载。

例如,给定两个点,其有效载荷如下:

[
  {
    "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的点,您需要使用嵌套对象过滤器。

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

这是通过使用由要关注的payload键和要应用的过滤器形成的nested条件类型来实现的。

键应指向一个对象数组,并且可以使用或不使用括号表示法(“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),
				},
			}),
		},
	},
})

匹配逻辑被修改为在有效载荷中的数组元素级别应用。

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

限制

嵌套对象过滤器中不支持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)),
		},
	},
})

全文匹配

自 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")

如果查询包含多个单词,则只有在文本中存在所有这些单词时,条件才会满足。

范围

{
  "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 条件设置了存储的有效载荷值的可能范围。 如果存储了多个值,其中至少有一个值应匹配该条件。

可以使用的比较:

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

可以应用于float整数负载。

日期时间范围

日期时间范围是一个独特的范围条件,用于datetime有效载荷,支持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 匹配

自 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")

地理

地理边界框

{
  "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)

它与左上角坐标为bottom_right,右下角坐标为top_left的矩形内的location匹配。

地理半径

{
  "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)

它与以center为中心,半径为radius米的圆内的location匹配。

如果存储了多个值,至少其中一个应匹配条件。 这些条件只能应用于匹配地理数据格式的有效载荷。

地理多边形

地理多边形搜索在您想要查找不规则形状区域内的点时非常有用,例如国家边界或森林边界。一个多边形总是有一个外部环,并且可以选择性地包括内部环。一个有岛屿的湖泊就是内部环的一个例子。如果您想查找水中的点而不是岛屿上的点,您可以为岛屿创建一个内部环。

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

目前,我们仅支持未投影的全球坐标(经度和纬度的十进制度数),并且我们不依赖于特定的基准面。


{
  "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},
		},
	})

匹配被视为给定多边形外部边界内或边界上的任何点位置,但不包括任何内部区域。

如果一个点存储了多个位置值,那么其中任何一个匹配都会将该点作为候选结果包含在结果集中。 这些条件只能应用于匹配地理数据格式的有效载荷。

数值计数

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

例如,给定数据:

[
  { "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。

是否为空

有时过滤掉缺少某些值的记录也很有用。 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[] 值的记录。

为空

无法使用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

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

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" }
]
这个页面有用吗?

感谢您的反馈!🙏

我们很抱歉听到这个消息。😔 你可以在GitHub上编辑这个页面,或者创建一个GitHub问题。