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/src/dbimou.cpp CHANGED
@@ -2,9 +2,253 @@
2
2
  #include "envmou.hpp"
3
3
  #include "txnmou.hpp"
4
4
  #include "typemou.hpp"
5
+ #include <limits>
5
6
 
6
7
  namespace mdbxmou {
7
8
 
9
+ namespace {
10
+
11
+ enum class range_output {
12
+ items,
13
+ keys,
14
+ values,
15
+ };
16
+
17
+ std::size_t parse_size_option(const Napi::Env& env, const Napi::Object& options, const char* key)
18
+ {
19
+ auto value = options.Get(key);
20
+ if (value.IsUndefined() || value.IsNull()) {
21
+ return std::numeric_limits<std::size_t>::max();
22
+ }
23
+ if (!value.IsNumber()) {
24
+ throw Napi::TypeError::New(env, std::string(key) + " must be a number");
25
+ }
26
+ auto parsed = value.As<Napi::Number>().Int64Value();
27
+ if (parsed < 0) {
28
+ throw Napi::TypeError::New(env, std::string(key) + " must be >= 0");
29
+ }
30
+ return static_cast<std::size_t>(parsed);
31
+ }
32
+
33
+ bool parse_bool_option(const Napi::Env& env, const Napi::Object& options, const char* key, bool fallback)
34
+ {
35
+ auto value = options.Get(key);
36
+ if (value.IsUndefined() || value.IsNull()) {
37
+ return fallback;
38
+ }
39
+ if (!value.IsBoolean()) {
40
+ throw Napi::TypeError::New(env, std::string(key) + " must be a boolean");
41
+ }
42
+ return value.As<Napi::Boolean>().Value();
43
+ }
44
+
45
+ struct range_options final
46
+ {
47
+ bool has_start{};
48
+ bool has_end{};
49
+ bool reverse{};
50
+ bool include_start{true};
51
+ bool include_end{true};
52
+ std::size_t limit{std::numeric_limits<std::size_t>::max()};
53
+ std::size_t offset{};
54
+ std::uint64_t start_num{};
55
+ std::uint64_t end_num{};
56
+ buffer_type start_buf{};
57
+ buffer_type end_buf{};
58
+ keymou start_key{};
59
+ keymou end_key{};
60
+ };
61
+
62
+ range_options parse_range_options(const Napi::Env& env, const Napi::Value& arg0, const dbimou& self)
63
+ {
64
+ range_options options{};
65
+ if (arg0.IsUndefined() || arg0.IsNull()) {
66
+ return options;
67
+ }
68
+ if (!arg0.IsObject()) {
69
+ throw Napi::TypeError::New(env, "getRange options must be an object");
70
+ }
71
+
72
+ auto obj = arg0.As<Napi::Object>();
73
+ options.limit = parse_size_option(env, obj, "limit");
74
+
75
+ auto offset = obj.Get("offset");
76
+ if (!offset.IsUndefined() && !offset.IsNull()) {
77
+ if (!offset.IsNumber()) {
78
+ throw Napi::TypeError::New(env, "offset must be a number");
79
+ }
80
+ auto parsed = offset.As<Napi::Number>().Int64Value();
81
+ if (parsed < 0) {
82
+ throw Napi::TypeError::New(env, "offset must be >= 0");
83
+ }
84
+ options.offset = static_cast<std::size_t>(parsed);
85
+ }
86
+
87
+ options.reverse = parse_bool_option(env, obj, "reverse", false);
88
+ options.include_start = parse_bool_option(env, obj, "includeStart", true);
89
+ options.include_end = parse_bool_option(env, obj, "includeEnd", true);
90
+
91
+ auto start = obj.Get("start");
92
+ if (!start.IsUndefined() && !start.IsNull()) {
93
+ options.has_start = true;
94
+ options.start_key = mdbx::is_ordinal(self.get_key_mode()) ?
95
+ keymou::from(start, env, options.start_num) :
96
+ keymou::from(start, env, options.start_buf);
97
+ }
98
+
99
+ auto end = obj.Get("end");
100
+ if (!end.IsUndefined() && !end.IsNull()) {
101
+ options.has_end = true;
102
+ options.end_key = mdbx::is_ordinal(self.get_key_mode()) ?
103
+ keymou::from(end, env, options.end_num) :
104
+ keymou::from(end, env, options.end_buf);
105
+ }
106
+
107
+ return options;
108
+ }
109
+
110
+ bool cursor_get(cursormou_managed& cursor, MDBX_cursor_op op, mdbx::slice& key, mdbx::slice& value)
111
+ {
112
+ auto rc = ::mdbx_cursor_get(cursor, &key, &value, op);
113
+ switch (rc) {
114
+ case MDBX_SUCCESS:
115
+ case MDBX_RESULT_TRUE:
116
+ return true;
117
+ case MDBX_NOTFOUND:
118
+ return false;
119
+ default:
120
+ mdbx::error::throw_exception(rc);
121
+ return false;
122
+ }
123
+ }
124
+
125
+ bool outside_range(MDBX_txn* txn, MDBX_dbi dbi, const mdbx::slice& key, const range_options& options)
126
+ {
127
+ if (options.reverse) {
128
+ if (!options.has_start) {
129
+ return false;
130
+ }
131
+ auto cmp = ::mdbx_cmp(
132
+ txn, dbi,
133
+ static_cast<const MDBX_val*>(&key),
134
+ static_cast<const MDBX_val*>(&options.start_key));
135
+ return cmp < 0 || (!options.include_start && cmp == 0);
136
+ }
137
+
138
+ if (!options.has_end) {
139
+ return false;
140
+ }
141
+ auto cmp = ::mdbx_cmp(
142
+ txn, dbi,
143
+ static_cast<const MDBX_val*>(&key),
144
+ static_cast<const MDBX_val*>(&options.end_key));
145
+ return cmp > 0 || (!options.include_end && cmp == 0);
146
+ }
147
+
148
+ MDBX_cursor_op range_start_op(const range_options& options)
149
+ {
150
+ if (options.reverse) {
151
+ if (!options.has_end) {
152
+ return MDBX_LAST;
153
+ }
154
+ return options.include_end ? MDBX_TO_KEY_LESSER_OR_EQUAL : MDBX_TO_KEY_LESSER_THAN;
155
+ }
156
+
157
+ if (!options.has_start) {
158
+ return MDBX_FIRST;
159
+ }
160
+ return options.include_start ? MDBX_TO_KEY_GREATER_OR_EQUAL : MDBX_TO_KEY_GREATER_THAN;
161
+ }
162
+
163
+ MDBX_cursor_op range_turn_op(const range_options& options)
164
+ {
165
+ return options.reverse ? MDBX_PREV : MDBX_NEXT;
166
+ }
167
+
168
+ Napi::Array collect_range(const Napi::Env& env, dbimou& self, txnmou& txn, const range_options& options, range_output output)
169
+ {
170
+ Napi::Array result = Napi::Array::New(env);
171
+ if (options.limit == 0) {
172
+ return result;
173
+ }
174
+
175
+ auto conv = self.get_convmou();
176
+ auto cursor = self.open_cursor(txn);
177
+ mdbx::slice key{};
178
+ mdbx::slice value{};
179
+
180
+ if (options.reverse) {
181
+ if (options.has_end) {
182
+ key = options.end_key;
183
+ }
184
+ } else if (options.has_start) {
185
+ key = options.start_key;
186
+ }
187
+
188
+ if (!cursor_get(cursor, range_start_op(options), key, value)) {
189
+ return result;
190
+ }
191
+
192
+ std::size_t skipped{};
193
+ std::size_t index{};
194
+ auto turn_op = range_turn_op(options);
195
+
196
+ while (true) {
197
+ if (outside_range(txn, self.get_id(), key, options)) {
198
+ break;
199
+ }
200
+
201
+ if (skipped < options.offset) {
202
+ ++skipped;
203
+ } else {
204
+ switch (output) {
205
+ case range_output::items: {
206
+ result.Set(index, conv.make_result(env, keymou{key}, valuemou{value}));
207
+ break;
208
+ }
209
+ case range_output::keys:
210
+ result.Set(index, conv.convert_key(env, keymou{key}));
211
+ break;
212
+ case range_output::values:
213
+ result.Set(index, conv.convert_value(env, valuemou{value}));
214
+ break;
215
+ }
216
+
217
+ ++index;
218
+ if (index >= options.limit) {
219
+ break;
220
+ }
221
+ }
222
+
223
+ if (!cursor_get(cursor, turn_op, key, value)) {
224
+ break;
225
+ }
226
+ }
227
+
228
+ return result;
229
+ }
230
+
231
+ Napi::Value run_range_query(const Napi::CallbackInfo& info, dbimou& self, const char* method_name, range_output output)
232
+ {
233
+ Napi::Env env = info.Env();
234
+ if (info.Length() < 1) {
235
+ throw Napi::TypeError::New(env, std::string(method_name) + ": txnmou required");
236
+ }
237
+
238
+ auto txn = txnmou::unwrap_checked(env, info[0], method_name);
239
+
240
+ try {
241
+ auto options = info.Length() > 1 ?
242
+ parse_range_options(env, info[1], self) :
243
+ range_options{};
244
+ return collect_range(env, self, *txn, options, output);
245
+ } catch (const std::exception& e) {
246
+ throw Napi::Error::New(env, std::string(method_name) + ": " + e.what());
247
+ }
248
+ }
249
+
250
+ } // namespace
251
+
8
252
  Napi::FunctionReference dbimou::ctor{};
9
253
 
10
254
  void dbimou::init(const char *class_name, Napi::Env env)
@@ -18,6 +262,9 @@ void dbimou::init(const char *class_name, Napi::Env env)
18
262
  InstanceMethod("stat", &dbimou::stat),
19
263
  InstanceMethod("keys", &dbimou::keys),
20
264
  InstanceMethod("keysFrom", &dbimou::keys_from),
265
+ InstanceMethod("getRange", &dbimou::get_range),
266
+ InstanceMethod("keysRange", &dbimou::keys_range),
267
+ InstanceMethod("valuesRange", &dbimou::values_range),
21
268
  InstanceMethod("drop", &dbimou::drop),
22
269
 
23
270
  // Свойства только для чтения
@@ -40,12 +287,7 @@ Napi::Value dbimou::put(const Napi::CallbackInfo& info)
40
287
  if (arg_len < 3) {
41
288
  throw Napi::Error::New(env, "put: txnmou, key and value required");
42
289
  }
43
- auto arg0 = info[0].As<Napi::Object>();
44
- // Дополнительная проверка - есть ли у объекта нужный constructor
45
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
46
- throw Napi::TypeError::New(env, "put: first argument must be MDBX_Txn instance");
47
- }
48
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
290
+ auto txn = txnmou::unwrap_checked(env, info[0], "put");
49
291
  try {
50
292
  std::uint64_t t;
51
293
  auto key = mdbx::is_ordinal(key_mode_) ?
@@ -68,14 +310,10 @@ Napi::Value dbimou::get(const Napi::CallbackInfo& info)
68
310
  if (arg_len < 2) {
69
311
  throw Napi::Error::New(env, "get: txnmou and key required");
70
312
  }
71
- auto arg0 = info[0].As<Napi::Object>();
72
- // Дополнительная проверка - есть ли у объекта нужный constructor
73
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
74
- throw Napi::TypeError::New(env, "get: first argument must be MDBX_Txn instance");
75
- }
76
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
313
+ auto txn = txnmou::unwrap_checked(env, info[0], "get");
77
314
 
78
315
  try {
316
+ auto conv = get_convmou();
79
317
  std::uint64_t t;
80
318
  auto key = mdbx::is_ordinal(key_mode_) ?
81
319
  keymou::from(info[1], env, t) :
@@ -86,8 +324,7 @@ Napi::Value dbimou::get(const Napi::CallbackInfo& info)
86
324
  return env.Undefined();
87
325
  }
88
326
 
89
- return (value_flag_.val & base_flag::string) ?
90
- val.to_string(env) : val.to_buffer(env);
327
+ return conv.convert_value(env, val);
91
328
  } catch (const std::exception& e) {
92
329
  throw Napi::Error::New(env, std::string("get: ") + e.what());
93
330
  }
@@ -102,12 +339,7 @@ Napi::Value dbimou::del(const Napi::CallbackInfo& info)
102
339
  if (arg_len < 2) {
103
340
  throw Napi::Error::New(env, "del: txnmou and key required");
104
341
  }
105
- auto arg0 = info[0].As<Napi::Object>();
106
- // Дополнительная проверка - есть ли у объекта нужный constructor
107
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
108
- throw Napi::TypeError::New(env, "del: first argument must be MDBX_Txn instance");
109
- }
110
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
342
+ auto txn = txnmou::unwrap_checked(env, info[0], "del");
111
343
 
112
344
  try {
113
345
  std::uint64_t t;
@@ -131,12 +363,7 @@ Napi::Value dbimou::has(const Napi::CallbackInfo& info)
131
363
  if (arg_len < 2) {
132
364
  throw Napi::Error::New(env, "has: txnmou and key required");
133
365
  }
134
- auto arg0 = info[0].As<Napi::Object>();
135
- // Дополнительная проверка - есть ли у объекта нужный constructor
136
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
137
- throw Napi::TypeError::New(env, "has: first argument must be MDBX_Txn instance");
138
- }
139
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
366
+ auto txn = txnmou::unwrap_checked(env, info[0], "has");
140
367
 
141
368
  try {
142
369
  std::uint64_t t;
@@ -160,12 +387,7 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info)
160
387
  if (arg_len < 2) {
161
388
  throw Napi::Error::New(env, "for_each: txnmou and function required");
162
389
  }
163
- auto arg0 = info[0].As<Napi::Object>();
164
- // Дополнительная проверка - есть ли у объекта нужный constructor
165
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
166
- throw Napi::TypeError::New(env, "for_each: first argument must be MDBX_Txn instance");
167
- }
168
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
390
+ auto txn = txnmou::unwrap_checked(env, info[0], "for_each");
169
391
 
170
392
  // Проверяем тип вызова: forEach(txn, fn), forEach(txn, fromKey, fn) или forEach(txn, fromKey, cursorMode, fn)
171
393
  if (info.Length() == 2 && info[1].IsFunction()) {
@@ -173,6 +395,7 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info)
173
395
  auto fn = info[1].As<Napi::Function>();
174
396
 
175
397
  try {
398
+ auto conv = get_convmou();
176
399
  auto stat = get_stat(*txn);
177
400
 
178
401
  // Проверяем, есть ли записи в базе данных
@@ -182,46 +405,20 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info)
182
405
 
183
406
  auto cursor = open_cursor(*txn);
184
407
  uint32_t index{};
185
- if (mdbx::is_ordinal(key_mode_)) {
186
- cursor.scan([&](const mdbx::pair& f) {
187
- keymou key{f.key};
188
- valuemou val{f.value};
189
- // Конвертируем ключ
190
- Napi::Value rc_key = (key_flag_.val & base_flag::bigint) ?
191
- key.to_bigint(env) : key.to_number(env);
192
- // Конвертируем значение
193
- Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
194
- val.to_string(env) : val.to_buffer(env);
195
- // Формируем результат
196
- Napi::Value result = fn.Call({ rc_key, rc_val,
197
- Napi::Number::New(env, static_cast<double>(index)) });
198
-
199
- index++;
200
-
201
- // true will stop the scan, false will continue
202
- return result.IsBoolean() ? result.ToBoolean() : false;
203
- });
204
- } else {
205
- cursor.scan([&](const mdbx::pair& f) {
206
- keymou key{f.key};
207
- valuemou val{f.value};
208
- // Конвертируем ключ
209
- Napi::Value rc_key = (key_flag_.val & base_flag::string) ?
210
- key.to_string(env) : key.to_buffer(env);
211
-
212
- // Конвертируем значение
213
- Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
214
- val.to_string(env) : val.to_buffer(env);
215
- // Формируем результат
216
- Napi::Value result = fn.Call({ rc_key, rc_val,
217
- Napi::Number::New(env, static_cast<double>(index)) });
218
-
219
- index++;
220
-
221
- // true will stop the scan, false will continue
222
- return result.IsBoolean() ? result.ToBoolean() : false;
408
+ cursor.scan([&](const mdbx::pair& f) {
409
+ keymou key{f.key};
410
+ valuemou val{f.value};
411
+ Napi::Value result = fn.Call({
412
+ conv.convert_key(env, key),
413
+ conv.convert_value(env, val),
414
+ Napi::Number::New(env, static_cast<double>(index))
223
415
  });
224
- }
416
+
417
+ index++;
418
+
419
+ // true will stop the scan, false will continue
420
+ return result.IsBoolean() ? result.ToBoolean() : false;
421
+ });
225
422
 
226
423
  return Napi::Number::New(env, static_cast<double>(index));
227
424
  } catch (const std::exception& e) {
@@ -247,12 +444,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info)
247
444
  throw Napi::Error::New(env, "for_each_from: txnmou, fromKey and function required");
248
445
  }
249
446
 
250
- auto arg0 = info[0].As<Napi::Object>();
251
- // Дополнительная проверка - есть ли у объекта нужный constructor
252
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
253
- throw Napi::TypeError::New(env, "for_each_from: first argument must be MDBX_Txn instance");
254
- }
255
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
447
+ auto txn = txnmou::unwrap_checked(env, info[0], "for_each_from");
256
448
 
257
449
  // Определяем позицию функции: info[2] или info[3]
258
450
  Napi::Function fn;
@@ -265,6 +457,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info)
265
457
  }
266
458
 
267
459
  try {
460
+ auto conv = get_convmou();
268
461
  auto cursor = dbi::open_cursor(*txn);
269
462
  auto stat = dbi::get_stat(*txn);
270
463
 
@@ -309,58 +502,30 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info)
309
502
  std::size_t index{};
310
503
  bool is_key_equal_mode = (cursor_mode == move_operation::key_equal ||
311
504
  cursor_mode == move_operation::multi_exactkey_value_equal);
312
- if (mdbx::is_ordinal(key_mode_)) {
313
- cursor.scan_from([&](const mdbx::pair& f) {
314
- keymou key{f.key};
315
- valuemou val{f.value};
316
- if (is_key_equal_mode) {
505
+ cursor.scan_from([&](const mdbx::pair& f) {
506
+ keymou key{f.key};
507
+ valuemou val{f.value};
508
+ if (is_key_equal_mode) {
509
+ if (mdbx::is_ordinal(key_mode_)) {
317
510
  if (t != key.as_int64()) {
318
511
  return true; // останавливаем сканирование
319
512
  }
513
+ } else if (from_key != key) {
514
+ return true; // останавливаем сканирование
320
515
  }
516
+ }
321
517
 
322
- // Конвертируем ключ
323
- Napi::Value rc_key = (key_flag_.val & base_flag::bigint) ?
324
- key.to_bigint(env) : key.to_number(env);
325
- // Конвертируем значение
326
- Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
327
- val.to_string(env) : val.to_buffer(env);
328
- // Формируем результат
329
- Napi::Value result = fn.Call({ rc_key, rc_val,
330
- Napi::Number::New(env, static_cast<double>(index)) });
331
-
332
- index++;
333
-
334
- // true will stop the scan, false will continue
335
- return result.IsBoolean() ? result.ToBoolean() : false;
336
- }, from_key, cursor_mode, turn_mode);
337
- } else {
338
- cursor.scan_from([&](const mdbx::pair& f) {
339
- keymou key{f.key};
340
- valuemou val{f.value};
341
- if (is_key_equal_mode) {
342
- if (from_key != key) {
343
- return true; // останавливаем сканирование
344
- }
345
- }
346
-
347
- // Конвертируем ключ
348
- Napi::Value rc_key = (key_flag_.val & base_flag::string) ?
349
- key.to_string(env) : key.to_buffer(env);
350
-
351
- // Конвертируем значение
352
- Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
353
- val.to_string(env) : val.to_buffer(env);
354
- // Формируем результат
355
- Napi::Value result = fn.Call({ rc_key, rc_val,
356
- Napi::Number::New(env, static_cast<double>(index)) });
518
+ Napi::Value result = fn.Call({
519
+ conv.convert_key(env, key),
520
+ conv.convert_value(env, val),
521
+ Napi::Number::New(env, static_cast<double>(index))
522
+ });
357
523
 
358
- index++;
524
+ index++;
359
525
 
360
- // true will stop the scan, false will continue
361
- return result.IsBoolean() ? result.ToBoolean() : false;
362
- }, from_key, cursor_mode, turn_mode);
363
- }
526
+ // true will stop the scan, false will continue
527
+ return result.IsBoolean() ? result.ToBoolean() : false;
528
+ }, from_key, cursor_mode, turn_mode);
364
529
 
365
530
  return Napi::Number::New(env, static_cast<double>(index));
366
531
  } catch (const std::exception& e) {
@@ -375,12 +540,7 @@ Napi::Value dbimou::stat(const Napi::CallbackInfo& info)
375
540
  if (arg_len < 1) {
376
541
  throw Napi::Error::New(env, "stat: txnmou required");
377
542
  }
378
- auto arg0 = info[0].As<Napi::Object>();
379
- // Дополнительная проверка - есть ли у объекта нужный constructor
380
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
381
- throw Napi::TypeError::New(env, "stat: first argument must be MDBX_Txn instance");
382
- }
383
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
543
+ auto txn = txnmou::unwrap_checked(env, info[0], "stat");
384
544
 
385
545
  try {
386
546
  auto stat = dbi::get_stat(*txn);
@@ -414,14 +574,10 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info)
414
574
  if (arg_len < 1) {
415
575
  throw Napi::Error::New(env, "keys: txnmou required");
416
576
  }
417
- auto arg0 = info[0].As<Napi::Object>();
418
- // Дополнительная проверка - есть ли у объекта нужный constructor
419
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
420
- throw Napi::TypeError::New(env, "keys: first argument must be MDBX_Txn instance");
421
- }
422
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
577
+ auto txn = txnmou::unwrap_checked(env, info[0], "keys");
423
578
 
424
579
  try {
580
+ auto conv = get_convmou();
425
581
  auto stat = get_stat(*txn);
426
582
 
427
583
  // Создаем массив для ключей
@@ -440,9 +596,6 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info)
440
596
  std::array<mdbx::slice, MDBXMOU_BATCH_LIMIT> pairs;
441
597
 
442
598
  uint32_t index{};
443
- auto is_ordinal = mdbx::is_ordinal(key_mode_);
444
- auto is_bigint = key_flag_.val & base_flag::bigint;
445
- auto is_string = key_flag_.val & base_flag::string;
446
599
 
447
600
  // Первый вызов с MDBX_FIRST
448
601
  size_t count = cursor.get_batch(pairs, MDBX_FIRST);
@@ -451,13 +604,7 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info)
451
604
  // pairs[0] = key1, pairs[1] = value1, pairs[2] = key2, ...
452
605
  for (size_t i = 0; i < count; i += 2) {
453
606
  keymou key{pairs[i]};
454
- Napi::Value rc_key;
455
- if (is_ordinal) {
456
- rc_key = is_bigint ? key.to_bigint(env) : key.to_number(env);
457
- } else {
458
- rc_key = is_string ? key.to_string(env) : key.to_buffer(env);
459
- }
460
- keys.Set(index++, rc_key);
607
+ keys.Set(index++, conv.convert_key(env, key));
461
608
  }
462
609
 
463
610
  // Следующий batch
@@ -479,14 +626,10 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info)
479
626
  if (arg_len < 2) {
480
627
  throw Napi::Error::New(env, "keysFrom: txnmou and 'from' key required");
481
628
  }
482
- auto arg0 = info[0].As<Napi::Object>();
483
- // Дополнительная проверка - есть ли у объекта нужный constructor
484
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
485
- throw Napi::TypeError::New(env, "keysFrom: first argument must be MDBX_Txn instance");
486
- }
487
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
629
+ auto txn = txnmou::unwrap_checked(env, info[0], "keysFrom");
488
630
 
489
631
  try {
632
+ auto conv = get_convmou();
490
633
  auto cursor = dbi::open_cursor(*txn);
491
634
 
492
635
  // Парсим аргументы: txn, from, limit, cursorMode
@@ -530,51 +673,28 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info)
530
673
  Napi::Array keys = Napi::Array::New(env);
531
674
  uint32_t index{};
532
675
  bool is_key_equal_mode = (cursor_mode == move_operation::key_equal ||
533
- cursor_mode == move_operation::multi_exactkey_value_equal);
676
+ cursor_mode == move_operation::multi_exactkey_value_equal);
534
677
 
535
- if (mdbx::is_ordinal(key_mode_)) {
536
- cursor.scan_from([&](const mdbx::pair& f) {
537
- if (index >= count) {
538
- return true; // останавливаем сканирование
539
- }
540
-
541
- keymou key{f.key};
542
- if (is_key_equal_mode) {
678
+ cursor.scan_from([&](const mdbx::pair& f) {
679
+ if (index >= count) {
680
+ return true; // останавливаем сканирование
681
+ }
682
+
683
+ keymou key{f.key};
684
+ if (is_key_equal_mode) {
685
+ if (mdbx::is_ordinal(key_mode_)) {
543
686
  if (t != key.as_int64()) {
544
687
  return true; // останавливаем сканирование
545
688
  }
546
- }
547
-
548
- // Конвертируем ключ
549
- Napi::Value rc_key = (key_flag_.val & base_flag::bigint) ?
550
- key.to_bigint(env) : key.to_number(env);
551
-
552
- keys.Set(index++, rc_key);
553
-
554
- return false; // продолжаем сканирование
555
- }, from_key, cursor_mode, turn_mode);
556
- } else {
557
- cursor.scan_from([&](const mdbx::pair& f) {
558
- if (index >= count) {
689
+ } else if (from_key != key) {
559
690
  return true; // останавливаем сканирование
560
691
  }
692
+ }
561
693
 
562
- keymou key{f.key};
563
- if (is_key_equal_mode) {
564
- if (from_key != key) {
565
- return true; // останавливаем сканирование
566
- }
567
- }
568
-
569
- // Конвертируем ключ
570
- Napi::Value rc_key = (key_flag_.val & base_flag::string) ?
571
- key.to_string(env) : key.to_buffer(env);
572
-
573
- keys.Set(index++, rc_key);
574
-
575
- return false; // продолжаем сканирование
576
- }, from_key, cursor_mode, turn_mode);
577
- }
694
+ keys.Set(index++, conv.convert_key(env, key));
695
+
696
+ return false; // продолжаем сканирование
697
+ }, from_key, cursor_mode, turn_mode);
578
698
 
579
699
  return keys;
580
700
  } catch (const std::exception& e) {
@@ -584,17 +704,28 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info)
584
704
  return env.Undefined();
585
705
  }
586
706
 
707
+ Napi::Value dbimou::get_range(const Napi::CallbackInfo& info)
708
+ {
709
+ return run_range_query(info, *this, "getRange", range_output::items);
710
+ }
711
+
712
+ Napi::Value dbimou::keys_range(const Napi::CallbackInfo& info)
713
+ {
714
+ return run_range_query(info, *this, "keysRange", range_output::keys);
715
+ }
716
+
717
+ Napi::Value dbimou::values_range(const Napi::CallbackInfo& info)
718
+ {
719
+ return run_range_query(info, *this, "valuesRange", range_output::values);
720
+ }
721
+
587
722
  Napi::Value dbimou::drop(const Napi::CallbackInfo& info)
588
723
  {
589
724
  Napi::Env env = info.Env();
590
725
  if (info.Length() < 1) {
591
726
  throw Napi::TypeError::New(env, "First argument must be a transaction");
592
727
  }
593
- auto arg0 = info[0].As<Napi::Object>();
594
- if (!arg0.InstanceOf(txnmou::ctor.Value())) {
595
- throw Napi::TypeError::New(env, "First argument must be a txnmou instance");
596
- }
597
- auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
728
+ auto txn = txnmou::unwrap_checked(env, info[0], "drop");
598
729
  bool delete_db = false;
599
730
  if (info.Length() > 1 && info[1].IsBoolean()) {
600
731
  delete_db = info[1].As<Napi::Boolean>().Value();
@@ -607,4 +738,4 @@ Napi::Value dbimou::drop(const Napi::CallbackInfo& info)
607
738
  return env.Undefined();
608
739
  }
609
740
 
610
- } // namespace mdbxmou
741
+ } // namespace mdbxmou