查询语法
学习如何使用查询语法
基本语法
您可以使用这些规则来为复杂查询使用简单的语法:
-
精确短语用引号括起来,例如,
"hello world"
。 -
多词短语是令牌的列表,例如,
foo bar baz
,并且暗示了这些术语的交集(AND)。 -
OR
联合使用管道符(|
)表示,例如,hello|hallo|shalom|hola
。注意:
考虑在示例
hello world | "goodbye" moon
中解析器行为的差异:- 在 DIALECT 1 中,此查询被解释为搜索
(hello world | "goodbye") moon
。 - 在 DIALECT 2 或更高版本中,此查询被解释为搜索
hello world
或"goodbye" moon
。
- 在 DIALECT 1 中,此查询被解释为搜索
-
NOT
表达式或子查询的否定用减号(-
)表示,例如hello -world
。纯否定查询如-foo
和-@title:(foo|bar)
也受支持。注意:
考虑一个带有否定的简单查询
-hello world
:- 在 DIALECT 1 中,此查询被解释为“查找任何不包含
hello
且 不包含world
的字段中的值”。等效于-(hello world)
或-hello -world
。 - 在 DIALECT 2 或更高版本中,此查询被解释为
-hello
且world
(仅hello
被否定)。 - 在 DIALECT 2 或更高版本中,要实现 DIALECT 1 的默认行为,请将查询更新为
-(hello world)
。
- 在 DIALECT 1 中,此查询被解释为“查找任何不包含
-
前缀/中缀/后缀匹配(所有以某个词开头/包含/结尾的术语)用星号
*
表示。出于性能原因,强制执行最小术语长度。默认值为2,但它是可配置的。 -
在DIALECT 2或更高版本中,通配符模式匹配表示为
"w'foo*bar?'"
。注意使用双引号来包含w模式。 -
一个特殊的通配符查询,返回索引中的所有结果,就是星号
*
。这不能与其他选项结合使用。 -
从v2.6.1版本开始,
DIALECT 3
从多值属性返回JSON而不是标量。 -
使用语法
hello @field:world
选择特定字段。 -
数值范围匹配在数值字段上使用语法
@field:[{min} {max}]
。 -
Georadius 匹配地理字段的语法为
@field:[{lon} {lat} {radius} {m|km|mi|ft}]
。 -
从2.6版本开始,支持使用语法
@field:[VECTOR_RANGE {radius} $query_vec]
对向量字段进行范围查询,其中query_vec
作为查询参数提供。 -
从v2.4版本开始,支持在向量字段上进行k近邻(KNN)查询,无论是否使用预过滤,语法为
{filter_query}=>[KNN {num} @field $query_vec]
。 -
使用语法
@field:{tag | tag | ...}
进行标签字段过滤。请参阅关于 tags 的完整文档。 -
可选术语或条款:
foo ~bar
表示 bar 是可选的,但包含bar
的文档将排名更高。 -
术语的模糊匹配:
%hello%
表示所有与它的Levenshtein距离为1的术语。使用多对'%'括号,最多三层,以增加Levenshtein距离。 -
查询中的表达式可以用括号括起来以消除歧义,例如,
(hello|hella) (world|werld)
。 -
查询属性可以应用于单个子句,例如,
(foo bar) => { $weight: 2.0; $slop: 1; $inorder: false; }
。 -
上述组合可以一起使用,例如,
hello (world|foo) "bar baz" bbbb
。
纯负面查询
从v0.19.3版本开始,可以有一个仅由否定表达式组成的查询。例如-hello
或-(@title:(foo|bar))
。结果是所有不包含查询词的文档。
字段修饰符
您可以在查询中指定字段修饰符,而不仅仅是通过使用INFIELDS
全局关键字。
要指定查询匹配的字段,请在每个表达式或子表达式前加上@
符号、字段名称和:
(冒号)符号。
如果一个字段修饰符位于多个单词或表达式之前,它仅适用于与DIALECT 1相邻的表达式。使用DIALECT 2或更高版本时,您可以将查询扩展到其他字段。
考虑这个简单的查询:@name:James Brown
。在这里,字段修饰符@name
后面跟着两个词:James
和Brown
。
- 在DIALECT 1中,此查询将被解释为“在
@name
字段中查找James Brown
”。 - 在DIALECT 2或更高版本中,此查询将被解释为“在
@name
字段中查找James
并且 在任何文本字段中查找Brown
。换句话说,它将被解释为(@name:James) Brown
。 - 在DIALECT 2或更高版本中,要实现DIALECT 1的默认行为,请将您的查询更新为
@name:(James Brown)
。
如果字段修饰符位于括号内的表达式之前,它仅适用于括号内的表达式。该表达式对于指定的字段应该是有效的,否则将被跳过。
要在多个字段上创建复杂的过滤,您可以组合多个修饰符。例如,如果您有一个汽车模型的索引,包含车辆类别、原产国和发动机类型,您可以使用以下查询搜索在韩国制造的混合动力或柴油发动机的SUV:
FT.SEARCH cars "@country:korea @engine:(diesel|hybrid) @class:suv"
你可以对同一个术语或分组术语应用多个修饰符:
FT.SEARCH idx "@title|body:(hello world) @url|image:mydomain"
现在,您搜索在正文或标题中包含"hello"
和"world"
,并且在url
或image
字段中包含术语mydomain
的文档。
查询中的数值过滤器
如果模式中的字段被定义为NUMERIC,可以在Redis请求中使用FILTER参数,或者在查询中指定过滤规则来过滤它。语法是@field:[{min} {max}]
,例如,@price:[100 200]
。
关于数值谓词的几点说明
-
可以指定一个数值谓词作为整个查询,而使用
FILTER
参数则无法做到这一点。 -
可以在同一查询中对多个数字过滤器进行交集或并集操作,无论是针对同一字段还是不同字段。
-
-inf
,inf
和+inf
是范围内可接受的数字。因此,大于100 表示为[(100 inf]
。 -
数值过滤器是包含性的。排他的最小值或最大值通过在数字前加上
(
来表示,例如,[(100 (200]
。 -
可以通过在过滤器前加上
-
符号来否定数值过滤器。例如,返回价格不等于100的结果可以表示为:@title:foo -@price:[100 100]
。
标签过滤器
从v0.91版本开始,你可以使用一种称为标签字段的特殊字段类型,它在索引中具有更简单的分词和编码。你不能使用普通的无字段搜索来访问这些字段中的值。相反,你需要使用特殊的语法:
@field:{ tag | tag | ...}
示例:
@cities:{ New York | Los Angeles | Barcelona }
标签可以包含多个单词或包含字段分隔符以外的其他标点符号(默认为,
)。标签中的以下字符应使用反斜杠(\
)进行转义:$
、{
、}
、\
和|
。
DIALECT 2
或更高版本,您可以在tag
查询中使用空格,即使有停用词。请注意,同一子句中的多个标签会创建一个包含任一标签的文档的联合。要创建一个包含所有标签的文档的交集,您应该多次重复标签过滤器。例如:
# Return all documents containing all three cities as tags
@cities:{ New York } @cities:{Los Angeles} @cities:{ Barcelona }
# Now, return all documents containing either city
@cities:{ New York | Los Angeles | Barcelona }
标签子句可以组合成任何子句,用作否定表达式、可选表达式等。
地理过滤器
自v0.21版本起,可以直接在查询语言中添加地理半径查询,语法为@field:[{lon} {lat} {radius} {m|km|mi|ft}]
。这将根据给定的经度、纬度点以及以米、千米、英里或英尺为单位的半径来过滤结果。有关更多详细信息,请参阅Redis的GEORADIUS
命令。
半径过滤器可以像数字过滤器一样添加到查询中。例如,在一个商业数据库中,寻找旧金山附近(5公里半径内)的中餐馆可以表示为:chinese restaurant @location:[-122.41 37.77 5 km]
。
多边形搜索
地理空间数据库对于管理和分析各种行业中的基于位置的数据至关重要。它们帮助组织做出数据驱动的决策,优化运营,并更有效地实现其战略目标。多边形搜索扩展了Redis的地理空间搜索能力,使其能够查询GEOSHAPE
属性中的值。该值必须遵循几何的"知名文本"(WKT)表示。支持两种这样的几何形状:
POINT
, 例如POINT(2 4)
.POLYGON
,例如POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))
。
有一个新的模式字段类型叫做GEOSHAPE
,它可以被指定为以下两种之一:
FLAT
用于笛卡尔X Y坐标SPHERICAL
用于地理经度和纬度坐标。这是默认的坐标系。
最后,有一个新的FT.SEARCH
语法,允许你查询包含或位于给定地理形状内的多边形。
@field:[{WITHIN|CONTAINS} $geometry] PARAMS 2 geometry {geometry}
这里有一个使用两个堆叠的多边形来表示一个包含在房子里的盒子的例子。
首先,使用FLAT
GEOSHAPE
创建一个索引,代表一个二维XY坐标系。
FT.CREATE polygon_idx PREFIX 1 shape: SCHEMA g GEOSHAPE FLAT t TEXT
接下来,创建表示图片中几何图形的数据结构。
HSET shape:1 t "this is my house" g "POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))"
HSET shape:2 t "this is a square in my house" g "POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))"
最后,使用FT.SEARCH
来查询几何图形。注意使用DIALECT 3
,这是必需的。以下是一些示例。
搜索包含指定点的多边形:
FT.SEARCH polygon_idx "@g:[CONTAINS $point]" PARAMS 2 point 'POINT(8 8)' DIALECT 3
1) (integer) 1
2) "shape:1"
3) 1) "t"
2) "this is my house"
3) "g"
4) "POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))"
搜索包含在指定多边形内的几何体:
FT.SEARCH polygon_idx "@g:[WITHIN $poly]" PARAMS 2 poly 'POLYGON((0 0, 0 100, 100 100, 100 0, 0 0))' DIALECT 3
1) (integer) 2
2) "shape:2"
3) 1) "t"
2) "this is a square in my house"
3) "g"
4) "POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))"
4) "shape:1"
5) 1) "t"
2) "this is my house"
3) "g"
4) "POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))"
搜索未包含在索引几何体中的多边形:
FT.SEARCH polygon_idx "@g:[CONTAINS $poly]" PARAMS 2 poly 'POLYGON((14 4, 14 6, 16 6, 16 4, 14 4))' DIALECT 3
1) (integer) 0
搜索已知包含在几何体(盒子)内的多边形:
FT.SEARCH polygon_idx "@g:[CONTAINS $poly]" PARAMS 2 poly 'POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))' DIALECT 3
1) (integer) 2
2) "shape:1"
3) 1) "t"
2) "this is my house"
3) "g"
4) "POLYGON((2 2, 2 8, 6 11, 10 8, 10 2, 2 2))"
4) "shape:2"
5) 1) "t"
2) "this is a square in my house"
3) "g"
4) "POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))"
请注意,房屋和盒子的形状都被返回了。
更多示例,请参见FT.CREATE
和FT.SEARCH
命令页面。
向量搜索
您可以通过以下方式直接在查询语言中添加向量相似性查询:
-
使用范围查询,语法为
@vector:[VECTOR_RANGE {radius} $query_vec]
,该语法将结果过滤到给定查询向量的给定半径内。距离度量源自索引模式中@vector字段的定义,例如,余弦或L2(自v2.6.1起)。 -
在@vector字段上运行k最近邻(KNN)查询。基本语法是
"*=>[ KNN {num|$num} @vector $query_vec ]"
。 也可以在过滤结果上运行混合查询。混合查询允许用户指定一个过滤条件,KNN查询中的所有结果都必须满足该条件。过滤条件可以包括任何类型的字段(即,在向量和其他值上创建的索引,如TEXT、PHONETIC、NUMERIC、GEO等)。 混合查询的一般语法是{some filter query}=>[ KNN {num|$num} @vector $query_vec]
,其中=>
将过滤查询与向量KNN查询分开。
示例:
-
返回10个最近的邻居实体,其中
query_vec
最接近存储在@vector_field
中的向量:*=>[KNN 10 @vector_field $query_vec]
-
在2020年至2022年之间发布的实体中,返回10个最近的邻居实体,其中
query_vec
最接近存储在@vector_field
中的向量:@published_year:[2020 2022]=>[KNN 10 @vector_field $query_vec]
-
返回每个实体,其存储在@vector_field下的向量与
query_vec
之间的距离在@vector_field距离度量中最多为0.5:@vector_field:[VECTOR_RANGE 0.5 $query_vec]
从v2.4版本开始,KNN向量搜索在查询中最多只能使用一次,而从v2.6版本开始,向量范围过滤器可以在查询中多次使用。有关向量相似度语法的更多信息,请参阅查询向量字段和向量搜索示例部分。
前缀匹配
当索引更新时,Redis会维护一个包含索引中所有术语的字典。这可以用来匹配所有以给定前缀开头的术语。选择前缀匹配是通过在前缀标记后附加*
来完成的。例如:
hel* world
将扩展以覆盖 (hello|help|helm|...) world
。
关于前缀搜索的一些注意事项
-
由于前缀可以扩展为许多术语,请谨慎使用它们。扩展将创建所有后缀的联合操作。
-
作为一种保护措施,为了避免选择过多的术语,阻塞Redis(它是单线程的),前缀匹配有两个限制:
-
前缀限制为2个字母或更多。您可以通过在模块命令行上使用
MINPREFIX
设置来更改此数字。 -
词干提取的最小单词长度为4个字母或更多。您可以通过在模块命令行上使用
MINSTEMLEN
设置来更改此数字。 -
扩展限制在200个术语或更少。您可以通过在模块命令行上使用
MAXEXPANSIONS
设置来更改此数字。
-
前缀匹配完全支持Unicode,并且不区分大小写。
-
目前,没有基于后缀流行度的排序或偏见。
中缀/后缀匹配
从v2.6.0版本开始,字典可以通过在标记后附加*
来用于中缀(包含)或后缀查询。例如:
*sun* *ing
这些查询是CPU密集型的,因为它们需要遍历整个字典。
使用后缀树
后缀树维护一个匹配后缀的术语列表。如果你使用WITHSUFFIXTRIE
关键字将后缀树添加到字段中,你可以创建更高效的中缀和后缀查询,因为它消除了遍历整个字典的需要。然而,联合上的迭代不会改变。
后缀查询从后缀词节点创建术语列表的联合。中缀查询使用后缀词作为前缀到trie,并创建所有匹配节点的所有术语的联合。
通配符匹配
从v2.6.0版本开始,您可以使用字典进行带有这些参数的通配符匹配查询。
?
- 代表任意单个字符*
- 用于表示任意字符重复零次或多次- ' 和 \ - 用于转义;其他特殊字符将被忽略
语法的一个例子是 "w'foo*bar?'"
。
使用后缀树
后缀树维护一个匹配后缀的术语列表。如果你使用WITHSUFFIXTRIE
关键字将后缀树添加到字段中,你可以创建更高效的通配符匹配查询,因为它消除了遍历整个字典的需要。然而,联合上的迭代不会改变。
使用后缀树时,通配符模式在每个*
字符处被分解为标记。使用启发式方法选择具有最少项的标记,并且每个项都与通配符模式匹配。
模糊匹配
自v1.2.0版本起,索引中所有术语的字典也可用于执行模糊匹配。 模糊匹配是基于Levenshtein距离(LD)进行的。 对术语进行模糊匹配时,需在术语前后加上'%',例如:
%hello% world
这将对所有LD为1的术语在hello
上执行模糊匹配。
从v1.4.0版本开始,模糊匹配的LD可以通过围绕它的'%'字符的数量来设置,因此%%hello%%
将对所有LD为2的术语执行'hello'的模糊匹配。
模糊匹配的最大LD为3。
通配符查询
从v1.1.0版本开始,您可以使用一个特殊的查询来检索索引中的所有文档。这主要是为聚合引擎设计的。您可以通过仅指定一个星号作为查询字符串来调用它,换句话说,FT.SEARCH myIndex *
。
你不能将此与任何其他过滤器、字段修饰符或查询中的任何内容结合使用。从技术上讲,可以在查询字符串之外使用已弃用的FILTER
和GEOFILTER
请求参数与通配符结合使用,但这会使通配符变得毫无意义,并且只会损害性能。
查询属性
从v1.2.0版本开始,您可以将特定的查询修改属性应用于查询的特定子句。
语法是 (foo bar) => { $attribute: value; $attribute:value; ...}
:
(foo bar) => { $weight: 2.0; $slop: 1; $inorder: true; }
~(bar baz) => { $weight: 0.5; }
支持的属性有:
- $weight: 确定子查询或标记在结果整体排名中的权重(默认值:1.0)。
- $slop: 确定查询子句中允许的最大间隔(术语之间的空格)(默认值:0)。
- $inorder: 查询子句中的术语是否必须以与查询中相同的顺序出现。这通常与
$slop
一起设置(默认值:false)。 - $phonetic: 是否执行语音匹配(默认值:true)。注意:对于未创建为
PHONETIC
的字段,将此属性设置为true将产生错误。
截至v2.6.1版本,查询属性语法支持以下附加属性:
- $yield_distance_as: 指定距离字段名称,用于后续排序和/或返回,适用于生成某些距离度量的子句。目前仅支持向量查询(包括KNN和范围查询)。
- 向量查询参数: 以键值格式传递向量查询的可选参数。
一些查询示例
-
简单短语查询 -
hello
ANDworld
:hello world
-
精确短语查询 -
hello
紧接着world
:"hello world"
-
联合 - 包含
hello
或world
的文档:hello|world
-
不包含 - 包含
hello
但不包含world
的文档:hello -world
-
并集的交集:
(hello|halo) (world|werld)
-
并集的否定:
hello -(world|werld)
-
短语内的联合:
(barack|barrack) obama
-
具有更高优先级的可选术语,包含更多匹配项:
obama ~barack ~michelle
-
一个字段中的确切短语,另一个字段中的一个单词:
@title:"barack obama" @job:president
-
结合AND、OR与字段指定符:
@title:"hello world" @body:(foo bar) @category:(articles|biographies)
-
前缀/中缀/后缀查询:
hello worl* hel* *worl hello -*worl*
-
通配符匹配查询:
"w'foo??bar??baz'" "w'???????'" "w'hello*world'"
-
数值过滤 - 名称为
tv
且价格范围在200到500之间的产品:@name:tv @price:[200 500]
-
数值过滤 - 年龄大于18岁的用户:
@age:[(18 +inf]
将常见的SQL谓词映射到Redis查询引擎
SQL 条件 | Redis 查询引擎等效 | 注释 |
---|---|---|
WHERE x='foo' AND y='bar' | @x:foo @y:bar | 为了减少歧义,请使用 (@x:foo) (@y:bar) |
WHERE x='foo' AND y!='bar' | @x:foo -@y:bar | |
WHERE x='foo' OR y='bar' | (@x:foo)|(@y:bar) | |
WHERE x IN ('foo', 'bar','hello world') | @x:(foo|bar|"hello world") | 引号表示精确短语 |
WHERE y='foo' AND x NOT IN ('foo','bar') | @y:foo (-@x:foo) (-@x:bar) | |
WHERE x 不在 ('foo','bar') 中 | -@x:(foo|bar) | |
WHERE num 在 10 和 20 之间 | @num:[10 20] | |
WHERE num >= 10 | @num:[10 +inf] | |
WHERE num > 10 | @num:[(10 +inf] | |
WHERE num < 10 | @num:[-inf (10] | |
WHERE num <= 10 | @num:[-inf 10] | |
WHERE num < 10 OR num > 20 | @num:[-inf (10] | @num:[(20 +inf] | |
WHERE name LIKE 'john%' | @name:john* |
技术说明
查询解析器是使用Lemon解析器生成器和基于Ragel的词法分析器构建的。你可以在这个git仓库中看到DIALECT 2
语法定义at this git repo。
你也可以查看DEFAULT_DIALECT配置参数。