Rumah pembangunan bahagian belakang Golang Bleve: Bagaimana untuk membina enjin carian pantas roket?

Bleve: Bagaimana untuk membina enjin carian pantas roket?

Jan 03, 2025 am 04:23 AM

Bleve: How to build a rocket-fast search engine?

Go/Golang ialah salah satu bahasa kegemaran saya; Saya suka minimalisme dan betapa bersihnya, ia sangat padat dari segi sintaks dan berusaha keras untuk memastikan perkara mudah (saya peminat tegar prinsip KISS).

Salah satu cabaran utama yang saya hadapi sejak kebelakangan ini ialah membina enjin carian yang pantas. Pasti ada pilihan seperti SOLR dan ElasticSearch; kedua-duanya berfungsi dengan baik dan sangat berskala, walau bagaimanapun, saya perlu memudahkan carian, dengan menjadikannya lebih pantas dan lebih mudah untuk digunakan dengan sedikit atau tiada kebergantungan.

Saya perlu mengoptimumkan cukup untuk saya memulangkan hasil dengan cepat supaya keputusan itu boleh ditarafkan semula. Walaupun C/Rust mungkin sesuai untuk ini, saya menghargai kelajuan pembangunan dan produktiviti. Golang adalah yang terbaik dari kedua-dua dunia saya rasa.

Dalam artikel ini, saya akan melihat contoh mudah bagaimana anda boleh membina enjin carian anda sendiri menggunakan Go, anda akan terkejut: ia tidaklah sesulit yang anda fikirkan.

Golang: Python pada steroid

Saya tidak tahu mengapa, tetapi Golang berasa seperti Python dalam satu cara. Sintaksnya sangat mudah untuk difahami, mungkin kerana kekurangan koma bertitik dan tanda kurung di mana-mana atau kekurangan kenyataan cuba-tangkap yang jelek. Mungkin ia adalah pemformat Go yang hebat, saya tidak tahu.

Apa pun, memandangkan Golang menjana satu binari serba lengkap, ia sangat mudah untuk digunakan ke mana-mana pelayan pengeluaran. Anda hanya "pergi membina" dan tukar keluar yang boleh laku.

Itulah yang saya perlukan.

Adakah anda Bleve?

Tidak, itu bukan kesilapan menaip ?. Bleve ialah perpustakaan carian yang berkuasa, mudah digunakan dan sangat fleksibel untuk Golang.

Semasa sebagai pembangun Go, anda biasanya mengelakkan pakej pihak ketiga seperti wabak; kadang-kadang masuk akal untuk menggunakan pakej pihak ke-3. Bleve adalah pantas, direka bentuk dengan baik dan memberikan nilai yang mencukupi untuk membenarkan penggunaannya.

Selain itu, inilah sebab mengapa saya "Bleve":

  • Sendiri, salah satu kelebihan besar Golang ialah binari tunggal, jadi saya ingin mengekalkan rasa itu dan tidak memerlukan DB atau perkhidmatan luaran untuk menyimpan dan menanyakan dokumen. Bleve berjalan dalam ingatan dan menulis pada cakera yang serupa dengan Sqlite.

  • Mudah dipanjangkan. Memandangkan ia hanya kod Go, saya boleh mengubah suai perpustakaan atau memanjangkannya dengan mudah dalam pangkalan kod saya mengikut keperluan.

  • Pantas: Hasil carian merentas 10 juta dokumen mengambil masa hanya 50-100ms, ini termasuk penapisan.

  • Faceting: anda tidak boleh membina enjin carian moden tanpa beberapa tahap sokongan faceting. Bleve mempunyai sokongan penuh untuk jenis faset biasa: seperti julat atau kiraan kategori mudah.

  • Pengindeksan pantas: Bleve agak perlahan daripada SOLR. SOLR boleh mengindeks 10 juta dokumen dalam masa 30 minit, manakala Bleve mengambil masa lebih sejam, bagaimanapun, sejam atau lebih masih cukup baik dan cukup pantas untuk keperluan saya.

  • Hasil kualiti yang baik. Bleve berfungsi dengan baik dengan hasil kata kunci tetapi beberapa carian jenis semantik juga berfungsi dengan baik dalam Bleve.

  • Permulaan pantas: Jika anda perlu memulakan semula atau menggunakan kemas kini, hanya milisaat diperlukan untuk memulakan semula Bleve. Tiada penyekatan bacaan untuk membina semula indeks dalam ingatan, jadi carian indeks boleh dilakukan tanpa gangguan hanya beberapa milisaat selepas dimulakan semula.

Sediakan indeks?

Dalam Bleve, "Indeks" boleh dianggap sebagai jadual pangkalan data atau koleksi (NoSQL). Tidak seperti jadual SQL biasa, anda tidak perlu menentukan setiap lajur tunggal, pada asasnya anda boleh lari dengan skema lalai untuk kebanyakan kes penggunaan.

Untuk memulakan indeks Bleve, anda boleh melakukan perkara berikut:

mappings := bleve.NewIndexMapping()
index, err = bleve.NewUsing("/some/path/index.bleve", mappings, "scorch", "scorch", nil)
if err != nil {
    log.Fatal(err)
}
Salin selepas log masuk
Salin selepas log masuk

Bleve menyokong beberapa jenis indeks yang berbeza, tetapi saya dapati setelah banyak mengutak-atik bahawa jenis indeks "hangus" memberikan anda prestasi terbaik. Jika anda tidak lulus dalam 3 hujah terakhir, Bleve hanya akan lalai kepada BoltDB.

Menambah dokumen

Menambah dokumen pada Bleve adalah mudah. Anda pada asasnya boleh menyimpan sebarang jenis struct dalam indeks:

type Book struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Genre string `json:"genre"`
}

b := Book{
    ID:    1234,
    Name:  "Some creative title",
    Genre: "Young Adult",
}
idStr := fmt.Sprintf("%d", b.ID)
// index(string, interface{})
index.index(idStr, b)
Salin selepas log masuk
Salin selepas log masuk

Jika anda mengindeks sejumlah besar dokumen, lebih baik menggunakan kumpulan:

// You would also want to check if the batch exists already
// - so that you don't recreate it.
batch := index.NewBatch()
if batch.Size() >= 1000 {
    err := index.Batch(batch)
    if err != nil {
        // failed, try again or log etc...
    }
    batch = index.NewBatch()
} else {
    batch.index(idStr, b)
}
Salin selepas log masuk
Salin selepas log masuk

Seperti yang anda akan perhatikan, tugas yang rumit seperti menyusun rekod dan menulisnya pada indeks dipermudahkan menggunakan "index.NewBatch" yang mencipta bekas untuk mengindeks dokumen buat sementara waktu.

Selepas itu anda hanya semak saiz semasa anda melingkari dan siram indeks sebaik sahaja anda mencapai had saiz kelompok.

Mencari indeks

Bleve mendedahkan berbilang penghurai pertanyaan carian berbeza yang boleh anda pilih bergantung pada keperluan carian anda. Untuk memastikan artikel ini pendek dan manis, saya hanya akan menggunakan penghurai rentetan pertanyaan standard.

searchParser := bleve.NewQueryStringQuery("chicken reciepe books")
maxPerPage := 50
ofsset := 0
searchRequest := bleve.NewSearchRequestOptions(searchParser, maxPerPage, offset, false)
// By default bleve returns just the ID, here we specify
// - all the other fields we would like to return.
searchRequest.Fields = []string{"id", "name", "genre"}
searchResults, err := index.Search(searchResult)
Salin selepas log masuk
Salin selepas log masuk

Dengan hanya beberapa baris ini, anda kini mempunyai enjin carian yang berkuasa yang memberikan hasil yang baik dengan memori dan jejak sumber yang rendah.

Berikut ialah perwakilan JSON bagi hasil carian, "hits" akan mengandungi dokumen yang sepadan:

{
    "status": {
        "total": 5,
        "failed": 0,
        "successful": 5
    },
    "request": {},
    "hits": [],
    "total_hits": 19749,
    "max_score": 2.221337297308545,
    "took": 99039137,
    "facets": null
}
Salin selepas log masuk
Salin selepas log masuk

Faceting

Seperti yang dinyatakan sebelum ini, Bleve menyediakan sokongan aspek penuh di luar kotak tanpa perlu menyediakannya dalam skema anda. Untuk Facet pada buku "Genre" misalnya, anda boleh melakukan perkara berikut:

//... build searchRequest -- see previous section.
// Add facets
genreFacet := bleve.NewFacetRequest("genre", 50)
searchRequest.AddFacet("genre", genreFacet)
searchResults, err := index.Search(searchResult)
Salin selepas log masuk
Salin selepas log masuk

Kami melanjutkan Permintaan carian kami dari awal dengan hanya 2 baris kod. "NewFacetRequest" mengambil 2 argumen:

  • Medan: medan dalam indeks kami kepada faset pada (rentetan).

  • Saiz: bilangan entri untuk dikira (integer). Oleh itu dalam contoh kami, ia hanya akan mengira 50 genre pertama.

Yang di atas kini akan mengisi "faset" dalam hasil carian kami.

Seterusnya, kami hanya menambah aspek kami pada permintaan carian. Yang mengambil "nama aspek" dan aspek sebenar. "Nama facet" ialah "kunci" anda akan dapati keputusan ini ditetapkan di bawah dalam hasil carian kami.

Pertanyaan lanjutan dan penapisan

Walaupun penghurai "QueryStringQuery" boleh memberikan anda sedikit perbatuan; kadangkala anda memerlukan pertanyaan yang lebih kompleks seperti "seseorang mesti sepadan" di mana anda ingin memadankan istilah carian dengan beberapa medan dan mengembalikan hasil asalkan sekurang-kurangnya satu medan sepadan.

Anda boleh menggunakan jenis pertanyaan "Disjunction" dan "Conjunction" untuk mencapai ini.

  • Pertanyaan Konjungsi: Pada asasnya, ia membolehkan anda merangkaikan berbilang pertanyaan bersama-sama untuk membentuk satu pertanyaan gergasi. Semua pertanyaan kanak-kanak mesti sepadan dengan sekurang-kurangnya satu dokumen.

  • Pertanyaan Disjungsi: Ini akan membolehkan anda melakukan pertanyaan "satu mesti sepadan" yang dinyatakan di atas. Anda boleh menghantar dalam x jumlah pertanyaan dan menetapkan bilangan pertanyaan kanak-kanak mesti sepadan dengan sekurang-kurangnya satu dokumen.

Contoh Pertanyaan Disjungsi:

mappings := bleve.NewIndexMapping()
index, err = bleve.NewUsing("/some/path/index.bleve", mappings, "scorch", "scorch", nil)
if err != nil {
    log.Fatal(err)
}
Salin selepas log masuk
Salin selepas log masuk

Sama seperti cara kami menggunakan "searchParser" sebelum ini, kami kini boleh menghantar "Disjunction Query" ke dalam pembina untuk "searchRequest" kami.

Walaupun tidak betul-betul sama, ini menyerupai SQL berikut:

type Book struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Genre string `json:"genre"`
}

b := Book{
    ID:    1234,
    Name:  "Some creative title",
    Genre: "Young Adult",
}
idStr := fmt.Sprintf("%d", b.ID)
// index(string, interface{})
index.index(idStr, b)
Salin selepas log masuk
Salin selepas log masuk

Anda juga boleh melaraskan betapa kaburnya carian anda mahukan dengan menetapkan "query.Fuzziness=[0 atau 1 atau 2]"

Contoh Pertanyaan Kata Hubung:

// You would also want to check if the batch exists already
// - so that you don't recreate it.
batch := index.NewBatch()
if batch.Size() >= 1000 {
    err := index.Batch(batch)
    if err != nil {
        // failed, try again or log etc...
    }
    batch = index.NewBatch()
} else {
    batch.index(idStr, b)
}
Salin selepas log masuk
Salin selepas log masuk

Anda akan perasan sintaksnya sangat serupa, pada asasnya anda hanya boleh menggunakan pertanyaan "Konjungsi" dan "Disjungsi" secara bergantian.

Ini akan kelihatan serupa dengan yang berikut dalam SQL:

searchParser := bleve.NewQueryStringQuery("chicken reciepe books")
maxPerPage := 50
ofsset := 0
searchRequest := bleve.NewSearchRequestOptions(searchParser, maxPerPage, offset, false)
// By default bleve returns just the ID, here we specify
// - all the other fields we would like to return.
searchRequest.Fields = []string{"id", "name", "genre"}
searchResults, err := index.Search(searchResult)
Salin selepas log masuk
Salin selepas log masuk

Ringkasnya; gunakan "Pertanyaan Konjungsi" apabila anda mahu semua pertanyaan kanak-kanak sepadan dengan sekurang-kurangnya satu dokumen dan "Pertanyaan Disjungsi" apabila anda mahu memadankan sekurang-kurangnya satu pertanyaan kanak-kanak tetapi tidak semestinya semua pertanyaan kanak-kanak.

Sharding

Jika anda menghadapi masalah kelajuan, Bleve juga memungkinkan untuk mengedarkan data anda merentasi berbilang serpihan indeks dan kemudian menanyakan serpihan tersebut dalam satu permintaan, contohnya:

{
    "status": {
        "total": 5,
        "failed": 0,
        "successful": 5
    },
    "request": {},
    "hits": [],
    "total_hits": 19749,
    "max_score": 2.221337297308545,
    "took": 99039137,
    "facets": null
}
Salin selepas log masuk
Salin selepas log masuk

Sharding boleh menjadi agak rumit, tetapi seperti yang anda lihat di atas, Bleve menghilangkan banyak kesakitan, kerana ia secara automatik "menggabungkan" semua indeks dan carian merentasinya, dan kemudian mengembalikan hasil dalam satu set keputusan sama seperti anda mencari satu indeks.

Saya telah menggunakan sharding untuk mencari 100 shards. Keseluruhan proses carian selesai dalam purata 100-200 milisaat sahaja.

Anda boleh mencipta serpihan seperti berikut:

//... build searchRequest -- see previous section.
// Add facets
genreFacet := bleve.NewFacetRequest("genre", 50)
searchRequest.AddFacet("genre", genreFacet)
searchResults, err := index.Search(searchResult)
Salin selepas log masuk
Salin selepas log masuk

Cuma pastikan anda membuat ID unik untuk setiap dokumen atau mempunyai semacam cara yang boleh diramal untuk menambah dan mengemas kini dokumen tanpa mengacaukan indeks.

Cara mudah untuk melakukan ini ialah dengan menyimpan awalan yang mengandungi nama serpihan dalam DB sumber anda, atau dari mana-mana sahaja anda mendapatkan dokumen itu. Supaya setiap kali anda cuba memasukkan atau mengemas kini, anda mencari "awalan" yang akan memberitahu anda bahagian mana yang hendak dipanggil ".index".

Bercakap tentang pengemaskinian, hanya memanggil "index.index(idstr, struct)" akan mengemas kini dokumen sedia ada.

Kesimpulan

Hanya menggunakan teknik carian asas ini di atas dan meletakkannya di belakang GIN atau pelayan HTTP Go standard, anda boleh membina API carian yang cukup berkuasa dan menyediakan berjuta-juta permintaan tanpa perlu melancarkan infrastruktur yang kompleks.

Satu kaveat; Bleve tidak memenuhi keperluan replikasi, bagaimanapun, kerana anda boleh membungkusnya dalam API. Hanya ada tugas cron yang membaca daripada sumber anda dan "meletupkan" kemas kini kepada semua pelayan Bleve anda menggunakan goroutine.

Sebagai alternatif, anda hanya boleh mengunci tulisan ke cakera selama beberapa saat dan kemudian hanya "rsync" data merentas ke indeks hamba, walaupun saya tidak menasihati berbuat demikian kerana anda mungkin juga perlu memulakan semula binari pergi setiap kali .

Atas ialah kandungan terperinci Bleve: Bagaimana untuk membina enjin carian pantas roket?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Tujuan Golang: Membina sistem yang cekap dan berskala Tujuan Golang: Membina sistem yang cekap dan berskala Apr 09, 2025 pm 05:17 PM

Pergi bahasa berfungsi dengan baik dalam membina sistem yang cekap dan berskala. Kelebihannya termasuk: 1. Prestasi Tinggi: Disusun ke dalam Kod Mesin, Kelajuan Berjalan Cepat; 2. Pengaturcaraan serentak: Memudahkan multitasking melalui goroutine dan saluran; 3. Kesederhanaan: sintaks ringkas, mengurangkan kos pembelajaran dan penyelenggaraan; 4. Cross-Platform: Menyokong kompilasi silang platform, penggunaan mudah.

Golang dan C: Konvensyen vs kelajuan mentah Golang dan C: Konvensyen vs kelajuan mentah Apr 21, 2025 am 12:16 AM

Golang lebih baik daripada C dalam kesesuaian, manakala C lebih baik daripada Golang dalam kelajuan mentah. 1) Golang mencapai kesesuaian yang cekap melalui goroutine dan saluran, yang sesuai untuk mengendalikan sejumlah besar tugas serentak. 2) C Melalui pengoptimuman pengkompil dan perpustakaan standard, ia menyediakan prestasi tinggi yang dekat dengan perkakasan, sesuai untuk aplikasi yang memerlukan pengoptimuman yang melampau.

Perlumbaan Prestasi: Golang vs C Perlumbaan Prestasi: Golang vs C Apr 16, 2025 am 12:07 AM

Golang dan C masing-masing mempunyai kelebihan sendiri dalam pertandingan prestasi: 1) Golang sesuai untuk kesesuaian tinggi dan perkembangan pesat, dan 2) C menyediakan prestasi yang lebih tinggi dan kawalan halus. Pemilihan harus berdasarkan keperluan projek dan tumpukan teknologi pasukan.

Golang vs Python: Prestasi dan Skala Golang vs Python: Prestasi dan Skala Apr 19, 2025 am 12:18 AM

Golang lebih baik daripada Python dari segi prestasi dan skalabiliti. 1) Ciri-ciri jenis kompilasi Golang dan model konkurensi yang cekap menjadikannya berfungsi dengan baik dalam senario konvensional yang tinggi. 2) Python, sebagai bahasa yang ditafsirkan, melaksanakan perlahan -lahan, tetapi dapat mengoptimumkan prestasi melalui alat seperti Cython.

Golang vs Python: Perbezaan dan Persamaan Utama Golang vs Python: Perbezaan dan Persamaan Utama Apr 17, 2025 am 12:15 AM

Golang dan Python masing -masing mempunyai kelebihan mereka sendiri: Golang sesuai untuk prestasi tinggi dan pengaturcaraan serentak, sementara Python sesuai untuk sains data dan pembangunan web. Golang terkenal dengan model keserasiannya dan prestasi yang cekap, sementara Python terkenal dengan sintaks ringkas dan ekosistem perpustakaan yang kaya.

C dan Golang: Apabila prestasi sangat penting C dan Golang: Apabila prestasi sangat penting Apr 13, 2025 am 12:11 AM

C lebih sesuai untuk senario di mana kawalan langsung sumber perkakasan dan pengoptimuman prestasi tinggi diperlukan, sementara Golang lebih sesuai untuk senario di mana pembangunan pesat dan pemprosesan konkurensi tinggi diperlukan. Kelebihan 1.C terletak pada ciri-ciri perkakasan dan keupayaan pengoptimuman yang tinggi, yang sesuai untuk keperluan berprestasi tinggi seperti pembangunan permainan. 2. Kelebihan Golang terletak pada sintaks ringkas dan sokongan konvensional semulajadi, yang sesuai untuk pembangunan perkhidmatan konvensional yang tinggi.

Impak Golang: Kelajuan, Kecekapan, dan Kesederhanaan Impak Golang: Kelajuan, Kecekapan, dan Kesederhanaan Apr 14, 2025 am 12:11 AM

Goimpactsdevelopmentpositivielythroughspeed, efficiency, andsimplicity.1) Speed: goCompilesquicklyandrunsefficiently, idealforlargeproject.2) Kecekapan: ITSComprehensivestandardlibraryraryrarexternaldependencies, enhingdevelyficiency.

Golang dan C: Perdagangan dalam prestasi Golang dan C: Perdagangan dalam prestasi Apr 17, 2025 am 12:18 AM

Perbezaan prestasi antara Golang dan C terutamanya ditunjukkan dalam pengurusan ingatan, pengoptimuman kompilasi dan kecekapan runtime. 1) Mekanisme pengumpulan sampah Golang adalah mudah tetapi boleh menjejaskan prestasi, 2) Pengurusan memori manual C dan pengoptimuman pengkompil lebih cekap dalam pengkomputeran rekursif.

See all articles