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

@magierin-schnee/solr-client

v1.0.0

Published

A simple and efficient Solr client for Node.js applications.

Downloads

42

Readme

@magierin-schnee/solr-client

A powerful and flexible Node.js client for Apache Solr, offering an intuitive API for document management, advanced querying, and collection administration.


✨ Features

  • Easy Setup: Connect to Solr with minimal configuration.
  • Full-Featured Document API: Add, update, and delete documents with simple, chainable methods.
  • Powerful Query Builder: Construct sophisticated queries with a fluent API.
  • Streaming Support: Efficiently index large datasets via streams.
  • Collection Management: Administer SolrCloud collections with ease.
  • Robust Error Handling: Modern async/await and promise-based error handling for reliable operations.

🚀 Getting Started

1. Installation

Install the library using your favorite package manager:

npm install @magierin-schnee/solr-client
yarn add @magierin-schnee/solr-client
pnpm add @magierin-schnee/solr-client

2. Creating a Client

To begin, import createClient and instantiate a new client with your Solr instance's configuration.

import { createClient } from '@magierin-schnee/solr-client'

// Connect to your Solr instance
const client = createClient({
  host: '127.0.0.1',  // Solr server hostname (defaults to '127.0.0.1')
  port: 8983,         // Solr server port (defaults to 8983)
  core: 'my_core',    // Solr core or collection name
  path: '/solr',      // Path to the Solr API (defaults to '/solr')
  secure: false,      // Use HTTPS (defaults to false)
  bigint: false,      // Use json-bigint for parsing (defaults to false)
})

3. Authentication

If your Solr instance requires authentication, you can set the credentials on the client.

// Set basic authentication credentials
client.setBasicAuth('your_username', 'your_password')

// To remove authentication
client.clearAuth()

Core Functionality

Adding Documents

Add a single document or an array of documents to your Solr index using addDocuments:

// Add a single document
const document = { id: '1', name: 'Example Document' }
await client.addDocuments(document, { commit: true })

// Add multiple documents
const documents = [
  { id: '2', name: 'Another Document' },
  { id: '3', name: 'Yet Another Document' },
]
await client.addDocuments(documents, { commit: true })

The commit: true option ensures changes are immediately visible. You can also use atomicUpdateDocuments as an alias for atomic updates.

Retrieving Documents

By ID

Fetch documents by their IDs with getDocumentsById:

// Single document
const response = await client.getDocumentsById('1')
console.log(response.response.docs)

// Multiple documents
const multiResponse = await client.getDocumentsById(['1', '2', '3'])
console.log(multiResponse.response.docs)

By Query

Search for documents using searchDocuments with a query:

const query = client.createQuery().setQuery('name:Example')
const response = await client.searchDocuments(query)
console.log(response.response.docs)

All Documents

Retrieve all documents in the index with searchAllDocuments:

const response = await client.searchAllDocuments()
console.log(response.response.docs)

Updating Documents

Update an existing document by re-adding it with the same ID:

const updatedDoc = { id: '1', name: 'Updated Document' }
await client.addDocuments(updatedDoc, { commit: true })

Deleting Documents

By ID

Remove a document by its ID:

await client.deleteById('1', { commit: true })

By Field

Delete documents matching a field-value pair:

await client.deleteByField('name', 'Example Document', { commit: true })

By Query

Delete documents matching a query:

await client.deleteByQuery('name:Example', { commit: true })

All Documents

Clear the entire index:

await client.deleteAllDocuments({ commit: true })

Committing Changes

Manually commit changes to make them visible:

await client.commit()

For a soft commit (faster, no disk flush):

await client.softCommit()

Prepare a commit without immediate visibility:

await client.prepareCommit()

Querying

Build complex queries using the Query class and its fluent API:

const query = client
  .createQuery()
  .setQuery('*:*') // Main query
  .addFilter('age:25') // Exact match filter
  .setOffset(0) // Starting index
  .setLimit(10) // Max results
  .setSort({ score: 'desc' }) // Sort by score descending

const response = await client.searchDocuments(query)
console.log(response.response.docs)

Key Query Methods

| Method | Description | Example | | ------------------------- | ----------------------------------------- | ---------------------------------------------------------------------- | | setQuery(query) | Sets the main query string or object. | .setQuery('name:Example') | | addFilter(filter) | Adds a filter query (fq). | .addFilter('category:books') | | addFilters(filters) | Adds multiple filter queries. | .addFilters(['inStock:true', 'price:[* TO 100]']) | | setOffset(offset) | Sets the starting offset for pagination. | .setOffset(10) | | setLimit(limit) | Limits the number of results. | .setLimit(20) | | setSort(fields) | Defines sort order. | .setSort({ score: 'desc' }) | | setResponseFields(fields) | Specifies fields to return. | .setResponseFields(['id', 'name']) | | setFacets(config) | Configures faceting using JSON Facet API. | .setFacets({ categories: { type: 'terms', field: 'cat', limit: 10 } }) | | setHighlighting(config) | Configures highlighting. | .setHighlighting({ on: true, fl: 'content' }) |

For more advanced options (e.g., grouping, spellchecking), refer to the source code.

Advanced Facets

The client supports Solr's powerful JSON Facet API, allowing you to build complex faceting logic. Use the setFacets method to define your facet structure.

Terms Facet

A terms facet computes counts for each unique term in a field. This is useful for creating category or tag clouds.

Example: Get the document count for each category.

const query = client
  .createQuery()
  .setQuery('*:*')
  .setLimit(0) // We only want the results of the facets
  .setFacets({
    category: {
      type: 'terms',
      field: 'category',
      limit: 10,
      mincount: 1,
    },
  })

const response = await client.searchDocuments(query)
console.log(response.facets)

Expected Response:

{
  "count": 150,
  "category": {
    "buckets": [
      { "val": "electronics", "count": 75 },
      { "val": "books", "count": 50 },
      { "val": "clothes", "count": 25 }
    ]
  }
}

Range Facet

A range facet groups documents into buckets based on a numeric or date field.

Example: Group documents by price ranges.

const query = client
  .createQuery()
  .setQuery('*:*')
  .setLimit(0)
  .setFacets({
    price: {
      type: 'range',
      field: 'price',
      start: 0,
      end: 1000,
      gap: 100,
    },
  })

const response = await client.searchDocuments(query)
console.log(response.facets)

Expected Response:

{
  "count": 150,
  "price": {
    "buckets": [
      { "val": 0, "count": 20 },
      { "val": 100, "count": 35 },
      { "val": 200, "count": 15 }
    ]
  }
}

Query Facet

A query facet returns a count for any arbitrary query. You can have multiple query facets.

Example: Count documents that are in stock.

const query = client
  .createQuery()
  .setQuery('*:*')
  .setLimit(0)
  .setFacets({
    inStock: {
      type: 'query',
      q: 'inStock:true',
    },
  })

const response = await client.searchDocuments(query)
console.log(response.facets)

Expected Response:

{
  "count": 150,
  "inStock": {
    "count": 120
  }
}

Nested facets

You can nest facets to create more complex aggregations. For example, you can calculate statistics for sub-facets.

Example: Create facets by publication date ranges using query facets.

const query = client
  .createQuery()
  .setQuery('*:*')
  .setLimit(0)
  .setFacets({
    publicationDate: {
      type: 'query',
      q: '*:*',
      facet: {
        today: {
          type: 'query',
          q: 'publicationDate:[NOW/DAY TO NOW]',
        },
        last7Days: {
          type: 'query',
          q: 'publicationDate:[NOW-7DAY/DAY TO NOW]',
        },
        last30Days: {
          type: 'query',
          q: 'publicationDate:[NOW-30DAY/DAY TO NOW]',
        },
      },
    },
  })

const response = await client.searchDocuments(query)
console.log(response.facets)

Expected Response:

{
  "count": 150,
  "publicationDate": {
    "count": 150,
    "today": { "count": 5 },
    "last7Days": { "count": 25 },
    "last30Days": { "count": 80 }
  }
}

Streaming Documents

Add documents efficiently using a stream:

const { stream, response } = client.createDocumentStream({ commit: true })

stream.write({ id: '1', name: 'Streamed Document' })
stream.end()

const result = await response
console.log(result)

Managing Collections

Create and manage Solr collections:

const collection = client.createCollection()
const response = await client.manageCollection(collection)
console.log(response)

Additional Utilities

Optimizing the Index

Optimize your Solr index for better performance:

await client.optimizeIndex({ waitSearcher: true })

Rolling Back Changes

Discard uncommitted changes:

await client.rollbackChanges()

Pinging the Server

Check the Solr server status:

const pingResponse = await client.pingServer()
console.log(pingResponse)

Escaping Special Characters

Escape Lucene special characters in queries:

const { escapeLuceneChars } = require('@magierin-schnee/solr-client')
const safeQuery = escapeLuceneChars('query with +special chars')
console.log(safeQuery) // "query with \+special chars"

Error Handling

All methods return promises that resolve with response data or reject with errors. Use try-catch blocks for robust error handling:

import { SolrError } from '@magierin-schnee/solr-client'

try {
  await client.addDocuments(document)
} catch (error) {
  if (error instanceof SolrError) {
    console.error('Http Status Code:', error.httpStatusCode)
    console.error('Message:', error.message)
    console.error('Metadata:', error.metadata)
  }
}

Contributing

We welcome contributions! If you find a bug or have a feature request, please:

  1. Open an issue on the GitHub repository (https://github.com/magierin-schnee/solr-client/issues).
  2. Submit a pull request with your changes.

License

This project is licensed under the MIT License. See the LICENSE file for details.