Home > Back-end >  Elastic search conditional field Script sorting does not sort and eats all results
Elastic search conditional field Script sorting does not sort and eats all results

Time:10-21

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"
      }
    }
  ]
}
  • Related