使用Elasticsearch 7 Java API使用Where语句进行部分查询 - java

我正在使用以下内容进行搜索。运行正常。但是,当找到完整的单词匹配时,它将返回结果。但是我想要部分查询的结果(至少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 1copy-to,它只会分解您的令牌,例如
    Zeppelin转换为min-gram,从而将这些值插入该字段的反向索引中。如果仅执行一个非常简单的max-gram查询,则不会返回该文档,因为您的反向索引将具有该子字符串。
    关于copy_to字段:
    Zep, Zepp, Zeppe, Zeppel, Zeppeli, Zeppelin参数允许您复制多个字段的值
    分组字段,然后可以将其作为单个字段进行查询。

    使用copy_to字段,我们对matchcopy_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…