Мое сопоставление документов выглядит следующим образом:
"_source": {
"id": 60,
"price": 60,
"locations": [
{
"id": 1
"price": 70
},
{
"id": 5,
"price": 80
}
]
}
Таким образом, поле цены может находиться как в корневом массиве, так и в массиве вложенных локаций. Я хочу иметь возможность фильтровать их по обоим значениям - если в параметрах запроса установлен locationId
, то фильтровать по цене в местоположениях, иначе - фильтровать по основной цене.
Я написал безболезненный скрипт:
if (params['locationId'] != null){
for (def location : doc['locations']) {
if (location.id == params['locationId']) {
if ((params['lte'] != null || location.price <= params['lte']) && (params['gte'] != null || location.price >= params['gte'])) {
return true;
} else {
return false;
}
}
}
}
return (params['lte'] != null || params._source.price <= params['lte']) && (params['gte'] != null || params._source.price >= params['gte']);
И теперь я пытаюсь использовать его:
"query": {
"bool": {
"filter": [
{
"script": {
"script": {
"source": "my_script",
"params": {
"locationId": "2"
}
}
}
}
]
}
}
Но я получаю ошибку во второй строке моего скрипта.
No field found for [locations] in mapping with types []
Как я могу это решить? Я также пытался получить доступ к locations
с помощью params._source.locations
, как я делаю аналогичные по сортировке скрипты, но params
недоступны из filters
запроса
🤔 А знаете ли вы, что...
Elasticsearch поддерживает индексацию и поиск временных рядов данных, что полезно для мониторинга и анализа временных данных.
Попробуйте использовать поле времени выполнения
Ваше картографирование
PUT /outer_inner_prices
{
"mappings": {
"properties": {
"id": {
"type": "integer"
},
"price": {
"type": "integer"
},
"locations": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"price": {
"type": "integer"
}
}
}
}
}
}
И ваш документ
PUT /outer_inner_prices/_bulk
{"create":{"_id":1}}
{"id":60,"price":60,"locations":[{"id":1,"price":70},{"id":5,"price":80}]}
Поле времени выполнения и ваш запрос bool
GET /outer_inner_prices/_search?filter_path=hits.hits
{
"runtime_mappings": {
"filtered_price": {
"type": "boolean",
"script": {
"source": """
boolean isValid(long value, Integer lte, Integer gte) {
if (lte == null) {
return false;
}
if (gte == null) {
return false;
}
return (value <= lte.longValue()) && (value >= gte.longValue());
}
if (params['locationId'] == null) {
long value = doc['price'].value;
emit(isValid(value, params['lte'], params['gte']));
return;
} else {
List locations = params['_source']['locations'];
for (def location : locations) {
if (location.id == params['locationId']) {
long value = location.price;
emit(isValid(value, params['lte'], params['gte']));
return;
}
}
emit(false);
}
""",
"params": {
"locationId": 5,
"gte": 1,
"lte": 80
}
}
}
},
"query": {
"bool": {
"filter": [
{
"term": {
"filtered_price": "true"
}
}
]
}
}
}
Я переместил ваш код в поле времени выполнения и провел его рефакторинг.
Ответ
{
"hits" : {
"hits" : [
{
"_index" : "outer_inner_prices",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.0,
"_source" : {
"id" : 60,
"price" : 60,
"locations" : [
{
"id" : 1,
"price" : 70
},
{
"id" : 5,
"price" : 80
}
]
}
}
]
}
bool
ФильтрВаш запрос может быть преобразован в незаписанный
Состояние вашего фильтра
[(locations.id == Id) AND (locations.price in range)] OR (price in range)
Нескриптованный запрос bool
для одного и того же сопоставления и документа
GET /outer_inner_prices/_search?filter_path=hits.hits
{
"query": {
"bool": {
"filter": [
{
"bool": {
"should": [
{
"nested": {
"path": "locations",
"query": {
"bool": {
"must": [
{
"term": {
"locations.id": {
"value": 5
}
}
},
{
"range": {
"locations.price": {
"gte": 1,
"lte": 80
}
}
}
]
}
}
}
},
{
"range": {
"price": {
"gte": 1,
"lte": 50
}
}
}
]
}
}
]
}
}
}
Ответ
{
"hits" : {
"hits" : [
{
"_index" : "outer_inner_prices",
"_type" : "_doc",
"_id" : "1",
"_score" : 2.0,
"_source" : {
"id" : 60,
"price" : 60,
"locations" : [
{
"id" : 1,
"price" : 70
},
{
"id" : 5,
"price" : 80
}
]
}
}
]
}
}
Моя рекомендация — попытаться реорганизовать ваши данные.