С сортировкой ElasticSearch по заданному массиву

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

weights: [{'id': 'mark', 'weight': 1}, {'id': 'jane', 'weight': 3}]

Мне нужно запросить каналы, сгруппированные по дате, но упорядоченные по таким весам. Я попытался сделать функцию сортировки безболезненной, но я застрял в определении весов:

{
    "size": 0,
    "query": {
        "bool": {
            "should": [
                {
                    "bool": {
                        "must": [
                            {
                                "term": {
                                    "actor.id": "mark"
                                }
                            },
                            {
                                "range": {
                                    "published": {"gte": "2017-09-30T15:37:21.530483"}
                                }
                            }
                        ]
                    }
                },
                {
                    "bool": {
                        "must": [
                            {
                                "term": {
                                    "actor.id": "jane"
                                }
                            },
                            {
                                "range": {
                                    "published": {"gte": "2017-09-30T15:37:21.530483"}
                                }
                            }
                        ]
                    }
                }
            ]
        }
    },
    "aggs": {
        "dates": {
            "terms": {
                "field": "published_date",
            },
            "aggs": {
                "top_verbs_hits": {
                    "top_hits": {
                        "sort": {
                            "_script": {
                                "type": "number",
                                "script": {
                                    "lang": "painless",
                                    "source": "def weights = [{'id': 'mark', 'weight': 1}, {'id': 'jane', 'weight': 3}]; def weight = 0; for (int i = 0; i < weights.length; ++i) { if (weights[i].id == doc.actor.id) return weights[i].weight; } return weight;"
                                },
                                "order": "asc"
                            }
                        },
                        "_source": {
                            "includes": ["published", "actor", "object", "target", "extra"]
                        },
                        "size": 100
                    }
                }
            }
        }
    },
    "sort": [
        {
            "published": {
                "order": "desc"
            }
        }
    ],
}

Для ясности безболезненная функция выглядит следующим образом:

def weights = [{'id': 'mark', 'weight': 1}, {'id': 'jane', 'weight': 3}]; 
def weight = 0; 
for (int i = 0; i < weights.length; ++i) 
{ 
    if (weights[i].id == doc.actor.id) 
    return weights[i].weight; 
} 
return weight;

Elastic дает мне ошибку компиляции рядом с определением массива. Я предполагаю, что я не могу определить список/массив объектов JSON:

compile error","script_stack":["def weights = [{'id': 'mark', 'weight ...","               ^---- HERE"]....

Есть ли способ сделать это с помощью скрипта сортировки или без него?


person QLands    schedule 01.10.2018    source источник


Ответы (1)


Painless — это не язык, похожий на javascript. Вы не можете просто определить массив с синтаксисом, подобным JSON.

Вы можете получить полную документацию здесь для массива. Также у вас есть создать карту для представления ваших объектов JSON.

Но в вашем случае вы должны обязательно использовать параметры скрипта

Не могли бы вы попробовать что-то вроде:

"sort": {
    "_script": {
        "type": "number",
        "script": {
            "lang": "painless",
            "source": "def weight = 0; for (int i = 0; i < params.weights.length; ++i) { if (params.weights[i].id == doc['actor.id'].value) return params.weights[i].weight; } return weight;"
            "params": {
              "weights" :[{'id': 'mark', 'weight': 1}, {'id': 'jane', 'weight': 3}]
            } 
        },
        "order": "asc"
    }
}

Используя параметры, вы можете определить свои входные данные с синтаксисом JSON И, кроме того, вы позволяете elasticsearch кэшировать скомпилированную версию вашего скрипта, поскольку источник останется прежним, даже если массив весов изменится.

person Pierre Mallet    schedule 01.10.2018
comment
Превосходно! Просто подправил сравнение внутри for с if (params.weights[i].id == doc['actor.id'].value) и работает хорошо. Большое спасибо - person QLands; 01.10.2018