{"id":13629390,"url":"https://github.com/Softmotions/ejdb","last_synced_at":"2025-04-17T09:33:30.348Z","repository":{"id":5250087,"uuid":"6427744","full_name":"Softmotions/ejdb","owner":"Softmotions","description":":snowboarder: EJDB2 — Embeddable JSON Database engine C library. Simple XPath like query language (JQL).","archived":false,"fork":false,"pushed_at":"2025-02-27T13:42:28.000Z","size":12030,"stargazers_count":1451,"open_issues_count":35,"forks_count":132,"subscribers_count":65,"default_branch":"master","last_synced_at":"2025-04-10T18:33:25.164Z","etag":null,"topics":["android","c","dart","database","ejdb","embedded","flutter","ios","java","jni","json","key-value","kv-store","nodejs","nosql","osx","react-native","reactnative","swift","websocket"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"Seeed-Studio/RFID_Library","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Softmotions.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2012-10-28T13:30:23.000Z","updated_at":"2025-03-25T09:54:19.000Z","dependencies_parsed_at":"2023-02-18T12:03:53.357Z","dependency_job_id":"730d6c85-f6d3-4dfb-8b90-ab41f3ab6313","html_url":"https://github.com/Softmotions/ejdb","commit_stats":{"total_commits":2661,"total_committers":31,"mean_commits":85.83870967741936,"dds":"0.14317925591882752","last_synced_commit":"37237a1d93857983be4972e26b0f28c9a1e9ec3b"},"previous_names":[],"tags_count":129,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Softmotions%2Fejdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Softmotions%2Fejdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Softmotions%2Fejdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Softmotions%2Fejdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Softmotions","download_url":"https://codeload.github.com/Softmotions/ejdb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249331627,"owners_count":21252619,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","c","dart","database","ejdb","embedded","flutter","ios","java","jni","json","key-value","kv-store","nodejs","nosql","osx","react-native","reactnative","swift","websocket"],"created_at":"2024-08-01T22:01:09.238Z","updated_at":"2025-04-17T09:33:29.550Z","avatar_url":"https://github.com/Softmotions.png","language":"C","funding_links":[],"categories":["C","Databases"],"sub_categories":[],"readme":"# EJDB 2.0\n\n**NOTE: Issues tracker is disabled. You are welcome to contribute, pull requests accepted.**\n\n[![license](https://img.shields.io/github/license/Softmotions/ejdb.svg)](https://github.com/Softmotions/ejdb/blob/master/LICENSE)\n![maintained](https://img.shields.io/maintenance/yes/2024.svg)\n\nEJDB2 is an embeddable JSON database engine published under MIT license.\n\n[The Story of the IT-depression, birds and EJDB 2.0](https://medium.com/@adamansky/ejdb2-41670e80897c)\n\n* C11 API\n* Single file database\n* Online backups support\n* 500K library size for Android\n* [iOS](https://github.com/Softmotions/EJDB2Swift) / [Android](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_android/test) / [React Native](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_react_native) / [Flutter](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_flutter) integration\n* Simple but powerful query language (JQL) as well as support of the following standards:\n  * [rfc6902](https://tools.ietf.org/html/rfc6902) JSON Patch\n  * [rfc7386](https://tools.ietf.org/html/rfc7386) JSON Merge patch\n  * [rfc6901](https://tools.ietf.org/html/rfc6901) JSON Path\n* [Support of collection joins](#jql-collection-joins)\n* Powered by [IOWOW](http://iowow.softmotions.com) - The persistent key/value storage engine\n* HTTP REST/Websockets endpoints powered by [IWNET](https://github.com/Softmotions/iwnet) and [BearSSL](https://github.com/Softmotions/BearSSL).\n* JSON documents are stored in using fast and compact [binn](https://github.com/liteserver/binn) binary format\n\n---\n* [Native language bindings](#native-language-bindings)\n* Supported platforms\n  * [macOS](#osx)\n  * [iOS](https://github.com/Softmotions/EJDB2Swift)\n  * [Linux](#linux)\n  * [Android](#android)\n  * [Windows](#windows)\n* **[JQL query language](#jql)**\n  * [Grammar](#jql-grammar)\n  * [Quick into](#jql-quick-introduction)\n  * [Data modification](#jql-data-modification)\n  * [Projections](#jql-projections)\n  * [Collection joins](#jql-collection-joins)\n  * [Sorting](#jql-sorting)\n  * [Query options](#jql-options)\n* [Indexes and performance](#jql-indexes-and-performance-tips)\n* [Network API](#http-restwebsocket-api-endpoint)\n  * [HTTP API](#http-api)\n  * [Websockets API](#websocket-api)\n* [C API](#c-api)\n* [License](#license)\n---\n\n[![EJDB2 Presentation](https://iowow.softmotions.com/articles/ejdb-presentation-cover.png)](https://iowow.softmotions.com/articles/ejdb/)\n\n## EJDB2 platforms matrix\n\n|                          | Linux              | macOS               | iOS                | Android            | Windows            |\n| ---                      | ---                | ---                 | ---                | ---                | ---                |\n| C library                | :heavy_check_mark: | :heavy_check_mark:  | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:\u003csup\u003e1\u003c/sup\u003e |\n| NodeJS                   | :heavy_check_mark: | :heavy_check_mark:  |                    |                    | :x:\u003csup\u003e3\u003c/sup\u003e    |\n| Java                     | :heavy_check_mark: | :heavy_check_mark:  |                    | :heavy_check_mark: | :heavy_check_mark:\u003csup\u003e2\u003c/sup\u003e |\n| DartVM\u003csup\u003e5\u003c/sup\u003e       | :heavy_check_mark: | :heavy_check_mark:\u003csup\u003e2\u003c/sup\u003e |         |                    | :x:\u003csup\u003e3\u003c/sup\u003e    |\n| Flutter\u003csup\u003e5\u003c/sup\u003e      |                    |                     | :heavy_check_mark: | :heavy_check_mark: |                    |\n| React Native\u003csup\u003e5\u003c/sup\u003e |                    |                     | :x:\u003csup\u003e4\u003c/sup\u003e    | :heavy_check_mark: |                    |\n| Swift\u003csup\u003e5\u003c/sup\u003e        | :heavy_check_mark: | :heavy_check_mark:  | :heavy_check_mark: |                    |                    |\n\n\u003cbr\u003e `[5]` **Bindings are unmaintained** Contributors needed.\n\u003cbr\u003e `[1]` No HTTP/Websocket support [#257](https://github.com/Softmotions/ejdb/issues/257)\n\u003cbr\u003e `[2]` Binaries are not distributed with dart `pub.` You can build it [manually](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_node#how-build-it-manually)\n\u003cbr\u003e `[3]` Can be build, but needed a linkage with windows node/dart `libs`.\n\u003cbr\u003e `[4]` Porting in progress [#273](https://github.com/Softmotions/ejdb/issues/273)\n\n## Native language bindings\n\n* [NodeJS](https://www.npmjs.com/package/ejdb2_node)\n* [Dart](https://pub.dartlang.org/packages/ejdb2_dart)\n* [Java](https://github.com/Softmotions/ejdb/blob/master/src/bindings/ejdb2_jni/README.md)\n* [Android support](#android)\n* [Swift | iOS](https://github.com/Softmotions/EJDB2Swift)\n* [React Native](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_react_native)\n* [Flutter](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_flutter)\n\n### Unofficial EJDB2 language bindings\n\n* Go\n  * https://github.com/memmaker/go-ejdb2\n* Rust \n  * https://crates.io/crates/ejdb2\n* .Net\n  * https://github.com/kmvi/ejdb2-csharp\n* Haskell\n  * https://github.com/cescobaz/ejdb2haskell\n  * https://hackage.haskell.org/package/ejdb2-binding\n* [Pharo](https://pharo.org)\n  * https://github.com/pharo-nosql/pharo-ejdb\n* Lua\n  * https://github.com/chriku/ejdb-lua\n\n## Status\n\n* **EJDB 2.0 core engine is well tested and used in various heavily loaded deployments**\n* Tested on `Linux`, `macOS` and `FreeBSD`. [Has limited Windows support](./WINDOWS.md)\n* Old EJDB 1.x version can be found in separate [ejdb_1.x](https://github.com/Softmotions/ejdb/tree/ejdb_1.x) branch.\n  We are not maintaining ejdb 1.x.\n\n## Used by\n\n* [Wirow video conferencing platform](https://github.com/wirow-io/wirow-server/)\n\nAre you using EJDB? [Let me know!](mailto:info@softmotions.com)\n\n## macOS\n\nEJDB2 code ported and tested on `High Sierra` / `Mojave` / `Catalina`\n\n[EJDB2 Swift binding](https://github.com/Softmotions/EJDB2Swift) for MacOS, iOS and Linux. \nSwift binding is outdated at now. Looking for contributors.\n\n```\nbrew install ejdb\n```\n\n## Building from sources \n\ncmake v3.24 or higher required\n\n```\ngit clone --recurse-submodules git@github.com:Softmotions/ejdb.git\n\nmkdir build \u0026\u0026 cd build\ncmake .. -DCMAKE_BUILD_TYPE=Release\nmake install\n```\n\n## Linux\n\n#### Building debian packages\n\n```sh\nmkdir build \u0026\u0026 cd build\ncmake .. -DCMAKE_BUILD_TYPE=Release -DPACKAGE_DEB=ON\nmake package\n```\n\n#### RPM based Linux distributions\n```sh\nmkdir build \u0026\u0026 cd build\ncmake .. -DCMAKE_BUILD_TYPE=Release -DPACKAGE_RPM=ON\nmake package\n```\n\n## Windows\nEJDB2 can be cross-compiled for windows\n\n**Note:** HTTP/Websocket network API is disabled and not yet supported\n\nNodejs/Dart bindings not yet ported to Windows.\n\n**[Cross-compilation Guide for Windows](./WINDOWS.md)**\n\n\n## IWSTART\n\nIWSTART is an automatic CMake initial project generator for C projects based on [iowow](https://github.com/Softmotions/iowow) / [iwnet](https://github.com/Softmotions/iwnet) / [ejdb2](https://github.com/Softmotions/ejdb) libs.\n\nhttps://github.com/Softmotions/iwstart\n\n\n# WARNING: Unsupported now. Maintainer needed.\n\n# Android\n\n* [Flutter binding](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_flutter)\n* [React Native binding](https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_react_native)\n\n## Sample Android application\n\n* https://github.com/Softmotions/ejdb/tree/master/src/bindings/ejdb2_android/test\n\n* https://github.com/Softmotions/ejdb_android_todo_app\n\n\n# JQL\n\nEJDB query language (JQL) syntax inspired by ideas behind XPath and Unix shell pipes.\nIt designed for easy querying and updating sets of JSON documents.\n\n## JQL grammar\n\nJQL parser created created by\n[peg/leg — recursive-descent parser generators for C](http://piumarta.com/software/peg/) Here is the formal parser grammar: https://github.com/Softmotions/ejdb/blob/master/src/jql/jqp.leg\n\n## Non formal JQL grammar adapted for brief overview\n\nNotation used below is based on SQL syntax description:\n\nRule | Description\n--- | ---\n`' '` | String in single quotes denotes unquoted string literal as part of query.\n\u003ccode\u003e{ a \u0026#124; b }\u003c/code\u003e | Curly brackets enclose two or more required alternative choices, separated by vertical bars.\n\u003ccode\u003e[ ]\u003c/code\u003e | Square brackets indicate an optional element or clause. Multiple elements or clauses are separated by vertical bars.\n\u003ccode\u003e\u0026#124;\u003c/code\u003e | Vertical bars separate two or more alternative syntax elements.\n\u003ccode\u003e...\u003c/code\u003e |  Ellipses indicate that the preceding element can be repeated. The repetition is unlimited unless otherwise indicated.\n\u003ccode\u003e( )\u003c/code\u003e | Parentheses are grouping symbols.\nUnquoted word in lower case| Denotes semantic of some query part. For example: `placeholder_name` - name of any placeholder.\n```\nQUERY = FILTERS [ '|' APPLY ] [ '|' PROJECTIONS ] [ '|' OPTS ];\n\nSTR = { quoted_string | unquoted_string };\n\nJSONVAL = json_value;\n\nPLACEHOLDER = { ':'placeholder_name | '?' }\n\nFILTERS = FILTER [{ and | or } [ not ] FILTER];\n\n  FILTER = [@collection_name]/NODE[/NODE]...;\n\n  NODE = { '*' | '**' | NODE_EXPRESSION | STR };\n\n  NODE_EXPRESSION = '[' NODE_EXPR_LEFT OP NODE_EXPR_RIGHT ']'\n                        [{ and | or } [ not ] NODE_EXPRESSION]...;\n\n  OP =   [ '!' ] { '=' | '\u003e=' | '\u003c=' | '\u003e' | '\u003c' | ~ }\n      | [ '!' ] { 'eq' | 'gte' | 'lte' | 'gt' | 'lt' }\n      | [ not ] { 'in' | 'ni' | 're' };\n\n  NODE_EXPR_LEFT = { '*' | '**' | STR | NODE_KEY_EXPR };\n\n  NODE_KEY_EXPR = '[' '*' OP NODE_EXPR_RIGHT ']'\n\n  NODE_EXPR_RIGHT =  JSONVAL | STR | PLACEHOLDER\n\nAPPLY = { 'apply' | 'upsert' } { PLACEHOLDER | json_object | json_array  } | 'del'\n\nOPTS = { 'skip' n | 'limit' n | 'count' | 'noidx' | 'inverse' | ORDERBY }...\n\n  ORDERBY = { 'asc' | 'desc' } PLACEHOLDER | json_path\n\nPROJECTIONS = PROJECTION [ {'+' | '-'} PROJECTION ]\n\n  PROJECTION = 'all' | json_path\n\n```\n\n* `json_value`: Any valid JSON value: object, array, string, bool, number.\n* `json_path`: Simplified JSON pointer. Eg.: `/foo/bar` or `/foo/\"bar with spaces\"/`\n* `*` in context of `NODE`: Any JSON object key name at particular nesting level.\n* `**` in context of `NODE`: Any JSON object key name at arbitrary nesting level.\n* `*` in context of `NODE_EXPR_LEFT`: Key name at specific level.\n* `**` in context of `NODE_EXPR_LEFT`: Nested array value of array element under specific key.\n\n## JQL quick introduction\n\nLets play with some very basic data and queries.\nFor simplicity we will use ejdb websocket network API which provides us a kind of interactive CLI. The same job can be done using pure `C` API too (`ejdb2.h jql.h`).\n\nNOTE: Take a look into [JQL test cases](https://github.com/Softmotions/ejdb/blob/master/src/jql/tests/jql_test1.c) for more examples.\n\n```json\n{\n  \"firstName\": \"John\",\n  \"lastName\": \"Doe\",\n  \"age\": 28,\n  \"pets\": [\n    {\"name\": \"Rexy rex\", \"kind\": \"dog\", \"likes\": [\"bones\", \"jumping\", \"toys\"]},\n    {\"name\": \"Grenny\", \"kind\": \"parrot\", \"likes\": [\"green color\", \"night\", \"toys\"]}\n  ]\n}\n```\nSave json as `sample.json` then upload it the `family` collection:\n\n```sh\n# Start HTTP/WS server protected by some access token\n./jbs -a 'myaccess01'\n8 Mar 16:15:58.601 INFO: HTTP/WS endpoint at localhost:9191\n```\n\nServer can be accessed using HTTP or Websocket endpoint. [More info](https://github.com/Softmotions/ejdb/blob/master/src/jbr/README.md)\n\n```sh\ncurl -d '@sample.json' -H'X-Access-Token:myaccess01' -X POST http://localhost:9191/family\n```\n\nWe can play around using interactive [wscat](https://www.npmjs.com/package/@softmotions/wscat) websocket client.\n\n```sh\nwscat  -H 'X-Access-Token:myaccess01' -c http://localhost:9191\nconnected (press CTRL+C to quit)\n\u003e k info\n\u003c k     {\n \"version\": \"2.0.0\",\n \"file\": \"db.jb\",\n \"size\": 8192,\n \"collections\": [\n  {\n   \"name\": \"family\",\n   \"dbid\": 3,\n   \"rnum\": 1,\n   \"indexes\": []\n  }\n ]\n}\n\n\u003e k get family 1\n\u003c k     1       {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"age\": 28,\n \"pets\": [\n  {\n   \"name\": \"Rexy rex\",\n   \"kind\": \"dog\",\n   \"likes\": [\n    \"bones\",\n    \"jumping\",\n    \"toys\"\n   ]\n  },\n  {\n   \"name\": \"Grenny\",\n   \"kind\": \"parrot\",\n   \"likes\": [\n    \"green color\",\n    \"night\",\n    \"toys\"\n   ]\n  }\n ]\n}\n```\n\nNote about the `k` prefix before every command; It is an arbitrary key chosen by client and designated to identify particular\nwebsocket request, this key will be returned with response to request and allows client to\nidentify that response for his particular request. [More info](https://github.com/Softmotions/ejdb/blob/master/src/jbr/README.md)\n\nQuery command over websocket has the following format:\n\n```\n\u003ckey\u003e query \u003ccollection\u003e \u003cquery\u003e\n```\n\nSo we will consider only `\u003cquery\u003e` part in this document.\n\n### Get all elements in collection\n```\nk query family /*\n```\nor\n```\nk query family /**\n```\nor specify collection name in query explicitly\n```\nk @family/*\n```\n\nWe can execute query by HTTP `POST` request\n```\ncurl --data-raw '@family/[firstName = John]' -H'X-Access-Token:myaccess01' -X POST http://localhost:9191\n\n1\t{\"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\"]}]}\n```\n\n### Set the maximum number of elements in result set\n\n```\nk @family/* | limit 10\n```\n\n### Get documents where specified json path exists\n\nElement at index `1` exists in `likes` array within a `pets` sub-object\n```\n\u003e k query family /pets/*/likes/1\n\u003c k     1       {\"firstName\":\"John\"...\n```\n\nElement at index `1` exists in `likes` array at any `likes` nesting level\n```\n\u003e k query family /**/likes/1\n\u003c k     1       {\"firstName\":\"John\"...\n```\n\n**From this point and below I will omit websocket specific prefix `k query family` and\nconsider only JQL queries.**\n\n\n### Get documents by primary key\n\nIn order to get documents by primary key the following options are available:\n\n1. Use API call `ejdb_get()`\n    ```ts\n     const doc = await db.get('users', 112);\n    ```\n\n1. Use the special query construction: `/=:?` or `@collection/=:?`\n\nGet document from `users` collection with primary key `112`\n```\n\u003e k @users/=112\n```\n\nUpdate tags array for document in `jobs` collection (TypeScript):\n```ts\n await db.createQuery('@jobs/ = :? | apply :? | count')\n    .setNumber(0, id)\n    .setJSON(1, { tags })\n    .completionPromise();\n```\n\nArray of primary keys can also be used for matching:\n\n```ts\n await db.createQuery('@jobs/ = :?| apply :? | count')\n    .setJSON(0, [23, 1, 2])\n    .setJSON(1, { tags })\n    .completionPromise();\n```\n\n### Matching JSON entry values\n\nBelow is a set of self explaining queries:\n\n```\n/pets/*/[name = \"Rexy rex\"]\n\n/pets/*/[name eq \"Rexy rex\"]\n\n/pets/*/[name = \"Rexy rex\" or name = Grenny]\n```\nNote about quotes around words with spaces.\n\nGet all documents where owner `age` greater than `20` and have some pet who like `bones` or `toys`\n```\n/[age \u003e 20] and /pets/*/likes/[** in [\"bones\", \"toys\"]]\n```\nHere `**` denotes some element in `likes` array.\n\n`ni` is the inverse operator to `in`.\nGet documents where `bones` somewhere in `likes` array.\n```\n/pets/*/[likes ni \"bones\"]\n```\n\nWe can create more complicated filters\n```\n( /[age \u003c= 20] or /[lastName re \"Do.*\"] )\n  and /pets/*/likes/[** in [\"bones\", \"toys\"]]\n```\nNote about grouping parentheses and regular expression matching using `re` operator.\n\n`~` is a prefix matching operator (Since ejdb `v2.0.53`).\nPrefix matching can benefit from using indexes.\n\nGet documents where `/lastName` starts with `\"Do\"`.\n```\n/[lastName ~ Do]\n```\n\n### Arrays and maps can be matched as is\n\nFilter documents with `likes` array exactly matched to `[\"bones\",\"jumping\",\"toys\"]`\n```\n/**/[likes = [\"bones\",\"jumping\",\"toys\"]]\n```\nMatching algorithms for arrays and maps are different:\n\n* Array elements are matched from start to end. In equal arrays\n  all values at the same index should be equal.\n* Object maps matching consists of the following steps:\n  * Lexicographically sort object keys in both maps.\n  * Do matching keys and its values starting from the lowest key.\n  * If all corresponding keys and values in one map are fully matched to ones in other\n    and vice versa, maps considered to be equal.\n    For example: `{\"f\":\"d\",\"e\":\"j\"}` and `{\"e\":\"j\",\"f\":\"d\"}` are equal maps.\n\n### Conditions on key names\n\nFind JSON document having `firstName` key at root level.\n```\n/[* = \"firstName\"]\n```\nI this context `*` denotes a key name.\n\nYou can use conditions on key name and key value at the same time:\n```\n/[[* = \"firstName\"] = John]\n```\n\nKey name can be either `firstName` or `lastName` but should have `John` value in any case.\n```\n/[[* in [\"firstName\", \"lastName\"]] = John]\n```\n\nIt may be useful in queries with dynamic placeholders (C API):\n```\n/[[* = :keyName] = :keyValue]\n```\n\n## JQL data modification\n\n`APPLY` section responsible for modification of documents content.\n\n```\nAPPLY = ({'apply' | `upsert`} { PLACEHOLDER | json_object | json_array  }) | 'del'\n```\n\nJSON patch specs conformed to `rfc7386` or `rfc6902` specifications followed after `apply` keyword.\n\nLet's add `address` object to all matched document\n```\n/[firstName = John] | apply {\"address\":{\"city\":\"New York\", \"street\":\"\"}}\n```\n\nIf JSON object is an argument of `apply` section it will be treated as merge match (`rfc7386`) otherwise\nit should be array which denotes `rfc6902` JSON patch. Placeholders also supported by `apply` section.\n```\n/* | apply :?\n```\n\nSet the street name in `address`\n```\n/[firstName = John] | apply [{\"op\":\"replace\", \"path\":\"/address/street\", \"value\":\"Fifth Avenue\"}]\n```\n\nAdd `Neo` fish to the set of John's `pets`\n```\n/[firstName = John]\n| apply [{\"op\":\"add\", \"path\":\"/pets/-\", \"value\": {\"name\":\"Neo\", \"kind\":\"fish\"}}]\n```\n\n`upsert` updates existing document by given json argument used as merge patch\n         or inserts provided json argument as new document instance.\n\n```\n/[firstName = John] | upsert {\"firstName\": \"John\", \"address\":{\"city\":\"New York\"}}\n```\n\n### Non standard JSON patch extensions\n\n#### increment\n\nIncrements numeric value identified by JSON path by specified value.\n\nExample:\n```\n Document:  {\"foo\": 1}\n Patch:     [{\"op\": \"increment\", \"path\": \"/foo\", \"value\": 2}]\n Result:    {\"foo\": 3}\n```\n#### add_create\n\nSame as JSON patch `add` but creates intermediate object nodes for missing JSON path segments.\n\nExample:\n```\nDocument: {\"foo\": {\"bar\": 1}}\nPatch:    [{\"op\": \"add_create\", \"path\": \"/foo/zaz/gaz\", \"value\": 22}]\nResult:   {\"foo\":{\"bar\":1,\"zaz\":{\"gaz\":22}}}\n```\n\nExample:\n```\nDocument: {\"foo\": {\"bar\": 1}}\nPatch:    [{\"op\": \"add_create\", \"path\": \"/foo/bar/gaz\", \"value\": 22}]\nResult:   Error since element pointed by /foo/bar is not an object\n```\n\n#### swap\n\nSwaps two values of JSON document starting from `from` path.\n\nSwapping rules\n\n1. If value pointed by `from` not exists error will be raised.\n1. If value pointed by `path` not exists it will be set by value from `from` path,\n  then object pointed by `from` path will be removed.\n1. If both values pointed by `from` and `path` are presented they will be swapped.\n\nExample:\n\n```\nDocument: {\"foo\": [\"bar\"], \"baz\": {\"gaz\": 11}}\nPatch:    [{\"op\": \"swap\", \"from\": \"/foo/0\", \"path\": \"/baz/gaz\"}]\nResult:   {\"foo\": [11], \"baz\": {\"gaz\": \"bar\"}}\n```\n\nExample (Demo of rule 2):\n\n```\nDocument: {\"foo\": [\"bar\"], \"baz\": {\"gaz\": 11}}\nPatch:    [{\"op\": \"swap\", \"from\": \"/foo/0\", \"path\": \"/baz/zaz\"}]\nResult:   {\"foo\":[],\"baz\":{\"gaz\":11,\"zaz\":\"bar\"}}\n```\n\n### Removing documents\n\nUse `del` keyword to remove matched elements from collection:\n```\n/FILTERS | del\n```\n\nExample:\n```\n\u003e k add family {\"firstName\":\"Jack\"}\n\u003c k     2\n\u003e k query family /[firstName re \"Ja.*\"]\n\u003c k     2       {\"firstName\":\"Jack\"}\n\n# Remove selected elements from collection\n\u003e k query family /[firstName=Jack] | del\n\u003c k     2       {\"firstName\":\"Jack\"}\n```\n\n## JQL projections\n\n```\nPROJECTIONS = PROJECTION [ {'+' | '-'} PROJECTION ]\n\n  PROJECTION = 'all' | json_path | join_clause\n```\n\nProjection allows to get only subset of JSON document excluding not needed data.\n\n**Query placeholders API is supported in projections.**\n\nLets add one more document to our collection:\n\n```sh\n$ cat \u003c\u003c EOF | curl -d @- -H'X-Access-Token:myaccess01' -X POST http://localhost:9191/family\n{\n\"firstName\":\"Jack\",\n\"lastName\":\"Parker\",\n\"age\":35,\n\"pets\":[{\"name\":\"Sonic\", \"kind\":\"mouse\", \"likes\":[]}]\n}\nEOF\n```\nNow query only pet owners firstName and lastName from collection.\n\n```\n\u003e k query family /* | /{firstName,lastName}\n\n\u003c k     3       {\"firstName\":\"Jack\",\"lastName\":\"Parker\"}\n\u003c k     1       {\"firstName\":\"John\",\"lastName\":\"Doe\"}\n\u003c k\n```\n\nAdd `pets` array for every document\n```\n\u003e k query family /* | /{firstName,lastName} + /pets\n\n\u003c k     3       {\"firstName\":\"Jack\",\"lastName\":\"Parker\",\"pets\":[...\n\u003c k     1       {\"firstName\":\"John\",\"lastName\":\"Doe\",\"pets\":[...\n```\n\nExclude only `pets` field from documents\n```\n\u003e k query family /* | all - /pets\n\n\u003c k     3       {\"firstName\":\"Jack\",\"lastName\":\"Parker\",\"age\":35}\n\u003c k     1       {\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":28,\"address\":{\"city\":\"New York\",\"street\":\"Fifth Avenue\"}}\n\u003c k\n```\nHere `all` keyword used denoting whole document.\n\nGet `age` and the first pet in `pets` array.\n```\n\u003e k query family /[age \u003e 20] | /age + /pets/0\n\n\u003c k     3       {\"age\":35,\"pets\":[{\"name\":\"Sonic\",\"kind\":\"mouse\",\"likes\":[]}]}\n\u003c k     1       {\"age\":28,\"pets\":[{\"name\":\"Rexy rex\",\"kind\":\"dog\",\"likes\":[\"bones\",\"jumping\",\"toys\"]}]}\n\u003c k\n```\n\n## JQL collection joins\n\nJoin materializes reference to document to a real document objects which will replace reference inplace.\n\nDocuments are joined by their primary keys only.\n\nReference keys should be stored in referrer document as number or string field.\n\nJoins can be specified as part of projection expression\nin the following form:\n\n```\n/.../field\u003ccollection\n```\nWhere\n\n* `field` \u0026dash; JSON field contains primary key of joined document.\n* `\u003c` \u0026dash; The special mark symbol which instructs EJDB engine to replace `field` key by body of joined document.\n* `collection` \u0026dash; name of DB collection where joined documents located.\n\nA referrer document will be untouched if associated document is not found.\n\nHere is the simple demonstration of collection joins in our interactive websocket shell:\n\n```\n\u003e k add artists {\"name\":\"Leonardo Da Vinci\", \"years\":[1452,1519]}\n\u003c k     1\n\u003e k add paintings {\"name\":\"Mona Lisa\", \"year\":1490, \"origin\":\"Italy\", \"artist\": 1}\n\u003c k     1\n\u003e k add paintings {\"name\":\"Madonna Litta - Madonna And The Child\", \"year\":1490, \"origin\":\"Italy\", \"artist\": 1}\n\u003c k     2\n\n# Lists paintings documents\n\n\u003e k @paintings/*\n\u003c k     2       {\"name\":\"Madonna Litta - Madonna And The Child\",\"year\":1490,\"origin\":\"Italy\",\"artist\":1}\n\u003c k     1       {\"name\":\"Mona Lisa\",\"year\":1490,\"origin\":\"Italy\",\"artist\":1}\n\u003c k\n\u003e\n\n# Do simple join with artists collection\n\n\u003e k @paintings/* | /artist\u003cartists\n\u003c k     2       {\"name\":\"Madonna Litta - Madonna And The Child\",\"year\":1490,\"origin\":\"Italy\",\n                  \"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\n\u003c k     1       {\"name\":\"Mona Lisa\",\"year\":1490,\"origin\":\"Italy\",\n                  \"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\u003c k\n\n\n# Strip all document fields except `name` and `artist` join\n\n\u003e k @paintings/* | /artist\u003cartists + /name + /artist/*\n\u003c k     2       {\"name\":\"Madonna Litta - Madonna And The Child\",\"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\u003c k     1       {\"name\":\"Mona Lisa\",\"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\u003c k\n\u003e\n\n# Same results as above:\n\n\u003e k @paintings/* | /{name, artist\u003cartists} + /artist/*\n\u003c k     2       {\"name\":\"Madonna Litta - Madonna And The Child\",\"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\u003c k     1       {\"name\":\"Mona Lisa\",\"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\u003c k\n\n```\n\nInvalid references:\n\n```\n\u003e  k add paintings {\"name\":\"Mona Lisa2\", \"year\":1490, \"origin\":\"Italy\", \"artist\": 9999}\n\u003c k     3\n\u003e k @paintings/* |  /artist\u003cartists\n\u003c k     3       {\"name\":\"Mona Lisa2\",\"year\":1490,\"origin\":\"Italy\",\"artist\":9999}\n\u003c k     2       {\"name\":\"Madonna Litta - Madonna And The Child\",\"year\":1490,\"origin\":\"Italy\",\"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\u003c k     1       {\"name\":\"Mona Lisa\",\"year\":1490,\"origin\":\"Italy\",\"artist\":{\"name\":\"Leonardo Da Vinci\",\"years\":[1452,1519]}}\n\n```\n\n## JQL results ordering\n\n```\n  ORDERBY = ({ 'asc' | 'desc' } PLACEHOLDER | json_path)...\n```\n\nLets add one more document then sort documents in collection according to `firstName` ascending and `age` descending order.\n\n```\n\u003e k add family {\"firstName\":\"John\", \"lastName\":\"Ryan\", \"age\":39}\n\u003c k     4\n```\n\n```\n\u003e k query family /* | /{firstName,lastName,age} | asc /firstName desc /age\n\u003c k     3       {\"firstName\":\"Jack\",\"lastName\":\"Parker\",\"age\":35}\n\u003c k     4       {\"firstName\":\"John\",\"lastName\":\"Ryan\",\"age\":39}\n\u003c k     1       {\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":28}\n\u003c k\n```\n\n`asc, desc` instructions may use indexes defined for collection to avoid a separate documents sorting stage.\n\n## JQL Options\n\n```\nOPTS = { 'skip' n | 'limit' n | 'count' | 'noidx' | 'inverse' | ORDERBY }...\n```\n\n* `skip n` Skip first `n` records before first element in result set\n* `limit n` Set max number of documents in result set\n* `count` Returns only `count` of matched documents\n  ```\n  \u003e k query family /* | count\n  \u003c k     3\n  \u003c k\n  ```\n* `noidx` Do not use any indexes for query execution.\n* `inverse` By default query scans documents from most recently added to older ones.\n   This option inverts scan direction to opposite and activates `noidx` mode.\n   Has no effect if query has `asc/desc` sorting clauses.\n\n## JQL Indexes and performance tips\n\nDatabase index can be build for any JSON field path containing values of number or string type.\nIndex can be an `unique` \u0026dash; not allowing value duplication and `non unique`.\nThe following index mode bit mask flags are used (defined in `ejdb2.h`):\n\nIndex mode | Description\n--- | ---\n\u003ccode\u003e0x01 EJDB_IDX_UNIQUE\u003c/code\u003e | Index is unique\n\u003ccode\u003e0x04 EJDB_IDX_STR\u003c/code\u003e | Index for JSON `string` field value type\n\u003ccode\u003e0x08 EJDB_IDX_I64\u003c/code\u003e | Index for `8 bytes width` signed integer field values\n\u003ccode\u003e0x10 EJDB_IDX_F64\u003c/code\u003e | Index for `8 bytes width` signed floating point field values.\n\nFor example unique index of string type will be specified by `EJDB_IDX_UNIQUE | EJDB_IDX_STR` = `0x05`.\nIndex can be defined for only one value type located under specific path in json document.\n\nLets define non unique string index for `/lastName` path:\n```\n\u003e k idx family 4 /lastName\n\u003c k\n```\nIndex selection for queries based on set of heuristic rules.\n\nYou can always check index usage by issuing `explain` command in WS API:\n```\n\u003e k explain family /[lastName=Doe] and /[age!=27]\n\u003c k     explain [INDEX] MATCHED  STR|3 /lastName EXPR1: 'lastName = Doe' INIT: IWKV_CURSOR_EQ\n[INDEX] SELECTED STR|3 /lastName EXPR1: 'lastName = Doe' INIT: IWKV_CURSOR_EQ\n [COLLECTOR] PLAIN\n```\n\nThe following statements are taken into account when using EJDB2 indexes:\n* Only one index can be used for particular query execution\n* If query consist of `or` joined part at top level or contains `negated` expressions at the top level\n  of query expression - indexes will not be in use at all.\n  So no indexes below:\n  ```\n  /[lastName != Andy]\n\n  /[lastName = \"John\"] or /[lastName = Peter]\n\n  ```\n  But will be used `/lastName` index defined above\n  ```\n  /[lastName = Doe]\n\n  /[lastName = Doe] and /[age = 28]\n\n  /[lastName = Doe] and not /[age = 28]\n\n  /[lastName = Doe] and /[age != 28]\n  ```\n* The following operators are supported by indexes (ejdb 2.0.x):\n  * `eq, =`\n  * `gt, \u003e`\n  * `gte, \u003e=`\n  * `lt, \u003c`\n  * `lte, \u003c=`\n  * `in`\n  * `~` (Prefix matching since ejdb 2.0.53)\n\n* `ORDERBY` clauses may use indexes to avoid result set sorting.\n* Array fields can also be indexed. Let's outline typical use case: indexing of some entity tags:\n  ```\n  \u003e k add books {\"name\":\"Mastering Ultra\", \"tags\":[\"ultra\", \"language\", \"bestseller\"]}\n  \u003c k     1\n  \u003e k add books {\"name\":\"Learn something in 24 hours\", \"tags\":[\"bestseller\"]}\n  \u003c k     2\n  \u003e k query books /*\n  \u003c k     2       {\"name\":\"Learn something in 24 hours\",\"tags\":[\"bestseller\"]}\n  \u003c k     1       {\"name\":\"Mastering Ultra\",\"tags\":[\"ultra\",\"language\",\"bestseller\"]}\n  \u003c k\n  ```\n  Create string index for `/tags`\n  ```\n  \u003e k idx books 4 /tags\n  \u003c k\n  ```\n  Filter books by `bestseller` tag and show index usage in query:\n  ```\n  \u003e k explain books /tags/[** in [\"bestseller\"]]\n  \u003c k     explain [INDEX] MATCHED  STR|4 /tags EXPR1: '** in [\"bestseller\"]' INIT: IWKV_CURSOR_EQ\n  [INDEX] SELECTED STR|4 /tags EXPR1: '** in [\"bestseller\"]' INIT: IWKV_CURSOR_EQ\n  [COLLECTOR] PLAIN\n\n  \u003c k     1       {\"name\":\"Mastering Ultra\",\"tags\":[\"ultra\",\"language\",\"bestseller\"]}\n  \u003c k     2       {\"name\":\"Learn something in 24 hours\",\"tags\":[\"bestseller\"]}\n  \u003c k\n  ```\n\n### Performance tip: Physical ordering of documents\n\nAll documents in collection are sorted by their primary key in `descending` order.\nSo if you use auto generated keys (`ejdb_put_new`) you may be sure what documents fetched as result of\nfull scan query will be ordered according to the time of insertion in descendant order,\nunless you don't use query sorting, indexes or `inverse` keyword.\n\n### Performance tip: Brute force scan vs indexed access\n\nIn many cases, using index may drop down the overall query performance.\nBecause index collection contains only document references (`id`) and engine may perform\nan addition document fetching by its primary key to finish query matching.\nSo for not so large collections a brute scan may perform better than scan using indexes.\nHowever, exact matching operations: `eq`, `in` and `sorting` by natural index order\nwill benefit from index in most cases.\n\n\n### Performance tip: Get rid of unnecessary document data\n\nIf you'd like update some set of documents with `apply` or `del` operations\nbut don't want fetching all of them as result of query - just add `count`\nmodifier to the query to get rid of unnecessary data transferring and json data conversion.\n\n\n\n# HTTP REST/Websocket API endpoint\n\nEJDB engine provides the ability to start a separate HTTP/Websocket endpoint worker exposing network API for quering and data modifications.\nSSL (TLS 1.2) is supported by `jbs` server.\n\nThe easiest way to expose database over the network is use the standalone `jbs` server. (Of course if you want to avoid `C API` integration).\n\n## jbs server\n\n```\nUsage:\n\n\t ./jbs [options]\n\n\t-v, --version\t\tPrint program version.\n\t-f, --file=\u003c\u003e\t\tDatabase file path. Default: ejdb2.db\n\t-p, --port=NUM\t\tHTTP server port numer. Default: 9191\n\t-l, --listen=\u003c\u003e\t\tNetwork address server will listen. Default: localhost\n\t-k, --key=\u003c\u003e\t\tPEM private key file for TLS 1.2 HTTP server.\n\t-c, --certs=\u003c\u003e\t\tPEM certificates file for TLS 1.2 HTTP server.\n\t-a, --access=TOKEN|@FILE\t\tAccess token to match 'X-Access-Token' HTTP header value.\n\t-r, --access-read\t\tAllows unrestricted read-only data access.\n\t-C, --cors\t\tEnable COSR response headers for HTTP server\n\t-t, --trunc\t\tCleanup/reset database file on open.\n\t-w, --wal\t\tuse the write ahead log (WAL). Used to provide data durability.\n\nAdvanced options:\n\t-S, --sbz=NUM\t\tMax sorting buffer size. If exceeded, an overflow temp file for data will be created.\n                  Default: 16777216, min: 1048576\n\t-D, --dsz=NUM\t\tInitial size of buffer to process/store document on queries. Preferable average size of document. \n                  Default: 65536, min: 16384\n\t-T, --trylock Exit with error if database is locked by another process. \n                If not set, current process will wait for lock release.\n\n```\n\n## HTTP API\n\nHTTP endpoint may be protected by a token specified with `--access` flag or C API `EJDB_HTTP` struct. \nIf access token was set, client should provide `X-Access-Token` HTTP header.\nIf token is required but not provided by client `401` HTTP code will be reported. \nIf access token is not matched to the token provided by client server will respond with `403` HTTP code.\n\n## REST API\n\n### POST /{collection}\nAdd a new document to the `collection`.\n* `200` success. Body: a new document identifier as `int64` number\n\n### PUT /{collection}/{id}\nReplaces/store document under specific numeric `id`\n* `200` on success. Empty body\n\n### DELETE /{collection}/{id}\nRemoves document identified by `id` from a `collection`\n* `200` on success. Empty body\n* `404` document not found\n\n### PATCH /{collection}/{id}\nPatch a document identified by `id` by [rfc7396](https://tools.ietf.org/html/rfc7396),\n[rfc6902](https://tools.ietf.org/html/rfc6902) data.\n* `200` on success. Empty body\n\n### GET | HEAD /{collections}/{id}\nRetrieve document identified by `id` from a `collection`.\n* `200` on success. Body: JSON document text.\n  * `content-type:application/json`\n  * `content-length:`\n* `404` document not found\n\n### POST /\nQuery a collection by provided query as POST body.\nBody of query should contains collection name in use in the first filter element: `@collection_name/...`\nRequest headers:\n* `X-Hints` comma separated extra hints to ejdb2 database engine.\n  * `explain` Show query execution plan before first element in result set separated by `--------------------` line.\nResponse:\n* Response data transfered using [HTTP chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding)\n* `200` on success.\n* JSON documents separated by `\\n` in the following format:\n  ```\n  \\r\\n\u003cdocument id\u003e\\t\u003cdocument JSON body\u003e\n  ...\n  ```\n\nExample:\n\n```\ncurl -v --data-raw '@family/[age \u003e 18]' -H 'X-Access-Token:myaccess01' http://localhost:9191\n* Rebuilt URL to: http://localhost:9191/\n*   Trying 127.0.0.1...\n* TCP_NODELAY set\n* Connected to localhost (127.0.0.1) port 9191 (#0)\n\u003e POST / HTTP/1.1\n\u003e Host: localhost:9191\n\u003e User-Agent: curl/7.58.0\n\u003e Accept: */*\n\u003e X-Access-Token:myaccess01\n\u003e Content-Length: 18\n\u003e Content-Type: application/x-www-form-urlencoded\n\u003e\n* upload completely sent off: 18 out of 18 bytes\n\u003c HTTP/1.1 200 OK\n\u003c connection:keep-alive\n\u003c content-type:application/json\n\u003c transfer-encoding:chunked\n\u003c\n\n4\t{\"firstName\":\"John\",\"lastName\":\"Ryan\",\"age\":39}\n3\t{\"firstName\":\"Jack\",\"lastName\":\"Parker\",\"age\":35,\"pets\":[{\"name\":\"Sonic\",\"kind\":\"mouse\",\"likes\":[]}]}\n1\t{\"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\"}}\n* Connection #0 to host localhost left intact\n```\n\n```\ncurl --data-raw '@family/[lastName = \"Ryan\"]' -H 'X-Access-Token:myaccess01' -H 'X-Hints:explain' http://localhost:9191\n[INDEX] MATCHED  STR|3 /lastName EXPR1: 'lastName = \"Ryan\"' INIT: IWKV_CURSOR_EQ\n[INDEX] SELECTED STR|3 /lastName EXPR1: 'lastName = \"Ryan\"' INIT: IWKV_CURSOR_EQ\n [COLLECTOR] PLAIN\n--------------------\n4\t{\"firstName\":\"John\",\"lastName\":\"Ryan\",\"age\":39}\n```\n\n### OPTIONS /\nFetch ejdb JSON metadata and available HTTP methods in `Allow` response header.\nExample:\n```\ncurl -X OPTIONS -H 'X-Access-Token:myaccess01'  http://localhost:9191/\n{\n \"version\": \"2.0.0\",\n \"file\": \"db.jb\",\n \"size\": 16384,\n \"collections\": [\n  {\n   \"name\": \"family\",\n   \"dbid\": 3,\n   \"rnum\": 3,\n   \"indexes\": [\n    {\n     \"ptr\": \"/lastName\",\n     \"mode\": 4,\n     \"idbf\": 64,\n     \"dbid\": 4,\n     \"rnum\": 3\n    }\n   ]\n  }\n ]\n}\n```\n\n## Websocket API\n\nEJDB supports simple text based protocol over HTTP websocket protocol.\nYou can use interactive websocket CLI tool [wscat](https://www.npmjs.com/package/@softmotions/wscat) to communicate with server by hands.\n\n### Commands\n\n#### ?\nWill respond with the following help text message:\n```\nwscat  -H 'X-Access-Token:myaccess01' -c http://localhost:9191\n\u003e ?\n\u003c\n\u003ckey\u003e info\n\u003ckey\u003e get     \u003ccollection\u003e \u003cid\u003e\n\u003ckey\u003e set     \u003ccollection\u003e \u003cid\u003e \u003cdocument json\u003e\n\u003ckey\u003e add     \u003ccollection\u003e \u003cdocument json\u003e\n\u003ckey\u003e del     \u003ccollection\u003e \u003cid\u003e\n\u003ckey\u003e patch   \u003ccollection\u003e \u003cid\u003e \u003cpatch json\u003e\n\u003ckey\u003e idx     \u003ccollection\u003e \u003cmode\u003e \u003cpath\u003e\n\u003ckey\u003e rmi     \u003ccollection\u003e \u003cmode\u003e \u003cpath\u003e\n\u003ckey\u003e rmc     \u003ccollection\u003e\n\u003ckey\u003e query   \u003ccollection\u003e \u003cquery\u003e\n\u003ckey\u003e explain \u003ccollection\u003e \u003cquery\u003e\n\u003ckey\u003e \u003cquery\u003e\n\u003e\n```\n\nNote about `\u003ckey\u003e` prefix before every command; It is an arbitrary key chosen by client and designated to identify particular websocket request, this key will be returned with response to request and allows client to identify that response for his particular request.\n\nErrors are returned in the following format:\n```\n\u003ckey\u003e ERROR: \u003cerror description\u003e\n```\n\n#### `\u003ckey\u003e info`\nGet database metadatas as JSON document.\n\n#### `\u003ckey\u003e get     \u003ccollection\u003e \u003cid\u003e`\nRetrieve document identified by `id` from a `collection`.\nIf document is not found `IWKV_ERROR_NOTFOUND` will be returned.\n\nExample:\n```\n\u003e k get family 3\n\u003c k     3       {\n \"firstName\": \"Jack\",\n \"lastName\": \"Parker\",\n \"age\": 35,\n \"pets\": [\n  {\n   \"name\": \"Sonic\",\n   \"kind\": \"mouse\",\n   \"likes\": []\n  }\n ]\n}\n```\nIf document not found we will get error:\n```\n\u003e k get family 55\n\u003c k ERROR: Key not found. (IWKV_ERROR_NOTFOUND)\n\u003e\n```\n\n#### `\u003ckey\u003e set     \u003ccollection\u003e \u003cid\u003e \u003cdocument json\u003e`\nReplaces/add document under specific numeric `id`.\n`Collection` will be created automatically if not exists.\n\n#### `\u003ckey\u003e add     \u003ccollection\u003e \u003cdocument json\u003e`\nAdd new document to `\u003ccollection\u003e` New `id` of document will be generated\nand returned as response. `Collection\u003e will be created automatically if not exists.\n\nExample:\n```\n\u003e k add mycollection {\"foo\":\"bar\"}\n\u003c k     1\n\u003e k add mycollection {\"foo\":\"bar\"}\n\u003c k     2\n\u003e\n```\n\n#### `\u003ckey\u003e del     \u003ccollection\u003e \u003cid\u003e`\nRemove document identified by `id` from the `collection`.\nIf document is not found `IWKV_ERROR_NOTFOUND` will be returned.\n\n#### `\u003ckey\u003e patch   \u003ccollection\u003e \u003cid\u003e \u003cpatch json\u003e`\nApply [rfc7396](https://tools.ietf.org/html/rfc7396) or\n[rfc6902](https://tools.ietf.org/html/rfc6902) patch to the document identified by `id`.\nIf document is not found `IWKV_ERROR_NOTFOUND` will be returned.\n\n#### `\u003ckey\u003e query   \u003ccollection\u003e \u003cquery\u003e`\nExecute query on documents in specified `collection`.\n**Response:** A set of WS messages with document boidies terminated by the last\nmessage with empty body.\n```\n\u003e k query family /* | /firstName\n\u003c k     4       {\"firstName\":\"John\"}\n\u003c k     3       {\"firstName\":\"Jack\"}\n\u003c k     1       {\"firstName\":\"John\"}\n\u003c k\n```\nNote about last message: `\u003ckey\u003e` with no body.\n\n#### `\u003ckey\u003e explain \u003ccollection\u003e \u003cquery\u003e`\nSame as `\u003ckey\u003e query   \u003ccollection\u003e \u003cquery\u003e` but the first response message will\nbe prefixed by `\u003ckey\u003e explain` and contains query execution plan.\n\nExample:\n```\n\u003e k explain family /* | /firstName\n\u003c k     explain [INDEX] NO [COLLECTOR] PLAIN\n\n\u003c k     4       {\"firstName\":\"John\"}\n\u003c k     3       {\"firstName\":\"Jack\"}\n\u003c k     1       {\"firstName\":\"John\"}\n\u003c k\n```\n\n#### \u003ckey\u003e \u003cquery\u003e\nExecute query text. Body of query should contains collection name in use in the first filter element: `@collection_name/...`. Behavior is the same as for: `\u003ckey\u003e query   \u003ccollection\u003e \u003cquery\u003e`\n\n#### `\u003ckey\u003e idx     \u003ccollection\u003e \u003cmode\u003e \u003cpath\u003e`\nEnsure index with specified `mode` (bitmask flag) for given json `path` and `collection`.\nCollection will be created if not exists.\n\nIndex mode | Description\n--- | ---\n\u003ccode\u003e0x01 EJDB_IDX_UNIQUE\u003c/code\u003e | Index is unique\n\u003ccode\u003e0x04 EJDB_IDX_STR\u003c/code\u003e | Index for JSON `string` field value type\n\u003ccode\u003e0x08 EJDB_IDX_I64\u003c/code\u003e | Index for `8 bytes width` signed integer field values\n\u003ccode\u003e0x10 EJDB_IDX_F64\u003c/code\u003e | Index for `8 bytes width` signed floating point field values.\n\n##### Example\nSet unique string index `(0x01 \u0026 0x04) = 5` on `/name` JSON field:\n```\nk idx mycollection 5 /name\n```\n\n#### `\u003ckey\u003e rmi     \u003ccollection\u003e \u003cmode\u003e \u003cpath\u003e`\nRemove index with specified `mode` (bitmask flag) for given json `path` and `collection`.\nReturn error if given index not found.\n\n#### `\u003ckey\u003e rmc     \u003ccollection\u003e`\nRemove collection and all of its data.\nNote: If `collection` is not found no errors will be reported.\n\n\n\n\n# Docker support\n\nIf you have [Docker](\"https://www.docker.com/\") installed, you can build a Docker image and run it in a container\n\n```\ncd docker\ndocker build -t ejdb2 .\ndocker run -d -p 9191:9191 --name myEJDB ejdb2 --access myAccessKey\n```\n\nor get an image of `ejdb2` directly from the Docker Hub\n\n```\ndocker run -d -p 9191:9191 --name myEJDB softmotions/ejdb2 --access myAccessKey\n```\n\n\n# C API\n\nEJDB can be embedded into any `C/C++` application.\n`C API` documented in the following headers:\n\n* [ejdb.h](https://github.com/Softmotions/ejdb/blob/master/src/ejdb2.h) Main API functions\n* [jbl.h](https://github.com/Softmotions/ejdb/blob/master/src/jbl/jbl.h) JSON documents management API\n* [jql.h](https://github.com/Softmotions/ejdb/blob/master/src/jql/jql.h) Query building API\n\nExample application:\n```c\n#include \u003cejdb2/ejdb2.h\u003e\n\n#define CHECK(rc_)          \\\n  if (rc_) {                 \\\n    iwlog_ecode_error3(rc_); \\\n    return 1;                \\\n  }\n\nstatic iwrc documents_visitor(EJDB_EXEC *ctx, const EJDB_DOC doc, int64_t *step) {\n  // Print document to stderr\n  return jbl_as_json(doc-\u003eraw, jbl_fstream_json_printer, stderr, JBL_PRINT_PRETTY);\n}\n\nint main() {\n\n  EJDB_OPTS opts = {\n    .kv = {\n      .path = \"example.db\",\n      .oflags = IWKV_TRUNC\n    }\n  };\n  EJDB db;     // EJDB2 storage handle\n  int64_t id;  // Document id placeholder\n  JQL q = 0;   // Query instance\n  JBL jbl = 0; // Json document\n\n  iwrc rc = ejdb_init();\n  CHECK(rc);\n\n  rc = ejdb_open(\u0026opts, \u0026db);\n  CHECK(rc);\n\n  // First record\n  rc = jbl_from_json(\u0026jbl, \"{\\\"name\\\":\\\"Bianca\\\", \\\"age\\\":4}\");\n  RCGO(rc, finish);\n  rc = ejdb_put_new(db, \"parrots\", jbl, \u0026id);\n  RCGO(rc, finish);\n  jbl_destroy(\u0026jbl);\n\n  // Second record\n  rc = jbl_from_json(\u0026jbl, \"{\\\"name\\\":\\\"Darko\\\", \\\"age\\\":8}\");\n  RCGO(rc, finish);\n  rc = ejdb_put_new(db, \"parrots\", jbl, \u0026id);\n  RCGO(rc, finish);\n  jbl_destroy(\u0026jbl);\n\n  // Now execute a query\n  rc =  jql_create(\u0026q, \"parrots\", \"/[age \u003e :age]\");\n  RCGO(rc, finish);\n\n  EJDB_EXEC ux = {\n    .db = db,\n    .q = q,\n    .visitor = documents_visitor\n  };\n\n  // Set query placeholder value.\n  // Actual query will be /[age \u003e 3]\n  rc = jql_set_i64(q, \"age\", 0, 3);\n  RCGO(rc, finish);\n\n  // Now execute the query\n  rc = ejdb_exec(\u0026ux);\n\nfinish:\n  jql_destroy(\u0026q);\n  jbl_destroy(\u0026jbl);\n  ejdb_close(\u0026db);\n  CHECK(rc);\n  return 0;\n}\n```\n\nCompile and run:\n```\ngcc -std=gnu11 -Wall -pedantic -c -o example1.o example1.c\ngcc -o example1 example1.o -lejdb2\n\n./example1\n{\n \"name\": \"Darko\",\n \"age\": 8\n}{\n \"name\": \"Bianca\",\n \"age\": 4\n}\n```\n\n# License\n```\n\nMIT License\n\nCopyright (c) 2012-2024 Softmotions Ltd \u003cinfo@softmotions.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSoftmotions%2Fejdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSoftmotions%2Fejdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSoftmotions%2Fejdb/lists"}