【问题标题】:Spring data Elasticsearch find by field and latest date querySpring data Elasticsearch 按字段查找和最新日期查询
【发布时间】:2019-11-17 20:01:58
【问题描述】:

我正在使用 Spring Boot 2.1.6 和 Elasticsearch 6.2.2

编辑以更好地澄清我的问题

当我让 Spring 在我的存储库中使用以下方法为我生成查询时:

Account findTop1ByAccountIdOrderByCreatedDesc(final String accountId);

我想这意味着它将从按 accountId 过滤的索引中进行选择,然后按创建的降序对结果进行排序,最后它只会返回第一个(最新的)结果。

但我在索引中只有两个条目,相同的条目减去创建日期,并且该查询返回两个结果。我认为这意味着它不会转化为我的想法,而是会选择具有该 ID 的所有帐户(因为这是一个键,所以都是“顶部”),按降序排列。

这将更接近我的查询,但它不是合法的命名:

Account findTop1OrderByCreatedDescByAccountId(final String accountId);

org.springframework.data.mapping.PropertyReferenceException: No property descByAccountId found for type LocalDateTime! Traversed path: Account.created.

还有这个:

Account findTop1OrderByCreatedDescAndAccountIdEquals(final String accountId);

org.springframework.data.mapping.PropertyReferenceException: No property desc found for type LocalDateTime! Traversed path: Account.created.

那么,如果可能的话,我如何将我的查询转换为 Spring 存储库魔法?

/编辑

原问题:

我有一个这样声明的 POJO(精简版):

@Document(indexName = "config.account", type = "account")
public class Account{

    @Id
    private String id;

    @Field(type = FieldType.Text)
    @JsonProperty("ilmAccountIdentifier")
    private String accountId;

    @Field(type = FieldType.Text)
    private String endOfBusinessDay;

    @Field(type = FieldType.Date)
    private LocalDateTime created;

}

我想查询索引以检索给定 accountId 的最新条目(created = max)。

我知道我可以使用查询生成器,但我想知道是否有一些魔法可以通过使用 spring 命名查询为我做到这一点,目前我正在尝试使用(找不到其他有效的措辞组合) :

Account findTop1ByAccountIdOrderByCreatedDesc(final String accountId);

但它返回 null。我看到数据在索引中(修剪版):

Account(id=W83u0GsBEjwDhWt1-Whn,
accountId=testfindByAccountIdReturnsLatestVersion,
endOfBusinessDay=17:00:00,
created=2019-07-08T09:34)

但是 Spring 生成的查询很奇怪,不是我所期望的:

SearchRequest{
searchType=QUERY_THEN_FETCH,
indices=[config.account],
indicesOptions=IndicesOptions[ignore_unavailable=false, allow_no_indices=true, expand_wildcards_open=true, expand_wildcards_closed=false, allow_aliases_to_multiple_indices=true, forbid_closed_indices=true, ignore_aliases=false],
types=[account],
routing='null',
preference='null',
requestCache=null,
scroll=null,
maxConcurrentShardRequests=0,
batchedReduceSize=512,
preFilterShardSize=128,
allowPartialSearchResults=null,
source={
  "from":0,
  "query":{
    "bool":{
      "must":[{
        "query_string": 
          {"query":
             "testfindByAccountIdReturnsLatestVersion",
             "fields": ["accountId^1.0"],
             "type":"best_fields",
             "default_operator":"and",
             "max_determinized_states":10000,
             "enable_position_increments":true,
             "fuzziness":"AUTO",
             "fuzzy_prefix_length":0,
             "fuzzy_max_expansions":50,
             "phrase_slop":0,
             "escape":false,
             "auto_generate_synonyms_phrase_query":true,
             "fuzzy_transpositions":true,
             "boost":1.0}
          }],
        "adjust_pure_negative":true,"boost":1.0}
      }
,"version":true}}

我想要的是这个 SQL 查询的等价物:

select *
from(
  select *
  from account
  where accountId = ?
  order by created desc
)
where rownum = 1

是否有可能与 Spring 魔法有关,还是我必须使用 QueryBuilder 或我自己的逻辑来实现它?

感谢和欢呼

编辑

不相关,但我意识到如果在使用 @JsonProperty 映射期间重命名字段,则 spring Repository 魔法不起作用。假设我不重命名,问题仍然是一样的。目前我通过实现我自己的逻辑来解决这个问题:

@Repository
public interface AccountRepository  extends ElasticsearchRepository<Account, String>, AccountRepositoryCustom {

    List<Account> findByIlmAccountIdentifierOrderByCreatedDesc(final String accountId);

}

public interface AccountRepositoryCustom {

    Account findLatestByAccountId(final String accountId);

}

@Repository
public class AccountRepositoryImpl implements AccountRepositoryCustom {

    @Autowired
    @Lazy
    private AccountRepository accountRepository;

    public Account findLatestByAccountId(final String accountId){
        return accountRepository.findByIlmAccountIdentifierOrderByCreatedDesc(accountId).get(0);
    }

}

【问题讨论】:

    标签: java spring spring-boot elasticsearch spring-data-elasticsearch


    【解决方案1】:

    对于从方法名称构建的查询,您必须使用 Document 类中的属性名称,所以这里 findByAccountId 是正确的。

    问题确实是使用@JsonProperty 注释在索引中重命名此属性。此注释用于将映射写入索引,据我回忆,在使用 Template 类而不是 Repository 类编写数据时也是如此。因此存储库查询在索引中搜索字段 accountId,而数据存储在字段 ilmAccountIdentifier 中。

    Spring Data Elasticsearch 3.2.0 版(目前 RC1,RC2 将于本月晚些时候发布,GA 应该在 9 月初发布)有一个新的 Mapper 实现可用,它可以在没有 @JsonProperty 的情况下工作只需使用@Field 注释即可:

    @Field(name="ilmAccountIdentifier", type = FieldType.Text)
    private String accountId;
    

    但 3.2.x 不适用于 Elasticsearch 6.2,您需要更新到 6.7。

    作为一种解决方法,您能否将accountId 属性重命名为ilmAccountIdentifier

    编辑 23.07.2019:

    我刚刚发现限制结果 topN 不起作用是 Spring Data Elasticsearch 中的 old bug,似乎它从未在此子模块中实现。所以请为这个问题投票。

    注意:spring-data-elasticsearch 是一个社区驱动的模块,所以我们靠贡献为生!

    编辑 28.11.2019:

    我在 2019 年 8 月实现了 topN,它将在 Spring Data Elasticsearch Neumann 发布(版本 4)中

    【讨论】:

    • 谢谢你,这对了解很有帮助。虽然我已经解决了这个问题,但我想知道的是我可以用 Spring 存储库的魔法编写我想到的查询吗?因为我作为示例发布的那个根本没有返回预期的结果,我需要使用解决方法。那么我做错了什么还是不能那样做?谢谢和欢呼
    • 我刚刚做了一个测试,现在的3.2.x版本好像不支持top关键字;我将不得不对此进行更多投资
    • 这似乎也很奇怪,因为docs 提供了它的示例,你的意思是目前我可以使用该关键字,但 spring 会忽略它并执行 findAll?
    • 我完全同意你应该支持它,文档的问题是,第一部分来自 spring-data-commons 项目,所有模块(jpy,elasticsearch,redis , mongodb 等) 基础。因此,尽管基本 spring 数据文档描述了这些关键字,但要由模块来实现它们。而这里对于elasticsearch,实现中仍然缺少一些部分。我将对此进行进一步调查并为此创建一张票(如果社区中没有人这样做,则实施它)。
    • 所以目前最好的办法是做一个 findAll - 这应该被 ES 限制为 10 条记录,然后取列表中的第一个。
    猜你喜欢
    • 2016-05-10
    • 2016-09-05
    • 1970-01-01
    • 2021-08-07
    • 1970-01-01
    • 2017-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多