Skip to content

Add tie breaker field mapping - #6859

Open
Mallets wants to merge 18 commits into
mainfrom
mallets/tiebreaker
Open

Mallets wants to merge 18 commits into
mainfrom
mallets/tiebreaker

Conversation

@Mallets

@Mallets Mallets commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds a new tie_breaker field mapping type, backed by Tantivy's generated TieBreaker fast field introduced in quickwit-oss/tantivy#3143.

A tie-breaker field is a u64 fast field whose values are generated at indexing time rather than read from documents. Within a split, values are consecutive starting from a random offset, and they are preserved when splits are merged, so a document keeps its value for its whole lifetime. The intended use is as a secondary sort key that reduces ties among documents with equal primary sort values.

Values are almost always distinct across splits but not guaranteed unique (they are currently limited to the u32 range). Paginating with search_after on a primary sort field plus a tie_breaker field can therefore skip documents that share both values; _shard_doc remains the way to get exact pagination.

Example doc mapping:

field_mappings:
  - name: tie_breaker
    type: tie_breaker

Behavior

  • Mapping: the type takes no parameters; any parameter is rejected. It serializes as {"name": ..., "type": "tie_breaker"}. array<tie_breaker> is not supported.
  • Doc mapping updates: adding a tie_breaker field (including nested in an object) through a doc mapping update is rejected, because existing splits would have no values for it. The index must be created with the field.
  • Indexing: values supplied in documents for this field are ignored.
  • Doc retrieval: the field is not stored, so it does not appear in _source/hits; it is readable as a fast field.
  • Queries:
    • Term / full-text queries on the field return a schema error (tie_breaker fields are not term-searchable).
    • Range queries are supported, treated as a u64 fast-field range.
  • Elasticsearch _mapping API: reported as long, like u64.
  • Docs: documented in docs/configuration/index-config.md.

Other changes

How was this PR tested?

  • New unit tests:
    • tie_breaker mapping round-trips through serialization/deserialization.
    • tie_breaker mappings with parameters are rejected.
    • tie_breaker type id parses and serializes correctly.
    • Values supplied in a document for a tie_breaker field are ignored.
    • Doc mapping updates adding a tie_breaker field are rejected.
  • cargo check on the full workspace; tests of quickwit-doc-mapper, quickwit-config and quickwit-query.
  • make fmt.

Not yet covered by tests: sorting on a tie_breaker field, the term-query error, and the Elasticsearch _mapping type.

@Mallets
Mallets marked this pull request as ready for review October 2, 2026 08:23
@Mallets
Mallets requested a review from a team as a code owner October 2, 2026 08:23
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-02T16:53:06.817740Z 28e1f52 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0c128488b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread quickwit/Cargo.toml Outdated
Comment thread quickwit/quickwit-doc-mapper/src/doc_mapper/field_mapping_entry.rs Outdated
Comment thread quickwit/quickwit-doc-mapper/src/doc_mapper/mapping_tree.rs
Comment thread docs/configuration/index-config.md Outdated
@Mallets Mallets self-assigned this Oct 2, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2a545cc4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread quickwit/quickwit-config/src/index_config/mod.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81fab0eff2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread quickwit/quickwit-doc-mapper/src/doc_mapper/mapping_tree.rs
Comment thread docs/configuration/index-config.md Outdated

The field has no parameters. Values provided in documents are ignored, and generated values are preserved across merges. Values are almost always distinct but not guaranteed to be unique, so they should not be used as document identifiers. In particular, paginating with `search_after` on a primary sort field and a `tie_breaker` field can skip documents that share both values. Sort on `_shard_doc` instead when every document must be returned exactly once.

The field is not stored and only supports range queries. `array<tie_breaker>` is not supported, and a `tie_breaker` field cannot be added to an existing index by updating its doc mapping: the index must be created with it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it's fast but not stored and not indexed? (to use the terminology of normal fields)

i don't recall wether we allow term:value to match on fast non-indexed values by transforming into a term:[value TO value] query, or if we require the caller to do that themself

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It's fast, not stored and not indexed. The docs say it behaves like a u64 field with fast: true, stored: false and indexed: false, and that term and range queries run on the fast field.

Quickwit does the conversion for fast-only fields: it builds a regular term and Tantivy falls back to an exact range on the fast field when it's not indexed.

d8f657a builds the same u64 term as a fast-only u64 field (see here). A tie_breaker:123 query now works.

Comment thread docs/configuration/index-config.md Outdated
type: tie_breaker
```

The field has no parameters. Values provided in documents are ignored, and generated values are preserved across merges. Values are almost always distinct but not guaranteed to be unique, so they should not be used as document identifiers. In particular, paginating with `search_after` on a primary sort field and a `tie_breaker` field can skip documents that share both values. Sort on `_shard_doc` instead when every document must be returned exactly once.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think in strict doc mapping this should reject the document, in lenient and dynamic ignored is correct

(also, because _shard_doc is mentioned, worth saying that contrary to tiebreakers, it isn't stable in quickwit)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0b987ba rejects documents that provide a tie_breaker value in strict_mode.

I removed the mention of _shard_doc from the docs in 53afd15.

Comment on lines +655 to +656
// Tie-breaker values are only generated for newly indexed documents, so existing splits would
// have no values for a tie-breaker field added by an update.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unless having docs with a value and other without causes specific problems, i don't think this should be enforced: it's fine to add fields in general, they will be missing from previous docs (and for non-generated fields, they can even be missing from new documents)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, removed in 10c437e. Docs indexed before the update just won't have a value, same as any newly added field. I updated the docs to mention it.

LeafType::IpAddr(_) => value_to_ip(value),
LeafType::F64(numeric_options) => value_to_float(value, numeric_options),
LeafType::U64(numeric_options) => value_to_u64(value, numeric_options),
LeafType::TieBreaker => match value {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it would make sense to be more lenient and use value_to_u64 here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 27a9807. The numeric options are built explicitly (fast only, not stored, not indexed, no coercion), with a comment noting that only output_format (Number) is actually read by value_to_u64.

Comment thread quickwit/quickwit-search/src/tests.rs Outdated
};

let ascending_hits = search_hits(SortOrder::Asc).await;
assert!(ascending_hits.windows(2).all(|hits| hits[0].2 <= hits[1].2));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mallets Mallets Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 27a9807

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27a9807dc0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/configuration/index-config.md Outdated

The field has no parameters. Values provided in documents are ignored, and generated values are preserved across merges. Values are almost always distinct but not guaranteed to be unique, so they should not be used as document identifiers. In particular, paginating with `search_after` on a primary sort field and a `tie_breaker` field can skip documents that share both values. Sort on `_shard_doc` instead when every document must be returned exactly once.

The field is not stored and only supports range queries. `array<tie_breaker>` is not supported. When a `tie_breaker` field is added to an existing index by updating its doc mapping, documents indexed before the update have no value for it.

This comment was marked as low quality.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants