我正在使用以下内容进行搜索。运行正常。但是,当找到完整的单词匹配时,它将返回结果。但是我想要部分查询的结果(至少3个字符匹配不完整的单词)。另一个检查应该是,我的文档中有一个campus
字段。其中具有campus: "Bradford"
,campus:"Oxford"
,campus:"Harvard"
等值。我希望我的查询返回的文档的campus
应该是Bradford or Oxford
,而Nel
在整个文档的其余部分中都可用。
RestHighLevelClient client;
QueryBuilder matchQueryBuilder = QueryBuilders.queryStringQuery("Nel");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(matchQueryBuilder);
SearchRequest searchRequest = new SearchRequest("index_name");
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
如果我们使用SQL语句映射,则使用where campus='Bradford' OR campus='Oxford'
。
在文档中,我有“纳尔逊·曼德拉二世”
当前,如果我将Nelson
编写为查询,但是我需要它与查询Nel
一起工作,则可以使用。
参考方案
基本上有两种方法可以实现您要寻找的用例。
解决方案1:使用通配符查询
假设您有两个字段
name
text
下面是您的Java代码的样子:
private static void wildcardQuery(RestHighLevelClient client, SearchSourceBuilder sourceBuilder)
throws IOException {
System.out.println("-----------------------------------------------------");
System.out.println("Wildcard Query");
MatchQueryBuilder campusClause_1 = QueryBuilders.matchQuery("campus", "oxford");
MatchQueryBuilder campusClause_2 = QueryBuilders.matchQuery("campus", "bradford");
//Using wildcard query
WildcardQueryBuilder nameClause = QueryBuilders.wildcardQuery("name", "nel*");
//Main Query
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(nameClause)
.should(campusClause_1)
.should(campusClause_2)
.minimumShouldMatch(1);
sourceBuilder.query(query);
SearchRequest searchRequest = new SearchRequest();
//specify your index name in the below parameter
searchRequest.indices("my_wildcard_index");
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(searchResponse.getHits().getTotalHits());
System.out.println("-----------------------------------------------------");
}
请注意,如果上面的字段是campus
类型,并且您需要完全匹配以区分大小写,则需要以下代码:
TermQueryBuilder campusClause_2 = QueryBuilders.termQuery("campus", "Bradford");
解决方案2.使用Edge Ngram令牌生成器(首选解决方案)
为此,您将需要使用Edge Ngram标记程序。
以下是您的映射方式:
对应:
PUT my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"filter": "lowercase",
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 10,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "my_analyzer"
},
"campus": {
"type": "text"
}
}
}
}
样本文件:
PUT my_index/_doc/1
{
"name": "Nelson Mandela",
"campus": "Bradford"
}
PUT my_index/_doc/2
{
"name": "Nel Chaz",
"campus": "Oxford"
}
查询DSL
POST my_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "nel"
}
}
],
"should": [
{
"match": {
"campus": "bradford"
}
},
{
"match": {
"campus": "oxford"
}
}
],
"minimum_should_match": 1
}
}
}
Java代码:
private static void boolMatchQuery(RestHighLevelClient client, SearchSourceBuilder sourceBuilder)
throws IOException {
System.out.println("-----------------------------------------------------");
System.out.println("Bool Query");
MatchQueryBuilder campusClause_1 = QueryBuilders.matchQuery("campus", "oxford");
MatchQueryBuilder campusClause_2 = QueryBuilders.matchQuery("campus", "bradford");
//Plain old match query would suffice here
MatchQueryBuilder nameClause = QueryBuilders.matchQuery("name", "nel");
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(nameClause)
.should(campusClause_1)
.should(campusClause_2)
.minimumShouldMatch(1);
sourceBuilder.query(query);
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("my_index");
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(searchResponse.getHits().getTotalHits());
}
请注意,我是如何对名称字段使用匹配查询的。我建议您阅读一下有关analysis,analyzer,tokenizer和edge-ngram tokenizers的内容。
在控制台中,您应该能够看到文档的总点击数。
同样,您也可以使用其他查询类型,例如如果您正在寻找text
字段等的精确匹配项,则可以使用上述解决方案中的keyword
。
更新的答案:
我个人不建议Term query
,因为单个字段本身会浪费很多计算能力,更不用说多个字段了。
为了进行多字段子字符串匹配,最好的方法是使用称为 keyword
的概念,然后对该字段使用Edge N-Gram标记器。
那么,这款Edge N-Gram令牌生成器到底能做什么?简而言之,根据Solution 1
和copy-to
,它只会分解您的令牌,例如
Zeppelin转换为min-gram
,从而将这些值插入该字段的反向索引中。如果仅执行一个非常简单的max-gram
查询,则不会返回该文档,因为您的反向索引将具有该子字符串。
关于copy_to字段:Zep, Zepp, Zeppe, Zeppel, Zeppeli, Zeppelin
参数允许您复制多个字段的值
分组字段,然后可以将其作为单个字段进行查询。
使用copy_to字段,我们对match
和copy_to
这两个字段具有以下映射。
对应:
PUT my_index
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"filter": "lowercase",
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "edge_ngram",
"min_gram": 3,
"max_gram": 10,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"properties": {
"name":{
"type": "text",
"copy_to": "search_string" <---- Note this
},
"campus": {
"type": "text",
"copy_to": "search_string" <---- Note this
},
"search_string": {
"type": "text",
"analyzer": "my_analyzer" <---- Note this
}
}
}
}
请注意,在上述映射中,我是如何仅将Edge N-gram专用分析器用于campus
的。请注意,这会占用磁盘空间,因此您可能需要后退一步,并确保不要对所有字段都使用此分析器,但是再次取决于您的用例。
示例文档:
POST my_index/_doc/1
{
"campus": "Cambridge University",
"name": "Ramanujan"
}
搜索查询:
POST my_index/_search
{
"query": {
"match": {
"search_string": "ram"
}
}
}
这将使您的Java代码非常简单,如下所示:
private static void boolMatchQuery(RestHighLevelClient client, SearchSourceBuilder sourceBuilder)
throws IOException {
System.out.println("-----------------------------------------------------");
System.out.println("Bool Query");
MatchQueryBuilder searchClause = QueryBuilders.matchQuery("search_string", "ram");
//Feel free to add multiple clauses
BoolQueryBuilder query = QueryBuilders.boolQuery()
.must(searchClause);
sourceBuilder.query(query);
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("my_index");
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(searchResponse.getHits().getTotalHits());
}
希望有帮助!
Elasticsearch-如何使用Java在JSON对象中添加或编辑字符串数组? - java我在Java中分别具有用户ID,名称和类型变量,如int,string和arraylist。我想像这样将其插入Elasticsearch数据库中:users { "userid": 5, "name": "test", "type": ["U1", "…
Java-搜索字符串数组中的字符串 - java在Java中,我们是否有任何方法可以发现特定字符串是字符串数组的一部分。我可以避免出现一个循环。例如String [] array = {"AA","BB","CC" }; string x = "BB" 我想要一个if (some condition to tell wheth…
Java Scanner读取文件的奇怪行为 - java因此,在使用Scanner类从文件读取内容时,我遇到了一个有趣的问题。基本上,我试图从目录中读取解析应用程序生成的多个输出文件,以计算一些准确性指标。基本上,我的代码只是遍历目录中的每个文件,并使用扫描仪将其打开以处理内容。无论出于何种原因,扫描程序都不会读取其中的一些文件(所有UTF-8编码)。即使文件不是空的,scanner.hasNextLine()在…
Java Globbing模式以匹配目录和文件 - java我正在使用递归函数遍历根目录下的文件。我只想提取*.txt文件,但不想排除目录。现在,我的代码如下所示:val stream = Files.newDirectoryStream(head, "*.txt") 但是这样做将不会匹配任何目录,并且返回的iterator()是False。我使用的是Mac,所以我不想包含的噪音文件是.DS_ST…
直接读取Zip文件中的文件-Java - java我的情况是我有一个包含一些文件(txt,png,...)的zip文件,我想直接按它们的名称读取它,我已经测试了以下代码,但没有结果(NullPointerExcepion):InputStream in = Main.class.getResourceAsStream("/resouces/zipfile/test.txt"); Buff…