mdbxmou 0.3.9 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CMakeLists.txt CHANGED
@@ -23,6 +23,7 @@ add_library(${PROJECT_NAME} SHARED
23
23
  "src/querymou.cpp"
24
24
  "src/envmou.cpp"
25
25
  "src/txnmou.cpp"
26
+ "src/convmou.cpp"
26
27
  "src/dbimou.cpp"
27
28
  "src/cursormou.cpp"
28
29
  "src/dbi.cpp")
@@ -63,4 +64,3 @@ string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
63
64
  string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
64
65
  target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR} "src")
65
66
 
66
-
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mdbxmou
2
2
 
3
- High-performance Node.js binding for [libmdbx](https://github.com/erthink/libmdbx) — a fast, lightweight, embedded key-value database.
3
+ Node.js binding for [libmdbx](https://github.com/Mithril-mine/libmdbx) — a fast, lightweight, embedded key-value database.
4
4
 
5
5
  ## Features
6
6
 
@@ -345,6 +345,40 @@ const bigIntKeys = dbi.keysFrom(txn, 42n, 50);
345
345
  const equalKeys = dbi.keysFrom(txn, 5, 10, 'keyEqual');
346
346
  ```
347
347
 
348
+ **getRange(txn, [options]) → Array<{ key, value }>**
349
+ ```javascript
350
+ const rows = dbi.getRange(txn, { start: 10, end: 15 });
351
+ // [
352
+ // { key: 10, value: ... },
353
+ // { key: 11, value: ... },
354
+ // ...
355
+ // ]
356
+ ```
357
+
358
+ **keysRange(txn, [options]) → Array**
359
+ ```javascript
360
+ const keys = dbi.keysRange(txn, { start: 10, end: 20, limit: 5 });
361
+ // [10, 11, 12, 13, 14]
362
+ ```
363
+
364
+ **valuesRange(txn, [options]) → Array**
365
+ ```javascript
366
+ const values = dbi.valuesRange(txn, {
367
+ start: 10,
368
+ end: 15,
369
+ reverse: true,
370
+ includeEnd: false
371
+ });
372
+ // values for keys 14, 13, 12, 11, 10
373
+ ```
374
+
375
+ Range options:
376
+ - `start`, `end` - inclusive bounds by default
377
+ - `includeStart`, `includeEnd` - control bound inclusion
378
+ - `reverse` - scan from upper bound to lower bound
379
+ - `limit` - maximum number of returned items
380
+ - `offset` - skip N items after initial positioning
381
+
348
382
  **drop(txn, [delete_db]) → void**
349
383
  ```javascript
350
384
  // Clear database contents (keep structure)
@@ -750,6 +784,60 @@ function cursorExample() {
750
784
  cursorExample();
751
785
  ```
752
786
 
787
+ ### Range Queries
788
+
789
+ ```javascript
790
+ const { MDBX_Env, MDBX_Param } = require('mdbxmou');
791
+
792
+ function rangeExample() {
793
+ const env = new MDBX_Env();
794
+ env.openSync({
795
+ path: './range-data',
796
+ valueFlag: MDBX_Param.valueFlag.string
797
+ });
798
+
799
+ const writeTxn = env.startWrite();
800
+ const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
801
+ for (let i = 0; i < 10; i++) {
802
+ dbi.put(writeTxn, i, `value_${i}`);
803
+ }
804
+ writeTxn.commit();
805
+
806
+ const readTxn = env.startRead();
807
+ const readDbi = readTxn.openMap(MDBX_Param.keyMode.ordinal);
808
+
809
+ const rows = readDbi.getRange(readTxn, { start: 3, end: 6 });
810
+ console.log(rows);
811
+ // [
812
+ // { key: 3, value: 'value_3' },
813
+ // { key: 4, value: 'value_4' },
814
+ // { key: 5, value: 'value_5' },
815
+ // { key: 6, value: 'value_6' }
816
+ // ]
817
+
818
+ const keys = readDbi.keysRange(readTxn, {
819
+ start: 3,
820
+ end: 8,
821
+ offset: 1,
822
+ limit: 3
823
+ });
824
+ console.log(keys); // [4, 5, 6]
825
+
826
+ const values = readDbi.valuesRange(readTxn, {
827
+ start: 3,
828
+ end: 6,
829
+ reverse: true,
830
+ includeEnd: false
831
+ });
832
+ console.log(values); // ['value_5', 'value_4', 'value_3']
833
+
834
+ readTxn.commit();
835
+ env.closeSync();
836
+ }
837
+
838
+ rangeExample();
839
+ ```
840
+
753
841
  ### Query API (Advanced Async)
754
842
 
755
843
  ```javascript
@@ -869,6 +957,7 @@ node test/readme-sync-example.js
869
957
  node test/readme-async-example.js
870
958
  node test/readme-key-types.js
871
959
  node test/readme-cursor-example.js
960
+ node test/readme-range-example.js
872
961
  node test/readme-query-example.js
873
962
  node test/readme-keys-example.js
874
963
  node test/readme-error-handling.js
package/lib/nativemou.js CHANGED
@@ -1,6 +1,5 @@
1
- const os = require('os');
2
-
3
1
  const nativePath = '../build/Release/mdbxmou';
2
+ /** @type {import("./types").MDBX_Native} */
4
3
  const nativeModule = require(nativePath);
5
4
  // Экспортируем объединенный модуль
6
5
  module.exports = nativeModule;
package/lib/types.d.ts CHANGED
@@ -79,6 +79,16 @@ export interface MDBXCursorResult<K extends MDBXKey = MDBXKey, V extends MDBXVal
79
79
  value: V;
80
80
  }
81
81
 
82
+ export interface MDBXRangeOptions<K extends MDBXKey = MDBXKey> {
83
+ start?: K;
84
+ end?: K;
85
+ limit?: number;
86
+ offset?: number;
87
+ reverse?: boolean;
88
+ includeStart?: boolean;
89
+ includeEnd?: boolean;
90
+ }
91
+
82
92
  /**
83
93
  * Database cursor for sequential access and range queries.
84
94
  * Must be closed before transaction commit/abort.
@@ -213,6 +223,9 @@ export interface MDBX_Dbi<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDB
213
223
  stat(txn: MDBX_Txn): MDBXDbiStat;
214
224
  keys(txn: MDBX_Txn): K[];
215
225
  keysFrom(txn: MDBX_Txn, fromKey: K, limit?: number, cursorMode?: MDBXCursorMode): K[];
226
+ getRange(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): MDBXCursorResult<K, V>[];
227
+ keysRange(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): K[];
228
+ valuesRange(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): V[];
216
229
  drop(txn: MDBX_Txn, deleteDb?: boolean): void;
217
230
  }
218
231
 
@@ -394,9 +407,10 @@ export interface MDBX_Param {
394
407
  };
395
408
  }
396
409
 
410
+ /** Runtime constants exported by the native module. */
397
411
  export declare const MDBX_Param: MDBX_Param;
398
412
 
399
413
  export interface MDBX_Native {
400
414
  MDBX_Env: typeof MDBX_Env;
401
- MDBX_Param: MDBX_Param;
415
+ MDBX_Param: typeof MDBX_Param;
402
416
  }
package/package.json CHANGED
@@ -50,6 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^22.10.2",
53
+ "msgpackr": "^1.11.9",
53
54
  "typescript": "^5.6.3"
54
55
  },
55
56
  "scripts": {
@@ -58,6 +59,8 @@
58
59
  "e4": "node ./test/e4.js",
59
60
  "e4async": "node ./test/e4async.mjs",
60
61
  "e5": "node ./test/e5.js",
62
+ "e7": "node ./test/e7.js",
63
+ "e8": "node ./test/e8.js",
61
64
  "test:types": "node ./test/types/run.mjs",
62
65
  "build": "node build.js",
63
66
  "build-dev": "node build-dev.js",
@@ -65,7 +68,7 @@
65
68
  },
66
69
  "gypfile": true,
67
70
  "name": "mdbxmou",
68
- "version": "0.3.9",
71
+ "version": "0.3.11",
69
72
  "description": "Node bindings for mdbx",
70
73
  "repository": {
71
74
  "type": "git",
@@ -1,10 +1,53 @@
1
1
  #include "envmou_keys.hpp"
2
2
  #include "envmou.hpp"
3
+ #include "convmou.hpp"
3
4
  #include "dbimou.hpp"
4
- #include "../valuemou.hpp"
5
+ #include "valuemou.hpp"
5
6
 
6
7
  namespace mdbxmou {
7
8
 
9
+ namespace {
10
+
11
+ template<bool Ordinal>
12
+ void scan_keys_from(mdbx::cursor_managed& cursor, keys_line& arg0,
13
+ keymou& from_key, mdbx::cursor::move_operation turn_mode)
14
+ {
15
+ auto& item = arg0.item;
16
+ auto cursor_mode = arg0.cursor_mode;
17
+ bool is_key_equal_mode = (cursor_mode == mdbx::cursor::move_operation::key_equal ||
18
+ cursor_mode == mdbx::cursor::move_operation::multi_exactkey_value_equal);
19
+
20
+ std::size_t index{};
21
+ cursor.scan_from([&](const mdbx::pair& f) {
22
+ if (index >= arg0.limit) {
23
+ return true;
24
+ }
25
+
26
+ keymou key{f.key};
27
+ if (is_key_equal_mode) {
28
+ if constexpr (Ordinal) {
29
+ if (arg0.id_buf != key.as_int64()) {
30
+ return true;
31
+ }
32
+ } else if (from_key != key) {
33
+ return true;
34
+ }
35
+ }
36
+
37
+ async_key rc{};
38
+ if constexpr (Ordinal) {
39
+ rc.id_buf = key.as_uint64();
40
+ } else {
41
+ rc.key_buf.assign(key.char_ptr(), key.end_char_ptr());
42
+ }
43
+ item.push_back(std::move(rc));
44
+ ++index;
45
+ return false;
46
+ }, from_key, cursor_mode, turn_mode);
47
+ }
48
+
49
+ } // namespace
50
+
8
51
  void async_keys::Execute()
9
52
  {
10
53
  try {
@@ -24,26 +67,14 @@ void async_keys::Execute()
24
67
  static Napi::Value write_row(Napi::Env env, const keys_line& row)
25
68
  {
26
69
  auto& param = row.item;
27
- auto key_mode = row.key_mod;
28
- auto key_flag = row.key_flag;
70
+ convmou conv{row.key_mod, row.key_flag};
29
71
  auto js_arr = Napi::Array::New(env, param.size());
30
72
  for (std::uint32_t j = 0; j < param.size(); ++j) {
31
73
  const auto& item = param[j];
32
- Napi::Value key_value;
33
- if (mdbx::is_ordinal(key_mode)) {
34
- if (key_flag.val & base_flag::number) {
35
- key_value = Napi::Number::New(env, static_cast<double>(item.id_buf));
36
- } else {
37
- key_value = Napi::BigInt::New(env, item.id_buf);
38
- }
39
- } else {
40
- if (key_flag.val & base_flag::string) {
41
- key_value = Napi::String::New(env, item.key_buf.data(), item.key_buf.size());
42
- } else {
43
- key_value = Napi::Buffer<char>::Copy(env, item.key_buf.data(), item.key_buf.size());
44
- }
45
- }
46
- js_arr.Set(j, key_value);
74
+ auto key = mdbx::is_ordinal(row.key_mod) ?
75
+ keymou{item.id_buf} :
76
+ keymou{item.key_buf};
77
+ js_arr.Set(j, conv.convert_key(env, key));
47
78
  }
48
79
  return js_arr;
49
80
  }
@@ -140,14 +171,12 @@ void async_keys::do_keys_batch(txnmou_managed& txn,
140
171
  void async_keys::do_keys_from(txnmou_managed& txn,
141
172
  mdbx::map_handle dbi, keys_line& arg0)
142
173
  {
143
- // сыллка на массив результатов
144
- auto& item = arg0.item;
145
- std::size_t count = 0;
146
174
  using move_operation = mdbx::cursor::move_operation;
175
+ auto is_ordinal = mdbx::is_ordinal(arg0.key_mod);
147
176
 
148
177
  auto cursor = txn.open_cursor(dbi);
149
178
 
150
- keymou from_key = mdbx::is_ordinal(arg0.key_mod) ?
179
+ keymou from_key = is_ordinal ?
151
180
  keymou{arg0.id_buf} :
152
181
  keymou{mdbx::slice{arg0.key_buf.data(), arg0.key_buf.size()}};
153
182
 
@@ -170,50 +199,10 @@ void async_keys::do_keys_from(txnmou_managed& txn,
170
199
  break;
171
200
  }
172
201
 
173
- bool is_key_equal_mode = (cursor_mode == move_operation::key_equal ||
174
- cursor_mode == move_operation::multi_exactkey_value_equal);
175
-
176
- std::size_t index{};
177
- if (mdbx::is_ordinal(arg0.key_mod)) {
178
- // Создаем ключ для позиционирования
179
- cursor.scan_from([&](const mdbx::pair& f) {
180
- if (index >= arg0.limit) {
181
- return true; // останавливаем сканирование
182
- }
183
-
184
- keymou key{f.key};
185
- if (is_key_equal_mode) {
186
- if (arg0.id_buf != key.as_int64()) {
187
- return true; // останавливаем сканирование
188
- }
189
- }
190
-
191
- async_key rc{};
192
- rc.id_buf = key.as_uint64();
193
- item.push_back(std::move(rc));
194
- index++;
195
- return false; // продолжаем сканирование
196
- }, from_key, arg0.cursor_mode, turn_mode);
202
+ if (is_ordinal) {
203
+ scan_keys_from<true>(cursor, arg0, from_key, turn_mode);
197
204
  } else {
198
- // Создаем ключ для позиционирования
199
- cursor.scan_from([&](const mdbx::pair& f) {
200
- if (index >= arg0.limit) {
201
- return true; // останавливаем сканирование
202
- }
203
-
204
- keymou key{f.key};
205
- if (is_key_equal_mode) {
206
- if (from_key != key) {
207
- return true; // останавливаем сканирование
208
- }
209
- }
210
-
211
- async_key rc{};
212
- rc.id_buf = key.as_uint64();
213
- item.push_back(std::move(rc));
214
- index++;
215
- return false; // продолжаем сканирование
216
- }, from_key, arg0.cursor_mode, turn_mode);
205
+ scan_keys_from<false>(cursor, arg0, from_key, turn_mode);
217
206
  }
218
207
  }
219
208
 
@@ -1,4 +1,5 @@
1
1
  #include "envmou_query.hpp"
2
+ #include "convmou.hpp"
2
3
  #include "envmou.hpp"
3
4
 
4
5
  namespace mdbxmou {
@@ -31,28 +32,16 @@ void async_query::Execute()
31
32
  static Napi::Value write_row(Napi::Env env, const query_line& row)
32
33
  {
33
34
  auto& param = row.item;
34
- auto key_mode = row.key_mod;
35
- auto key_flag = row.key_flag;
36
35
  auto mode = row.mode;
36
+ convmou conv{row.key_mod, row.key_flag, row.value_flag};
37
37
  auto js_arr = Napi::Array::New(env, param.size());
38
38
  for (std::size_t j = 0; j < param.size(); ++j) {
39
39
  const auto& item = param[j];
40
- Napi::Value key_value;
41
40
  Napi::Object js_item = Napi::Object::New(env);
42
- if (mdbx::is_ordinal(key_mode)) {
43
- if (key_flag.val & base_flag::number) {
44
- key_value = Napi::Number::New(env, static_cast<double>(item.id_buf));
45
- } else {
46
- key_value = Napi::BigInt::New(env, item.id_buf);
47
- }
48
- } else {
49
- if (key_flag.val & base_flag::string) {
50
- key_value = Napi::String::New(env, item.key_buf.data(), item.key_buf.size());
51
- } else {
52
- key_value = Napi::Buffer<char>::Copy(env, item.key_buf.data(), item.key_buf.size());
53
- }
54
- }
55
- js_item.Set("key", key_value);
41
+ auto key = mdbx::is_ordinal(row.key_mod) ?
42
+ keymou{item.id_buf} :
43
+ keymou{item.key_buf};
44
+ js_item.Set("key", conv.convert_key(env, key));
56
45
 
57
46
  // все методы которые должны показать value в результате
58
47
  const auto mask{query_mode::get|query_mode::upsert|
@@ -63,14 +52,8 @@ static Napi::Value write_row(Napi::Env env, const query_line& row)
63
52
  if (val_buf.empty()) {
64
53
  js_item.Set("value", env.Null());
65
54
  } else {
66
- auto value_flag = row.value_flag;
67
- Napi::Value val_value;
68
- if (value_flag.val & base_flag::string) {
69
- val_value = Napi::String::New(env, val_buf.data(), val_buf.size());
70
- } else {
71
- val_value = Napi::Buffer<char>::Copy(env, val_buf.data(), val_buf.size());
72
- }
73
- js_item.Set("value", val_value);
55
+ js_item.Set("value",
56
+ conv.convert_value(env, valuemou{val_buf}));
74
57
  }
75
58
  }
76
59
 
@@ -162,4 +145,4 @@ void async_query::do_put(txnmou_managed& txn,
162
145
  }
163
146
  }
164
147
 
165
- } // namespace mdbxmou
148
+ } // namespace mdbxmou
@@ -0,0 +1,34 @@
1
+ #include "convmou.hpp"
2
+
3
+ namespace mdbxmou {
4
+
5
+ Napi::Value convmou::convert_key(const Napi::Env& env, const keymou& key) const
6
+ {
7
+ if (mdbx::is_ordinal(key_mode_)) {
8
+ return (key_flag_ & base_flag::bigint) ?
9
+ key.to_bigint(env) :
10
+ key.to_number(env);
11
+ }
12
+
13
+ return (key_flag_ & base_flag::string) ?
14
+ key.to_string(env) :
15
+ key.to_buffer(env);
16
+ }
17
+
18
+ Napi::Value convmou::convert_value(const Napi::Env& env, const valuemou& val) const
19
+ {
20
+ return (value_flag_ & base_flag::string) ?
21
+ val.to_string(env) :
22
+ val.to_buffer(env);
23
+ }
24
+
25
+ Napi::Object convmou::make_result(const Napi::Env& env,
26
+ const keymou& key, const valuemou& val) const
27
+ {
28
+ auto result = Napi::Object::New(env);
29
+ result.Set("key", convert_key(env, key));
30
+ result.Set("value", convert_value(env, val));
31
+ return result;
32
+ }
33
+
34
+ } // namespace mdbxmou
@@ -0,0 +1,30 @@
1
+ #pragma once
2
+
3
+ #include "valuemou.hpp"
4
+
5
+ namespace mdbxmou {
6
+
7
+ struct convmou
8
+ {
9
+ key_mode key_mode_{};
10
+ base_flag key_flag_{};
11
+ base_flag value_flag_{};
12
+
13
+ convmou() = default;
14
+
15
+ convmou(key_mode key_mode, base_flag key_flag,
16
+ base_flag value_flag = {}) noexcept
17
+ : key_mode_{key_mode}
18
+ , key_flag_{key_flag}
19
+ , value_flag_{value_flag}
20
+ { }
21
+
22
+ Napi::Value convert_key(const Napi::Env& env, const keymou& key) const;
23
+
24
+ Napi::Value convert_value(const Napi::Env& env, const valuemou& val) const;
25
+
26
+ Napi::Object make_result(const Napi::Env& env,
27
+ const keymou& key, const valuemou& val) const;
28
+ };
29
+
30
+ } // namespace mdbxmou
package/src/cursormou.cpp CHANGED
@@ -72,32 +72,7 @@ namespace mdbxmou
72
72
  throw Napi::Error::New(env, mdbx_strerror(rc));
73
73
  }
74
74
 
75
- // Возвращаем {key, value}
76
- auto result = Napi::Object::New(env);
77
-
78
- auto key_mode = dbi_->get_key_mode();
79
- auto key_flag = dbi_->get_key_flag();
80
- auto value_flag = dbi_->get_value_flag();
81
-
82
- // Ключ
83
- if (mdbx::is_ordinal(key_mode)) {
84
- if (key_flag.val & base_flag::bigint) {
85
- result.Set("key", key.to_bigint(env));
86
- } else {
87
- result.Set("key", key.to_number(env));
88
- }
89
- } else {
90
- result.Set("key", key.to_string(env));
91
- }
92
-
93
- // Значение
94
- if (value_flag.val & base_flag::string) {
95
- result.Set("value", val.to_string(env));
96
- } else {
97
- result.Set("value", val.to_buffer(env));
98
- }
99
-
100
- return result;
75
+ return dbi_->get_convmou().make_result(env, key, val);
101
76
  }
102
77
 
103
78
  Napi::Value cursormou::first(const Napi::CallbackInfo &info)
@@ -224,31 +199,7 @@ namespace mdbxmou
224
199
  throw Napi::Error::New(env, mdbx_strerror(rc));
225
200
  }
226
201
 
227
- // Возвращаем {key, value}
228
- auto result = Napi::Object::New(env);
229
-
230
- auto key_flag = dbi_->get_key_flag();
231
- auto value_flag = dbi_->get_value_flag();
232
-
233
- // Ключ
234
- if (mdbx::is_ordinal(key_mode)) {
235
- if (key_flag.val & base_flag::bigint) {
236
- result.Set("key", key.to_bigint(env));
237
- } else {
238
- result.Set("key", key.to_number(env));
239
- }
240
- } else {
241
- result.Set("key", key.to_string(env));
242
- }
243
-
244
- // Значение
245
- if (value_flag.val & base_flag::string) {
246
- result.Set("value", val.to_string(env));
247
- } else {
248
- result.Set("value", val.to_buffer(env));
249
- }
250
-
251
- return result;
202
+ return dbi_->get_convmou().make_result(env, key, val);
252
203
  }
253
204
 
254
205
  Napi::Value cursormou::seek(const Napi::CallbackInfo &info)
@@ -338,12 +289,9 @@ namespace mdbxmou
338
289
  auto callback = info[0].As<Napi::Function>();
339
290
  bool backward = info.Length() > 1 && info[1].ToBoolean().Value();
340
291
 
341
- auto key_mode = dbi_->get_key_mode();
342
- auto key_flag = dbi_->get_key_flag();
343
- auto value_flag = dbi_->get_value_flag();
344
-
345
292
  MDBX_cursor_op start_op = backward ? MDBX_LAST : MDBX_FIRST;
346
293
  MDBX_cursor_op move_op = backward ? MDBX_PREV : MDBX_NEXT;
294
+ auto conv = dbi_->get_convmou();
347
295
 
348
296
  keymou key{};
349
297
  valuemou val{};
@@ -351,25 +299,7 @@ namespace mdbxmou
351
299
  auto rc = mdbx_cursor_get(cursor_, key, val, start_op);
352
300
  while (MDBX_SUCCESS == rc)
353
301
  {
354
- auto result = Napi::Object::New(env);
355
-
356
- // Ключ
357
- if (mdbx::is_ordinal(key_mode)) {
358
- if (key_flag.val & base_flag::bigint) {
359
- result.Set("key", key.to_bigint(env));
360
- } else {
361
- result.Set("key", key.to_number(env));
362
- }
363
- } else {
364
- result.Set("key", key.to_string(env));
365
- }
366
-
367
- // Значение
368
- if (value_flag.val & base_flag::string) {
369
- result.Set("value", val.to_string(env));
370
- } else {
371
- result.Set("value", val.to_buffer(env));
372
- }
302
+ auto result = conv.make_result(env, key, val);
373
303
 
374
304
  // Вызов callback
375
305
  auto ret = callback.Call({result});