Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
8b7a956
pyseekdb collection支持按namespace隔离
JingYuChe Apr 22, 2026
c4897dd
pyseekdb支持设置namespace_id相关的session缓存
JingYuChe Apr 29, 2026
da41d77
代码检视问题修改
JingYuChe Apr 30, 2026
5f0e1a9
delete
JingYuChe May 12, 2026
f7f6b1c
pyseekdb修改ob的search index with parser
JingYuChe May 13, 2026
ca1a069
新增 namespace 统计 catalog 表
LYAccc May 13, 2026
ef3d119
Merge pull request #1 from JingYuChe/feat/sdk-namespaces-stats
JingYuChe May 14, 2026
0622ed9
prewarm集成测试用例加强
JingYuChe May 14, 2026
5eb9b44
提供set_namespace_partition_count动态设置分区数
JingYuChe May 14, 2026
2d901a4
屏蔽hybrid_search用search_para解析成sql,直接向内核输入hybrid_search接口
JingYuChe May 15, 2026
750f581
混搜支持data_content过滤
JingYuChe May 15, 2026
0c62edc
kv table到key 改为varbinary
JingYuChe May 15, 2026
765dcee
namespace.add,get等操作显示设置session
JingYuChe May 18, 2026
941a0c9
namespace.add,get等操作显示设置session
JingYuChe May 18, 2026
4ee60df
pyseekdb新增dbms_logic_table.drop_namespace用例
JingYuChe May 18, 2026
69542a1
logic table id采用真实分配的值
JingYuChe May 27, 2026
33ab2c3
fts适配default_operator:and
JingYuChe May 28, 2026
17743d8
Fix conflict
skylhd May 28, 2026
4ae23cd
or修复
JingYuChe May 28, 2026
5d10d73
修复json_search双引号问题
JingYuChe Jun 1, 2026
4afb949
add namespace case
skylhd Jun 1, 2026
dafc36d
Merge branch 'develop' of https://github.com/skylhd/pyseekdb into adb…
skylhd Jun 1, 2026
13c0101
修复scalar term query in must/should clause not supported
JingYuChe Jun 1, 2026
f82317f
fix dsl
skylhd Jun 1, 2026
accc8ef
sdk改名
JingYuChe Jun 2, 2026
6ea284c
fix(hybrid_search): hoist $not_contains must_not out of scoring bool …
JingYuChe Jun 3, 2026
ee11730
open spfresh and add case
skylhd Jun 3, 2026
65bf56c
open spfresh and add case
skylhd Jun 3, 2026
79c0b9c
fix conflict
skylhd Jun 3, 2026
c18230d
fix case
skylhd Jun 3, 2026
17f89a1
fix(hybrid_search): hoist metadata $ne/$nin must_not for FTS and KNN …
JingYuChe Jun 3, 2026
a555bdc
drop namespace联调sdk适配修改
JingYuChe Jun 3, 2026
6d7ffb6
fix(hybrid_search): replace exists with OB-compatible positive must_n…
JingYuChe Jun 3, 2026
78c3d55
prewarm_lob sdk适配
JingYuChe Jun 4, 2026
db373ac
Merge branch 'develop' of https://github.com/JingYuChe/pyseekdb into …
skylhd Jun 4, 2026
227fdb3
add cosine case
skylhd Jun 4, 2026
d67cbfb
prewarm_lob sdk适配
JingYuChe Jun 4, 2026
c564ff9
fix case
skylhd Jun 6, 2026
c77f23b
修query里面有向量查询SDK要转成DSL
JingYuChe Jun 8, 2026
9df48f6
fix case
skylhd Jun 8, 2026
1ee5a99
del print
skylhd Jun 9, 2026
e2e0da1
Merge branch 'develop' of https://github.com/JingYuChe/pyseekdb into …
skylhd Jun 9, 2026
181fecf
Merge pull request #2 from skylhd/develop
JingYuChe Jun 9, 2026
551e351
ss模式和sn模式逻辑表向量索引随表创建
JingYuChe Jun 9, 2026
0a722a7
用户自定义创建向量,全文索引
JingYuChe Jun 10, 2026
94dcf5f
set_namespace_partition_count接口改名
JingYuChe Jun 11, 2026
52dcbad
ob内核版本拦截+只支持ivf_flat拦截
JingYuChe Jun 12, 2026
9e284ee
add prewarm for namespace test case
skylhd Jun 15, 2026
4a6cd4b
Merge branch 'develop' of https://github.com/JingYuChe/pyseekdb into …
skylhd Jun 15, 2026
da44ebe
fix run
skylhd Jun 15, 2026
591a413
Merge pull request #3 from skylhd/develop
JingYuChe Jun 16, 2026
ad3697a
fix:【pyseekdb】collection被删除之后,仍能执行ns = coll.create_namespace
JingYuChe Jun 16, 2026
81b36cf
fix:【pyseekdb】namespace删除后,执行ns.add,仍旧执行成功
JingYuChe Jun 16, 2026
28b7d92
fix:[pyseekdb][namespace] get_collection 打开带 embedding_function setti…
JingYuChe Jun 16, 2026
210c2d1
设置分区数改为collection粒度,改成create collection的配置项
JingYuChe Jun 16, 2026
553c861
fix:n_results值有误在sdk直接报错
JingYuChe Jun 16, 2026
1454126
update与collection场景保持一下
JingYuChe Jun 16, 2026
d3c3879
测试用例embedding用例修复
JingYuChe Jun 16, 2026
c1cdef5
fix:[pyseekdb]创建collection过程中,ob重启情况下,再次创建collection第一次创建会失败
JingYuChe Jun 17, 2026
fb708d2
fix:去掉drop_namespace的mock
JingYuChe Jun 17, 2026
c9b44b6
更新centroids_fresh_mode名适配内核
JingYuChe Jun 18, 2026
c77839e
fix_drop_table
JingYuChe Jun 18, 2026
c27ea4a
fix:除data_table意外默认使用index表
JingYuChe Jun 22, 2026
3e5676d
Merge remote-tracking branch 'origin/develop' into develop
JingYuChe Jun 22, 2026
626caca
pyseekdb对外ivf参数fresh_mode改为centroids_fresh_mode
JingYuChe Jun 22, 2026
dff351f
fix:【AgentDB/逻辑表】并发 get_or_create_namespace 同名 namespace 不幂等,唯一键冲突 1062
JingYuChe Jun 22, 2026
d16c80e
fix:【pyseekdb】并发执行ns.upsert,保存多行相同ids
JingYuChe Jun 22, 2026
0be3947
fix:多个client并发执行delete_collection与has_collection,出现不一致
JingYuChe Jun 22, 2026
8b50723
fix:issue_fix
JingYuChe Jun 23, 2026
444d4ba
fix:
JingYuChe Jun 23, 2026
f874066
fix:【pyseekdb】 创建namespcae报错1235,应该是obproxy不支持get_lock函数问题
JingYuChe Jun 23, 2026
c688423
Docstring 覆盖率修复
JingYuChe Jun 24, 2026
4d4d0dc
fix:【pyseekdb】对于没有物理表的collection,仍旧可以get到,并且能够创建namespace
JingYuChe Jun 24, 2026
9139b9e
fix:review
JingYuChe Jun 25, 2026
dde32f1
docstring_add
JingYuChe Jun 25, 2026
a9d0f59
fix:【pyseekdb】非法 include 字段没有报错
JingYuChe Jun 25, 2026
081d24f
fix:【pyseekdb】sdk没有对内核报错进行包装
JingYuChe Jun 25, 2026
908186a
fix:【pyseekdb】ns.query支持的where_document操作符不完全
JingYuChe Jun 26, 2026
a0c9599
fix:issue
JingYuChe Jun 26, 2026
513a33a
fix:【pyseekdb】ob内核不支持ivf索引超越2048维,但是sdk给的范围是[1,4096]
JingYuChe Jun 26, 2026
b39abbd
fix:【pyseekdb】 不传入向量索引配置的时候,向量列是sdk内部指定的384维,ns.add报错信息不明确
JingYuChe Jun 26, 2026
e06c500
issue_fix
JingYuChe Jun 26, 2026
251eb05
ut适配collection_dimension
JingYuChe Jun 29, 2026
7abf5c7
非namespace路径不去校验_validate_include
JingYuChe Jun 29, 2026
ec803f2
dont run namespace testcases on workflow
JingYuChe Jun 29, 2026
b59159a
import:_validate_namespace_name
JingYuChe Jun 29, 2026
843e8bd
fix_quality
JingYuChe Jun 29, 2026
6d2f357
skip_lakebase
JingYuChe Jun 29, 2026
15ab7fc
namespace模式只支持lakebase
JingYuChe Jun 29, 2026
dc95767
适配内核计算lob头
JingYuChe Jun 29, 2026
6860dbd
fix_workflow
JingYuChe Jun 30, 2026
b071f5d
fix_issue_hnwyllmm
JingYuChe Jun 30, 2026
7811b2b
without ignore
JingYuChe Jun 30, 2026
3546102
quality_fix
JingYuChe Jun 30, 2026
fb2c7cf
fix_test
JingYuChe Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ jobs:
log_level=INFO
fi
set -o pipefail
uv run pytest tests/integration_tests/ -v --log-cli-level=${log_level} -k "not server and not embedded and not oceanbase" | tee pytest.log
uv run pytest tests/integration_tests/ -v --log-cli-level=${log_level} \
Comment thread
JingYuChe marked this conversation as resolved.
-k "not server and not embedded and not oceanbase" | tee pytest.log
tail -n 1 pytest.log | grep '=======' | grep 'passed' |grep -q 'failed' && exit 1 || exit 0

integration-test:
Expand Down Expand Up @@ -158,5 +159,6 @@ jobs:
log_level=INFO
fi
set -o pipefail
uv run pytest tests/integration_tests/ -v --log-cli-level=${log_level} -k "${{ matrix.test_mode }}" | tee pytest.log
uv run pytest tests/integration_tests/ -v --log-cli-level=${log_level} \
-k "${{ matrix.test_mode }}" | tee pytest.log
tail -n 1 pytest.log | grep '=======' | grep 'passed' | grep -q 'failed' && exit 1 || exit 0
5 changes: 3 additions & 2 deletions docs/guide/dql.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ results = collection.query(
- `n_results` (int, required): Number of similar results to return (default: 10)
- `where` (dict, optional): Metadata filter conditions (see Filter Operators section)
- `where_document` (dict, optional): Document content filter
- `include` (List[str], optional): List of fields to include: `["documents", "metadatas", "embeddings"]`
- `include` (List[str], optional): List of fields to include: `["documents", "metadatas", "embeddings", "distances"]` (singular aliases accepted). Invalid field names raise `ValueError` before the query runs.

**Returns:**
Dict with keys (chromadb-compatible format):
Expand Down Expand Up @@ -288,7 +288,8 @@ results = collection.hybrid_search(
### Document Filters (`where_document` parameter)
- `$contains`: full-text match
- `$not_contains`: exclude matches
- `$or` / `$and` combining multiple `$contains` clauses
- `$regex`: regular expression match (namespace `query` applies via SDK post-filter when OB knn filter cannot enforce it)
- `$or` / `$and` combining multiple document clauses (including nested combinations)

## 5.5 Collection Information Methods

Expand Down
1 change: 1 addition & 0 deletions docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This guide covers installation, connections, data modeling, and common operation
client-connection
admin-client
collection-management
namespace
dml
dql
embedding-functions
Expand Down
99 changes: 99 additions & 0 deletions docs/guide/namespace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Namespace Collections

Namespace collections partition data inside a single physical collection. Each **namespace** is a logical slice (similar to a tenant or dataset) that shares catalog tables and IVF-backed vector indexes, while keeping documents isolated by `namespace_id`.

**Requirements**

- LakeBase **4.6.1.0** or newer (`OceanBase Database AI` in `SELECT version()`)
- `use_namespace=True` when creating the collection
- An explicit **IVF** `Schema` (the default non-namespace path builds **HNSW**, which namespace collections do not support)

## 1. Create a namespace-enabled collection

```python
import pyseekdb
from pyseekdb import IVFConfiguration, FulltextIndexConfig, VectorIndexConfig, Schema

client = pyseekdb.Client(host="127.0.0.1", port=2881, database="test")

schema = Schema(
vector_index=VectorIndexConfig(
ivf=IVFConfiguration(dimension=384, distance="cosine", centroids_fresh_mode="spfresh"),
embedding_function=None, # or pass your embedding function
),
fulltext_index=FulltextIndexConfig(analyzer="ik"),
)

collection = client.create_collection(
name="products",
schema=schema,
use_namespace=True,
partition_count=4, # optional; controls table partitioning
)
```

If you omit `schema` with `use_namespace=True`, pyseekdb raises a clear error instead of silently building the default HNSW schema.

**Not supported yet**

- Default / HNSW schemas (`configuration=HNSWConfiguration(...)` without an IVF `Schema`)
- `SparseVectorIndexConfig` on namespace collections

## 2. Manage namespaces

```python
ns = collection.create_namespace("electronics")
# or idempotently:
ns = collection.get_or_create_namespace("electronics")

collection.list_namespaces() # ["electronics", ...]
collection.delete_namespace("electronics")
```

## 3. Add and query data inside a namespace

Namespace DML uses **record ids** — per-document business keys you pass as `ids` in `namespace.add()`, `get()`, `update()`, and `delete()`. They are stored in `data_content.id` (JSON), not as the table primary key.

```python
ns.add(
ids=["doc_1", "doc_2"],
embeddings=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
documents=["phone", "laptop"],
metadatas=[{"brand": "A"}, {"brand": "B"}],
)

rows = ns.get(ids=["doc_1"], include=["documents", "metadatas"])
hits = ns.query(query_embeddings=[[0.1, 0.2, 0.3]], n_results=5)
```

**Record id rules (namespace path)**

- Must be non-empty strings
- Currently limited to `[A-Za-z0-9_]` (letters, digits, underscore)
- Maximum length 512 characters

Standard (non-namespace) collections accept broader `_id` values (for example UUIDs with hyphens). If you need UUID-style ids on the namespace path, normalize them (for example `doc_a1b2c3d4`) or wait for a future relaxation.

## 4. `get_or_create_collection` compatibility

`get_or_create_collection(..., use_namespace=...)` must match how the collection was originally created:

```python
# OK — reuses the existing namespace collection
client.get_or_create_collection("products", schema=schema, use_namespace=True)

# Raises ValueError — name already used by a standard HNSW collection
client.get_or_create_collection("legacy_coll", use_namespace=True)
```

Delete the old collection or pick a new name when switching between standard and namespace modes.

## 5. Collection-level hybrid search

Namespace collections also expose `collection.hybrid_search(...)` across namespaces. See integration examples under `tests/integration_tests/test_namespace_*.py` for FTS + vector patterns.

## See also

- [Collection management](collection-management.md) — standard HNSW collections
- [DML operations](dml.md) — `add` / `update` / `delete` on standard collections
- `examples/namespace_example.py` — minimal runnable sample
52 changes: 52 additions & 0 deletions examples/namespace_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Namespace collection example (LakeBase 4.6.1.0+).

Demonstrates:
1. Creating a namespace-enabled collection with an explicit IVF schema
2. Creating a namespace and adding documents with record ids
3. Vector query inside the namespace
"""

import pyseekdb
from pyseekdb import FulltextIndexConfig, IVFConfiguration, Schema, VectorIndexConfig

# Connect to LakeBase / OceanBase (adjust host/port/credentials)
client = pyseekdb.Client(
host="127.0.0.1",
port=2881,
database="test",
user="root@test",
password="",
)

schema = Schema(
vector_index=VectorIndexConfig(
ivf=IVFConfiguration(dimension=3, distance="l2", centroids_fresh_mode="spfresh"),
embedding_function=None,
),
fulltext_index=FulltextIndexConfig(analyzer="ik"),
)

collection_name = "demo_namespace_collection"
collection = client.create_collection(
name=collection_name,
schema=schema,
use_namespace=True,
partition_count=4,
)

ns = collection.get_or_create_namespace("demo")

# Record ids are per-document keys inside this namespace (see docs/guide/namespace.md).
ns.add(
ids=["item_alpha", "item_beta"],
embeddings=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
documents=["alpha product", "beta product"],
metadatas=[{"tier": "a"}, {"tier": "b"}],
)

results = ns.query(query_embeddings=[[1.0, 0.0, 0.0]], n_results=2, include=["documents", "metadatas"])
print("Top match:", results["ids"][0][0], results["documents"][0][0])

collection.delete_namespace("demo")
client.delete_collection(collection_name)
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ ignore = [
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101", "S608"]
"src/pyseekdb/client/client_base.py" = ["S608"]
"tests/**" = ["S101", "S608", "C901", "S105", "S110", "TRY300", "B017"]
"src/pyseekdb/client/client_base.py" = ["S608", "C901"]
"src/pyseekdb/client/document_query_builder.py" = ["C901"]

[tool.ruff.format]
preview = true
Expand Down
8 changes: 8 additions & 0 deletions src/pyseekdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@
HNSWConfiguration,
IKMode,
IKProperties,
IVFConfiguration,
IVFIndexLib,
IVFIndexType,
K,
Ngram2Properties,
NgramProperties,
Expand All @@ -103,6 +106,7 @@
register_sparse_embedding_function,
)
from .client.collection import Collection
from .client.namespace import Namespace

try:
__version__ = importlib.metadata.version("pyseekdb")
Expand All @@ -128,7 +132,11 @@
"HNSWConfiguration",
"IKMode",
"IKProperties",
"IVFConfiguration",
"IVFIndexLib",
"IVFIndexType",
"K",
"Namespace",
"Ngram2Properties",
"NgramProperties",
"RemoteServerClient",
Expand Down
7 changes: 7 additions & 0 deletions src/pyseekdb/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
HNSWConfiguration,
IKMode,
IKProperties,
IVFConfiguration,
IVFIndexLib,
IVFIndexType,
Ngram2Properties,
NgramProperties,
SpaceProperties,
Expand Down Expand Up @@ -68,6 +71,7 @@ def _resolve_password(password: str) -> str:

def _default_seekdb_path() -> str:
# Keep existing behavior: default to "seekdb.db" under current working directory.
"""Return the default on-disk path for the embedded SeekDB store."""
return os.path.abspath("seekdb.db")


Expand Down Expand Up @@ -167,6 +171,9 @@ def __getattr__(name: str) -> Any:
"HNSWConfiguration",
"IKMode",
"IKProperties",
"IVFConfiguration",
"IVFIndexLib",
"IVFIndexType",
"K",
"Ngram2Properties",
"NgramProperties",
Expand Down
6 changes: 6 additions & 0 deletions src/pyseekdb/client/admin_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def fork_database(self, source_name: str, destination_name: str, tenant: str = D
return self._server.fork_database(source_name=source_name, destination_name=destination_name, tenant=tenant)

def __repr__(self):
"""Return the developer-readable representation."""
return f"<AdminClient server={self._server}>"

def __enter__(self):
Expand Down Expand Up @@ -194,6 +195,7 @@ def create_collection(
schema: SchemaParam = None,
configuration: ConfigurationParam = _NOT_PROVIDED,
embedding_function: EmbeddingFunctionParam = _NOT_PROVIDED,
use_namespace: bool = False,
**kwargs,
) -> "Collection":
"""Proxy to server implementation - collection operations only"""
Expand All @@ -202,6 +204,7 @@ def create_collection(
schema=schema,
configuration=configuration,
embedding_function=embedding_function,
use_namespace=use_namespace,
**kwargs,
)

Expand All @@ -227,6 +230,7 @@ def get_or_create_collection(
schema: SchemaParam = None,
configuration: ConfigurationParam = _NOT_PROVIDED,
embedding_function: EmbeddingFunctionParam = _NOT_PROVIDED,
use_namespace: bool = False,
**kwargs,
) -> "Collection":
"""Proxy to server implementation - collection operations only"""
Expand All @@ -235,6 +239,7 @@ def get_or_create_collection(
schema=schema,
configuration=configuration,
embedding_function=embedding_function,
use_namespace=use_namespace,
**kwargs,
)

Expand All @@ -243,6 +248,7 @@ def count_collection(self) -> int:
return self._server.count_collection()

def __repr__(self):
"""Return the developer-readable representation."""
return f"<Client server={self._server}>"

def __enter__(self):
Expand Down
Loading
Loading