Use query() to send a standard Solr query request. Common options are typed directly on the query object; parser-specific options are grouped under parserOptions and flattened into Solr request parameters. The documented query patterns are also represented by a compile-only consumer fixture in test/query-doc-examples.ts; npm run test:types compiles it on every Node.js CI job, including negative checks for misspelled options.
const result = await solr.query({
query: "title:typescript",
filter: [
"status:published",
{ query: "category:books", tag: "category" }
],
fields: ["id", "title"],
sort: solrSort(
{ expression: "publishedAt", direction: "desc" },
{ expression: "id", direction: "asc" }
),
limit: 25,
offset: 0,
defType: "edismax",
"q.op": "AND",
wt: "json"
});Use the typed debug option to request query parsing, timing, score explanations, or distributed tracking diagnostics. If components is omitted, Solr returns all available debug sections. Structured explanations are available with explainStructured; explainOther requests explanations for documents matching a second Lucene query.
const diagnostic = await solr.query<SolrQueryResponse<Product>>({
query: "title:distributed",
debug: {
components: ["query", "results", "timing"],
explainStructured: true,
explainOther: "id:product-42"
}
});
const parsedQuery = diagnostic.debug?.parsedquery;
const explanation = diagnostic.debug?.explain?.["product-1"];
const explanationDetails = typeof explanation === "string" ? undefined : explanation?.details;componentsacceptsquery,timing,results,track, orall; an array sends repeateddebugparameters.- Supplying
debug: {}enables all debug sections. Setenabled: falseto explicitly disable them. explainStructured: truemaps todebug.explain.structured=true. Response explanations are typed as either plain strings or recursive structured explanation trees.explainOthermust be a non-empty Lucene query and maps to Solr'sexplainOtherparameter.- Debug output can be verbose and may expose query details; enable it selectively. Runtime response behavior for this new typed interface remains unverified on the pinned Solr 8/9/10 matrix.
See the official Solr 10.1 common query parameters reference for supported debug sections, structured explain output, and explainOther. The documented parameter forms are also present in the Solr 8.11 and 9.11 guides.
The filter property accepts one filter or an array. Arrays are sent as repeated fq parameters; raw strings remain unchanged. Structured filters support tags and Solr filter-cache hints:
await solr.query({
query: "*:*",
filter: [
{ query: "category:books", tag: "category" },
{ query: "inStock:true", tag: ["stock", "availability"] },
{ query: "price:[10 TO 50]", cache: false, cost: 25 }
]
});A structured filter serializes to Solr local params, for example {!tag=category}, cache=false, and cost=25. Additional filter-local parameters can be supplied through localParams; use raw strings or solrLocalParams() when you need syntax outside the structured properties.
Tagged filters can be excluded from supported facets or query parsers by passing their tags through the relevant local parameter. For a classic facet field, Solr uses the ex local param:
import { solrExcludeTags, solrLocalParams } from "node-solr-lambda";
await solr.query({
query: "*:*",
filter: [{ query: "category:books", tag: "category" }],
params: {
"facet": "true",
"facet.field": solrLocalParams({ ex: solrExcludeTags("category") }, "category")
}
});Some query parsers instead accept an excludeTags local parameter. Keep it in that parser's local-params prefix, for example solrLocalParams({ type: "parent", which: "type:parent", excludeTags: solrExcludeTags("category") }, query). Exclusion is contextual: this client does not globally remove tagged filters.
Use the typed facets property for the classic Facet Component. It supports field facets, query facets, and numeric or date range facets. Global field-facet defaults can be overridden per field.
const result = await solr.query<SolrQueryResponse<Product>>({
query: "*:*",
facets: {
fields: [{ field: "category_s", limit: 10, sort: "count", minCount: 1 }, "brand_s"],
queries: ["price_d:[0 TO 20]", "inStock_b:true"],
ranges: [
{ field: "price_d", start: 0, end: 100, gap: 10, hardEnd: true, other: "between" },
{ field: "created_dt", start: "NOW/DAY-30DAYS", end: "NOW/DAY", gap: "+1DAY" }
],
limit: 20,
offset: 0,
sort: "count",
minCount: 1,
missing: true,
method: "fc",
rangeMethod: "filter"
}
});
const categoryCounts = result.facet_counts?.facet_fields?.category_s;
const priceRangeCounts = result.facet_counts?.facet_ranges?.price_d?.counts;- Field facets accept raw field names or objects with per-field
limit,offset,sort,minCount,missing,prefix, andmethodoverrides. Field methods areenum,fc, orfcs. - Query facets accept raw Solr query expressions. Query values are sent as repeated
facet.queryparameters. - Range facets accept numeric values or date math strings for
start,end, andgap. They supporthardEnd, repeatedincludeandothervalues, and the range methodsfilteranddv. - Date bucketing is expressed with range facets on a date field. The legacy
facet.dateparameters were removed in Solr 6, so they are not supported by the target Solr 8/9/10 versions. See the Solr 10.1 upgrade notes and Solr faceting reference. facet_counts.facet_fieldsuses Solr's alternating term/count array representation. Query counts are infacet_queries; numeric and date range counts are infacet_ranges.- The
paramsescape hatch remains available for facet parameters not represented by these models. Runtime behavior is covered by the pinned Solr integration matrix; verify any server-specific or uncommon facet option against the version you deploy.
Use the typed spatial property for a circular geofilt filter or its bounding-box approximation bbox. The client emits the parser filter and associated pt and d request parameters. The point string is passed through unchanged; for geodetic fields Solr expects latitude,longitude order.
const nearby = await solr.query({
query: "*:*",
spatial: { parser: "geofilt", field: "store_location", point: "45.15,-93.85", distance: 5 },
sort: "geodist() asc"
});
await solr.query({
query: "*:*",
spatial: { parser: "bbox", field: "store_location", point: "45.15,-93.85", distance: 5, cache: false }
});The standalone solrGeofilt() and solrBbox() helpers build raw filter expressions; solrGeoDistance() builds the geodist() expression for sort or scoring.
geofiltapplies a circular great-circle distance filter.bboxuses the bounding rectangle of the same radius and may include points outside the circle.geodist()computes distance for sorting/scoring. Field-type support and units depend on the Solr schema.- The client does not convert units. Common lat/lon fields use kilometers by default; RPT and BBoxField may configure
distanceUnits.
See the Solr 8.11, Solr 9.11, and Solr 10.1 Spatial Search references. Runtime behavior is integration-tested on pinned Solr 8.11.4, 9.11.0, and 10.1.0: geofilt and bbox both include the center and nearby point while excluding a distant point, and geodist sorting orders documents by increasing distance.
Use the typed highlighting property to configure the Solr Highlighting component. The response snippets are returned separately from response.docs, keyed first by document unique key and then by field name.
const result = await solr.query<SolrQueryResponse<Product>>({
query: "title_txt:distributed",
fields: ["id", "title_txt"],
highlighting: {
fields: ["title_txt"],
method: "unified",
snippets: 3,
fragmentSize: 120,
requireFieldMatch: true,
alternateField: "summary_txt",
maxAlternateFieldLength: 200,
usePhraseHighlighter: true,
highlightMultiTerm: true,
encoder: "html",
tagPre: "<mark>",
tagPost: "</mark>",
perField: {
title_txt: { snippets: 2, fragmentSize: 80 }
}
}
});
const snippets = result.highlighting?.[result.response?.docs?.[0]?.id ?? ""]?.title_txt;fieldsmaps tohl.fl;snippetsandfragmentSizemap tohl.snippetsandhl.fragsize.methodselectsunified,original, orfastVector. Highlighter options differ by implementation; for example,mergeContiguousandformatterapply to the Original Highlighter.alternateFieldandmaxAlternateFieldLengthconfigure fallback summaries.usePhraseHighlighterandhighlightMultiTermcontrol phrase and multi-term query highlighting.encoderacceptshtmlorsimple.tagPreandtagPostconfigure common highlighter tags;simplePreandsimplePostconfigure the Original Highlighter's simple formatter.perFieldapplies supported overrides using Solr'sf.<field>.hl.*parameter syntax.- The
highlightingresponse section is typed as document ID → field name → snippet array. Solr returns it only when highlighting is enabled and a requested field has a matching snippet.
The Solr Highlighting reference describes highlighter-specific behavior and schema/performance requirements. Highlighting fields must be stored, and the schema's unique key is used to associate snippets with documents.
Use grouping for Solr Result Grouping, which returns a grouped response section containing each group and its document list. Field, function, and query grouping can be combined; limit and offset control each group's document list, while groupSort orders documents within a group.
const grouped = await solr.query<SolrQueryResponse<Product>>({
query: "*:*",
grouping: {
fields: "category_s",
functions: ["sum(price_d)"],
queries: ["price_d:[0 TO 20]", "price_d:[20 TO *]"],
limit: 3,
offset: 0,
sort: "score desc",
groupSort: "price_d desc",
numGroups: true,
format: "grouped"
}
});
const groups = grouped.grouped?.category_s?.groups;
const topDocument = groups?.[0]?.doclist?.docs?.[0];grouping.fields requires single-valued indexed string fields. Function grouping is not supported for distributed searches. format: "simple" and main: true return a flattened main result list instead of the usual grouped response structure.
Use collapse to select one representative document per field value. The library serializes this as a Collapsing Query Parser filter query and combines it with any existing filter values.
const collapsed = await solr.query<SolrQueryResponse<Product>>({
query: "*:*",
collapse: {
field: "product_id_s",
max: "price_d",
nullPolicy: "collapse",
hint: "top_fc"
},
expand: { field: "product_id_s", rows: 5, sort: "price_d desc" }
});
const representatives = collapsed.response?.docs;
const expandedGroups = collapsed.expanded;collapse supports one representative selector (min, max, or sort), plus nullPolicy, hint, size, and cost. expand can return additional documents for each collapsed group head. Collapse is performed per core/shard; distributed deployments should co-locate documents by collapse key or account for shard-local behavior.
Result Grouping and Collapse/Expand are separate Solr features. They cannot be enabled together in the same request. Prefer Collapse/Expand for most use cases when its per-shard semantics fit the deployment; use Result Grouping when its grouped response structure or distributed grouping behavior is required.
See the official Result Grouping guide and Collapse and Expand guide for version-specific limitations.
Use the typed moreLikeThis option to enable Solr's MoreLikeThis search component on the standard /select handler. It returns similar-document result lists keyed by the unique key of each document in the main query response.
const result = await solr.query<SolrQueryResponse<Product>>({
query: "category:books",
fields: ["id", "title_txt"],
moreLikeThis: {
fields: ["title_txt", "description_txt"],
count: 5,
minTermFrequency: 1,
minDocumentFrequency: 2,
maxQueryTerms: 20,
boost: true,
interestingTerms: "details"
}
});
const similarBooks = result.moreLikeThis?.[result.response?.docs?.[0]?.id ?? ""]?.docs;fieldsmaps tomlt.fland selects stored fields used for similarity; term vectors can improve performance.queryFieldsmaps tomlt.qf, uses DisMax field/boost syntax, and must name fields also included infields.countmaps tomlt.count. Term and document thresholds map tomlt.mintf,mlt.mindf,mlt.maxdf, andmlt.maxdfpct. Word-length, query-term, and parsed-token limits map tomlt.minwl,mlt.maxwl,mlt.maxqt, andmlt.maxntp.boostmaps tomlt.boost.interestingTermsacceptsnone,list, ordetails; the detailed terms mode is intended for diagnostics and may be expensive. For the Search Component, interesting terms are unavailable for distributed queries.- The
moreLikeThisresponse is keyed by each source document unique key and contains a normal result object withnumFound,start, anddocs;SolrMoreLikeThisResponse<TDocument>models this response. SupplyingmoreLikeThisenables the component by default. Setenabled: falseto explicitly disable it. - This API covers the Search Component on
/select. It does not configure the separate MoreLikeThis request handler (/mlt) or the MoreLikeThis query parser. The request handler must be configured in Solr before use. - The source document needs a stored unique key and usable stored text fields; stored term vectors are recommended for performance. Similarity behavior depends on field analysis and the configured schema.
The Search Component and its parameters are documented in the Solr 8.11 guide, Solr 9.11 guide, and Solr 10.1 guide. The integration suite exercises the component against the pinned Solr 8.11.4, 9.11.0, and 10.1.0 images.
Use the typed stats property to request statistics for one or more fields. Each field can select individual statistics with local parameters, request percentiles or approximate cardinality, and attach a key/tag for integration with other components. facets breaks each field's statistics down by the requested facet fields.
const result = await solr.query<SolrQueryResponse<Product>>({
query: "*:*",
limit: 0,
stats: {
fields: [
"price_d",
{
field: "price_d",
key: "price_summary",
tag: "price_pivot",
min: true,
max: true,
mean: true,
percentiles: [50, 90],
cardinality: 0.5
}
],
facets: ["category_s", "brand_s"],
calcDistinct: true
}
});
const priceStats = result.stats?.stats_fields?.price_summary;
const median = priceStats?.percentiles?.["50.0"];
const booksMean = priceStats?.facets?.category_s?.books?.mean;fieldsaccepts field names or per-field options. Per-field statistic flags includemin,max,sum,count,missing,sumOfSquares,mean,stddev,distinctValues, andcountDistinct.percentilesaccepts numeric cut-off values from 0 to 100 and uses Solr's approximate T-Digest calculation.tdigestCompressioncontrols its accuracy/memory trade-off.cardinalityacceptstrueor an accuracy value from 0 to 1 and requests an approximate distinct count using HyperLogLog.tag,excludeTags, andkeymap to Stats Component local parameters, allowing statistics to participate in pivot faceting or exclude tagged filters.facetsmaps tostats.facet;calcDistinctmaps tostats.calcdistinctfor all requested fields.- Results are available under
stats.stats_fields. Each field result includes typed numeric statistics, optional percentile values, approximate cardinality, distinct values, and nested per-facet statistics.
Statistics supported depend on field type; for example, sum/mean/stddev are meaningful for numeric/date fields, while percentile calculations require numeric fields. Distinct value collection and exact distinct counts can be expensive for high-cardinality fields.
See the official Stats Component reference for supported statistics and local-parameter behavior. The integration suite verifies the common numeric statistics, distinct counts, and facet breakdowns on Solr 8.11.4, 9.11.0, and 10.1.0.
query<T>() lets the caller choose the complete response type. For the standard /select handler, SolrQueryResponse<TDocument, TJsonFacet> provides named sections for document metadata, classic facets, JSON facets, highlighting, grouping, statistics, and debug information. The first generic parameter controls the document shape; the second describes the JSON Facet API's facets object.
import type { SolrQueryResponse } from "node-solr-lambda";
interface Product {
id: string;
title: string;
price: number;
}
interface ProductFacets {
count: number;
categories: { buckets: { val: string; count: number }[] };
}
const page = await solr.query<SolrQueryResponse<Product, ProductFacets>>({
query: "inStock:true",
fields: ["id", "title", "price"],
params: { hl: true, "hl.fl": "title", facet: true, "facet.field": "category" }
});
const products: Product[] = page.response?.docs ?? [];
const total = page.response?.numFound;
const firstSnippet = page.highlighting?.[products[0]?.id ?? ""]?.title?.[0];
const classicFacetCount = page.facet_counts?.facet_queries?.["inStock:true"];
const categoryBuckets = page.facets?.categories.buckets;The existing SolrResponse<TDocument> remains available and keeps its original one-parameter usage; it now also exposes the common optional response sections. Use SolrQueryResponse when you want an explicit query-response name or a typed JSON Facet shape. Solr omits sections that were not requested, and exact facet/group/debug payloads depend on the query components and Solr version. These interfaces describe expected JSON shapes; they do not enable a component or certify server-side support. The second generic is intentionally caller-defined because JSON Facet responses are shaped by the requested facet tree.
Cursor pagination is useful for walking large sorted result sets without increasing the start offset. The first request uses cursorMark: "*"; pass the response's nextCursorMark into the next request. Stop when the returned mark equals the mark used for that request:
import type { SolrResponse } from "node-solr-lambda";
interface Product { id: string; updatedAt: string; }
let cursorMark = "*";
let done = false;
while (!done) {
const page = await solr.query<SolrResponse<Product>>({
query: "*:*",
cursorMark,
sort: "updatedAt asc,id asc",
limit: 500
});
for (const product of page.response?.docs ?? []) {
await processProduct(product);
}
const nextCursorMark = page.nextCursorMark;
if (nextCursorMark === undefined) {
throw new Error("Solr response did not include nextCursorMark");
}
done = nextCursorMark === cursorMark;
cursorMark = nextCursorMark;
}TypeScript requires sort whenever cursorMark is supplied and rejects non-zero offset values; Solr permits start=0 but no other start offset with a cursor. Runtime validation enforces the same rules for JavaScript callers. The library cannot infer the collection's schema, so callers must include the configured uniqueKey field in the sort expression (in either direction), usually as the final tie-breaker. Use a stable sort across every page. Avoid sort expressions whose value changes between requests, such as date math based on a moving NOW; in distributed SolrCloud, sorting by score can also be unstable across replicas. Index updates that change a document's sort value during a cursor scan can still cause duplicates or omissions. Cursor pagination and its constraints are documented in the Solr Reference Guide.
The helpers build Solr syntax strings; they do not parse or restrict the full Solr expression language. Raw strings remain supported for custom functions, transformers, local syntax, and server extensions.
import { solrFieldAlias, solrFields, solrFunction, solrLocalParams, solrSort } from "node-solr-lambda";
const sort = solrSort(
{ expression: "price", direction: "asc" },
{ expression: "score", direction: "desc" },
"div(popularity,price) desc"
);
const fields = solrFields(
"id",
"title",
{ alias: "sales_price", expression: "price" },
solrFunction("sum", "price", 10),
"[explain]"
);
const priceField = solrFieldAlias("sales_price", "price");
const query = solrLocalParams({ type: "lucene", df: "title", "q.op": "AND" }, "distributed search");
await solr.query({ query, sort, fields });solrSort()accepts structured{ expression, direction }clauses or raw Solr sort strings.solrFields()accepts raw field-list items or{ alias, expression }entries. Raw items retain support for wildcards, functions, and document transformers.solrFieldAlias(alias, expression)returns Solr'sdisplayName:expressionsyntax.solrFunction(name, ...args)builds function-call expressions and can be nested.solrLocalParams(params, value?)creates a{!...}prefix and can append the associated query value.solrFilter(query, options)builds a structured filter with tags, cache/cost hints, and additional local parameters.solrExcludeTags(...tags)joins tags for Solr local parameters that accept comma-separated exclusions.
These helpers only compose request strings. Solr validates field names, functions, local parameters, sortability, and transformer availability at query time. Keep raw strings when using Solr syntax not represented by the structured helpers.
const result = await solr.query({
query: "typescript solr client",
defType: "edismax",
parserOptions: {
type: "edismax",
qf: "title^10 description",
pf: "title^20",
pf2: "title description",
mm: "2<75%",
tie: 0.1,
"mm.autoRelax": true,
uf: "* -internal"
}
});await solr.query({
query: "distributed search",
defType: "dismax",
parserOptions: { type: "dismax", qf: "title^2 body", mm: "2", tie: 0.1 }
});
await solr.query({
query: "title:distributed",
defType: "lucene",
parserOptions: { type: "lucene", df: "text", sow: true }
});The type discriminator selects the TypeScript option set and is not sent to Solr. The option keys themselves are serialized using Solr's parameter names (including dotted names such as mm.autoRelax). Set defType to the same parser as parserOptions.type; TypeScript intentionally keeps these separate so custom parser names remain supported. For Solr-specific or plugin-specific parameters not yet modelled, use the explicit params property. Parser parameters are documented by the Solr Reference Guide; verify parser availability and behavior against the Solr version you deploy.
The client maps query, filter, fields, limit, and offset to q, fq, fl, rows, and start. Unknown top-level properties are rejected by TypeScript.
Use queryJson() to send a JSON Request API body. The method accepts the typed SolrJsonQueryRequest model for common query properties and recursive JSON Facet API definitions. A compatibility overload remains for custom request bodies that do not use a facet property; put additional top-level parameters in the explicit params property when using the typed request.
The JSON Facet API models terms, query, and range facets as a recursive tree. Each facet can contain nested facets or metrics, and can apply a domain transformation before evaluating its buckets.
import type { SolrJsonQueryRequest, SolrJsonQueryResponse } from "node-solr-lambda";
interface Product { id: string; category_s: string; price_d: number; }
interface ProductFacets {
count: number;
categories: { buckets: { val: string; count: number; averagePrice: number }[] };
expensive: { count: number; maxPrice: number };
}
const request: SolrJsonQueryRequest = {
query: "*:*",
filter: "{!tag=stock}inStock_b:true",
limit: 0,
facet: {
categories: {
type: "terms",
field: "category_s",
limit: 20,
sort: "count desc",
domain: { excludeTags: "stock" },
facet: {
averagePrice: "avg(price_d)",
nestedRanges: {
type: "range",
field: "price_d",
start: 0,
end: 100,
gap: 10,
facet: { count: "count()" }
}
}
},
expensive: {
type: "query",
q: "price_d:[50 TO *]",
facet: { maxPrice: { type: "func", func: "max(price_d)" } }
}
}
};
const result = await solr.queryJson<SolrJsonQueryResponse<Product, ProductFacets>>(request);
const categoryBuckets = result.facets?.categories.buckets;- Terms facets support field, limit, offset, sort, mincount, missing, prefix, method, and nested metrics/facets.
- Query facets count arbitrary query expressions and can include nested metrics/facets.
- Range facets support numeric values and date math for start/end/gap, plus hardend, include, other, and mincount.
- Metrics accept function expressions such as
avg(price_d)or the explicit{ type: "func", func: "..." }form. - Domain changes support filter, query, excludeTags, blockParent, blockChildren, and join transformations.
- The response's
facetssection is caller-typed through the secondSolrJsonQueryResponsegeneric. The library does not infer a response shape from the requested facet tree at runtime.
The JSON Facet API is documented in the Solr Reference Guide. Check the version-specific guide when using advanced facet options or domain transformations.