MarkLogicStore — MarkLogic multi-model store
Read this page in the documentation
MarkLogicStore — MarkLogic multi-model store Overview MarkLogic is a multi-model database (document, graph/RDF triples, search, relational views) where documents are addressed by an opaque URI. That access pattern does not fit the SQL-shaped Dialect interface, so MarkLogicStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes MarkLogic's real documents.write/read/remove plus documents.query API through a small document-CRUD convenience layer. Collection + id are mapped to a URI of the form /<collection>/<id>.json. Identity: Property | Value | --------- | ------------- | name | 'marklogic' | library | 'marklogic' | The store is built on the official marklogic driver. Its operations use the driver's chained-then-.result() pattern (client.documents.read(uri).result()), and collection queries are built with the driver's queryBuilder. Lazy loading — not a hard dependency marklogic is not a hard dependency of this package. It is an optional peer dependency loaded lazily via require('marklogic') inside connect(), rather than a top-level import. Importing this module therefore does not require the driver to be installed — it is only needed when a MarkLogic store is actually connected without an injected client. Injected client MarkLogicStoreOptions accepts a pre-built client (a native MarkLogic database client) and, optionally, a pre-built queryBuilder. When client is provided, connect() uses it directly; if queryBuilder is not also injected, the store attempts a lazy require('marklogic').queryBuilder but tolerates its absence. This is how the test suite injects both a mock client and a mock query builder (no driver, no network). Connection Build a store from connection options and call connect(): Without an injected client, connect() calls marklogic.createDatabaseClient({ host, port, user, password, authType, database }). MarkLogicStoreOptions: Option | Type | Purpose | -------------- | ----------------- | --------------------------------------------------------------- | host | string | Server host. | port | number | App-server port, e.g. 8000. | user | string | Username passed to createDatabaseClient. | password | string | Password. | authType | string | Authentication type, e.g. 'DIGEST', 'BASIC'. | database | string | Target database name. | client | MarkLogicClient | A pre-built client to use directly. When set, the driver is not loaded to build a client. | queryBuilder | QueryBuilder | A pre-built queryBuilder to use instead of the driver's. | Injected-client form Supply your own client and builder (or mocks) to bypass driver-based construction: Methods Each CRUD method targets the URI /<collection>/<id>.json and drives the driver's documents.(...).result() chain. Driver failures are wrapped in a DatabaseError (preserving the original error); using any method before connect() (or after disconnect()) throws a ConnectionError, and building a default collection query without an available queryBuilder also throws ConnectionError. Lifecycle Method | Signature | Behavior | ------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client/queryBuilder if provided, otherwise lazy-requires marklogic and calls createDatabaseClient. Idempotent when already connected. Wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | Drops the client reference (the client is stateless over HTTP). A later call must connect() again. | isConnected | isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): MarkLogicClient | Returns the underlying client. Throws ConnectionError if not connected. | Document CRUD & query Method | Signature | Behavior | -------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | insert | insert(collection: string, doc: Record<string, unknown>): Promise<unknown> | Writes the doc via documents.write({ uri, contentType: 'application/json', collections: [collection], content: doc }).result(). Uses doc.id if present, otherwise generates one. Returns the id used. | get | get(collection: string, id: string): Promise<unknown> | Reads via documents.read(uri).result(). Returns the first document's content. | update | update(collection: string, id: string, patch: Record<string, unknown>): Promise<unknown> | Reads the current content, shallow-merges patch, and re-writes it. Returns the merged content. | delete | delete(collection: string, id: string): Promise<unknown> | Removes via documents.remove(uri).result(). Returns the raw driver response. | query | query(collection: string, builtQuery?: unknown): Promise<unknown[]> | Runs documents.query(query).result(). When builtQuery is omitted, builds queryBuilder.where(queryBuilder.collection(collection)). Returns the result documents (or []). | Example Verification status Unit / mock-verified only. The tests in tests/nosql/marklogic.test.ts are fully mock-driven: a mock client (whose documents. return objects with a result() promise) and a mock queryBuilder are injected via options. The real marklogic package is not installed, and there is no live MarkLogic access and no network in the test run. What this proves: insert writes to the /<collection>/<id>.json URI with the right collections/content, using an explicit id or generating one, and returns the id. get reads the URI and returns content; update reads-merges-rewrites and returns the merged content; delete removes the URI. query builds a collection query via queryBuilder.where(queryBuilder.collection(...)) when no query is given, and passes a provided query straight through. Lifecycle: connecting via an injected client without loading the driver, clean disconnect(), and ConnectionError before connect(). Error handling: driver failures wrapped in DatabaseError for both CRUD and query paths. What this does not prove: live execution against a real MarkLogic server. The call/response shapes are verified against the driver's documented contract, but end-to-end execution over the wire has not been exercised here. Related reading All data stores — the full catalogue, grouped by purpose Database types — where this sits among the 22 categories