SolrStore — Apache Solr search engine
Read this page in the documentation
SolrStore — Apache Solr search engine Overview Apache Solr is a search engine built on Lucene. Documents are schema-flexible JSON records indexed into a core (or a collection in SolrCloud), and the primary operation is a Lucene/DisMax query (q, fq, fl, sort, start/rows, faceting) against that inverted index — not SQL, not joins, not a transaction log. Writes are buffered until an explicit commit makes them searchable. None of that fits the SQL-shaped Dialect interface used by the SQL dialects in this repo, so SolrStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes Solr's real operations (add, search, delete, commit, optimize) directly. Identity: Property | Value | --------- | --------------- | name | 'solr' | library | 'solr-client' | Lazy loading — not a hard dependency The solr-client driver is not a hard dependency of this package. It is loaded lazily via require('solr-client') inside connect() rather than a top-level import, so importing this module does not require the driver to be installed — it is only needed when a Solr store is actually connected without an injected client. Driver call shape (callback vs promise) The classic solr-client API is Node-style callbacks: client.add(docs, (err, obj) => ...), client.commit(cb), client.search(query, cb), etc. Some builds/forks instead return a Promise when no callback is passed. To work with either, every call goes through an internal invoke() helper, which passes a callback and inspects the return value: if the driver returned a thenable it is awaited directly (the callback is ignored), otherwise the callback settles the Promise. Injected client SolrStoreOptions.client accepts a pre-built solr-client client (typed as SolrClientLike). When provided, connect() skips the require('solr-client') / createClient() step and uses it directly. This is how the test suite injects a mock client (no driver, no network). Connection Build a store from connection options and call connect(). The whole options object is forwarded to solr.createClient(...), so any additional driver options (protocol, secure, bigint, etc.) pass straight through. Option | Type | Purpose | ---------- | ------------------ | ------------------------------------------------------------------- | host | string | Solr host. Defaults to the driver's own default (127.0.0.1). | port | number \| string | Solr port. Defaults to the driver's own default (8983). | core | string | The core (or SolrCloud collection) to target. | path | string | Base path Solr is mounted under. Defaults to the driver's /solr. | protocol | string | http or https. | secure | boolean | Use HTTPS. | client | SolrClientLike | A pre-built client to use directly (mock or custom config). | [key] | unknown | Any other option accepted by solr.createClient(...). | Injected-client form Methods Every operation goes through an internal exec() wrapper: driver failures are wrapped in a DatabaseError (preserving the original error, prefixed e.g. Solr ADD failed: ...); using any method before connect() (or after disconnect()) throws a ConnectionError. Lifecycle Method | Signature | Behavior | ------------ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires solr-client and builds a client via createClient(...). Wraps failures in ConnectionError (mapping ECONNREFUSED/connect failed to a 'Connection refused' message). Idempotent when already connected. | disconnect | disconnect(): Promise<void> | Drops the client reference (the solr-client is a stateless HTTP client — no socket to close). | isConnected| isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): SolrClientLike | Returns the underlying solr-client client. Throws ConnectionError if not connected. | Operations Method | Signature | Behavior | --------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | add | add(docs: Record<string, unknown> \| Array<Record<string, unknown>>): Promise<unknown> | Index one or more documents, then commit so they become searchable. Returns the raw add result. Batch multiple adds before a single commit via getClient(). | search | search(query: unknown): Promise<unknown> | Run a query. Pass a raw query object/string (e.g. 'q=title:solr&rows=10') or a query built by query(). Returns the driver's raw response ({ responseHeader, response: { numFound, docs, ... } }). | query | query(): unknown | Build a query with the driver's fluent builder (.q(...), .start(...), .rows(...), .sort(...)), when client.query() exists. Throws DatabaseError if this driver build lacks a builder — construct a raw query and pass it to search() instead. Synchronous (not a Promise). | deleteByID | deleteByID(id: string \| number): Promise<unknown> | Delete a single document by its unique-key value, then commit. | deleteByQuery | deleteByQuery(query: string): Promise<unknown> | Delete every document matching a Solr query (e.g. 'category:obsolete'), then commit. | commit | commit(): Promise<unknown> | Commit buffered writes so they become visible to search. | optimize | optimize(): Promise<unknown> | Optimize the index (merge segments). Optional — throws DatabaseError if this solr-client build does not expose optimize. | Example Verification status Unit / mock-verified only. The tests in tests/nosql/solr.test.ts are fully mock-driven: a fake client with add/search/delete/commit/optimize spies is injected via SolrStoreOptions.client. The real solr-client package is not installed, and there is no live Solr access and no network in the test run. What this proves: Each method calls the correct driver method with the expected arguments, and add/deleteByID/deleteByQuery each follow up with a commit. search passes a raw query through unchanged and returns the driver's response shape; query() returns the driver's builder, or throws DatabaseError when no builder is exposed. optimize throws DatabaseError when the driver lacks optimize. Lifecycle: connecting via an injected client without the driver, clean disconnect, ConnectionError before connect(), and ConnectionError when the driver require() fails (driver absent, no client injected). Both call conventions: promise-returning mocks (resolved via invoke()'s thenable branch) and Node-style callback mocks are exercised, with callback errors surfacing as DatabaseError. What this does not prove: live execution against a real Solr core/collection. Behavior is verified against the driver's documented contract, not end-to-end over the wire. Related reading All data stores — the full catalogue, grouped by purpose Database types — where this sits among the 22 categories