web-dev-qa-db-fra.com

ElasticSearch P inde script: comment itérer dans un tableau d'objets imbriqués

J'essaie de créer un script en utilisant le script_score du function_score. J'ai plusieurs documents dont le champ rankings est type="nested". Le mappage de ce champ est le suivant:

"rankings": {
        "type": "nested",
        "properties": {
          "rank1": {
            "type": "long"
          },
          "rank2": {
            "type": "float"
          },
          "subject": {
            "type": "text"
          }
        }
      }

Un exemple de document est:

"rankings": [
{
    "rank1": 1051,
    "rank2": 78.5,
    "subject": "s1"
},
{
    "rank1": 45,
    "rank2": 34.7,
    "subject": "s2"
}]

Ce que je veux réaliser est de parcourir les objets imbriqués du classement. En fait, j’ai besoin d’utiliser une boucle for pour trouver un subject particulier et utiliser le rank1, rank2 pour calculer quelque chose . Jusqu’à présent, j’utilise quelque chose comme ceci mais cela ne semble pas fonctionner (en générant une erreur Compile):

"function_score": {
"script_score": {
    "script": {
        "lang": "painless",
        "inline": 
                 "sum = 0;"
                 "for (item in doc['rankings_cug']) {"
                     "sum = sum + doc['rankings_cug.rank1'].value;"
                 "}"
         }
    }
}

J'ai aussi essayé les options suivantes:

  1. Boucle for utilisant : au lieu de in: for (item:doc['rankings']) sans succès.
  2. Boucle for utilisant in mais en essayant de parcourir un élément spécifique de l'objet, c'est-à-dire le rank1: for (item in doc['rankings.rank1'].values), qui compile réellement, mais il semble qu'il trouve un tableau de longueur nulle de rank1.

J'ai lu que l'élément _source est celui qui peut renvoyer des objets de type JSON, mais d'après ce que j'ai découvert, il n'est pas pris en charge dans les requêtes de recherche.

Pouvez-vous me donner quelques idées sur la manière de procéder?

Merci beaucoup.

8
christinabo

Vous pouvez accéder à _source via params._source. Celui-ci fonctionnera:

PUT /rankings/result/1?refresh
{
  "rankings": [
    {
      "rank1": 1051,
      "rank2": 78.5,
      "subject": "s1"
    },
    {
      "rank1": 45,
      "rank2": 34.7,
      "subject": "s2"
    }
  ]
}

POST rankings/_search

POST rankings/_search
{
  "query": {
    "match": {
      "_id": "1"
    }
  },
  "script_fields": {
    "script_score": {
      "script": {
        "lang": "painless",
        "inline": "double sum = 0.0; for (item in params._source.rankings) { sum += item.rank2; } return sum;"
      }
    }
  }
}

DELETE rankings
7
Rahul Singhai

Malheureusement, les scripts ElasticSearch en général ne permettent pas d'accéder aux documents imbriqués de cette manière (y compris à l'indolore). Envisagez peut-être une structure différente de vos mappages, dans laquelle les classements sont stockés dans des champs à valeurs multiples si vous devez pouvoir les parcourir de cette manière. En fin de compte, les données imbriquées devront être dé-normalisées et placées dans les documents parents pour pouvoir obtenir des scores de la manière décrite ici.

5
jdconrad

Pour les objets imbriqués dans un tableau, itérés sur les éléments et cela a fonctionné ... Voici mon exemple de données dans elasticsearch index:

{
  "_index": "activity_index",
  "_type": "log",
  "_id": "AVjx0UTvgHp45Y_tQP6z",
  "_version": 4,
  "found": true,
  "_source": {
    "updated": "2016-12-11T22:56:13.548641",
    "task_log": [
      {
        "week_end_date": "2016-12-11",
        "log_hours": 16,
        "week_start_date": "2016-12-05"
      },
      {
        "week_start_date": "2016-03-21",
        "log_hours": 0,
        "week_end_date": "2016-03-27"
      },
      {
        "week_start_date": "2016-04-24",
        "log_hours": 0,
        "week_end_date": "2016-04-30"
      }
    ],
    "created": "2016-12-11T22:56:13.548635",
    "userid": 895,
    "misc": {

    },
    "current": false,
    "taskid": 1023829
  }
}

Voici le script "indolore" pour parcourir les objets imbriqués:

{
  "script": {
    "lang": "painless",
    "inline": 
        "boolean contains(def x, def y) {
          for (item in x) {
            if (item['week_start_date'] == y){
              return true
            }
          }
          return false 
         }
         if(!contains(ctx._source.task_log, params.start_time_param) {
           ctx._source.task_log.add(params.week_object)
         }",
         "params": {
            "start_time_param": "2016-04-24",
             "week_object": {
               "week_start_date": "2016-04-24",
               "week_end_date": "2016-04-30",
               "log_hours": 0
              }
          }
  }
}

Script utilisé ci-dessus pour la mise à jour: /activity_index/log/AVjx0UTvgHp45Y_tQP6z/_update Dans le script, a créé une fonction appelée "contient" avec deux arguments. Appelé la fonction . L'ancien style groovy: ctx._source.task_log.contains () ne fonctionnera pas car ES 5.X stocke les objets imbriqués dans un document séparé. J'espère que ça aide!

3
Priyesh