Hologres V4.0 enhances vector search with the HGraph vector search algorithm, delivering high performance, high accuracy, and low latency. Use the Hologres Python Search SDK (holo-search-sdk) to run full-text search and vector search against your Hologres instance from Python.
Prerequisites
Before you begin, ensure that you have:
-
An Alibaba Cloud account with an AccessKey (ID and secret). See Create an AccessKey.
-
A RAM user with the required permissions. See Grant permissions to a RAM user.
-
Python 3.8 or later
Install the SDK
pip install holo-search-sdk
This topic uses holo-search-sdk version 0.3.0. If you encounter errors, check your installed version and upgrade if your version is earlier than 0.3.0:
pip show holo-search-sdk
pip install --upgrade holo-search-sdk
For more information, see the holo-search-sdk PyPI page.
Get started
Complete the following steps to connect to Hologres, create a table, load data, configure indexes, and run search queries:
Step 1: Connect to Hologres
import holo_search_sdk as holo
client = holo.connect(
host="<HOLO_HOST>", # Endpoint — get from Hologres console > Instance Details > Network Information
port=<HOLO_PORT>, # Port — same location as above
database="<HOLO_DBNAME>", # Name of your Hologres database
access_key_id="<ACCESS_KEY_ID>", # AccessKey ID
access_key_secret="<ACCESS_KEY_SECRET>", # AccessKey secret
schema="public" # Update if your tables are in a different schema
)
client.connect()
Replace the following placeholders:
| Placeholder | Description |
|---|---|
<HOLO_HOST> |
Endpoint of your Hologres instance. Go to the Hologres console and choose Instances > Instance ID/Name > Instance Details > Network Information. |
<HOLO_PORT> |
Port of your Hologres instance. Same location as the endpoint. |
<HOLO_DBNAME> |
Name of your Hologres database. |
<ACCESS_KEY_ID> |
Your AccessKey ID. Go to AccessKey Management. |
<ACCESS_KEY_SECRET> |
Your AccessKey secret. |
Step 2: Create a table
Create a table using a DDL statement. The following example creates a table with a text column and a 3-dimensional vector column:
create_table_sql = """
CREATE TABLE IF NOT EXISTS <TABLE_NAME> (
id BIGINT PRIMARY KEY,
content TEXT,
vector_column FLOAT4[] CHECK (array_ndims(vector_column) = 1 AND array_length(vector_column, 1) = 3),
publish_date TIMESTAMP
);
"""
client.execute(create_table_sql, fetch_result=False)
Replace <TABLE_NAME> with your actual table name.
Step 3: Open a table
Before reading from or writing to a table, open it:
table = client.open_table("<TABLE_NAME>")
Step 4: Insert data
Insert multiple rows at once with insert_multi:
data = [
[1, "Hello world", [0.1, 0.2, 0.3], "2023-01-01"],
[2, "Python SDK", [0.4, 0.5, 0.6], "2024-01-01"],
[3, "Vector search", [0.7, 0.8, 0.9], "2025-01-01"]
]
table.insert_multi(data, ["id", "content", "vector_column", "publish_date"])
To update existing rows, use upsert_one or upsert_multi. Setting update=True applies an update on conflict:
# Update a single record
table.upsert_one(
index_column="id",
values=[1, "Updated content", [0.3, 0.2, 0.1], "2026-01-01"],
column_names=["id", "content", "vector_column", "publish_date"],
update=True
)
# Update multiple records — limit which columns are overwritten with update_columns
table.upsert_multi(
index_column="id",
values=[
[1, "Updated content 1", [0.2, 0.3, 0.4], "2026-02-01"],
[2, "Updated content 2", [0.6, 0.5, 0.7], "2024-02-01"]
],
column_names=["id", "content", "vector_column", "publish_date"],
update=True,
update_columns=["content", "vector_column"]
)
Step 5: Set indexes
Set a vector index
Configure the vector index before running vector searches. The following example uses Cosine distance with RaBitQ quantization:
table.set_vector_index(
column="vector_column",
distance_method="Cosine",
base_quantization_type="rabitq",
use_reorder=True,
max_degree=64,
ef_construction=400
)
For guidance on choosing index parameters, see HGraph Index User Guide.
Set a full-text index
# Create a full-text index with the jieba tokenizer
table.create_text_index(
index_name="ft_idx_content",
column="content",
tokenizer="jieba"
)
# Switch to a different tokenizer
table.set_text_index(
index_name="ft_idx_content",
tokenizer="ik"
)
# Delete the index when no longer needed
table.drop_text_index(index_name="ft_idx_content")
Step 6: Query data
Vector search
All vector search examples use search_vector and chain modifiers before calling fetchall.
query_vector = [0.2, 0.3, 0.4]
# Return the top 10 nearest vectors
results = table.search_vector(
vector=query_vector,
column="vector_column",
distance_method="Cosine"
).limit(10).fetchall()
# Filter out low-similarity results — use min_distance when you want to exclude
# results below a relevance threshold rather than capping by count
results = table.search_vector(
vector=query_vector,
column="vector_column",
distance_method="Cosine"
).min_distance(0.5).fetchall()
# Rename the distance output column — use output_name when downstream code
# expects a specific column name (for example, when combining with text search scores)
results = table.search_vector(
vector=query_vector,
column="vector_column",
output_name="similarity_score",
distance_method="Cosine"
).fetchall()
Full-text search
All full-text search examples use search_text. The mode parameter controls how the expression is interpreted.
# Basic full-text search — returns all columns
results = table.search_text(
column="content",
expression="machine learning",
return_all_columns=True
).fetchall()
# Include a BM25 relevance score in the results
results = table.search_text(
column="content",
expression="deep learning",
return_score=True,
return_score_name="relevance_score"
).select(["id", "vector_column", "content"]).fetchall()
# Keyword mode (default) — all keywords must be present
results = table.search_text(
column="content",
expression="python programming",
mode="match",
operator="AND"
).fetchall()
# Phrase mode — exact phrase match
results = table.search_text(
column="content",
expression="machine learning",
mode="phrase"
).fetchall()
# Natural language mode — use + and - operators to include/exclude terms
results = table.search_text(
column="content",
expression="+python -java",
mode="natural_language"
).fetchall()
# Term mode — exact match with no tokenization
results = table.search_text(
column="content",
expression="python",
mode="term"
).fetchall()
Hybrid search
Combine a text or vector search with scalar filters by chaining .where(), .order_by(), and .limit().
# Full-text search filtered by date, sorted by relevance
results = (
table.search_text(
column="content",
expression="artificial intelligence",
return_score=True,
return_score_name="score"
)
.where("publish_date > '2023-01-01'")
.order_by("score", "desc")
.limit(10)
.fetchall()
)
# Vector search filtered by date, sorted by similarity
results = (
table.search_vector(
vector=query_vector,
column="vector_column",
output_name="similarity_score",
distance_method="Cosine"
)
.where("publish_date > '2023-01-01'")
.order_by("similarity_score", "desc")
.limit(10)
.fetchall()
)
Primary key point query
# Fetch a single record by primary key
result = table.get_by_key(
key_column="id",
key_value=1,
return_columns=["id", "content", "vector_column"] # Omit to return all columns
).fetchone()
# Fetch multiple records by primary key list
results = table.get_multi_by_keys(
key_column="id",
key_values=[1, 2, 3],
return_columns=["id", "content"] # Omit to return all columns
).fetchall()
Step 7: Close the connection
client.disconnect()
FAQ
I get `ImportError: no pq wrapper available` when importing `holo_search_sdk`.
This means the psycopg binary library is missing from your environment. Install it with:
pip install psycopg-binary