Let's say I have mapping like this, and I want to search by the "requestId.keyword" field to fetch the exact match requests. How can I implement it with the Spring Data Elasticsearch repository without using @Query annotation?
"requestId": {
"type": "text",
"analyzer": "1_to_15_analyzer_without_space",
"search_analyzer": "all_symbols_and_fold_analyzer",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
CodePudding user response:
This is not possible with the mechanism to build queries by introspecting the method name. The first idea is to have something like (I am using a Foo
entity here):
SearchHits<Foo> searchByRequestId_Keyword(String keyword);
The analysis of the method name is done in the spring-data-common module which only uses the property names of the Java properties of an entity (might be nested). But the keyword subfield only exists in Elasticsearch and - if not autocreated - in the @MultiField
annotation. But the code to parse the methodname does not use store-specific information and so an approach like this will not work and fail with the error that keyword
is not a property of text
- which is right for the Java object.
What you can do is to first add a custom repository fragment interface:
public interface FooKeywordRepository {
SearchHits<Foo> searchByRequestIdKeyword(String keyword);
}
and provide an implementation that must be named like the interface with Impl as suffix:
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
public class FooKeywordRepositoryImpl implements FooKeywordRepository {
private final ElasticsearchOperations operations;
public FooKeywordRepositoryImpl(ElasticsearchOperations operations) {
this.operations = operations;
}
@Override
public SearchHits<Foo> searchByRequestIdKeyword(String keyword) {
Query query1 = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.termQuery("requestId.keyword", keyword))
.build();
Query query2 = new CriteriaQuery(Criteria.where("requestId.keyword").is(keyword));
return operations.search(query1, Foo.class); // could be query2 as well
}
}
You have an ElasticsearchOperations
injected and use that to execute a query that you build. I have put in two ways to build the query, both work.
Your repository definition to use would then be:
public interface FooRepository extends ElasticsearchRepository<Foo, String>, FooKeywordRepository {
// other custom methods if needed
}