npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mdbxmou

v0.3.13

Published

Node bindings for mdbx

Downloads

326

Readme

mdbxmou

Node.js binding for libmdbx — a fast, lightweight, embedded key-value database.

Features

  • Synchronous API — Direct MDBX operations in main thread
  • Asynchronous API — Background operations with async/await
  • Transactions — ACID transactions with read/write modes
  • Multiple key/value types — String, binary, ordinal keys and ordinal duplicate values
  • Batch operations — Efficient multi-key read/write
  • Memory-mapped — High-performance memory-mapped I/O

Installation

npm install mdbxmou

Quick Start

CommonJS:

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

async function main() {
  // Create environment
  const env = new MDBX_Env();
  await env.open({ 
    path: './data',
    keyFlag: MDBX_Param.keyFlag.string,    // Default key encoding (optional)
    valueFlag: MDBX_Param.valueFlag.string // Default value encoding (optional)
  });

  // Write data
  const txn = env.startWrite();
  const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
  dbi.put(txn, 1, "hello");
  dbi.put(txn, 2, "world");
  txn.commit();

  // Read data
  const readTxn = env.startRead();
  const readDbi = readTxn.openMap(BigInt(MDBX_Param.keyMode.ordinal));
  const value = readDbi.get(readTxn, 1);
  console.log(value); // "hello"
  readTxn.commit();

  await env.close();
}

main().catch(console.error);

ESM:

import { MDBX_Env, MDBX_Param } from "mdbxmou";

API Reference

Environment (MDBX_Env)

Constructor

const env = new MDBX_Env();

Methods

open(options) → Promise

await env.open({
  path: './database',           // Database directory
  keyFlag: MDBX_Param.keyFlag.string,    // Default key encoding (optional)
  valueFlag: MDBX_Param.valueFlag.string, // Default value encoding (optional)
  flags: MDBX_Param.envFlag.nostickythreads
});

Options:

  • path - Database directory path
  • flags - Environment flags (optional, defaults to 0)
  • keyFlag - Default key encoding for all operations (optional, defaults to Buffer)
    • Only string can be set (ordinal mode uses number/bigint separately)
  • valueFlag - Default value encoding for all operations (optional, defaults to Buffer)
    • string affects normal string values
    • number and bigint are meaningful for valueMode.multiOrdinal
  • maxDbi - Maximum number of databases (optional, default 32)
  • mode - Filesystem permissions mode (optional, default 0664)
  • geometry - Map size/geometry options (optional)

Note: When keyFlag or valueFlag are set at environment level, they become defaults for all subsequent operations unless explicitly overridden.

Key Type Configuration

The library uses a two-level system for configuring key types:

Level 1: Environment (keyFlag)

When opening the environment, keyFlag controls how string/binary keys are returned:

| keyFlag value | String/Binary keys returned as | |-----------------|-------------------------------| | 0 (default) | Buffer | | keyFlag.string (2) | String |

// Keys returned as Buffer (default)
await env.open({ path: './data' });

// Keys returned as String
await env.open({ 
  path: './data',
  keyFlag: MDBX_Param.keyFlag.string 
});

Level 2: Database (keyMode)

When opening/creating a database, the argument type determines how ordinal (integer) keys are returned:

| Argument type | Ordinal keys returned as | |---------------|-------------------------| | Number | Number | | BigInt | BigInt |

// Ordinal keys as Number
const dbi = txn.openMap(MDBX_Param.keyMode.ordinal);        // keyMode.ordinal = 8
dbi.keys(txn);  // [0, 1, 2, 3, ...]

// Ordinal keys as BigInt  
const dbi = txn.openMap(BigInt(MDBX_Param.keyMode.ordinal)); // BigInt(8)
dbi.keys(txn);  // [0n, 1n, 2n, 3n, ...]

Summary

| Key type | Configuration level | Option | Result type | |----------|--------------------|--------------------|-------------| | String/Binary | env.open() | keyFlag: 0 | Buffer | | String/Binary | env.open() | keyFlag: keyFlag.string | String | | Ordinal | openMap/createMap | keyMode.ordinal (Number) | Number | | Ordinal | openMap/createMap | BigInt(keyMode.ordinal) | BigInt |

close() → Promise

await env.close();

openSync(options)

env.openSync({
  path: './database',
  valueFlag: MDBX_Param.valueFlag.string
});

closeSync()

env.closeSync();

setOption(option, value)

env.setOption(MDBX_Param.envOption.syncBytes, 4 * 1024 * 1024);
env.setOption(MDBX_Param.envOption.syncPeriod, 0.2); // 200 ms

Note: syncPeriod is passed in seconds and may be fractional. Other options are passed as non-negative integer values. Note: maxDb and maxReaders are pre-open options. Set them through open/openSync (maxDbi, maxReaders) instead of setOption().

syncEx(force, nonblock) → number

env.syncEx(true, false);

startWrite() → Transaction

const txn = env.startWrite();

startRead() → Transaction

const txn = env.startRead();

query(requests) → Promise (Async batch operations)

const result = await env.query([
  {
    dbi,
    mode: MDBX_Param.queryMode.get,
    item: [{ key: 1 }, { key: 2 }]
  },
  {
    dbi,
    mode: MDBX_Param.queryMode.upsert,
    putFlag: MDBX_Param.putFlag.noOverwrite,
    item: [{ key: 3, value: "v3" }]
  }
]);

query() uses the passed dbi and inherits key/value settings from it. queryMode selects the operation (get, del, or base write mode), and optional putFlag adds write-only MDBX flags. In query() only noOverwrite, noDupData, current, append, and appendDup are supported.

Transaction

Methods

createMap([db_name | keyMode], [keyMode | valueMode], [valueMode]) → DBI createMap({ name, keyFlag, valueFlag, keyMode, valueMode }) → DBI

// No arguments - default DB with env key/value flags (buffer if not set)
const dbi = txn.createMap();

// One argument - keyMode only
const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);

// One argument - db_name only (uses default keyMode)
const namedDbi = txn.createMap("my-table");

// Two arguments - keyMode + valueMode
const dbi = txn.createMap(MDBX_Param.keyMode.ordinal, MDBX_Param.valueMode.multi);

// Two arguments - db_name + keyMode
const namedDbi = txn.createMap("my-table", MDBX_Param.keyMode.ordinal);

// Three arguments - db_name + keyMode + valueMode  
const namedDbi = txn.createMap("my-table", MDBX_Param.keyMode.ordinal, MDBX_Param.valueMode.multi);

// Object form - explicit flags/modes
const dbi = txn.createMap({
  name: "my-table",
  keyFlag: MDBX_Param.keyFlag.string,
  valueFlag: MDBX_Param.valueFlag.string,
  keyMode: MDBX_Param.keyMode.reverse,
  valueMode: MDBX_Param.valueMode.multi
});

Note: Use createMap in write transactions - it will create the database if it doesn't exist, or open it if it does. This is safer for new environments.

openMap([db_name | keyMode], [keyMode]) → DBI openMap({ name, keyFlag, valueFlag, keyMode, valueMode }) → DBI

// No arguments - default DB with env key/value flags (buffer if not set)
const dbi = txn.openMap();

// One argument - keyMode only
// Number keyMode - keys returned as numbers
const dbi = txn.openMap(MDBX_Param.keyMode.ordinal);
// BigInt keyMode - keys returned as BigInts
const dbi = txn.openMap(BigInt(MDBX_Param.keyMode.ordinal));

// One argument - db_name only (uses default keyMode)
const namedDbi = txn.openMap("my-table");

// Two arguments - db_name + keyMode
const namedDbi = txn.openMap("my-table", MDBX_Param.keyMode.ordinal);
const namedDbiBigInt = txn.openMap("my-table", BigInt(MDBX_Param.keyMode.ordinal));

// Object form - explicit flags/modes
const dbi = txn.openMap({
  name: "my-table",
  keyFlag: MDBX_Param.keyFlag.string,
  valueFlag: MDBX_Param.valueFlag.string,
  keyMode: MDBX_Param.keyMode.reverse,
  valueMode: MDBX_Param.valueMode.multi
});

Note: Use openMap in read transactions or when you're sure the database already exists. For write transactions on new environments, prefer createMap.

Note: When using ordinal keyMode, the key type in results depends on how you specify keyMode:

  • keyMode: number → keys returned as number
  • keyMode: BigInt(number) → keys returned as BigInt
  • When you pass keyMode.ordinal as a positional argument (Number/BigInt), it also updates keyFlag to number/bigint unless a numeric keyFlag was already set in env or explicitly provided.

Note: When valueMode.multiOrdinal is used and valueFlag is not specified, values are returned as number by default. Set valueFlag: MDBX_Param.valueFlag.bigint if you need BigInt on read.

commit()

txn.commit();

abort()

txn.abort();

DBI (Database Instance)

Methods

put(txn, key, value, [flags])

dbi.put(txn, 123, "value");
dbi.put(txn, "key", Buffer.from("binary data"));

// Insert only if key does not exist
dbi.put(txn, 123, "value", MDBX_Param.putFlag.noOverwrite);

// Fast append for sorted inserts
dbi.put(txn, 124, "value", MDBX_Param.putFlag.append);

get(txn, key) → value

const value = dbi.get(txn, 123);
const binary = dbi.get(txn, "key");

For valueMode.multiOrdinal, get() returns the first duplicate value for the key, decoded as number by default.

del(txn, key) → boolean

const deleted = dbi.del(txn, 123);

has(txn, key) → boolean

const exists = dbi.has(txn, 123);

stat(txn) → Object

const stats = dbi.stat(txn);
// { pageSize: 4096, depth: 1, entries: 10, ... }

forEach(txn, callback)

dbi.forEach(txn, (key, value, index) => {
  console.log(`${key}: ${value}`);
  return false; // continue iteration (or undefined)
  // return true; // stop iteration
});

Note: forEach continues scanning while callback returns undefined or false, and stops when callback returns true.

keys(txn) → Array

// Get all keys
const allKeys = dbi.keys(txn);

keysFrom(txn, startKey, [limit], [cursorMode]) → Array

// Get keys starting from specific key
const keys = dbi.keysFrom(txn, 42, 50); // 50 keys starting from 42

// With cursor mode
const keys = dbi.keysFrom(txn, 42, 50, 'keyGreaterOrEqual');

// BigInt keys
const bigIntKeys = dbi.keysFrom(txn, 42n, 50);

// Key equal mode (for multi-value databases)
const equalKeys = dbi.keysFrom(txn, 5, 10, 'keyEqual');

getRange(txn, [options]) → Array<{ key, value }>

const rows = dbi.getRange(txn, { start: 10, end: 15 });
// [
//   { key: 10, value: ... },
//   { key: 11, value: ... },
//   ...
// ]

getCount(txn, [options]) → number

const total = dbi.getCount(txn, { start: 10, end: 20 });
// 11

keysRange(txn, [options]) → Array

const keys = dbi.keysRange(txn, { start: 10, end: 20, limit: 5 });
// [10, 11, 12, 13, 14]

valuesRange(txn, [options]) → Array

const values = dbi.valuesRange(txn, {
  start: 10,
  end: 15,
  reverse: true,
  includeEnd: false
});
// values for keys 14, 13, 12, 11, 10

Range options:

  • start, end - inclusive bounds by default
  • includeStart, includeEnd - control bound inclusion
  • reverse - scan from upper bound to lower bound
  • limit - maximum number of returned items
  • offset - skip N items after initial positioning
  • getCount() ignores offset and limit and returns the total size of the bounded range

drop(txn, [delete_db]) → void

// Clear database contents (keep structure)
dbi.drop(txn, false);

// Delete database completely
dbi.drop(txn, true);

// Default behavior (clear contents)
dbi.drop(txn);

Cursor (MDBX_Cursor)

Cursors provide low-level control for database traversal with positioning and iteration capabilities.

Creating a Cursor

const txn = env.startRead();
const dbi = txn.openMap();
const cursor = txn.openCursor(dbi);

Navigation Methods

first() → {key, value} | undefined

const item = cursor.first();
if (item) {
  console.log(item.key, item.value);
}

last() → {key, value} | undefined

const item = cursor.last();

next() → {key, value} | undefined

const item = cursor.next();

prev() → {key, value} | undefined

const item = cursor.prev();

current() → {key, value} | undefined

const item = cursor.current();

Search Methods

seek(key) → {key, value} | undefined

Exact key match. Returns undefined if key not found.

const item = cursor.seek('user:123');
if (item) {
  console.log('Found:', item.value);
}

seekGE(key) → {key, value} | undefined

Find first key greater or equal to given key (lower_bound).

const item = cursor.seekGE('user:100');
// Returns first key >= 'user:100'

Modification Methods

put(key, value, [flags])

Insert or update a record at cursor position.

cursor.put('newKey', 'newValue');

// With flags (MDBX_NOOVERWRITE, etc.)
cursor.put('key', 'value', MDBX_Param.putFlag.noOverwrite);

del([flags]) → boolean

Delete record at current cursor position. Returns true if deleted, false if not found.

cursor.seek('keyToDelete');
const deleted = cursor.del();

Control Methods

close()

cursor.close();

Cursor Examples

Iterate all records:

const cursor = txn.openCursor(dbi);

for (let item = cursor.first(); item; item = cursor.next()) {
  console.log(item.key, item.value.toString());
}

cursor.close();
txn.abort();

Range iteration:

const cursor = txn.openCursor(dbi);

// Find all keys starting with 'user:'
for (let item = cursor.seekGE('user:'); item; item = cursor.next()) {
  if (!item.key.startsWith('user:')) break;
  console.log(item.key, item.value.toString());
}

cursor.close();

Pagination:

function getPage(cursor, offset, limit) {
  const results = [];
  
  let item = cursor.first();
  
  // Skip offset
  for (let i = 0; i < offset && item; i++) {
    item = cursor.next();
  }
  
  // Collect limit items
  for (let i = 0; i < limit && item; i++) {
    results.push({ key: item.key, value: item.value.toString() });
    item = cursor.next();
  }
  
  return results;
}

const txn = env.startRead();
const dbi = txn.openMap();
const cursor = txn.openCursor(dbi);

const page1 = getPage(cursor, 0, 10);   // First 10 items
const page2 = getPage(cursor, 10, 10);  // Next 10 items

cursor.close();
txn.abort();

Reverse iteration:

const cursor = txn.openCursor(dbi);

for (let item = cursor.last(); item; item = cursor.prev()) {
  console.log(item.key, item.value.toString());
}

cursor.close();

Bulk insert with cursor:

const txn = env.startWrite();
const dbi = txn.createMap();
const cursor = txn.openCursor(dbi);

for (let i = 0; i < 1000; i++) {
  cursor.put(`key${i}`, `value${i}`);
}

cursor.close();
txn.commit();

Delete with cursor:

const txn = env.startWrite();
const dbi = txn.openMap();
const cursor = txn.openCursor(dbi);

// Delete specific key
if (cursor.seek('keyToDelete')) {
  cursor.del();
}

// Delete range
for (let item = cursor.seekGE('prefix:'); item; item = cursor.next()) {
  if (!item.key.startsWith('prefix:')) break;
  cursor.del();
}

cursor.close();
txn.commit();

Key and Value Types

Key Modes (MDBX_Param.keyMode)

  • Default (0) - Buffer keys (no flags, default behavior)
  • reverse - Keys sorted in reverse order
  • ordinal - Integer keys (4 or 8 bytes, native endian)

Value Modes (MDBX_Param.valueMode)

  • single - Single value per key (default)
  • multi - MDBX_DUPSORT, multiple values per key
  • multiReverse - MDBX_DUPSORT | MDBX_REVERSEDUP
  • multiSamelength - MDBX_DUPSORT | MDBX_DUPFIXED
  • multiOrdinal - MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP
  • multiReverseSamelength - MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_DUPFIXED

Value Flags (MDBX_Param.valueFlag)

  • binary - Raw binary data (default, represented by 0)
  • string - UTF-8 strings
  • number - Numeric decode for ordinal duplicate values
  • bigint - BigInt decode for ordinal duplicate values

valueFlag.number and valueFlag.bigint matter primarily for valueMode.multiOrdinal. For ordinary values, use Buffer (default) or valueFlag.string.

Examples

Basic Usage (Synchronous)

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

function syncExample() {
  const env = new MDBX_Env();
  
  // Synchronous open
  env.openSync({ path: './data' });

  // Write transaction
  const writeTxn = env.startWrite();
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
  
  for (let i = 0; i < 1000; i++) {
    dbi.put(writeTxn, i, `value_${i}`);
  }
  writeTxn.commit();

  // Read transaction with number keys
  const readTxn = env.startRead();
  const readDbi = readTxn.openMap(MDBX_Param.keyMode.ordinal); // keys as numbers
  
  const value = readDbi.get(readTxn, 42);
  console.log(value); // "value_42"
  
  // Iterate with cursor
  readDbi.forEach(readTxn, (key, value, index) => {
    console.log(`Key ${key} (type: ${typeof key}): ${value}`); // key is number
    return index >= 9; // stop after 10 items (indices 0-9)
  });
  
  // Get specific keys
  const someKeys = readDbi.keysFrom(readTxn, 100, 50);
  console.log(`Keys 100-149:`, someKeys); // array of numbers
  
  readTxn.commit();
  
  // Synchronous close
  env.closeSync();
}

async function asyncExample() {
  const env = new MDBX_Env();
  await env.open({ path: './data-async' });

  // Write transaction  
  const writeTxn = env.startWrite();
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
  
  for (let i = 0; i < 1000; i++) {
    dbi.put(writeTxn, i, `value_${i}`);
  }
  writeTxn.commit();

  // Read transaction with BigInt keys
  const readTxn = env.startRead();
  const readDbi = readTxn.openMap(BigInt(MDBX_Param.keyMode.ordinal)); // keys as BigInts
  
  const value = readDbi.get(readTxn, 42);
  console.log(value); // "value_42"
  
  // Iterate with BigInt keys
  readDbi.forEach(readTxn, (key, value, index) => {
    console.log(`Key ${key} (type: ${typeof key}): ${value}`); // key is bigint
    return index >= 9; // stop after 10 items (indices 0-9)
  });
  
  // Get BigInt keys
  const bigIntKeys = readDbi.keysFrom(readTxn, 100n, 50);
  console.log(`Keys 100n-149n:`, bigIntKeys); // array of BigInts
  
  readTxn.commit();
  await env.close();
}

syncExample();
asyncExample().catch(console.error);

Key Type Behavior

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

function keyTypesExample() {
  const env = new MDBX_Env();
  env.openSync({ path: './key-types' });

  const txn = env.startWrite();
  const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
  
  // Store some data
  dbi.put(txn, 1, "one");
  dbi.put(txn, 2, "two");
  dbi.put(txn, 3, "three");
  txn.commit();

  // Read with number keyMode
  const readTxn1 = env.startRead();
  const numberDbi = readTxn1.openMap(MDBX_Param.keyMode.ordinal);
  
  numberDbi.forEach(readTxn1, (key, value) => {
    console.log(`Number key: ${key} (${typeof key})`); // number
    // return undefined; // continue iteration (default)
  });
  readTxn1.commit();

  // Read with BigInt keyMode  
  const readTxn2 = env.startRead();
  const bigintDbi = readTxn2.openMap(BigInt(MDBX_Param.keyMode.ordinal));
  
  bigintDbi.forEach(readTxn2, (key, value) => {
    console.log(`BigInt key: ${key} (${typeof key})`); // bigint
    // return false; // continue iteration
  });
  readTxn2.commit();

  env.closeSync();
}

keyTypesExample();

Cursor Operations

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

function cursorExample() {
  const env = new MDBX_Env();
  env.openSync({ path: './cursor-data' });

  const txn = env.startWrite();
  const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
  
  // Store test data
  for (let i = 0; i < 100; i++) {
    dbi.put(txn, i, `value_${i}`);
  }
  txn.commit();

  const readTxn = env.startRead();
  const readDbi = readTxn.openMap(MDBX_Param.keyMode.ordinal);
  
  // Get all keys
  const allKeys = readDbi.keys(readTxn);
  console.log(`Total keys: ${allKeys.length}`);
  
  // Get limited keys - use keysFrom with limit
  const firstTen = readDbi.keysFrom(readTxn, 0, 10);
  console.log(`First 10 keys:`, firstTen);
  
  // Get keys from specific position
  const fromFifty = readDbi.keysFrom(readTxn, 50, 20);
  console.log(`Keys 50-69:`, fromFifty);
  
  // Reverse iteration - need manual logic or forEach
  const allKeysForReverse = readDbi.keys(readTxn);
  const lastTen = allKeysForReverse.slice(-10).reverse();
  console.log(`Last 10 keys:`, lastTen);
  
  // Manual iteration with forEach
  let count = 0;
  readDbi.forEach(readTxn, (key, value, index) => {
    if (key >= 80) {
      console.log(`Key ${key}: ${value}`);
      count++;
    }
    return count >= 5; // stop after 5 items >= 80
  });
  
  readTxn.commit();
  env.closeSync();
}

cursorExample();

Range Queries

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

function rangeExample() {
  const env = new MDBX_Env();
  env.openSync({
    path: './range-data',
    valueFlag: MDBX_Param.valueFlag.string
  });

  const writeTxn = env.startWrite();
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
  for (let i = 0; i < 10; i++) {
    dbi.put(writeTxn, i, `value_${i}`);
  }
  writeTxn.commit();

  const readTxn = env.startRead();
  const readDbi = readTxn.openMap(MDBX_Param.keyMode.ordinal);

  const rows = readDbi.getRange(readTxn, { start: 3, end: 6 });
  console.log(rows);
  // [
  //   { key: 3, value: 'value_3' },
  //   { key: 4, value: 'value_4' },
  //   { key: 5, value: 'value_5' },
  //   { key: 6, value: 'value_6' }
  // ]

  const total = readDbi.getCount(readTxn, { start: 3, end: 8 });
  console.log(total); // 6

  const keys = readDbi.keysRange(readTxn, {
    start: 3,
    end: 8,
    offset: 1,
    limit: 3
  });
  console.log(keys); // [4, 5, 6]

  const values = readDbi.valuesRange(readTxn, {
    start: 3,
    end: 6,
    reverse: true,
    includeEnd: false
  });
  console.log(values); // ['value_5', 'value_4', 'value_3']

  readTxn.commit();
  env.closeSync();
}

rangeExample();

MultiOrdinal Values

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

function multiOrdinalExample() {
  const env = new MDBX_Env();
  env.openSync({ path: './multi-ordinal-data' });

  const writeTxn = env.startWrite();
  const dbi = writeTxn.createMap({
    name: 'dup-ids',
    keyMode: MDBX_Param.keyMode.ordinal,
    valueMode: MDBX_Param.valueMode.multiOrdinal
  });

  dbi.put(writeTxn, 5, 30);
  dbi.put(writeTxn, 5, 10);
  dbi.put(writeTxn, 5, 20n);
  writeTxn.commit();

  const readTxn = env.startRead();
  const readDbi = readTxn.openMap({
    name: 'dup-ids',
    keyMode: MDBX_Param.keyMode.ordinal,
    valueMode: MDBX_Param.valueMode.multiOrdinal
  });

  console.log(readDbi.get(readTxn, 5)); // 10
  console.log(readDbi.valuesRange(readTxn, { start: 5, end: 5 })); // [10, 20, 30]

  readTxn.commit();
  env.closeSync();
}

multiOrdinalExample();

Query API (Advanced Async)

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

async function queryExample() {
  const env = new MDBX_Env();
  await env.open({ path: './query-data' });

  // Create DBI first in synchronous transaction
  const writeTxn = env.startWrite();
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
  writeTxn.commit();

  // Async query with DBI object (not database name)
  const results = await env.query([
    {
      dbi,
      mode: MDBX_Param.queryMode.upsert,
      putFlag: MDBX_Param.putFlag.noOverwrite,
      item: [
        { key: 1, value: JSON.stringify({ name: "Alice" }) },
        { key: 2, value: JSON.stringify({ name: "Bob" }) }
      ]
    },
    {
      dbi,
      mode: MDBX_Param.queryMode.get,
      item: [
        { key: 1 },
        { key: 2 }
      ]
    }
  ]);

  console.log('Query results:', JSON.stringify(results, null, 2));
  await env.close();
}

queryExample().catch(console.error);

Async Keys API

const { MDBX_Env, MDBX_Param } = require('mdbxmou');

async function keysExample() {
  const env = new MDBX_Env();
  await env.open({ path: './keys-data' });

  // Create DBI first
  const writeTxn = env.startWrite();
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
  
  // Add some test data
  for (let i = 1; i <= 10; i++) {
    dbi.put(writeTxn, i, `value-${i}`);
  }
  writeTxn.commit();

  // Get all keys from DBI
  const allKeys = await env.keys(dbi);
  console.log("All keys:", allKeys);

  // Get keys with DBI object parameter  
  const allKeys2 = await env.keys({ dbi });
  console.log("All keys (object):", allKeys2);

  // Get keys from multiple DBIs
  const multiKeys = await env.keys([dbi, dbi]);
  console.log("Multi DBI keys:", multiKeys);

  // Get limited keys from specific position
  const limitedKeys = await env.keys([
    { dbi, limit: 3, from: 5 }
  ]);
  console.log("Limited keys:", limitedKeys);

  await env.close();
}

keysExample().catch(console.error);

Error Handling

async function errorHandlingExample() {
  let txn;
  try {
    const env = new MDBX_Env();
    await env.open({ path: './data' });
    
    txn = env.startWrite();
    const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
    
    // This might throw if key already exists with MDBX_NOOVERWRITE
    dbi.put(txn, 123, "value");
    
    txn.commit();
  } catch (error) {
    console.error('Database error:', error.message);
    if (txn) txn.abort();
  }
}

errorHandlingExample().catch(console.error);

Runnable README Tests

The examples above have matching runnable tests in test/. You can execute them directly:

node test/readme-quick-start.js
node test/readme-sync-example.js
node test/readme-async-example.js
node test/readme-key-types.js
node test/readme-cursor-example.js
node test/readme-range-example.js
node test/readme-query-example.js
node test/readme-keys-example.js
node test/readme-error-handling.js

Configuration Options

Available Constants (MDBX_Param)

const { MDBX_Param } = require('mdbxmou');

// Key modes
MDBX_Param.keyMode.reverse    // MDBX_REVERSEKEY - reverse key order
MDBX_Param.keyMode.ordinal    // MDBX_INTEGERKEY - integer keys (use with number/bigint)
// Default (0) - Buffer keys (no flags)

// Key flags (optional, control key representation)
MDBX_Param.keyFlag.string     // UTF-8 string encoding
MDBX_Param.keyFlag.number     // Number type (used with ordinal mode)
MDBX_Param.keyFlag.bigint     // BigInt type (used with ordinal mode)
// Default - Buffer representation

// Value modes
MDBX_Param.valueMode.multi                 // MDBX_DUPSORT
MDBX_Param.valueMode.multiReverse          // MDBX_DUPSORT | MDBX_REVERSEDUP
MDBX_Param.valueMode.multiSamelength       // MDBX_DUPSORT | MDBX_DUPFIXED
MDBX_Param.valueMode.multiOrdinal          // MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP
MDBX_Param.valueMode.multiReverseSamelength // MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_DUPFIXED

// Value flags (optional, control value representation)
MDBX_Param.valueFlag.string   // UTF-8 string values
MDBX_Param.valueFlag.number   // Number values for multiOrdinal
MDBX_Param.valueFlag.bigint   // BigInt values for multiOrdinal
// Default - Buffer representation

Note: For ordinal (integer) keys, use keyFlag.number or keyFlag.bigint to specify the data type.
For `valueMode.multiOrdinal`, values are returned as `number` by default, or as `bigint` when `valueFlag.bigint` is used.

Environment Flags

  • MDBX_Param.envFlag.nostickythreads - Don't stick reader transactions to threads
  • MDBX_Param.envFlag.rdonly - Open database in read-only mode
  • MDBX_Param.envFlag.validation - Enable page validation
  • MDBX_Param.envFlag.exclusive - Exclusive mode
  • MDBX_Param.envFlag.accede - Open existing environment
  • MDBX_Param.envFlag.writemap - Use writable memory map
  • MDBX_Param.envFlag.nordahead - Disable OS readahead
  • MDBX_Param.envFlag.nomeminit - Disable memory initialization
  • MDBX_Param.envFlag.liforeclaim - LIFO reclaim
  • MDBX_Param.envFlag.nometasync - Disable metadata flushes
  • MDBX_Param.envFlag.safeNosync - Safe nosync mode
  • MDBX_Param.envFlag.utterlyNosync - Utterly nosync mode

Environment Options

  • MDBX_Param.envOption.maxDb - Raw MDBX_opt_max_db option id
  • MDBX_Param.envOption.maxReaders - Raw MDBX_opt_max_readers option id
  • MDBX_Param.envOption.syncBytes - Threshold for forced flushes in weak sync modes
  • MDBX_Param.envOption.syncPeriod - Sync period in seconds when used with setOption()
  • MDBX_Param.envOption.rpAugmentLimit - Raw MDBX_opt_rp_augment_limit
  • MDBX_Param.envOption.looseLimit - Raw MDBX_opt_loose_limit
  • MDBX_Param.envOption.dpReserveLimit - Raw MDBX_opt_dp_reserve_limit
  • MDBX_Param.envOption.txnDpLimit - Raw MDBX_opt_txn_dp_limit
  • MDBX_Param.envOption.txnDpInitial - Raw MDBX_opt_txn_dp_initial
  • MDBX_Param.envOption.spillMaxDenominator - Raw MDBX_opt_spill_max_denominator
  • MDBX_Param.envOption.spillMinDenominator - Raw MDBX_opt_spill_min_denominator
  • MDBX_Param.envOption.spillParent4childDenominator - Raw MDBX_opt_spill_parent4child_denominator
  • MDBX_Param.envOption.mergeThreshold16dot16Percent - Raw MDBX_opt_merge_threshold_16dot16_percent
  • MDBX_Param.envOption.writethroughThreshold - Raw MDBX_opt_writethrough_threshold
  • MDBX_Param.envOption.prefaultWriteEnable - Raw MDBX_opt_prefault_write_enable
  • MDBX_Param.envOption.gcTimeLimit - Raw MDBX_opt_gc_time_limit
  • MDBX_Param.envOption.preferWafInsteadofBalance - Raw MDBX_opt_prefer_waf_insteadof_balance
  • MDBX_Param.envOption.subpageLimit - Raw MDBX_opt_subpage_limit
  • MDBX_Param.envOption.subpageRoomThreshold - Raw MDBX_opt_subpage_room_threshold
  • MDBX_Param.envOption.subpageReservePrereq - Raw MDBX_opt_subpage_reserve_prereq
  • MDBX_Param.envOption.subpageReserveLimit - Raw MDBX_opt_subpage_reserve_limit

Database Modes

  • MDBX_Param.dbMode.create - Create database if it doesn't exist
  • MDBX_Param.dbMode.accede - Open existing database with any flags

Query Modes

  • MDBX_Param.queryMode.get - Read operations
  • MDBX_Param.queryMode.upsert - Base write mode (insert or update)
  • MDBX_Param.queryMode.update - Base write mode with MDBX_CURRENT
  • MDBX_Param.queryMode.insertUnique - Base write mode with MDBX_NOOVERWRITE
  • MDBX_Param.queryMode.del - Delete operations

Put Flags

  • MDBX_Param.putFlag.noOverwrite - MDBX_NOOVERWRITE
  • MDBX_Param.putFlag.noDupData - MDBX_NODUPDATA
  • MDBX_Param.putFlag.current - MDBX_CURRENT
  • MDBX_Param.putFlag.allDups - MDBX_ALLDUPS
  • MDBX_Param.putFlag.reserve - MDBX_RESERVE
  • MDBX_Param.putFlag.append - MDBX_APPEND
  • MDBX_Param.putFlag.appendDup - MDBX_APPENDDUP
  • MDBX_Param.putFlag.multiple - MDBX_MULTIPLE

For env.query() write requests, only noOverwrite, noDupData, current, append, and appendDup are supported.

Cursor Modes

  • MDBX_Param.cursorMode.first - First key
  • MDBX_Param.cursorMode.last - Last key
  • MDBX_Param.cursorMode.next - Next key
  • MDBX_Param.cursorMode.prev - Previous key
  • MDBX_Param.cursorMode.keyLesserThan - Keys less than target
  • MDBX_Param.cursorMode.keyLesserOrEqual - Keys less than or equal to target
  • MDBX_Param.cursorMode.keyEqual - Keys exactly equal to target
  • MDBX_Param.cursorMode.keyGreaterOrEqual - Keys greater than or equal to target
  • MDBX_Param.cursorMode.keyGreaterThan - Keys greater than target

Note: Cursor modes can be used with keysFrom() and forEach() methods to control iteration direction and filtering.

Performance Tips

  1. Use ordinal keys for integer data - much faster than string keys
  2. Batch operations - Use query API for bulk operations
  3. Reuse transactions - Keep read transactions open for multiple operations
  4. Memory mapping - MDBX uses memory-mapped files for fast I/O
  5. Transaction scope - Always pass transaction object to DBI methods

License

Apache License 2.0


Documentation generated by GitHub Copilot (Claude 3.5 Sonnet) on August 27, 2025
Reviewed and approved by the library author