使用“?”进行通配符查询的 Spring Data Elasticsearch 的奇怪行为

问题描述 投票:0回答:1

我使用 spring-data-elasticsearch 5.1.1 创建了一个 Spring Boot 应用程序。当我在数据库上执行查询时,我得到了正确的结果,但使用 API 却没有,我不明白为什么。

我有一个 Elasticsearch 数据库,其索引“用户”配置如下:

{
    "mappings": {
        "properties": {
            "id": {
                "type": "long"
            },
            "country": {
                "type": "keyword"
            },
            "firstname": {
                "type": "keyword"
            },
            "lastname": {
                "type": "keyword"
            }
    }
}

当我执行查询时:GET http://localhost:9200/_sql?

{
    "query": "SELECT * FROM users WHERE firstname LIKE 'Micka_l' and country = 'FR' LIMIT 100"
}

我找到了用户 Mickael。

当我修改查询时:

{
    "query": "SELECT * FROM users WHERE firstname LIKE 'Mick_l' and country = 'FR' LIMIT 100"
}

我没有得到任何结果,因为我删除了“Mick_l”中的一个字母。

通过执行查询:GET http://localhost:9200/_sql/translate,我检索了生成的查询以在我的 Spring Boot 服务中使用:

    public List<UserDTO> searchUsers(SearchDTO searchDTO) {

        String country = searchDTO.getCountry();
        String firstname = searchDTO.getFirstname();

        StringQuery queryString = new StringQuery("{\"bool\": {\"must\": [{\"term\": {\"country\": " +
                "{\"value\": \"" + country + "\"}}},{\"wildcard\": {\"firstname\": {\"wildcard\": \"" + firstname + "\"," +
                "\"boost\": 1.0}}}],\"boost\": 1.0}}");

        SearchHits<User> searchHits = searchOperations.search(queryString, User.class);

        List<User> userList = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());

        return userList.stream().map((user) -> UserMapper.MAPPER.mapToDTO(user)).collect(Collectors.toList());
    }

}

此查询允许我按名字搜索用户,如下所示:

发布http://localhost:8080/api/user/search

{
    "country": "FRA",
    "firstname": "Micka_l"
}

返回:迈克尔

但是:

{
    "country": "FRA",
    "firstname": "Mick_l"
}

还返回:Mickael,而我需要搜索区分大小写。为什么会有“?”表现得像“*”?

我尝试了几种使用 JPA 的方法:

List<User> findByCountryAndFirstnameLike(country, firstname)

还有@Query:

@Query("{\"bool\": {\"must\": [{\"term\": {\"country\": \"{\"value\": \" ?0 \"}}},{\"wildcard\": {\"firstname\": {\"wildcard\": \" ?1 \","\"boost\": 1.0}}}],\"boost\": 1.0}}")
List<User> findByCountryAndFirstnameLike(country, firstname)

行为是一样的。

您测试过这种行为吗?

我尝试了 NativeSearchQueryBuilder() 的最后一种方法,但无法完成配置:

    public List<UserDTO> searchUsers(SearchDTO searchDTO) {

        String country = searchDTO.getCountry();
        String firstname = searchDTO.getFirstname();

        TermQuery termQuery = new TermQuery.Builder().field("country").value(country).build();
        WildcardQuery wildcardQuery = new WildcardQuery.Builder().value(firstname).caseInsensitive(true).boost(1.0F).build();

// Doesn't Work:
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(new BoolQuery.Builder()
                        .must(t -> t.term(termQuery))
                        .must(w -> w.wildcard(wildcardQuery))
                ).build();


        SearchHits<User> searchHits = searchOperations.search(searchQuery, User.class);

        List<User> userList = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());

        return userList.stream().map((user) -> UserMapper.MAPPER.mapToDTO(user)).collect(Collectors.toList());
    }

}

我不确定这种方法是否可以解决使用“?”时的意外行为。

java spring-boot elasticsearch spring-data-jpa spring-data-elasticsearch
1个回答
0
投票

首先,生成 Elasticsearch 查询的第一种方法是让您容易遭受代码注入攻击。我希望您不要在生产代码中做这样的事情。不管怎样,你的问题有很多——SQL、JQuery、Spring Data,它似乎无处不在。那么让我们从后端开始。在elasticsearch中,一切都按预期进行:

DELETE test

PUT test
{
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      },
      "country": {
        "type": "keyword"
      },
      "firstname": {
        "type": "keyword"
      },
      "lastname": {
        "type": "keyword"
      }
    }
  }
}

POST test/_bulk?refresh
{"index":{}}
{"firstname":"Mickael", "lastname": "Carreira", "country": "PT"}
{"index":{}}
{"firstname":"Mickael", "lastname": "Landreau", "country": "FR"}

// Returns one record
GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "country": {
              "value": "FR"
            }
          }
        },
        {
          "wildcard": {
            "firstname": {
              "wildcard": "Micka?l",
              "boost": 1
            }
          }
        }
      ],
      "boost": 1
    }
  }
}

// Returns no records
GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "country": {
              "value": "FR"
            }
          }
        },
        {
          "wildcard": {
            "firstname": {
              "wildcard": "Mick?l",
              "boost": 1
            }
          }
        }
      ],
      "boost": 1
    }
  }
}

现在让我们弄清楚您的代码生成了哪个查询以及哪个部分不起作用。

顺便说一句,

"boost": 1
没有做任何事情,所以我们可以将其简化为

// Returns one record
GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "country": {
              "value": "FR"
            }
          }
        },
        {
          "wildcard": {
            "firstname": {
              "wildcard": "Micka?l"
            }
          }
        }
      ]
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.