I've two entities, Search
and Amount
that I'm mapping using Hibernate Search in this way:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
@Entity
@Indexed
public class Search extends SearchableItem {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEARCH_SEQ")
@DocumentId
private Long id;
@Embedded
@Valid
@IndexedEmbedded
private Amount maxAmount;
@Embedded
@Valid
@IndexedEmbedded
private Amount minAmount;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
@Embeddable
public class Amount {
@NotNull
@ScaledNumberField(sortable = Sortable.YES)
private BigDecimal value;
@NotNull
@Enumerated(EnumType.STRING)
@GenericField
private CurrencyCode currency;
}
These annotations creates my index in this way on Elasticsearch:
{
"search-000001": {
"aliases": {
"search-read": {
"is_write_index": false
},
"search-write": {
"is_write_index": true
}
},
"mappings": {
"dynamic": "strict",
"properties": {
"_entity_type": {
"type": "keyword",
"index": false
},
"maxAmount": {
"dynamic": "strict",
"properties": {
"currency": {
"type": "keyword",
"doc_values": false
},
"value": {
"type": "scaled_float",
"scaling_factor": 100
}
}
},
"minAmount": {
"dynamic": "strict",
"properties": {
"currency": {
"type": "keyword",
"doc_values": false
},
"value": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "search-000001",
"creation_date": "1644485712235",
"analysis": {
"normalizer": {
"sort": {
"filter": [
"asciifolding",
"lowercase"
],
"type": "custom"
}
},
"analyzer": {
"name": {
"filter": [
"asciifolding",
"lowercase"
],
"type": "custom",
"tokenizer": "standard"
},
"generic_text": {
"filter": [
"asciifolding",
"lowercase",
"porter_stem"
],
"type": "custom",
"tokenizer": "standard"
}
}
},
"number_of_replicas": "1",
"uuid": "GBfV36PLT2-JEVPlbE2DVw",
"version": {
"created": "7160399"
}
}
}
}
}
For the search on Elasticsearch, I'm using a custom score function which try to access to minAmount and maxAmount objects inside the search document, but I receive this error:
Unhandled Exception illegal_argument_exception
No field found for [maxAmount] in mapping
Stack:
[
"org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:100)",
"org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:28)",
"score = calculateScoreAmount(doc.maxAmount, doc.minAmount, params);\n ",
" ^---- HERE"
]
I had the same problem in the past on other entities and I solved it specifying @GenericField(sortable = Sortable.YES)
on the property that I needed to access in the score script (please note that without the explicit Sortable.YES I received the error from Elasticsearch during the script score function execution). The problem here is that Amount
is a custom defined object and @GenericField(sortable = Sortable.YES)
cannot be specified on it.
Can someone help on this?
CodePudding user response:
maxAmount
is an object field. It does not make sense to retrieve its value through doc.maxAmount
, because this syntax is for accessing doc values, and object fields are not represented in doc values.
I'm not sure what your script is or where you are passing it to Elasticsearch, but the problem is probably there.
If you want to access the docvalues of maxAmount.value
/minAmount.value
, then you should probably replace calculateScoreAmount(doc.maxAmount, doc.minAmount, params);
with calculateScoreAmount(doc['maxAmount.value'].value, doc['minAmount.value'].value, params);
If you want to access the JSON object representing maxAmount
/minAmount
, then you should not use docvalues but the _source
: calculateScoreAmount(ctx._source.maxAmount, ctx._source.minAmount, params);
. I'm not sure this syntax is exactly right, since I couldn't find any documentation to access the source in a sort, but it should be close to this. Also, keep in mind that this will be slow if you have lots of documents.
See also: