
Reindexer ist eine eingebettbare, in Memory, dokumentorientierte Datenbank mit einer hochrangigen Query Builder-Schnittstelle.
Das Ziel von Reindexer ist es, schnelle Suche mit komplexen Fragen zu liefern. Wir bei Restream waren mit Elasticsearch nicht zufrieden und erstellten Reindexer als eine leistungsfähigere Alternative.
Der Kern ist in C ++ geschrieben und die API der Anwendungsebene ist in Go.
In diesem Dokument beschreibt Go GO Connector und seine API. Informationen zu Reinerdexer Server und der HTTP -API finden Sie in der Dokumentation von REINDExer
Es gibt zwei LTS-Versionen von Reindexer: v3.xx und v4.xx
3.xx ist derzeit unser Mainstream-Zweig und 4.xx (Release/4-Zweig) ist Beta-Version mit experimentellem Raft-Cluster und Sharding-Unterstützung. Speicher sind zwischen diesen Versionen kompatibel, Replikationskonfigurationen sind jedoch völlig unterschiedlich. Die Versionen 3 und 4 erhalten alle gleichen Fehler und Funktionen (mit Ausnahme der replikationsbezogenen).
Schlüsselmerkmale:
Leistung war von Anfang an die oberste Priorität, und wir denken, wir haben es geschafft, es ziemlich gut zu machen. Benchmarks zeigen, dass die Leistung von Reindexer einer typischen Schlüsselwertdatenbank entspricht. Auf einem einzelnen CPU -Kern bekommen wir:
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 ...Siehe Benchmarking -Ergebnisse und weitere Details im Benchmarking Repo
Reindexer zielt darauf ab, so wenig Gedächtnis wie möglich zu konsumieren. Die meisten Abfragen werden ohne Speicherallokation bearbeitet.
Um dies zu erreichen, werden mehrere Optimierungen verwendet, sowohl auf der C ++ - als auch auf der GO -Ebene:
Dokumente und Indizes werden in dichten Binär -C ++ - Strukturen gespeichert, sodass sie GO's Müllsammler nicht belasten.
String -Duplikate werden zusammengeführt.
Der Speicheraufwand beträgt etwa 32 Bytes pro Dokument + ~ 4-16 Bytes pro Suchindex.
Auf der GO -Ebene befindet sich ein Objektcache für Deserialisierte Dokumente, die nach der Abfrageausführung erstellt wurden. Zukünftige Abfragen verwenden vor dezeserialisierte Dokumente, wodurch die wiederholte Deserialisierungs- und Zuordnungskosten gesenkt werden
Die Abfrageschnittstelle verwendet sync.Pool , um interne Strukturen und Puffer wiederzuverwenden. Die Kombination dieser Technologien ermöglicht es Reinerdexer, die meisten Abfragen ohne Zuweisungen zu behandeln.
Reindexer hat eine interne Volltexten -Suchmaschine. Dokumentation und Beispiele für Volltext -Suchnutzung und Beispiele finden Sie hier
ReinIdexer kann Dokumente in der Festplatte über LevelDB speichern und laden. Dokumente werden asynchron in das Speicher -Backend -Schreiben von großen Stapeln im Hintergrund geschrieben.
Wenn ein Namespace erstellt wird, werden alle Dokumente in RAM gespeichert, sodass die Abfragen in diesen Dokumenten vollständig im Memory-Modus ausgeführt werden.
Hier finden Sie ein vollständiges Beispiel für eine grundlegende Reinerdexer -Verwendung:
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 )
}
}Es gibt auch einige grundlegende Proben für C ++ und gehen Sie hierher
Als Alternative zum Abfragebuilder bietet Reindedexer SQL -kompatible Abfrageschnittstelle. Hier ist ein Beispiel der SQL -Schnittstellenverwendung:
...
iterator := db . ExecSQL ( "SELECT * FROM items WHERE name='Vasya' AND year > 2020 AND articles IN (6,1,8) ORDER BY year LIMIT 10" )
...Bitte beachten Sie, dass die Schnittstelle für Abfrage Builder bevorzugt wird: Sie hat mehr Funktionen und ist schneller als die SQL -Schnittstelle
String -Literale sollten in einzelne Zitate eingeschlossen sein.
Zusammengesetzte Indizes sollten in doppelten Zitaten eingeschlossen sein.
SELECT * FROM items WHERE " field1+field2 " = ' Vasya 'Wenn der Feldname nicht mit Alpha, '_' oder '#' beginnt, muss er in doppelten Zitaten, Beispiele, beigefügt sein:
UPDATE items DROP " 123 " SELECT * FROM ns WHERE " 123 " = ' some_value ' SELECT * FROM ns WHERE " 123abc " = 123 DELETE FROM ns WHERE " 123abc123 " = 111Einfache Verbindungen können über die Standard -SQL -Syntax durchgeführt werden:
SELECT * FROM ns INNER JOIN ns2 ON ns2 . id = ns . fk_id WHERE a > 0Verbindet sich mit Bedingung im linken Namespace muss eine subbildungsähnliche Syntax verwenden:
SELECT * FROM ns WHERE a > 0 AND INNER JOIN ( SELECT * FROM ns2 WHERE b > 10 AND c = 1 ) ON ns2 . id = ns . fk_idDie Unterabfrage kann auch Teil der Kondition sein:
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 kann in 3 verschiedenen Modi ausgeführt werden:
embedded (builtin) Reinerdexer ist als statische Bibliothek in die Anwendung eingebettet und benötigt keinen separaten Serverprozess.embedded with server (builtinserver) ist in die Anwendung als statische Bibliothek eingebettet und der Server starten. In diesem Modus können andere Clients über CPROTO, UCPROTO oder HTTP eine Verbindung zur Anwendung herstellen.standalone Reinerdexer als eigenständige Server wird die Anwendung über Netzwerk- oder UNIX -Domänen -Sockets hergestellt.In diesem Modus hängt die Startkreis von Reindexer nicht von der statischen Bibliothek von Reindexer ab.
Der einfachste Weg, um den Rindexer -Server zu erhalten, besteht darin, das Docker -Bild von DockerHub zu ziehen und auszuführen.
docker run -p9088:9088 -p6534:6534 -it reindexer/reindexerDockerfile
Der Kern von Reindexer ist in C ++ 17 geschrieben und verwendet LevelDB als Speicher -Backend. Daher müssen CMake, C ++ 17 Toolchain und LevelDB vor der Installation von REINDExer installiert werden.
Zum Aufbau von Reinerdexer ist G ++ 8+, Clang 7+ oder Mingw64 erforderlich.
In diesen Modi hängt die Startkreis von Reindexer von den statischen Bibliotheken von Reindexer (Kern, Server und Ressource) ab.
Dieser Weg wird empfohlen und passt für die meisten Szenarien.
Go -Module mit Go.Mod erlauben nicht, C ++ -Bibliotheken in den Modulenverzeichnissen zu erstellen. GO-Binding verwendet PKG-Config, um die Verzeichnisse der Bibliotheken zu erkennen.
Die Bibliotheken von Reindexer müssen entweder aus Quellen oder aus dem vorgebauten Paket über Package Manager installiert werden.
Dann holen Sie sich das Modul:
go get -a github.com/restream/reindexer/v3 Wenn Sie geänderte Quellen von Reinerdexer benötigen, können Sie so replace verwenden.
# 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/reindexerIn diesem Fall generiert GO-Binding explizite Bibliotheken und Pfadeliste und verwendet keine PKG-Konfiguration.
Wenn Sie go.mod nicht verwenden, ist es möglich, Reinerdexer auf diese Weise aus Quellen zu bekommen und aufzubauen:
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 unterstützt keinen ordnungsgemäßen Verkauf für CGO-Code (Golang/Go#26366). Es ist jedoch möglich, Vend zu verwenden, um die Quellen von Reindexer in Lieferantenverzeichnis zu kopieren.
Mit vend können Sie go generate -mod=vendor für builtin und builtinserver anrufen, der in Ihrem Lieferantenverzeichnis platziert ist.
Es ist auch möglich, die Quellen von Reindexer einfach zu kopieren, indem Sie mit git clone in Jugendprojekte in das Jugendprojekt kopieren.
In diesen Fällen müssen alle Abhängigkeiten von Reindexer's Go.Mod manuell mit geeigneten Versionen installiert werden.
Intern werden Strukturen in zwei Teile unterteilt:
reindex -Struktur -Tag Abfragen sind nur auf den indexierten Feldern möglich, die mit reindex -Tag markiert sind. Das reindex -Tag enthält den Indexnamen, den Typ und die zusätzlichen Optionen:
reindex:"<name>[[,<type>],<opts>]"
name - Indexname.type - Indextyp:hash - Schnell auswählen nach EQ und setze Match. Standardmäßig verwendet. Ermöglicht eine langsame und ineffiziente Sortierung nach Feld.tree - schnelle Auswahl nach Reichweite, GT und LT -Übereinstimmungen. Ein bisschen langsamer für EQ und Set Matches als hash -Index. Ermöglicht schnelle Sortierergebnisse nach Feld.text - Volltext -Suchindex. Verwendungsdetails zur Volltextsuche werden hier beschrieben- - Spaltenindex. Ich kann keine schnelle Auswahl durchführen, da es mit Full-Scan-Technik implementiert ist. Hat den kleinsten Speicheraufwand.ttl - TTL -Index, der nur mit INT64 -Feldern funktioniert. Diese Indizes sind für die Darstellung von Datumsfeldern (als UNIX -Zeitstempel gespeichert) sehr geeignet, die nach der angegebenen Menge von Sekunden ausfallen.rtree - Nur verfügbare Dwithin -Match. Nur für [2]float64 (oder reindexer.Point ) Feldtyp akzeptabel. Details finden Sie unter dem Unterabschnitt Geometrie.opts - Zusätzliche Indexoptionen:pk - Feld ist Teil eines Primärschlüssels. Struktur muss mindestens 1 Feld mit pk markiert habencomposite - Erstellen Sie den zusammengesetzten Index. Der Feldtyp muss eine leere Struktur sein: struct{} .joined - Field ist ein Empfänger für Join. Der Feldtyp muss []*SubitemType sein.dense - Reduzieren Sie die Indexgröße. Für hash und tree speichert es 8 Bytes pro eindeutigem Schlüsselwert. Für - es speichert 4-8 Bytes pro Element. Nützlich für Indizes mit hoher Selektivität, aber für tree und hash -Indizes mit niedriger Selektivität kann die Leistung der Aktualisierung ernsthaft verringern. Ebenfalls dense wird es aufgrund der mangelnden CPU -Cache -Optimierung die weiten Abfragen in den Full -Scan -Abfragen auf - Indizes verlangsamen.sparse - Zeile (Dokument) enthält einen Wert des spärlichen Index nur falls er absichtlich festgelegt ist - es gibt keine leeren (oder Standard-) Datensätze dieser Art von Indizes in der Zeile (Dokument). Es ermöglicht RAM, aber es kostet Ihre Leistung - es funktioniert etwas langsamer als normale Indizes.collate_numeric - String -Index erstellen, der die Werte in numerischer Reihenfolge der Werte liefert. Der Feldtyp muss eine Zeichenfolge sein.collate_ascii - CREATE CASE -INSEITIVE STING -Index funktioniert mit ASCII. Der Feldtyp muss eine Zeichenfolge sein.collate_utf8 - Erstellen Sie den Fall -Insensitiv -String -Index mit UTF8. Der Feldtyp muss eine Zeichenfolge sein.collate_custom=<ORDER> - Erstellen Sie den benutzerdefinierten Auftragsstring -Index. Der Feldtyp muss eine Zeichenfolge sein. <ORDER> ist eine Abfolge von Buchstaben, die Sortierreihenfolge definiert.linear , quadratic , greene oder rstar - Algorithmus für den Aufbau des rtree -Index (standardmäßig rstar ) angeben. Details finden Sie unter dem Unterabschnitt Geometrie.uuid - Speichern Sie diesen Wert als UUID. Dies ist vom Standpunkt der RAM/Network -Vollmessung für UUIDs viel effektiver als Zeichenfolgen. Nur hash und - Indextypen werden für UUIDs unterstützt. Kann mit jeder UUID -Variante verwendet werden, mit Ausnahme der Variante 0 Felder mit regulären Indizes sind nicht nullbar. Der Zustand wird is NULL nur durch sparse und array -Indizes unterstützt.
Standardmäßig scannt Reinerdexer alle verschachtelten Strukturen und fügt ihre Felder dem Namespace (sowie den angegebenen Indizes) hinzu. Während der Indizes scannen Sie private (unerpasste Felder), Felder mit reindex:"-" und Felder mit json:"-" werden übersprungen.
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 kann Dokumente nach Feldern (einschließlich verschachtelter Felder der zusammengefügten Namespaces) oder nach Ausdrücken in aufsteigender oder absteigender Reihenfolge sortieren.
Um nach Nicht-Index-Feldern zu sortieren, müssen alle Werte miteinander konvertierbar sein, dh entweder die gleichen Typen oder eine der numerischen Typen ( bool , int , int64 oder float ).
Sortieren können Ausdrücke enthalten:
bool , int , int64 , float oder string -Typen. Alle Werte müssen in Zahlen umgewandelt werden, die führende und beendete Räume ignorieren.rank() , abs() und ST_Distance() ;+ , - (Unär und binär), * und / . Wenn der Feldname + müssen sie durch den Raum getrennt werden, um zusammengesetzten Indexnamen zu unterscheiden. Felder der zusammengefügten Namespaces müssen so geschrieben werden: joined_namespace.field .
Abs() bedeutet absolutes Wert eines Arguments.
Rank() bedeutet FullText -Rang der Übereinstimmung und ist nur in FullText -Abfrage anwendbar.
ST_Distance() bedeutet Abstand zwischen Geometriepunkten (siehe Geometrie -Unterabschnitt). Die Punkte können Spalten in aktuellen oder verbundenen Namenspaces oder festen Punkten im Format ST_GeomFromText('point(1 -3)') sein.
In SQL Query Sort -Ausdruck muss zitiert werden.
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" )Es ist auch möglich, eine benutzerdefinierte Sortierreihenfolge wie diese festzulegen
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=a-zA-Z0-9"`
}oder so
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=АаБбВвГгДдЕеЖжЗзИиКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭ-ЯAaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0-9ЁёЙйэ-я"`
}Der allererste Charakter in dieser Liste hat die höchste Priorität, die Priorität des letzten Charakters ist der kleinste. Dies bedeutet, dass Sortieralgorithmus Elemente, die mit dem ersten Charakter vor anderen beginnen. Wenn einige Zeichen übersprungen werden, hätten ihre Prioritäten ihre üblichen Werte (gemäß den Zeichen in der Liste).
Für einfache Suchtextmuster in Stringfeldern kann der LIKE verwendet werden. Es sucht nach Saiten, die dem Muster entsprechen. Im _ bedeutet jeder Zeichen und % jede Sequenz von Zeichen.
Gehen Sie Beispiel:
query := db . Query ( "items" ).
Where ( "field" , reindexer . LIKE , "pattern" )SQL Beispiel:
SELECT * FROM items WHERE fields LIKE ' pattern ''me_t' entspricht 'Meet', 'Fleisch', 'schmelzen' und so über '%tion' entspricht 'tion', 'Zustand', 'Schöpfung' und so weiter
Achtung : Bedingung wie verwendet Scan -Methode. Es kann für Debug -Zwecke oder innerhalb von Abfragen mit anderen guten selektiven Bedingungen verwendet werden.
Im Allgemeinen empfehlen wir für die Volltext -Suche mit angemessener Geschwindigkeit, den FullText -Index zu verwenden.
Update -Abfragen werden verwendet, um vorhandene Elemente eines Namespace zu ändern. Es gibt verschiedene Arten von Update-Abfragen: Aktualisierung vorhandener Felder, Hinzufügen neuer Felder und Löschen vorhandener nicht indizierten Felder.
Aktualisieren Sie SQL-Syntax
UPDATE nsName
SET field1 = value1, field2 = value2, ..
WHERE condition;Es ist auch möglich, arithmetische Ausdrücke mit +, -, /, * und Klammern zu verwenden
UPDATE NS SET field1 = field2 + field3 - (field4 + 5 ) / 2 einschließlich Funktionen wie now() , sec() und serial() . Um Ausdrücke aus der Methode golang code SetExpression() zu verwenden, muss anstelle von Set() aufgerufen werden.
Ein Arrayfeld leer zu machen
UPDATE NS SET arrayfield = [] WHERE id = 100und setzen Sie es auf Null
UPDATE NS SET field = NULL WHERE id > 100Bei nicht indizierten Feldern ersetzt die Festlegung des Wertes auf einen Wert eines anderen Typs ihn vollständig. Bei indizierten Feldern ist es nur möglich, ihn vom benachbarten Typ (Integraltypen und Bool), numerischen Zeichenfolgen (wie "123456") in Integraltypen und zurück zu konvertieren. Das Festlegen des indizierten Feldes in Null setzt es auf einen Standardwert zurück.
Es ist möglich, vorhandene Artikel neue Felder hinzuzufügen
UPDATE NS SET newField = ' Brand new! ' WHERE id > 100und fügen Sie sogar ein neues Feld durch einen komplexen verschachtelten Weg wie diesen hinzu
UPDATE NS SET nested . nested2 . nested3 . nested4 .newField = ' new nested field! ' WHERE id > 100Erstellt die folgenden verschachtelten Objekte: verschachtelt, verschachtelt2, verschachtelt3, verschachtelt4 und Newfield als Mitglied des Objekts Nested4.
Beispiel für die Verwendung von Update -Abfragen im Golang -Code:
db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Set ( "field1" , values ). Update ()Reindexer ermöglicht das Aktualisieren und Hinzufügen von Objektfeldern. Das Objekt kann entweder durch eine Struktur, eine Karte oder ein Byte -Array festgelegt werden (dh eine JSON -Version der Objektdarstellung).
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 () In diesem Fall kann Map in Golang nur mit String als Schlüssel arbeiten. map[string]interface{} ist eine perfekte Wahl.
Aktualisierung des Objektfelds durch SQL -Anweisung:
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 ;Aktualisieren Sie SQL-Syntax von Abfragen, die vorhandene nicht indizierte Felder fallen lassen:
UPDATE nsName
DROP field1, field2, ..
WHERE condition; db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Drop ( "field1" ). Update ()Der Mechanismus für den REINDExer -Update ermöglicht das Ändern von Array -Feldern: ein bestimmtes Element eines vorhandenen Arrays oder sogar ein ganzes Feld zu ändern.
So wird ein Element -Abonnement -Operator -Syntax verwendet:
UPDATE NS SET array[ * ].prices[ 0 ] = 9999 WHERE id = 5 wo * bedeutet alle Gegenstände.
Um das gesamte Array zu aktualisieren, wird Folgendes verwendet:
UPDATE NS SET prices = [ 999 , 1999 , 2999 ] WHERE id = 9Jedes nicht indizierte Feld kann mit dieser Syntax leicht in ein Array konvertiert werden.
REINDEXER ermöglicht auch die Aktualisierung der Elemente von Objektarrays:
UPDATE NS SET extra . objects [ 0 ] = { " Id " : 0 , " Description " : " Updated! " } WHERE id = 9Auch so
db . Query ( "clients" ). Where ( "id" , reindexer . EQ , 100 ). SetObject ( "extra.objects[0]" , updatedValue ). Update ()Reindexer unterstützt heterogene Arrays:
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 ()Index-Array-Felder unterstützen Werte, die nur in einen Indextyp konvertiert werden können. Bei der Speicherung können solche Werte aufgrund der Umwandlung die Präzision ändern.
UPDATE NS SET prices_idx = [ 11 , ' 2 ' , 3 ]So entfernen Sie Element nach Index, sollten Sie Folgendes ausführen:
UPDATE NS DROP array[ 5 ]So fügen Sie Elemente zu einem vorhandenen Array hinzu. Die folgende Syntax wird unterstützt:
UPDATE NS SET integer_array = integer_array || [ 5 , 6 , 7 , 8 ]Und
UPDATE NS SET integer_array = [ 1 , 2 , 3 , 4 , 5 ] || integer_array Der erste fügt Elemente zum Ende von integer_array hinzu, das zweite fügt 5 Elemente an der Vorderseite hinzu. Damit diesen Code in Golang SetExpression() funktioniert, sollte anstelle von Set() verwendet werden.
Um Elemente nach Wert in ein vorhandenes Array zu entfernen, wird die folgende Syntax unterstützt:
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 , 6 , 7 , 8 ])Und
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 , 6 ]) Der erste beseitigt alle Vorkommen der aufgelisteten Werte in integer_array , der zweite löscht nur das erste gefundene Ereignis. Damit diesen Code in Golang SetExpression() funktioniert, sollte anstelle von Set() verwendet werden. Wenn Sie einen Wert entfernen müssen, können Sie quadratische Klammern [5] oder einen einfachen Wert 5 verwenden.
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 ]) update ns set integer_array = array_remove(integer_array, 5 )Der Befehl entfernen kann mit Array -okatenat kombiniert werden:
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 ]) || [ 1 , 2 , 3 ]Auch so
db . Query ( "main_ns" ). SetExpression ( "integer_array" , "array_remove(integer_array, [5,6,7,8]) || [1,2,3]" ). Update ()Es ist möglich, die Werte des zweiten Feldes aus den Werten des ersten Feldes zu entfernen. Und auch neue Werte hinzufügen usw. Hinweis: Der erste Parameter in Befehlen wird erwartet, dass es sich um ein Array/Feld-Array handelt. Der zweite Parameter kann ein Array/Scalar/Field-Array/Field-Scalar sein. Für Werte Kompatibilität/Konvertierbarkeit erforderlich
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 unterstützt Transaktionen. Die Transaktion wird atomic Namespace -Update durchgeführt. Es gibt synchrone und asynchronisierte Transaktion. Um die Transaktionsmethode zu starten, wird db.BeginTx() verwendet. Diese Methode erstellt Transaktionsobjekt, das die Anwendung bei der Anwendung üblich/upsert/einfügen/löschen. Für RPC -Clients gibt es eine Begrenzung der Transaktionen - jede Verbindung kann nicht mehr als 1024 offene Transaktionen gleichzeitig haben.
// 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 )
}Für die Beschleunigung des Insertion von Bulk Records kann der asynchronen Modus verwendet werden.
// 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 )
} Das zweite Argument der UpsertAsync ist die Abschlussfunktion, die nach Empfangsempfangsreaktion aufgerufen wird. Wenn bei der Vorbereitung des Vorbereitungsprozesses ein Fehler aufgetreten ist, sollte tx.Commit einen Fehler zurückgeben. Daher reicht es aus, Fehler zu überprüfen, die von tx.Commit zurückgegeben wurden - um sicher zu sein, dass alle Daten erfolgreich begangen wurden oder nicht.
Abhängig von der Menge der Transaktionsänderungen gibt es zwei mögliche Festungsstrategien:
Die Datenmenge zur Auswahl einer Commit -Strategie kann in der Namespace -Konfiguration ausgewählt werden. Überprüfen Sie Felder StartCopyPolicyTxSize , CopyPolicyMultiplier und TxSizeToAlwaysCopy in struct DBNamespacesConfig (Describer.go)
Wenn die Transaktionsgröße weniger als TxSizeToAlwaysCopy ist, verwendet Reinerdexer zusätzliche Heuristik und versucht, den Namespace -Kopieren zu vermeiden, wenn keine Auswahlabfragen für diesen Namespace zu sehen sind. In einigen Fällen kann diese Heuristik erhöht werden, sodass sie deaktiviert werden kann, indem sie die Variable REINDEXER_NOTXHEURISTIC Env auf nicht leere Wert einstellen.
tx.Query("ns").Exec() ... ;ReinIdexer kann Dokumente aus mehreren Namespaces in ein einzelnes Ergebnis verbinden:
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 ()In diesem Beispiel verwendet Reinerdexer Reflexion unter der Motorhaube, um Schauspieler -Slice und Kopieren der Schauspielerstruktur zu erstellen.
Die Join -Abfrage kann von eins zu mehreren On Bedingungen haben, die mit And (standardmäßig) Or Not Operatoren verbunden sind:
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" ) Ein InnerJoin kombiniert Daten aus zwei Namespaces, in denen sich die Verbindungsfelder in beiden Namespaces übereinstimmen. Ein LeftJoin gibt alle gültigen Elemente aus den Namespaces auf der linken Seite des Schlüsselworts LeftJoin zurück, zusammen mit den Werten aus der Tabelle auf der rechten Seite oder nichts, wenn es nicht vorhanden ist. Join ist ein Alias für LeftJoin .
InnerJoins können als Zustand in Where Klausel verwendet werden:
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 ) Beachten Sie, dass normalerweise Or der Bediener Kurzschluss für Where Bedingungen implementieren: Wenn die vorherige Bedingung wahr ist, wird der nächste nicht bewertet. Im Falle von InnerJoin funktioniert es jedoch unterschiedlich: In query1 (aus dem obigen Beispiel) werden beide InnerJoin -Bedingungen trotz des Ergebniss von WhereInt bewertet. Limit(0) als Teil von InnerJoin ( query3 aus dem Beispiel oben) verbindet keine Daten - es funktioniert nur wie ein Filter, um die Bedingungen zu überprüfen.
ReinIdexer unterstützt keine ANTI JOIN -SQL -Konstruktion, unterstützt jedoch logische Operationen mit Joins. Tatsächlich ist NOT (INNER JOIN ...) dem ANTI JOIN völlig äquivalent:
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
) Um die Verwendung von Reflexion zu vermeiden, kann Item Joinable implementieren. Wenn dies implementiert ist, verwendet Reinerdexer dies anstelle der langsamen Reflexions-basierten Implementierung. Dies erhöht die Gesamtleistung um 10-20%und verringert die Anzahl der Zuordnungen.
// 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 ))
}
}
}Eine Bedingung könnte auf das Ergebnis einer anderen Abfrage (Unterabfrage) in die aktuelle Abfrage angewendet werden. Die Bedingung kann entweder auf resultierenden Reihen der Unterabfrage liegen:
query := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ), reindexer . GE , 100 )oder zwischen einem Gebiet der Hauptabfrage -Namespace und dem Ergebnis der Unterabfrage:
query := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 )) Das Ergebnis der Unterabfrage kann entweder ein bestimmtes Feld sein, das nach Select Methode gezeigt wird (in diesem Fall muss das Einzelfeldfilter festgelegt werden):
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 )) oder Zählung von Elementen, die für die Unterabfrage erfüllt werden, die durch ReqTotal oder CachedTotal Methoden gefordert wird:
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 ())oder Aggregation:
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 und CountCached -Aggregationen sind nur zulässig. Die Unterabfrage kann nicht mehrere Aggregationen gleichzeitig enthalten.
Die Unterabfrage kann auf denselben Namespace oder auf den anderen angewendet werden.
Die Unterabfrage kann keine andere Unterabfrage, Verbindung oder Verschmelzung enthalten.
Wenn Sie überprüfen möchten, ob mindestens eines der Artikel für die Unterabfragen zufriedenstellend ist, können Sie ANY oder EMPTY Zustand verwenden:
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 )Ein Dokument kann mehrere Felder als Primärschlüssel haben. Um diese Funktion zu aktivieren, fügen Sie den zusammengesetzten Index zu Struct hinzu. Der zusammengesetzte Index ist ein Index, der mehrere Felder umfasst und anstelle von mehreren separaten Indizes verwendet werden kann.
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"`
}ODER
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"`
}Auch zusammengesetzte Indizes sind nützlich, um die Ergebnisse nach mehreren Feldern zu sortieren:
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 }) Für eine Abfrage zum zusammengesetzten Index [] STANDSCHAFT {} an .WhereComposite -Funktion des Abfragebuilders:
// Get results where rating == 5 and year == 2010
query := db . Query ( "items" ). WhereComposite ( "rating+year" , reindexer . EQ ,[] interface {}{ 5 , 2010 }) Alle Felder im regulären (nicht-fulltext) zusammengesetzten Index müssen indiziert werden. IE, um eine zusammengesetzte Indexbewertung rating+year erstellen zu können, ist es erforderlich, zuerst einige Arten von Indizes für raiting und year zu erstellen:
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"`
}ReinIdexer ermöglicht das Abrufen aggregierter Ergebnisse. Derzeit werden Count, Countcached, Durchschnitt, Summe, Minimum, Maximum, Facetten und unterschiedliche Aggregationen unterstützt.
Count - Gesamtzahl der Dokumente erhalten, die den Bedingungen des Queries erfüllenCountCached - Erhalten Sie die Gesamtzahl der Dokumente, die den Bedingungen des Queries entsprechen. Der Ergebniswert wird zwischengespeichert und kann von den anderen Abfragen mit CountCached Aggregation wiederverwendet werdenAggregateMax - maximalen Feldwert erhaltenAggregateMin - Mindestfeldwert erhaltenAggregateSum - Summenfeldwert erhaltenAggregateAvg - Durchschnittlich Feldwert erhaltenAggregateFacet - Felderfacettenwert erhaltenDistinct - Liste der eindeutigen Werte des Feldes erhalten Um die Aggregation zu unterstützen, hat Query Methoden AggregateAvg , AggregateSum , AggregateMin , AggregateMax , AggregateFacet und Distinct vor der Query aufgerufen. Die Aggregationsfacette ist für mehrere Datenspalten anwendbar, und das Ergebnis kann durch jede Datenspalte oder "Zählen" sortiert und nach Offset und Grenzwert abgeschnitten werden. Um diese Funktionalitätsmethode zu unterstützen, gibt AggregateFacet AggregationFacetRequest zurück, die Methoden Sort , Limit und Offset .
Anfragen mit Merge wenden Aggregationen von der Hauptabfrage auf alle zusammengeführten Unterabfragen an. Unterabfragen können keine eigenen Aggregationen haben. Verfügbare Aggregationen für Merge-Querien sind: Graf, CountCached, Sum, Sum, Min und Max.
Um Aggregationsergebnisse zu erzielen, hat Iterator AggResults : Es ist nach der Ausführung von Abfragen verfügbar und gibt die Ergebnisse der Ergebnisse zurück.
Beispielcode für aggregierte items nach price und 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 )
} Sortieren nach den Feldern der aggregierten FACET hat eine unterschiedliche Syntax in seiner SQL -Version:
SELECT FACET(name, price ORDER BY " name " ASC , " count " DESC ) FROM itemsReinIdexer ermöglicht das Durchsuchen von Daten in Array -Feldern, wenn die Übereinstimmungswerte die gleichen Indizespositionen aufweisen. Zum Beispiel haben wir eine Reihe von Strukturen:
type Elem struct {
F1 int `reindex:"f1"`
F2 int `reindex:"f2"`
}
type A struct {
Elems [] Elem
}Häufiger Versuch, Werte in diesem Array zu durchsuchen
db . Query ( "Namespace" ). Where ( "f1" , EQ , 1 ). Where ( "f2" , EQ , 2 ) Findet alle Elemente von Array Elem[] wobei f1 gleich 1 ist und f2 gleich 2 ist.
EqualPosition Funktionsfunktion ermöglicht es, in Array -Feldern mit gleichen Indizes zu suchen. Abfragen wie diese:
db . Query ( "Namespace" ). Where ( "f1" , reindexer . GE , 5 ). Where ( "f2" , reindexer . EQ , 100 ). EqualPosition ( "f1" , "f2" )oder
SELECT * FROM Namespace WHERE f1 >= 5 AND f2 = 100 EQUAL_POSITION(f1,f2); Findet alle Elemente von Array Elem[] mit equal Array -Indizes, in denen f1 größer oder gleich 5 und f2 entspricht.
Mit komplexen Ausdrücken (Ausdrücke mit Klammern) können Equal_Position () in einer Klammer liegen:
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 funktioniert nicht mit den folgenden Bedingungen: ist null, ist leer und in (mit leerer Parameterliste).
Es gibt Atomfunktionen, die unter dem Namespace -Sperre ausgeführt werden, und garantiert daher die Datenkonsistenz:
Diese Funktionen können in 3-RD- und Nächsten Argumenten an UpSert/INSERT/UPDATE übergeben werden.
Wenn diese Funktionen bereitgestellt werden, wird das von Referenzelement bestehende Übergebene in den aktualisierten Wert geändert
// 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()" )Der Datenverlauf ist nützlich für einige Informationsklassen, darunter maschinell generierte Ereignisdaten, Protokolle und Sitzungsinformationen, die nur für einen begrenzten Zeitraum bestehen müssen.
ReinIdexer ermöglicht es, TTL (Zeit zum Leben) für Namespace -Elemente festzulegen. Das Hinzufügen von TTLIndex zum Namespace entfernt automatisch die Elemente nach einer bestimmten Anzahl von Sekunden.
TTL -Indizes funktionieren nur mit INT64 -Feldern und speichern UNIX -Zeitstempeldaten. Elemente, die den TTL -Index enthalten, verfallen nach expire_after Sekunden. Beispiel für die Deklaration von Ttlindex in 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 ()In diesem Fall verfallen Elemente des Namespace NamespaceExample in 3600 Sekunden nach dem NamespaceExample.Date -Feldwert (der UNIX -Zeitstempel ist).
Ein TTL-Index unterstützt Abfragen genauso wie nicht-TTL-Indizes.
Wenn Quelldaten im JSON -Format verfügbar sind, ist es möglich, die Leistung von Upsert/Löschen -Vorgängen zu verbessern, indem JSON direkt an Reinerdexer weitergegeben wird. Die JSON -Deserialisierung erfolgt mit C ++ - Code ohne zusätzliche Allocs/Deserialisierung im Go -Code.
Upsert- oder Löschen von Funktionen können JSON verarbeiten, indem sie [] Byte -Argument mit JSON bestanden haben
json := [] byte ( `{"id":1,"name":"test"}` )
db . Upsert ( "items" , json )Es ist nur schneller entspricht:
item := & Item {}
json . Unmarshal ([] byte ( `{"id":1,"name":"test"}` ), item )
db . Upsert ( "items" , item )Im Falle einer Voraussetzung zur Serialisierung von Ergebnissen der Abfrage im JSON -Format ist es möglich, die Leistung zu verbessern, indem die Ergebnisse des JSON -Formats von Reindexer direkt erhalten werden. Die JSON -Serialisierung erfolgt mit C ++ - Code ohne zusätzliche Allocs/Serialisierung im Go -Code.
...
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 ))
...Dieser Code druckt so etwas wie:
{ "root_object" : [{ "id" : 1 , "name" : " test " }] } Um Rennbedingungen zu vermeiden, wird standardmäßig der Objekt -Cache ausgeschaltet und alle Objekte pro Abfrage aus dem internen Format (CJSON) aus dem Reinerdexer -Format (genannt CJSON ) zugeteilt und deserialisiert. Die Deserialisierung verwendet Reflexion, daher ist ihre Geschwindigkeit nicht optimal (tatsächlich ist die Deserialisierung CJSON ~ 3-10x schneller als JSON und ~ 1,2x schneller als GOB ), aber die Leistung ist immer noch durch den Reflexionsaufwand begrenzt.
Es gibt 2 Möglichkeiten, um Objekt -Cache zu aktivieren:
Wenn das Objekt eine DeepCopy -Schnittstelle implementiert, schaltet REINDExer den Objekt -Cache ein und kopiert Objekte von Cache zum Abfragen von Ergebnissen. Die DeepCopy -Schnittstelle ist dafür verantwortlich, eine tiefe Kopie des Quellobjekts zu erstellen.
Hier ist die Probe der Implementierung der DeepCopy -Schnittstelle
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
} Um Abfragen zu beschleunigen und keine neuen Objekte pro Abfrage zuordnen, ist es möglich, dass die Abfrage -Rückgabeobjekte direkt aus dem Objekt -Cache. Um dieses Verhalten zu aktivieren, rufen Sie AllowUnsafe(true) auf Iterator auf.
WARNUNG: Bei Verwendung von AllowUnsafe(true) Abfragen Returns Returns Shared Zeiger zu Strukturen im Objekt -Cache. Daher darf die Anwendung zurückgegebene Objekte nicht ändern.
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"
} Standardmäßig beträgt die maximale Größe des Objekt -Cache 256000 Elemente für jeden Namespace. Um die maximale Größe zu ändern. Verwenden Sie die ObjCacheSize -Verfahrensmethode der NamespaceOptions . z.B
// Set object cache limit to 4096 items
db . OpenNamespace ( "items_with_huge_cache" , reindexer . DefaultNamespaceOptions (). ObjCacheSize ( 4096 ), Item {})! Dieser Cache sollte nicht für die Namespaces verwendet werden, die aus den anderen Knoten repliziert wurden: Er kann für die Namespaces dieser Replik inkonsistent sein.
Der einzige unterstützte Geometrie -Datentyp ist 2D -Punkt, der in Golang als [2]float64 ( reindexer.Point ) implementiert wird.
In SQL kann ein Punkt als ST_GeomFromText('point(1 -3)') .
Die einzige unterstützte Anfrage für Geometriefeld besteht darin, alle Punkte innerhalb eines Abstands von einem Punkt zu finden. DWithin(field_name, point, distance) wie auf Beispiel unten.
Die entsprechende SQL -Funktion ist ST_DWithin(field_name, point, distance) .
Der RTREE -Index kann für Punkte erstellt werden. Zu diesem Zweck sollten rtree und linear , quadratic , greene oder rstar -Tags deklariert werden. linear , quadratic , greene oder rstar -Mittelwerte, welcher Algorithmus der RTREE -Konstruktion verwendet werden würde. Hier werden Algorithmen in der Reihenfolge von optimiert für das Einsetzen bis zur Suche für die Suche aufgeführt. Aber es hängt von Daten ab. Test, was für Sie besser geeignet ist. Standardalgorithmus ist 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 ); Der REINDExer -Logger kann von db.SetLogger() -Methode eingeschaltet werden, genau wie in diesem Code -Snippet:
type Logger struct {
}
func ( Logger ) Printf ( level int , format string , msg ... interface {}) {
log . Printf ( format , msg ... )
}
...
db . SetLogger ( Logger {}) Reindexer unterstützt die Protokollierung langsamer Aktionen. Es kann über profiling.long_queries_logging konfiguriert werden #config Die Protokollierung der nächsten Aktionen kann konfiguriert werden:
Wählen Sie Abfragen aus:
threshold_us (integer) : Der Schwellenwert (in Mikrosekunden) für die Ausführung der Auswahl von Abfragen. Wenn es überschritten wird, wird ein Kerneintrag erfolgen, wenn threshold_us -1 -Protokollierung deaktiviert ist.normalized (boolean) : Ausgabe der Abfrage in einer normalisierten Form.Abfragen aktualisieren und löschen:
threshold_us (integer) : Der Schwellenwert (in Mikrosekunden) zur Ausführung von Aktualisierung oder Löschen von Abfragen. Wenn es überschritten wird, wird ein Kerneintrag erfolgen, wenn threshold_us -1 -Protokollierung deaktiviert ist.normalized (boolean) : Ausgabe der Abfrage in einer normalisierten Form.Transaktionen:
threshold_us (integer) : Schwellenwert (in Mikrosekunden) für die gesamte Transaktionsausschusseszeit, wenn threshold_us -1 -Protokollierung durch die gesamte Transaktionsausschusseszeit ist deaktiviert ist.avg_step_threshold_us (integer) : Schwellenwert (in Mikrosekunden) für die durchschnittliche Schrittdauerzeit in der Transaktion. Wenn avg_step_threshold_us nach durchschnittlicher Transaktionsdauer -1 -Protokollierung ist.Eine weitere nützliche Funktion ist das Debugg -Druck von verarbeiteten Abfragen. Um Druckanfragen Details zu debuggen, gibt es 2 Methoden:
db.SetDefaultQueryDebug(namespace string,level int) - Es ermöglicht global Druckdetails aller Abfragen mit dem Namespace
query.Debug(level int) - Druckdetails zur level der Abfrage sind Ausführungsstufe:
reindexer.INFO - druckt nur Abfragebedingungen aus
reindexer.TRACE - druckt Abfragebedingungen und Ausführungsdetails mit Timings aus
query.Explain () - Berechnen und Ladenabfrageausführungsdetails.
iterator.GetExplainResults () - Details zur Ausführung von Abfragen zurückgeben
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/