我们有一个OData兼容的API,它将一些全文搜索需求委托给一个Elasticsearch集群。由于OData表达式可能会变得相当复杂,我们决定简单地将它们转换为等效的Lucene查询语法,并将其提供给query_string
查询。
我们确实支持一些文本相关的OData过滤器表达式,比如:
以(字段,“bla”)开始
EndsWith(字段,“bla”)
substringof('bla',field)
名称eq'bla'
我们匹配的字段可以是analyzed
、not_analyzed
或两者(即通过多字段)。所搜索的文本可以是单个令牌(例如表
)、仅是其一部分(例如标签
)、或多个令牌(例如表1.
、表10
等)。搜索必须不区分大小写。
下面是一些我们需要支持的行为示例:
startswith(name,'table1')
必须与“table 1”、“table 100”、“table 1.5”、“table 112 uper level”相匹配Endswith(name,'table1')
必须与“Room 1,Table 1”、“subtable 1”、“Table 1”、“Jeff Table 1”相匹配substringof(“Table 1”,name)
必须与“big Table 1 back”、“Table 1”、“Table 1”、“small table12”相匹配名称eq“table 1”
必须与“table 1”、“table 1”、“table 1”匹配因此,基本上,我们接受用户输入(即传入的第2个参数startswith
/endswith
的内容,即substringof
的第1个参数,即eq
的右侧值),并尝试将其完全匹配,无论令牌完全匹配还是仅部分匹配。
现在,我们正在使用一个笨拙的解决方案,下面重点介绍了这个方案,它工作得很好,但远不是理想的方案。
在我们的query_string
中,我们使用正则表达式语法与not_analyth
字段进行匹配。由于字段是not_analyth
并且搜索必须不区分大小写,因此我们在准备正则表达式以提供给查询的同时进行自己的标记化,以便得出类似的结果,即这相当于OData筛选器endswith(name,'table8')
(=>匹配name
以“table 8”结尾的所有文档)
"query": {
"query_string": {
"query": "name.raw:/.*(T|t)(A|a)(B|b)(L|l)(E|e) 8/",
"lowercase_expanded_terms": false,
"analyze_wildcard": true
}
}
因此,尽管这个解决方案工作得很好,性能也不是太差(这是令人惊讶的),但我们希望采用不同的方法,充分利用分析器的能力,以便将所有的负担转移到索引时间而不是搜索时间。但是,由于重新索引我们所有的数据将花费数周的时间,我们想首先调查是否有一个令牌过滤器和分析器的良好组合,以帮助我们实现上面列举的相同搜索需求。
我的想法是,理想的解决方案将包含一些Whingle(即几个令牌在一起)和edge-nGram(即在令牌的开始或结束处匹配)的明智混合。但是,我不确定的是,是否可以使它们协同工作以匹配几个令牌,其中一个令牌可能没有完全由用户输入)。例如,如果索引的名称字段是“Big Table 123”,我需要substringof('table1',name)
来匹配它,因此“Table”是完全匹配的令牌,而“1”只是下一个令牌的前缀。
提前感谢分享你们的想法。
更新1:测试Andrei的解决方案后
=>精确匹配(eq
)和startswith
完全工作。
A.端带
故障
搜索substringof('table112',name)
会产生107个文档。搜索更具体的情况,如endswith(name,'table112')
会生成1525个文档,而它应该生成更少的文档(后缀匹配应该是子字符串匹配的子集)。在更深入的检查中,我发现了一些不匹配的地方,例如“Social Club,Table 12”(不包含“112”)或“Order 312”(既不包含“Table”也不包含“112”)。我猜是因为它们以“12”结尾,而这是令牌“112”的有效克数,因此匹配。
B.substringof
故障
搜索substringof('table',name)
与“Party Table”、“Alex on big Table”匹配,但与“Table 1”、“Table 112”等不匹配。搜索substringof('tabl',name)
与任何内容都不匹配
更新2
这是一种暗示,但我忘记了明确地提到解决方案必须使用query_string
查询,这主要是因为OData表达式(无论它们多么复杂)将不断被翻译成它们的Lucene等价物。我知道我们正在用Lucene的查询语法来权衡Elasticsearch查询DSL的功能,后者的功能稍差,表达能力也稍差,但这是我们无法真正改变的。不过,我们已经很接近了!
更新3(2019年6月25日):
ES7.2引入了一种新的数据类型,称为search_as_you_type
,它在本地允许这种行为。欲了解更多信息,请访问:https://www.elastic.co/guide/en/elasticsearch/reference/7.2/search-as-you-type.html
这是一个有趣的用例。以下是我的看法:
{
"settings": {
"analysis": {
"analyzer": {
"my_ngram_analyzer": {
"tokenizer": "my_ngram_tokenizer",
"filter": ["lowercase"]
},
"my_edge_ngram_analyzer": {
"tokenizer": "my_edge_ngram_tokenizer",
"filter": ["lowercase"]
},
"my_reverse_edge_ngram_analyzer": {
"tokenizer": "keyword",
"filter" : ["lowercase","reverse","substring","reverse"]
},
"lowercase_keyword": {
"type": "custom",
"filter": ["lowercase"],
"tokenizer": "keyword"
}
},
"tokenizer": {
"my_ngram_tokenizer": {
"type": "nGram",
"min_gram": "2",
"max_gram": "25"
},
"my_edge_ngram_tokenizer": {
"type": "edgeNGram",
"min_gram": "2",
"max_gram": "25"
}
},
"filter": {
"substring": {
"type": "edgeNGram",
"min_gram": 2,
"max_gram": 25
}
}
}
},
"mappings": {
"test_type": {
"properties": {
"text": {
"type": "string",
"analyzer": "my_ngram_analyzer",
"fields": {
"starts_with": {
"type": "string",
"analyzer": "my_edge_ngram_analyzer"
},
"ends_with": {
"type": "string",
"analyzer": "my_reverse_edge_ngram_analyzer"
},
"exact_case_insensitive_match": {
"type": "string",
"analyzer": "lowercase_keyword"
}
}
}
}
}
}
}
my_ngram_analyzer
用于将每个文本分割成小块,小块的大小取决于您的用例。出于测试目的,我选择了25个字符。使用lowercase
是因为您说了不区分大小写。基本上,这是用于substringof('table1',name)
的标记器。查询很简单:{
"query": {
"term": {
"text": {
"value": "table 1"
}
}
}
}
my_edge_ngram_analyzer
用于从开头开始拆分文本,这特别用于startswith(name,'table1')
用例。同样,查询很简单:{
"query": {
"term": {
"text.starts_with": {
"value": "table 1"
}
}
}
}
EndsWith(name,'table1')
的部分。为此,我定义了my_reverse_edge_ngram_analyzer
,它使用了关键字
标记器,以及小写
和edgengram
筛选器,前面和后面是反向
筛选器。这个标记器的主要作用是拆分edgengram中的文本,但边缘是文本的结尾,而不是开头(就像常规的edgengram
)。查询:{
"query": {
"term": {
"text.ends_with": {
"value": "table 1"
}
}
}
}
名称eq“table 1”
大小写,一个简单的关键字
标记器和一个小写
筛选器可以执行以下查询:{
"query": {
"term": {
"text.exact_case_insensitive_match": {
"value": "table 1"
}
}
}
}
关于query_string
,这会稍微改变解决方案,因为我希望term
不分析输入文本,并将其与索引中的一个术语精确匹配。
但如果为其指定了适当的分析器
,则可以使用query_string
“模拟”这种情况。
解决方案是如下所示的一组查询(始终使用该分析器,只更改字段名):
{
"query": {
"query_string": {
"query": "text.starts_with:(\"table 1\")",
"analyzer": "lowercase_keyword"
}
}
}
问题内容: 我们有一个与OData兼容的API,它将某些全文搜索需求委托给Elasticsearch集群。由于OData表达式可能变得非常复杂,因此我们决定将它们简单地转换为等效的Lucene查询语法,并将其输入查询中。 我们确实支持一些与文本相关的OData过滤器表达式,例如: 我们要匹配的字段可以是,也可以是两者(即通过多字段)。所搜索的文本可以是一个单一的令牌(例如),仅其一部分(例如),或
我想使用Impex从表中删除一些项目。下面的示例不会抛出错误,但不会删除任何内容。 查询产生预期的结果。是REMOVE与灵活的搜索不兼容,还是我遗漏了什么? 问题是,我正在hotfolder上运行导入,我想事先删除所有现有项目。我们欢迎其他解决方案。
我目前正在使用Spring security建立一个反向代理安全域,其想法是默认情况下要求所有请求上都有承载令牌,除了一些例外情况,如注册等。当前我的配置功能如下所示: Ant Matcher非常有用,但您必须单独传递所有URL。有没有办法让我传入一个字符串数组,这样我就可以将配置分开?
我对灵活的查询有问题。这是我的疑问: 这是我执行时的错误: 有人能帮我吗?谢谢。
如何获得所有A、B、C、D的pk 注意:使用字符串值C,D,我想使用灵活的搜索查询获取产品C,D以及A,B的pk 细节: 我有产品的清单。 在每个产品中都有一个名为“X”的属性,其中包含string类型的产品ID代码。 注意:“产品ID代码”是指产品列表中另一个产品的“产品ID”。 现在我想根据产品ID代码获得产品的pk?
我为Jetty 9和Kafka使用ssl密钥库。我需要提供密钥库和密钥密码来访问密钥库和私钥。但是,我不想在配置文件中以明文形式提供这些密码。还有哪些其他选项可以安全地提供/加密密码?每种方法的优缺点是什么?