Tantivy Search Engine Library

repository·main·Indexed 12 days ago

https://github.com/quickwit-oss/tantivy

A fast, full-text search engine library written in Rust, designed to be embedded into applications similarly to Apache Lucene. Version 0.27.0 includes core search capabilities, a query grammar parser, an aggregation system mimicking Elasticsearch, and specialized storage components like the tantivy-columnar and tantivy-sstable crates.

Tokens
47.9K
Snippets
129
Records
222
Agent score
97%

What's inside Tantivy

  1. Overview of the tantivy-sstable crate

    main

    The tantivy-sstable crate provides a Sorted String Table (SSTable) implementation designed primarily for use in quickwit. It serves as an alternative to the default tantivy FST dictionary and as a storage mechanism for column indices in dynamic fast fields.

    Key benefits include:

    • Locality: Unlike the fst crate, which requires downloading the entire dictionary to search a key, tantivy-sstable allows a get operation to be performed with a single fetch once the index is downloaded.

    Note: Current performance for get operations is considered poor due to the specific block index and default block size design optimized for Quickwit.

  2. What is Tantivy and its core purpose?

    main

    Tantivy is a high-performance full-text search engine library written in Rust. Its primary goal is to ingest large sets of textual documents and build an index that allows for rapid selection of documents matching specific predicates (queries) and the collection of information about them.

    Key characteristics include:

    • Full-Text Search: Efficiently returns the K-most relevant documents for a given text query.
    • BM25 Scoring: Uses the BM25 relevance scoring algorithm (the same default used in Lucene and Elasticsearch).
    • Versatility: Beyond simple search, it can be used for document counting, computing aggregations (e.g., average price), ranking based on historical data, or powering OLAP databases.
    • Performance-Oriented: Designed for $O(1)$ memory search and sublinear memory indexing, prioritizing search speed.
    • Batch Updates: While it supports adding and deleting documents, it is optimized for handling these updates in large batches rather than highly dynamic, single-document updates.
  3. What is the Tantivy Query Grammar crate?

    main
    The tantivy-query-grammar crate is a specialized component used by the core tantivy engine to parse query strings into a structured format that the search engine can execute. It defines the syntax and parsing logic for the query language supported by Tantivy.
  4. Understand SingleBlockSStable format

    main

    For tables containing very few keys, the standard SSTable footer (which costs ~70 bytes) is inefficient. To mitigate this, SingleBlockSStable omits the Fst and BlockAddrStore.

    Instead, it implicitly uses a single block with:

    • FirstOrdinal: 0
    • RangeStart: 0
    • RangeEnd: IndexOffset

    All operations are performed against this implicit block.

  5. Understand the SSTable on-disk format

    main

    The SSTable format is composed of a sequence of blocks followed by a footer. All numbers are little-endian unless otherwise noted.

    High-level Structure

    [ Block | Block | ... | Footer ]

    SSTBlock Structure

    Each block is independent and contains:

    • BlockLen (u32): Total length of the block, including the compression byte.
    • Compress (u8): 0 if not compressed, 1 if compressed.
    • Values: Application-defined format for storing a sequence of values.
    • Delta: A sequence of deltas used for key compression.

    SSTFooter Structure

    The footer contains the metadata required to navigate the table:

    • Fst (Fst): A finite state transducer mapping keys to block numbers.
    • BlockAddrStore (BlockAddrStore): Maps block numbers to their BlockAddr.
    • StoreOffset (u64): Offset to the start of the BlockAddrStore. If zero, the table is treated as a SingleBlockSStable.
    • IndexOffset (u64): Offset to the start of the SSTFooter.
    • NumTerm (u64): Total number of terms in the SSTable.
    • Version (u32): Currently version 3.
  6. How to perform searches using a Searcher

    main

    Users interact with the index through a Searcher. A Searcher provides a point-in-time snapshot of the index by holding a list of SegmentReader objects.

    Crucially, because the Searcher holds a snapshot, search results remain consistent even if background processes like segment merges, commits, or file garbage collection occur. To maintain a consistent view of the data, reuse the same Searcher instance.

  7. Use Fast Fields for high-performance random access

    main

    Fast fields (equivalent to Lucene's DocValues) provide column-oriented storage optimized for random access and aggregations. They use bitpacking compression and are designed for minimal memory overhead via mmap.

    Common Use Cases:

    • Ranking/Scoring: Combining values (like upvotes) with relevancy scores.
    • Aggregations: Computing metrics like the mean price of items in a result set.
    • Filtering: Post-filtering a DocSet based on a range (e.g., price).
    • Learning-to-Rank: Storing byte payloads for advanced model features.
    • Faceting: Specialized fast fields for facet navigation.

    Technical Detail: Fetching a value for a DocId is highly efficient, typically requiring only one memory fetch using the formula: min_value + fetch_bits(num_bits * doc_id..num_bits * (doc_id+1))

  8. How JSON object types work in Tantivy

    main

    As of version 0.17, Tantivy supports a JSON object type to enable schema-less searching. When indexing a JSON object, Tantivy "flattens" the structure by emitting terms represented as a triplet: (json_path, value_type, value).

    For example, indexing the following document:

    {
        "user": {
            "name": "Paul Masurel",
            "address": {
                "city": "Tokyo",
                "country": "Japan"
            },
            "created_at": "2018-11-12T23:20:50.52Z"
        }
    }

    results in emitted tokens such as:

    • ("name", Text, "Paul")
    • ("name", Text, "Masurel")
    • ("address.city", Text, "Tokyo")
    • ("address.country", Text, "Japan")
    • ("created_at", Date, 15420648505)
  9. Understand Tantivy's aggregation architecture

    main

    Tantivy's aggregation system is designed to mimic Elasticsearch's aggregation model. Aggregations are categorized into two main types:

    1. Bucket Aggregations (bucket submodule): These group documents into buckets (e.g., range aggregation). They support nested sub-aggregations.
    2. Metric Aggregations (metric submodule): These perform calculations on document values (e.g., average aggregation) and do not support sub-aggregations.

    The aggregation lifecycle involves several distinct data structures that evolve as the request moves from a user query to a final result:

  10. How Delta encoding and KeepAdd work in SSTables

    main

    To optimize key storage, SSTables use Delta encoding. Each Delta consists of a KeepAdd instruction and a Suffix (the actual bytes to append to the previous key).

    KeepAdd Representations

    KeepAdd determines how many bytes to pop from the previous key (Keep) and how many to push from the current suffix (Add).

    1. Compact Representation (used when keep < 16 and add < 16):

      • A single byte containing Add (4 bits) and Keep (4 bits).
    2. Variable-length Representation (used otherwise):

      • A prefix byte 0x01 followed by Keep (VInt) and Add (VInt).

    Note: Because SSTables do not support redundant keys, there is no ambiguity between these representations. Add is always non-zero, except for the very first key where Keep is guaranteed to be zero.

  11. Understand the Tantivy core concept

    main
    Tantivy is a high-performance full-text search engine library written in Rust. Unlike Elasticsearch or Apache Solr, which are standalone search engine servers, Tantivy is a library (a Rust crate) designed to be embedded into applications to build search engines. It is strongly inspired by Apache Lucene's design and is intended for developers who want to build their own search infrastructure rather than using an off-the-shelf server.