ملاحظة: تم تعطيل أداة تعقب المشكلات. أنتم مدعوون للمساهمة، وقبول طلبات السحب.
EJDB2 هو محرك قاعدة بيانات JSON قابل للتضمين ومنشور بموجب ترخيص MIT.
قصة اكتئاب تكنولوجيا المعلومات والطيور وEJDB 2.0
لينكس | ماك | دائرة الرقابة الداخلية | أندرويد | ويندوز | |
---|---|---|---|---|---|
مكتبة ج | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ 1 |
NodeJS | ✔️ | ✔️ | 3 | ||
جافا | ✔️ | ✔️ | ✔️ | ✔️ 2 | |
دارت في إم 5 | ✔️ | ✔️ 2 | 3 | ||
الرفرفة 5 | ✔️ | ✔️ | |||
رد الفعل الأصلي 5 | 4 | ✔️ | |||
سويفت 5 | ✔️ | ✔️ | ✔️ |
[5]
الارتباطات غير قابلة للصيانة ويحتاج إليها المساهمون.
[1]
لا يوجد دعم HTTP/Websocket رقم 257
[2]
لا يتم توزيع الثنائيات مع دارت pub.
يمكنك بنائه يدويًا
[3]
يمكن إنشاؤه، ولكنه يحتاج إلى ارتباط مع عقدة windows/dart libs
.
[4]
النقل قيد التقدم رقم 273
Linux
و macOS
و FreeBSD
. لديه دعم محدود لنظام التشغيل Windowsهل تستخدم EJDB؟ اسمحوا لي أن أعرف!
تم نقل كود EJDB2 واختباره على High Sierra
/ Mojave
/ Catalina
ربط EJDB2 Swift لأنظمة MacOS وiOS وLinux. الربط السريع أصبح قديمًا في الوقت الحالي. أبحث عن المساهمين.
brew install ejdb
مطلوب cmake v3.24 أو أعلى
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 بشكل مشترك للنوافذ
ملاحظة: واجهة برمجة تطبيقات شبكة HTTP/Websocket معطلة وغير مدعومة بعد
لم يتم نقل روابط Nodejs/Dart إلى Windows بعد.
دليل التجميع المتقاطع لنظام التشغيل Windows
IWSTART هو منشئ مشروع أولي تلقائي لـ CMake لمشاريع C استنادًا إلى iowow / iwnet / ejdb2 libs.
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
بناء جملة لغة الاستعلام (JQL) EJDB مستوحى من الأفكار الكامنة وراء أنابيب XPath وUnix Shell. إنه مصمم لسهولة الاستعلام وتحديث مجموعات مستندات JSON.
تم إنشاء محلل JQL بواسطة peg/leg - مولدات المحلل اللغوي العودية للغة C إليك قواعد المحلل اللغوي الرسمية: https://github.com/Softmotions/ejdb/blob/master/src/jql/jqp.leg
يعتمد الترميز المستخدم أدناه على وصف بناء جملة SQL:
قاعدة | وصف |
---|---|
' ' | تشير السلسلة الموجودة بين علامات الاقتباس المفردة إلى سلسلة حرفية غير مقتبسة كجزء من الاستعلام. |
{ a | b } | تحتوي الأقواس المتعرجة على خيارين بديلين مطلوبين أو أكثر، مفصولة بأشرطة رأسية. |
[ ] | تشير الأقواس المربعة إلى عنصر أو جملة اختيارية. يتم فصل العناصر أو الجمل المتعددة بواسطة أشرطة عمودية. |
| | تفصل الأشرطة العمودية بين عنصرين بديلين أو أكثر من عناصر بناء الجملة. |
... | تشير علامات الحذف إلى إمكانية تكرار العنصر السابق. التكرار غير محدود ما لم تتم الإشارة إلى خلاف ذلك. |
( ) | الأقواس هي رموز التجميع. |
كلمة غير مقتبسة بالأحرف الصغيرة | يدل على دلالات بعض أجزاء الاستعلام. على سبيل المثال: placeholder_name - اسم أي عنصر نائب. |
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
: أي قيمة JSON صالحة: كائن، صفيف، سلسلة، منطقي، رقم.json_path
: مؤشر JSON المبسط. على سبيل المثال: /foo/bar
أو /foo/"bar with spaces"/
*
في سياق NODE
: أي اسم مفتاح كائن JSON عند مستوى تداخل معين.**
في سياق NODE
: أي اسم مفتاح كائن JSON على مستوى التداخل التعسفي.*
في سياق NODE_EXPR_LEFT
: اسم المفتاح عند مستوى محدد.**
في سياق NODE_EXPR_LEFT
: قيمة الصفيف المتداخلة لعنصر الصفيف تحت مفتاح محدد. دعونا نلعب مع بعض البيانات والاستعلامات الأساسية للغاية. من أجل البساطة، سوف نستخدم واجهة برمجة تطبيقات شبكة ejdb websocket التي توفر لنا نوعًا من واجهة سطر الأوامر (CLI) التفاعلية. يمكن القيام بنفس المهمة باستخدام واجهة برمجة تطبيقات C
الخالصة أيضًا ( ejdb2.h jql.h
).
ملحوظة: قم بإلقاء نظرة على حالات اختبار JQL لمزيد من الأمثلة.
{
"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 " ]}
]
}
احفظ json كـ sample.json
ثم قم بتحميله إلى مجموعة 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
يمكن الوصول إلى الخادم باستخدام نقطة نهاية HTTP أو Websocket. مزيد من المعلومات
curl -d ' @sample.json ' -H ' X-Access-Token:myaccess01 ' -X POST http://localhost:9191/family
يمكننا اللعب باستخدام عميل wscat websocket التفاعلي.
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 "
]
}
]
}
ملاحظة حول البادئة k
قبل كل أمر؛ إنه مفتاح عشوائي يختاره العميل ويخصص لتحديد طلب websocket معين، وسيتم إرجاع هذا المفتاح مع الاستجابة للطلب ويسمح للعميل بتحديد تلك الاستجابة لطلبه المحدد. مزيد من المعلومات
أمر الاستعلام عبر websocket له التنسيق التالي:
<key> query <collection> <query>
لذلك سننظر في جزء <query>
فقط في هذه الوثيقة.
k query family /*
أو
k query family /**
أو حدد اسم المجموعة في الاستعلام بشكل صريح
k @family/*
يمكننا تنفيذ الاستعلام عن طريق طلب 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
العنصر الموجود في الفهرس 1
موجود في مصفوفة likes
ضمن كائن فرعي pets
> k query family /pets/*/likes/1
< k 1 {"firstName":"John"...
العنصر الموجود في الفهرس 1
موجود في مصفوفة likes
عند أي مستوى تداخل likes
> k query family /**/likes/1
< k 1 {"firstName":"John"...
من هذه النقطة وما يليها، سأحذف k query family
ذات البادئة المحددة لـ websocket وأفكر فقط في استعلامات JQL.
للحصول على المستندات عن طريق المفتاح الأساسي، تتوفر الخيارات التالية:
استخدم استدعاء API ejdb_get()
const doc = await db . get ( 'users' , 112 ) ;
استخدم بناء الاستعلام الخاص: /=:?
أو @collection/=:?
احصل على المستند من مجموعة users
باستخدام المفتاح الأساسي 112
> k @users/=112
تحديث مجموعة العلامات للمستند في مجموعة jobs
(TypeScript):
await db . createQuery ( '@jobs/ = :? | apply :? | count' )
. setNumber ( 0 , id )
. setJSON ( 1 , { tags } )
. completionPromise ( ) ;
يمكن أيضًا استخدام مجموعة من المفاتيح الأساسية للمطابقة:
await db . createQuery ( '@jobs/ = :?| apply :? | count' )
. setJSON ( 0 , [ 23 , 1 , 2 ] )
. setJSON ( 1 , { tags } )
. completionPromise ( ) ;
فيما يلي مجموعة من الاستفسارات التي تشرح نفسها بنفسها:
/pets/*/[name = "Rexy rex"]
/pets/*/[name eq "Rexy rex"]
/pets/*/[name = "Rexy rex" or name = Grenny]
ملاحظة حول الاقتباسات حول الكلمات ذات المسافات.
احصل على جميع المستندات التي يزيد age
المالك فيها عن 20
ولديه حيوانًا أليفًا يحب bones
أو toys
/[age > 20] and /pets/*/likes/[** in ["bones", "toys"]]
هنا **
يشير إلى بعض العناصر في مصفوفة likes
.
ni
هو العامل العكسي لـ in
. احصل على المستندات التي تحتوي على bones
في مكان ما في مجموعة likes
.
/pets/*/[likes ni "bones"]
يمكننا إنشاء مرشحات أكثر تعقيدا
( /[age <= 20] or /[lastName re "Do.*"] )
and /pets/*/likes/[** in ["bones", "toys"]]
ملاحظة حول تجميع الأقواس ومطابقة التعبير العادي باستخدام عامل التشغيل re
.
~
هو عامل مطابقة للبادئة (منذ ejdb v2.0.53
). يمكن أن تستفيد مطابقة البادئات من استخدام الفهارس.
احصل على المستندات التي يبدأ فيها /lastName
بـ "Do"
.
/[lastName ~ Do]
قم بتصفية المستندات بمصفوفة likes
المطابقة تمامًا لـ ["bones","jumping","toys"]
/**/[likes = ["bones","jumping","toys"]]
تختلف خوارزميات المطابقة للمصفوفات والخرائط:
{"f":"d","e":"j"}
و {"e":"j","f":"d"}
خرائط متساوية. ابحث عن مستند JSON الذي يحتوي على مفتاح firstName
على مستوى الجذر.
/[* = "firstName"]
أنا هذا السياق *
يدل على اسم المفتاح.
يمكنك استخدام الشروط على اسم المفتاح وقيمة المفتاح في نفس الوقت:
/[[* = "firstName"] = John]
يمكن أن يكون اسم المفتاح إما firstName
أو lastName
ولكن يجب أن يكون له قيمة John
في أي حال.
/[[* in ["firstName", "lastName"]] = John]
قد يكون مفيدًا في الاستعلامات ذات العناصر النائبة الديناميكية (C API):
/[[* = :keyName] = :keyValue]
قسم APPLY
مسؤول عن تعديل محتوى المستندات.
APPLY = ({'apply' | `upsert`} { PLACEHOLDER | json_object | json_array }) | 'del'
تتوافق مواصفات تصحيح JSON مع مواصفات rfc7386
أو rfc6902
المتبعة بعد apply
الكلمة الأساسية.
دعونا نضيف كائن address
إلى كل المستندات المتطابقة
/[firstName = John] | apply {"address":{"city":"New York", "street":""}}
إذا كان كائن JSON عبارة عن وسيطة لقسم apply
، فسيتم التعامل معه على أنه مطابقة دمج ( rfc7386
) وإلا فيجب أن يكون صفيفًا يشير إلى تصحيح rfc6902
JSON. يتم أيضًا دعم العناصر النائبة من خلال قسم apply
.
/* | apply :?
قم بتعيين اسم الشارع في address
/[firstName = John] | apply [{"op":"replace", "path":"/address/street", "value":"Fifth Avenue"}]
أضف أسماك Neo
إلى مجموعة pets
جون الأليفة
/[firstName = John]
| apply [{"op":"add", "path":"/pets/-", "value": {"name":"Neo", "kind":"fish"}}]
يقوم upsert
بتحديث المستند الموجود من خلال وسيطة json معينة تستخدم كتصحيح دمج أو إدراج وسيطة json كمثيل مستند جديد.
/[firstName = John] | upsert {"firstName": "John", "address":{"city":"New York"}}
زيادة القيمة الرقمية المحددة بواسطة مسار JSON بقيمة محددة.
مثال:
Document: {"foo": 1}
Patch: [{"op": "increment", "path": "/foo", "value": 2}]
Result: {"foo": 3}
تمامًا مثل add
تصحيح JSON ولكنه ينشئ عقد كائنات وسيطة لمقاطع مسار JSON المفقودة.
مثال:
Document: {"foo": {"bar": 1}}
Patch: [{"op": "add_create", "path": "/foo/zaz/gaz", "value": 22}]
Result: {"foo":{"bar":1,"zaz":{"gaz":22}}}
مثال:
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
مبادلة قيمتين لمستند JSON بدءًا from
المسار.
قواعد المبادلة
from
غير موجودة سيتم رفع الخطأ.path
غير موجودة، فسيتم تعيينها بواسطة القيمة from
المسار، ثم ستتم إزالة الكائن المشار إليه from
المسار.from
و path
، فسيتم تبديلهما.مثال:
Document: {"foo": ["bar"], "baz": {"gaz": 11}}
Patch: [{"op": "swap", "from": "/foo/0", "path": "/baz/gaz"}]
Result: {"foo": [11], "baz": {"gaz": "bar"}}
مثال (عرض توضيحي للقاعدة 2):
Document: {"foo": ["bar"], "baz": {"gaz": 11}}
Patch: [{"op": "swap", "from": "/foo/0", "path": "/baz/zaz"}]
Result: {"foo":[],"baz":{"gaz":11,"zaz":"bar"}}
استخدم الكلمة الأساسية del
لإزالة العناصر المطابقة من المجموعة:
/FILTERS | del
مثال:
> 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
يسمح الإسقاط بالحصول على مجموعة فرعية فقط من مستند JSON باستثناء البيانات غير المطلوبة.
يتم دعم واجهة برمجة تطبيقات العناصر النائبة للاستعلام في التوقعات.
دعونا نضيف مستندًا آخر إلى مجموعتنا:
$ 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
استعلم الآن فقط عن الاسم الأول واسم العائلة لأصحاب الحيوانات الأليفة من المجموعة.
> k query family /* | /{firstName,lastName}
< k 3 {"firstName":"Jack","lastName":"Parker"}
< k 1 {"firstName":"John","lastName":"Doe"}
< k
أضف مجموعة pets
لكل مستند
> k query family /* | /{firstName,lastName} + /pets
< k 3 {"firstName":"Jack","lastName":"Parker","pets":[...
< k 1 {"firstName":"John","lastName":"Doe","pets":[...
استبعاد حقل pets
فقط من المستندات
> 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
هنا all
الكلمات الرئيسية المستخدمة تشير إلى المستند بأكمله.
احصل على age
وأول حيوان أليف في مجموعة 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
يجسد الانضمام مرجعًا للمستند إلى كائنات مستند حقيقية والتي ستحل محل المرجع في مكانه.
يتم ضم المستندات بواسطة مفاتيحها الأساسية فقط.
يجب تخزين المفاتيح المرجعية في المستند المرجعي كحقل رقم أو سلسلة.
يمكن تحديد الصلات كجزء من تعبير الإسقاط بالشكل التالي:
/.../field<collection
أين
field
- يحتوي حقل JSON على المفتاح الأساسي للمستند المنضم.<
- رمز العلامة الخاصة الذي يوجه محرك EJDB لاستبدال مفتاح field
بنص المستند المرتبط.collection
- اسم مجموعة قاعدة البيانات حيث توجد المستندات المنضمة.لن يتم المساس بالمستند المرجعي إذا لم يتم العثور على المستند المرتبط به.
فيما يلي عرض توضيحي بسيط لصلات المجموعة في غلاف websocket التفاعلي الخاص بنا:
> 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
مراجع غير صالحة:
> 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)...
لنقم بإضافة مستند آخر ثم فرز المستندات في المجموعة وفقًا للترتيب التصاعدي firstName
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
الفهارس المحددة للتجميع لتجنب مرحلة فرز المستندات المنفصلة.
OPTS = { 'skip' n | 'limit' n | 'count' | 'noidx' | 'inverse' | ORDERBY }...
skip n
تخطي سجلات n
الأولى قبل العنصر الأول في مجموعة النتائجlimit n
قم بتعيين الحد الأقصى لعدد المستندات في مجموعة النتائجcount
يُرجع count
المستندات المطابقة فقط > k query family /* | count
< k 3
< k
noidx
لا تستخدم أي فهارس لتنفيذ الاستعلام.inverse
يقوم الاستعلام افتراضيًا بمسح المستندات من المستندات المضافة مؤخرًا إلى المستندات القديمة. يعكس هذا الخيار اتجاه المسح إلى الاتجاه المعاكس وينشط وضع noidx
. ليس له أي تأثير إذا كان الاستعلام يحتوي على عبارات فرز asc/desc
. يمكن إنشاء فهرس قاعدة البيانات لأي مسار حقل JSON يحتوي على قيم رقم أو نوع سلسلة. يمكن أن يكون الفهرس unique
- لا يسمح بتكرار القيمة non unique
. يتم استخدام إشارات قناع البت لوضع الفهرس التالية (المحددة في ejdb2.h
):
وضع الفهرس | وصف |
---|---|
0x01 EJDB_IDX_UNIQUE | الفهرس فريد من نوعه |
0x04 EJDB_IDX_STR | فهرس لنوع قيمة حقل string JSON |
0x08 EJDB_IDX_I64 | فهرس لعرض 8 bytes width قيم حقل عدد صحيح |
0x10 EJDB_IDX_F64 | فهرس لعرض 8 bytes width قيم حقل النقطة العائمة الموقعة. |
على سبيل المثال، سيتم تحديد فهرس فريد لنوع السلسلة بواسطة EJDB_IDX_UNIQUE | EJDB_IDX_STR
= 0x05
. يمكن تعريف الفهرس لنوع قيمة واحد فقط يقع ضمن مسار محدد في مستند json.
يتيح تحديد فهرس سلسلة غير فريد لمسار /lastName
:
> k idx family 4 /lastName
< k
اختيار الفهرس للاستعلامات بناءً على مجموعة من القواعد الإرشادية.
يمكنك دائمًا التحقق من استخدام الفهرس عن طريق إصدار أمر explain
في 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
يتم أخذ العبارات التالية بعين الاعتبار عند استخدام فهارس EJDB2:
يمكن استخدام فهرس واحد فقط لتنفيذ استعلام معين
إذا كان الاستعلام يتكون من جزء متصل or
مرتبط في المستوى الأعلى أو يحتوي على تعبيرات negated
في المستوى الأعلى من تعبير الاستعلام - فلن يتم استخدام الفهارس على الإطلاق. لذلك لا توجد فهارس أدناه:
/[lastName != Andy]
/[lastName = "John"] or /[lastName = Peter]
ولكن سيتم استخدام فهرس /lastName
المحدد أعلاه
/[lastName = Doe]
/[lastName = Doe] and /[age = 28]
/[lastName = Doe] and not /[age = 28]
/[lastName = Doe] and /[age != 28]
العوامل التالية مدعومة بالفهارس (ejdb 2.0.x):
eq, =
gt, >
gte, >=
lt, <
lte, <=
in
~
(مطابقة البادئة منذ ejdb 2.0.53) قد تستخدم عبارات ORDERBY
الفهارس لتجنب فرز مجموعة النتائج.
يمكن أيضًا فهرسة حقول المصفوفة. دعونا نحدد حالة الاستخدام النموذجي: فهرسة بعض علامات الكيانات:
> 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
قم بإنشاء فهرس سلسلة لـ /tags
> k idx books 4 /tags
< k
تصفية الكتب حسب علامة الكتب bestseller
وإظهار استخدام الفهرس في الاستعلام:
> 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
يتم فرز جميع المستندات الموجودة في المجموعة حسب مفتاحها الأساسي بترتيب descending
. لذا، إذا كنت تستخدم المفاتيح التي تم إنشاؤها تلقائيًا ( ejdb_put_new
) فقد تكون متأكدًا من أن المستندات التي تم جلبها نتيجة لاستعلام المسح الكامل سيتم ترتيبها وفقًا لوقت الإدراج بترتيب تنازلي، إلا إذا كنت لا تستخدم فرز الاستعلام أو الفهارس أو الكلمة الأساسية inverse
.
في العديد من الحالات، قد يؤدي استخدام الفهرس إلى انخفاض أداء الاستعلام الإجمالي. لأن مجموعة الفهرس تحتوي فقط على مراجع المستندات ( id
) وقد يقوم المحرك بإجراء جلب مستند إضافي بواسطة مفتاحه الأساسي لإنهاء مطابقة الاستعلام. لذلك، بالنسبة للمجموعات غير الكبيرة، قد يكون أداء المسح الغاشمة أفضل من المسح باستخدام الفهارس. ومع ذلك، فإن عمليات المطابقة التامة: eq
و in
sorting
حسب ترتيب الفهرس الطبيعي ستستفيد من الفهرس في معظم الحالات.
إذا كنت ترغب في تحديث بعض مجموعة المستندات باستخدام عمليات apply
أو del
ولكنك لا تريد جلبها جميعًا كنتيجة للاستعلام - فما عليك سوى إضافة معدل count
إلى الاستعلام للتخلص من نقل البيانات غير الضرورية وتحويل بيانات json.
يوفر محرك EJDB القدرة على بدء عامل نقطة نهاية HTTP/Websocket منفصل للكشف عن واجهة برمجة تطبيقات الشبكة للاستعلام وتعديلات البيانات. يتم دعم SSL (TLS 1.2) بواسطة خادم jbs
.
أسهل طريقة لكشف قاعدة البيانات عبر الشبكة هي استخدام خادم jbs
المستقل. (بالطبع إذا كنت تريد تجنب تكامل 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.
يمكن حماية نقطة نهاية HTTP بواسطة رمز مميز محدد بعلامة --access
أو بنية C API EJDB_HTTP
. إذا تم تعيين رمز الوصول، فيجب على العميل توفير رأس X-Access-Token
HTTP. إذا كان الرمز المميز مطلوبًا ولكن لم يتم توفيره بواسطة العميل 401
فسيتم الإبلاغ عن كود HTTP. إذا لم يكن رمز الوصول مطابقًا للرمز المميز الذي يوفره خادم العميل، فسوف يستجيب برمز HTTP 403
.
أضف مستندًا جديدًا إلى collection
.
200
نجاح. النص: معرف مستند جديد كرقم int64
يستبدل/يخزن المستند تحت id
رقمي محدد
200
على النجاح. جسد فارغ يزيل المستند المحدد بواسطة id
من collection
200
على النجاح. جسد فارغ404
قم بتصحيح مستند تم تحديده بواسطة id
بواسطة بيانات rfc7396 وrfc6902.
200
على النجاح. جسد فارغ استرداد الوثيقة التي تم تحديدها بواسطة id
من collection
.
200
على النجاح. الجسم: نص مستند JSON.content-type:application/json
content-length:
404
الاستعلام عن مجموعة عن طريق الاستعلام المقدم كنص POST. يجب أن يحتوي نص الاستعلام على اسم المجموعة المستخدم في عنصر التصفية الأول: @collection_name/...
رؤوس الطلب:
X-Hints
بفاصلة عن التلميحات الإضافية لمحرك قاعدة بيانات ejdb2.explain
إظهار خطة تنفيذ الاستعلام قبل العنصر الأول في مجموعة النتائج مفصولاً بسطر --------------------
. إجابة:200
على النجاح.n
بالتنسيق التالي: rn<document id>t<document JSON body>
...
مثال:
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}
قم بإحضار بيانات تعريف ejdb JSON وطرق HTTP المتاحة في رأس Allow
بالاستجابة. مثال:
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 البروتوكول البسيط القائم على النص عبر بروتوكول HTTP websocket. يمكنك استخدام أداة websocket CLI التفاعلية wscat للتواصل مع الخادم يدويًا.
سيتم الرد برسالة المساعدة النصية التالية:
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>
>
لاحظ حول البادئة <key>
قبل كل أمر؛ إنه مفتاح عشوائي يختاره العميل ويخصص لتحديد طلب websocket معين، وسيتم إرجاع هذا المفتاح مع الاستجابة للطلب ويسمح للعميل بتحديد تلك الاستجابة لطلبه المحدد.
يتم إرجاع الأخطاء بالتنسيق التالي:
<key> ERROR: <error description>
<key> info
احصل على البيانات التعريفية لقاعدة البيانات كمستند JSON.
<key> get <collection> <id>
استرداد الوثيقة التي تم تحديدها بواسطة id
من collection
. إذا لم يتم العثور على الوثيقة IWKV_ERROR_NOTFOUND
سيتم إرجاعها.
مثال:
> k get family 3
< k 3 {
"firstName": "Jack",
"lastName": "Parker",
"age": 35,
"pets": [
{
"name": "Sonic",
"kind": "mouse",
"likes": []
}
]
}
إذا لم يتم العثور على المستند، فسنحصل على خطأ:
> k get family 55
< k ERROR: Key not found. (IWKV_ERROR_NOTFOUND)
>
<key> set <collection> <id> <document json>
يستبدل/يضيف مستندًا تحت id
رقمي محدد. سيتم إنشاء Collection
تلقائيًا إذا لم تكن موجودة.
<key> add <collection> <document json>
إضافة مستند جديد إلى <collection>
سيتم إنشاء id
جديد للمستند وإعادته كاستجابة. سيتم إنشاء `المجموعة> تلقائيًا في حالة عدم وجودها.
مثال:
> k add mycollection {"foo":"bar"}
< k 1
> k add mycollection {"foo":"bar"}
< k 2
>
<key> del <collection> <id>
قم بإزالة المستند المحدد بواسطة id
من collection
. إذا لم يتم العثور على الوثيقة IWKV_ERROR_NOTFOUND
سيتم إرجاعها.
<key> patch <collection> <id> <patch json>
قم بتطبيق تصحيح rfc7396 أو rfc6902 على المستند المحدد بواسطة id
. إذا لم يتم العثور على الوثيقة IWKV_ERROR_NOTFOUND
سيتم إرجاعها.
<key> query <collection> <query>
تنفيذ الاستعلام عن المستندات الموجودة في collection
المحددة. الاستجابة: مجموعة من رسائل WS ذات عناصر مستند منتهية بآخر رسالة ذات نص فارغ.
> k query family /* | /firstName
< k 4 {"firstName":"John"}
< k 3 {"firstName":"Jack"}
< k 1 {"firstName":"John"}
< k
ملاحظة حول الرسالة الأخيرة: <key>
بدون نص.
<key> explain <collection> <query>
تمامًا مثل <key> query <collection> <query>
ولكن رسالة الاستجابة الأولى ستكون مسبوقة بـ <key> explain
وتحتوي على خطة تنفيذ الاستعلام.
مثال:
> k explain family /* | /firstName
< k explain [INDEX] NO [COLLECTOR] PLAIN
< k 4 {"firstName":"John"}
< k 3 {"firstName":"Jack"}
< k 1 {"firstName":"John"}
< k
تنفيذ نص الاستعلام. يجب أن يحتوي نص الاستعلام على اسم المجموعة المستخدم في عنصر التصفية الأول: @collection_name/...
. السلوك هو نفسه بالنسبة إلى: <key> query <collection> <query>
<key> idx <collection> <mode> <path>
تأكد من وجود فهرس mode
المحدد (علامة قناع نقطي) path
json المحدد collection
. سيتم إنشاء المجموعة إذا لم تكن موجودة.
وضع الفهرس | وصف |
---|---|
0x01 EJDB_IDX_UNIQUE | الفهرس فريد من نوعه |
0x04 EJDB_IDX_STR | فهرس لنوع قيمة حقل string JSON |
0x08 EJDB_IDX_I64 | فهرس لعرض 8 bytes width قيم حقل عدد صحيح |
0x10 EJDB_IDX_F64 | فهرس لعرض 8 bytes width قيم حقل النقطة العائمة الموقعة. |
قم بتعيين فهرس السلسلة الفريد (0x01 & 0x04) = 5
في حقل /name
JSON:
k idx mycollection 5 /name
<key> rmi <collection> <mode> <path>
قم بإزالة الفهرس mode
المحدد (علامة قناع البت) path
json المحدد collection
. خطأ في الإرجاع إذا لم يتم العثور على الفهرس المحدد.
<key> rmc <collection>
إزالة المجموعة وجميع بياناتها. ملحوظة: إذا لم يتم العثور على collection
فلن يتم الإبلاغ عن أي أخطاء.
إذا كان Docker مثبتًا لديك، فيمكنك إنشاء صورة Docker وتشغيلها في حاوية
cd docker
docker build -t ejdb2 .
docker run -d -p 9191:9191 --name myEJDB ejdb2 --access myAccessKey
أو احصل على صورة ejdb2
مباشرة من Docker Hub
docker run -d -p 9191:9191 --name myEJDB softmotions/ejdb2 --access myAccessKey
يمكن تضمين EJDB في أي تطبيق C/C++
. C API
موثقة في الرؤوس التالية:
تطبيق المثال:
#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 ;
}
تجميع وتشغيل:
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.