Building an Intelligent Book Search App from Scratch with seekdb

📖 Want your own intelligent book search app? Give it a try at https://github.com/oceanbase/seekdb! Semantic search, hybrid retrieval, RRF ranking… follow this article step by step, and you’ll find that AI application development is actually quite fun~

What Kind of Database Is seekdb?

I recently took seekdb for a spin—here are a few first impressions.

First, it’s a single-node, lightweight design. It runs easily on my MacBook deployed via Docker Desktop, and on Linux you can install it directly with pip. Word is that macOS/Windows support is coming soon, which will skip Docker entirely—you’ll just install it with a single command.

Second, it’s an integrated design that natively fuses five data types—relational, vector, full-text, JSON, and GIS. All indexes are updated atomically within the same transaction, which means Zero Data Lag and strict ACID, completely avoiding the latency and inconsistency problems caused by traditional CDC synchronization.

Third, it’s an AI-Native database. This shows up in its built-in embedding model and AI Functions: a single SQL statement performs a joint query over vector + full-text + scalar filtering, with no need to write reams of complex glue-layer logic to stitch together various tech stacks—it drives the RAG flow directly (see the figure).

Fourth, its API has a Schema-free design—you simply write data in, with no requirement to define a strict table schema in advance.

Fifth, it’s fully MySQL-compatible, which means traditional databases can easily be upgraded with AI capabilities.

The sixth point matters just as much: it’s open sourced under the Apache 2.0 license, and it carries OceanBase’s DNA. Its long-term development is assured—it will only grow more mature over time.

seekdb's integrated architecture drives the RAG flow

Tutorial: Building an Intelligent Book Search App with seekdb

This tutorial will take you from scratch to building an “intelligent book search” program with seekdb, demonstrating how to implement core seekdb capabilities such as semantic search and hybrid search.

Specifically, the tutorial covers:

  1. Data import
    • Import data from a CSV file into seekdb.
    • Support for batched data import.
    • Automatically convert each book’s text information into a 384-dimensional vector embedding.
  2. Three search capabilities used
    • Semantic search: based on vector similarity, find semantically related books using natural language queries.
    • Metadata filtering: precisely filter by fields such as rating, genre, year, and price.
    • Hybrid search: combine semantic search + metadata filtering, fusing the rankings with the RRF algorithm.
  3. Index optimization
    • Create an HNSW vector index to improve semantic search performance.
    • Generated-column indexes on metadata (extracting fields from JSON to create indexes).
  4. Tech stack
    • Database: seekdb, pyseekdb (seekdb’s Python SDK), pymysql.
    • Data processing tools: pandas.

Preparation

1. Install OrbStack

OrbStack is a lightweight Docker alternative, optimized for Mac, with fast startup and a low resource footprint. We’ll use it to deploy seekdb locally.

Step 1, install via Homebrew (recommended):

1
brew install orbstack

Or download from the official site: visit https://orbstack.dev to download the installer.

Step 2, launch OrbStack:

1
2
3
4
5
# launch OrbStack
open -a OrbStack

# verify the installation
orb version

2. Deploy the seekdb Image

If it gets stuck, first configure a domestic Docker mirror source in OrbStack.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# pull the SeekDB image
docker pull oceanbase/seekdb:latest

# start the SeekDB container
docker run -d \
--name seekdb \
-p 2881:2881 \
-e MODE=slim \
oceanbase/seekdb:latest

# check container status
docker ps | grep seekdb

# check logs (to confirm the service started successfully)
docker logs seekdb

Wait about 30 seconds for seekdb to fully start. You can view the startup logs with docker logs -f seekdb; seeing “boot success” indicates startup is complete.

3. Download the Dataset

Download the dataset: https://www.kaggle.com/datasets/sootersaalu/amazon-top-50-bestselling-books-2009-2019

Name the dataset bestsellers_with_categories.csv. It contains 550 records of historically bestselling Amazon books, with contents as shown in the figure:

Dataset content preview

4. Download the Tutorial Code

1
git clone https://github.com/kejun/demo-seekdb-hybridsearch.git

Project structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
demo-seekdb-books-hybrid-search/
├── database/
│ ├── db_client.py # database client wrapper
│ └── index_manager.py # index manager
├── data/
│ └── processor.py # data processor
├── models/
│ └── book_metadata.py # book metadata model
├── utils/
│ └── text_utils.py # text processing utilities
├── import_data.py # data import script
├── hybrid_search.py # hybrid search demo
└── bestsellers_with_categories.csv # data file

Create a Python virtual environment:

1
2
3
4
5
6
7
# create the virtual environment
python3 -m venv venv

# activate the virtual environment
source venv/bin/activate # macOS/Linux
# or
.\venv\Scripts\activate # Windows

Install dependencies:

1
pip install -r requirements.txt

Execution Results

Run python import_data.py to import the data. You can watch the whole process: load the data file → connect to the database → create the database → create the collection → import the data in batches → create the metadata indexes (note: seekdb currently only supports creating HNSW indexes on the embedding column and full-text indexes on the document column; creating indexes on metadata fields is not yet supported, though it’s said to be planned).

Data import flow

seekdb uses a schema-free interface design. For example, in data/processor.py, when calling collection.add() you pass in an arbitrary dictionary directly:

1
2
3
4
5
collection.add(
ids=valid_ids,
documents=valid_documents,
metadatas=valid_metadatas # pass a list of dicts directly, no predefined schema needed
)

The full results (somewhat trimmed) are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Loading data file: bestsellers_with_categories.csv
Data loaded successfully!
- Total rows: 550
- Total columns: 7
- Column names: Name, Author, User Rating, Reviews, Price, Year, Genre
- Load time: 0.01 s

Connecting to database...
Host: 127.0.0.1:2881
Database: demo_books
Collection: book_info
Database ready
Database connected successfully

Creating/rebuilding collection...
Collection name: book_info
Vector dimension: 384
Distance metric: cosine
Collection created successfully

Processing data...
Data preprocessing complete!
- Total records: 550
- Validation errors: 0
- Processing time: 0.05 s

Importing data into collection...
- Batch size: 100
- Total batches: 6
- Starting import...

Import progress: 100%|█████████████████████████████████████| 6/6 [00:53<00:00, 8.97s/batch]

Data import complete!
- Import time: 53.83 s
- Average speed: 10 records/s

Creating metadata indexes...
- Index fields: genre, year, user_rating, author, reviews, price
Index creation complete!
- Creation time: 3.81 s

Data import flow complete!
Total time: 59.64 s
Records imported: 550
Database: demo_books
Collection: book_info

Once the data is imported, you can query the database directly in the terminal using the mysql client or by installing obclient.

1
2
3
4
5
# enter the SeekDB container
docker exec -it seekdb bash

# connect using the MySQL client (SeekDB is MySQL-protocol compatible)
mysql -h127.0.0.1 -P2881 -uroot

book_info is a seekdb collection; the corresponding underlying table name is c$v1$book_info:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- list all databases
SHOW DATABASES;

-- switch to the demo database
USE demo;

-- list all tables (collections)
SHOW TABLES;

-- view the collection structure
DESC c$v1$articles;

-- query the collection data
SELECT * FROM c$v1$articles LIMIT 10;

-- count records
SELECT COUNT(*) FROM c$v1$articles;

-- exit
EXIT;

Let’s look at the table structure with DESC c$v1$book_info:

book_info collection table structure

Let’s look at the indexes that were created:

(Note: pyseekdb does not yet directly support creating indexes on metadata columns, so the project implements metadata indexing via pymysql + SQL DDL. It’s said that the next pyseekdb version will support automatically indexing metadata fields.)

List of created indexes

Next, run the search with python hybrid_search.py. seekdb’s built-in embedding model is sentence-transformers/all-MiniLM-L6-v2, with a maximum vector dimension of 384. For better results, you should still configure an external model service.

Hybrid search is seekdb’s killer feature. It performs full-text retrieval and vector retrieval at the same time, then merges them using the RRF (Reciprocal Rank Fusion) algorithm.

Illustration of how hybrid search works

Let’s look at a concrete code example. query_params defines a full-text search for “inspirational,” filtered by the user rating (user_rating) in the metadata (rating greater than or equal to 4.5). knn_params is the semantic search, with query_texts being “inspirational life advice,” filtered by the same user rating.

Code snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
query_params = {
"where_document": {"$contains": "inspirational"},
"where": {"user_rating": {"$gte": 4.5}},
"n_results": 5
}
knn_params = {
"query_texts": ["inspirational life advice"],
"where": {"user_rating": {"$gte": 4.5}},
"n_results": 5
}

results = collection.hybrid_search(
query=query_params,
knn=knn_params,
rank={"rrf": {}},
n_results=5,
include=["metadatas", "documents", "distances"]
)

You can vibe-eval the results—they feel pretty accurate. The full execution results (somewhat trimmed) are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
=== Semantic Search ===
Query: ['self improvement motivation success']

Semantic search - found 5 results:

[1] The 7 Habits of Highly Effective People: Powerful Lessons in Personal Change
Author: Stephen R. Covey
Rating: 4.6
Reviews: 9325
Price: $24.0
Year: 2011
Genre: Non Fiction
Similarity distance: 0.5358
Similarity: 0.4642

(others omitted......)


=== Hybrid Search (rating >= 4.5) ===
Query: {'where_document': {'$contains': 'inspirational'}, 'where': {'user_rating': {'$gte': 4.5}}, 'n_results': 5}
KNN Query Texts: ['inspirational life advice']

Hybrid search (rating >= 4.5) - found 5 results:

[1] Mindset: The New Psychology of Success
Author: Carol S. Dweck
Rating: 4.6
Reviews: 5542
Price: $10.0
Year: 2014
Genre: Non Fiction
Similarity distance: 0.0159
Similarity: 0.9841

(others omitted......)


=== Hybrid Search (Non Fiction) ===
Query: {'where_document': {'$contains': 'business'}, 'where': {'genre': 'Non Fiction'}, 'n_results': 5}
KNN Query Texts: ['business entrepreneurship leadership']

Hybrid search (Non Fiction) - found 5 results:

[1] The Five Dysfunctions of a Team: A Leadership Fable
Author: Patrick Lencioni
Rating: 4.6
Reviews: 3207
Price: $6.0
Year: 2009
Genre: Non Fiction
Similarity distance: 0.0164
Similarity: 0.9836

(others omitted......)


=== Hybrid Search (Fiction, after 2015, rating >= 4.0) ===
Query: {'where_document': {'$contains': 'fiction'}, 'where': {'$and': [{'year': {'$gte': 2015}}, {'user_rating': {'$gte': 4.0}}, {'genre': 'Fiction'}]}, 'n_results': 5}
KNN Query Texts: ['fiction story novel']

Hybrid search (Fiction, after 2015, rating >= 4.0) - found 5 results:

[1] A Gentleman in Moscow: A Novel
Author: Amor Towles
Rating: 4.7
Reviews: 19699
Price: $15.0
Year: 2017
Genre: Fiction
Similarity distance: 0.0154
Similarity: 0.9846

(others omitted......)

=== Hybrid Search (reviews >= 10000) ===
Query: {'where_document': {'$contains': 'popular'}, 'where': {'reviews': {'$gte': 10000}}, 'n_results': 10}
KNN Query Texts: ['popular bestseller']

Hybrid search (reviews >= 10000) - found 10 results:

[1] Twilight (The Twilight Saga, Book 1)
Author: Stephenie Meyer
Rating: 4.7
Reviews: 11676
Price: $9.0
Year: 2009
Genre: Fiction
Similarity distance: 0.0143
Similarity: 0.9857

[2] 1984 (Signet Classics)
Author: George Orwell
Rating: 4.7
Reviews: 21424
Price: $6.0
Year: 2017
Genre: Fiction
Similarity distance: 0.0145
Similarity: 0.9855

[3] Last Week Tonight with John Oliver Presents A Day in the Life of Marlon Bundo (Better Bundo Book, LGBT Childrens Book)
Author: Jill Twiss
Rating: 4.9
Reviews: 11881
Price: $13.0
Year: 2018
Genre: Fiction
Similarity distance: 0.0147
Similarity: 0.9853

(others omitted......)

Vibe Coding Friendly

If you develop with Cursor or Claude Code, you’ve surely installed context7-mcp. It queries the latest API docs, code examples, and more—the perfect companion for #Vibecoding. I noticed seekdb has also been added to Context7:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"mcpServers": {
"context7": {
"command": "npx",
"args": [
"-y",
"@upstash/context7-mcp",
"--api-key",
"<the apiKey you created on context7>"
]
}
}
}

Once installed, you can learn and use it at the same time.

I hope this tutorial helps you get started with #seekdb more smoothly. Enjoy!