Unifying OLAP, Vector Search, and Full-Text Search with Apache Doris
Tags: #Doris #OLAP #VectorSearch #HNSW #InvertedIndex #FullTextSearch #coomia-dip
“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:
Traditional Multi-Engine Architecture:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ ClickHouse │ │ Qdrant │ │Elasticsearch│
│ (OLAP) │ │ (Vector) │ │ (Full-Text) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└────────┬───────┴────────────────┘
│
┌───────┴───────┐
│ Application │
│ Query Router │
└───────────────┘
This architecture introduces three core problems:
| Problem | Impact | Quantified Cost |
|---|---|---|
| Data Synchronization | Same data written to three systems; consistency is hard | Dev cycle +40% |
| Operational Burden | Three clusters to deploy, monitor, upgrade, scale | Ops effort x3 |
| Cross-Engine Queries | Results must be merged and re-sorted at the application layer | Query latency +200ms-2s |
| Resource Waste | Hot data replicated across three systems | Storage 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:
Unified Engine Architecture (coomia-dip):
┌──────────────────────┐
│ Apache Doris │
│ ┌────┬─────┬─────┐ │
│ │OLAP│HNSW │Inv. │ │
│ │Agg │Vec │Index│ │
│ └────┴─────┴─────┘ │
└──────────┬───────────┘
│
┌───────┴───────┐
│ Application │
│ (Single API) │
└───────────────┘
Key Capability Comparison:
| Capability | ClickHouse | Doris 2.1+ | Gap |
|---|---|---|---|
| Columnar OLAP | Excellent | Excellent | Parity |
| Materialized Views | Partial | Sync + Async | Doris stronger |
| Vector Search (HNSW) | Not supported | Native | Doris unique |
| Inverted Index | Not supported | Native | Doris unique |
| MySQL Protocol | Incompatible | Fully compatible | Doris friendlier |
| Join Performance | Average | Shuffle + Colocation Join | Doris better |
#1.3 Decision Matrix
We used a weighted scoring method for the final decision:
| Evaluation Dimension | Weight | Multi-Engine | Doris Unified |
|---|---|---|---|
| Query Performance | 30% | 9 (each optimized) | 8 (slight trade-off) |
| Ops Cost | 25% | 3 (three systems) | 9 (single system) |
| Data Consistency | 20% | 4 (cross-system sync) | 10 (single source) |
| Dev Efficiency | 15% | 4 (multiple SDKs) | 9 (unified SQL) |
| Resource Utilization | 10% | 4 (duplicate storage) | 9 (shared storage) |
| Weighted Total | -- | 5.35 | 8.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:
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:
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
-- 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:
- Partition pruning: Only scan relevant partitions based on the
created_atcondition - Predicate pushdown: The
world_idfilter is applied at the storage layer - Vectorized computation: COUNT, AVG aggregations use SIMD instructions
- Parallel execution: Each BE node processes its own buckets concurrently
#3. HNSW Vector Index: Semantic Search
#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:
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
-- 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:
| Parameter | Default | Recommended | Description |
|---|---|---|---|
M | 16 | 32 | Max connections per node. Higher = better recall, larger index |
ef_construction | 100 | 200 | Search width during build. Higher = better quality, slower build |
ef_search | 100 | 150 | Search width during query. Higher = better recall, slower query |
metric_type | L2 | COSINE | Distance metric. COSINE recommended for semantic search |
dim | - | 768 | Vector dimension. Must match embedding model output |
#3.3 Vector Search Queries
-- 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:
-- 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:
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. Inverted Index: Full-Text Search
#4.1 Doris Inverted Index Architecture
Doris 2.0 introduced CLucene-based inverted indexes supporting full-text search, phrase matching, and tokenization:
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
-- 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
-- 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
-- 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:
-- 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:
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 Type | Multi-Engine | Doris Unified | Improvement |
|---|---|---|---|
| Pure OLAP aggregation | 180ms (CH) | 210ms | -14% |
| Pure vector Top-10 | 12ms (Qdrant) | 25ms | -52% |
| Pure full-text search | 35ms (ES) | 45ms | -22% |
| OLAP + vector combined | 380ms (cross-sys) | 85ms | +77% |
| OLAP + text combined | 290ms (cross-sys) | 65ms | +78% |
| Three-in-one combined | 650ms (cross-sys) | 120ms | +82% |
| Write latency | 3x writes 150ms | 1x 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
# 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
# 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
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
-- 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
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
# 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
// 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
| Configuration | Value |
|---|---|
| Cluster Size | 3 FE + 5 BE |
| BE Specs | 32C / 128GB / 2TB NVMe SSD |
| Data Volume | 50 million entity_common records |
| Vector Dimension | 768 (BGE-large) |
| Indexes | HNSW(M=32, ef=200) + Inverted Index |
#8.2 Test Results
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
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:
-- 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:
-- 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:
-- 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
coomia-dip Storage Layer Overview:
┌─────────────────────────────────────────────────┐
│ Query Layer │
│ ┌──────────────────────────────────────────┐ │
│ │ QueryFederationService (C) │ │
│ └──────┬──────────────────┬────────────────┘ │
│ │ │ │
│ ┌────┴─────┐ ┌────┴─────┐ │
│ │ Doris │ │ DuckDB │ │
│ │ (Primary)│ │ (Aux) │ │
│ └────┬─────┘ └──────────┘ │
│ │ │
│ ┌────┴──────────────────────────────┐ │
│ │ Storage Layer │ │
│ │ ┌─────────┐ ┌────────────────┐ │ │
│ │ │ MinIO │ │ Nessie+Iceberg │ │ │
│ │ │(Object) │ │ (Versioned │ │ │
│ │ │ │ │ Lakehouse) │ │ │
│ │ └─────────┘ └────────────────┘ │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
#Key Takeaways
-
Unified engine beats specialized engine combinations in hybrid query scenarios. Doris delivers 70-82% better performance on combined queries while drastically reducing operational complexity.
-
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.
-
Inverted indexes support full-text search natively. The built-in unicode tokenizer plus custom dictionaries handle multilingual full-text retrieval effectively.
-
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.
-
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