3692 Commits

Author SHA1 Message Date
Pavlos Rontidis 039a72958e Merge pull request #3041 from quickwit-oss/cose-sync-security-policy-20260813224347
chore: sync security-policy
2026-08-14 09:09:08 -04:00
DavIvek a1aa571905 test(query-parser): cover json phrase prefix and slop against indexed documents
Adds an end-to-end test that indexes real JSON documents and asserts hit
counts, complementing the existing logical-AST assertions. The test fails
without the fix (phrase prefix returns 0 hits instead of 2).
2026-08-14 16:10:31 +08:00
DavIvek 51d3000ce6 fix(query-parser): honor phrase prefix and slop on JSON fields
Phrase prefix (`"..."*`) and slop (`"..."~N`) were silently dropped on JSON
fields. `generate_literals_for_json_object` hard-coded `slop: 0` and
`prefix: false`, so a query like `data.name:"foo bar"*` degraded to an exact
phrase, even though the underlying `PhrasePrefixQuery` already supports
JSON-path terms.

Thread `slop`/`prefix` through the JSON branch, exactly as the `Str` branch
already does, and add the "phrase prefix requires at least two terms" guard for
the single-token case (mirroring `generate_literals_for_str`).

Tests added as JSON analogues of the existing text-field tests:
- test_phrase_prefix_on_json_field
- test_phrase_prefix_too_short_on_json_field
- test_phrase_slop_on_json_field

This aligns JSON fields with the phrase `~`/`*` behavior already documented for
`QueryParser`.
2026-08-14 16:10:31 +08:00
David Yaffe afb3aed299 chore: sync security-policy from cose 2026-08-13 18:43:47 -04:00
Pascal Seitz 7373d54c2d fix clippy 2026-08-12 21:08:06 +08:00
Paul Masurel 1f32c1a8af Removing TODO.txt (#3037)
Co-authored-by: Paul Masurel <paul.masurel@datadoghq.com>
2026-08-11 10:30:15 +02:00
Ming 0401b45781 feat: Extensible segment components via plugin trait (#2993)
## Motivation

Today every segment component — postings, fast fields, field norms, store — is hardcoded into several places. Adding a new per-segment data structure means forking Tantivy and editing each of those sites.

This PR introduces a `SegmentPlugin` trait that lets a custom component participate in the full segment lifecycle — write, serialize, merge, garbage collection, space usage — through the same interface the built-ins use, without touching Tantivy internals. The four built-in components are themselves reimplemented as plugins.

We (ParadeDB) plan on using this trait for 1) additional segment metadata for partitioning 2) custom vector index.

## Plugin Trait

Two traits. The first is the `SegmentPlugin` factory:

```rust
pub trait SegmentPlugin: Send + Sync + 'static {
    /// File extensions this component owns, e.g. ["idx", "pos", "term"] for postings.
    fn extensions(&self) -> &[&str];

    /// Create a writer for the indexing path.
    fn create_writer(&self, ctx: &PluginWriterContext) -> crate::Result<Box<dyn PluginWriter>>;

    /// Merge this component across several source segments into the target segment.
    fn merge(&self, ctx: PluginMergeContext) -> crate::Result<()>;

    /// Report on-disk space usage, keyed by component name. Has a default impl.
    fn space_usage(&self, reader: &SegmentReader)
        -> crate::Result<BTreeMap<String, ComponentSpaceUsage>>;
}
```

A `SegmentPlugin` owns one or more file extensions and knows how to (a) build a writer for the indexing path and (b) merge itself across segments.

The second trait is the segment writer:

```rust
pub trait PluginWriter: Send + Any {
    /// Called once per document, in doc-id order, for every plugin writer.
    fn add_document(&mut self, doc_id: DocId, doc: &TantivyDocument, schema: &Schema)
        -> crate::Result<()> { Ok(()) }

    /// Serialize accumulated data to segment files (honoring an optional doc-id remap).
    fn serialize(&mut self, segment: &Segment, doc_id_map: Option<&DocIdMapping>) -> crate::Result<()>;

    fn close(self: Box<Self>) -> crate::Result<()>;
    fn mem_usage(&self) -> usize;

    fn as_any(&self) -> &dyn Any;       // downcast support, Rust 1.86
    fn as_any_mut(&mut self) -> &mut dyn Any;
}
```

The write path no longer has any by-name wiring: `SegmentWriter` hands every document to every plugin writer's `add_document`, and `finalize()` calls `serialize` then close on each. 

## Key Design Decisions

1. The index — not the segment — owns the plugin set. The set of custom plugins is recorded once, at index creation, in `IndexMeta`. `#[serde(default)]` makes this backward compatible.

2. Plugins are registered, like tokenizers — and re-registration is enforced fail-closed. Plugins are not serialized; they're re-attached on every `Index::open` via `register_plugin`, exactly like custom tokenizers. To prevent consumers from accidentally forgetting to register a plugin, we validate the registered plugin set against the persisted set when the index is first used for a write/merge/GC operation.

3. Registration order is the write/merge order. Built-ins come first (field norms → postings → fast fields → store), then custom plugins.

4. The read side needs no plugin hook. Custom component data is read back through the existing public surface `SegmentReader::open_read`.

5. Backwards compatibility — behavior for existing indexes is unchanged.
2026-08-10 16:06:11 -07:00
PSeitz-dd 52b607d7cd Merge pull request #3018 from PSeitz/low_card_histogram_lanes
Up to 2x faster fused term-histogram aggregation
2026-08-10 09:39:48 +02:00
Pascal Seitz d212a9c532 Address fused term-histogram review feedback
Decouple the low-cardinality sub-aggregation threshold from the fused
count-lane threshold, rely on the full-column invariant in the linear
resolver, and explicitly test the single-bucket resolver.
2026-08-10 15:30:57 +08:00
Pascal Seitz 6797d8c577 const assert, unreachable 2026-08-10 15:03:46 +08:00
trinity-1686a 5fc9fccb61 Merge pull request #3030 from foundational-io/fix/streamer-term-ord-skipped-blocks
fix(sstable): report the real term ordinal when an automaton prunes blocks
2026-08-07 17:14:53 +02:00
dependabot[bot] 1990205ecf Bump actions/checkout from 6.0.3 to 7.0.1 (#3006)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...3d3c42e5aac5ba805825da76410c181273ba90b1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-07 12:03:20 +02:00
mmustafasenoglu cac4ba9404 chore: downgrade all info! logs to debug! (#3013)
* chore: downgrade info! logs to debug! in managed_directory.rs

* chore: downgrade info! logs to debug! in file_watcher.rs

* chore: downgrade info! logs to debug! in index_writer.rs

* chore: downgrade info! logs to debug! in prepared_commit.rs

* chore: downgrade info! logs to debug! in segment_updater.rs
2026-08-07 11:58:56 +02:00
dependabot[bot] a91476fd56 build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 (#3015)
Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.3 to 2.4.4.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/4eaacf0543bb3f2c246792bd56e8cdeffafb205a...2d1146689b8cda280b9bc96326124645441f03bc)

---
updated-dependencies:
- dependency-name: ossf/scorecard-action
  dependency-version: 2.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-07 11:58:11 +02:00
dependabot[bot] 60a982c6e1 build(deps): update base64 requirement from 0.22.0 to 0.23.0 (#3014)
Updates the requirements on [base64](https://github.com/marshallpierce/rust-base64) to permit the latest version.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: base64
  dependency-version: 0.23.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-07 11:57:54 +02:00
Marc Bachmann 52a95b159b fix: do not drive a non-summing score combiner with Block-WAND (#3022)
`BooleanWeight::for_each_pruning` handed a union of term scorers straight
to `block_wand`, whatever the weight's score combiner. Block-WAND prunes on
the sum of the per-term block maxima and scores the survivors the same way,
so it silently replaces the combiner with a sum. A `DisjunctionMaxQuery`
collected through `TopDocs` therefore scored a document matching in two
fields as the sum of both instead of the better of the two, and ignored the
tie breaker.

The union only reaches that path when every clause reads term frequencies,
which is why the bug hid: a query asking for `Basic` postings falls back to
`BufferedUnionScorer`, which does honor the combiner.

`ScoreCombiner` now declares whether Block-WAND may drive it. Only
`SumCombiner` opts in; everything else falls back to the plain union, which
still prunes on the threshold but makes no assumption about how the scores
combine.

The intersection specialization needs no such fallback: `Intersection::score`
sums its children whatever the combiner (the combiner only shapes how should
clauses combine), so `block_wand_intersection`'s summing already matches the
unpruned scorer for every combiner. A second test pins that equivalence by
comparing `for_each_pruning` against a manual scorer walk on a must+must
weight built with `DisjunctionMaxCombiner`.
2026-08-07 11:57:22 +02:00
Xuanwo 3a55bc986d Replace rust-stemmers with frostem for stemming (#3028)
rust-stemmers is unmaintained and lags behind upstream Snowball.
frostem tracks snowball main and regenerates algorithms automatically.
2026-08-06 10:54:56 +02:00
dependabot[bot] 5ca3933200 Update lru requirement from 0.16.3 to 0.18.2 (#3034)
Updates the requirements on [lru](https://github.com/jeromefroe/lru-rs) to permit the latest version.
- [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jeromefroe/lru-rs/compare/0.16.3...0.18.2)

---
updated-dependencies:
- dependency-name: lru
  dependency-version: 0.18.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-06 10:18:16 +02:00
Pascal Seitz 3bd8a6b4d1 Optimize fused term-histogram aggregation
Avoid histogram decoding for single-bucket ranges and use precomputed
boundaries for small ranges. Split hot counters into lanes to reduce
write dependencies on low-cardinality grids.
2026-08-06 08:41:07 +02:00
Pascal Seitz 62ca2f3608 add termhistogram bench for few buckets 2026-08-06 08:41:07 +02:00
trinity-1686a 86641f72df Merge pull request #3029 from quickwit-oss/trinity.pointard/get-ff-tokenizer-comeback
reintroduce get_fast_field_tokenizer_name
2026-08-04 21:04:46 +02:00
ildis 7f6685f5d6 fix(sstable): report the real term ordinal when an automaton prunes blocks
`Streamer::term_ord()` derived the ordinal by counting `delta_reader.advance()`
calls from a seed taken only when the stream had a key lower bound. An automaton
search has no key bounds, so the seed was 0 — but the reader it is handed *is*
block-pruned by that automaton (`get_block_iterator_for_range_and_automaton`).
Every block skipped ahead of the first match went uncounted, so `term_ord()`
returned the term's position among the blocks actually scanned rather than its
ordinal in the dictionary.

The error is silent and grows with how deep the first match sits: on a 262k-term
dictionary, a regex matching only the last key reported ordinal 999 instead of
262142. Callers that resolve those ordinals back to terms therefore act on a
different term entirely — `build_allowed_term_ids_for_str` builds the terms
aggregation's allowed-ordinal bitset this way, so an `include` regex could make
the aggregation count unrelated terms while returning the expected bucket count.
Broad patterns matching from the start of the dictionary hid it, since nothing is
pruned ahead of the first match.

Each slice handed to the reader now carries the ordinal of its first term, and
the streamer resets to it on entering a slice instead of incrementing across the
gap. Blocks merged into one slice stay contiguous, so counting within a slice is
still correct.

`Dictionary::sorted_ords_to_term_cb` already tracked `BlockAddr::first_ordinal`
explicitly, which is why ordinal->term resolution was unaffected.
2026-08-04 21:04:17 +03:00
trinity-1686a 0743fe7d67 reintroduce get_fast_field_tokenizer_name 2026-08-04 18:24:15 +02:00
Pascal Seitz bc225eecdf rename bench name 2026-08-04 13:23:19 +02:00
Pascal Seitz 83ffe96189 Reuse existing term ordinals for multi-terms missing values
Map a configured string missing value to its dictionary ordinal when the
term is already present. This coalesces real and missing documents before
the segment-level cutoff, preserving counts and sub-aggregation results.

Keep synthetic sentinels for absent terms and non-string columns, and add
regression coverage with a segment size of one.
2026-08-04 13:23:19 +02:00
Pascal Seitz 92fd5c5cde Refactor multi-term key collection paths
Extract full and non-full field key construction into dedicated helpers.
Rename the surviving document buffer and avoid copying IDs on the full-field path.
2026-08-04 13:23:19 +02:00
Pascal Seitz c9bf057162 Skip unreachable multi-term missing handling
Omit missing accessors when any physical column is full because the logical
field cannot be absent.
2026-08-04 13:23:19 +02:00
Pascal Seitz e77c256f01 simpler missing algorithm 2026-08-04 13:23:19 +02:00
Pascal Seitz 1bfdf14415 restore TODO 2026-08-04 13:23:19 +02:00
Pascal Seitz 21bb20ff1a better clustering algorithm 2026-08-04 13:23:19 +02:00
Pascal Seitz 16f38f6a64 refactor 2026-08-04 13:23:19 +02:00
Pascal Seitz d3abc9a0b3 simplify 2026-08-04 13:23:19 +02:00
Pascal Seitz 17de33d8f0 merge multi-terms code 2026-08-04 13:23:19 +02:00
Pascal Seitz 09e869dae1 refactor benches 2026-08-04 13:23:19 +02:00
Pascal Seitz 295d5e4aa8 fix merge 2026-08-04 13:23:19 +02:00
Pascal Seitz 90ff347cc9 move impl 2026-08-04 13:23:19 +02:00
Pascal Seitz 766454d32a improve naming 2026-08-04 13:23:19 +02:00
Pascal Seitz 3010dbb8ea Optimize sparse multi-terms collection
Filter documents without values after each single-valued field so later
decoding and key construction only process viable documents. Use exact
block lengths to validate aligned results and keep unsupported missing-value
combinations on the general path.
2026-08-04 13:23:19 +02:00
Pascal Seitz 864a1d4b2d improve missing performance 2026-08-04 13:23:19 +02:00
Pascal Seitz 3e4cb34bfd add missing to benchmark 2026-08-04 13:23:19 +02:00
Pascal Seitz 2d1ddca54d fmt 2026-08-04 13:23:19 +02:00
Pascal Seitz f1efd748fa cleanup 2026-08-04 13:23:19 +02:00
Pascal Seitz 164f62c4cc Simplify optional multi-terms document filtering
Track valid document positions in a reusable bitset instead of compacting
parallel document and index buffers after each optional field. Decode each
field against the original block and consult the mask before writing keys or
populating buckets.

This favors simpler collection logic over shrinking later sparse decodes.
2026-08-04 13:23:19 +02:00
Pascal Seitz 035fbe4d47 Refactor multi-terms collection around typed key codecs
Fan out dynamic/JSON physical column combinations during aggregation tree
construction so each segment collector handles one typed accessor per
field and applies missing values exactly once.

Use a generic key codec and shared term bucket maps for both packed and
unpacked keys. Keep optional and multivalued fields eligible for packed
storage, short-circuit sparse documents, and account for spilled composite
keys.
2026-08-04 13:23:19 +02:00
Pascal Seitz 95e8760050 columnar storage - add batched rank 2026-08-04 13:23:19 +02:00
Pascal Seitz 3cdc15e8b6 add filtered benchmark 2026-08-04 13:23:19 +02:00
Pascal Seitz f72044faf9 more benchmarks to compare 2026-08-04 13:23:19 +02:00
Pascal Seitz 2d91c31468 rename vars 2026-08-04 13:23:19 +02:00
Pascal Seitz 59b6859c16 move term req data to collector 2026-08-04 13:23:19 +02:00
trinity-1686a 5c1937a1ec rename agg benches 2026-08-04 13:23:19 +02:00