Elasticsearch безболезненно обновляет список уникальных элементов

РЕДАКТИРОВАТЬ:

Моя проблема была вызвана ";" в конце строки, о чем я мог догадаться, прочитав полное сообщение об ошибке:

'error': {
    'type': 'illegal_argument_exception', 
    'reason': 'failed to execute script', 
    'caused_by': {
        'type': 'script_exception', 
        'reason': 'compile error', 
        'script_stack': ['... dd(params.collected_via)};
        ctx ...,                              ^---- HERE'],
        'caused_by': {
            'type': 'illegal_argument_exception', 
            'reason': "unexpected token [';'] was expecting one of [<EOF>]."
        }
    }
}

Я исправил это, переключив 2 строки в моем третьем скриптовом решении:

"script": {
    "source": "ctx._source.internal_count = params.internal_count; \
    if (!ctx._source.collected_via.contains(params.collected_via)) { ctx._source.collected_via.add(params.collected_via) }",
    "lang": "painless",
    "params": {
        "collected_via": my_tag,
        "internal_count": my_count
    }

================================================== ===================

ОРИГИНАЛЬНЫЙ ВОПРОС:

Поле "collection_via" в нашем индексе elasticsearch (6.8) представляет собой список тегов. При обновлении документов список тегов обновляется с помощью следующего скрипта:

"script": {
    "source": "ctx._source.collected_via.add(params.collected_via); \
    ctx._source.internal_count = params.internal_count",
    "lang": "painless",
    "params": {
        "collected_via": my_tag,
        "internal_count": my_count
    }

Как преобразовать этот скрипт, чтобы каждый тег добавлялся в список только один раз? Вот решения, которые я пробовал и которые не работают ("причина": "не удалось выполнить скрипт"):

"script": {
    "source": "if (ctx._source.collected_via.contains(params.collected_via)) {} { ctx._source.collected_via.add(params.collected_via) }; \
    ctx._source.internal_count = params.internal_count",
    "lang": "painless",
    "params": {
        "collected_via": my_tag,
        "internal_count": my_count
    }
"script": {
    "source": "ctx._source.collected_via = ctx._source.collected_via.add(params.collected_via).unique(); \
    ctx._source.internal_count = params.internal_count",
    "lang": "painless",
    "params": {
        "collected_via": my_tag,
        "internal_count": my_count
    }
"script": {
    "source": "if (!ctx._source.collected_via.contains(params.collected_via)) { ctx._source.collected_via.add(params.collected_via) }; \
    ctx._source.internal_count = params.internal_count",
    "lang": "painless",
    "params": {
        "collected_via": my_tag,
        "internal_count": my_count
    }

1 ответ

Математика спешит на помощь! Лучший способ добиться этого - просто использовать Set, который по определению всегда будет содержать только одно вхождение данного элемента.

// 1. create a set out of the current list
def set = new HashSet(ctx._source.collected_via);

// 2. add all new elements to the set
set.add(params.collected_via);

// 3. convert the set back to a list
ctx._source.collected_via = new ArrayList(set);