CATATAN: Pelacak masalah dinonaktifkan. Anda dipersilakan untuk berkontribusi, permintaan tarik diterima.
EJDB2 adalah mesin database JSON yang dapat disematkan yang diterbitkan di bawah lisensi MIT.
Kisah depresi TI, burung dan EJDB 2.0
Linux | macOS | iOS | Android | jendela | |
---|---|---|---|---|---|
perpustakaan C | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ 1 |
NodeJS | ✔️ | ✔️ | 3 | ||
Jawa | ✔️ | ✔️ | ✔️ | ✔️ 2 | |
DartVM 5 | ✔️ | ✔️ 2 | 3 | ||
Berkibar 5 | ✔️ | ✔️ | |||
Bereaksi Asli 5 | 4 | ✔️ | |||
Cepat 5 | ✔️ | ✔️ | ✔️ |
[5]
Binding diperlukan Kontributor yang tidak dipelihara .
[1]
Tidak ada dukungan HTTP/Websocket #257
[2]
Biner tidak didistribusikan dengan dart pub.
Anda dapat membuatnya secara manual
[3]
Dapat dibuat, tetapi memerlukan hubungan dengan windows node/dart libs
.
[4]
Pemindahan sedang berlangsung #273
Linux
, macOS
dan FreeBSD
. Memiliki dukungan Windows yang terbatasApakah Anda menggunakan EJDB? Beritahu aku!
Kode EJDB2 di-porting dan diuji pada High Sierra
/ Mojave
/ Catalina
Pengikatan EJDB2 Swift untuk MacOS, iOS dan Linux. Pengikatan cepat sudah ketinggalan jaman saat ini. Mencari kontributor.
brew install ejdb
cmake v3.24 atau lebih tinggi diperlukan
git clone --recurse-submodules [email protected]:Softmotions/ejdb.git
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make install
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DPACKAGE_DEB=ON
make package
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DPACKAGE_RPM=ON
make package
EJDB2 dapat dikompilasi silang untuk windows
Catatan: API jaringan HTTP/Websocket dinonaktifkan dan belum didukung
Pengikatan Nodejs/Dart belum di-porting ke Windows.
Panduan kompilasi silang untuk Windows
IWSTART adalah generator proyek awal CMake otomatis untuk proyek C berdasarkan lib iowow/iwnet/ejdb2.
https://github.com/Softmotions/iwstart
https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_android/test
https://github.com/Softmotions/ejdb_android_todo_app
Sintaks bahasa kueri EJDB (JQL) terinspirasi oleh ide di balik pipa shell XPath dan Unix. Ini dirancang untuk memudahkan kueri dan memperbarui kumpulan dokumen JSON.
Parser JQL dibuat oleh peg/leg - generator parser keturunan rekursif untuk C Berikut adalah tata bahasa parser formal: https://github.com/Softmotions/ejdb/blob/master/src/jql/jqp.leg
Notasi yang digunakan di bawah ini didasarkan pada deskripsi sintaksis SQL:
Aturan | Keterangan |
---|---|
' ' | String dalam tanda kutip tunggal menunjukkan string literal yang tidak diberi tanda kutip sebagai bagian dari kueri. |
{ a | b } | Tanda kurung kurawal mengapit dua atau lebih pilihan alternatif yang diperlukan, dipisahkan oleh garis vertikal. |
[ ] | Tanda kurung siku menunjukkan elemen atau klausa opsional. Beberapa elemen atau klausa dipisahkan oleh garis vertikal. |
| | Bilah vertikal memisahkan dua atau lebih elemen sintaksis alternatif. |
... | Elips menunjukkan bahwa elemen sebelumnya dapat diulang. Pengulangannya tidak terbatas kecuali dinyatakan lain. |
( ) | Tanda kurung adalah pengelompokan simbol. |
Kata yang tidak dikutip dalam huruf kecil | Menunjukkan semantik dari beberapa bagian kueri. Misalnya: placeholder_name - nama placeholder mana pun. |
QUERY = FILTERS [ '|' APPLY ] [ '|' PROJECTIONS ] [ '|' OPTS ];
STR = { quoted_string | unquoted_string };
JSONVAL = json_value;
PLACEHOLDER = { ':'placeholder_name | '?' }
FILTERS = FILTER [{ and | or } [ not ] FILTER];
FILTER = [@collection_name]/NODE[/NODE]...;
NODE = { '*' | '**' | NODE_EXPRESSION | STR };
NODE_EXPRESSION = '[' NODE_EXPR_LEFT OP NODE_EXPR_RIGHT ']'
[{ and | or } [ not ] NODE_EXPRESSION]...;
OP = [ '!' ] { '=' | '>=' | '<=' | '>' | '<' | ~ }
| [ '!' ] { 'eq' | 'gte' | 'lte' | 'gt' | 'lt' }
| [ not ] { 'in' | 'ni' | 're' };
NODE_EXPR_LEFT = { '*' | '**' | STR | NODE_KEY_EXPR };
NODE_KEY_EXPR = '[' '*' OP NODE_EXPR_RIGHT ']'
NODE_EXPR_RIGHT = JSONVAL | STR | PLACEHOLDER
APPLY = { 'apply' | 'upsert' } { PLACEHOLDER | json_object | json_array } | 'del'
OPTS = { 'skip' n | 'limit' n | 'count' | 'noidx' | 'inverse' | ORDERBY }...
ORDERBY = { 'asc' | 'desc' } PLACEHOLDER | json_path
PROJECTIONS = PROJECTION [ {'+' | '-'} PROJECTION ]
PROJECTION = 'all' | json_path
json_value
: Nilai JSON apa pun yang valid: objek, array, string, bool, angka.json_path
: Penunjuk JSON yang disederhanakan. Misalnya: /foo/bar
atau /foo/"bar with spaces"/
*
dalam konteks NODE
: Nama kunci objek JSON apa pun pada tingkat bersarang tertentu.**
dalam konteks NODE
: Nama kunci objek JSON apa pun pada tingkat sarang yang sewenang-wenang.*
dalam konteks NODE_EXPR_LEFT
: Nama kunci pada tingkat tertentu.**
dalam konteks NODE_EXPR_LEFT
: Nilai array bersarang dari elemen array di bawah kunci tertentu. Mari bermain dengan beberapa data dan pertanyaan yang sangat mendasar. Untuk mempermudah kita akan menggunakan API jaringan websocket ejdb yang memberi kita semacam CLI interaktif. Pekerjaan yang sama juga dapat dilakukan menggunakan C
API murni ( ejdb2.h jql.h
).
CATATAN: Lihat kasus uji JQL untuk contoh lebih lanjut.
{
"firstName" : " John " ,
"lastName" : " Doe " ,
"age" : 28 ,
"pets" : [
{ "name" : " Rexy rex " , "kind" : " dog " , "likes" : [ " bones " , " jumping " , " toys " ]},
{ "name" : " Grenny " , "kind" : " parrot " , "likes" : [ " green color " , " night " , " toys " ]}
]
}
Simpan json sebagai sample.json
lalu unggah ke koleksi family
:
# Start HTTP/WS server protected by some access token
./jbs -a ' myaccess01 '
8 Mar 16:15:58.601 INFO: HTTP/WS endpoint at localhost:9191
Server dapat diakses menggunakan titik akhir HTTP atau Websocket. Informasi lebih lanjut
curl -d ' @sample.json ' -H ' X-Access-Token:myaccess01 ' -X POST http://localhost:9191/family
Kita bisa bermain-main menggunakan klien websocket wscat interaktif.
wscat -H ' X-Access-Token:myaccess01 ' -c http://localhost:9191
connected (press CTRL+C to quit)
> k info
< k {
" version " : " 2.0.0 " ,
" file " : " db.jb " ,
" size " : 8192,
" collections " : [
{
" name " : " family " ,
" dbid " : 3,
" rnum " : 1,
" indexes " : []
}
]
}
> k get family 1
< k 1 {
" firstName " : " John " ,
" lastName " : " Doe " ,
" age " : 28,
" pets " : [
{
" name " : " Rexy rex " ,
" kind " : " dog " ,
" likes " : [
" bones " ,
" jumping " ,
" toys "
]
},
{
" name " : " Grenny " ,
" kind " : " parrot " ,
" likes " : [
" green color " ,
" night " ,
" toys "
]
}
]
}
Catatan tentang awalan k
sebelum setiap perintah; Ini adalah kunci arbitrer yang dipilih oleh klien dan ditunjuk untuk mengidentifikasi permintaan soket web tertentu, kunci ini akan dikembalikan dengan respons terhadap permintaan dan memungkinkan klien untuk mengidentifikasi respons tersebut untuk permintaan khususnya. Informasi lebih lanjut
Perintah kueri melalui websocket memiliki format berikut:
<key> query <collection> <query>
Jadi kami hanya akan mempertimbangkan bagian <query>
dalam dokumen ini.
k query family /*
atau
k query family /**
atau tentukan nama koleksi dalam kueri secara eksplisit
k @family/*
Kita dapat mengeksekusi query dengan permintaan HTTP POST
curl --data-raw '@family/[firstName = John]' -H'X-Access-Token:myaccess01' -X POST http://localhost:9191
1 {"firstName":"John","lastName":"Doe","age":28,"pets":[{"name":"Rexy rex","kind":"dog","likes":["bones","jumping","toys"]},{"name":"Grenny","kind":"parrot","likes":["green color","night","toys"]}]}
k @family/* | limit 10
Elemen pada indeks 1
ada dalam array likes
dalam sub-objek pets
> k query family /pets/*/likes/1
< k 1 {"firstName":"John"...
Elemen pada indeks 1
ada dalam array likes
di tingkat sarang likes
mana pun
> k query family /**/likes/1
< k 1 {"firstName":"John"...
Dari titik ini dan di bawah ini saya akan menghilangkan k query family
awalan spesifik websocket dan hanya mempertimbangkan kueri JQL.
Untuk mendapatkan dokumen dengan kunci utama, tersedia opsi berikut:
Gunakan panggilan API ejdb_get()
const doc = await db . get ( 'users' , 112 ) ;
Gunakan konstruksi kueri khusus: /=:?
atau @collection/=:?
Dapatkan dokumen dari koleksi users
dengan kunci utama 112
> k @users/=112
Perbarui susunan tag untuk dokumen dalam kumpulan jobs
(TypeScript):
await db . createQuery ( '@jobs/ = :? | apply :? | count' )
. setNumber ( 0 , id )
. setJSON ( 1 , { tags } )
. completionPromise ( ) ;
Array kunci utama juga dapat digunakan untuk pencocokan:
await db . createQuery ( '@jobs/ = :?| apply :? | count' )
. setJSON ( 0 , [ 23 , 1 , 2 ] )
. setJSON ( 1 , { tags } )
. completionPromise ( ) ;
Di bawah ini adalah serangkaian pertanyaan yang menjelaskan dirinya sendiri:
/pets/*/[name = "Rexy rex"]
/pets/*/[name eq "Rexy rex"]
/pets/*/[name = "Rexy rex" or name = Grenny]
Catatan tentang kutipan di sekitar kata dengan spasi.
Dapatkan semua dokumen yang pemiliknya age
lebih dari 20
dan memiliki hewan peliharaan yang menyukai bones
atau toys
/[age > 20] and /pets/*/likes/[** in ["bones", "toys"]]
Di sini **
menunjukkan beberapa elemen dalam array likes
.
ni
adalah operator kebalikan dari in
. Dapatkan dokumen dengan bones
di suatu tempat dalam susunan likes
.
/pets/*/[likes ni "bones"]
Kita dapat membuat filter yang lebih rumit
( /[age <= 20] or /[lastName re "Do.*"] )
and /pets/*/likes/[** in ["bones", "toys"]]
Catatan tentang pengelompokan tanda kurung dan pencocokan ekspresi reguler menggunakan operator re
.
~
adalah operator pencocokan awalan (Sejak ejdb v2.0.53
). Pencocokan awalan bisa mendapatkan keuntungan dari penggunaan indeks.
Dapatkan dokumen yang /lastName
dimulai dengan "Do"
.
/[lastName ~ Do]
Filter dokumen dengan susunan likes
yang sama persis dengan ["bones","jumping","toys"]
/**/[likes = ["bones","jumping","toys"]]
Algoritme pencocokan untuk array dan peta berbeda:
{"f":"d","e":"j"}
dan {"e":"j","f":"d"}
adalah peta yang setara. Temukan dokumen JSON yang memiliki kunci firstName
di tingkat root.
/[* = "firstName"]
Dalam konteks ini *
menunjukkan nama kunci.
Anda dapat menggunakan ketentuan pada nama kunci dan nilai kunci secara bersamaan:
/[[* = "firstName"] = John]
Nama kunci dapat berupa firstName
atau lastName
, namun tetap harus memiliki nilai John
.
/[[* in ["firstName", "lastName"]] = John]
Ini mungkin berguna dalam kueri dengan placeholder dinamis (C API):
/[[* = :keyName] = :keyValue]
Bagian APPLY
bertanggung jawab untuk modifikasi konten dokumen.
APPLY = ({'apply' | `upsert`} { PLACEHOLDER | json_object | json_array }) | 'del'
Spesifikasi patch JSON sesuai dengan spesifikasi rfc7386
atau rfc6902
diikuti setelah kata kunci apply
.
Mari tambahkan objek address
ke semua dokumen yang cocok
/[firstName = John] | apply {"address":{"city":"New York", "street":""}}
Jika objek JSON adalah argumen bagian apply
maka akan diperlakukan sebagai kecocokan gabungan ( rfc7386
) jika tidak maka harus berupa array yang menunjukkan patch JSON rfc6902
. Placeholder juga didukung oleh bagian apply
.
/* | apply :?
Tetapkan nama jalan di address
/[firstName = John] | apply [{"op":"replace", "path":"/address/street", "value":"Fifth Avenue"}]
Tambahkan ikan Neo
ke kumpulan pets
John
/[firstName = John]
| apply [{"op":"add", "path":"/pets/-", "value": {"name":"Neo", "kind":"fish"}}]
upsert
memperbarui dokumen yang ada dengan argumen json tertentu yang digunakan sebagai patch gabungan atau menyisipkan argumen json yang disediakan sebagai contoh dokumen baru.
/[firstName = John] | upsert {"firstName": "John", "address":{"city":"New York"}}
Menambah nilai numerik yang diidentifikasi oleh jalur JSON berdasarkan nilai yang ditentukan.
Contoh:
Document: {"foo": 1}
Patch: [{"op": "increment", "path": "/foo", "value": 2}]
Result: {"foo": 3}
Sama seperti add
patch JSON tetapi membuat node objek perantara untuk segmen jalur JSON yang hilang.
Contoh:
Document: {"foo": {"bar": 1}}
Patch: [{"op": "add_create", "path": "/foo/zaz/gaz", "value": 22}]
Result: {"foo":{"bar":1,"zaz":{"gaz":22}}}
Contoh:
Document: {"foo": {"bar": 1}}
Patch: [{"op": "add_create", "path": "/foo/bar/gaz", "value": 22}]
Result: Error since element pointed by /foo/bar is not an object
Menukar dua nilai dokumen JSON mulai from
jalur.
Aturan pertukaran
from
tidak ada kesalahan akan dimunculkan.path
tidak ada maka akan ditentukan oleh nilai from
jalur tersebut, maka objek yang ditunjuk from
jalur tersebut akan dihapus.from
dan path
disajikan, maka keduanya akan ditukar.Contoh:
Document: {"foo": ["bar"], "baz": {"gaz": 11}}
Patch: [{"op": "swap", "from": "/foo/0", "path": "/baz/gaz"}]
Result: {"foo": [11], "baz": {"gaz": "bar"}}
Contoh (Demo aturan 2):
Document: {"foo": ["bar"], "baz": {"gaz": 11}}
Patch: [{"op": "swap", "from": "/foo/0", "path": "/baz/zaz"}]
Result: {"foo":[],"baz":{"gaz":11,"zaz":"bar"}}
Gunakan kata kunci del
untuk menghapus elemen yang cocok dari koleksi:
/FILTERS | del
Contoh:
> k add family {"firstName":"Jack"}
< k 2
> k query family /[firstName re "Ja.*"]
< k 2 {"firstName":"Jack"}
# Remove selected elements from collection
> k query family /[firstName=Jack] | del
< k 2 {"firstName":"Jack"}
PROJECTIONS = PROJECTION [ {'+' | '-'} PROJECTION ]
PROJECTION = 'all' | json_path | join_clause
Proyeksi memungkinkan untuk mendapatkan hanya sebagian dari dokumen JSON tidak termasuk data yang tidak diperlukan.
API placeholder kueri didukung dalam proyeksi.
Mari tambahkan satu dokumen lagi ke koleksi kita:
$ cat << EOF | curl -d @- -H'X-Access-Token:myaccess01' -X POST http://localhost:9191/family
{
"firstName":"Jack",
"lastName":"Parker",
"age":35,
"pets":[{"name":"Sonic", "kind":"mouse", "likes":[]}]
}
EOF
Sekarang hanya kueri nama depan dan nama belakang pemilik hewan peliharaan dari koleksi.
> k query family /* | /{firstName,lastName}
< k 3 {"firstName":"Jack","lastName":"Parker"}
< k 1 {"firstName":"John","lastName":"Doe"}
< k
Tambahkan array pets
untuk setiap dokumen
> k query family /* | /{firstName,lastName} + /pets
< k 3 {"firstName":"Jack","lastName":"Parker","pets":[...
< k 1 {"firstName":"John","lastName":"Doe","pets":[...
Kecualikan hanya bidang pets
dari dokumen
> k query family /* | all - /pets
< k 3 {"firstName":"Jack","lastName":"Parker","age":35}
< k 1 {"firstName":"John","lastName":"Doe","age":28,"address":{"city":"New York","street":"Fifth Avenue"}}
< k
Di sini all
kata kunci yang digunakan menunjukkan keseluruhan dokumen.
Dapatkan age
dan hewan peliharaan pertama dalam rangkaian pets
.
> k query family /[age > 20] | /age + /pets/0
< k 3 {"age":35,"pets":[{"name":"Sonic","kind":"mouse","likes":[]}]}
< k 1 {"age":28,"pets":[{"name":"Rexy rex","kind":"dog","likes":["bones","jumping","toys"]}]}
< k
Gabung mewujudkan referensi ke dokumen ke objek dokumen nyata yang akan menggantikan referensi di tempat.
Dokumen digabungkan hanya dengan kunci utamanya saja.
Kunci referensi harus disimpan dalam dokumen perujuk sebagai bidang angka atau string.
Gabungan dapat ditentukan sebagai bagian dari ekspresi proyeksi dalam bentuk berikut:
/.../field<collection
Di mana
field
- Bidang JSON berisi kunci utama dokumen yang digabungkan.<
- Simbol tanda khusus yang menginstruksikan mesin EJDB untuk mengganti kunci field
demi isi dokumen yang digabungkan.collection
- nama koleksi DB tempat dokumen yang digabungkan berada.Dokumen perujuk tidak akan disentuh jika dokumen terkait tidak ditemukan.
Berikut adalah demonstrasi sederhana gabungan koleksi di shell websocket interaktif kami:
> k add artists {"name":"Leonardo Da Vinci", "years":[1452,1519]}
< k 1
> k add paintings {"name":"Mona Lisa", "year":1490, "origin":"Italy", "artist": 1}
< k 1
> k add paintings {"name":"Madonna Litta - Madonna And The Child", "year":1490, "origin":"Italy", "artist": 1}
< k 2
# Lists paintings documents
> k @paintings/*
< k 2 {"name":"Madonna Litta - Madonna And The Child","year":1490,"origin":"Italy","artist":1}
< k 1 {"name":"Mona Lisa","year":1490,"origin":"Italy","artist":1}
< k
>
# Do simple join with artists collection
> k @paintings/* | /artist<artists
< k 2 {"name":"Madonna Litta - Madonna And The Child","year":1490,"origin":"Italy",
"artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k 1 {"name":"Mona Lisa","year":1490,"origin":"Italy",
"artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k
# Strip all document fields except `name` and `artist` join
> k @paintings/* | /artist<artists + /name + /artist/*
< k 2 {"name":"Madonna Litta - Madonna And The Child","artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k 1 {"name":"Mona Lisa","artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k
>
# Same results as above:
> k @paintings/* | /{name, artist<artists} + /artist/*
< k 2 {"name":"Madonna Litta - Madonna And The Child","artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k 1 {"name":"Mona Lisa","artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k
Referensi tidak valid:
> k add paintings {"name":"Mona Lisa2", "year":1490, "origin":"Italy", "artist": 9999}
< k 3
> k @paintings/* | /artist<artists
< k 3 {"name":"Mona Lisa2","year":1490,"origin":"Italy","artist":9999}
< k 2 {"name":"Madonna Litta - Madonna And The Child","year":1490,"origin":"Italy","artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
< k 1 {"name":"Mona Lisa","year":1490,"origin":"Italy","artist":{"name":"Leonardo Da Vinci","years":[1452,1519]}}
ORDERBY = ({ 'asc' | 'desc' } PLACEHOLDER | json_path)...
Mari tambahkan satu dokumen lagi lalu urutkan dokumen dalam koleksi menurut urutan firstName
menaik dan age
.
> k add family {"firstName":"John", "lastName":"Ryan", "age":39}
< k 4
> k query family /* | /{firstName,lastName,age} | asc /firstName desc /age
< k 3 {"firstName":"Jack","lastName":"Parker","age":35}
< k 4 {"firstName":"John","lastName":"Ryan","age":39}
< k 1 {"firstName":"John","lastName":"Doe","age":28}
< k
asc, desc
dapat menggunakan indeks yang ditentukan untuk pengumpulan guna menghindari tahap penyortiran dokumen terpisah.
OPTS = { 'skip' n | 'limit' n | 'count' | 'noidx' | 'inverse' | ORDERBY }...
skip n
Lewati n
catatan pertama sebelum elemen pertama dalam kumpulan hasillimit n
Tetapkan jumlah maksimal dokumen dalam kumpulan hasilcount
Mengembalikan hanya count
dokumen yang cocok > k query family /* | count
< k 3
< k
noidx
Jangan gunakan indeks apa pun untuk eksekusi kueri.inverse
Secara default, kueri memindai dokumen dari yang paling baru ditambahkan ke yang lebih lama. Opsi ini membalikkan arah pemindaian ke arah sebaliknya dan mengaktifkan mode noidx
. Tidak berpengaruh jika kueri memiliki klausa pengurutan asc/desc
. Indeks basis data dapat dibuat untuk jalur bidang JSON apa pun yang berisi nilai tipe angka atau string. Indeks dapat bersifat unique
– tidak memungkinkan duplikasi nilai dan non unique
. Flag bit mask mode indeks berikut digunakan (didefinisikan dalam ejdb2.h
):
Modus indeks | Keterangan |
---|---|
0x01 EJDB_IDX_UNIQUE | Indeks itu unik |
0x04 EJDB_IDX_STR | Indeks untuk jenis nilai bidang string JSON |
0x08 EJDB_IDX_I64 | Indeks untuk nilai bidang bilangan bulat bertanda 8 bytes width |
0x10 EJDB_IDX_F64 | Indeks untuk nilai bidang floating point bertanda 8 bytes width . |
Misalnya indeks unik tipe string akan ditentukan oleh EJDB_IDX_UNIQUE | EJDB_IDX_STR
= 0x05
. Indeks dapat ditentukan hanya untuk satu jenis nilai yang terletak di bawah jalur tertentu dalam dokumen json.
Mari kita tentukan indeks string non unik untuk jalur /lastName
:
> k idx family 4 /lastName
< k
Pemilihan indeks untuk kueri berdasarkan seperangkat aturan heuristik.
Anda selalu dapat memeriksa penggunaan indeks dengan mengeluarkan perintah explain
di WS API:
> k explain family /[lastName=Doe] and /[age!=27]
< k explain [INDEX] MATCHED STR|3 /lastName EXPR1: 'lastName = Doe' INIT: IWKV_CURSOR_EQ
[INDEX] SELECTED STR|3 /lastName EXPR1: 'lastName = Doe' INIT: IWKV_CURSOR_EQ
[COLLECTOR] PLAIN
Pernyataan berikut diperhitungkan saat menggunakan indeks EJDB2:
Hanya satu indeks yang dapat digunakan untuk eksekusi kueri tertentu
Jika kueri terdiri dari or
bagian gabungan di tingkat atas atau berisi ekspresi negated
di tingkat atas ekspresi kueri - indeks tidak akan digunakan sama sekali. Jadi tidak ada indeks di bawah:
/[lastName != Andy]
/[lastName = "John"] or /[lastName = Peter]
Tapi akan digunakan indeks /lastName
yang ditentukan di atas
/[lastName = Doe]
/[lastName = Doe] and /[age = 28]
/[lastName = Doe] and not /[age = 28]
/[lastName = Doe] and /[age != 28]
Operator berikut didukung oleh indeks (ejdb 2.0.x):
eq, =
gt, >
gte, >=
lt, <
lte, <=
in
~
(Pencocokan awalan sejak ejdb 2.0.53) Klausa ORDERBY
dapat menggunakan indeks untuk menghindari penyortiran kumpulan hasil.
Bidang array juga dapat diindeks. Mari kita uraikan kasus penggunaan umum: pengindeksan beberapa tag entitas:
> k add books {"name":"Mastering Ultra", "tags":["ultra", "language", "bestseller"]}
< k 1
> k add books {"name":"Learn something in 24 hours", "tags":["bestseller"]}
< k 2
> k query books /*
< k 2 {"name":"Learn something in 24 hours","tags":["bestseller"]}
< k 1 {"name":"Mastering Ultra","tags":["ultra","language","bestseller"]}
< k
Buat indeks string untuk /tags
> k idx books 4 /tags
< k
Filter buku berdasarkan tag bestseller
dan tampilkan penggunaan indeks dalam kueri:
> k explain books /tags/[** in ["bestseller"]]
< k explain [INDEX] MATCHED STR|4 /tags EXPR1: '** in ["bestseller"]' INIT: IWKV_CURSOR_EQ
[INDEX] SELECTED STR|4 /tags EXPR1: '** in ["bestseller"]' INIT: IWKV_CURSOR_EQ
[COLLECTOR] PLAIN
< k 1 {"name":"Mastering Ultra","tags":["ultra","language","bestseller"]}
< k 2 {"name":"Learn something in 24 hours","tags":["bestseller"]}
< k
Semua dokumen dalam koleksi diurutkan berdasarkan kunci utama dalam urutan descending
. Jadi jika Anda menggunakan kunci yang dibuat secara otomatis ( ejdb_put_new
) Anda dapat yakin dokumen apa yang diambil sebagai hasil pemindaian penuh kueri akan diurutkan sesuai dengan waktu penyisipan dalam urutan turunan, kecuali Anda tidak menggunakan pengurutan kueri, indeks, atau kata kunci inverse
.
Dalam banyak kasus, penggunaan indeks dapat menurunkan kinerja kueri secara keseluruhan. Karena kumpulan indeks hanya berisi referensi dokumen ( id
) dan mesin dapat melakukan pengambilan dokumen tambahan dengan kunci utamanya untuk menyelesaikan pencocokan kueri. Jadi untuk koleksi yang tidak terlalu besar, pemindaian kasar mungkin memiliki kinerja lebih baik daripada pemindaian menggunakan indeks. Namun, operasi pencocokan tepat: eq
, in
dan sorting
berdasarkan urutan indeks alami akan mendapatkan keuntungan dari indeks dalam banyak kasus.
Jika Anda ingin memperbarui beberapa kumpulan dokumen dengan operasi apply
atau del
tetapi tidak ingin mengambil semuanya sebagai hasil kueri - cukup tambahkan pengubah count
ke kueri untuk menghilangkan transfer data yang tidak perlu dan konversi data json.
Mesin EJDB menyediakan kemampuan untuk memulai pekerja titik akhir HTTP/Websocket terpisah yang mengekspos API jaringan untuk kueri dan modifikasi data. SSL (TLS 1.2) didukung oleh server jbs
.
Cara termudah untuk mengekspos database melalui jaringan adalah menggunakan server jbs
mandiri. (Tentu saja jika Anda ingin menghindari integrasi C API
).
Usage:
./jbs [options]
-v, --version Print program version.
-f, --file=<> Database file path. Default: ejdb2.db
-p, --port=NUM HTTP server port numer. Default: 9191
-l, --listen=<> Network address server will listen. Default: localhost
-k, --key=<> PEM private key file for TLS 1.2 HTTP server.
-c, --certs=<> PEM certificates file for TLS 1.2 HTTP server.
-a, --access=TOKEN|@FILE Access token to match 'X-Access-Token' HTTP header value.
-r, --access-read Allows unrestricted read-only data access.
-C, --cors Enable COSR response headers for HTTP server
-t, --trunc Cleanup/reset database file on open.
-w, --wal use the write ahead log (WAL). Used to provide data durability.
Advanced options:
-S, --sbz=NUM Max sorting buffer size. If exceeded, an overflow temp file for data will be created.
Default: 16777216, min: 1048576
-D, --dsz=NUM Initial size of buffer to process/store document on queries. Preferable average size of document.
Default: 65536, min: 16384
-T, --trylock Exit with error if database is locked by another process.
If not set, current process will wait for lock release.
Titik akhir HTTP mungkin dilindungi oleh token yang ditentukan dengan flag --access
atau struct C API EJDB_HTTP
. Jika token akses ditetapkan, klien harus menyediakan header HTTP X-Access-Token
. Jika token diperlukan tetapi tidak disediakan oleh klien, kode HTTP 401
akan dilaporkan. Jika token akses tidak cocok dengan token yang disediakan oleh server klien akan merespons dengan kode HTTP 403
.
Tambahkan dokumen baru ke collection
.
200
sukses. Isi: pengidentifikasi dokumen baru sebagai nomor int64
Mengganti/menyimpan dokumen dengan id
numerik tertentu
200
untuk kesuksesan. Tubuh kosong Menghapus dokumen yang diidentifikasi berdasarkan id
dari collection
200
untuk kesuksesan. Tubuh kosong404
dokumen tidak ditemukan Tambal dokumen yang diidentifikasi berdasarkan id
oleh rfc7396, data rfc6902.
200
untuk kesuksesan. Tubuh kosong Ambil dokumen yang diidentifikasi berdasarkan id
dari collection
.
200
untuk kesuksesan. Isi: Teks dokumen JSON.content-type:application/json
content-length:
404
dokumen tidak ditemukan Kueri koleksi dengan kueri yang disediakan sebagai isi POST. Isi kueri harus berisi nama koleksi yang digunakan di elemen filter pertama: @collection_name/...
Header permintaan:
X-Hints
memisahkan petunjuk tambahan dengan koma ke mesin database ejdb2.explain
Tampilkan rencana eksekusi kueri sebelum elemen pertama dalam kumpulan hasil dipisahkan oleh baris --------------------
. Tanggapan:200
untuk kesuksesan.n
dalam format berikut: rn<document id>t<document JSON body>
...
Contoh:
curl -v --data-raw '@family/[age > 18]' -H 'X-Access-Token:myaccess01' http://localhost:9191
* Rebuilt URL to: http://localhost:9191/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9191 (#0)
> POST / HTTP/1.1
> Host: localhost:9191
> User-Agent: curl/7.58.0
> Accept: */*
> X-Access-Token:myaccess01
> Content-Length: 18
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 18 out of 18 bytes
< HTTP/1.1 200 OK
< connection:keep-alive
< content-type:application/json
< transfer-encoding:chunked
<
4 {"firstName":"John","lastName":"Ryan","age":39}
3 {"firstName":"Jack","lastName":"Parker","age":35,"pets":[{"name":"Sonic","kind":"mouse","likes":[]}]}
1 {"firstName":"John","lastName":"Doe","age":28,"pets":[{"name":"Rexy rex","kind":"dog","likes":["bones","jumping","toys"]},{"name":"Grenny","kind":"parrot","likes":["green color","night","toys"]}],"address":{"city":"New York","street":"Fifth Avenue"}}
* Connection #0 to host localhost left intact
curl --data-raw '@family/[lastName = "Ryan"]' -H 'X-Access-Token:myaccess01' -H 'X-Hints:explain' http://localhost:9191
[INDEX] MATCHED STR|3 /lastName EXPR1: 'lastName = "Ryan"' INIT: IWKV_CURSOR_EQ
[INDEX] SELECTED STR|3 /lastName EXPR1: 'lastName = "Ryan"' INIT: IWKV_CURSOR_EQ
[COLLECTOR] PLAIN
--------------------
4 {"firstName":"John","lastName":"Ryan","age":39}
Ambil metadata JSON ejdb dan metode HTTP yang tersedia di header Allow
respons. Contoh:
curl -X OPTIONS -H 'X-Access-Token:myaccess01' http://localhost:9191/
{
"version": "2.0.0",
"file": "db.jb",
"size": 16384,
"collections": [
{
"name": "family",
"dbid": 3,
"rnum": 3,
"indexes": [
{
"ptr": "/lastName",
"mode": 4,
"idbf": 64,
"dbid": 4,
"rnum": 3
}
]
}
]
}
EJDB mendukung protokol berbasis teks sederhana melalui protokol websocket HTTP. Anda dapat menggunakan alat CLI websocket interaktif wscat untuk berkomunikasi dengan server secara tangan.
Akan merespons dengan pesan teks bantuan berikut:
wscat -H 'X-Access-Token:myaccess01' -c http://localhost:9191
> ?
<
<key> info
<key> get <collection> <id>
<key> set <collection> <id> <document json>
<key> add <collection> <document json>
<key> del <collection> <id>
<key> patch <collection> <id> <patch json>
<key> idx <collection> <mode> <path>
<key> rmi <collection> <mode> <path>
<key> rmc <collection>
<key> query <collection> <query>
<key> explain <collection> <query>
<key> <query>
>
Catatan tentang awalan <key>
sebelum setiap perintah; Ini adalah kunci arbitrer yang dipilih oleh klien dan ditunjuk untuk mengidentifikasi permintaan soket web tertentu, kunci ini akan dikembalikan dengan respons terhadap permintaan dan memungkinkan klien untuk mengidentifikasi respons tersebut untuk permintaan khususnya.
Kesalahan dikembalikan dalam format berikut:
<key> ERROR: <error description>
<key> info
Dapatkan metadata database sebagai dokumen JSON.
<key> get <collection> <id>
Ambil dokumen yang diidentifikasi berdasarkan id
dari collection
. Jika dokumen tidak ditemukan IWKV_ERROR_NOTFOUND
akan dikembalikan.
Contoh:
> k get family 3
< k 3 {
"firstName": "Jack",
"lastName": "Parker",
"age": 35,
"pets": [
{
"name": "Sonic",
"kind": "mouse",
"likes": []
}
]
}
Jika dokumen tidak ditemukan kita akan mendapatkan error:
> k get family 55
< k ERROR: Key not found. (IWKV_ERROR_NOTFOUND)
>
<key> set <collection> <id> <document json>
Mengganti/menambahkan dokumen dengan id
numerik tertentu. Collection
akan dibuat secara otomatis jika tidak ada.
<key> add <collection> <document json>
Tambahkan dokumen baru ke <collection>
id
dokumen baru akan dibuat dan dikembalikan sebagai respons. `Koleksi> akan dibuat secara otomatis jika tidak ada.
Contoh:
> k add mycollection {"foo":"bar"}
< k 1
> k add mycollection {"foo":"bar"}
< k 2
>
<key> del <collection> <id>
Hapus dokumen yang diidentifikasi berdasarkan id
dari collection
. Jika dokumen tidak ditemukan IWKV_ERROR_NOTFOUND
akan dikembalikan.
<key> patch <collection> <id> <patch json>
Terapkan patch rfc7396 atau rfc6902 ke dokumen yang diidentifikasi oleh id
. Jika dokumen tidak ditemukan IWKV_ERROR_NOTFOUND
akan dikembalikan.
<key> query <collection> <query>
Jalankan kueri pada dokumen dalam collection
tertentu. Respons: Satu set pesan WS dengan isi dokumen diakhiri oleh pesan terakhir dengan isi kosong.
> k query family /* | /firstName
< k 4 {"firstName":"John"}
< k 3 {"firstName":"Jack"}
< k 1 {"firstName":"John"}
< k
Catatan tentang pesan terakhir: <key>
tanpa isi.
<key> explain <collection> <query>
Sama seperti <key> query <collection> <query>
tetapi pesan respon pertama akan diawali dengan <key> explain
dan berisi rencana eksekusi query.
Contoh:
> k explain family /* | /firstName
< k explain [INDEX] NO [COLLECTOR] PLAIN
< k 4 {"firstName":"John"}
< k 3 {"firstName":"Jack"}
< k 1 {"firstName":"John"}
< k
Jalankan teks kueri. Isi kueri harus berisi nama koleksi yang digunakan di elemen filter pertama: @collection_name/...
. Perilakunya sama seperti untuk: <key> query <collection> <query>
<key> idx <collection> <mode> <path>
Pastikan indeks dengan mode
yang ditentukan (bendera bitmask) untuk path
dan collection
json tertentu. Koleksi akan dibuat jika tidak ada.
Modus indeks | Keterangan |
---|---|
0x01 EJDB_IDX_UNIQUE | Indeks itu unik |
0x04 EJDB_IDX_STR | Indeks untuk jenis nilai bidang string JSON |
0x08 EJDB_IDX_I64 | Indeks untuk nilai bidang bilangan bulat bertanda 8 bytes width |
0x10 EJDB_IDX_F64 | Indeks untuk nilai bidang floating point bertanda 8 bytes width . |
Tetapkan indeks string unik (0x01 & 0x04) = 5
pada bidang /name
JSON:
k idx mycollection 5 /name
<key> rmi <collection> <mode> <path>
Hapus indeks dengan mode
tertentu (bendera bitmask) untuk path
dan collection
json tertentu. Kesalahan pengembalian jika indeks yang diberikan tidak ditemukan.
<key> rmc <collection>
Hapus koleksi dan semua datanya. Catatan: Jika collection
tidak ditemukan, tidak ada kesalahan yang akan dilaporkan.
Jika Anda telah menginstal Docker, Anda dapat membuat image Docker dan menjalankannya dalam sebuah container
cd docker
docker build -t ejdb2 .
docker run -d -p 9191:9191 --name myEJDB ejdb2 --access myAccessKey
atau dapatkan image ejdb2
langsung dari Docker Hub
docker run -d -p 9191:9191 --name myEJDB softmotions/ejdb2 --access myAccessKey
EJDB dapat disematkan ke dalam aplikasi C/C++
apa pun. C API
didokumentasikan dalam header berikut:
Contoh aplikasi:
#include <ejdb2/ejdb2.h>
#define CHECK ( rc_ )
if (rc_) {
iwlog_ecode_error3(rc_);
return 1;
}
static iwrc documents_visitor ( EJDB_EXEC * ctx , const EJDB_DOC doc , int64_t * step ) {
// Print document to stderr
return jbl_as_json ( doc -> raw , jbl_fstream_json_printer , stderr , JBL_PRINT_PRETTY );
}
int main () {
EJDB_OPTS opts = {
. kv = {
. path = "example.db" ,
. oflags = IWKV_TRUNC
}
};
EJDB db ; // EJDB2 storage handle
int64_t id ; // Document id placeholder
JQL q = 0 ; // Query instance
JBL jbl = 0 ; // Json document
iwrc rc = ejdb_init ();
CHECK ( rc );
rc = ejdb_open ( & opts , & db );
CHECK ( rc );
// First record
rc = jbl_from_json ( & jbl , "{"name":"Bianca", "age":4}" );
RCGO ( rc , finish );
rc = ejdb_put_new ( db , "parrots" , jbl , & id );
RCGO ( rc , finish );
jbl_destroy ( & jbl );
// Second record
rc = jbl_from_json ( & jbl , "{"name":"Darko", "age":8}" );
RCGO ( rc , finish );
rc = ejdb_put_new ( db , "parrots" , jbl , & id );
RCGO ( rc , finish );
jbl_destroy ( & jbl );
// Now execute a query
rc = jql_create ( & q , "parrots" , "/[age > :age]" );
RCGO ( rc , finish );
EJDB_EXEC ux = {
. db = db ,
. q = q ,
. visitor = documents_visitor
};
// Set query placeholder value.
// Actual query will be /[age > 3]
rc = jql_set_i64 ( q , "age" , 0 , 3 );
RCGO ( rc , finish );
// Now execute the query
rc = ejdb_exec ( & ux );
finish:
jql_destroy ( & q );
jbl_destroy ( & jbl );
ejdb_close ( & db );
CHECK ( rc );
return 0 ;
}
Kompilasi dan jalankan:
gcc -std=gnu11 -Wall -pedantic -c -o example1.o example1.c
gcc -o example1 example1.o -lejdb2
./example1
{
"name": "Darko",
"age": 8
}{
"name": "Bianca",
"age": 4
}
MIT License
Copyright (c) 2012-2024 Softmotions Ltd <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.