Skip to content
Back to skills

Persistence And Corpus Io

ASecurity

"Persist and reopen bm25s indexes and JSONL corpora safely,

  • 247 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
businesspythonrustapibackendsecurity

Works with

  • api

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned September 8, 2026

npx -y skills add VectorSpaceLab/AREX-Skill --skill persistence-and-corpus-io --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Persistence And Corpus Io?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Persistence And Corpus Io
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vectorspacelab-persistence-and-corpus-io/badge)](https://www.skillsdirectory.com/skills/vectorspacelab-persistence-and-corpus-io)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: persistence-and-corpus-io
description: "Persist and reopen bm25s indexes and JSONL corpora safely,
  including custom filenames, mmap loading, JsonlCorpus access, vocabulary
  loading, and large-index memory planning."
disable-model-invocation: true
metadata:
  disco-role: operating
license: MIT
---

# Persistence and corpus I/O

Use this route when an index already exists or must be written for later
retrieval. It owns the low-level `BM25.save`, `BM25.load`, and
`BM25.load_scores` contracts, the optional saved corpus, and on-disk memory
behavior. It does not replace tokenization, scoring, high-level file ingestion,
Hub transfer, or evaluation workflows.

## Choose the loading mode

- **Normal local reload:** `BM25.load(index_dir, load_corpus=False)` loads the
  numeric arrays and BM25 parameters into memory. Add `load_corpus=True` only
  when result documents should be returned from the saved corpus.
- **Large index:** use `mmap=True` for the three CSC score arrays. If documents
  are also needed, `load_corpus=True, mmap=True` uses `JsonlCorpus` rather than
  reading the whole JSONL file into a list.
- **IDs-only retrieval:** leave `load_corpus=False`; retrieval returns document
  IDs. Keep the separately managed corpus in exactly the same index order if it
  will be applied later.
- **Scores-only inspection:** create a BM25 object and call `load_scores` with
  the saved array names and the known `num_docs`. This does not load parameters,
  vocabulary, or corpus metadata; use `load` for a usable retriever.

Read [file-format-and-api.md](references/file-format-and-api.md) for exact
signatures, filenames, serialization rules, and custom-name examples. Read
[large-corpus.md](references/large-corpus.md) before loading a large index or
batching retrieval. Use [troubleshooting.md](references/troubleshooting.md) for
failure diagnosis. Run the bounded local check in
[scripts/save_reload_smoke.py](scripts/save_reload_smoke.py) after changing a
persistence workflow.

## Safe save/reload recipe

1. Tokenize and index the documents with one stable vocabulary. The corpus used
   for result display is not the tokenized index: it is a positional document
   store, so keep its order and length aligned with the indexed documents.
2. Save the index directory. `save` creates parent directories and writes the
   three numeric CSC arrays, BM25 parameters, and BM25 vocabulary. Pass
   `corpus=...` (or set `BM25(corpus=...)`) to write a JSONL corpus as well.
3. If a `Tokenizer` will be reconstructed independently, save its tokenizer
   vocabulary under its default `vocab.tokenizer.json` or another distinct
   name. Do not overwrite the BM25 model's default `vocab.index.json`.
4. Reload using the same custom filenames used during save. For textual query
   tokenization, also restore the matching tokenizer vocabulary; for integer
   token IDs, ensure the IDs came from the saved BM25 vocabulary.
5. Verify one small query and, when `load_corpus=True`, verify that the returned
   document at each result ID is the expected positional entry.

```python
import bm25s

index = bm25s.BM25(method="bm25+")
index.index(bm25s.tokenize(["red fox", "blue whale"], stopwords=[]))
index.save(
    "local-index",
    corpus=[{"id": "r", "text": "red fox"}, {"id": "b", "text": "blue whale"}],
    data_name="scores.npy", indices_name="rows.npy", indptr_name="columns.npy",
    vocab_name="model-vocab.json", params_name="model-params.json",
    nnoc_name="nonoccurrence.npy", corpus_name="documents.jsonl",
)
reloaded = bm25s.BM25.load(
    "local-index", load_corpus=True, mmap=True,
    data_name="scores.npy", indices_name="rows.npy", indptr_name="columns.npy",
    vocab_name="model-vocab.json", params_name="model-params.json",
    nnoc_name="nonoccurrence.npy", corpus_name="documents.jsonl",
)
```

The `mmap` flag is a loading choice, not a save format. It maps `data`,
`indices`, and `indptr` with NumPy read mode; it does not make indexing or
serialization incremental. The BM25L/BM25+ non-occurrence array is loaded
separately and is required for those methods.

## Corpus and file invariants

- The default files are `data.csc.index.npy`, `indices.csc.index.npy`,
  `indptr.csc.index.npy`, `vocab.index.json`, `params.index.json`,
  `nonoccurrence_array.index.npy`, and `corpus.jsonl`.
- `params.index.json` is required to reconstruct the model. The three CSC
  arrays and the vocabulary are required for a normal usable load. Missing
  files should be repaired or the correct custom names supplied; do not create
  empty placeholders.
- `load_vocab=False` deliberately skips the BM25 vocabulary. In this version
  the resulting `vocab_dict` and `unique_token_ids_set` are empty, so the
  object is suitable for metadata/array inspection but not normal retrieval.
  Use `load_vocab=True` for retrieval, or explicitly restore a verified matching
  vocabulary before issuing queries.
- `load_corpus=True` is best effort: if the named corpus file is absent, load
  still returns the index with no loaded corpus. It does not validate that the
  corpus length or order equals the indexed document count.
- Strings are written as `{"id": <position>, "text": <string>}`. Top-level
  dictionaries, lists, and tuples are accepted and are written as JSON. Nested
  values must be JSON-serializable (null, booleans, numbers, strings, arrays,
  and objects); sets, bytes, arbitrary Python objects, and most NumPy objects
  are not portable JSON values.
- A document that cannot be serialized is warned about and skipped, which can
  make the saved corpus shorter than the index. Treat that warning as a data
  integrity failure, not as a harmless partial save. A non-iterable corpus is
  not a valid `save` input.
- A JSONL companion index is written beside the corpus. For `documents.jsonl`
  the companion is `documents.mmindex.json`; `JsonlCorpus` uses an existing
  companion without checking that it is fresh. Regenerate it after editing the
  JSONL file.

## Security and compatibility

Keep `allow_pickle=False` unless a trusted legacy index explicitly requires
object-array loading. Setting it to `True` permits NumPy pickle handling and
must never be used to load an untrusted index directory. Numeric arrays written
by the normal save path should work with the default `False` setting. The
stdlib JSON fallback and the optional `orjson` implementation both produce the
same documented JSON-shaped corpus contract; `orjson` is not a reason to enable
pickle.

Install the base package for NumPy-backed local persistence. The `core` extra
adds optional helpers such as `orjson`, progress reporting, stemming, and
Numba; it is not required for the save/load file contract. Keep optional
backends explicit and CPU-safe. Do not download a benchmark corpus merely to
validate a local index.

## Operational handoff

Report the index directory, exact filenames, whether `mmap` and
`load_corpus` were used, the model method, corpus count versus
`scores["num_docs"]`, vocabulary strategy, and any skipped-document warnings.
Close a loaded `JsonlCorpus` when a long-lived process no longer needs it. For a
missing BM25L/BM25+ non-occurrence file or a corpus/index order mismatch, stop
retrieval and repair the artifacts rather than silently returning plausible but
wrong scores or documents.

Files in this skill

  • SKILL.md7.1 KB
  • references/file-format-and-api.md7 KB
  • references/large-corpus.md5.3 KB
  • references/troubleshooting.md6.2 KB
  • scripts/save_reload_smoke.py2.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.