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

@crowi/plugin-search-opensearch

v0.1.0-alpha.1

Published

OpenSearch search driver for Crowi 2.0.

Readme

@crowi/plugin-search-opensearch

OpenSearch search driver for Crowi 2.0. Indexes pages on create / update / delete, serves the wiki search box, and rebuilds the whole index from scratch on demand. Targets a <indexName>-current alias so a rebuild can swap the underlying index atomically.

The plugin is a sibling of @crowi/plugin-search-elasticsearch: the wire-level document shape, alias name, mapping JSONs and query DSL are identical, so a cluster migration between the two backends is a re-point + rebuild rather than a mapping rewrite.

Install

crowi-admin plugin add @crowi/plugin-search-opensearch

(or, in dev: pnpm --filter @crowi/api add -D @crowi/plugin-search-opensearch)

Configure

1. Activate the driver in crowi.config.json

{
  "plugins": ["@crowi/plugin-search-opensearch"],
  "search": { "driver": "opensearch" }
}

A server restart is required when search.driver changes — Crowi reads this file once at boot.

2. Fill in connection settings in the admin UI

Open /admin/plugins and edit @crowi/plugin-search-opensearch:

  • urlhttps://[user:pass@]host[:port][/indexName]. The URL embeds the cluster password, so it is encrypted at rest with CROWI_ENCRYPTION_KEY. Only Basic Auth via the URL is supported (AWS SigV4 / IAM auth is intentionally out of scope; a managed OpenSearch deployment using fine-grained access control still works with a Basic Auth user).
  • indexName — base index name (default crowi). The driver reads / writes the <indexName>-current alias.
  • requestTimeout — per-request timeout in ms (default 5000).
  • analyzerdefault / kuromoji / sudachi (see below).

The admin UI is the single source of truth for these settings — there is no env-variable fallback.

3. Build the initial index

crowi-admin search rebuild

This creates a fresh <indexName>-<timestamp>-<rand> index, indexes all pages in 2000-document bulk batches with pre-fetched bookmark counts, and atomically swaps <indexName>-current to the new index.

Hot-reload (no restart needed)

This plugin implements reconfigure, so saving connection settings in the admin UI applies without a server restart. When you save:

  • the url / indexName / requestTimeout / analyzer changes are picked up by the live driver,
  • a fresh OpenSearch client is built and the previous one is closed in the background (its HTTP keep-alive pool drains),
  • the admin UI shows a "saved — applied immediately" toast.

Mechanics: the driver holds a module-scope state ref; each operation (query / index / remove / rebuild) snapshots the state once at the top of the call, so a save that lands mid-request cannot retarget an inflight operation onto a different cluster. The next request sees the new settings.

Caveats

  • Analyzer changes need a manual rebuild. Switching analyzer (default / kuromoji / sudachi) updates the setting immediately, but the existing index keeps its old analyzer — analyzers are fixed at index-creation time. Run crowi-admin search rebuild to create a new index with the new analyzer and swap the alias to it.
  • Empty url → configured url is restart-only. If url was empty at boot, the driver is not registered and there is nothing for reconfigure to mutate; configure a url and restart once. After that, all further changes hot-reload. Clearing a configured url (configured → empty) is handled live — search requests then fail with a clear Search not configured error until a url is set again.
  • A rebuild that is already running when you reconfigure runs to completion against the cluster / index name it started with.

Analyzer flavours

| Analyzer | Cluster requirement | |---|---| | default | No extra OpenSearch plugin. | | kuromoji | analysis-kuromoji (Apache 2.0, from opensearch-project/analysis-kuromoji). Unlike Elasticsearch, this is a separate distribution from OpenSearch core — install it on every cluster node with bin/opensearch-plugin install analysis-kuromoji and restart. | | sudachi | Third-party analysis-sudachi (OpenSearch-compatible fork from WorksApplications) + a dictionary. Operators must bundle these into a custom image. Picking this without the plugin makes rebuild() fail. |

For a wiki with mostly Japanese content, kuromoji is the typical choice. The mapping JSON (src/mappings/kuromoji.json) names the analyzer as kuromoji, which is the identifier both the Elastic and OpenSearch distributions install under, so no per-engine tweak is needed in the mapping itself.

Trying it locally

The default docker compose stack ships an Elasticsearch service for the sibling plugin, not OpenSearch. To exercise this plugin locally, add an override file:

# compose.override.yml
services:
  opensearch:
    image: opensearchproject/opensearch:2.18.0
    environment:
      - discovery.type=single-node
      - plugins.security.disabled=true
      - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Crowi-Dev-Passw0rd!
    ports:
      - "9201:9200"

Then:

  1. docker compose up -d opensearch — brings up OpenSearch on :9201 (:9200 is taken by the dev Elasticsearch service if you left it running).
  2. In /admin/plugins, set @crowi/plugin-search-opensearch's url to http://opensearch:9201/crowi (or http://localhost:9201/crowi from outside the compose network) and save; restart once if the driver was previously unconfigured.
  3. From /admin/search, run a rebuild to populate the index.
  4. Change requestTimeout (or point indexName at a freshly rebuilt index) and save without restarting. The next search query uses the new settings — confirmed by the api log line reconfigured opensearch search driver (...).

Why a separate plugin from @crowi/plugin-search-elasticsearch?

Both backends speak the same query DSL today, but the SDKs differ (@opensearch-project/opensearch vs @elastic/elasticsearch) and the backends are intentionally allowed to diverge — OpenSearch's neural / k-NN extensions, the Elastic-licensed features in newer ES versions, and the analyzer-plugin distribution stories are all separate concerns. Keeping the drivers in distinct npm packages means each can pin its own SDK / mapping fork without forcing the other to follow.

The parse-query / query-builder / mapping JSON files are 1:1 copies of the ES plugin's today, but on purpose — we will revisit a shared core when a third driver arrives.

See also