Back to Blog

Unifying OLAP, Vector Search, and Full-Text Search with Apache Doris

Tags: #Doris #OLAP #VectorSearch #HNSW #InvertedIndex #FullTextSearch #coomia-dip

CoomiaPublished on July 10, 202516 min read
Share this articleTwitter / X

Series: S3 Data Foundation · Article 1 | Level: Advanced | Reading Time: 20 min

Unifying OLAP, Vector Search, and Full-Text Search with Apache Doris

Tags: #Doris #OLAP #VectorSearch #HNSW #InvertedIndex #FullTextSearch #coomia-dip

#TL;DR

In coomia-dip (the Ontology-driven Intelligent Decision PaaS), we chose Apache Doris as our core analytics engine, handling OLAP aggregation, vector similarity search (HNSW index), and full-text retrieval (inverted index) in a single system. This article explains why we abandoned the ClickHouse + Qdrant + Elasticsearch stack, how one query can simultaneously perform aggregation, semantic search, and keyword matching, and presents benchmark results proving the unified approach delivers 70-82% better performance on hybrid queries while dramatically reducing operational complexity.

#1. Why a Unified Engine

#1.1 The Pain of Multi-Engine Architectures

Traditional data platforms handle diverse query needs with specialized engines:

Code
Traditional Multi-Engine Architecture:

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ ClickHouse  │  │   Qdrant    │  │Elasticsearch│
│  (OLAP)     │  │  (Vector)   │  │ (Full-Text) │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────────┬───────┴────────────────┘
                │
        ┌───────┴───────┐
        │  Application  │
        │  Query Router │
        └───────────────┘

This architecture introduces three core problems:

ProblemImpactQuantified Cost
Data SynchronizationSame data written to three systems; consistency is hardDev cycle +40%
Operational BurdenThree clusters to deploy, monitor, upgrade, scaleOps effort x3
Cross-Engine QueriesResults must be merged and re-sorted at the application layerQuery latency +200ms-2s
Resource WasteHot data replicated across three systemsStorage cost x2.5

#1.2 Doris Unified Capability Matrix

Starting from version 2.0, Apache Doris progressively added vector indexing and inverted indexing capabilities, enabling a single engine to cover all three scenarios:

Code
Unified Engine Architecture (coomia-dip):

                ┌──────────────────────┐
                │    Apache Doris      │
                │  ┌────┬─────┬─────┐  │
                │  │OLAP│HNSW │Inv. │  │
                │  │Agg │Vec  │Index│  │
                │  └────┴─────┴─────┘  │
                └──────────┬───────────┘
                           │
                   ┌───────┴───────┐
                   │  Application  │
                   │ (Single API)  │
                   └───────────────┘

Key Capability Comparison:

CapabilityClickHouseDoris 2.1+Gap
Columnar OLAPExcellentExcellentParity
Materialized ViewsPartialSync + AsyncDoris stronger
Vector Search (HNSW)Not supportedNativeDoris unique
Inverted IndexNot supportedNativeDoris unique
MySQL ProtocolIncompatibleFully compatibleDoris friendlier
Join PerformanceAverageShuffle + Colocation JoinDoris better

#1.3 Decision Matrix

We used a weighted scoring method for the final decision:

Evaluation DimensionWeightMulti-EngineDoris Unified
Query Performance30%9 (each optimized)8 (slight trade-off)
Ops Cost25%3 (three systems)9 (single system)
Data Consistency20%4 (cross-system sync)10 (single source)
Dev Efficiency15%4 (multiple SDKs)9 (unified SQL)
Resource Utilization10%4 (duplicate storage)9 (shared storage)
Weighted Total--5.358.85

#2. Doris OLAP Core Capabilities

#2.1 Columnar Storage Engine

Doris uses an MPP (Massively Parallel Processing) architecture with data stored column-wise across multiple BE (Backend) nodes:

Code
Doris Cluster Architecture:

 ┌─────────┐
 │   FE    │  Frontend: SQL parsing, query planning, metadata
 │ (Leader)│
 └────┬────┘
      │
┌─────┼─────────────────────────────┐
│     │         BE Cluster          │
│  ┌──┴──┐  ┌─────┐  ┌─────┐      │
│  │ BE1 │  │ BE2 │  │ BE3 │      │
│  │     │  │     │  │     │      │
│  │Tab1 │  │Tab1 │  │Tab1 │      │
│  │Part │  │Part │  │Part │      │
│  │ 1,4 │  │ 2,5 │  │ 3,6 │      │
│  └─────┘  └─────┘  └─────┘      │
└──────────────────────────────────┘

#2.2 Table Design in coomia-dip

Using the entity_common table as an example (detailed in S3-05), our Doris DDL:

SQL
CREATE TABLE IF NOT EXISTS entity_common (
    -- Partition keys
    world_id        VARCHAR(64)   NOT NULL COMMENT 'World ID',
    object_type_id  VARCHAR(128)  NOT NULL COMMENT 'Object Type ID',
    entity_id       VARCHAR(128)  NOT NULL COMMENT 'Entity ID',

    -- Core properties (high-frequency query columns)
    title           VARCHAR(512)  NULL COMMENT 'Title',
    status          VARCHAR(32)   NULL COMMENT 'Status',
    created_at      DATETIME      NOT NULL COMMENT 'Created timestamp',
    updated_at      DATETIME      NOT NULL COMMENT 'Updated timestamp',

    -- Flexible properties (JSON column)
    properties      JSON          NULL COMMENT 'Dynamic properties JSON',

    -- Vector column (embedding)
    embedding       ARRAY<FLOAT>  NULL COMMENT 'Semantic embedding vector (768d)',

    -- Full-text search column
    search_text     TEXT          NULL COMMENT 'Full-text search content'
)
ENGINE = OLAP
DUPLICATE KEY(world_id, object_type_id, entity_id)
PARTITION BY RANGE(created_at) (
    PARTITION p202601 VALUES LESS THAN ('2026-02-01'),
    PARTITION p202602 VALUES LESS THAN ('2026-03-01'),
    PARTITION p202603 VALUES LESS THAN ('2026-04-01')
)
DISTRIBUTED BY HASH(entity_id) BUCKETS 16
PROPERTIES (
    "replication_allocation" = "tag.location.default: 3",
    "storage_format" = "V2",
    "enable_unique_key_merge_on_write" = "true"
);

#2.3 OLAP Aggregation Query Examples

SQL
-- Count entities by type, grouped by month
SELECT
    object_type_id,
    DATE_TRUNC('month', created_at) AS month,
    COUNT(*) AS entity_count,
    COUNT(DISTINCT status) AS status_variety,
    AVG(JSON_EXTRACT_DOUBLE(properties, '$.risk_score')) AS avg_risk
FROM entity_common
WHERE world_id = 'world-prod-001'
  AND created_at >= '2026-01-01'
GROUP BY object_type_id, DATE_TRUNC('month', created_at)
ORDER BY month DESC, entity_count DESC
LIMIT 100;

Doris excels in OLAP scenarios through the combination of its vectorized execution engine and columnar storage. For the query above, Doris will:

  1. Partition pruning: Only scan relevant partitions based on the created_at condition
  2. Predicate pushdown: The world_id filter is applied at the storage layer
  3. Vectorized computation: COUNT, AVG aggregations use SIMD instructions
  4. Parallel execution: Each BE node processes its own buckets concurrently

#3.1 Vector Search Principles

HNSW (Hierarchical Navigable Small World) is a graph-based approximate nearest neighbor (ANN) search algorithm. Its core idea is building a multi-layer skip-list-style graph structure:

Code
HNSW Multi-Layer Graph Structure:

Layer 2:   [A]─────────────────[D]
            │                    │
Layer 1:   [A]────[B]────[C]───[D]────[E]
            │      │      │     │      │
Layer 0:   [A]─[F]─[B]─[G]─[C]─[H]─[D]─[I]─[E]─[J]

Search process (finding nearest neighbor of Q):
1. Start from entry point A at Layer 2
2. Greedy search on Layer 2: A -> D (D is closer to Q)
3. Descend to Layer 1: D -> E (E is closer to Q)
4. Descend to Layer 0: E -> I -> J (nearest neighbor found)

#3.2 Creating HNSW Index in Doris

SQL
-- Create HNSW index on the embedding column of entity_common
ALTER TABLE entity_common
ADD INDEX idx_embedding_hnsw (embedding)
USING INVERTED
PROPERTIES (
    "index_type" = "HNSW",
    "metric_type" = "COSINE",
    "dim" = "768",
    "M" = "32",
    "ef_construction" = "200"
);

HNSW Parameter Reference:

ParameterDefaultRecommendedDescription
M1632Max connections per node. Higher = better recall, larger index
ef_construction100200Search width during build. Higher = better quality, slower build
ef_search100150Search width during query. Higher = better recall, slower query
metric_typeL2COSINEDistance metric. COSINE recommended for semantic search
dim-768Vector dimension. Must match embedding model output

#3.3 Vector Search Queries

SQL
-- Semantic search: find 10 most similar entities to a given vector
SELECT
    entity_id,
    title,
    object_type_id,
    COSINE_DISTANCE(embedding, ARRAY[0.12, -0.34, ..., 0.56]) AS distance
FROM entity_common
WHERE world_id = 'world-prod-001'
  AND object_type_id = 'Equipment'
ORDER BY distance ASC
LIMIT 10;

#3.4 Vector Search + OLAP Combined Query

This is the core advantage of Doris as a unified engine -- combining scalar filtering and vector search in one SQL statement:

SQL
-- Combined query: semantic search with specific conditions
SELECT
    entity_id,
    title,
    status,
    JSON_EXTRACT_STRING(properties, '$.department') AS department,
    COSINE_DISTANCE(embedding, ARRAY[0.12, -0.34, ..., 0.56]) AS similarity
FROM entity_common
WHERE world_id = 'world-prod-001'
  AND object_type_id = 'Employee'
  AND status = 'active'
  AND created_at >= '2026-01-01'
ORDER BY similarity ASC
LIMIT 20;

Execution Plan Analysis:

Code
Query Execution Plan:

┌────────────────────────┐
│    Result (Top 20)     │
└────────┬───────────────┘
         │
┌────────┴───────────────┐
│   Sort by similarity   │
│   (TopN Heap Sort)     │
└────────┬───────────────┘
         │
┌────────┴───────────────┐
│  HNSW Vector Search    │
│  (ANN on embedding)    │
└────────┬───────────────┘
         │
┌────────┴───────────────┐
│  Predicate Filter      │
│  world_id = '...'      │
│  object_type_id = '...'│
│  status = 'active'     │
│  created_at >= '...'   │
└────────┬───────────────┘
         │
┌────────┴───────────────┐
│  Partition Pruning     │
│  (created_at range)    │
└────────────────────────┘

#4.1 Doris Inverted Index Architecture

Doris 2.0 introduced CLucene-based inverted indexes supporting full-text search, phrase matching, and tokenization:

Code
Inverted Index Internal Structure:

Document: "Apache Doris is a high-performance analytics database"

Tokenization:
┌────────┬──────────────────────────┐
│ Token  │ Posting List (Doc IDs)   │
├────────┼──────────────────────────┤
│ apache │ [1, 15, 42, 88]         │
│ doris  │ [1, 3, 15, 42]          │
│ high   │ [1, 7, 23, 56, 88]     │
│ perf*  │ [1, 7, 12, 56]         │
│ analyt*│ [1, 3, 42, 67]         │
│ datab* │ [1, 3, 7, 42, 67, 88]  │
└────────┴──────────────────────────┘

#4.2 Creating Inverted Indexes

SQL
-- Create inverted index on search_text column (with CJK tokenization)
ALTER TABLE entity_common
ADD INDEX idx_search_text (search_text)
USING INVERTED
PROPERTIES (
    "parser" = "unicode",
    "support_phrase" = "true",
    "lower_case" = "true"
);

-- Create inverted index on title column
ALTER TABLE entity_common
ADD INDEX idx_title_inv (title)
USING INVERTED
PROPERTIES (
    "parser" = "unicode",
    "support_phrase" = "true"
);

#4.3 Full-Text Search Queries

SQL
-- Full-text search: find entities containing "risk assessment"
SELECT
    entity_id,
    title,
    object_type_id,
    search_text
FROM entity_common
WHERE world_id = 'world-prod-001'
  AND MATCH_ALL(search_text, 'risk assessment')
ORDER BY updated_at DESC
LIMIT 20;

-- Phrase matching
SELECT entity_id, title
FROM entity_common
WHERE MATCH_PHRASE(search_text, 'data quality check')
LIMIT 10;

-- Fuzzy matching
SELECT entity_id, title
FROM entity_common
WHERE MATCH_ALL(title, 'equipment failure')
  AND object_type_id = 'MaintenanceRecord'
LIMIT 10;

#4.4 Tokenizer Configuration

SQL
-- The unicode tokenizer handles mixed CJK and Latin text natively
-- Verify tokenization results
SELECT TOKENIZE('equipment failure report Q1 2026', 'unicode');
-- Result: ["equipment", "failure", "report", "q1", "2026"]

#5. Three-in-One Query: OLAP + Vector + Full-Text

#5.1 The Ultimate Combined Query

This is the pinnacle of the unified engine experience -- one query leveraging all three capabilities:

SQL
-- Scenario: Among active equipment, find records semantically related
-- to "abnormal vibration", containing the keyword "maintenance",
-- aggregated by department

WITH semantic_matches AS (
    SELECT
        entity_id,
        title,
        status,
        JSON_EXTRACT_STRING(properties, '$.department') AS department,
        JSON_EXTRACT_DOUBLE(properties, '$.severity') AS severity,
        COSINE_DISTANCE(embedding, ARRAY[0.12, -0.34, ..., 0.56]) AS vec_distance
    FROM entity_common
    WHERE world_id = 'world-prod-001'
      AND object_type_id = 'MaintenanceRecord'
      AND status IN ('open', 'in_progress')
      AND MATCH_ALL(search_text, 'maintenance')
    ORDER BY vec_distance ASC
    LIMIT 200
)
SELECT
    department,
    COUNT(*) AS record_count,
    AVG(severity) AS avg_severity,
    MIN(vec_distance) AS best_semantic_match,
    GROUP_CONCAT(title ORDER BY vec_distance ASC SEPARATOR ' | ') AS top_titles
FROM semantic_matches
GROUP BY department
ORDER BY avg_severity DESC;

Query Execution Flow:

Code
Three-in-One Query Execution:

Step 1: Partition Pruning
  +-- created_at range -> scan only relevant partitions

Step 2: Predicate Push-down
  +-- world_id = 'world-prod-001'        -> Prefix Index
  +-- object_type_id = 'Maintenance...'   -> Prefix Index
  +-- status IN ('open', 'in_progress')   -> Bitmap Index

Step 3: Inverted Index Scan
  +-- MATCH_ALL(search_text, 'maintenance') -> Posting List

Step 4: HNSW Vector Search
  +-- COSINE_DISTANCE(embedding, [...])   -> ANN Top-200

Step 5: Intersection
  +-- Step2 INTERSECT Step3 INTERSECT Step4

Step 6: Aggregation
  +-- GROUP BY department + AVG + COUNT

#5.2 Performance Comparison

Benchmarks using 10 million entity_common records:

Query TypeMulti-EngineDoris UnifiedImprovement
Pure OLAP aggregation180ms (CH)210ms-14%
Pure vector Top-1012ms (Qdrant)25ms-52%
Pure full-text search35ms (ES)45ms-22%
OLAP + vector combined380ms (cross-sys)85ms+77%
OLAP + text combined290ms (cross-sys)65ms+78%
Three-in-one combined650ms (cross-sys)120ms+82%
Write latency3x writes 150ms1x write 50ms+67%

Key Finding: While Doris is slightly slower than specialized engines in single-modal queries (10-50%), it dramatically outperforms multi-engine setups in combined queries (70-80%) by eliminating cross-system network latency and result merging overhead.

#6. Configuration and Tuning

#6.1 FE Configuration

PROPERTIES
# fe.conf - coomia-dip recommended configuration

# Memory
JAVA_OPTS="-Xmx8g -Xms4g"

# Query timeout
max_query_timeout = 300

# Vector search parameters
default_hnsw_ef_search = 150

# Inverted index configuration
inverted_index_ram_dir_enable = true

# Concurrency control
max_running_txn_num_per_db = 1000
qe_max_connection = 2048

#6.2 BE Configuration

PROPERTIES
# be.conf - coomia-dip recommended configuration

# Memory
mem_limit = 80%
storage_page_cache_limit = 40%

# Vector search memory
vector_index_cache_capacity = 2147483648  # 2GB

# Compaction
default_rowset_type = BETA
compaction_task_num_per_disk = 4

# Concurrency
doris_scanner_thread_pool_thread_num = 48
doris_scanner_thread_pool_queue_size = 102400

# Storage
storage_root_path = /data/doris/storage

#6.3 Index Selection Guide

Code
Index Selection Decision Tree:

                    ┌──────────────────┐
                    │ What query type?  │
                    └────────┬─────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
        ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
        │ Exact/     │ │ Semantic   │ │ Full-Text │
        │ Range      │ │ Similarity │ │ Keywords  │
        └─────┬─────┘ └─────┬─────┘ └─────┬─────┘
              │              │              │
        ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
        │ Prefix/   │ │  HNSW     │ │ Inverted  │
        │ Bitmap    │ │  Index    │ │  Index    │
        │ Index     │ │           │ │           │
        └───────────┘ └───────────┘ └───────────┘

#6.4 Partition and Bucket Strategy

SQL
-- Dynamic partition configuration (automatic monthly partitions)
ALTER TABLE entity_common SET (
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "MONTH",
    "dynamic_partition.start" = "-12",
    "dynamic_partition.end" = "3",
    "dynamic_partition.prefix" = "p",
    "dynamic_partition.buckets" = "16",
    "dynamic_partition.replication_allocation" = "tag.location.default: 3"
);

#7. coomia-dip Integration Architecture

#7.1 Data Write Flow

Code
Data Write Flow:

┌──────────┐     ┌─────────────┐     ┌──────────────┐
│ Control  │gRPC │ Data Layer  │     │   Doris      │
│  Layer   ├────>│ WriteService├────>│   (Stream    │
│ (B)      │     │             │     │    Load)     │
└──────────┘     └──────┬──────┘     └──────────────┘
                        │
                        │ async
                        v
                 ┌──────────────┐
                 │  Embedding   │
                 │  Service (D) │
                 │  (generate   │
                 │   vectors)   │
                 └──────┬───────┘
                        │
                        v
                 ┌──────────────┐
                 │ Doris UPDATE │
                 │ (embedding   │
                 │  column)     │
                 └──────────────┘

#7.2 Query Service Architecture

Python
# Doris query client in python-sdk
class DorisQueryClient:
    """Unified query client supporting OLAP/vector/full-text three-in-one"""

    def __init__(self, config: DorisConfig):
        self.pool = ConnectionPool(
            host=config.host,
            port=config.query_port,
            user=config.user,
            password=config.password,
            database=config.database,
            pool_size=config.pool_size
        )

    async def unified_search(
        self,
        world_id: str,
        object_type_id: str,
        *,
        filters: dict | None = None,
        vector_query: list[float] | None = None,
        text_query: str | None = None,
        aggregations: list[str] | None = None,
        limit: int = 100
    ) -> QueryResult:
        """Three-in-one unified search"""
        sql_builder = UnifiedSQLBuilder()

        # Base conditions
        sql_builder.add_condition("world_id", "=", world_id)
        sql_builder.add_condition("object_type_id", "=", object_type_id)

        # Scalar filters
        if filters:
            for key, value in filters.items():
                sql_builder.add_condition(key, "=", value)

        # Vector search
        if vector_query:
            sql_builder.add_vector_search("embedding", vector_query, metric="cosine")

        # Full-text search
        if text_query:
            sql_builder.add_text_search("search_text", text_query)

        # Aggregations
        if aggregations:
            for agg in aggregations:
                sql_builder.add_aggregation(agg)

        sql = sql_builder.build(limit=limit)
        return await self.execute(sql)

#7.3 gRPC Service Definition

PROTOBUF
// query_service.proto
service UnifiedQueryService {
    // Unified query interface
    rpc ExecuteQuery(QueryRequest) returns (QueryResponse);

    // Vector search
    rpc VectorSearch(VectorSearchRequest) returns (SearchResponse);

    // Full-text search
    rpc TextSearch(TextSearchRequest) returns (SearchResponse);

    // Hybrid search
    rpc HybridSearch(HybridSearchRequest) returns (SearchResponse);
}

message HybridSearchRequest {
    string world_id = 1;
    string object_type_id = 2;
    repeated Filter filters = 3;
    VectorQuery vector_query = 4;
    TextQuery text_query = 5;
    repeated Aggregation aggregations = 6;
    int32 limit = 7;
}

#8. Production Benchmark Results

#8.1 Test Environment

ConfigurationValue
Cluster Size3 FE + 5 BE
BE Specs32C / 128GB / 2TB NVMe SSD
Data Volume50 million entity_common records
Vector Dimension768 (BGE-large)
IndexesHNSW(M=32, ef=200) + Inverted Index

#8.2 Test Results

Code
Query Latency Distribution (P50 / P95 / P99):

OLAP Aggregation (10-column GROUP BY):
  P50: 120ms  P95: 350ms  P99: 800ms
  ████████████▒▒▒░░░░░░░░░░░░░░░░░░

Vector Search Top-100:
  P50: 18ms   P95: 45ms   P99: 90ms
  ███▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░

Full-Text Search (tokenized):
  P50: 25ms   P95: 80ms   P99: 150ms
  █████▒▒░░░░░░░░░░░░░░░░░░░░░░░░░

Three-in-One Combined:
  P50: 85ms   P95: 250ms  P99: 500ms
  █████████▒▒▒░░░░░░░░░░░░░░░░░░░░

Write Throughput (Stream Load):
  Single BE: 50,000 rows/sec
  Cluster:   200,000 rows/sec

#8.3 Resource Usage

Code
Cluster Resource Utilization (5 BE nodes average):

CPU:     ██████████████░░░░░░  68%
Memory:  ████████████████░░░░  82%
Disk IO: ██████████░░░░░░░░░░  48%
Network: ████░░░░░░░░░░░░░░░░  22%

#9. Common Issues and Solutions

#9.1 Slow Vector Index Build

Problem: Adding HNSW index to large tables takes too long.

Solution:

SQL
-- 1. Reduce build parameters (lower ef_construction)
ALTER TABLE entity_common
ADD INDEX idx_emb_hnsw (embedding) USING INVERTED
PROPERTIES ("index_type"="HNSW", "M"="16", "ef_construction"="100");

-- 2. Build incrementally (create table with index first, then import data)
-- 3. Use Routine Load for incremental builds

#9.2 Insufficient Vector Recall

Problem: HNSW approximate search misses some relevant results.

Solution:

SQL
-- Increase ef_search (trade speed for recall)
SET SESSION hnsw_ef_search = 300;

-- Expand candidate set
SELECT entity_id, COSINE_DISTANCE(embedding, ...) AS dist
FROM entity_common
WHERE ...
ORDER BY dist ASC
LIMIT 50;  -- Fetch more, re-rank in application layer

#9.3 Tokenization Quality

Problem: Default tokenizer handles domain terms poorly.

Solution:

SQL
-- Use custom dictionaries
-- Add custom dictionary files to BE conf/dict/ directory
-- custom_dict.txt:
-- ontology
-- data lineage
-- knowledge graph

-- Rebuild index to apply
ALTER TABLE entity_common DROP INDEX idx_search_text;
ALTER TABLE entity_common ADD INDEX idx_search_text (search_text)
USING INVERTED PROPERTIES (
    "parser" = "unicode",
    "support_phrase" = "true",
    "dict_path" = "custom_dict.txt"
);

#10. Relationship to Other coomia-dip Components

Code
coomia-dip Storage Layer Overview:

┌─────────────────────────────────────────────────┐
│                  Query Layer                     │
│  ┌──────────────────────────────────────────┐   │
│  │       QueryFederationService (C)          │   │
│  └──────┬──────────────────┬────────────────┘   │
│         │                  │                     │
│    ┌────┴─────┐      ┌────┴─────┐               │
│    │  Doris   │      │  DuckDB  │               │
│    │ (Primary)│      │ (Aux)    │               │
│    └────┬─────┘      └──────────┘               │
│         │                                        │
│    ┌────┴──────────────────────────────┐        │
│    │         Storage Layer              │        │
│    │  ┌─────────┐  ┌────────────────┐  │        │
│    │  │ MinIO   │  │ Nessie+Iceberg │  │        │
│    │  │(Object) │  │ (Versioned     │  │        │
│    │  │         │  │  Lakehouse)    │  │        │
│    │  └─────────┘  └────────────────┘  │        │
│    └────────────────────────────────────┘        │
└─────────────────────────────────────────────────┘

#Key Takeaways

  1. Unified engine beats specialized engine combinations in hybrid query scenarios. Doris delivers 70-82% better performance on combined queries while drastically reducing operational complexity.

  2. HNSW indexes give OLAP engines semantic search capability. With proper M and ef parameter tuning, you can find the optimal balance between recall rate and performance.

  3. Inverted indexes support full-text search natively. The built-in unicode tokenizer plus custom dictionaries handle multilingual full-text retrieval effectively.

  4. Three-in-one queries are the killer feature. Performing OLAP aggregation + vector search + full-text retrieval in a single SQL statement is something no multi-engine setup can do efficiently.

  5. Proper index strategy is the performance key. Choose the right index type (Prefix/Bitmap/HNSW/Inverted) based on query patterns, and optimize data distribution through partition and bucket strategies.

#Next Article

The next article, S3-02 "Managing Data Like Git: Data Version Control with Nessie + Iceberg", deep-dives into how coomia-dip uses Nessie and Iceberg to provide Git-style version control for the data layer, including branching, merging, conflict resolution, and time-travel queries.

Tags: #ApacheDoris #OLAP #VectorSearch #HNSW #InvertedIndex #FullTextSearch #coomia-dip #UnifiedEngine #DataFoundation