Repository navigation
feat(global-index): align FullTextSearch with Java JSON DSL query model - #424
Merged
Merged
Conversation
SteNicholas
force-pushed
the
PAIMON-401
branch
2 times, most recently
from
October 9, 2026 15:59
7fdeb97 to
ec15e0d
Compare
zjw1111
reviewed
Oct 12, 2026
zjw1111
reviewed
Oct 12, 2026
SteNicholas
force-pushed
the
PAIMON-401
branch
from
October 12, 2026 15:11
ec15e0d to
90d936d
Compare
SteNicholas
force-pushed
the
PAIMON-401
branch
from
October 13, 2026 02:58
90d936d to
02a4ee7
Compare
Align FullTextSearch with org.apache.paimon.predicate.FullTextSearch: - Make FullTextSearch take a field name, a JSON DSL query, a positive limit and optional include_row_ids, which replaces pre_filter. Remove SearchType, with_score, min_score and ReplacePreFilter. - Add FullTextSearch::OffsetRange(from, to), which clips include_row_ids to a shard range and shifts them to local row ids, and use it in OffsetGlobalIndexReader. - Make GlobalIndexReader::VisitFullTextSearch return ScoredGlobalIndexResult and drop the unscored and min_score paths of the full-text reader. - Translate match, multi_match, match_phrase and boolean queries into Lucene queries in the lucene-fts reader, validating them as the paimon-full-text engine does. Reject boost queries and fuzzy matching, and remove prefix, wildcard and unlimited Lucene searches. Parse queries without recursion and reject queries that nest more than 127 JSON arrays and objects, as serde_json does in the engine. - Use include_row_ids in file index full-text reads from apache#372, and replace their with_score check by rejecting reads that project _INDEX_SCORE.
SteNicholas
force-pushed
the
PAIMON-401
branch
from
October 13, 2026 03:07
02a4ee7 to
2f31b8a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Linked issue: close #401
Java
org.apache.paimon.predicate.FullTextSearchis(fieldName, query, limit)plus a nullableincludeRowIds:queryis a JSON DSL string passed unchanged to the native engine,limitmustbe positive and results are always scored. The C++
FullTextSearchused a different model(
SearchType, an optionallimit,with_score,min_scoreandpre_filter), so a query writtenfor Java could not run in C++, and the
with_score×limitmatrix could return unscored orunlimited results that Java never produces.
This PR aligns the C++ API with Java and takes option 2 of #401 for
lucene-fts: remove the oldmodel and translate the DSL.
FullTextSearch: takes a field name, a JSON DSL query, a positivelimitand optionalinclude_row_ids, which replacespre_filterwith JavaincludeRowIdssemantics. As withpre_filter, these are global row ids forGlobalIndexReaderand file-local row ids forFileIndexReader.SearchType,with_score,min_scoreandReplacePreFilterare removed.FullTextSearch::OffsetRange(from, to): as JavaFullTextSearch#offsetRange, clipsinclude_row_idsto a shard range and shifts them to shard-local row ids.OffsetGlobalIndexReaderuses it.GlobalIndexReader::VisitFullTextSearchreturnsScoredGlobalIndexResult.UnionGlobalIndexReadermerges scored results as for vector search, and the readers that reject full-text search (BTree,
bitmap, lazy-filtered BTree, primary-key sorted, Lumina, file index wrapper) are updated.
full-textreader: passes the query to the engine unchanged and drops the unscored andmin_scorepaths.lucene-ftsreader: translatesmatch,multi_match,match_phrase(aliasphrase) andbooleaninto Lucene term, boolean and phrase queries. Field names, aliases, defaults andvalidation follow
core/src/query.rsandcore/src/index.rsof apache/paimon-full-text:operators, boolean occurrences and column names are trimmed of Unicode whitespace as Rust
str::trimdoes, boosts are validated after rounding tof32, duplicate known fields arerejected and unknown fields are ignored. Queries are parsed without recursion and may nest at
most 127 JSON arrays and objects, not counting unknown fields, as serde_json allows in the
engine.
boostqueries and fuzzy matching are rejected withNotImplemented, and prefix,wildcard and unlimited Lucene searches are removed.
RawFileSplitReadintersectsinclude_row_idswith the rows kept by deletion vectors. File index full-text search returns unscored rows, so a
read that projects
_INDEX_SCOREis rejected withNotImplemented, replacing thewith_scorecheck.
limitapplies to each index shard. Searches across multiple shards return the union of the shardcandidates, which can exceed
limit; a final global top-k is tracked in #402.Tests
UT:
src/paimon/common/predicate/full_text_search_test.cpp:TestOffsetRangeWithoutIncludeRowIds,TestOffsetRangeWithIncludeRowIds(including row ids beyond 32 bits).src/paimon/common/global_index/offset_global_index_reader_test.cpp:TestVisitFullTextSearchWithOffset,TestVisitFullTextSearchConvertsIncludeRowIds.src/paimon/common/global_index/union_global_index_reader_test.cpp:TestVisitFullTextSearchUnion.MockGlobalIndexReaderreturns scored results for full-textsearch.
src/paimon/global_index/full_text/full_text_global_index_test.cpp:TestWriteAndSearch(
multi_matchon the nativetextfield),TestIncludeRowIdsAndLimit,TestNestedBooleanAndBoost(nestedbooleanqueries, andmatch.boost2.0 doubling the scoresof
boost1.0 with the same row ids),TestInvalidRead.src/paimon/global_index/lucene/lucene_global_index_test.cpp:TestSimpleandTestSimpleChinese(DSL queries, boosts, Unicode whitespace, blankmatchandmatch_phrasetext, scores and
include_row_ids),TestNestedQuery(nestedbooleanqueries and the nesting-depth limit, including 100000 levels),TestInvalidQuery(invalid JSON, unknown query types, duplicate fields, invalid values, boostqueries and fuzzy matching).
src/paimon/core/operation/internal_read_context_test.cpp:TestFileFullTextSearchScoreIsNotSupported(a read that projects_INDEX_SCORE).src/paimon/core/operation/raw_file_split_read_test.cpp:TestFullTextSearchFiltersDeletionVectorBeforeLimit(include_row_idswith deletion vectors),TestFullTextSearchRenamedUnprojectedFieldInOldFile.empty_file_index_reader_test.cpp,file_index_reader_test.cpp,file_index_evaluator_test.cpp,read_context_test.cppandtable_read_test.cppbuild searches with the new constructor.IT,
test/inte/global_index_test.cpp:TestLuceneWriteCommitScanReadIndexWithScore,TestWriteCommitScanReadLuceneIndexWithPartition,TestFullTextWriteCommitScanReadIndex,TestWriteCommitScanReadFullTextIndexWithPartition.API and Format
Yes, public API changes in
include/paimon/:FullTextSearch(include/paimon/predicate/full_text_search.h): the constructor is nowFullTextSearch(field_name, query, limit, include_row_ids = std::nullopt).SearchType,search_type,pre_filter,with_score,min_scoreandReplacePreFilterare removed,limitis
int32_tinstead ofstd::optional<int32_t>, andOffsetRangeis added.GlobalIndexReader::VisitFullTextSearch(include/paimon/global_index/global_index_reader.h)returns
Result<std::shared_ptr<ScoredGlobalIndexResult>>.lucene-ftsqueries must be written in the JSON DSL. A file index full-text read throughReadContextthat projects_INDEX_SCOREfails withNotImplemented. No storage format change.Documentation
Yes.
docs/source/user_guide/global_index.rstdescribes the JSON DSL queries of thefull-textand
lucene-ftsindexes, howlucene-ftstranslates and validates them, and the per-shardlimit.Generative AI tooling
Generated-by: Claude Code 2.1.296 (Claude Opus 5.5)
🤖 Generated with Claude Code