Cara mencari bagian kata dengan ElasticSearch


128

Saya baru-baru ini mulai menggunakan ElasticSearch dan sepertinya saya tidak dapat membuatnya mencari bagian kata.

Contoh: Saya punya tiga dokumen dari couchdb saya yang diindeks di ElasticSearch:

{
  "_id" : "1",
  "name" : "John Doeman",
  "function" : "Janitor"
}
{
  "_id" : "2",
  "name" : "Jane Doewoman",
  "function" : "Teacher"
}
{
  "_id" : "3",
  "name" : "Jimmy Jackal",
  "function" : "Student"
} 

Jadi sekarang, saya ingin mencari semua dokumen yang mengandung "Doe"

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

Itu tidak menghasilkan hit. Tetapi jika saya mencari

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

Itu mengembalikan satu dokumen (John Doeman).

Saya telah mencoba menetapkan analisator dan filter yang berbeda sebagai properti indeks saya. Saya juga telah mencoba menggunakan kueri penuh yang meledak (misalnya:

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

) Tapi sepertinya tidak ada yang berhasil.

Bagaimana saya dapat membuat ElasticSearch menemukan John Doeman dan Jane Doewoman ketika saya mencari "Doe"?

MEMPERBARUI

Saya mencoba menggunakan tokenizer dan filter nGram, seperti yang diusulkan Igor, seperti ini:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "bulk_size": "100",
    "bulk_timeout": "10ms",
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "my_ngram_filter"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      }
    }
  }
}

Masalah yang saya alami sekarang adalah setiap query mengembalikan SEMUA dokumen. Ada petunjuk? Dokumentasi ElasticSearch tentang penggunaan nGram tidak bagus ...


9
tidak heran, Anda harus mengatur min / max ngram ke 1, jadi 1 huruf :)
Martin B.

Jawaban:


85

Saya menggunakan nGram juga. Saya menggunakan tokenizer standar dan nGram hanya sebagai filter. Ini pengaturan saya:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "analysis": {
      "index_analyzer": {
        "my_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "mynGram"
          ]
        }
      },
      "search_analyzer": {
        "my_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "mynGram"
          ]
        }
      },
      "filter": {
        "mynGram": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 50
        }
      }
    }
  }
}

Biarkan Anda menemukan bagian kata hingga 50 huruf. Sesuaikan max_gram yang Anda butuhkan. Dalam bahasa Jerman kata-kata bisa menjadi sangat besar, jadi saya mengaturnya ke nilai yang tinggi.



Apakah itu yang Anda dapatkan dari pengaturan indeks atau apakah itu yang Anda poskan ke elasticsearch untuk mengkonfigurasinya?
Tomas Jansson

Ini POST untuk mengonfigurasi Elasticsearch.
roka

Saya tidak tegas dengan versi Elasticsearch saat ini, tetapi harus menyebutkannya dalam dokumen: elastic.co/guide/en/elasticsearch/reference/current/index.html
roka

1
@JimC Saya belum menggunakan ElasticSearch selama setidaknya 7 tahun, jadi saya tidak tahu perubahan proyek saat ini.
roka

63

Pencarian dengan wildcard terkemuka dan tertinggal akan sangat lambat pada indeks besar. Jika Anda ingin dapat mencari dengan awalan kata, hapus wildcard terkemuka. Jika Anda benar-benar perlu menemukan substring di tengah kata, Anda akan lebih baik menggunakan tokenizer ngram.


14
Igor benar. Setidaknya hapus yang terkemuka *. Untuk contoh NGram ElasticSearch, lihat intisari ini: gist.github.com/988923
karmi

3
@karmi: Terima kasih atas contoh lengkap Anda! Mungkin Anda ingin menambahkan komentar Anda sebagai jawaban yang sebenarnya, itulah yang membuatnya bekerja untuk saya dan apa yang ingin saya utarakan.
Fabian Steeg

54

Saya pikir tidak perlu mengubah pemetaan apa pun. Coba gunakan query_string , itu sempurna. Semua skenario akan berfungsi dengan penganalisa standar standar:

Kami memiliki data:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Skenario 1:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }

Tanggapan:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Skenario 2:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }

Tanggapan:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}

Skenario 3:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }

Tanggapan:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

EDIT - Implementasi yang sama dengan pencarian elastis data pegas https://stackoverflow.com/a/43579948/2357869

Satu lagi penjelasan bagaimana query_string lebih baik daripada yang lain https://stackoverflow.com/a/43321606/2357869


3
saya pikir ini yang paling mudah
Esgi Dendyanri

Iya . Saya telah menerapkan proyek saya.
Opster Elasticsearch Pro-Vijay

Bagaimana cara memasukkan beberapa bidang untuk dicari?
Shubham A.

coba ini: - {"kueri": {"kueri_string": {"bidang": ["konten", "nama"], "kueri": "ini DAN itu"}}}
Opster Elasticsearch Pro-Vijay


14

tanpa mengubah pemetaan indeks Anda, Anda bisa melakukan permintaan awalan sederhana yang akan melakukan pencarian parsial seperti yang Anda harapkan

yaitu.

{
  "query": { 
    "prefix" : { "name" : "Doe" }
  }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html


dapatkah Anda melakukan pencarian multi bidang menggunakan kueri awalan?
batmaci

Terima kasih, apa yang saya cari! Adakah pemikiran tentang dampak kinerja?
Vingtoft

6

Coba solusinya dengan dijelaskan di sini: Pencarian Substring yang Tepat dalam ElasticSearch

{
    "mappings": {
        "my_type": {
            "index_analyzer":"index_ngram",
            "search_analyzer":"search_ngram"
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "ngram_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            },
            "analyzer": {
                "index_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [ "ngram_filter", "lowercase" ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": "lowercase"
                }
            }
        }
    }
}

Untuk mengatasi masalah penggunaan disk dan masalah istilah pencarian yang terlalu panjang, digunakan karakter pendek dengan 8 karakter ngram (dikonfigurasi dengan: "max_gram": 8 ). Untuk mencari istilah dengan lebih dari 8 karakter, ubah pencarian Anda menjadi boolean DAN kueri yang mencari setiap substring 8 karakter yang berbeda dalam string itu. Misalnya, jika pengguna mencari halaman besar (string 10 karakter), pencariannya adalah:

"Arge ya DAN arge yar DAN rge yard .


2
tautan mati, mohon perbaiki
DarkMukke

Saya telah mencari sesuatu seperti ini untuk sementara waktu. Terima kasih! Apakah Anda tahu bagaimana skala memori dengan min_gramdan max_gramsepertinya itu akan tergantung secara linear pada ukuran nilai bidang dan rentang mindan max. Seberapa disukai menggunakan sesuatu seperti ini?
Glen Thompson

Juga adakah alasan mengapa ngramfilter ini menggunakan tokenizer? dapatkah Anda tidak hanya memilikinya sebagai tokenizer dan kemudian menerapkan filter huruf kecil ... index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }Saya mencobanya dan tampaknya memberikan hasil yang sama dengan menggunakan uji penganalisa api
Glen Thompson

2

Jika Anda ingin menerapkan fungsi autocomplete, maka Completion Suggester adalah solusi yang paling rapi. Posting blog berikutnya berisi deskripsi yang sangat jelas bagaimana ini bekerja.

Dalam dua kata, ini adalah struktur data dalam memori yang disebut FST yang berisi saran yang valid dan dioptimalkan untuk pengambilan cepat dan penggunaan memori. Pada dasarnya, ini hanyalah sebuah grafik. Misalnya, dan FST mengandung kata-kata hotel, marriot, mercure, munchendan munichakan terlihat seperti ini:

masukkan deskripsi gambar di sini


2

Anda dapat menggunakan regexp.

{ "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"}
{ "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher"  }
{ "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student"  } 

jika Anda menggunakan kueri ini:

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

Anda akan memberikan semua data yang namanya dimulai dengan "J". Pertimbangkan Anda ingin menerima hanya dua catatan pertama yang namanya diakhiri dengan "man" sehingga Anda dapat menggunakan kueri ini:

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

dan jika Anda ingin menerima semua catatan yang ada namanya "m", Anda dapat menggunakan pertanyaan ini:

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

Ini bekerja untuk saya. Dan saya harap jawaban saya cocok untuk menyelesaikan masalah Anda.


1

Menggunakan kartu wil (*) mencegah perhitungan skor


1
Bisakah Anda menambahkan lebih banyak detail pada jawaban Anda? Berikan kode contoh atau rujukan ke dokumentasi tentang apa yang dilakukannya.
Cray

0

Saya menggunakan ini dan membuat saya bekerja

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

-6

Lupakan.

Saya harus melihat dokumentasi Lucene. Sepertinya saya bisa menggunakan wildcard! :-)

curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*

lakukan triknya!


11
Lihat jawaban @imotov. Penggunaan wildcard tidak akan berskala sama sekali.
Mike Munroe

5
@ IDx - Lihat bagaimana jawaban Anda sendiri diturunkan. Downvotes mewakili bagaimana kualitas dan relevansi suatu jawaban. Bisakah Anda menyisihkan waktu sebentar untuk menerima jawaban yang benar? Setidaknya pengguna baru akan berterima kasih kepada Anda.
asyncunggu

3
Downvotes cukup. OP menjelaskan apa jawaban terbaik saat ini. +1 untuk membagikan apa yang tampaknya menjadi jawaban terbaik sebelum seseorang mengeposkan yang lebih baik.
s.Daniel
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.