mirror of
https://github.com/lancedb/lancedb.git
synced 2026-09-11 07:42:26 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
477a49cecb | ||
|
|
bd86cc5aa6 | ||
|
|
2779b75d0d | ||
|
|
0d19a6c546 | ||
|
|
c0f33f8627 |
+1
-1
@@ -1,5 +1,5 @@
|
||||
[tool.bumpversion]
|
||||
current_version = "0.39.0-beta.0"
|
||||
current_version = "0.39.0-beta.1"
|
||||
parse = """(?x)
|
||||
(?P<major>0|[1-9]\\d*)\\.
|
||||
(?P<minor>0|[1-9]\\d*)\\.
|
||||
|
||||
Generated
+3
-3
@@ -5403,7 +5403,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lancedb"
|
||||
version = "0.39.0-beta.0"
|
||||
version = "0.39.0-beta.1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -5491,7 +5491,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lancedb-nodejs"
|
||||
version = "0.39.0-beta.0"
|
||||
version = "0.39.0-beta.1"
|
||||
dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
@@ -5516,7 +5516,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lancedb-python"
|
||||
version = "0.39.0-beta.0"
|
||||
version = "0.39.0-beta.1"
|
||||
dependencies = [
|
||||
"arrow",
|
||||
"async-trait",
|
||||
|
||||
@@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`:
|
||||
<dependency>
|
||||
<groupId>com.lancedb</groupId>
|
||||
<artifactId>lancedb-core</artifactId>
|
||||
<version>0.39.0-beta.0</version>
|
||||
<version>0.39.0-beta.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ If this is false, and another index already exists on the same columns
|
||||
and the same name, then an error will be returned. This is true even if
|
||||
that index is out of date.
|
||||
|
||||
The default is true
|
||||
The default is false
|
||||
|
||||
***
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>com.lancedb</groupId>
|
||||
<artifactId>lancedb-parent</artifactId>
|
||||
<version>0.39.0-beta.0</version>
|
||||
<version>0.39.0-beta.1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.lancedb</groupId>
|
||||
<artifactId>lancedb-parent</artifactId>
|
||||
<version>0.39.0-beta.0</version>
|
||||
<version>0.39.0-beta.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>LanceDB Java SDK Parent POM</description>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "lancedb-nodejs"
|
||||
edition.workspace = true
|
||||
version = "0.39.0-beta.0"
|
||||
version = "0.39.0-beta.1"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
description.workspace = true
|
||||
|
||||
@@ -1526,11 +1526,7 @@ describe("When creating an index", () => {
|
||||
|
||||
it("should allow me to replace (or not) an existing index", async () => {
|
||||
await tbl.createIndex("id");
|
||||
// Default is replace=true
|
||||
await tbl.createIndex("id");
|
||||
await expect(tbl.createIndex("id", { replace: false })).rejects.toThrow(
|
||||
"already exists",
|
||||
);
|
||||
await expect(tbl.createIndex("id")).rejects.toThrow("already exists");
|
||||
await tbl.createIndex("id", { replace: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -841,7 +841,7 @@ export interface IndexOptions {
|
||||
* and the same name, then an error will be returned. This is true even if
|
||||
* that index is out of date.
|
||||
*
|
||||
* The default is true
|
||||
* The default is false
|
||||
*/
|
||||
replace?: boolean;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-darwin-arm64",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"],
|
||||
"main": "lancedb.darwin-arm64.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"],
|
||||
"main": "lancedb.linux-arm64-gnu.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-arm64-musl",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"],
|
||||
"main": "lancedb.linux-arm64-musl.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-x64-gnu",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"],
|
||||
"main": "lancedb.linux-x64-gnu.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-x64-musl",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"],
|
||||
"main": "lancedb.linux-x64-musl.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-win32-arm64-msvc",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-win32-x64-msvc",
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"],
|
||||
"main": "lancedb.win32-x64-msvc.node",
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"ann"
|
||||
],
|
||||
"private": false,
|
||||
"version": "0.39.0-beta.0",
|
||||
"version": "0.39.0-beta.1",
|
||||
"main": "dist/index.js",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
@@ -44,7 +44,7 @@
|
||||
"@biomejs/biome": "^1.7.3",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@napi-rs/cli": "3.7.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.30.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.10.0",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/jest": "^29.1.2",
|
||||
"@types/node": "22.7.4",
|
||||
@@ -56,7 +56,7 @@
|
||||
"eslint": "^8.57.0",
|
||||
"jest": "^29.7.0",
|
||||
"shx": "^0.3.4",
|
||||
"tmp": "^0.2.3",
|
||||
"tmp": "^0.2.7",
|
||||
"ts-jest": "^29.1.2",
|
||||
"typedoc": "0.26.4",
|
||||
"typedoc-plugin-markdown": "4.2.1",
|
||||
|
||||
Generated
+593
-437
File diff suppressed because it is too large
Load Diff
@@ -16,3 +16,41 @@ allowBuilds:
|
||||
onnxruntime-node: true
|
||||
protobufjs: true
|
||||
sharp: true
|
||||
|
||||
minimumReleaseAgeExclude:
|
||||
- protobufjs@7.5.8
|
||||
- tmp@0.2.6
|
||||
- form-data@4.0.6
|
||||
- tar@7.5.16
|
||||
- markdown-it@14.1.2
|
||||
- linkify-it@5.0.1
|
||||
- js-yaml@3.15.0
|
||||
- js-yaml@4.1.2
|
||||
- protobufjs@7.6.1
|
||||
- protobufjs@7.6.3
|
||||
- '@babel/core@7.29.1'
|
||||
- axios@1.18.0
|
||||
- brace-expansion@2.1.2
|
||||
- brace-expansion@1.1.16
|
||||
- js-yaml@4.3.0
|
||||
- tar@7.5.18
|
||||
- tar@7.5.19
|
||||
- tar@7.5.17
|
||||
- protobufjs@7.6.5
|
||||
- linkify-it@5.0.2
|
||||
- sharp@0.35.0
|
||||
- brace-expansion@1.1.17
|
||||
- brace-expansion@2.1.3
|
||||
- brace-expansion@2.1.4
|
||||
- brace-expansion@1.1.18
|
||||
- js-yaml@3.15.1
|
||||
- js-yaml@4.3.1
|
||||
- tar@7.5.21
|
||||
- '@opentelemetry/core@2.8.0'
|
||||
|
||||
# @huggingface/transformers pins sharp ^0.33.5 and no released version has moved
|
||||
# past ^0.34.5, all of which inherit the libvips CVEs in GHSA-f88m-g3jw-g9cj.
|
||||
# Force the patched line. sharp is only reached by transformers' image pipeline,
|
||||
# which LanceDB's text embedding function never uses.
|
||||
overrides:
|
||||
sharp: ^0.35.4
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lancedb-python"
|
||||
version = "0.39.0-beta.0"
|
||||
version = "0.39.0-beta.1"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
description = "Python bindings for LanceDB"
|
||||
|
||||
@@ -402,6 +402,7 @@ class RemoteTable(Table):
|
||||
/,
|
||||
*,
|
||||
config: IndexConfigType,
|
||||
replace: bool = ...,
|
||||
wait_timeout: Optional[timedelta] = ...,
|
||||
name: Optional[str] = ...,
|
||||
train: bool = ...,
|
||||
@@ -416,7 +417,7 @@ class RemoteTable(Table):
|
||||
index_cache_size: Optional[int] = ...,
|
||||
num_partitions: Optional[int] = ...,
|
||||
num_sub_vectors: Optional[int] = ...,
|
||||
replace: Optional[bool] = ...,
|
||||
replace: bool = ...,
|
||||
accelerator: Optional[str] = ...,
|
||||
index_type: Literal[
|
||||
"VECTOR", "IVF_FLAT", "IVF_SQ", "IVF_PQ", "IVF_HNSW_SQ", "IVF_HNSW_PQ"
|
||||
@@ -435,7 +436,7 @@ class RemoteTable(Table):
|
||||
index_cache_size: Optional[int] = None,
|
||||
num_partitions: Optional[int] = None,
|
||||
num_sub_vectors: Optional[int] = None,
|
||||
replace: Optional[bool] = None,
|
||||
replace: bool = False,
|
||||
accelerator: Optional[str] = None,
|
||||
index_type="vector",
|
||||
wait_timeout: Optional[timedelta] = None,
|
||||
@@ -479,7 +480,6 @@ class RemoteTable(Table):
|
||||
vector_column_name,
|
||||
accelerator,
|
||||
index_cache_size,
|
||||
replace,
|
||||
)
|
||||
|
||||
if is_legacy:
|
||||
@@ -503,12 +503,6 @@ class RemoteTable(Table):
|
||||
"If you have 100M+ vectors to index,"
|
||||
"please contact us at contact@lancedb.com"
|
||||
)
|
||||
if replace is not None:
|
||||
logging.warning(
|
||||
"replace is not supported on LanceDB cloud."
|
||||
"Existing indexes will always be replaced."
|
||||
)
|
||||
|
||||
idx_type = index_type.upper()
|
||||
if idx_type == "VECTOR" or idx_type == "IVF_PQ":
|
||||
config = IvfPq(
|
||||
@@ -561,7 +555,7 @@ class RemoteTable(Table):
|
||||
column: str,
|
||||
*,
|
||||
config: IndexConfigType,
|
||||
replace: Optional[bool] = None,
|
||||
replace: bool = False,
|
||||
wait_timeout: Optional[timedelta] = None,
|
||||
name: Optional[str] = None,
|
||||
train: bool = True,
|
||||
@@ -593,7 +587,6 @@ class RemoteTable(Table):
|
||||
vector_column_name: str,
|
||||
accelerator: Optional[str],
|
||||
index_cache_size: Optional[int],
|
||||
replace: Optional[bool],
|
||||
) -> bool:
|
||||
"""Detect if this is a legacy create_index call."""
|
||||
if config is not None:
|
||||
@@ -605,7 +598,6 @@ class RemoteTable(Table):
|
||||
num_sub_vectors,
|
||||
accelerator,
|
||||
index_cache_size,
|
||||
replace,
|
||||
)
|
||||
):
|
||||
return True
|
||||
|
||||
@@ -1132,7 +1132,7 @@ class Table(ABC):
|
||||
num_partitions: Optional[int] = None,
|
||||
num_sub_vectors: Optional[int] = None,
|
||||
vector_column_name: str = VECTOR_COLUMN_NAME,
|
||||
replace: bool = True,
|
||||
replace: bool = False,
|
||||
accelerator: Optional[str] = None,
|
||||
index_cache_size: Optional[int] = None,
|
||||
*,
|
||||
@@ -1166,7 +1166,7 @@ class Table(ABC):
|
||||
The index configuration object. If provided, uses the new unified API.
|
||||
Can be one of: IvfFlat, IvfPq, IvfSq, IvfRq, HnswPq, HnswSq,
|
||||
BTree, Bitmap, LabelList, Fm, FTS.
|
||||
replace : bool, default True
|
||||
replace : bool, default False
|
||||
Whether to replace an existing index on this column.
|
||||
wait_timeout : timedelta, optional
|
||||
Timeout to wait for async indexing to complete.
|
||||
@@ -1198,7 +1198,7 @@ class Table(ABC):
|
||||
column: str,
|
||||
*,
|
||||
config: IndexConfigType,
|
||||
replace: Optional[bool] = None,
|
||||
replace: bool = False,
|
||||
wait_timeout: Optional[timedelta] = None,
|
||||
name: Optional[str] = None,
|
||||
train: bool = True,
|
||||
@@ -1260,7 +1260,7 @@ class Table(ABC):
|
||||
self,
|
||||
column: str,
|
||||
*,
|
||||
replace: bool = True,
|
||||
replace: bool = False,
|
||||
index_type: ScalarIndexType = "BTREE",
|
||||
wait_timeout: Optional[timedelta] = None,
|
||||
name: Optional[str] = None,
|
||||
@@ -1272,7 +1272,7 @@ class Table(ABC):
|
||||
column : str
|
||||
The column to be indexed. Must be a boolean, integer, float,
|
||||
or string column.
|
||||
replace : bool, default True
|
||||
replace : bool, default False
|
||||
Replace the existing index if it exists.
|
||||
index_type: Literal["BTREE", "BITMAP", "LABEL_LIST"], default "BTREE"
|
||||
The type of index to create.
|
||||
@@ -2988,7 +2988,7 @@ class LanceTable(Table):
|
||||
num_partitions: Optional[int] = None,
|
||||
num_sub_vectors: Optional[int] = None,
|
||||
vector_column_name: str = VECTOR_COLUMN_NAME,
|
||||
replace: bool = True,
|
||||
replace: bool = False,
|
||||
accelerator: Optional[str] = None,
|
||||
index_cache_size: Optional[int] = None,
|
||||
num_bits: int = 8,
|
||||
@@ -3030,7 +3030,7 @@ class LanceTable(Table):
|
||||
The index configuration object. If provided, uses the new unified API.
|
||||
Can be one of: IvfFlat, IvfPq, IvfSq, IvfRq, HnswPq, HnswSq,
|
||||
BTree, Bitmap, LabelList, Fm, FTS.
|
||||
replace : bool, default True
|
||||
replace : bool, default False
|
||||
Whether to replace an existing index on this column.
|
||||
wait_timeout : timedelta, optional
|
||||
Timeout to wait for async indexing to complete.
|
||||
@@ -3169,7 +3169,7 @@ class LanceTable(Table):
|
||||
column: str,
|
||||
*,
|
||||
config: IndexConfigType,
|
||||
replace: Optional[bool] = None,
|
||||
replace: bool = False,
|
||||
wait_timeout: Optional[timedelta] = None,
|
||||
name: Optional[str] = None,
|
||||
train: bool = True,
|
||||
@@ -3424,7 +3424,7 @@ class LanceTable(Table):
|
||||
self,
|
||||
column: str,
|
||||
*,
|
||||
replace: bool = True,
|
||||
replace: bool = False,
|
||||
index_type: ScalarIndexType = "BTREE",
|
||||
name: Optional[str] = None,
|
||||
):
|
||||
@@ -5313,7 +5313,7 @@ class AsyncTable:
|
||||
self,
|
||||
column: str,
|
||||
*,
|
||||
replace: Optional[bool] = None,
|
||||
replace: bool = False,
|
||||
config: Optional[
|
||||
Union[
|
||||
IvfFlat,
|
||||
@@ -5344,14 +5344,14 @@ class AsyncTable:
|
||||
----------
|
||||
column: str
|
||||
The column to index.
|
||||
replace: bool, default True
|
||||
replace: bool, default False
|
||||
Whether to replace the existing index
|
||||
|
||||
If this is false, and another index already exists on the same columns
|
||||
and the same name, then an error will be returned. This is true even if
|
||||
that index is out of date.
|
||||
|
||||
The default is True
|
||||
The default is False
|
||||
config: default None
|
||||
For advanced configuration you can specify the type of index you would
|
||||
like to create. You can also specify index-specific parameters when
|
||||
@@ -5409,7 +5409,7 @@ class AsyncTable:
|
||||
self,
|
||||
column: str,
|
||||
*,
|
||||
replace: Optional[bool] = None,
|
||||
replace: bool = False,
|
||||
config: Optional[
|
||||
Union[
|
||||
IvfFlat,
|
||||
|
||||
@@ -97,6 +97,9 @@ async def test_create_index_async_returns_done_job(some_table: AsyncTable):
|
||||
async def test_create_scalar_index(some_table: AsyncTable):
|
||||
# Can create
|
||||
await some_table.create_index("id")
|
||||
# Can't recreate by default
|
||||
with pytest.raises(RuntimeError, match="already exists"):
|
||||
await some_table.create_index("id")
|
||||
# Can recreate if replace=True
|
||||
await some_table.create_index("id", replace=True)
|
||||
indices = await some_table.list_indices()
|
||||
@@ -110,7 +113,7 @@ async def test_create_scalar_index(some_table: AsyncTable):
|
||||
with pytest.raises(RuntimeError, match="already exists"):
|
||||
await some_table.create_index("id", replace=False)
|
||||
# can also specify index type
|
||||
await some_table.create_index("id", config=BTree())
|
||||
await some_table.create_index("id", config=BTree(), replace=True)
|
||||
|
||||
await some_table.drop_index("id_idx")
|
||||
indices = await some_table.list_indices()
|
||||
@@ -351,13 +354,18 @@ async def test_full_text_search_index(some_table: AsyncTable):
|
||||
async def test_create_vector_index(some_table: AsyncTable):
|
||||
# Can create
|
||||
await some_table.create_index("vector")
|
||||
# Can't recreate by default
|
||||
with pytest.raises(RuntimeError, match="already exists"):
|
||||
await some_table.create_index("vector")
|
||||
# Can recreate if replace=True
|
||||
await some_table.create_index("vector", replace=True)
|
||||
# Can't recreate if replace=False
|
||||
with pytest.raises(RuntimeError, match="already exists"):
|
||||
await some_table.create_index("vector", replace=False)
|
||||
# Can also specify index type
|
||||
await some_table.create_index("vector", config=IvfPq(num_partitions=100))
|
||||
await some_table.create_index(
|
||||
"vector", config=IvfPq(num_partitions=100), replace=True
|
||||
)
|
||||
indices = await some_table.list_indices()
|
||||
assert len(indices) == 1
|
||||
assert indices[0].index_type == "IvfPq"
|
||||
|
||||
@@ -834,7 +834,7 @@ def test_table_create_indices():
|
||||
vector_req = received_requests[2]
|
||||
assert "name" in vector_req
|
||||
assert vector_req["name"] == "custom_vector_idx"
|
||||
assert "replace" not in vector_req
|
||||
assert vector_req["replace"] is False
|
||||
|
||||
table.wait_for_index(["custom_scalar_idx"], timedelta(seconds=2))
|
||||
table.wait_for_index(
|
||||
|
||||
@@ -1600,7 +1600,7 @@ def test_create_index_method(mock_create_index, mem_db: DBConnection):
|
||||
)
|
||||
mock_create_index.assert_called_with(
|
||||
"my_vector",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=expected_config,
|
||||
wait_timeout=None,
|
||||
name=None,
|
||||
@@ -1620,7 +1620,7 @@ def test_create_index_method(mock_create_index, mem_db: DBConnection):
|
||||
)
|
||||
mock_create_index.assert_called_with(
|
||||
"my_vector",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=expected_config,
|
||||
wait_timeout=None,
|
||||
name=None,
|
||||
@@ -1646,7 +1646,7 @@ def test_create_index_name_and_train_parameters(
|
||||
expected_config = IvfPq() # Default config
|
||||
mock_create_index.assert_called_with(
|
||||
"vector",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=expected_config,
|
||||
wait_timeout=None,
|
||||
name="my_custom_index",
|
||||
@@ -1657,7 +1657,7 @@ def test_create_index_name_and_train_parameters(
|
||||
table.create_index(vector_column_name="vector", train=False)
|
||||
mock_create_index.assert_called_with(
|
||||
"vector",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=expected_config,
|
||||
wait_timeout=None,
|
||||
name=None,
|
||||
@@ -1668,7 +1668,7 @@ def test_create_index_name_and_train_parameters(
|
||||
table.create_index(vector_column_name="vector", name="my_index_name", train=True)
|
||||
mock_create_index.assert_called_with(
|
||||
"vector",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=expected_config,
|
||||
wait_timeout=None,
|
||||
name="my_index_name",
|
||||
@@ -1705,7 +1705,7 @@ def test_create_index_new_api(mock_create_index, mem_db: DBConnection):
|
||||
table.create_index("vector", config=IvfPq(distance_type="l2"))
|
||||
mock_create_index.assert_called_with(
|
||||
"vector",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=IvfPq(distance_type="l2"),
|
||||
wait_timeout=None,
|
||||
name=None,
|
||||
@@ -1716,7 +1716,7 @@ def test_create_index_new_api(mock_create_index, mem_db: DBConnection):
|
||||
table.create_index("category", config=BTree())
|
||||
mock_create_index.assert_called_with(
|
||||
"category",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=BTree(),
|
||||
wait_timeout=None,
|
||||
name=None,
|
||||
@@ -1727,7 +1727,7 @@ def test_create_index_new_api(mock_create_index, mem_db: DBConnection):
|
||||
table.create_index("text", config=FTS(with_position=True))
|
||||
mock_create_index.assert_called_with(
|
||||
"text",
|
||||
replace=True,
|
||||
replace=False,
|
||||
config=FTS(with_position=True),
|
||||
wait_timeout=None,
|
||||
name=None,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lancedb"
|
||||
version = "0.39.0-beta.0"
|
||||
version = "0.39.0-beta.1"
|
||||
edition.workspace = true
|
||||
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
||||
license.workspace = true
|
||||
|
||||
@@ -200,14 +200,14 @@ impl IndexBuilder {
|
||||
parent,
|
||||
index,
|
||||
columns,
|
||||
replace: true,
|
||||
replace: false,
|
||||
train: true,
|
||||
wait_timeout: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to replace the existing index, the default is `true`.
|
||||
/// Whether to replace the existing index, the default is `false`.
|
||||
///
|
||||
/// If this is false, and another index already exists on the same columns
|
||||
/// and the same name, then an error will be returned. This is true even if
|
||||
|
||||
+35
-17
@@ -40,6 +40,7 @@ impl TerminalResult {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "remote")]
|
||||
pub(crate) fn remote(value: Option<Value>, request_id: String) -> Self {
|
||||
Self {
|
||||
value,
|
||||
@@ -47,30 +48,46 @@ impl TerminalResult {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "remote")]
|
||||
pub(crate) fn value(&self) -> Option<&Value> {
|
||||
self.value.as_ref()
|
||||
}
|
||||
|
||||
fn decode<T: DeserializeOwned>(self) -> Result<T> {
|
||||
let value = self.value.ok_or_else(|| match &self.request_id {
|
||||
Some(request_id) => Error::Http {
|
||||
source: "successful typed job response did not contain a result".into(),
|
||||
request_id: request_id.clone(),
|
||||
status_code: None,
|
||||
},
|
||||
None => Error::Runtime {
|
||||
let value = self.value.ok_or_else(|| {
|
||||
#[cfg(feature = "remote")]
|
||||
if let Some(request_id) = &self.request_id {
|
||||
return Error::Http {
|
||||
source: "successful typed job response did not contain a result".into(),
|
||||
request_id: request_id.clone(),
|
||||
status_code: None,
|
||||
};
|
||||
}
|
||||
Error::Runtime {
|
||||
message: "successful typed job did not contain a result".to_string(),
|
||||
},
|
||||
}
|
||||
})?;
|
||||
serde_json::from_value(value).map_err(|error| match self.request_id {
|
||||
Some(request_id) => Error::Http {
|
||||
source: format!("failed to parse typed job result: {error}").into(),
|
||||
request_id,
|
||||
status_code: None,
|
||||
},
|
||||
None => Error::Runtime {
|
||||
message: format!("failed to parse typed job result: {error}"),
|
||||
},
|
||||
serde_json::from_value(value).map_err(|error| {
|
||||
#[cfg(feature = "remote")]
|
||||
{
|
||||
match self.request_id {
|
||||
Some(request_id) => Error::Http {
|
||||
source: format!("failed to parse typed job result: {error}").into(),
|
||||
request_id,
|
||||
status_code: None,
|
||||
},
|
||||
None => Error::Runtime {
|
||||
message: format!("failed to parse typed job result: {error}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "remote"))]
|
||||
{
|
||||
let _ = self.request_id;
|
||||
Error::Runtime {
|
||||
message: format!("failed to parse typed job result: {error}"),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -117,6 +134,7 @@ impl Job<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "remote")]
|
||||
pub(crate) fn new(handle: Box<dyn JobHandle>) -> Self {
|
||||
Self {
|
||||
inner: JobInner::Handle {
|
||||
|
||||
@@ -524,13 +524,10 @@ impl<S: HttpSend> RemoteTable<S> {
|
||||
_ => resolve_arrow_field_path(&schema, &column)?,
|
||||
};
|
||||
let mut body = serde_json::json!({
|
||||
"column": canonical_column
|
||||
"column": canonical_column,
|
||||
"replace": index.replace,
|
||||
});
|
||||
|
||||
if !index.replace {
|
||||
body["replace"] = false.into();
|
||||
}
|
||||
|
||||
// Add name parameter if provided (for backwards compatibility, only include if Some)
|
||||
if let Some(ref name) = index.name {
|
||||
body["name"] = serde_json::Value::String(name.clone());
|
||||
@@ -6309,6 +6306,7 @@ mod tests {
|
||||
let mut expected_body = expected_body.clone();
|
||||
expected_body["column"] = "a".into();
|
||||
expected_body[INDEX_TYPE_KEY] = index_type.into();
|
||||
expected_body["replace"] = false.into();
|
||||
|
||||
assert_eq!(body, expected_body);
|
||||
|
||||
@@ -6327,7 +6325,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_index_forwards_replace_false_on_existing_route() {
|
||||
async fn test_create_index_forwards_default_replace_false_on_existing_route() {
|
||||
let table = Table::new_with_handler("my_table", move |request| {
|
||||
assert_eq!(request.method(), "POST");
|
||||
match request.url().path() {
|
||||
@@ -6354,7 +6352,40 @@ mod tests {
|
||||
|
||||
table
|
||||
.create_index(&["a"], Index::BTree(Default::default()))
|
||||
.replace(false)
|
||||
.execute()
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_index_forwards_explicit_replace_true_on_existing_route() {
|
||||
let table = Table::new_with_handler("my_table", move |request| {
|
||||
assert_eq!(request.method(), "POST");
|
||||
match request.url().path() {
|
||||
"/v1/table/my_table/describe/" => {
|
||||
let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
|
||||
http::Response::builder()
|
||||
.status(200)
|
||||
.body(describe_response(&schema))
|
||||
.unwrap()
|
||||
}
|
||||
"/v1/table/my_table/create_index/" => {
|
||||
let body = request.body().unwrap().as_bytes().unwrap();
|
||||
let body: serde_json::Value = serde_json::from_slice(body).unwrap();
|
||||
assert_eq!(body["replace"], json!(true));
|
||||
|
||||
http::Response::builder()
|
||||
.status(200)
|
||||
.body("{}".to_string())
|
||||
.unwrap()
|
||||
}
|
||||
path => panic!("Unexpected path: {}", path),
|
||||
}
|
||||
});
|
||||
|
||||
table
|
||||
.create_index(&["a"], Index::BTree(Default::default()))
|
||||
.replace(true)
|
||||
.execute()
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -6585,38 +6616,46 @@ mod tests {
|
||||
json!({
|
||||
"column": "rowId",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
json!({
|
||||
"column": "`row-id`",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
json!({
|
||||
"column": "userId",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
json!({
|
||||
"column": "MetaData.userId",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
json!({
|
||||
"column": "metadata.user_id",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
json!({
|
||||
"column": "image.embedding",
|
||||
"index_type": "IVF_PQ",
|
||||
"metric_type": "l2",
|
||||
"replace": false,
|
||||
}),
|
||||
{
|
||||
let mut body = serde_json::to_value(InvertedIndexParams::default()).unwrap();
|
||||
body["column"] = "payload.text".into();
|
||||
body["index_type"] = "FTS".into();
|
||||
body["replace"] = false.into();
|
||||
body
|
||||
},
|
||||
{
|
||||
let mut body = serde_json::to_value(InvertedIndexParams::default()).unwrap();
|
||||
body["column"] = "docs.content".into();
|
||||
body["index_type"] = "FTS".into();
|
||||
body["replace"] = false.into();
|
||||
body
|
||||
},
|
||||
{
|
||||
@@ -6624,15 +6663,18 @@ mod tests {
|
||||
body["column"] = "docs.content".into();
|
||||
body["index_type"] = "FTS".into();
|
||||
body["document_granularity"] = "list_element".into();
|
||||
body["replace"] = false.into();
|
||||
body
|
||||
},
|
||||
json!({
|
||||
"column": "`meta-data`.`user-id`",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
json!({
|
||||
"column": "literal.`a.b`",
|
||||
"index_type": "BTREE",
|
||||
"replace": false,
|
||||
}),
|
||||
]);
|
||||
let request_idx = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
@@ -133,7 +133,7 @@ impl NativeTable {
|
||||
),
|
||||
});
|
||||
}
|
||||
(resolved.canonical_path, resolved.field)
|
||||
(resolved.canonical_path, resolved.terminal_field)
|
||||
} else {
|
||||
Self::resolve_index_field(dataset.schema(), &opts.columns[0])?
|
||||
};
|
||||
@@ -439,7 +439,8 @@ mod tests {
|
||||
use arrow_array::record_batch;
|
||||
use arrow_array::{
|
||||
Array, ArrayRef, BinaryArray, BooleanArray, FixedSizeListArray, Float32Array, Int32Array,
|
||||
LargeBinaryArray, LargeStringArray, RecordBatch, StringArray, StructArray,
|
||||
LargeBinaryArray, LargeStringArray, ListArray, RecordBatch, StringArray, StructArray,
|
||||
UInt32Array,
|
||||
};
|
||||
use arrow_data::ArrayDataBuilder;
|
||||
use arrow_schema::{DataType, Field, Schema};
|
||||
@@ -458,6 +459,7 @@ mod tests {
|
||||
use crate::query::{ExecutableQuery, QueryBase};
|
||||
use crate::table::optimize::{CompactionOptions, OptimizeAction};
|
||||
use lance_index::scalar::FullTextSearchQuery;
|
||||
use lance_index::scalar::inverted::query::{FtsQuery, MatchQuery};
|
||||
|
||||
fn create_fixed_size_list<T: Array>(
|
||||
values: T,
|
||||
@@ -599,6 +601,80 @@ mod tests {
|
||||
assert!(invalid_granularity.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_nested_list_fts_uses_deepest_document_coordinates() {
|
||||
let conn = connect("memory://").execute().await.unwrap();
|
||||
let mut docs = ListBuilder::new(ListBuilder::new(StringBuilder::new()));
|
||||
|
||||
docs.values().values().append_value("alpha");
|
||||
docs.values().values().append_value("beta");
|
||||
docs.values().append(true);
|
||||
docs.values().values().append_value("gamma");
|
||||
docs.values().values().append_value("alpha delta");
|
||||
docs.values().append(true);
|
||||
docs.append(true);
|
||||
|
||||
docs.values().append(true);
|
||||
docs.values().values().append_value("alpha");
|
||||
docs.values().append(true);
|
||||
docs.append(true);
|
||||
|
||||
let batch = RecordBatch::try_from_iter(vec![
|
||||
("id", Arc::new(Int32Array::from(vec![0, 1])) as ArrayRef),
|
||||
("docs", Arc::new(docs.finish()) as ArrayRef),
|
||||
])
|
||||
.unwrap();
|
||||
let table = conn.create_table("nested", batch).execute().await.unwrap();
|
||||
|
||||
let job = table
|
||||
.create_index(
|
||||
&["docs"],
|
||||
Index::FTS(
|
||||
FtsIndexBuilder::default()
|
||||
.document_granularity(DocumentGranularity::ListElement),
|
||||
),
|
||||
)
|
||||
.execute_async()
|
||||
.await
|
||||
.unwrap();
|
||||
job.wait().await.unwrap();
|
||||
|
||||
let query = FullTextSearchQuery::new_query(FtsQuery::Match(
|
||||
MatchQuery::new("alpha".to_string())
|
||||
.with_column(Some("docs".to_string()))
|
||||
.with_document_granularity(DocumentGranularity::ListElement),
|
||||
));
|
||||
let batches = table
|
||||
.query()
|
||||
.full_text_search(query)
|
||||
.limit(10)
|
||||
.execute()
|
||||
.await
|
||||
.unwrap()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut hits = Vec::new();
|
||||
for batch in batches {
|
||||
let ids = batch["id"].as_any().downcast_ref::<Int32Array>().unwrap();
|
||||
let coordinates = batch["_doc_index"]
|
||||
.as_any()
|
||||
.downcast_ref::<ListArray>()
|
||||
.unwrap();
|
||||
for row in 0..batch.num_rows() {
|
||||
let coordinate = coordinates.value(row);
|
||||
let coordinate = coordinate.as_any().downcast_ref::<UInt32Array>().unwrap();
|
||||
hits.push((ids.value(row), coordinate.values().to_vec()));
|
||||
}
|
||||
}
|
||||
hits.sort_unstable();
|
||||
assert_eq!(
|
||||
hits,
|
||||
vec![(0, vec![0, 0]), (0, vec![1, 1]), (1, vec![1, 0])]
|
||||
);
|
||||
}
|
||||
|
||||
/// Concurrent waiters, and a wait issued after the job settled, all
|
||||
/// succeed once the build does.
|
||||
#[tokio::test]
|
||||
@@ -649,12 +725,11 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Rebuilding the same index without replace fails once the build
|
||||
// Rebuilding the same index without explicit replace fails once the build
|
||||
// starts, so the failure reaches the job rather than execute_async.
|
||||
let job = Arc::new(
|
||||
table
|
||||
.create_index(&["id"], Index::BTree(BTreeIndexBuilder::default()))
|
||||
.replace(false)
|
||||
.execute_async()
|
||||
.await
|
||||
.unwrap(),
|
||||
@@ -693,7 +768,6 @@ mod tests {
|
||||
|
||||
let job = table
|
||||
.create_index(&["id"], Index::BTree(BTreeIndexBuilder::default()))
|
||||
.replace(false)
|
||||
.execute_async()
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -1030,6 +1104,7 @@ mod tests {
|
||||
// Can also specify btree
|
||||
table
|
||||
.create_index(&["i"], Index::BTree(BTreeIndexBuilder::default()))
|
||||
.replace(true)
|
||||
.execute()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -227,7 +227,7 @@ pub(crate) fn resolve_arrow_field_path(schema: &Schema, column: &str) -> Result<
|
||||
|
||||
pub(crate) struct ResolvedFtsField {
|
||||
pub canonical_path: String,
|
||||
pub field: Field,
|
||||
pub terminal_field: Field,
|
||||
pub list_depth: usize,
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ pub(crate) fn resolve_lance_fts_field_path(
|
||||
);
|
||||
Ok(ResolvedFtsField {
|
||||
canonical_path,
|
||||
field: Field::from(field),
|
||||
terminal_field: Field::from(terminal),
|
||||
list_depth,
|
||||
})
|
||||
}
|
||||
@@ -375,7 +375,7 @@ pub(crate) fn resolve_arrow_fts_field_path(
|
||||
message: format!("Invalid schema: {}", e),
|
||||
})?;
|
||||
let resolved = resolve_lance_fts_field_path(&lance_schema, column)?;
|
||||
Ok((resolved.canonical_path, resolved.field))
|
||||
Ok((resolved.canonical_path, resolved.terminal_field))
|
||||
}
|
||||
|
||||
pub fn supported_btree_data_type(dtype: &DataType) -> bool {
|
||||
@@ -647,8 +647,9 @@ mod tests {
|
||||
Field::new("docs", text_list(), true),
|
||||
]);
|
||||
|
||||
let (path, _) = resolve_arrow_fts_field_path(&schema, "docs.content").unwrap();
|
||||
let (path, field) = resolve_arrow_fts_field_path(&schema, "docs.content").unwrap();
|
||||
assert_eq!(path, "docs.content");
|
||||
assert_eq!(field.data_type(), &DataType::Utf8);
|
||||
|
||||
let lance_schema = lance_core::datatypes::Schema::try_from(&schema).unwrap();
|
||||
let field_id = lance_schema
|
||||
|
||||
Reference in New Issue
Block a user