
Reindexer adalah database yang dapat disematkan, dalam memori, dan berorientasi dokumen dengan antarmuka pembangun kueri tingkat tinggi.
Tujuan Reindexer adalah untuk memberikan pencarian cepat dengan pertanyaan yang kompleks. Kami di Restream tidak senang dengan Elasticsearch dan menciptakan Reindexer sebagai alternatif yang lebih berkinerja.
Inti ditulis dalam C ++ dan API tingkat aplikasi sedang dalam perjalanan.
Dokumen ini menjelaskan konektor Go dan API -nya. Untuk mendapatkan informasi tentang server Reindexer dan API HTTP, lihat dokumentasi Reindexer
Ada dua LTS-versi reindexer yang tersedia: v3.xx dan v4.xx
3.xx saat ini adalah cabang utama kami dan 4.xx (rilis/4 cabang) adalah versi beta dengan cluster rakit eksperimental dan dukungan sharding. Storage kompatibel antara versi -versi tersebut, namun, konfigurasi replikasi sangat berbeda. Versi 3 dan 4 mendapatkan semua perbaikan bug dan fitur yang sama (kecuali yang terkait replikasi).
Fitur Utama:
Kinerja telah menjadi prioritas utama kami sejak awal, dan kami pikir kami berhasil mendapatkannya dengan cukup baik. Benchmarks menunjukkan bahwa kinerja Reindexer setara dengan database nilai kunci yang khas. Pada inti CPU tunggal, kami mendapatkan:
SELECT * FROM items WHERE id='?'SELECT * FROM items WHERE year > 2010 AND name = 'string' AND id IN (....)SELECT * FROM items WHERE year > 2010 AND name = 'string' JOIN subitems ON ...Lihat Hasil Benchmarking dan detail lebih lanjut dalam Benchmarking Repo
Reindexer bertujuan untuk mengonsumsi memori sesedikit mungkin; Sebagian besar pertanyaan diproses tanpa alokasi memori sama sekali.
Untuk mencapai itu, beberapa optimisasi digunakan, baik pada level C ++ dan GO:
Dokumen dan indeks disimpan dalam struct C ++ biner yang padat, sehingga mereka tidak memaksakan beban apa pun pada pengumpul sampah Go.
Duplikat string digabungkan.
Overhead memori adalah sekitar 32 byte per dokumen + ≈4-16 byte per setiap indeks pencarian.
Ada cache objek pada level GO untuk dokumen deserialized yang diproduksi setelah eksekusi kueri. Kueri di masa depan menggunakan dokumen pra-deserialisasi, yang memotong biaya deserialisasi dan alokasi berulang
Antarmuka kueri menggunakan sync.Pool untuk menggunakan kembali struktur dan buffer internal. Kombinasi teknologi ini memungkinkan reindexer untuk menangani sebagian besar kueri tanpa alokasi apa pun.
Reindexer memiliki mesin pencari teks lengkap internal. Dokumentasi dan Contoh Penggunaan Pencarian Teks Lengkap Ada Di Sini
Reindexer dapat menyimpan dokumen ke dan memuat dokumen dari disk melalui LevelDB. Dokumen ditulis ke backend penyimpanan secara asinkron oleh batch besar secara otomatis di latar belakang.
Ketika namespace dibuat, semua dokumennya disimpan menjadi RAM, sehingga pertanyaan pada dokumen-dokumen ini sepenuhnya berjalan dalam mode dalam memori.
Berikut adalah contoh lengkap penggunaan reindexer dasar:
package main
// Import package
import (
"fmt"
"math/rand"
"github.com/restream/reindexer/v3"
// choose how the Reindexer binds to the app (in this case "builtin," which means link Reindexer as a static library)
_ "github.com/restream/reindexer/v3/bindings/builtin"
// OR use Reindexer as standalone server and connect to it via TCP or unix domain socket (if available).
// _ "github.com/restream/reindexer/v3/bindings/cproto"
// OR link Reindexer as static library with bundled server.
// _ "github.com/restream/reindexer/v3/bindings/builtinserver"
// "github.com/restream/reindexer/v3/bindings/builtinserver/config"
)
// Define struct with reindex tags. Fields must be exported - private fields can not be written into reindexer
type Item struct {
ID int64 `reindex:"id,,pk"` // 'id' is primary key
Name string `reindex:"name"` // add index by 'name' field
Articles [] int `reindex:"articles"` // add index by articles 'articles' array
Year int `reindex:"year,tree"` // add sortable index by 'year' field
}
func main () {
// Init a database instance and choose the binding (builtin)
db := reindexer . NewReindex ( "builtin:///tmp/reindex/testdb" )
// OR - Init a database instance and choose the binding (connect to server via TCP sockets)
// Database should be created explicitly via reindexer_tool or via WithCreateDBIfMissing option:
// If server security mode is enabled, then username and password are mandatory
// db := reindexer.NewReindex("cproto://user:[email protected]:6534/testdb", reindexer.WithCreateDBIfMissing())
// OR - Init a database instance and choose the binding (connect to server via unix domain sockets)
// Unix domain sockets are available on the unix systems only (socket file has to be explicitly set on the server's side with '--urpcaddr' option)
// Database should be created explicitly via reindexer_tool or via WithCreateDBIfMissing option:
// If server security mode is enabled, then username and password are mandatory
// db := reindexer.NewReindex("ucproto://user:pass@/tmp/reindexer.socket:/testdb", reindexer.WithCreateDBIfMissing())
// OR - Init a database instance and choose the binding (builtin, with bundled server)
// serverConfig := config.DefaultServerConfig ()
// If server security mode is enabled, then username and password are mandatory
// db := reindexer.NewReindex("builtinserver://user:pass@testdb",reindexer.WithServerConfig(100*time.Second, serverConfig))
// Create new namespace with name 'items', which will store structs of type 'Item'
db . OpenNamespace ( "items" , reindexer . DefaultNamespaceOptions (), Item {})
// Generate dataset
for i := 0 ; i < 100000 ; i ++ {
err := db . Upsert ( "items" , & Item {
ID : int64 ( i ),
Name : "Vasya" ,
Articles : [] int { rand . Int () % 100 , rand . Int () % 100 },
Year : 2000 + rand . Int () % 50 ,
})
if err != nil {
panic ( err )
}
}
// Query a single document
elem , found := db . Query ( "items" ).
Where ( "id" , reindexer . EQ , 40 ).
Get ()
if found {
item := elem .( * Item )
fmt . Println ( "Found document:" , * item )
}
// Query multiple documents
query := db . Query ( "items" ).
Sort ( "year" , false ). // Sort results by 'year' field in ascending order
WhereString ( "name" , reindexer . EQ , "Vasya" ). // 'name' must be 'Vasya'
WhereInt ( "year" , reindexer . GT , 2020 ). // 'year' must be greater than 2020
WhereInt ( "articles" , reindexer . SET , 6 , 1 , 8 ). // 'articles' must contain one of [6,1,8]
Limit ( 10 ). // Return maximum 10 documents
Offset ( 0 ). // from 0 position
ReqTotal () // Calculate the total count of matching documents
// Execute the query and return an iterator
iterator := query . Exec ()
// Iterator must be closed
defer iterator . Close ()
fmt . Println ( "Found" , iterator . TotalCount (), "total documents, first" , iterator . Count (), "documents:" )
// Iterate over results
for iterator . Next () {
// Get the next document and cast it to a pointer
elem := iterator . Object ().( * Item )
fmt . Println ( * elem )
}
// Check the error
if err := iterator . Error (); err != nil {
panic ( err )
}
}Ada juga beberapa sampel dasar untuk C ++ dan pergi ke sini
Sebagai alternatif untuk pembangun permintaan Reindexer menyediakan antarmuka kueri yang kompatibel dengan SQL. Berikut adalah contoh penggunaan antarmuka SQL:
...
iterator := db . ExecSQL ( "SELECT * FROM items WHERE name='Vasya' AND year > 2020 AND articles IN (6,1,8) ORDER BY year LIMIT 10" )
...Harap dicatat, bahwa antarmuka Query Builder lebih disukai: ia memiliki lebih banyak fitur dan lebih cepat dari antarmuka SQL
Literal string harus dilampirkan dalam kutipan tunggal.
Indeks gabungan harus dilampirkan dalam kutipan ganda.
SELECT * FROM items WHERE " field1+field2 " = ' Vasya 'Jika nama bidang tidak dimulai dengan alpha, '_' atau '#' itu harus dilampirkan dalam kutipan ganda, contoh:
UPDATE items DROP " 123 " SELECT * FROM ns WHERE " 123 " = ' some_value ' SELECT * FROM ns WHERE " 123abc " = 123 DELETE FROM ns WHERE " 123abc123 " = 111Bergabung sederhana dapat dilakukan melalui sintaks SQL default:
SELECT * FROM ns INNER JOIN ns2 ON ns2 . id = ns . fk_id WHERE a > 0Bergabung dengan kondisi di namespace kiri harus menggunakan sintaksis seperti subquery:
SELECT * FROM ns WHERE a > 0 AND INNER JOIN ( SELECT * FROM ns2 WHERE b > 10 AND c = 1 ) ON ns2 . id = ns . fk_idSubquery juga bisa menjadi bagian dari where-condition:
SELECT * FROM ns WHERE ( SELECT * FROM ns2 WHERE id < 10 LIMIT 0 ) IS NOT NULL SELECT * FROM ns WHERE id = ( SELECT id FROM ns2 WHERE id < 10 ) SELECT * FROM ns WHERE ( SELECT COUNT ( * ) FROM ns2 WHERE id < 10 ) > 18 Reindexer dapat berjalan dalam 3 mode berbeda:
embedded (builtin) tertanam ke dalam aplikasi sebagai pustaka statis, dan tidak memerlukan proses server yang terpisah.embedded with server (builtinserver) Reindexer tertanam ke dalam aplikasi sebagai pustaka statis, dan mulai server. Dalam mode ini klien lain dapat terhubung ke aplikasi melalui CPRoto, UCPROTO atau HTTP.standalone dijalankan sebagai server mandiri, aplikasi terhubung ke reindexer melalui jaringan atau soket domain UNIX.Dalam mode ini go-binding Reindexer tidak tergantung pada perpustakaan statis Reindexer.
Cara paling sederhana untuk mendapatkan server Reindexer, adalah menarik & menjalankan gambar Docker dari Dockerhub.
docker run -p9088:9088 -p6534:6534 -it reindexer/reindexerDockerfile
Inti Reindexer ditulis dalam C ++ 17 dan menggunakan LevelDB sebagai backend penyimpanan, sehingga CMake, C ++ 17 Toolchain dan LevelDB harus diinstal sebelum menginstal reindexer.
Untuk membangun reindexer, G ++ 8+, Clang 7+ atau Mingw64 diperlukan.
Dalam mode-mode tersebut, GO-Binding Reindexer tergantung pada pustaka statis Reindexer (Core, Server, dan Sumber Daya).
Dengan cara ini direkomendasikan dan akan cocok untuk skenario terbanyak.
GO Modul dengan go.mod Jangan memungkinkan untuk membangun perpustakaan C ++ di direktori modul. Go-Binding akan menggunakan PKG-Config untuk mendeteksi direktori perpustakaan.
Perpustakaan Reindexer harus diinstal dari sumber atau dari Paket Prebuilt melalui Paket Manajer.
Kemudian dapatkan modulnya:
go get -a github.com/restream/reindexer/v3 Jika Anda memerlukan sumber reindexer yang dimodifikasi, Anda dapat menggunakan replace seperti itu.
# Clone reindexer via git. It's also possible to use 'go get -a github.com/restream/reindexer/v3', but it's behavior may vary depending on Go's version
git clone https://github.com/restream/reindexer.git $GOPATH /src/reindexer
bash $GOPATH /src/reindexer/dependencies.sh
# Generate builtin binding
cd $GOPATH /src/reindexer
go generate ./bindings/builtin
# Optional (build builtin server binding)
go generate ./bindings/builtinserver # Go to your app's directory
cd /your/app/path
go get -a github.com/restream/reindexer/v3
go mod edit -replace github.com/restream/reindexer/v3= $GOPATH /src/reindexerDalam hal ini, Go-Binding akan menghasilkan daftar perpustakaan dan jalur eksplisit dan tidak akan menggunakan pkg-config.
Jika Anda tidak menggunakan go.mod itu mungkin untuk mendapatkan dan membangun reindexer dari sumber dengan cara ini:
export GO111MODULE=off # Disable go1.11 modules
# Go to your app's directory
cd /your/app/path
# Clone reindexer via git. It's also possible to use 'go get -a github.com/restream/reindexer', but it's behavior may vary depending on Go's version
git clone --branch master https://github.com/restream/reindexer.git vendor/github.com/restream/reindexer/v3
# Generate builtin binding
go generate -x ./vendor/github.com/restream/reindexer/v3/bindings/builtin
# Optional (build builtin server binding)
go generate -x ./vendor/github.com/restream/reindexer/v3/bindings/builtinserverGO tidak mendukung vendoring yang tepat untuk kode CGO (Golang/Go#26366), namun, dimungkinkan untuk menggunakan Vend untuk menyalin sumber-sumber Reindexer ke Vendor-Directory.
Dengan vend Anda dapat menelepon go generate -mod=vendor untuk builtin dan builtinserver , ditempatkan di vendor -direktori Anda.
Dimungkinkan juga untuk menyalin cukup menyalin sumber Reindexer ke proyek pemuda, menggunakan git clone .
Dalam kasus ini semua dependensi dari go reindexer.mod harus dipasang secara manual dengan versi yang tepat.
Secara internal, struct dibagi menjadi dua bagian:
reindex struct Kueri hanya dimungkinkan pada bidang yang diindeks, ditandai dengan tag reindex . Tag reindex berisi nama indeks, jenis, dan opsi tambahan:
reindex:"<name>[[,<type>],<opts>]"
name - Nama Indeks.type - Tipe Indeks:hash - Pilih Cepat oleh EQ dan Set Cocok. Digunakan secara default. Memungkinkan penyortiran yang lambat dan tidak efisien berdasarkan bidang.tree - Pilih Cepat dengan Range, GT, dan LT cocok. Sedikit lebih lambat untuk pertandingan EQ dan set dari indeks hash . Memungkinkan hasil penyortiran cepat berdasarkan bidang.text - Indeks Pencarian Teks Lengkap. Detail Penggunaan Pencarian Teks Lengkap Dijelaskan Di Sini- - Indeks kolom. Tidak dapat melakukan Pilih Cepat karena diimplementasikan dengan Teknik Pemindaian Penuh. Memiliki overhead memori terkecil.ttl - Indeks TTL yang hanya berfungsi dengan bidang INT64. Indeks ini cukup nyaman untuk representasi bidang tanggal (disimpan sebagai cap waktu UNIX) yang kedaluwarsa setelah jumlah detik yang ditentukan.rtree - Tersedia hanya DIwithin Match. Hanya dapat diterima untuk tipe bidang [2]float64 (atau reindexer.Point ). Untuk detailnya lihat ayat geometri.opts - Opsi Indeks Tambahan:pk - Field adalah bagian dari kunci utama. Struct harus memiliki setidaknya 1 bidang yang ditandai dengan pkcomposite - Buat Indeks Komposit. Jenis bidang harus menjadi struct kosong: struct{} .joined - Field adalah penerima untuk bergabung. Jenis bidang harus []*SubitemType .dense - Kurangi ukuran indeks. Untuk hash dan tree itu akan menghemat 8 byte per nilai kunci unik. Untuk - itu akan menghemat 4-8 byte per setiap elemen. Berguna untuk indeks dengan selektivitas tinggi, tetapi untuk indeks tree dan hash dengan selektivitas rendah dapat secara serius mengurangi kinerja pembaruan. Juga dense akan memperlambat kueri fullscan yang luas pada - indeks, karena kurangnya optimasi cache CPU.sparse - Baris (dokumen) berisi nilai indeks jarang hanya jika disetel dengan sengaja - tidak ada catatan kosong (atau default) dari jenis indeks ini di baris (dokumen). Ini memungkinkan untuk menghemat RAM, tetapi akan dikenakan biaya kinerja Anda - berfungsi sedikit lebih lambat daripada indeks reguler.collate_numeric - Buat indeks string yang menyediakan urutan nilai dalam urutan numerik. Jenis bidang harus berupa string.collate_ascii - Buat indeks string yang tidak sensitif case berfungsi dengan ASCII. Jenis bidang harus berupa string.collate_utf8 - Buat indeks string yang tidak sensitif case berfungsi dengan UTF8. Jenis bidang harus berupa string.collate_custom=<ORDER> - Buat indeks string pesanan khusus. Jenis bidang harus berupa string. <ORDER> adalah urutan huruf, yang mendefinisikan urutan sortir.linear , quadratic , greene atau rstar - Tentukan algoritma untuk konstruksi indeks rtree (secara default rstar ). Untuk detailnya lihat ayat geometri.uuid - Simpan nilai ini sebagai UUID. Ini jauh lebih efektif dari sudut pandang penyempurnaan RAM/jaringan untuk UUID, daripada string. Hanya tipe hash dan - indeks yang didukung untuk UUID. Dapat digunakan dengan varian uuid apa pun, kecuali varian 0 Bidang dengan indeks reguler tidak dapat dibatalkan. Kondisi is NULL hanya didukung oleh indeks sparse dan array .
Secara default, Reindexer memindai semua struct bersarang dan menambahkan bidangnya ke namespace (serta indeks yang ditentukan). Selama indeks memindai pribadi (bidang yang tidak diekspor), bidang yang ditandai dengan reindex:"-" dan bidang yang ditandai dengan json:"-" akan dilewati.
type Actor struct {
Name string `reindex:"actor_name"`
Age int `reindex:"age"`
}
type BaseItem struct {
ID int64 `reindex:"id,hash,pk"`
UUIDValue string `reindex:"uuid_value,hash,uuid"`
}
type ComplexItem struct {
BaseItem // Index fields of BaseItem will be added to reindex
Actor [] Actor // Index fields ("name" and "age") of Actor will be added to reindex as array-indexes
Name string `reindex:"name"` // Hash-index for "name"
Year int `reindex:"year,tree"` // Tree-index for "year"
Value int `reindex:"value,-"` // Store(column)-index for "value"
Metainfo int `json:"-"` // Field "MetaInfo" will not be stored in reindexer
Parent * Item `reindex:"-"` // Index fields of "Parent" will NOT be added to reindex and all of the "Parent" exported content will be stored as non-indexed data
ParentHidden * Item `json:"-"` // Indexes and fields of "ParentHidden" will NOT be added to reindexer
privateParent * Item // Indexes and fields of "ParentHidden" will NOT be added to reindexer (same as with `json:"-"`)
AnotherActor Actor `reindex:"actor"` // Index fields of "AnotherActor" will be added to reindexer with prefix "actor." (in this example two indexes will be created: "actor.actor_name" and "actor.age")
}Reindexer dapat mengurutkan dokumen berdasarkan bidang (termasuk bersarang dan bidang namespaces yang bergabung) atau dengan ekspresi dalam urutan naik atau turun.
Untuk mengurutkan berdasarkan bidang non-indeks, semua nilai harus dikonversi satu sama lain, yaitu memiliki tipe yang sama atau menjadi salah satu jenis numerik ( bool , int , int64 atau float ).
Urutkan ekspresi dapat berisi:
bool , int , int64 , float atau tipe string . Semua nilai harus dapat dikonversi ke angka yang mengabaikan ruang terkemuka dan finishing;rank() , abs() dan ST_Distance() ;+ , - (Unary and Binary), * dan / . Jika nama lapangan diikuti oleh + mereka harus dipisahkan oleh ruang untuk membedakan nama indeks komposit. Fields of the Namespaces yang bergabung harus ditulis seperti ini: joined_namespace.field .
Abs() berarti nilai absolut dari suatu argumen.
Rank() berarti peringkat pertandingan fullText dan hanya berlaku dalam kueri fullText.
ST_Distance() berarti jarak antara titik geometri (lihat ayat geometri). Poin dapat berupa kolom dalam namespaces saat ini atau bergabung atau titik tetap dalam format ST_GeomFromText('point(1 -3)')
Dalam SQL Query Sort Ekspresi harus dikutip.
type Person struct {
Name string `reindex:"name"`
Age int `reindex:"age"`
}
type City struct {
Id int `reindex:"id"`
NumberOfPopulation int `reindex:"population"`
Center reindexer. Point `reindex:"center,rtree,linear"`
}
type Actor struct {
ID int `reindex:"id"`
PersonData Person `reindex:"person"`
Price int `reindex:"price"`
Description string `reindex:"description,text"`
BirthPlace int `reindex:"birth_place_id"`
Location reindexer. Point `reindex:"location,rtree,greene"`
}
... .
query := db . Query ( "actors" ). Sort ( "id" , true ) // Sort by field
... .
query = db . Query ( "actors" ). Sort ( "person.age" , true ) // Sort by nested field
... .
// Sort by joined field
// Works for inner join only, when each item from left namespace has exactly one joined item from right namespace
query = db . Query ( "actors" ).
InnerJoin ( db . Query ( "cities" )). On ( "birth_place_id" , reindexer . EQ , "id" ).
Sort ( "cities.population" , true )
... .
// Sort by expression:
query = db . Query ( "actors" ). Sort ( "person.age / -10 + price / 1000 * (id - 5)" , true )
... .
query = db . Query ( "actors" ). Where ( "description" , reindexer . EQ , "ququ" ).
Sort ( "rank() + id / 100" , true ) // Sort with fulltext rank
... .
// Sort by geometry distance
query = db . Query ( "actors" ).
Join ( db . Query ( "cities" )). On ( "birth_place_id" , reindexer . EQ , "id" ).
SortStPointDistance ( cities . center , reindexer. Point { 1.0 , - 3.0 }, true ).
SortStFieldDistance ( "location" , "cities.center" , true )
... .
// In SQL query:
iterator := db . ExecSQL ( "SELECT * FROM actors ORDER BY person.name ASC" )
... .
iterator := db . ExecSQL ( "SELECT * FROM actors WHERE description = 'ququ' ORDER BY 'rank() + id / 100' DESC" )
... .
iterator := db . ExecSQL ( "SELECT * FROM actors ORDER BY 'ST_Distance(location, ST_GeomFromText( ' point(1 -3) ' ))' ASC" )
... .
iterator := db . ExecSQL ( "SELECT * FROM actors ORDER BY 'ST_Distance(location, cities.center)' ASC" )Dimungkinkan juga untuk mengatur urutan penyortiran kustom seperti ini
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=a-zA-Z0-9"`
}atau seperti ini
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=АаБбВвГгДдЕеЖжЗзИиКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭ-ЯAaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0-9ЁёЙйэ-я"`
}Karakter pertama dalam daftar ini memiliki prioritas tertinggi, prioritas karakter terakhir adalah yang terkecil. Ini berarti bahwa algoritma penyortiran akan menempatkan item yang dimulai dengan karakter pertama sebelum orang lain. Jika beberapa karakter dilewati prioritas mereka akan memiliki nilai -nilai biasa (menurut karakter dalam daftar).
Untuk pola teks pencarian sederhana dalam kondisi bidang string LIKE dapat digunakan. Ini mencari string yang cocok dengan polanya. Dalam pola _ berarti arang dan % pun berarti urutan karakter apa pun.
Contoh Pergi:
query := db . Query ( "items" ).
Where ( "field" , reindexer . LIKE , "pattern" )Contoh SQL:
SELECT * FROM items WHERE fields LIKE ' pattern ''Me_t' sesuai dengan 'bertemu', 'daging', 'meleleh' dan sebagainya '%tion' sesuai dengan 'tion', 'kondisi', 'kreasi' dan sebagainya
Perhatian : Kondisi seperti menggunakan metode pemindaian. Ini dapat digunakan untuk tujuan debug atau dalam pertanyaan dengan kondisi selektif yang baik.
Umumnya untuk pencarian teks lengkap dengan kecepatan yang wajar, kami sarankan untuk menggunakan indeks fullText.
Perbarui kueri digunakan untuk memodifikasi item namespace yang ada. Ada beberapa jenis pertanyaan pembaruan: memperbarui bidang yang ada, menambahkan bidang baru dan menjatuhkan bidang yang tidak diindeks yang ada.
Perbarui SQL-Syntax
UPDATE nsName
SET field1 = value1, field2 = value2, ..
WHERE condition;Dimungkinkan juga untuk menggunakan ekspresi aritmatika dengan +, -,, /, * dan kurung
UPDATE NS SET field1 = field2 + field3 - (field4 + 5 ) / 2 termasuk fungsi seperti now() , sec() dan serial() . Untuk menggunakan ekspresi dari metode Golang Code SetExpression() perlu dipanggil alih -alih Set() .
Untuk membuat lapangan array kosong
UPDATE NS SET arrayfield = [] WHERE id = 100dan atur ke null
UPDATE NS SET field = NULL WHERE id > 100Dalam hal bidang yang tidak diindeks, menetapkan nilainya ke nilai jenis yang berbeda akan menggantikannya sepenuhnya; Dalam hal bidang yang diindeks, hanya mungkin untuk mengonversinya dari tipe yang berdekatan (tipe integral dan bool), string numerik (seperti "123456") menjadi tipe integral dan kembali. Mengatur bidang yang diindeks ke NULL mengatur ulang ke nilai default.
Dimungkinkan untuk menambahkan bidang baru ke item yang ada
UPDATE NS SET newField = ' Brand new! ' WHERE id > 100dan bahkan menambahkan bidang baru dengan jalur bersarang yang kompleks seperti ini
UPDATE NS SET nested . nested2 . nested3 . nested4 .newField = ' new nested field! ' WHERE id > 100akan membuat benda -benda bersarang berikut: Nested, Nested2, Nested3, Nested4 dan Newfield sebagai anggota Object Nested4.
Contoh menggunakan kueri pembaruan dalam kode golang:
db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Set ( "field1" , values ). Update ()Reindexer Mengaktifkan untuk memperbarui dan menambahkan bidang objek. Objek dapat diatur oleh struct, peta atau array byte (yaitu versi JSON dari representasi objek).
type ClientData struct {
Name string `reindex:"name" json:"name"`
Age int `reindex:"age" json:"age"`
Address int `reindex:"year" json:"year"`
Occupation string `reindex:"occupation" json:"occupation"`
TaxYear int `reindex:tax_year json:"tax_year"`
TaxConsultant string `reindex:tax_consultant json:"tax_consultant"`
}
type Client struct {
ID int `reindex:"id" json:"id"`
Data ClientData `reindex:"client_data" json:"client_data"`
...
}
clientData := updateClientData ( clientId )
db . Query ( "clients" ). Where ( "id" , reindexer . EQ , 100 ). SetObject ( "client_data" , clientData ). Update () Dalam hal ini, Map di Golang hanya dapat bekerja dengan string sebagai kunci. map[string]interface{} adalah pilihan yang sempurna.
Memperbarui Bidang Objek dengan Pernyataan SQL:
UPDATE clients SET client_data = { " Name " : " John Doe " , " Age " : 40 , " Address " : " Fifth Avenue, Manhattan " , " Occupation " : " Bank Manager " , " TaxYear " : 1999 , " TaxConsultant " : " Jane Smith " } WHERE id = 100 ;Perbarui SQL-Syntax dari kueri yang menjatuhkan bidang yang tidak diindeks:
UPDATE nsName
DROP field1, field2, ..
WHERE condition; db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Drop ( "field1" ). Update ()Mekanisme Pembaruan Reindexer memungkinkan untuk memodifikasi bidang array: untuk memodifikasi item tertentu dari array yang ada atau bahkan untuk mengganti seluruh bidang.
Untuk memperbarui sintaks operator langganan item digunakan:
UPDATE NS SET array[ * ].prices[ 0 ] = 9999 WHERE id = 5 dimana * berarti semua item.
Untuk memperbarui seluruh array, berikut ini digunakan:
UPDATE NS SET prices = [ 999 , 1999 , 2999 ] WHERE id = 9Bidang apa pun yang tidak diindeks dapat dengan mudah dikonversi menjadi array menggunakan sintaks ini.
Reindexer juga memungkinkan untuk memperbarui item array objek:
UPDATE NS SET extra . objects [ 0 ] = { " Id " : 0 , " Description " : " Updated! " } WHERE id = 9juga seperti ini
db . Query ( "clients" ). Where ( "id" , reindexer . EQ , 100 ). SetObject ( "extra.objects[0]" , updatedValue ). Update ()Reindexer mendukung array heterogen:
UPDATE NS SET info = [ " hi " , " bro " , 111 , 2 . 71 ] WHERE id = 9 q := DB . Query ( ns ). Where ( "id" , reindexer . EQ , 1 ). Set ( "array" , [] interface {}{ "whatsup" , 777 , "bro" })
res , err := q . Update (). FetchAll ()Indeks bidang array mendukung nilai yang dapat dikonversi menjadi tipe indeks saja. Saat disimpan, nilai -nilai tersebut dapat mengubah presisi karena konversi.
UPDATE NS SET prices_idx = [ 11 , ' 2 ' , 3 ]Untuk menghapus item dengan indeks Anda harus melakukan hal berikut:
UPDATE NS DROP array[ 5 ]Untuk menambahkan item ke array yang ada, sintaks berikut didukung:
UPDATE NS SET integer_array = integer_array || [ 5 , 6 , 7 , 8 ]Dan
UPDATE NS SET integer_array = [ 1 , 2 , 3 , 4 , 5 ] || integer_array Yang pertama menambahkan elemen ke ujung integer_array , yang kedua menambahkan 5 item ke bagian depannya. Untuk membuat kode ini berfungsi di golang SetExpression() harus digunakan sebagai ganti Set() .
Untuk menghapus item berdasarkan nilai ke array yang ada, sintaks berikut didukung:
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 , 6 , 7 , 8 ])Dan
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 , 6 ]) Yang pertama menghapus semua kemunculan dari nilai -nilai yang terdaftar di integer_array , yang kedua menghapus hanya kejadian pertama yang ditemukan. Untuk membuat kode ini berfungsi di golang SetExpression() harus digunakan sebagai ganti Set() . Jika Anda perlu menghapus satu nilai, Anda dapat menggunakan tanda kurung persegi [5] atau nilai sederhana 5 .
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 ]) update ns set integer_array = array_remove(integer_array, 5 )Hapus perintah dapat dikombinasikan dengan array concatenate:
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 ]) || [ 1 , 2 , 3 ]juga seperti ini
db . Query ( "main_ns" ). SetExpression ( "integer_array" , "array_remove(integer_array, [5,6,7,8]) || [1,2,3]" ). Update ()Dimungkinkan untuk menghapus nilai bidang kedua dari nilai bidang pertama. Dan juga menambahkan nilai baru dll. Catatan: Parameter pertama dalam perintah diharapkan menjadi array/field-array, parameter kedua dapat berupa array/skalar/field-array/field-scalar. Untuk kompatibilitas nilai/konvertibilitas yang diperlukan
UPDATE NS SET integer_array = [ 3 ] || array_remove(integer_array, integer_array2) || integer_array3 || array_remove_once(integer_array, [ 8 , 1 ]) || [ 2 , 4 ] UPDATE NS SET integer_array = array_remove(integer_array, integer_array2) || array_remove(integer_array, integer_array3) || array_remove_once(integer_array, [ 33 , 777 ]) db . Query ( "main_ns" ). SetExpression ( "integer_array" , "[3] || array_remove(integer_array, integer_array2) || integer_array3 || array_remove(integer_array, [8,1]) || [2,4]" ). Update () Reindexer mendukung transaksi. Transaksi melakukan pembaruan namespace atom. Ada transaksi sinkron dan async yang tersedia. Untuk memulai metode transaksi db.BeginTx() digunakan. Metode ini membuat objek transaksi, yang menyediakan antarmuka pembaruan/upsert/insert/hapus yang biasa untuk aplikasi. Untuk klien RPC ada batasan jumlah transaksi - setiap koneksi tidak dapat memiliki lebih dari 1024 transaksi terbuka secara bersamaan.
// Create new transaction object
tx , err := db . BeginTx ( "items" );
if err != nil {
panic ( err )
}
// Fill transaction object
tx . Upsert ( & Item { ID : 100 })
tx . Upsert ( & Item { ID : 101 })
tx . Query (). WhereInt ( "id" , reindexer . EQ , 102 ). Set ( "Name" , "Petya" ). Update ()
// Apply transaction
if err := tx . Commit (); err != nil {
panic ( err )
}Untuk mempercepat penyisipan catatan massal, mode async dapat digunakan.
// Create new transaction object
tx , err := db . BeginTx ( "items" );
if err != nil {
panic ( err )
}
// Prepare transaction object async.
tx . UpsertAsync ( & Item { ID : 100 }, func ( err error ) {})
tx . UpsertAsync ( & Item { ID : 100 }, func ( err error ) {})
// Wait for async operations done, and apply transaction.
if err := tx . Commit (); err != nil {
panic ( err )
} Argumen kedua dari UpsertAsync adalah fungsi penyelesaian, yang akan dipanggil setelah menerima respons server. Juga, jika ada kesalahan yang terjadi selama proses persiapan, maka tx.Commit harus mengembalikan kesalahan. Jadi sudah cukup, untuk memeriksa kesalahan yang dikembalikan oleh tx.Commit - pasti, bahwa semua data telah berhasil berkomitmen atau tidak.
Bergantung pada jumlah perubahan dalam transaksi ada 2 kemungkinan strategi komit:
Jumlah data untuk memilih strategi komit dapat dipilih dalam konfigurasi namespace. Periksa bidang StartCopyPolicyTxSize , CopyPolicyMultiplier dan TxSizeToAlwaysCopy di struct DBNamespacesConfig (gambaran.go)
Jika ukuran transaksi kurang dari TxSizeToAlwaysCopy , Reindexer menggunakan heuristik ekstra dan mencoba menghindari penyalinan namespace, jika tidak ada pertanyaan pemilihan yang terlihat untuk namespace ini. Dalam beberapa kasus, heuristik ini dapat meningkatkan pemilihan latensi, sehingga dapat dinonaktifkan dengan mengatur variabel env REINDEXER_NOTXHEURISTIC ke nilai apa pun yang tidak kosong.
tx.Query("ns").Exec() ... ;Reindexer dapat bergabung dengan dokumen dari beberapa ruang nama menjadi satu hasil:
type Actor struct {
ID int `reindex:"id"`
Name string `reindex:"name"`
IsVisible bool `reindex:"is_visible"`
}
// Fields, marked with 'joined' must also be exported - otherwise reindexer's binding will not be able to put data in those fields
type ItemWithJoin struct {
ID int `reindex:"id"`
Name string `reindex:"name"`
ActorsIDs [] int `reindex:"actors_ids"`
ActorsNames [] int `reindex:"actors_names"`
Actors [] * Actor `reindex:"actors,,joined"`
}
... .
query := db . Query ( "items_with_join" ). Join (
db . Query ( "actors" ).
WhereBool ( "is_visible" , reindexer . EQ , true ),
"actors"
). On ( "actors_ids" , reindexer . SET , "id" )
it := query . Exec ()Dalam contoh ini, Reindexer menggunakan refleksi di bawah kap untuk membuat irisan aktor dan menyalin struct aktor.
Kueri bergabung mungkin dari satu ke beberapa On yang terhubung dengan And (secara default), Or atau Not :
query := db . Query ( "items_with_join" ).
Join (
db . Query ( "actors" ).
WhereBool ( "is_visible" , reindexer . EQ , true ),
"actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
Or ().
On ( "actors_names" , reindexer . SET , "name" ) InnerJoin menggabungkan data dari dua ruang nama di mana ada kecocokan di bidang gabungan di kedua namespaces. LeftJoin mengembalikan semua item yang valid dari namespaces di sisi kiri kata kunci LeftJoin , bersama dengan nilai -nilai dari tabel di sisi kanan, atau tidak sama sekali jika item yang cocok tidak ada. Join adalah alias untuk LeftJoin .
InnerJoins dapat digunakan sebagai kondisi di Where klausa:
query1 := db . Query ( "items_with_join" ).
WhereInt ( "id" , reindexer . RANGE , [] int { 0 , 100 }).
Or ().
InnerJoin ( db . Query ( "actors" ). WhereString ( "name" , reindexer . EQ , "ActorName" ), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
Or ().
InnerJoin ( db . Query ( "actors" ). WhereInt ( "id" , reindexer . RANGE , [] int { 100 , 200 }), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" )
query2 := db . Query ( "items_with_join" ).
WhereInt ( "id" , reindexer . RANGE , [] int { 0 , 100 }).
Or ().
OpenBracket ().
InnerJoin ( db . Query ( "actors" ). WhereString ( "name" , reindexer . EQ , "ActorName" ), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
InnerJoin ( db . Query ( "actors" ). WhereInt ( "id" , reindexer . RANGE , [] int { 100 , 200 }), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
CloseBracket ()
query3 := db . Query ( "items_with_join" ).
WhereInt ( "id" , reindexer . RANGE , [] int { 0 , 100 }).
Or ().
InnerJoin ( db . Query ( "actors" ). WhereInt ( "id" , reindexer . RANGE , [] int { 100 , 200 }), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
Limit ( 0 ) Perhatikan bahwa biasanya Or operator mengimplementasikan hubungan pendek untuk kondisi Where : jika kondisi sebelumnya benar, yang berikutnya tidak dievaluasi. Tetapi dalam kasus InnerJoin berfungsi secara berbeda: di query1 (dari contoh di atas) kedua kondisi InnerJoin dievaluasi meskipun ada hasil dari WhereInt . Limit(0) Sebagai bagian dari InnerJoin ( query3 dari contoh di atas) tidak bergabung dengan data apa pun - berfungsi seperti filter hanya untuk memverifikasi kondisi.
Reindexer tidak mendukung ANTI JOIN SQL Construction, namun, ini mendukung operasi logis dengan gabungan. Faktanya NOT (INNER JOIN ...) sama sekali setara dengan ANTI JOIN :
query := db . Query ( "items_with_join" ).
Not ().
OpenBracket (). // Brackets are essential here for NOT to work
InnerJoin (
db . Query ( "actors" ).
WhereBool ( "is_visible" , reindexer . EQ , true ),
"actors" ).
On ( "id" , reindexer . EQ , "id" )
CloseBracket () SELECT * FROM items_with_join
WHERE
NOT (
INNER JOIN (
SELECT * FROM actors WHERE is_visible = true
) ON items_with_join . id = actors . id
) Untuk menghindari penggunaan refleksi, Item dapat mengimplementasikan antarmuka Joinable . Jika itu diimplementasikan, Reindexer menggunakan ini alih-alih implementasi berbasis refleksi yang lambat. Ini meningkatkan kinerja keseluruhan sebesar 10-20%, dan mengurangi jumlah alokasi.
// Joinable interface implementation.
// Join adds items from the joined namespace to the `ItemWithJoin` object.
// When calling Joinable interface, additional context variable can be passed to implement extra logic in Join.
func ( item * ItemWithJoin ) Join ( field string , subitems [] interface {}, context interface {}) {
switch field {
case "actors" :
for _ , joinItem := range subitems {
item . Actors = append ( item . Actors , joinItem .( * Actor ))
}
}
}Suatu kondisi dapat diterapkan untuk hasil kueri lain (subquery) yang dimasukkan ke dalam kueri saat ini. Kondisi ini mungkin berada pada baris yang dihasilkan dari subquery:
query := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ), reindexer . GE , 100 )atau antara bidang namespace kueri utama dan hasil dari subquery:
query := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 )) Hasil subquery dapat berupa bidang tertentu yang ditunjuk dengan metode Select (dalam hal ini harus mengatur filter bidang tunggal):
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ), reindexer . GE , 100 )
query2 := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 )) atau hitungan item yang memuaskan subquery yang diperlukan dengan metode ReqTotal atau CachedTotal :
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). ReqTotal (), reindexer . GE , 100 )
query2 := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). CachedTotal ())atau agregasi:
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). AggregateMax ( "age" ), reindexer . GE , 33 )
query2 := db . Query ( "main_ns" ).
Where ( "age" , reindexer . GE , db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). AggregateAvg ( "age" )) Min , Max , Avg , Sum , Count dan agregasi CountCached hanya diizinkan. Subquery tidak dapat mengandung beberapa agregasi secara bersamaan.
Subquery dapat diterapkan pada namespace yang sama atau yang lain.
Subquery tidak dapat berisi subquery lain, bergabung atau gabungkan.
Jika Anda ingin memeriksa apakah setidaknya salah satu item memuaskan subqueries, Anda dapat menggunakan kondisi ANY atau EMPTY :
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ), reindexer . ANY , nil )
query2 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . LE , 18 ), reindexer . EMPTY , nil )Dokumen dapat memiliki beberapa bidang sebagai kunci utama. Untuk mengaktifkan fitur ini, tambahkan indeks komposit ke struct. Indeks Komposit adalah indeks yang melibatkan banyak bidang, dapat digunakan sebagai pengganti beberapa indeks terpisah.
type Item struct {
ID int64 `reindex:"id"` // 'id' is a part of a primary key
SubID int `reindex:"sub_id"` // 'sub_id' is a part of a primary key
// Fields
// ....
// Composite index
_ struct {} `reindex:"id+sub_id,,composite,pk"`
}ATAU
type Item struct {
ID int64 `reindex:"id,-"` // 'id' is a part of primary key, WITHOUT personal searchable index
SubID int `reindex:"sub_id,-"` // 'sub_id' is a part of a primary key, WITHOUT a personal searchable index
SubSubID int `reindex:"sub_sub_id,-"` // 'sub_sub_id' is a part of a primary key WITHOUT a personal searchable index
// Fields
// ....
// Composite index
_ struct {} `reindex:"id+sub_id+sub_sub_id,,composite,pk"`
}Indeks gabungan juga berguna untuk menyortir hasil dengan beberapa bidang:
type Item struct {
ID int64 `reindex:"id,,pk"`
Rating int `reindex:"rating"`
Year int `reindex:"year"`
// Composite index
_ struct {} `reindex:"rating+year,tree,composite"`
}
...
// Sort query results by rating first, then by year
query := db . Query ( "items" ). Sort ( "rating+year" , true )
// Sort query results by rating first, then by year, and put item where rating == 5 and year == 2010 first
query := db . Query ( "items" ). Sort ( "rating+year" , true ,[] interface {}{ 5 , 2010 }) Untuk membuat kueri ke indeks komposit, lulus [] antarmuka {} ke .WhereComposite fungsi pembangun kueri:
// Get results where rating == 5 and year == 2010
query := db . Query ( "items" ). WhereComposite ( "rating+year" , reindexer . EQ ,[] interface {}{ 5 , 2010 }) Semua bidang dalam indeks komposit reguler (non-fulltext) harus diindeks. Yaitu untuk dapat membuat rating+year , perlu membuat beberapa jenis indeks untuk raiting dan year pertama:
type Item struct {
ID int64 `reindex:"id,,pk"`
Rating int `reindex:"rating,-"` // this field must be indexed (using index type '-' in this example)
Year int `reindex:"year"` // this field must be indexed (using index type 'hash' in this example)
_ struct {} `reindex:"rating+year,tree,composite"`
}Reindexer memungkinkan untuk mengambil hasil agregat. Saat ini menghitung, dihitung, rata -rata, jumlah, minimum, maksimum, segi dan agregasi yang berbeda didukung.
Count - Dapatkan jumlah total dokumen yang memenuhi kondisi querieCountCached - Dapatkan jumlah total dokumen yang memenuhi kondisi querie. Nilai hasil akan di -cache dan dapat digunakan kembali oleh pertanyaan lain dengan agregasi dihitungAggregateMax - Dapatkan Nilai Bidang MaksimumAggregateMin - Dapatkan nilai bidang minimumAggregateSum - Dapatkan Nilai Bidang JumlahAggregateAvg - Dapatkan nilai bidang rata -rataAggregateFacet - Dapatkan nilai Fields FacetDistinct - Dapatkan daftar nilai unik dari bidang ini Untuk mendukung agregasi, Query memiliki metode AggregateAvg , AggregateSum , AggregateMin , AggregateMax , AggregateFacet dan Distinct yang harus dipanggil sebelum eksekusi Query : ini akan meminta reindexer untuk menghitung agregasi data. Agregasi Facet berlaku untuk beberapa kolom data dan hasilnya dapat diurutkan berdasarkan kolom data apa pun atau 'hitung' dan dipotong dengan offset dan batas. Untuk mendukung metode fungsionalitas ini AggregateFacet mengembalikan AggregationFacetRequest yang memiliki Sort , Limit , dan Offset .
Kueri dengan penggabungan akan menerapkan agregasi dari kueri utama ke semua subqueries yang digabungkan. Subqueries tidak dapat memiliki agregasi sendiri. Agregasi yang tersedia untuk gabungan adalah: Count, Countcached, Sum, Min dan Max.
Untuk mendapatkan hasil agregasi, Iterator memiliki metode AggResults : tersedia setelah eksekusi kueri dan mengembalikan hasil hasil.
Contoh kode untuk items agregat berdasarkan price dan name
query := db . Query ( "items" )
query . AggregateMax ( "price" )
query . AggregateFacet ( "name" , "price" ). Sort ( "name" , true ). Sort ( "count" , false ). Offset ( 10 ). Limit ( 100 )
iterator := query . Exec ()
// Check the error
if err := iterator . Error (); err != nil {
panic ( err )
}
defer iterator . Close ()
aggMaxRes := iterator . AggResults ()[ 0 ]
if aggMaxRes . Value != nil {
fmt . Printf ( "max price = %d n " , * aggMaxRes . Value )
} else {
fmt . Println ( "no data to aggregate" )
}
aggFacetRes := iterator . AggResults ()[ 1 ]
fmt . Printf ( "'name' 'price' -> count" )
for _ , facet := range aggFacetRes . Facets {
fmt . Printf ( "'%s' '%s' -> %d" , facet . Values [ 0 ], facet . Values [ 1 ], facet . Count )
} query := db . Query ( "items" )
query . Distinct ( "name" ). Distinct ( "price" )
iterator := query . Exec ()
// Check the error
if err := iterator . Error (); err != nil {
panic ( err )
}
defer iterator . Close ()
aggResults := iterator . AggResults ()
distNames := aggResults [ 0 ]
fmt . Println ( "names:" )
for _ , name := range distNames . Distincts {
fmt . Println ( name )
}
distPrices := aggResults [ 1 ]
fmt . Println ( "prices:" )
for _ , price := range distPrices . Distincts {
fmt . Println ( price )
} Penyortiran berdasarkan bidang FACET agregat memiliki sintaks yang berbeda dalam versi SQL itu:
SELECT FACET(name, price ORDER BY " name " ASC , " count " DESC ) FROM itemsReindexer memungkinkan untuk mencari data di bidang array saat mencocokkan nilai memiliki posisi indeks yang sama. Misalnya, kami memiliki serangkaian struktur:
type Elem struct {
F1 int `reindex:"f1"`
F2 int `reindex:"f2"`
}
type A struct {
Elems [] Elem
}Upaya umum untuk mencari nilai dalam array ini
db . Query ( "Namespace" ). Where ( "f1" , EQ , 1 ). Where ( "f2" , EQ , 2 ) Menemukan semua item array Elem[] di mana f1 sama dengan 1 dan f2 sama dengan 2.
Fungsi EqualPosition memungkinkan untuk mencari di bidang array dengan indeks yang sama. Pertanyaan seperti ini:
db . Query ( "Namespace" ). Where ( "f1" , reindexer . GE , 5 ). Where ( "f2" , reindexer . EQ , 100 ). EqualPosition ( "f1" , "f2" )atau
SELECT * FROM Namespace WHERE f1 >= 5 AND f2 = 100 EQUAL_POSITION(f1,f2); akan menemukan semua item array Elem[] dengan indeks array equal di mana f1 lebih besar atau sama dengan 5 dan f2 sama dengan 100 (misalnya, kueri mengembalikan 5 item di mana hanya elemen ke -3 dari kedua array yang memiliki nilai yang sesuai).
Dengan ekspresi kompleks (ekspresi dengan tanda kurung) equal_position () dapat berada dalam braket:
SELECT * FROM Namespace WHERE (f1 >= 5 AND f2 = 100 EQUAL_POSITION(f1,f2)) OR (f3 = 3 AND f4 < 4 AND f5 = 7 EQUAL_POSITION(f3,f4,f5));
SELECT * FROM Namespace WHERE (f1 >= 5 AND f2 = 100 AND f3 = 3 AND f4 < 4 EQUAL_POSITION(f1,f3) EQUAL_POSITION(f2,f4)) OR (f5 = 3 AND f6 < 4 AND f7 = 7 EQUAL_POSITION(f5,f7));
SELECT * FROM Namespace WHERE f1 >= 5 AND (f2 = 100 AND f3 = 3 AND f4 < 4 EQUAL_POSITION(f2,f3)) AND f5 = 3 AND f6 < 4 EQUAL_POSITION(f1,f5,f6); equal_position tidak berfungsi dengan kondisi berikut: adalah nol, kosong dan dalam (dengan daftar parameter kosong).
Ada fungsi atom, yang dieksekusi di bawah kunci namespace, dan karenanya menjamin konsistensi data:
Fungsi-fungsi ini dapat diteruskan ke UpSert/Insert/Update dalam argumen 3-RD dan berikutnya.
Jika fungsi -fungsi ini disediakan, item referensi yang diteruskan akan diubah ke nilai yang diperbarui
// set ID field from serial generator
db . Insert ( "items" , & item , "id=serial()" )
// set current timestamp in nanoseconds to updated_at field
db . Update ( "items" , & item , "updated_at=now(NSEC)" )
// set current timestamp and ID
db . Upsert ( "items" , & item , "updated_at=now(NSEC)" , "id=serial()" )Kedaluwarsa data berguna untuk beberapa kelas informasi, termasuk data acara yang dihasilkan mesin, log, dan informasi sesi yang hanya perlu bertahan untuk jangka waktu terbatas.
Reindexer memungkinkan untuk mengatur TTL (Time to Live) untuk item namespace. Menambahkan TTLIndex ke namespace secara otomatis menghapus item setelah jumlah detik yang ditentukan.
Indeks TTL hanya berfungsi dengan bidang INT64 dan menyimpan data timestamp UNIX. Item yang berisi indeks TTL kedaluwarsa setelah expire_after detik. Contoh menyatakan ttlindex di golang:
type NamespaceExample struct {
ID int `reindex:"id,,pk" json:"id"`
Date int64 `reindex:"date,ttl,,expire_after=3600" json:"date"`
}
...
ns . Date = time . Now (). Unix ()Dalam hal ini item namespace namespaceexample kedaluwarsa dalam 3600 detik setelah namespaceexample.Date value bidang (yang merupakan timestamp UNIX).
Indeks TTL mendukung kueri dengan cara yang sama dengan indeks non-TTL.
Jika data sumber tersedia dalam format JSON, maka dimungkinkan untuk meningkatkan kinerja operasi UpSert/Delete dengan langsung menyerahkan JSON ke Reindexer. Deserialisasi JSON akan dilakukan dengan kode C ++, tanpa allok/deserialisasi tambahan dalam kode GO.
Upsert atau Delete Functions dapat memproses JSON hanya dengan melewati argumen byte dengan JSON
json := [] byte ( `{"id":1,"name":"test"}` )
db . Upsert ( "items" , json )Itu setara dengan lebih cepat dari:
item := & Item {}
json . Unmarshal ([] byte ( `{"id":1,"name":"test"}` ), item )
db . Upsert ( "items" , item )Dalam hal persyaratan untuk membuat serialisasi hasil kueri dalam format JSON, maka dimungkinkan untuk meningkatkan kinerja dengan secara langsung mendapatkan hasil dalam format JSON dari Reindexer. Serialisasi JSON akan dilakukan dengan kode C ++, tanpa allok/serialisasi tambahan dalam kode GO.
...
iterator := db . Query ( "items" ).
Select ( "id" , "name" ). // Filter output JSON: Select only "id" and "name" fields of items, another fields will be omitted. This fields should be specified in the same case as the jsonpaths corresponding to them.
Limit ( 1 ).
ExecToJson ( "root_object" ) // Name of root object of output JSON
json , err := iterator . FetchAll ()
// Check the error
if err != nil {
panic ( err )
}
fmt . Printf ( "%s n " , string ( json ))
...Kode ini akan mencetak sesuatu seperti:
{ "root_object" : [{ "id" : 1 , "name" : " test " }] } Untuk menghindari kondisi ras, secara default objek cache dimatikan dan semua objek dialokasikan dan deserialized dari format internal reindexer (disebut CJSON ) per setiap kueri. Deserialisasi menggunakan refleksi, sehingga kecepatannya tidak optimal (pada kenyataannya deserialisasi CJSON ~ 3-10x lebih cepat dari JSON , dan ~ 1,2x lebih cepat dari GOB ), tetapi kinerja masih secara serius dibatasi oleh overhead refleksi.
Ada 2 cara untuk mengaktifkan cache objek:
Jika objek mengimplementasikan antarmuka DeepCopy, maka Reindexer akan menyalakan cache objek dan menggunakan antarmuka DeepCopy untuk menyalin objek dari cache ke hasil kueri. Antarmuka DeepCopy bertanggung jawab untuk membuat salinan objek sumber yang dalam.
Berikut adalah contoh implementasi antarmuka deepcopy
func ( item * Item ) DeepCopy () interface {} {
copyItem := & Item {
ID : item . ID ,
Name : item . Name ,
Articles : make ([] int , cap ( item . Articles ), len ( item . Articles )),
Year : item . Year ,
}
copy ( copyItem . Articles , item . Articles )
return copyItem
} Untuk mempercepat kueri dan tidak mengalokasikan objek baru per setiap kueri, dimungkinkan, meminta objek pengembalian permintaan langsung dari cache objek. Untuk mengaktifkan perilaku ini, hubungi AllowUnsafe(true) di Iterator .
PERINGATAN: Saat digunakan AllowUnsafe(true) Queries mengembalikan pointer bersama ke struct dalam cache objek. Oleh karena itu, aplikasi tidak boleh memodifikasi objek yang dikembalikan.
res , err := db . Query ( "items" ). WhereInt ( "id" , reindexer . EQ , 1 ). Exec (). AllowUnsafe ( true ). FetchAll ()
if err != nil {
panic ( err )
}
if len ( res ) > 1 {
// item is SHARED pointer to struct in object cache
item = res [ 0 ].( * Item )
// It's OK - fmt.Printf will not modify item
fmt . Printf ( "%v" , item )
// It's WRONG - can race, and will corrupt data in object cache
item . Name = "new name"
} Secara default, ukuran maksimum cache objek adalah 256.000 item untuk setiap namespace. Untuk mengubah ukuran maksimum, gunakan metode ObjCacheSize dari NamespaceOptions , diteruskan ke OpenNaPespace. misalnya
// Set object cache limit to 4096 items
db . OpenNamespace ( "items_with_huge_cache" , reindexer . DefaultNamespaceOptions (). ObjCacheSize ( 4096 ), Item {})! Cache ini tidak boleh digunakan untuk ruang nama, yang direplikasi dari node lain: mungkin tidak konsisten untuk ruang nama replika tersebut.
Satu -satunya tipe data geometri yang didukung adalah 2D poin, yang diimplementasikan di Golang sebagai [2]float64 ( reindexer.Point ).
Dalam SQL, suatu titik dapat dibuat sebagai ST_GeomFromText('point(1 -3)') .
Satu -satunya permintaan yang didukung untuk bidang geometri adalah menemukan semua titik dalam jarak dari suatu titik. DWithin(field_name, point, distance) seperti pada contoh di bawah ini.
Fungsi SQL yang sesuai adalah ST_DWithin(field_name, point, distance) .
Indeks RTREE dapat dibuat untuk poin. Untuk melakukannya, rtree dan linear , quadratic , tag greene atau rstar harus dinyatakan. linear , quadratic , greene atau rstar berarti algoritma konstruksi RTREE mana yang akan digunakan. Di sini algoritma tercantum dalam urutan dari yang dioptimalkan untuk dimasukkan untuk dioptimalkan untuk pencarian. Tapi itu tergantung pada data. Tes mana yang lebih tepat untuk Anda. Algoritma default adalah rstar .
type Item struct {
id int `reindex:"id,,pk"`
pointIndexed reindexer. Point `reindex:"point_indexed,rtree,linear"`
pointNonIndexed reindexer. Point `json:"point_non_indexed"`
}
query1 := db . Query ( "items" ). DWithin ( "point_indexed" , reindexer. Point { - 1.0 , 1.0 }, 4.0 ) SELECT * FROM items WHERE ST_DWithin(point_non_indexed, ST_GeomFromText( ' point(1 -3.5) ' ), 5 . 0 ); Reindexer Logger dapat dihidupkan dengan metode db.SetLogger() , seperti dalam cuplikan kode ini:
type Logger struct {
}
func ( Logger ) Printf ( level int , format string , msg ... interface {}) {
log . Printf ( format , msg ... )
}
...
db . SetLogger ( Logger {}) Reindexer mendukung pencatatan tindakan lambat. Ini dapat dikonfigurasi melalui profiling.long_queries_logging dari namespace sistem #config . Pencatatan tindakan berikutnya dapat dikonfigurasi:
Pilih pertanyaan:
threshold_us (integer) : Nilai ambang batas (dalam mikrodetik) untuk eksekusi kueri terpilih. Jika terlampaui, entri inti -log akan dibuat, jika threshold_us adalah -1 logging dinonaktifkan.normalized (boolean) : Keluaran kueri dalam bentuk yang dinormalisasi.Perbarui dan hapus pertanyaan:
threshold_us (integer) : Nilai ambang batas (dalam mikrodetik) untuk pelaksanaan pembaruan atau hapus kueri. Jika terlampaui, entri inti -log akan dibuat, jika threshold_us adalah -1 logging dinonaktifkan.normalized (boolean) : Keluaran kueri dalam bentuk yang dinormalisasi.Transaksi:
threshold_us (integer) : Nilai ambang (dalam mikrodetik) untuk total waktu komit transaksi, jika threshold_us adalah -1 logging dengan total waktu komit transaksi dinonaktifkan.avg_step_threshold_us (integer) : Nilai ambang (dalam mikrodetik) untuk rata -rata waktu durasi langkah dalam transaksi. Jika avg_step_threshold_us adalah -1 logging dengan durasi langkah transaksi rata -rata dinonaktifkan.Fitur lain yang bermanfaat adalah cetakan debug dari kueri olahan. Untuk men -debug detail pertanyaan cetak ada 2 metode:
db.SetDefaultQueryDebug(namespace string,level int) - secara global memungkinkan detail cetak dari semua kueri oleh namespace
query.Debug(level int) - Cetak detail level eksekusi kueri adalah tingkat verbositas:
reindexer.INFO - hanya akan mencetak kondisi kueri
reindexer.TRACE - Akan mencetak kondisi kueri dan detail eksekusi dengan waktu
query.Explain () - Hitung dan simpan detail eksekusi kueri.
iterator.GetExplainResults () - Return Query Execution Rincian
Reindexer has support for TCMalloc (which is also a part of GPerfTools) and JEMalloc allocators (check ENABLE_TCMALLOC and ENABLE_JEMALLOC in CMakeLists.txt).
If you have built standalone server from sources available allocators will be detected and used automatically.
In go:generate builds and prebuilt packages reindexer has TCMalloc support, however none of TCMalloc libraries will be linked automatically. To force allocator's libraries linkage LD_PRELOAD with required library has to be used:
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc_and_profiler.so ./my_executable
Custom allocator may be handy to track memory consummation, profile heap/CPU or to improve general performance.
Because reindexer core is written in C++ all calls to reindexer and their memory consumption are not visible for go profiler. To profile reindexer core there are cgo profiler available. cgo profiler now is part of reindexer, but it can be used with any another cgo code.
Usage of cgo profiler is very similar with usage of go profiler.
import _ "github.com/restream/reindexer/v3/pprof" go func () {
log . Println ( http . ListenAndServe ( "localhost:6060" , nil ))
}()HEAPPROFILE=/tmp/pprofpprof -symbolize remote http://localhost:6060/debug/cgo/pprof/heapInternal Reindexer's profiler is based on gperf_tools library and unable to get CPU profile via Go runtime. However, go profiler may be used with symbolizer to retrieve C++ CPU usage.
import _ "net/http/pprof" go func () {
log . Println ( http . ListenAndServe ( "localhost:6060" , nil ))
}()REINDEXER_CGOBACKTRACE=1pprof -symbolize remote http://localhost:6060/debug/pprof/profile ? seconds=10Due to internal Golang's specific it's not recommended to try to get CPU and heap profiles simultaneously, because it may cause deadlock inside the profiler.
Go binding for Reindexer comes with optional support for OpenTelemetry integration.
To enable generation of OpenTelemetry tracing spans for all exported client side calls ( OpenNamespace , Upsert , etc), pass reindexer.WithOpenTelemetry() option when creating a Reindexer DB instance:
db := reindexer . NewReindex ( "cproto://user:[email protected]:6534/testdb" , reindexer . WithOpenTelemetry ()) All client side calls on the db instance will generate OpenTelemetry spans with the name of the performed operation and information about Reindexer DSN, namespace name (if applicable), etc.
For example, a call like this on the db instance above:
db . OpenNamespace ( "items" , reindexer . DefaultNamespaceOptions (), Item {}) will generate an OpenTelemetry span with the span name of Reindexer.OpenNamespace and with span attributes like this:
rx.dsn : cproto://user:[email protected]:6534/testdbrx.ns : items Use opentelemetry-go in your client go application to export the information externally. For example, as a minimum, you will need to configure OpenTelemetry SDK exporter to expose the generated spans externally (see the Getting Started guide for more information).
A list of connectors for work with Reindexer via other program languages (TBC later):
Pyreindexer is official connector, and maintained by Reindexer's team. It supports both builtin and standalone modes. Before installation reindexer-dev (version >= 2.10) should be installed. See installation instructions for details.
For install run:
pip3 install pyreindexerURLs:
Python version >=3.6 is required.
Reindexer for java is official connector, and maintained by Reindexer's team. It supports both builtin and standalone modes. For enable builtin mode support reindexer-dev (version >= 3.1.0) should be installed. See installation instructions for details.
For install reindexer to Java or Kotlin project add the following lines to maven project file
<dependency>
<groupId>com.github.restream</groupId>
<artifactId>rx-connector</artifactId>
<version>[LATEST_VERSION]</version>
</dependency>
URL: https://github.com/Restream/reindexer-java
Note: Java version >= 1.8 is required.
Spring wrapper for Java-connector: https://github.com/evgeniycheban/spring-data-reindexer
URL: https://github.com/Smolevich/reindexer-client
URL: https://github.com/coinrust/reindexer-rs
URL: https://github.com/oruchreis/ReindexerNet
Currently, Reindexer is stable and production ready, but it is still a work in progress, so there are some limitations and issues:
You can get help in several ways:
Landing: https://reindexer.io/
Packages repo: https://repo.reindexer.io/
More documentation (RU): https://reindexer.io/reindexer-docs/