Back to Blog

Apache Doris Deep Practice (Part 2): Vector Index + Inverted Index

In coomia-dip's earlier architecture designs, we initially considered the typical multi-engine approach:

CoomiaPublished on November 10, 202517 min read
Share this articleTwitter / X

Series: S8 Technology Deep Dives · Article 2 | Level: Advanced | Reading Time: 20 min

Apache Doris Deep Practice (Part 2): Vector Index + Inverted Index

#TL;DR

  • Doris 2.1+ natively supports HNSW vector indexes and inverted indexes, enabling coomia-dip to achieve semantic search and full-text retrieval without deploying standalone Milvus/Elasticsearch
  • Sub-second full-text search via inverted indexes replaces the traditional Elasticsearch approach, achieving < 300ms search latency on 50 million Ontology description documents
  • The hybrid query combining vector index + inverted index + OLAP analytics is coomia-dip's core differentiating capability; this article details configuration, tuning, and production practices

#1. The Value of a Unified Engine

#1.1 Pain Points of Traditional Approaches

In coomia-dip's earlier architecture designs, we initially considered the typical multi-engine approach:

Code
OLAP Analytics     → ClickHouse / Doris
Full-text Search   → Elasticsearch
Vector Semantic    → Milvus / Pinecone

This approach has three core pain points:

  1. Data consistency: The same data needs to be synchronized to three engines; latency and consistency are hard to guarantee
  2. Operational complexity: Deployment, monitoring, upgrades, and backups for three clusters
  3. Query fragmentation: Hybrid queries (e.g., "find data from the last 30 days that is similar to a vector and contains specific keywords") require multiple queries and result merging at the application layer

#1.2 Doris 2.1+ Unified Capabilities

Doris 2.1 introduced vector indexes and inverted indexes, enabling a single engine to satisfy all three requirements:

CapabilityImplementationPerformance
OLAP AnalyticsColumnar + vectorized execution + MPPSub-second aggregation
Full-text SearchInverted Index (CLucene-based)< 300ms
Vector SearchHNSW Index< 200ms (Top-100)

#1.3 Application Scenarios in coomia-dip

ScenarioIndex UsedExample
Ontology object searchInverted indexSearch all objects with "supply chain" in name or description
Semantic similar object discoveryVector indexFind Actions semantically similar to "supply chain risk assessment"
Hybrid intelligent searchInverted + vector + OLAPFind recently created objects matching user query semantically and by keyword
Knowledge base Q&AVector indexRetrieve most relevant knowledge fragments in RAG scenarios
Anomaly detection assistanceVector indexFind historical records most similar to anomaly sample embeddings

#2. Inverted Index Deep Analysis

#2.1 Inverted Index Architecture

Doris's inverted index is built on CLucene (C++ implementation of Lucene), tightly integrated with columnar data:

Code
┌──────────────────────────────────────┐
│            Doris Segment             │
│  ┌─────────────┬──────────────────┐  │
│  │ Column Data  │ Inverted Index   │  │
│  │ (Columnar)   │ (CLucene)        │  │
│  │              │ ┌──────────────┐ │  │
│  │ tenant_id    │ │ Term Dict    │ │  │
│  │ object_type  │ │ Posting List │ │  │
│  │ display_name │ │ Doc Values   │ │  │
│  │ description  │ │ Norms        │ │  │
│  │ properties   │ └──────────────┘ │  │
│  └─────────────┴──────────────────┘  │
└──────────────────────────────────────┘

#2.2 Creating Inverted Indexes

Inverted index configuration for the Ontology object table in coomia-dip:

SQL
CREATE TABLE ontology_object_searchable (
    tenant_id      VARCHAR(64)    NOT NULL COMMENT 'Tenant ID',
    object_type    VARCHAR(128)   NOT NULL COMMENT 'Object type',
    object_rid     VARCHAR(256)   NOT NULL COMMENT 'Object RID',
    display_name   VARCHAR(512)   NOT NULL COMMENT 'Display name',
    description    TEXT           COMMENT 'Description',
    tags           ARRAY<VARCHAR(64)> COMMENT 'Tag array',
    properties_json TEXT          COMMENT 'Properties JSON',
    status         VARCHAR(32)    COMMENT 'Status',
    created_at     DATETIME       COMMENT 'Created at',
    updated_at     DATETIME       COMMENT 'Updated at',

    -- Inverted index definitions
    INDEX idx_display_name (display_name)
        USING INVERTED
        PROPERTIES("parser" = "unicode", "support_phrase" = "true"),

    INDEX idx_description (description)
        USING INVERTED
        PROPERTIES(
            "parser" = "unicode",
            "support_phrase" = "true",
            "lower_case" = "true"
        ),

    INDEX idx_tags (tags)
        USING INVERTED
        PROPERTIES("parser" = "unicode"),

    INDEX idx_properties (properties_json)
        USING INVERTED
        PROPERTIES("parser" = "unicode", "support_phrase" = "true"),

    INDEX idx_status (status)
        USING INVERTED,

    INDEX idx_object_type (object_type)
        USING INVERTED
)
UNIQUE KEY(tenant_id, object_type, object_rid)
DISTRIBUTED BY HASH(tenant_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "enable_unique_key_merge_on_write" = "true",
    "inverted_index_storage_format" = "V2",
    "store_row_column" = "true"
);

#2.3 Tokenizer Selection

TokenizerUse CaseConfigurationExample
unicodeMixed CJK+English text"parser" = "unicode""supply chain" → ["supply", "chain"]
englishPure English text"parser" = "english""supply chain" → ["supply", "chain"]
chinesePure Chinese text"parser" = "chinese"Uses jieba tokenization
NoneExact matchNo parser specifiedSuitable for enum values

coomia-dip tokenizer strategy: Since the platform supports mixed Chinese-English scenarios, we use the unicode tokenizer uniformly. For fields requiring exact match (status, object_type), no tokenizer is configured for exact indexing.

#2.4 Full-Text Search Query Syntax

SQL
-- Basic full-text search: MATCH_ANY (OR semantics)
SELECT object_rid, display_name, description
FROM ontology_object_searchable
WHERE tenant_id = 'tenant-001'
  AND display_name MATCH_ANY 'supply chain risk'
ORDER BY updated_at DESC
LIMIT 20;

-- Phrase search: MATCH_PHRASE (requires contiguous word order)
SELECT object_rid, display_name, description
FROM ontology_object_searchable
WHERE tenant_id = 'tenant-001'
  AND description MATCH_PHRASE 'supply chain risk assessment'
LIMIT 20;

-- Full match search: MATCH_ALL (AND semantics)
SELECT object_rid, display_name, description
FROM ontology_object_searchable
WHERE tenant_id = 'tenant-001'
  AND description MATCH_ALL 'supply chain risk assessment'
LIMIT 20;

-- Prefix search: MATCH_PHRASE_PREFIX
SELECT object_rid, display_name
FROM ontology_object_searchable
WHERE tenant_id = 'tenant-001'
  AND display_name MATCH_PHRASE_PREFIX 'supply ch'
LIMIT 20;

-- Combined conditions: full-text search + exact filter
SELECT object_rid, display_name, description, status, created_at
FROM ontology_object_searchable
WHERE tenant_id = 'tenant-001'
  AND description MATCH_ANY 'risk assessment alert'
  AND status = 'ACTIVE'
  AND created_at >= '2025-01-01'
ORDER BY created_at DESC
LIMIT 50;

#2.5 Inverted Index Performance Benchmarks

Benchmarks on coomia-dip test environment (3 BE nodes, 50 million records):

Query TypeWithout Inverted IndexWith Inverted IndexSpeedup
Exact match (status)1.2s0.03s40x
Keyword search (MATCH_ANY)5.6s0.18s31x
Phrase search (MATCH_PHRASE)8.3s0.25s33x
Fuzzy search (LIKE '%keyword%')12.1s0.30s40x
Combined query (text+filter+sort)15.2s0.42s36x

#3. Vector Index Deep Analysis

#3.1 Vector Index Principle: HNSW

Doris uses the HNSW (Hierarchical Navigable Small World) algorithm for vector indexing:

Code
Layer 3:  [A] ────────────────── [F]
           |                      |
Layer 2:  [A] ──── [C] ──── [F] ──── [H]
           |        |        |        |
Layer 1:  [A] ─ [B] ─ [C] ─ [D] ─ [F] ─ [G] ─ [H]
           |    |    |    |    |    |    |    |
Layer 0:  [A]-[B]-[C]-[D]-[E]-[F]-[G]-[H]-[I]-[J]

HNSW Key Parameters:

ParameterMeaningRecommendedPerformance Impact
MMax neighbors per layer16-64Higher = more accurate, more memory
efConstructionSearch width during build200-500Higher = slower build, more accurate
efSearchSearch width during query100-300Higher = slower query, more accurate

#3.2 Creating Vector Index Tables

Semantic search table definition in coomia-dip:

SQL
CREATE TABLE ontology_embeddings (
    tenant_id       VARCHAR(64)    NOT NULL COMMENT 'Tenant ID',
    object_type     VARCHAR(128)   NOT NULL COMMENT 'Object type',
    object_rid      VARCHAR(256)   NOT NULL COMMENT 'Object RID',
    display_name    VARCHAR(512)   COMMENT 'Display name',
    description     TEXT           COMMENT 'Description text',
    embedding       ARRAY<FLOAT>   NOT NULL COMMENT 'Embedding vector (768-dim)',
    model_version   VARCHAR(32)    COMMENT 'Model version',
    created_at      DATETIME       COMMENT 'Created at',

    -- Vector index
    INDEX idx_embedding (embedding)
        USING INVERTED
        PROPERTIES(
            "index_type" = "HNSW",
            "metric_type" = "L2",
            "dim" = "768",
            "M" = "32",
            "efConstruction" = "400"
        ),

    -- Text inverted index (for hybrid queries)
    INDEX idx_display_name (display_name)
        USING INVERTED
        PROPERTIES("parser" = "unicode"),

    INDEX idx_description (description)
        USING INVERTED
        PROPERTIES("parser" = "unicode", "support_phrase" = "true")
)
UNIQUE KEY(tenant_id, object_type, object_rid)
DISTRIBUTED BY HASH(tenant_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "enable_unique_key_merge_on_write" = "true"
);

#3.3 Vector Data Import

Using the Python SDK to import embeddings into Doris:

Python
# intelligence-Layer/embedding_indexer.py
import numpy as np
from typing import List, Dict
from sentence_transformers import SentenceTransformer
from doris_stream_loader import DorisStreamLoader

class OntologyEmbeddingIndexer:
    """Ontology object embedding indexer"""

    def __init__(
        self,
        model_name: str = "BAAI/bge-base-zh-v1.5",
        doris_host: str = "doris-fe-1",
        doris_port: int = 8030
    ):
        self.model = SentenceTransformer(model_name)
        self.loader = DorisStreamLoader(doris_host, doris_port)
        self.model_version = model_name.split("/")[-1]

    def index_objects(
        self,
        tenant_id: str,
        objects: List[Dict]
    ) -> int:
        """Batch index Ontology objects"""
        records = []
        texts = []

        for obj in objects:
            text = f"{obj['display_name']}. {obj.get('description', '')}"
            texts.append(text)

        # Batch encode
        embeddings = self.model.encode(
            texts,
            batch_size=64,
            show_progress_bar=True,
            normalize_embeddings=True
        )

        for obj, embedding in zip(objects, embeddings):
            records.append({
                "tenant_id": tenant_id,
                "object_type": obj["object_type"],
                "object_rid": obj["object_rid"],
                "display_name": obj["display_name"],
                "description": obj.get("description", ""),
                "embedding": embedding.tolist(),
                "model_version": self.model_version,
                "created_at": obj.get("created_at")
            })

        # Bulk import
        result = self.loader.load_json_data(
            database="ontology_db",
            table="ontology_embeddings",
            data=records
        )

        return len(records)

#3.4 Vector Search Queries

SQL
-- Basic vector similarity search (L2 distance)
SELECT
    object_rid,
    display_name,
    L2_DISTANCE(embedding, ARRAY[0.1, 0.2, ...]) as distance
FROM ontology_embeddings
WHERE tenant_id = 'tenant-001'
ORDER BY distance ASC
LIMIT 10;

-- Cosine similarity search
SELECT
    object_rid,
    display_name,
    COSINE_DISTANCE(embedding, ARRAY[0.1, 0.2, ...]) as distance
FROM ontology_embeddings
WHERE tenant_id = 'tenant-001'
ORDER BY distance ASC
LIMIT 10;

-- Inner product search
SELECT
    object_rid,
    display_name,
    INNER_PRODUCT(embedding, ARRAY[0.1, 0.2, ...]) as score
FROM ontology_embeddings
WHERE tenant_id = 'tenant-001'
ORDER BY score DESC
LIMIT 10;

#3.5 Vector Index Performance Benchmarks

Vector search benchmarks on coomia-dip test environment (768-dimension vectors):

Data VolumeBrute ForceHNSW (M=16)HNSW (M=32)HNSW (M=64)
1M850ms12ms8ms6ms
5M4.2s25ms15ms11ms
10M8.5s45ms28ms18ms
50M42s120ms75ms52ms

Recall@100 Accuracy:

ConfigefSearch=100efSearch=200efSearch=400
M=1692.3%96.1%98.5%
M=3295.7%98.2%99.3%
M=6497.8%99.1%99.7%

#4. Hybrid Query: OLAP + Vector + Full-Text

#4.1 Hybrid Query Architecture

One of coomia-dip's most powerful capabilities is combining OLAP analytics, vector search, and full-text search in a single query:

Code
User query: "Find active objects created in the last 30 days
             semantically similar to 'supply chain risk assessment'"
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
   Vector Search  Text Search   OLAP Filter
   (Semantic)     (Keywords)    (Time+Status)
        │           │           │
        └───────────┼───────────┘
                    ▼
           Result Fusion Ranking
           (Weighted Scoring)

#4.2 Hybrid Query Implementation

SQL
-- Hybrid query: vector similarity + full-text match + OLAP filter
SELECT
    e.object_rid,
    e.display_name,
    e.description,
    e.object_type,
    L2_DISTANCE(e.embedding, ARRAY[/* query embedding */]) as vector_distance,
    e.created_at
FROM ontology_embeddings e
WHERE e.tenant_id = 'tenant-001'
  -- OLAP filter
  AND e.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
  -- Full-text search
  AND e.description MATCH_ANY 'supply chain risk'
ORDER BY vector_distance ASC
LIMIT 20;

#4.3 Advanced Hybrid Query: Weighted Scoring

SQL
-- Weighted hybrid scoring query
WITH vector_results AS (
    SELECT
        object_rid,
        display_name,
        description,
        L2_DISTANCE(embedding, ARRAY[/* query embedding */]) as v_dist,
        ROW_NUMBER() OVER (ORDER BY L2_DISTANCE(embedding, ARRAY[/* query embedding */])) as v_rank
    FROM ontology_embeddings
    WHERE tenant_id = 'tenant-001'
    ORDER BY v_dist ASC
    LIMIT 100
),
text_results AS (
    SELECT
        object_rid,
        display_name,
        description,
        ROW_NUMBER() OVER (ORDER BY updated_at DESC) as t_rank
    FROM ontology_object_searchable
    WHERE tenant_id = 'tenant-001'
      AND description MATCH_ANY 'supply chain risk assessment'
    LIMIT 100
)
SELECT
    COALESCE(v.object_rid, t.object_rid) as object_rid,
    COALESCE(v.display_name, t.display_name) as display_name,
    -- Weighted score: vector similarity weight 0.6, text match weight 0.4
    (COALESCE(1.0 / v.v_rank, 0) * 0.6 +
     COALESCE(1.0 / t.t_rank, 0) * 0.4) as hybrid_score
FROM vector_results v
FULL OUTER JOIN text_results t ON v.object_rid = t.object_rid
ORDER BY hybrid_score DESC
LIMIT 20;

#4.4 Search API Wrapper in coomia-dip

Python
# intelligence-Layer/search/hybrid_search.py
from dataclasses import dataclass, field
from typing import List, Optional
from enum import Enum

class SearchMode(Enum):
    KEYWORD = "keyword"
    SEMANTIC = "semantic"
    HYBRID = "hybrid"

@dataclass
class SearchRequest:
    tenant_id: str
    query: str
    mode: SearchMode = SearchMode.HYBRID
    object_types: Optional[List[str]] = None
    status_filter: Optional[str] = None
    date_from: Optional[str] = None
    date_to: Optional[str] = None
    top_k: int = 20
    vector_weight: float = 0.6
    text_weight: float = 0.4

@dataclass
class SearchResult:
    object_rid: str
    display_name: str
    description: str
    object_type: str
    score: float
    highlights: List[str] = field(default_factory=list)

class HybridSearchEngine:
    """Hybrid search engine — unified vector + full-text + OLAP"""

    def __init__(self, doris_connection, embedding_model):
        self.conn = doris_connection
        self.model = embedding_model

    async def search(self, request: SearchRequest) -> List[SearchResult]:
        if request.mode == SearchMode.KEYWORD:
            return await self._keyword_search(request)
        elif request.mode == SearchMode.SEMANTIC:
            return await self._semantic_search(request)
        else:
            return await self._hybrid_search(request)

    async def _hybrid_search(self, request: SearchRequest) -> List[SearchResult]:
        # 1. Generate query vector
        query_embedding = self.model.encode(
            request.query,
            normalize_embeddings=True
        )

        # 2. Build hybrid query SQL
        embedding_str = ", ".join(str(x) for x in query_embedding.tolist())

        filters = [f"tenant_id = '{request.tenant_id}'"]
        if request.object_types:
            types_str = ", ".join(f"'{t}'" for t in request.object_types)
            filters.append(f"object_type IN ({types_str})")
        if request.status_filter:
            filters.append(f"status = '{request.status_filter}'")
        if request.date_from:
            filters.append(f"created_at >= '{request.date_from}'")

        where_clause = " AND ".join(filters)

        sql = f"""
        WITH vector_scores AS (
            SELECT object_rid, display_name, description, object_type,
                   1.0 / (1.0 + L2_DISTANCE(embedding, ARRAY[{embedding_str}])) as v_score
            FROM ontology_embeddings
            WHERE {where_clause}
            ORDER BY L2_DISTANCE(embedding, ARRAY[{embedding_str}]) ASC
            LIMIT {request.top_k * 3}
        ),
        text_scores AS (
            SELECT object_rid, display_name, description, object_type,
                   1.0 as t_score
            FROM ontology_object_searchable
            WHERE {where_clause}
              AND (display_name MATCH_ANY '{request.query}'
                   OR description MATCH_ANY '{request.query}')
            LIMIT {request.top_k * 3}
        )
        SELECT
            COALESCE(v.object_rid, t.object_rid) as object_rid,
            COALESCE(v.display_name, t.display_name) as display_name,
            COALESCE(v.description, t.description) as description,
            COALESCE(v.object_type, t.object_type) as object_type,
            (COALESCE(v.v_score, 0) * {request.vector_weight} +
             COALESCE(t.t_score, 0) * {request.text_weight}) as score
        FROM vector_scores v
        FULL OUTER JOIN text_scores t ON v.object_rid = t.object_rid
        ORDER BY score DESC
        LIMIT {request.top_k}
        """

        rows = await self.conn.execute(sql)
        return [
            SearchResult(
                object_rid=row["object_rid"],
                display_name=row["display_name"],
                description=row["description"],
                object_type=row["object_type"],
                score=row["score"]
            )
            for row in rows
        ]

#5. Performance Comparison with Standalone Solutions

#5.1 Full-Text Search: Doris vs Elasticsearch

Comparison test on 50 million Ontology object records:

MetricDoris Inverted IndexElasticsearch 8.x
Index build time45 min60 min
Storage space28 GB42 GB
Simple keyword search P5035ms25ms
Simple keyword search P99180ms120ms
Phrase search P5065ms45ms
Phrase search P99280ms200ms
Aggregation + search P50120ms350ms (requires callback)
Aggregation + search P99450ms1200ms
Concurrent 100 QPS latency250ms180ms
Data consistency delay0 (same source)1-5s (async sync)

Conclusion: Elasticsearch has a slight advantage in pure search scenarios, but Doris significantly leads in search + analytics hybrid scenarios, with no data consistency issues.

#5.2 Vector Search: Doris vs Milvus

Comparison test on 10 million 768-dimension vectors:

MetricDoris HNSWMilvus 2.4 (HNSW)
Index build time25 min18 min
Memory usage12 GB8 GB
Top-10 query P5015ms8ms
Top-10 query P9945ms25ms
Top-100 query P5028ms15ms
Top-100 query P9975ms42ms
Recall@10098.2%98.5%
Filter + vector search P5035ms65ms (pre/post filter)
Filter + vector search P9995ms180ms
Vector + aggregationNative supportNot supported

Conclusion: Milvus is faster for pure vector search, but Doris performs better for filtered vector search and vector + analytics hybrid scenarios.

#6. Index Management and Operations

#6.1 Index Build Monitoring

SQL
-- Check index build progress
SHOW BUILD INDEX FROM ontology_db;

-- View table index information
SHOW INDEX FROM ontology_embeddings;

-- View segment-level index status
SHOW TABLET FROM ontology_embeddings;

#6.2 Index Rebuild

SQL
-- Drop and rebuild inverted index
DROP INDEX idx_description ON ontology_object_searchable;

CREATE INDEX idx_description ON ontology_object_searchable(description)
    USING INVERTED
    PROPERTIES("parser" = "unicode", "support_phrase" = "true");

-- Trigger index rebuild
BUILD INDEX idx_description ON ontology_object_searchable;

#6.3 Storage Space Optimization

SQL
-- Check index storage usage
SHOW DATA FROM ontology_embeddings;

-- Vector index compression (PQ quantization)
ALTER TABLE ontology_embeddings
MODIFY INDEX idx_embedding
PROPERTIES("index_type" = "HNSW", "pq_enable" = "true", "pq_subvector_num" = "48");

#7. Production Configuration Checklist

#7.1 Vector Search Optimization

PROPERTIES
# be.conf — vector search related
enable_vectorized_engine = true
inverted_index_ram_dir_enable = true  # Cache index in memory

# Vector index search threads
vector_index_search_thread_pool_size = 16

# HNSW search parameters (global defaults)
default_hnsw_ef_search = 200

#7.2 Inverted Index Optimization

PROPERTIES
# be.conf — inverted index related
inverted_index_cache_stale_sweep_time_sec = 600
inverted_index_max_buffered_docs = 65536
inverted_index_compaction_enable = true

# Tokenizer cache
inverted_index_searcher_cache_limit = 10%

#7.3 Memory Allocation Recommendations

For BE nodes simultaneously using OLAP analytics, inverted indexes, and vector indexes:

Code
Total 64GB allocation plan:
├── Query execution engine    30GB (47%)
├── Vector index cache        10GB (16%)
├── Inverted index cache       8GB (12%)
├── Page cache                 8GB (12%)
├── Compaction                 5GB (8%)
└── System reserved            3GB (5%)

#8. Common Issues and Solutions

#8.1 Vector Dimension Mismatch

Problem: Different models generate embeddings with different dimensions; mixing causes query failures.

Solution:

SQL
-- Use model_version field to isolate vectors from different models
SELECT * FROM ontology_embeddings
WHERE tenant_id = 'tenant-001'
  AND model_version = 'bge-base-zh-v1.5'
ORDER BY L2_DISTANCE(embedding, ARRAY[...]) ASC
LIMIT 10;

#8.2 Inverted Index Space Bloat

Problem: Inverted indexes on TEXT fields consume excessive storage.

Solution:

SQL
-- Limit indexed tokens per document for long text fields
ALTER TABLE ontology_object_searchable
MODIFY INDEX idx_description
PROPERTIES("max_token_per_doc" = "10000");

#8.3 Unstable Hybrid Query Performance

Problem: Large vector search result sets slow down subsequent OLAP computation.

Solution:

SQL
-- Strategy 1: Filter with scalar predicates first, then vector search
-- Strategy 2: Use partition key + vector index for partition pruning
-- Strategy 3: Two-phase query — coarse screening then fine ranking

#9. Evolution Roadmap

coomia-dip Doris vector + full-text capability evolution plan:

PhaseTimelineGoal
V1 (Current)2025 Q1Basic inverted index + HNSW vector index
V22025 Q2Hybrid search score fusion + BM25 scoring
V32025 Q3Multi-modal vectors (text + image)
V42025 Q4Adaptive index selection + AutoML scoring

#Key Takeaways

  1. Unified engine is the greatest value: Doris's inverted and vector indexes allow coomia-dip to avoid the additional complexity of maintaining Elasticsearch + Milvus. While individual performance is not the absolute best, the data consistency and operational simplification from a unified engine far outweigh the performance gap.

  2. Hybrid query is the core differentiator: The ability to combine OLAP filtering + full-text search + vector search in a single SQL statement is coomia-dip's core competitive advantage over traditional analytics platforms. Palantir Foundry requires multiple service coordination to achieve similar functionality.

  3. Tokenizer and index parameters need scenario-specific tuning: The unicode tokenizer suits mixed CJK+English content; HNSW with M=32 + efSearch=200 is the optimal balance between accuracy and performance. In production, ensure sufficient memory is reserved for index caching.

#Next Article Preview

S8-03: Apache Nessie Deep Dive: Git-Like Data Version Control — Exploring how Nessie enables data branching, merging, and time travel, and how coomia-dip's Lakehouse architecture manages Ontology data version evolution.

Tags: #apache-doris #vector-index #inverted-index #hnsw #full-text-search #hybrid-search #coomia-dip #Layer-c