I am using Java and Spring data, Elasticsearch 6.8.14 Api. to communicate with Elasticsearch. I have index that returns such data (I am including this search result to show the mapping structure also)
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 6,
"max_score": 1.0,
"hits": [
{
"_index": "people",
"_type": "companyindexeddata",
"_id": "8",
"_score": 1.0,
"_source": {
"id": "8",
"privatePerson": {
"id": "10001",
"name": "Awski"
},
"legalPerson": null
}
},
{
"_index": "people",
"_type": "companyindexeddata",
"_id": "9",
"_score": 1.0,
"_source": {
"id": "9",
"privatePerson": null,
"legalPerson": {
"id": "10001",
"companyName": "Bwski"
}
}
}
]
}
}
So basically there are private and legal person in Elastic. Now I want to get all entries sorted by name. For Legal person it would be companyName
field and for simple person name
.
I am searching documents..
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(query).withPageable(pageRequest);
SearchQuery searchQuery = nativeSearchQueryBuilder.withSort(SortBuilders.scriptSort(new Script(getFirstScript()), ScriptSortBuilder.ScriptSortType.STRING).order(SortOrder.ASC));
Page<CompanyIndexedData> results= companyIndexedDataRepository.search(searchQuery);
When I use this script approach, I don't event get any results from Elastic. When I would remove sorting, all results are returned. Maybe there must be some problem with the script but I don't see any errors in log.. Or maybe the problem is that it is nested object ?
nativeSearchQueryBuilder.withSort(SortBuilders.scriptSort(new Script(getFirstScript()), ScriptSortBuilder.ScriptSortType.STRING).order(SortOrder.ASC));
private String getFirstScript() {
return "if (doc.containsKey('privatePerson') && !doc['privatePerson'].empty) {"
" return doc['privatePerson.name'].value;"
" }"
" else if (doc.containsKey('legalPerson') && !doc['legalPerson'].empty) {"
" return doc['legalPerson.companyName'].value;"
" } else {"
" return "";"
" }";
}
Since the privatePerson.name (similar to company name) mapping is like this:
"name": {
"type": "text",
"index": false,
"fields": {
"keyword": {
"type": "keyword"
}
}
}
I also tried with this script but then results are also empty:
private String getFirstScript() {
return "if (doc.containsKey('privatePerson') && !doc['privatePerson'].empty) {"
" return doc['privatePerson.name.keyword'].value;"
" }"
" else if (doc.containsKey('legalPerson') && !doc['legalPerson'].empty) {"
" return doc['legalPerson.companyName.keyword'].value;"
" } else {"
" return "";"
" }";
}
CodePudding user response:
For script sort you can only return type "Number" not "string"
You need to use runtime mapping in search request.
{
"runtime_mappings": {
"sort_name": {
"type": "keyword",
"script": {
"source": """
if(doc["privatePerson.name.keyword"].size()!=0)
emit(doc["privatePerson.name.keyword"].value);
else emit(doc["legalPerson.companyName.keyword"].value);
"""
}
}
},
"sort": [
{
"sort_name": { --> sort on runtime field
"order": "asc"
}
}
]
}
To improve performance. You can index runtime field and directly use it in query
PUT <index-name>/_mapping
{
"runtime": {
"sort_name": {
"type": "keyword",
"script": {
"source": """
if(doc["privatePerson.name.keyword"].size()!=0)
emit(doc["privatePerson.name.keyword"].value);
else emit(doc["legalPerson.companyName.keyword"].value);
"""
}
}
}
}
GET <index-name>/_search
{
"sort": [
{
"sort_name": {
"order": "asc"
}
}
]
}