Tentang apa ini
Bagaimana cara melakukan pencarian segi di toko online? Bagaimana nilai dihasilkan dalam filter pencarian segi? Bagaimana memilih nilai dalam filter memengaruhi nilai di filter yang berdekatan? Dalam mencari jawaban, saya mencapai halaman kelima hasil pencarian Google. Saya tidak menemukan informasi yang lengkap, saya harus mencari tahu sendiri. Artikel tersebut menjelaskan:
- bagaimana UI bereaksi saat pengguna menggunakan filter;
- algoritma untuk menghasilkan nilai filter;
- Templat kueri ElasticSearch dan struktur indeks dengan penjelasan.
Tidak ada solusi yang siap pakai di sini. Anda tidak dapat menyalin dan menempel. Untuk mengatasi masalah Anda sendiri, Anda harus mendalami.

Konsep untuk membuatnya jelas
Pencarian teks lengkap - mencari produk dengan kata atau frase. Untuk pengguna, ini adalah bidang untuk memasukkan teks dengan tombol "Temukan", yang tersedia di halaman mana pun di situs.
Pencarian segi - mencari produk dengan beberapa karakteristik: warna, ukuran, ukuran memori, harga, dll. Untuk pengguna, ini adalah sekumpulan filter. Setiap filter hanya dikaitkan dengan satu karakteristik dan sebaliknya. Nilai filter adalah semua nilai yang mungkin untuk suatu karakteristik. Pengguna melihat filter pada halaman bagian, kategori, pada halaman dengan hasil pencarian teks lengkap. Saat pengguna memilih nilai, filter dianggap aktif.
Perilaku filter segi
Singkatnya, filter memfilter produk dan memfilter opsi pemilihan di filter lain.
Memfilter produk
Sangat mudah dengan ini. Pengguna memilih:
- satu nilai, melihat produk yang sesuai dengan nilai;
- beberapa nilai dalam satu filter, melihat produk yang setidaknya cocok dengan satu;
- nilai di beberapa filter, melihat produk yang sesuai dengan nilai dari setiap filter.
Dalam istilah aljabar Boolean: ada logika "AND" di antara filter, logika "OR" di antara nilai-nilai dalam filter . Logika sederhana.
Memfilter pilihan di filter lain
"Nah ... opsi apa yang ada - ditampilkan, apa yang tidak - tersembunyi" - begitulah cara bisnis menjelaskan perilaku filter. Kedengarannya logis. Dalam praktiknya, ini berfungsi seperti ini:
- Pergi ke bagian Telepon, lihat filter menurut karakteristik: Merek, Diagonal, Memori. Setiap filter berisi nilai.
- . . 1.
- . , . , 2.
- . . , 3.
- «» . 3 ..
Jumlah nilai filter bergantung pada jumlah produk: semakin banyak produk dengan nilai karakteristik berbeda, semakin banyak nilai dalam filter. Pengguna mengurangi jumlah produk dalam pemilihan untuk filter yang tersisa saat mereka memilih merek. Hal ini menghasilkan pembaruan daftar nilai.
Ini menyiratkan aturan universal: nilai filter diambil dari pilihan produk yang dibentuk oleh filter aktif lainnya.
Setiap filter aktif memiliki pilihan produknya sendiri.
Jika kami memiliki N filter dan:
- tidak aktif, maka sampelnya umum. Ini sama untuk semua filter dan cocok dengan hasil pencarian;
- M aktif, dan M <N, maka jumlah sampel adalah M + 1, di mana 1 adalah sampel yang semua filter aktif diterapkan. Itu sama untuk semua filter tidak aktif dan bertepatan dengan hasil pencarian;
- aktif M, dan N = M, maka banyaknya sampel N. Setiap filter memiliki sampelnya sendiri-sendiri.
Akhirnya, saat pengguna memilih nilai filter faset, hal berikut akan terjadi:
- pemilihan pencarian barang dibentuk;
- nilai untuk filter tidak aktif diambil dari pilihan pencarian;
- untuk setiap filter aktif, sampel baru dibentuk dan nilai baru filter aktif diambil darinya.
Muncul pertanyaan - bagaimana menerapkan ini dalam praktik?
Penerapan Elasticsearch (ES)
Karakteristik produk tidak universal, jadi Anda tidak akan menemukan struktur indeks siap pakai di sini untuk menyimpan produk atau kueri yang sudah jadi. Sebaliknya, akan ada link ke dokumentasi yang menjelaskan cara membuat sendiri indeks dan kueri yang "benar". “Benar” - berdasarkan pengalaman dan pengetahuan saya.
Jenis kotak teks yang "benar"
Di ES, kami tertarik pada 2 tipe data:
- teks untuk pencarian teks lengkap. Bidang jenis ini tidak dapat digunakan untuk perbandingan, pengurutan, agregasi yang tepat;
- kata kunci untuk string yang terlibat dalam operasi perbandingan, pengurutan, agregasi.
ES mem-parsing nilai di bidang teks dan membuat kamus untuk pencarian teks lengkap. Nilai dalam bidang kata kunci diindeks sebagai diterima. Agregasi dan pengurutan hanya tersedia untuk bidang dengan jenis kata kunci.
Pengguna menggunakan karakteristik dalam kedua kasus: dalam penelusuran teks lengkap dan melalui filter. ES tidak mengizinkan Anda menetapkan 2 jenis ke satu bidang, tetapi menawarkan solusi lain:
bidang
PUT my_index
{
«mappings»: {
«properties»: {
«some_property»: {
«type»: «text», // 1
«fields»: { // 2
«raw»: {
«type»: «keyword»
}
}
}
}
}
}
- kami mendeklarasikan karakteristik produk sebagai bidang teks tipe .
- menggunakan parameter bidang, buat bidang virtual anak dari kata kunci jenis . Virtual, karena ada di indeks dan bukan di deskripsi produk. ES secara otomatis menyimpan data ke bidang anak saat diterima.
Jadi untuk setiap karakteristik.
Dalam kueri untuk operasi perbandingan, pengurutan, dan agregasi yang tepat, Anda harus menggunakan bidang virtual anak jenis kata kunci . Dalam contoh, ini adalah some_property.raw . Untuk pencarian teks - orang tua.
copy_to .
PUT my_index
{
«mappings»: {
«properties»: {
«all_properties»: { // 1
«type»: «text»
}, «some_property_1»: {
«type»: «keyword»,
«copy_to»: «all_properties» // 2
},
«some_property_2»: {
«type»: «keyword»,
«copy_to»: «all_properties»
}
}
}
- Buat bidang virtual dengan jenis teks di indeks .
- Deklarasikan setiap karakteristik sebagai kata kunci dengan parameter copy_to . Tentukan bidang virtual dengan nilai parameter. ES menyalin nilai semua karakteristik ke bidang virtual saat dokumen disimpan.
Untuk operasi perbandingan, pengurutan, dan agregasi yang tepat, Anda perlu menggunakan kolom karakteristik, untuk penelusuran teks - kolom dengan nilai dari semua karakteristik.
Kedua pendekatan membuat bidang tambahan dalam indeks yang tidak ada dalam struktur dokumen asli. Oleh karena itu, untuk membuat query, Anda perlu mengetahui struktur index.
Saya lebih suka opsi copy_to . Kemudian, untuk membuat kueri penelusuran teks lengkap, cukup mengetahui satu bidang dengan salinan nilai dari semua karakteristik.
Pertanyaan
Untuk mencari produk
Mari kita asumsikan bahwa struktur indeks sama seperti pada varian copy_to . Untuk pencarian teks lengkap di ES, konstruksi pencocokan digunakan , untuk perbandingan dengan nilai filter segi - kueri istilah . Kueri boolean menggabungkan konstruksi menjadi satu kueri. Ini akan menjadi seperti ini:
{
«query» : {
«bool»: {
«must»: {
«match»: {
«virtual_field_for_fulltext_searching»: {
«query»: «some text»
}
}
},
«filter»: {
«must»: [
{«property_1»: [ «value_1_1», …, «value_1_n»]},
…
{«property_n»: [ «value_n_1», …, «value_n_m»]}
]
}
}
}
}
query.bool.must.match utama
kueri penelusuran teks lengkap query.bool.filter filter untuk mempersempit kueri utama. harus di dalam berarti logis "dan" di antara filter. Larik nilai di setiap filter adalah boolean atau.
Untuk nilai filter
The istilah agregasi klausul kelompok produk berdasarkan nilai karakteristik dan menghitung kuantitas dalam setiap kelompok. Operasi ini disebut agregasi. Kesulitannya adalah untuk setiap filter aktif, agregasi istilah harus dilakukan pada pilihan produk yang dibentuk oleh filter aktif lainnya. Untuk filter tidak aktif - pada pilihan yang cocok dengan hasil pencarian. Konstruksi agregasi filter memungkinkan Anda membuat pilihan terpisah untuk setiap agregasi dan operasi "paket" ke dalam satu kueri.
Struktur permintaan akan seperti ini:
{
«size»: 0,
«query» : {
«bool»: {
«must»: {
«match»: {
«field_for_fulltext_searching»: {
«fuzziness»: 2,
«query»: «some text»
}
}
},
«filter»: {
}
}
},
«aggs» : {
«inavtive_filter_agg» : {
«filter» : { …
},
«aggs»: {
«some_inavtive_filter_subagg»: {
«terms» : {
«field» : «some_property»
}
},
...
«some_other_inavtive_filter_subagg»: {
«terms» : {
«field» : «some_other_property»
}
}
}
},
«active_filter_1_agg» : {
«filter»: {
… },
«aggs»: {
«active_filter_1_subagg»: {
«terms» : {
«field»: «property_1»
}
}
}
},
…,
«active_filter_N_agg» : {
«filter»: {
…
},
«aggs»: {
«active_filter_N_subagg»: {
«terms» : {
«field»: «property_N»
}
}
}
}
}
}
query.bool - kueri utama, operasi pemfilteran dilakukan dalam konteksnya. Terdiri dari:
- cocok - permintaan untuk pencarian teks lengkap;
- filter - filter menurut karakteristik yang tidak terkait dengan filter faset dan harus ada di subset mana pun. Ini bisa menjadi filter menurut stok in, is_visible, jika Anda selalu ingin menampilkan hanya produk dalam stok atau hanya yang terlihat.
aggs.inavtive_filter_agg - agregasi untuk filter faset tidak aktif terdiri dari:
- filter - kondisi menurut karakteristik yang dibentuk oleh filter faset aktif. Bersama dengan permintaan utama, pilihan barang dibentuk, di mana agregasi turunan dari bagian ini dilakukan;
- aggs adalah objek agregasi bernama untuk setiap filter tidak aktif .
aggs.active_filter_1_agg - agregasi untuk mendapatkan nilai pertama dari filter faset aktif. Setiap desain dikaitkan dengan satu filter faset. Terdiri dari:
- filter - kondisi menurut karakteristik yang dibentuk oleh filter faset aktif, kecuali yang sekarang. Bersama-sama dengan kueri utama, ini membentuk pilihan barang, di mana agregasi turunan dari bagian ini dilakukan;
- aggs adalah sebuah objek dari satu agregasi sesuai dengan karakteristik filter faset yang sedang aktif.
Penting untuk menentukan "size": 0 , jika tidak, Anda akan mendapatkan daftar produk yang cocok dengan kueri utama tanpa agregasi.
Akhirnya
Menerima dua permintaan:
- untuk hasil pencarian, mengembalikan produk untuk ditampilkan kepada pengguna;
- untuk nilai filter, melakukan agregasi, mengembalikan nilai filter dan jumlah produk dengan nilai tersebut.
Setiap permintaan berdiri sendiri, jadi yang terbaik adalah menjalankannya secara asinkron.
PS Saya mengakui bahwa ada lebih banyak pendekatan dan alat yang "benar" untuk memecahkan masalah pencarian segi. Saya akan berterima kasih atas informasi tambahan dan contoh di komentar.