2015-10-20 5 views
1

У меня есть 3 миллионов элементов с этой структурой:Как отсортировать элементы по размеру массива в ElasticSearch?

{ 
    "id": "some_id", 
    "title": "some_title", 
    "photos": [ 
     {...}, 
     {...}, 
     ... 
    ] 
} 

Некоторые элементы могут иметь пустое photos поле:

{ 
    "id": "some_id", 
    "title": "some_title", 
    "photos": [] 
} 

Я хочу, чтобы отсортировать по количеству фотографий, чтобы привести к элементам без фотографий были в конец списка.

У меня есть один рабочий раствор, но это очень медленно на 3 миллиона единиц:

GET myitems/_search 
{ 
    "filter": { 
     ...some filters... 
    }, 
    "sort": [ 
     { 
      "_script": { 
       "script": "_source.photos.size()", 
       "type": "number", 
       "order": "desc" 
      } 
     } 
    ] 
} 

Этот запрос выполняется 55 секунд. Как оптимизировать этот запрос?

+2

Можно ли просто сохранить размер массива в отдельном поле? – Bertvan

+0

Да, это может быть единственный вариант ^^. –

+0

@Bertvan, это вызовет переиндексацию :( – acelot

ответ

0

Как было предложено в комментариях, добавление нового поля с количеством фотографий было бы возможным. Есть способ достичь этого без переиндексации все ваши данные с помощью update by query plugin.

В принципе, после установки плагина вы можете запустить следующий запрос, и все ваши документы получат это новое поле. Тем не менее, убедитесь, что ваш процесс индексирования также заполнит, что новое поле в новых документах:

curl -XPOST 'localhost:9200/myitems/_update_by_query' -d '{ 
    "query" : { 
     "match_all" : {} 
    }, 
    "script" : "ctx._source.nb_photos = ctx._source.photos.size();" 
}' 

После этого закончилось, вы будете иметь возможность сортировать ваши результаты просто с:

"sort": {"nb_photos": "desc"} 

Примечания : для этого плагина необходимо иметь scripting enabled, это уже так, потому что вы могли использовать скрипт сортировки, но я просто упомянул об этом для полноты.

+0

Это будет работать с данными, которые не будут меняться. Если новые записи будут добавлены к эластичному, они не будут иметь правильное значение. В сочетании с подходом, который будет вычислять данные перед вставкой, он также способен обрабатывать новые данные. – Bertvan

+0

Вот почему я упомянул, что он должен убедиться, что «... процесс индексирования также заполняет это новое поле в новых документах» ;-) – Val

0

Проблема была решена с помощью директивы Transform. Теперь у меня есть отображение:

PUT /myitems/_mapping/lol 
{ 
    "lol" : { 
     "transform": { 
      "lang": "groovy", 
      "script": "ctx._source['has_photos'] = ctx._source['photos'].size() > 0" 
     }, 
     "properties" : { 
      ... fields ... 
      "photos" : {"type": "object"}, 
      "has_photos": {"type": "boolean"} 
      ... fields ... 
     } 
    } 
} 

Теперь можно сортировать товары по фотографии существования:

GET /test/_search 
{ 
    "sort": [ 
     { 
      "has_photos": { 
       "order": "desc" 
      } 
     } 
    ] 
} 

К сожалению, это приведет к полной переиндексации.

+0

Если вы просто хотите иметь документы с 0 фотографиями в конце, вы также можете сделайте 2 запроса (возможно, multiquery), где вы используете фильтр match_all для фотографий для первого, а не (match_all) для второго. – Bertvan

Смежные вопросы