
Reindexer เป็นฐานข้อมูลที่มุ่งเน้นในหน่วยความจำในหน่วยความจำที่เน้นเอกสารด้วยอินเทอร์เฟซตัวสร้างแบบสอบถามระดับสูง
เป้าหมายของ Reindexer คือการค้นหาแบบสอบถามที่ซับซ้อนอย่างรวดเร็ว พวกเราที่ Restream ไม่ได้มีความสุขกับ Elasticsearch และสร้าง Reindexer เป็นทางเลือกที่นักแสดงมากขึ้น
แกนกลางเขียนใน C ++ และ API ระดับแอปพลิเคชันอยู่ในระหว่างการเดินทาง
เอกสารนี้อธิบายถึงตัวเชื่อมต่อ Go และ API เพื่อรับข้อมูลเกี่ยวกับ Reindexer Server และ HTTP API โปรดดูเอกสารประกอบ Reindexer
มี LTS-Versions สองตัวของ Reindexer: V3.xx และ v4.xx
3.xx ปัจจุบันเป็นสาขากระแสหลักของเราและ 4.xx (สาขารีลีส/4) เป็นรุ่นเบต้าที่มีการทดลองแพ-คลัสเตอร์และการสนับสนุน Storages นั้นเข้ากันได้ระหว่างรุ่นเหล่านั้นอย่างไรก็ตามการจำลองการจำลองแบบนั้นแตกต่างกันโดยสิ้นเชิง เวอร์ชัน 3 และ 4 ได้รับการแก้ไขข้อผิดพลาดและคุณสมบัติเดียวกันทั้งหมด (ยกเว้นสิ่งที่เกี่ยวข้องกับการจำลองแบบ)
คุณสมบัติที่สำคัญ:
การแสดงเป็นสิ่งสำคัญที่สุดของเราตั้งแต่เริ่มต้นและเราคิดว่าเราจัดการเพื่อให้ได้มันค่อนข้างดี เกณฑ์มาตรฐานแสดงให้เห็นว่าประสิทธิภาพของ Reindexer อยู่ในระดับเดียวกับฐานข้อมูลคีย์ค่าทั่วไป ใน CPU Core เดียวเราได้รับ:
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 ...ดูผลการเปรียบเทียบและรายละเอียดเพิ่มเติมในการเปรียบเทียบการซื้อคืน
Reindexer มีจุดมุ่งหมายที่จะบริโภคหน่วยความจำน้อยที่สุดเท่าที่จะทำได้ คำค้นหาส่วนใหญ่จะถูกประมวลผลโดยไม่มีการจัดสรรหน่วยความจำใด ๆ เลย
เพื่อให้บรรลุเป้าหมายได้มีการใช้การปรับให้เหมาะสมหลายครั้งทั้งในระดับ C ++ และ GO:
เอกสารและดัชนีจะถูกเก็บไว้ในโครงสร้างไบนารี C ++ หนาแน่นดังนั้นพวกเขาจึงไม่ได้กำหนดภาระใด ๆ ในตัวเก็บขยะของ GO
สตริงซ้ำกันถูกรวมเข้าด้วยกัน
ค่าใช้จ่ายหน่วยความจำประมาณ 32 ไบต์ต่อเอกสาร + ≈4-16ไบต์ต่อดัชนีการค้นหาแต่ละรายการ
มีแคชวัตถุในระดับ GO สำหรับเอกสาร deserialized ที่ผลิตหลังจากการดำเนินการแบบสอบถาม แบบสอบถามในอนาคตใช้เอกสารที่กำหนดล่วงหน้าซึ่งลดค่าใช้จ่าย deserialization และการจัดสรรซ้ำซ้ำ
อินเทอร์เฟซแบบสอบถามใช้ sync.Pool สำหรับนำโครงสร้างภายในและบัฟเฟอร์กลับมาใช้ซ้ำ การรวมกันของเทคโนโลยีเหล่านี้ช่วยให้ Reindexer สามารถจัดการการสืบค้นส่วนใหญ่ได้โดยไม่ต้องจัดสรร
Reindexer มีเครื่องมือค้นหาข้อความเต็มรูปแบบภายใน เอกสารและตัวอย่างการใช้ข้อความค้นหาแบบเต็มอยู่ที่นี่
Reindexer สามารถจัดเก็บเอกสารและโหลดเอกสารจากดิสก์ผ่าน LevelDB เอกสารจะถูกเขียนไปยังแบ็กเอนด์ที่เก็บข้อมูลแบบอะซิงโครนัสโดยแบทช์ขนาดใหญ่โดยอัตโนมัติในพื้นหลัง
เมื่อสร้างเนมสเปซเอกสารทั้งหมดจะถูกเก็บไว้ใน RAM ดังนั้นการสืบค้นเอกสารเหล่านี้ทำงานทั้งหมดในโหมดในหน่วยความจำ
นี่คือตัวอย่างที่สมบูรณ์ของการใช้เรนเดกเซอร์พื้นฐาน:
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 )
}
}นอกจากนี้ยังมีตัวอย่างพื้นฐานสำหรับ C ++ และไปที่นี่
เป็นทางเลือกในการสืบค้น Builder Reindexer ให้อินเทอร์เฟซแบบสอบถามที่เข้ากันได้กับ SQL นี่คือตัวอย่างของการใช้อินเตอร์เฟส SQL:
...
iterator := db . ExecSQL ( "SELECT * FROM items WHERE name='Vasya' AND year > 2020 AND articles IN (6,1,8) ORDER BY year LIMIT 10" )
...โปรดทราบว่าอินเทอร์เฟซตัวสร้างแบบสอบถามเป็นที่ต้องการ: มีคุณสมบัติมากขึ้นและเร็วกว่าอินเตอร์เฟส SQL
ตัวอักษรสตริงควรอยู่ในคำพูดเดียว
ดัชนีคอมโพสิตควรอยู่ในคำพูดสองครั้ง
SELECT * FROM items WHERE " field1+field2 " = ' Vasya 'หากชื่อฟิลด์ไม่ได้เริ่มต้นด้วยอัลฟ่า '_' หรือ '#' จะต้องถูกปิดไว้ในคำพูดสองเท่าตัวอย่าง:
UPDATE items DROP " 123 " SELECT * FROM ns WHERE " 123 " = ' some_value ' SELECT * FROM ns WHERE " 123abc " = 123 DELETE FROM ns WHERE " 123abc123 " = 111การเข้าร่วมอย่างง่ายอาจทำได้ผ่านทางไวยากรณ์ SQL เริ่มต้น:
SELECT * FROM ns INNER JOIN ns2 ON ns2 . id = ns . fk_id WHERE a > 0เข้าร่วมกับเงื่อนไขบนเนมสเปซด้านซ้ายจะต้องใช้ไวยากรณ์เหมือนคำถามย่อย:
SELECT * FROM ns WHERE a > 0 AND INNER JOIN ( SELECT * FROM ns2 WHERE b > 10 AND c = 1 ) ON ns2 . id = ns . fk_idSubQuery ยังสามารถเป็นส่วนหนึ่งของเงื่อนไขที่:
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 สามารถทำงานใน 3 โหมดที่แตกต่างกัน:
embedded (builtin) Reindexer ถูกฝังลงในแอปพลิเคชันเป็นไลบรารีแบบคงที่และไม่จำเป็นต้องใช้กระบวนการเซิร์ฟเวอร์แยกต่างหากembedded with server (builtinserver) Reindexer ถูกฝังลงในแอปพลิเคชันเป็นไลบรารีแบบคงที่และเริ่มต้นเซิร์ฟเวอร์ ในโหมดนี้ไคลเอนต์อื่น ๆ สามารถเชื่อมต่อกับแอปพลิเคชันผ่าน CPROTO, UCPROTO หรือ HTTPstandalone Reindexer เรียกใช้เป็นเซิร์ฟเวอร์สแตนด์อโลนแอปพลิเคชันเชื่อมต่อกับ Reindexer ผ่านซ็อกเก็ตเครือข่ายหรือ UNIX โดเมนในโหมดนี้การผูกมัดของ Reindexer ไม่ได้ขึ้นอยู่กับห้องสมุดคงที่ของ Reindexer
วิธีที่ง่ายที่สุดในการรับเซิร์ฟเวอร์ Reindexer คือการดึงและเรียกใช้อิมเมจ Docker จาก DockerHub
docker run -p9088:9088 -p6534:6534 -it reindexer/reindexerDockerfile
แกนกลางของ Reindexer เขียนใน C ++ 17 และใช้ LevelDB เป็นแบ็กเอนด์ที่เก็บข้อมูลดังนั้น CMake, C ++ 17 Toolchain และ LevelDB ต้องติดตั้งก่อนที่จะติดตั้ง Reindexer
ในการสร้าง Reindexer, G ++ 8+, Clang 7+ หรือ MingW64 เป็นสิ่งจำเป็น
ในโหมดเหล่านั้นที่มีผลผูกพันกับ Reindexer ขึ้นอยู่กับไลบรารีคงที่ของ Reindexer (Core, Server และ Resource)
วิธีนี้แนะนำและจะเหมาะกับสถานการณ์มากที่สุด
โมดูล GO ด้วย go.mod ไม่อนุญาตให้สร้างไลบรารี C ++ ในไดเรกทอรีของโมดูล Go-binding จะใช้ PKG-Config เพื่อตรวจจับไดเรกทอรีของห้องสมุด
ไลบรารีของ Reindexer จะต้องติดตั้งจากแหล่งที่มาหรือจากแพ็คเกจ prebuilt ผ่าน Package Manager
จากนั้นรับโมดูล:
go get -a github.com/restream/reindexer/v3 หากคุณต้องการแหล่งที่มาของ Reindexer ที่ได้รับการแก้ไขคุณสามารถใช้ replace เช่นนั้นได้
# 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/reindexerในกรณีนี้การผูกมัด GO จะสร้างรายการไลบรารีและเส้นทาง 'ที่ชัดเจนและจะไม่ใช้ PKG-Config
หากคุณไม่ได้ใช้ go.mod เป็นไปได้ที่จะได้รับและสร้าง Reindexer จากแหล่งที่มาด้วยวิธีนี้:
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 ไม่รองรับการขายที่เหมาะสมสำหรับรหัส CGO (Golang/Go#26366) อย่างไรก็ตามเป็นไปได้ที่จะใช้ Vend เพื่อคัดลอกแหล่งข้อมูลของ Reindexer ลงในไดเรกทอรีของผู้ขาย
ด้วย vend คุณจะสามารถโทรไป go generate -mod=vendor สำหรับ builtin และ builtinserver ซึ่งวางไว้ในไดเรกทอรีผู้ขายของคุณ
นอกจากนี้ยังเป็นไปได้ที่จะคัดลอกเพียงคัดลอกแหล่งข้อมูลของ Reindexer ลงในโครงการเยาวชนโดยใช้ git clone
ในกรณีเหล่านี้การพึ่งพาทั้งหมดจาก GO ของ Reindexer ต้องติดตั้งด้วยตนเองด้วยเวอร์ชันที่เหมาะสม
ภายในโครงสร้างแบ่งออกเป็นสองส่วน:
reindex แบบสอบถามเป็นไปได้เฉพาะในฟิลด์ที่จัดทำดัชนีซึ่งทำเครื่องหมายด้วยแท็ก reindex แท็ก reindex มีชื่อดัชนีประเภทและตัวเลือกเพิ่มเติม:
reindex:"<name>[[,<type>],<opts>]"
name - ชื่อดัชนีtype - ประเภทดัชนี:hash - เลือกอย่างรวดเร็วโดย EQ และตั้งค่าการจับคู่ ใช้โดยค่าเริ่มต้น ช่วยให้การเรียงลำดับ ช้า และไม่มีประสิทธิภาพตามสนามtree - SELECT อย่างรวดเร็วตาม Range, GT และ LT Matches ช้าลงเล็กน้อยสำหรับ EQ และตั้งค่าการจับคู่มากกว่าดัชนี hash อนุญาตให้การเรียงลำดับอย่างรวดเร็วตามฟิลด์text - ดัชนีการค้นหาข้อความแบบเต็ม รายละเอียดการใช้งานการค้นหาข้อความแบบเต็มอธิบายไว้ที่นี่- - ดัชนีคอลัมน์ ไม่สามารถเลือกได้อย่างรวดเร็วเนื่องจากใช้งานด้วยเทคนิคการสแกนเต็มรูปแบบ มีหน่วยความจำที่เล็กที่สุดttl - TTL ที่ใช้งานได้กับฟิลด์ Int64 เท่านั้น ดัชนีเหล่านี้ค่อนข้างสะดวกสำหรับการเป็นตัวแทนของฟิลด์วันที่ (จัดเก็บเป็นเวลา unix timestamps) ที่จะหมดอายุหลังจากจำนวนวินาทีที่ระบุrtree - มีเฉพาะการแข่งขัน Dwithin เท่านั้น ยอมรับได้เฉพาะสำหรับประเภทฟิลด์ [2]float64 (หรือ reindexer.Point ) สำหรับรายละเอียดโปรดดูที่ส่วนย่อยเรขาคณิตopts - ตัวเลือกดัชนีเพิ่มเติม:pk - ฟิลด์เป็นส่วนหนึ่งของคีย์หลัก โครงสร้างต้องมีฟิลด์อย่างน้อย 1 แท็กด้วย pkcomposite - สร้างดัชนีคอมโพสิต ประเภทของฟิลด์จะต้องเป็น struct ที่ว่างเปล่า: struct{}joined - Field เป็นผู้รับการเข้าร่วม ประเภทของฟิลด์จะต้องเป็น []*SubitemTypedense - ลดขนาดดัชนี สำหรับ hash และ tree มันจะประหยัด 8 ไบต์ต่อค่าคีย์ที่ไม่ซ้ำกัน สำหรับ - มันจะประหยัด 4-8 ไบต์ต่อแต่ละองค์ประกอบ มีประโยชน์สำหรับดัชนีที่มีการเลือกสูง แต่สำหรับดัชนี tree และ hash ที่มีการเลือกต่ำสามารถลดประสิทธิภาพการอัปเดตได้อย่างจริงจัง นอกจากนี้ dense จะชะลอการสืบค้นเต็มรูปแบบที่กว้างบน - ดัชนีเนื่องจากขาดการเพิ่มประสิทธิภาพแคช CPUsparse - Row (เอกสาร) มีค่าของดัชนีกระจัดกระจายเฉพาะในกรณีที่มีการตั้งค่าตามวัตถุประสงค์ - ไม่มีระเบียนที่ว่างเปล่า (หรือเริ่มต้น) ของดัชนีประเภทนี้ในแถว (เอกสาร) ช่วยให้ประหยัด RAM ได้ แต่มันจะทำให้คุณมีประสิทธิภาพ - มันทำงานได้ช้ากว่าดัชนีปกติเล็กน้อยcollate_numeric - สร้างดัชนีสตริงที่ให้ลำดับค่าในลำดับตัวเลข ประเภทฟิลด์ต้องเป็นสตริงcollate_ascii - สร้างดัชนีสตริงที่ไม่ได้รับการตอบสนองต่อตัวพิมพ์ใหญ่กับ ASCII ประเภทฟิลด์ต้องเป็นสตริงcollate_utf8 - สร้างดัชนีสตริงที่ไม่ได้รับการตอบสนองการทำงานกับ UTF8 ประเภทฟิลด์ต้องเป็นสตริงcollate_custom=<ORDER> - สร้างดัชนีสตริงคำสั่งซื้อที่กำหนดเอง ประเภทฟิลด์ต้องเป็นสตริง <ORDER> คือลำดับของตัวอักษรซึ่งกำหนดลำดับการเรียงลำดับlinear , quadratic , greene หรือ rstar - ระบุอัลกอริทึมสำหรับการก่อสร้างดัชนี rtree (โดยค่าเริ่มต้น rstar ) สำหรับรายละเอียดโปรดดูที่ส่วนย่อยเรขาคณิตuuid - เก็บค่านี้เป็น UUID สิ่งนี้มีประสิทธิภาพมากขึ้นจากมุมมองการบรรลุผลของ RAM/เครือข่ายสำหรับ UUIDS มากกว่าสตริง รองรับเฉพาะ hash และ - ดัชนีเท่านั้นที่รองรับ UUID สามารถใช้กับตัวแปร uuid ใด ๆ ยกเว้นตัวแปร 0 ฟิลด์ที่มีดัชนีปกติจะไม่เป็นโมฆะ เงื่อนไข is NULL ได้รับการสนับสนุนโดยดัชนี sparse และ array เท่านั้น
โดยค่าเริ่มต้น Reindexer จะสแกนโครงสร้างที่ซ้อนกันทั้งหมดและเพิ่มฟิลด์ของพวกเขาลงในเนมสเปซ (รวมถึงดัชนีที่ระบุ) ในระหว่างดัชนีสแกนส่วนตัว (ฟิลด์ที่ไม่ได้ตั้งใจ) ฟิลด์ที่ติดแท็กด้วย reindex:"-" และฟิลด์ที่ติดแท็กด้วย json:"-" จะถูกข้ามไป
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 สามารถเรียงลำดับเอกสารตามฟิลด์ (รวมถึงซ้อนและฟิลด์ของเนมสเปซที่เข้าร่วม) หรือโดยนิพจน์ในลำดับจากน้อยไปหามากขึ้นหรือลดลง
ในการเรียงลำดับโดยฟิลด์ที่ไม่ใช่ดัชนีค่าทั้งหมดจะต้องแปลงสภาพซึ่งกันและกันเช่นมีประเภทเดียวกันหรือเป็นหนึ่งในประเภทตัวเลข ( bool , int , int64 หรือ float )
การจัดเรียงนิพจน์สามารถมีได้:
bool , int , int64 , ประเภท float หรือ string ค่าทั้งหมดจะต้องแปลงเป็นตัวเลขโดยไม่สนใจพื้นที่นำและการตกแต่งrank() , abs() และ ST_Distance() ;+ , - (Unary and Binary), * และ / หากชื่อฟิลด์ตามด้วย + พวกเขาจะต้องคั่นด้วยพื้นที่เพื่อแยกชื่อดัชนีคอมโพสิต ฟิลด์ของเนมสเปซที่เข้าร่วมจะต้องเขียนเช่นนี้: joined_namespace.field _namespace.field
Abs() หมายถึงค่าสัมบูรณ์ของการโต้แย้ง
Rank() หมายถึง FullText Rank of Match และใช้ได้เฉพาะในแบบสอบถาม FullText เท่านั้น
ST_Distance() หมายถึงระยะห่างระหว่างจุดเรขาคณิต (ดูส่วนย่อยเรขาคณิต) คะแนนอาจเป็นคอลัมน์ในเนมสเปซปัจจุบันหรือเข้าร่วมหรือจุดคงที่ในรูปแบบ ST_GeomFromText('point(1 -3)')
ในนิพจน์การเรียงลำดับ SQL ต้องได้รับการยกมา
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" )นอกจากนี้ยังเป็นไปได้ที่จะตั้งค่าการเรียงลำดับแบบกำหนดเองเช่นนี้
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=a-zA-Z0-9"`
}หรือเช่นนี้
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=АаБбВвГгДдЕеЖжЗзИиКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭ-ЯAaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0-9ЁёЙйэ-я"`
}ตัวละครตัวแรกในรายการนี้มีลำดับความสำคัญสูงสุดลำดับความสำคัญของตัวละครตัวสุดท้ายคือตัวละครที่เล็กที่สุด หมายความว่าอัลกอริทึมการเรียงลำดับจะทำให้รายการที่เริ่มต้นด้วยตัวละครตัวแรกก่อนอื่น หากตัวละครบางตัวถูกข้ามลำดับความสำคัญของพวกเขาจะมีค่าปกติของพวกเขา (ตามตัวละครในรายการ)
สำหรับรูปแบบข้อความการค้นหาอย่างง่ายในเงื่อนไขของฟิลด์สตริง LIKE สามารถใช้งานได้ มันมองหาสตริงที่ตรงกับรูปแบบ ในรูปแบบ _ หมายถึงถ่านและ % ใด ๆ หมายถึงลำดับของตัวอักษรใด ๆ
ตัวอย่าง:
query := db . Query ( "items" ).
Where ( "field" , reindexer . LIKE , "pattern" )ตัวอย่าง SQL:
SELECT * FROM items WHERE fields LIKE ' pattern ''me_t' สอดคล้องกับ 'พบ', 'เนื้อ', 'ละลาย' และต่อไป '%tion' สอดคล้องกับ 'tion', 'เงื่อนไข', 'การสร้าง' และอื่น ๆ
ข้อควรระวัง : เงื่อนไขเช่นใช้วิธีการสแกน สามารถใช้เพื่อจุดประสงค์ในการดีบักหรือภายในแบบสอบถามที่มีเงื่อนไขการเลือกที่ดีอื่น
โดยทั่วไปสำหรับการค้นหาข้อความแบบเต็มด้วยความเร็วที่สมเหตุสมผลเราแนะนำให้ใช้ดัชนี FullText
คิวรีอัปเดตใช้เพื่อแก้ไขรายการที่มีอยู่ของเนมสเปซ มีการสืบค้นการอัปเดตหลายประเภท: การอัปเดตฟิลด์ที่มีอยู่เพิ่มฟิลด์ใหม่และวางฟิลด์ที่ไม่มีการจัดทำดัชนีที่มีอยู่
อัปเดต sql-syntax
UPDATE nsName
SET field1 = value1, field2 = value2, ..
WHERE condition;นอกจากนี้ยังเป็นไปได้ที่จะใช้นิพจน์เลขคณิตด้วย +, -, /, * และวงเล็บ
UPDATE NS SET field1 = field2 + field3 - (field4 + 5 ) / 2 รวมถึงฟังก์ชั่นเช่น now() , sec() และ serial() หากต้องการใช้นิพจน์จากวิธีการ Golang Code SetExpression() ต้องเรียกใช้แทน Set()
เพื่อให้อาร์เรย์ฟิลด์ว่างเปล่า
UPDATE NS SET arrayfield = [] WHERE id = 100และตั้งค่าเป็นโมฆะ
UPDATE NS SET field = NULL WHERE id > 100ในกรณีของฟิลด์ที่ไม่ได้จัดทำดัชนีการตั้งค่าเป็นค่าของประเภทอื่นจะแทนที่อย่างสมบูรณ์ ในกรณีของฟิลด์ที่จัดทำดัชนีมันเป็นไปได้ที่จะแปลงจากประเภทที่อยู่ติดกัน (ประเภทอินทิกรัลและบูล) สตริงตัวเลข (เช่น "123456") เป็นประเภทอินทิกรัลและด้านหลัง การตั้งค่าฟิลด์ที่จัดทำดัชนีเป็น NULL รีเซ็ตเป็นค่าเริ่มต้น
เป็นไปได้ที่จะเพิ่มฟิลด์ใหม่ในรายการที่มีอยู่
UPDATE NS SET newField = ' Brand new! ' WHERE id > 100และแม้แต่เพิ่มฟิลด์ใหม่ด้วยเส้นทางซ้อนที่ซับซ้อนเช่นนี้
UPDATE NS SET nested . nested2 . nested3 . nested4 .newField = ' new nested field! ' WHERE id > 100จะสร้างวัตถุซ้อนต่อไปนี้: ซ้อนกันซ้อน 2, ซ้อนกัน 3, ซ้อนกัน 4 และนิวฟิลด์ในฐานะสมาชิกของวัตถุซ้อนกัน 4
ตัวอย่างการใช้การสืบค้นอัปเดตในรหัส Golang:
db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Set ( "field1" , values ). Update ()Reindexer ช่วยให้สามารถอัปเดตและเพิ่มฟิลด์วัตถุ วัตถุสามารถตั้งค่าได้โดย struct, แผนที่หรืออาร์เรย์ไบต์ (นั่นคือเวอร์ชัน JSON ของการแสดงวัตถุ)
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 () ในกรณีนี้ Map ใน Golang สามารถทำงานกับสตริงเป็นคีย์ได้เท่านั้น map[string]interface{} เป็นตัวเลือกที่สมบูรณ์แบบ
การอัปเดตฟิลด์วัตถุโดยคำสั่ง 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 ;อัปเดต SQL-Syntax ของแบบสอบถามที่วางฟิลด์ที่ไม่มีดัชนีที่มีอยู่:
UPDATE nsName
DROP field1, field2, ..
WHERE condition; db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Drop ( "field1" ). Update ()กลไกการอัปเดตของ Reindexer ช่วยให้สามารถแก้ไขฟิลด์อาร์เรย์ได้: เพื่อแก้ไขรายการบางรายการของอาร์เรย์ที่มีอยู่หรือแม้แต่แทนที่ฟิลด์ทั้งหมด
หากต้องการอัปเดตไวยากรณ์ตัวดำเนินการสมัครสมาชิกรายการ: ใช้:
UPDATE NS SET array[ * ].prices[ 0 ] = 9999 WHERE id = 5 โดยที่ * หมายถึงรายการทั้งหมด
หากต้องการอัปเดตอาร์เรย์ทั้งหมดต่อไปนี้ใช้:
UPDATE NS SET prices = [ 999 , 1999 , 2999 ] WHERE id = 9ฟิลด์ที่ไม่ได้จัดทำดัชนีใด ๆ สามารถแปลงเป็นอาร์เรย์ได้อย่างง่ายดายโดยใช้ไวยากรณ์นี้
Reindexer ยังอนุญาตให้อัปเดตรายการอาร์เรย์วัตถุ:
UPDATE NS SET extra . objects [ 0 ] = { " Id " : 0 , " Description " : " Updated! " } WHERE id = 9ยังเช่นนี้
db . Query ( "clients" ). Where ( "id" , reindexer . EQ , 100 ). SetObject ( "extra.objects[0]" , updatedValue ). Update ()Reindexer รองรับอาร์เรย์ที่แตกต่างกัน:
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 ()ค่าดัชนีอาร์เรย์ฟิลด์ฟิลด์ที่สามารถแปลงเป็นประเภทดัชนีเท่านั้น เมื่อบันทึกค่าดังกล่าวอาจเปลี่ยนความแม่นยำเนื่องจากการแปลง
UPDATE NS SET prices_idx = [ 11 , ' 2 ' , 3 ]หากต้องการลบรายการตามดัชนีคุณควรทำสิ่งต่อไปนี้:
UPDATE NS DROP array[ 5 ]หากต้องการเพิ่มรายการลงในอาร์เรย์ที่มีอยู่แล้วไวยากรณ์ต่อไปนี้ได้รับการสนับสนุน:
UPDATE NS SET integer_array = integer_array || [ 5 , 6 , 7 , 8 ]และ
UPDATE NS SET integer_array = [ 1 , 2 , 3 , 4 , 5 ] || integer_array อันแรกเพิ่มองค์ประกอบไปยังจุดสิ้นสุดของ integer_array อันที่สองเพิ่ม 5 รายการที่ด้านหน้าของมัน เพื่อให้รหัสนี้ใช้งานได้ใน Golang SetExpression() ควรใช้แทน Set()
หากต้องการลบรายการโดยค่าลงในอาร์เรย์ที่มีอยู่รองรับไวยากรณ์ต่อไปนี้:
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 , 6 , 7 , 8 ])และ
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 , 6 ]) ครั้งแรกจะลบเหตุการณ์ทั้งหมดที่ระบุไว้ใน integer_array หนึ่งครั้งที่สองจะลบเฉพาะเหตุการณ์ที่เกิดขึ้นครั้งแรกเท่านั้น เพื่อให้รหัสนี้ใช้งานได้ใน Golang SetExpression() ควรใช้แทน Set() หากคุณต้องการลบค่าเดียวคุณสามารถใช้วงเล็บสี่เหลี่ยม [5] หรือค่าง่าย 5
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 ]) update ns set integer_array = array_remove(integer_array, 5 )ลบคำสั่งสามารถรวมกับอาร์เรย์ concatenate:
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 ]) || [ 1 , 2 , 3 ]ยังเช่นนี้
db . Query ( "main_ns" ). SetExpression ( "integer_array" , "array_remove(integer_array, [5,6,7,8]) || [1,2,3]" ). Update ()เป็นไปได้ที่จะลบค่าของฟิลด์ที่สองออกจากค่าของฟิลด์แรก และเพิ่มค่าใหม่ ฯลฯ หมายเหตุ: พารามิเตอร์แรกในคำสั่งคาดว่าจะเป็นอาร์เรย์/ฟิลด์อาเรย์พารามิเตอร์ที่สองสามารถเป็นอาร์เรย์/สเกลาร์/ฟิลด์อาเรย์/ฟิลด์สคาลาร์ สำหรับค่าความเข้ากันได้/ความสามารถในการแปลง
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 รองรับการทำธุรกรรม การทำธุรกรรมจะดำเนินการอัปเดต namespace อะตอม มีการทำธุรกรรมแบบซิงโครนัสและ async ในการเริ่มต้นวิธีการทำธุรกรรม db.BeginTx() ใช้ วิธีนี้สร้างวัตถุธุรกรรมซึ่งให้การอัปเดต/upsert/แทรก/ลบตามปกติสำหรับแอปพลิเคชัน สำหรับไคลเอนต์ RPC มีข้อ จำกัด การนับธุรกรรม - การเชื่อมต่อแต่ละครั้งจะไม่มีการทำธุรกรรมเปิดมากกว่า 1024 รายการในเวลาเดียวกัน
// 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 )
}สำหรับการแทรกความเร็วในการแทรกโหมด Async จำนวนมากสามารถใช้งานได้
// 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 )
} อาร์กิวเมนต์ที่สองของ UpsertAsync คือฟังก์ชั่นการเสร็จสิ้นซึ่งจะถูกเรียกหลังจากได้รับการตอบกลับเซิร์ฟเวอร์ นอกจากนี้หากเกิดข้อผิดพลาดใด ๆ ในระหว่างการเตรียมกระบวนการ tx.Commit ควรส่งคืนข้อผิดพลาด ดังนั้นจึงเพียงพอที่จะตรวจสอบข้อผิดพลาดที่ส่งคืนโดย tx.Commit - เพื่อให้แน่ใจว่าข้อมูลทั้งหมดได้รับการกระทำสำเร็จหรือไม่
ขึ้นอยู่กับจำนวนการเปลี่ยนแปลงในการทำธุรกรรมมี 2 กลยุทธ์การกระทำที่เป็นไปได้:
จำนวนข้อมูลสำหรับการเลือกกลยุทธ์การกระทำสามารถเลือกได้ในการกำหนดค่าเนมสเปซ ตรวจสอบเขตข้อมูล StartCopyPolicyTxSize , CopyPolicyMultiplier และ TxSizeToAlwaysCopy ใน struct DBNamespacesConfig (describer.go)
หากขนาดการทำธุรกรรมน้อยกว่า TxSizeToAlwaysCopy Reindexer ใช้ฮิวริสติกพิเศษและพยายามหลีกเลี่ยงการคัดลอกเนมสเปซหากไม่มีการเลือกแบบสอบถามที่เห็นสำหรับเนมสเปซนี้ ในบางกรณีฮิวริสติกนี้อาจเพิ่มความล่าช้าในการเลือกดังนั้นจึงอาจปิดการใช้งานได้โดยการตั้งค่าตัวแปร en REINDEXER_NOTXHEURISTIC env เป็นค่าที่ไม่ว่างเปล่า
tx.Query("ns").Exec() ... ;Reindexer สามารถเข้าร่วมเอกสารจากเนมสเปซหลายรายการเป็นผลลัพธ์เดียว:
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 ()ในตัวอย่างนี้ Reindexer ใช้การสะท้อนกลับภายใต้ประทุนเพื่อสร้างชิ้นนักแสดงและคัดลอกโครงสร้างนักแสดง
เข้าร่วมการสอบถามอาจมีจากหนึ่งถึงหลาย On ที่เชื่อมต่อกับ And (โดยค่าเริ่มต้น) Or หรือ 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 รวมข้อมูลจากสองเนมสเปซที่มีการจับคู่ในฟิลด์การเข้าร่วมในทั้งสองเนมสเปซ LeftJoin ส่งคืนรายการที่ถูกต้องทั้งหมดจากเนมสเปซทางด้านซ้ายของคำหลัก LeftJoin พร้อมกับค่าจากตารางทางด้านขวาหรือไม่มีอะไรถ้าไม่มีรายการที่ตรงกัน Join เป็นนามแฝงสำหรับ LeftJoin
InnerJoins สามารถใช้เป็นเงื่อนไขใน Where ประโยค:
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 ) โปรดทราบว่าโดยปกติ Or ผู้ปฏิบัติงานจะใช้การไหลเวียนของการไหลเวียนอย่างสั้น ๆ สำหรับ Where : หากเงื่อนไขก่อนหน้านี้เป็นจริงจะไม่ได้รับการประเมินเงื่อนไขถัดไป แต่ในกรณีของ InnerJoin มันทำงานแตกต่างกัน: ใน query1 (จากตัวอย่างด้านบน) ทั้งสองเงื่อนไข InnerJoin จะได้รับการประเมินแม้จะมีผลของ WhereInt Limit(0) เป็นส่วนหนึ่งของ InnerJoin ( query3 จากตัวอย่างด้านบน) ไม่ได้เข้าร่วมข้อมูลใด ๆ - มันทำงานเหมือนตัวกรองเท่านั้นเพื่อตรวจสอบเงื่อนไข
Reindexer ไม่สนับสนุน ANTI JOIN การก่อสร้าง SQL แต่สนับสนุนการดำเนินงานเชิงตรรกะด้วยการเข้าร่วม ในความเป็นจริง NOT (INNER JOIN ...) เทียบเท่ากับ 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
) เพื่อหลีกเลี่ยงการใช้การสะท้อนกลับ Item สามารถใช้อินเทอร์เฟซ Joinable หากดำเนินการดังกล่าว Reindexer จะใช้สิ่งนี้แทนการใช้งานแบบสะท้อนแสงที่ช้า สิ่งนี้จะเพิ่มประสิทธิภาพโดยรวม 10-20%และลดปริมาณการจัดสรร
// 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 ))
}
}
}เงื่อนไขสามารถนำไปใช้กับผลลัพธ์ของการสืบค้นอื่น (subquery) รวมอยู่ในแบบสอบถามปัจจุบัน เงื่อนไขอาจอยู่ในแถวที่เกิดขึ้นของคำถามย่อย:
query := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ), reindexer . GE , 100 )หรือระหว่างฟิลด์ของเนมสเปซของคิวรีหลักและผลลัพธ์ของคำถามย่อย:
query := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 )) ผลลัพธ์ของคำถามย่อยอาจเป็นฟิลด์บางอย่างที่ชี้ด้วยวิธี Select (ในกรณีนี้จะต้องตั้งค่าตัวกรองฟิลด์เดียว):
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 )) หรือนับรายการที่พึงพอใจกับคำถามย่อยที่ต้องการโดยวิธี ReqTotal หรือ 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 ())หรือการรวม:
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 และการรวมตัวของ CountCached ได้รับอนุญาตเท่านั้น SubQuery ไม่สามารถมีการรวมหลายอย่างในเวลาเดียวกัน
SubQuery สามารถนำไปใช้กับเนมสเปซเดียวกันหรืออีกอันหนึ่ง
SubQuery ไม่สามารถมีคำถามย่อยอื่นเข้าร่วมหรือรวม
หากคุณต้องการตรวจสอบว่าอย่างน้อยหนึ่งรายการนั้นเป็นที่น่าพอใจกับการคาดการณ์ย่อยคุณสามารถใช้เงื่อนไข ANY หรือ 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 )เอกสารสามารถมีหลายฟิลด์เป็นคีย์หลัก เพื่อเปิดใช้งานคุณสมบัตินี้เพิ่มดัชนีคอมโพสิตลงในโครงสร้าง ดัชนีคอมโพสิตเป็นดัชนีที่เกี่ยวข้องกับหลายฟิลด์สามารถใช้แทนดัชนีแยกต่างหากหลายรายการ
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"`
}หรือ
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"`
}ดัชนีคอมโพสิตยังมีประโยชน์สำหรับการเรียงลำดับผลลัพธ์โดยหลายฟิลด์:
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 }) สำหรับการสอบถามไปยังดัชนีคอมโพสิตส่งผ่าน [] อินเตอร์เฟส {} ถึง .WhereComposite ฟังก์ชั่นที่ใช้ในการสืบค้นของตัวสร้างแบบสอบถาม:
// Get results where rating == 5 and year == 2010
query := db . Query ( "items" ). WhereComposite ( "rating+year" , reindexer . EQ ,[] interface {}{ 5 , 2010 }) ฟิลด์ทั้งหมดในดัชนีคอมโพสิตปกติ (ไม่ใช่แบบเต็มเท็กซ์) จะต้องได้รับการจัดทำดัชนี เช่นเพื่อให้สามารถสร้าง rating+year จำเป็นต้องสร้างดัชนีบางอย่างสำหรับ raiting และ year แรก:
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 อนุญาตให้ดึงผลลัพธ์ที่รวมเข้าด้วยกัน ปัจจุบันนับ, นับ, countcached, เฉลี่ย, ผลรวม, ขั้นต่ำ, สูงสุด, facet และการรวมที่แตกต่างกันได้รับการสนับสนุน
Count - รับจำนวนเอกสารทั้งหมดที่ตรงตามเงื่อนไขของ QuerieCountCached - รับจำนวนเอกสารทั้งหมดที่ตรงตามเงื่อนไขของ Querie ค่าผลลัพธ์จะถูกแคชและอาจนำกลับมาใช้ใหม่โดยการสืบค้นอื่น ๆ ที่มีการรวม countcachedAggregateMax - รับค่าสูงสุดของฟิลด์AggregateMin - รับค่าต่ำสุดAggregateSum - รับค่าฟิลด์ผลรวมAggregateAvg - รับค่าฟิลด์เฉลี่ยAggregateFacet - รับค่า facets ฟิลด์Distinct - รับรายการค่าที่ไม่ซ้ำกันของฟิลด์ เพื่อสนับสนุนการรวมตัว Query มีวิธีการ AggregateAvg , AggregateSum , AggregateMin , AggregateMax , AggregateFacet และ Distinct ที่ควรเรียกก่อนการดำเนินการ Query : สิ่งนี้จะขอให้ Reindexer คำนวณการรวมข้อมูล facet การรวมตัวใช้กับคอลัมน์ข้อมูลหลายคอลัมน์และผลลัพธ์ที่สามารถเรียงลำดับได้ตามคอลัมน์ข้อมูลใด ๆ หรือ 'นับ' และตัดออกโดยออฟเซ็ตและ จำกัด เพื่อรองรับวิธีการใช้งานวิธีการนี้รวมผลตอบแทน AggregationFacetRequest AggregateFacet ซึ่งมีวิธี Sort Limit และ Offset
การสืบค้นกับการผสานจะใช้การรวมตัวจากแบบสอบถามหลักกับการคาดการณ์ย่อยทั้งหมด Subqueries ไม่สามารถมีการรวมตัวของตัวเองได้ การรวมตัวที่มีอยู่สำหรับการผสานการตรวจสอบคือ: นับ, นับ cached, sum, min และ max
เพื่อให้ได้ผลลัพธ์การรวมตัว Iterator มีวิธี AggResults : มันสามารถใช้ได้หลังจากการดำเนินการแบบสอบถามและส่งคืนผลลัพธ์
ตัวอย่างรหัสสำหรับ items รวมตาม price และ 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 )
} การเรียงลำดับโดยฟิลด์ของ FACET ที่รวมกันมีไวยากรณ์ที่แตกต่างกันในเวอร์ชัน SQL:
SELECT FACET(name, price ORDER BY " name " ASC , " count " DESC ) FROM itemsReindexer อนุญาตให้ค้นหาข้อมูลในฟิลด์อาร์เรย์เมื่อค่าการจับคู่มีตำแหน่งดัชนีเดียวกัน ตัวอย่างเช่นเรามีโครงสร้างอาร์เรย์:
type Elem struct {
F1 int `reindex:"f1"`
F2 int `reindex:"f2"`
}
type A struct {
Elems [] Elem
}ความพยายามทั่วไปในการค้นหาค่าในอาร์เรย์นี้
db . Query ( "Namespace" ). Where ( "f1" , EQ , 1 ). Where ( "f2" , EQ , 2 ) ค้นหารายการทั้งหมดของอาร์เรย์ Elem[] โดยที่ f1 เท่ากับ 1 และ f2 เท่ากับ 2
ฟังก์ชั่น EqualPosition ช่วยให้สามารถค้นหาในฟิลด์อาร์เรย์ที่มีดัชนีเท่ากัน แบบสอบถามเช่นนี้:
db . Query ( "Namespace" ). Where ( "f1" , reindexer . GE , 5 ). Where ( "f2" , reindexer . EQ , 100 ). EqualPosition ( "f1" , "f2" )หรือ
SELECT * FROM Namespace WHERE f1 >= 5 AND f2 = 100 EQUAL_POSITION(f1,f2); จะพบรายการทั้งหมดของอาร์เรย์ Elem[] ที่มีดัชนีอาร์เรย์ equal ซึ่ง f1 สูงกว่าหรือเท่ากับ 5 และ f2 เท่ากับ 100 (ตัวอย่างเช่นการสืบค้นส่งคืน 5 รายการที่องค์ประกอบที่ 3 ของอาร์เรย์ทั้งสองมีค่าที่เหมาะสม)
ด้วยนิพจน์ที่ซับซ้อน (นิพจน์ที่มีวงเล็บ) equal_position () อาจอยู่ในวงเล็บ:
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 ไม่ทำงานกับเงื่อนไขต่อไปนี้: เป็นโมฆะว่างเปล่าและใน (พร้อมรายการพารามิเตอร์ว่าง)
มีฟังก์ชั่นอะตอมซึ่งดำเนินการภายใต้การล็อคเนมสเปซและดังนั้นจึงรับประกันความสอดคล้องของข้อมูล:
ฟังก์ชั่นเหล่านี้สามารถส่งผ่านไปยัง Upsert/Insert/Update ใน 3-RD และอาร์กิวเมนต์ถัดไป
หากมีการจัดเตรียมฟังก์ชั่นเหล่านี้แล้วรายการอ้างอิงจะถูกเปลี่ยนเป็นค่าที่อัปเดต
// 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()" )การหมดอายุของข้อมูลมีประโยชน์สำหรับข้อมูลบางชั้นรวมถึงข้อมูลเหตุการณ์ที่สร้างขึ้นของเครื่องบันทึกและข้อมูลเซสชันที่ต้องคงอยู่ในระยะเวลาที่ จำกัด เท่านั้น
Reindexer ทำให้เป็นไปได้ที่จะตั้งค่า TTL (Time to Live) สำหรับรายการเนมสเปซ การเพิ่ม TTLINDEX ไปยังเนมสเปซจะลบรายการโดยอัตโนมัติหลังจากจำนวนวินาทีที่ระบุ
ดัชนี TTL ทำงานเฉพาะกับฟิลด์ INT64 และจัดเก็บข้อมูลการประทับเวลา UNIX รายการที่มีดัชนี TTL หมดอายุหลังจาก expire_after วินาที ตัวอย่างของการประกาศ ttlindex ใน 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 ()ในกรณีนี้รายการของ namespaceexample จะหมดอายุใน 3600 วินาทีหลังจาก namespaceExample.date ค่าฟิลด์ (ซึ่งเป็นเวลา UNIX Timestamp)
ดัชนี TTL รองรับการสืบค้นในลักษณะเดียวกับดัชนีที่ไม่ใช่ TTL
หากข้อมูลต้นฉบับมีอยู่ในรูปแบบ JSON ก็เป็นไปได้ที่จะปรับปรุงประสิทธิภาพของการดำเนินการ UPSERT/DELETE โดยผ่าน JSON โดยตรงไปยัง Reindexer JSON deserialization จะทำโดยรหัส C ++ โดยไม่ต้องจัดสรรเพิ่มเติม/deserialization ในรหัส GO
ฟังก์ชั่น upsert หรือลบสามารถประมวลผล JSON ได้โดยผ่าน [] BYTE อาร์กิวเมนต์กับ JSON
json := [] byte ( `{"id":1,"name":"test"}` )
db . Upsert ( "items" , json )มันเทียบเท่ากันเร็วกว่า:
item := & Item {}
json . Unmarshal ([] byte ( `{"id":1,"name":"test"}` ), item )
db . Upsert ( "items" , item )ในกรณีที่มีความต้องการที่จะทำให้ผลลัพธ์ของการสืบค้นในรูปแบบ JSON นั้นเป็นไปได้ที่จะปรับปรุงประสิทธิภาพโดยการได้รับผลลัพธ์โดยตรงในรูปแบบ JSON จาก Reindexer การทำให้เป็นอนุกรม JSON จะทำโดยรหัส C ++ โดยไม่ต้องจัดสรร/อนุกรมเพิ่มเติมในรหัส 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 ))
...รหัสนี้จะพิมพ์บางอย่างเช่น:
{ "root_object" : [{ "id" : 1 , "name" : " test " }] } เพื่อหลีกเลี่ยงเงื่อนไขการแข่งขันโดยแคชวัตถุเริ่มต้นจะถูกปิดและวัตถุทั้งหมดจะถูกจัดสรรและ deserialized จากรูปแบบภายในของ Reindexer (เรียกว่า CJSON ) ต่อการสืบค้นแต่ละแบบ Deserialization ใช้การสะท้อนดังนั้นความเร็วของมันจึงไม่เหมาะสม (ในความเป็นจริง CJSON Deserialization นั้นเร็วกว่า JSON ~ 3-10X และเร็วกว่า GOB ~ 1.2x) แต่ประสิทธิภาพยังคงถูก จำกัด อย่างจริงจัง
มี 2 วิธีในการเปิดใช้งานแคชวัตถุ:
หากวัตถุใช้อินเทอร์เฟซ DeepCopy แล้ว Reindexer จะเปิดแคชวัตถุและใช้อินเทอร์เฟซ DeepCopy เพื่อคัดลอกวัตถุจากแคชไปยังผลลัพธ์การสืบค้น อินเทอร์เฟซ DeepCopy มีหน้าที่ทำสำเนาวัตถุต้นฉบับ
นี่คือตัวอย่างของการใช้งานอินเทอร์เฟซ 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
} เพื่อเพิ่มความเร็วในการสืบค้นและไม่จัดสรรวัตถุใหม่ต่อการสืบค้นแต่ละครั้งเป็นไปได้ที่จะถามคำถามที่คิวรีส่งคืนโดยตรงจากแคชวัตถุ สำหรับการเปิดใช้งานพฤติกรรมนี้ให้โทรไป AllowUnsafe(true) บน Iterator
คำเตือน: เมื่อใช้ AllowUnsafe(true) การสืบค้นส่งคืนพอยน์เตอร์ที่ใช้ร่วมกันไปยังโครงสร้างในแคชวัตถุ ดังนั้นแอปพลิเคชันจะต้องไม่แก้ไขวัตถุที่ส่งคืน
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"
} โดยค่าเริ่มต้นขนาดสูงสุดของแคชวัตถุคือ 256000 รายการสำหรับแต่ละเนมสเปซ หากต้องการเปลี่ยนขนาดสูงสุดให้ใช้วิธี ObjCacheSize ของ NamespaceOptions , ส่งผ่านไปยัง opennamespace เช่น
// Set object cache limit to 4096 items
db . OpenNamespace ( "items_with_huge_cache" , reindexer . DefaultNamespaceOptions (). ObjCacheSize ( 4096 ), Item {})! แคชนี้ไม่ควรใช้สำหรับเนมสเปซซึ่งทำซ้ำจากโหนดอื่น ๆ : อาจไม่สอดคล้องกันสำหรับเนมสเปซของแบบจำลองเหล่านั้น
ชนิดข้อมูลเรขาคณิตที่รองรับเพียงอย่างเดียวคือจุด 2D ซึ่งนำไปใช้ใน Golang เป็น [2]float64 ( reindexer.Point )
ใน SQL สามารถสร้างจุดเป็น ST_GeomFromText('point(1 -3)')
คำขอที่ได้รับการสนับสนุนเพียงอย่างเดียวสำหรับสนามเรขาคณิตคือการหาจุดทั้งหมดภายในระยะทางจากจุดหนึ่ง DWithin(field_name, point, distance) ตามตัวอย่างด้านล่าง
ฟังก์ชั่น SQL ที่สอดคล้องกันคือ ST_DWithin(field_name, point, distance)
ดัชนี RTree สามารถสร้างขึ้นได้สำหรับคะแนน ในการทำเช่นนั้นควรประกาศแท็ก rtree และ linear , quadratic , greene หรือ rstar linear , quadratic , greene หรือ rstar หมายความว่าอัลกอริทึมของการก่อสร้าง rtree จะใช้ อัลกอริทึมที่นี่มีการระบุไว้ในลำดับจากการปรับให้เหมาะสมสำหรับการแทรกไปยังที่ดีที่สุดสำหรับการค้นหา แต่ขึ้นอยู่กับข้อมูล การทดสอบที่เหมาะสมกว่าสำหรับคุณ อัลกอริทึมเริ่มต้นคือ 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 ); Logger Reindexer สามารถเปิดใช้งานได้โดยวิธี db.SetLogger() เช่นเดียวกับในโค้ดตัวอย่างนี้:
type Logger struct {
}
func ( Logger ) Printf ( level int , format string , msg ... interface {}) {
log . Printf ( format , msg ... )
}
...
db . SetLogger ( Logger {}) Reindexer รองรับการบันทึกการกระทำที่ช้า สามารถกำหนดค่าได้ผ่าน profiling.long_queries_logging ส่วนของ namespace ระบบ #config การบันทึกการกระทำต่อไปสามารถกำหนดค่าได้:
เลือกแบบสอบถาม:
threshold_us (integer) : ค่าเกณฑ์ (ใน microseconds) สำหรับการดำเนินการของแบบสอบถามที่เลือก หากเกินกว่าจะมีการทำรายการหลักการหาก threshold_us เป็น -1 การบันทึกถูกปิดใช้งานnormalized (boolean) : เอาต์พุตแบบสอบถามในรูปแบบปกติอัปเดตและลบการสืบค้น:
threshold_us (integer) : ค่าเกณฑ์ (ใน microseconds) สำหรับการดำเนินการอัปเดตหรือลบแบบสอบถาม หากเกินกว่าจะมีการทำรายการหลักการหาก threshold_us เป็น -1 การบันทึกถูกปิดใช้งานnormalized (boolean) : เอาต์พุตแบบสอบถามในรูปแบบปกติธุรกรรม:
threshold_us (integer) : ค่าเกณฑ์ (ใน microseconds) สำหรับการทำธุรกรรมทั้งหมดเวลากระทำถ้า threshold_us คือ -1 การบันทึกโดยการทำธุรกรรมทั้งหมดจะถูกปิดใช้งานavg_step_threshold_us (integer) : ค่าเกณฑ์ (ใน microseconds) สำหรับระยะเวลาขั้นตอนเฉลี่ยในการทำธุรกรรม หาก avg_step_threshold_us คือ -1 การบันทึกโดยระยะเวลาขั้นตอนการทำธุรกรรมเฉลี่ยจะถูกปิดใช้งานคุณสมบัติที่มีประโยชน์อีกประการหนึ่งคือการดีบักการพิมพ์ของแบบสอบถามที่ประมวลผล ในการดีบักรายละเอียดการสอบถามรายละเอียดการพิมพ์มี 2 วิธี:
db.SetDefaultQueryDebug(namespace string,level int) - ทั่วโลกเปิดใช้งานรายละเอียดการพิมพ์ของแบบสอบถามทั้งหมดโดยเนมสเปซ
query.Debug(level int) - รายละเอียดการพิมพ์ของการดำเนินการแบบสอบถาม level การดำเนินการคือระดับของคำฟาง:
reindexer.INFO - จะพิมพ์เงื่อนไขการสืบค้นเท่านั้น
reindexer.TRACE - จะพิมพ์เงื่อนไขการสืบค้นและรายละเอียดการดำเนินการด้วยการกำหนดเวลา
query.Explain () - คำนวณและจัดเก็บรายละเอียดการดำเนินการแบบสอบถาม
iterator.GetExplainResults () - ส่งคืนรายละเอียดการดำเนินการค้นหาแบบสอบถาม
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/