ElasticSearch - Mengembalikan Nilai Unik


122

Bagaimana saya mendapatkan nilai dari semua languagescatatan dan membuatnya unik.

Rekaman

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

Pertanyaan

GET items/_search
{ ... }

# => Expected Response
[10, 11]

Bantuan apa pun akan sangat bagus.


1
fields: [languages]hanya akan memberikan nilai dari bidang tertentu, tetapi membuatnya unik mungkin lebih mudah dilakukan dalam kode. Meskipun mungkin ada kumpulan praktis yang dapat melakukannya untuk Anda.
Ashalynd

1
Bagi mereka yang meneliti topik ini, ada juga diskusi yang berguna di sini: Temukan nilai yang berbeda, bukan hitungan yang berbeda di elasticsearch
blong

Jawaban:


165

Anda dapat menggunakan istilah agregasi .

{
"size": 0,
"aggs" : {
    "langs" : {
        "terms" : { "field" : "language",  "size" : 500 }
    }
}}

Pencarian akan menghasilkan sesuatu seperti:

{
"took" : 16,
"timed_out" : false,
"_shards" : {
  "total" : 2,
  "successful" : 2,
  "failed" : 0
},
"hits" : {
"total" : 1000000,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
  "langs" : {
    "buckets" : [ {
      "key" : "10",
      "doc_count" : 244812
    }, {
      "key" : "11",
      "doc_count" : 136794

    }, {
      "key" : "12",
      "doc_count" : 32312
       } ]
    }
  }
}

The sizeparameter dalam menspesifikasikan agregasi jumlah maksimum istilah untuk menyertakan dalam hasil agregasi. Jika Anda membutuhkan semua hasil, setel ini ke nilai yang lebih besar dari jumlah istilah unik dalam data Anda.


2
"fields" : ["language"]mengembalikan hasil yang sama. Bisakah Anda memperluas jawaban Anda untuk melihat apakah kerangka agregasi hanya dapat mengembalikan nilai bahasa? #=> [10, 11, 10]
ChuckJHardy

1
@CharlesJHardy, ini tidak memiliki hasil yang sama. Data yang Anda cari berada di bawah kunci "agregasi". Saya mengedit jawaban saya dengan contoh hasil. Anda dapat / harus juga menyetel "size": 0, untuk tidak menyertakan dokumen apa pun, hanya hasil gabungan yang Anda inginkan.
Anton

1
Perhatikan bahwa jika Anda memiliki banyak kemungkinan nilai, languageAnda mungkin ingin menambahkan size=0dan shard_size=0, untuk memastikan Anda mendapatkan semua nilai. Lihat elasticsearch.org/guide/en/elasticsearch/reference/current/…
Dror

3
Saya pikir jawaban ini tidak membahas OP. Pertanyaan asli menginginkan nilai yang berbeda tidak dihitung. Apakah saya melewatkan sesuatu?
bhurlow

4
@BHBH, Jawabannya memang memberikan nilai yang berbeda. Mereka adalah nilai "kunci", yaitu "10", "11" dan "12". (agregasi> bahasa> keranjang> kunci ...)
Anton

9

Elasticsearch 1.1+ memiliki Agregasi Kardinalitas yang akan memberi Anda hitungan unik

Perhatikan bahwa ini sebenarnya adalah perkiraan dan akurasi dapat berkurang dengan kumpulan data berkardinalitas tinggi, tetapi umumnya cukup akurat dalam pengujian saya.

Anda juga dapat menyetel akurasi dengan precision_thresholdparameter. Trade-off, atau tentu saja, adalah penggunaan memori.

Grafik dari dokumen ini menunjukkan bagaimana hasil yang lebih tinggi precision_thresholdmengarah ke hasil yang jauh lebih akurat.


Kesalahan relatif vs ambang batas


2
Apakah Kardinalitas Agregasi menjamin bahwa jika ada istilah, maka akan muncul di hasil (dengan hitungan> = 1)? Atau mungkinkah melewatkan beberapa istilah yang hanya muncul sekali dalam kumpulan data besar?
tandai

2
@ tandai itu tergantung pada ambang presisi yang Anda tetapkan. Semakin tinggi ambang batasnya, semakin kecil kemungkinannya meleset. Perhatikan bahwa ada batasan 40.000 dalam pengaturan ambang presisi. Artinya, kumpulan data yang lebih tinggi dari itu, akan ada perkiraan dan karenanya nilai tunggal mungkin terlewatkan
Sundar

12
Saya yakin jawaban ini salah. Agregasi kardinalitas adalah alat yang sangat baik. Namun, tugasnya adalah mengambil kembali istilah itu sendiri, bukan memperkirakan berapa banyak istilah berbeda yang ada.
Anton

4

jika Anda ingin mendapatkan dokumen pertama untuk setiap languagebidang nilai unik, Anda dapat melakukan ini:

{
 "query": {
    "match_all": {
    }
  },
  "collapse": {
    "field": "language.keyword",
    "inner_hits": {
    "name": "latest",
      "size": 1
    }
  }
}

3

Saya mencari solusi semacam ini untuk diri saya juga. Saya menemukan referensi dalam istilah agregasi .

Nah, berikut ini adalah solusi yang tepat.

{
"aggs" : {
    "langs" : {
        "terms" : { "field" : "language",  
                    "size" : 500 }
    }
}}

Tetapi jika Anda mengalami kesalahan berikut:

"error": {
        "root_cause": [
            {
                "type": "illegal_argument_exception",
                "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [fastest_method] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
            }
        ]}

Dalam hal ini, Anda harus menambahkan " KEYWORD " dalam permintaan, seperti berikut:

   {
    "aggs" : {
        "langs" : {
            "terms" : { "field" : "language.keyword",  
                        "size" : 500 }
        }
    }}

1

Jika Anda ingin mendapatkan semua nilai unik tanpa perkiraan atau menyetel angka ajaib ( size: 500), gunakan AGREGASI KOMPOSIT (ES 6.5+) .

Dari dokumentasi resmi :

"Jika Anda ingin mengambil semua istilah atau semua kombinasi istilah dalam agregasi istilah bertingkat, Anda harus menggunakan AGREGASI KOMPOSIT yang memungkinkan pemberian nomor halaman pada semua istilah yang memungkinkan daripada menyetel ukuran yang lebih besar daripada kardinalitas bidang dalam agregasi istilah. agregasi istilah dimaksudkan untuk mengembalikan istilah teratas dan tidak mengizinkan penomoran halaman. "

Contoh implementasi di JavaScript:

const ITEMS_PER_PAGE = 1000;

const body =  {
    "size": 0, // Returning only aggregation results: https://www.elastic.co/guide/en/elasticsearch/reference/current/returning-only-agg-results.html
    "aggs" : {
        "langs": {
            "composite" : {
                "size": ITEMS_PER_PAGE,
                "sources" : [
                    { "language": { "terms" : { "field": "language" } } }
                ]
            }
        }
     }
};

const uniqueLanguages = [];

while (true) {
  const result = await es.search(body);

  const currentUniqueLangs = result.aggregations.langs.buckets.map(bucket => bucket.key);

  uniqueLanguages.push(...currentUniqueLangs);

  const after = result.aggregations.langs.after_key;

  if (after) {
      // continue paginating unique items
      body.aggs.langs.composite.after = after;
  } else {
      break;
  }
}

console.log(uniqueLanguages);

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.