mirror of
https://github.com/lancedb/lancedb.git
synced 2025-12-23 05:19:58 +00:00
Compare commits
21 Commits
python-v0.
...
python-v0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a300a238db | ||
|
|
a41ff1df0a | ||
|
|
77b005d849 | ||
|
|
167fccc427 | ||
|
|
2bffbcefa5 | ||
|
|
905552f993 | ||
|
|
e4898c9313 | ||
|
|
cab36d94b2 | ||
|
|
b64252d4fd | ||
|
|
6fc006072c | ||
|
|
d4bb59b542 | ||
|
|
6b2dd6de51 | ||
|
|
dbccd9e4f1 | ||
|
|
b12ebfed4c | ||
|
|
1dadb2aefa | ||
|
|
eb9784d7f2 | ||
|
|
ba755626cc | ||
|
|
7760799cb8 | ||
|
|
4beb2d2877 | ||
|
|
a00b8595d1 | ||
|
|
9c8314b4fd |
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.20.1-beta.2"
|
current_version = "0.21.1-beta.1"
|
||||||
parse = """(?x)
|
parse = """(?x)
|
||||||
(?P<major>0|[1-9]\\d*)\\.
|
(?P<major>0|[1-9]\\d*)\\.
|
||||||
(?P<minor>0|[1-9]\\d*)\\.
|
(?P<minor>0|[1-9]\\d*)\\.
|
||||||
|
|||||||
398
Cargo.lock
generated
398
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
32
Cargo.toml
32
Cargo.toml
@@ -21,14 +21,14 @@ categories = ["database-implementations"]
|
|||||||
rust-version = "1.78.0"
|
rust-version = "1.78.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
lance = { "version" = "=0.30.0", "features" = ["dynamodb"] }
|
lance = { "version" = "=0.31.1", features = ["dynamodb"] }
|
||||||
lance-io = "=0.30.0"
|
lance-io = { "version" = "=0.31.1" }
|
||||||
lance-index = "=0.30.0"
|
lance-index = { "version" = "=0.31.1" }
|
||||||
lance-linalg = "=0.30.0"
|
lance-linalg = { "version" = "=0.31.1" }
|
||||||
lance-table = "=0.30.0"
|
lance-table = { "version" = "=0.31.1" }
|
||||||
lance-testing = "=0.30.0"
|
lance-testing = { "version" = "=0.31.1" }
|
||||||
lance-datafusion = "=0.30.0"
|
lance-datafusion = { "version" = "=0.31.1" }
|
||||||
lance-encoding = "=0.30.0"
|
lance-encoding = { "version" = "=0.31.1" }
|
||||||
# Note that this one does not include pyarrow
|
# Note that this one does not include pyarrow
|
||||||
arrow = { version = "55.1", optional = false }
|
arrow = { version = "55.1", optional = false }
|
||||||
arrow-array = "55.1"
|
arrow-array = "55.1"
|
||||||
@@ -39,20 +39,20 @@ arrow-schema = "55.1"
|
|||||||
arrow-arith = "55.1"
|
arrow-arith = "55.1"
|
||||||
arrow-cast = "55.1"
|
arrow-cast = "55.1"
|
||||||
async-trait = "0"
|
async-trait = "0"
|
||||||
datafusion = { version = "47.0", default-features = false }
|
datafusion = { version = "48.0", default-features = false }
|
||||||
datafusion-catalog = "47.0"
|
datafusion-catalog = "48.0"
|
||||||
datafusion-common = { version = "47.0", default-features = false }
|
datafusion-common = { version = "48.0", default-features = false }
|
||||||
datafusion-execution = "47.0"
|
datafusion-execution = "48.0"
|
||||||
datafusion-expr = "47.0"
|
datafusion-expr = "48.0"
|
||||||
datafusion-physical-plan = "47.0"
|
datafusion-physical-plan = "48.0"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
half = { "version" = "=2.5.0", default-features = false, features = [
|
half = { "version" = "2.6.0", default-features = false, features = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
] }
|
] }
|
||||||
futures = "0"
|
futures = "0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
moka = { version = "0.12", features = ["future"] }
|
moka = { version = "0.12", features = ["future"] }
|
||||||
object_store = "0.11.0"
|
object_store = "0.12.0"
|
||||||
pin-project = "1.0.7"
|
pin-project = "1.0.7"
|
||||||
snafu = "0.8"
|
snafu = "0.8"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|||||||
@@ -428,7 +428,7 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"**Why?** \n",
|
"**Why?** \n",
|
||||||
"Embedding the UFO dataset and ingesting it into LanceDB takes **~2 hours on a T4 GPU**. To save time: \n",
|
"Embedding the UFO dataset and ingesting it into LanceDB takes **~2 hours on a T4 GPU**. To save time: \n",
|
||||||
"- **Use the pre-prepared table with index created ** (provided below) to proceed directly to step7: search. \n",
|
"- **Use the pre-prepared table with index created** (provided below) to proceed directly to **Step 7**: search. \n",
|
||||||
"- **Step 5a** contains the full ingestion code for reference (run it only if necessary). \n",
|
"- **Step 5a** contains the full ingestion code for reference (run it only if necessary). \n",
|
||||||
"- **Step 6** contains the details on creating the index on the multivector column"
|
"- **Step 6** contains the details on creating the index on the multivector column"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.20.1-beta.2</version>
|
<version>0.21.1-beta.1</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.20.1-beta.2</version>
|
<version>0.21.1-beta.1</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>LanceDB Parent</name>
|
<name>LanceDB Parent</name>
|
||||||
|
|||||||
44
node/package-lock.json
generated
44
node/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "vectordb",
|
"name": "vectordb",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "vectordb",
|
"name": "vectordb",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64",
|
"x64",
|
||||||
"arm64"
|
"arm64"
|
||||||
@@ -52,11 +52,11 @@
|
|||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@lancedb/vectordb-darwin-arm64": "0.20.1-beta.2",
|
"@lancedb/vectordb-darwin-arm64": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-darwin-x64": "0.20.1-beta.2",
|
"@lancedb/vectordb-darwin-x64": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-linux-arm64-gnu": "0.20.1-beta.2",
|
"@lancedb/vectordb-linux-arm64-gnu": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-linux-x64-gnu": "0.20.1-beta.2",
|
"@lancedb/vectordb-linux-x64-gnu": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-win32-x64-msvc": "0.20.1-beta.2"
|
"@lancedb/vectordb-win32-x64-msvc": "0.21.1-beta.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@apache-arrow/ts": "^14.0.2",
|
"@apache-arrow/ts": "^14.0.2",
|
||||||
@@ -327,9 +327,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lancedb/vectordb-darwin-arm64": {
|
"node_modules/@lancedb/vectordb-darwin-arm64": {
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-arm64/-/vectordb-darwin-arm64-0.20.1-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-arm64/-/vectordb-darwin-arm64-0.21.1-beta.1.tgz",
|
||||||
"integrity": "sha512-mqi0yI+ZwBTydaDy1FRHAUZwrWS28u6tbHTe1s4uSrmERbVI6PfmoPR+NZWWAp6ZhlseSdl/+yeI4imk11rQSw==",
|
"integrity": "sha512-D9SOLFb/40E2/9bt82xOti3jogRAaR1UkT2LfGZJw/0wBu8d8/xKjWgfm3d26S5K6in6DWsX1njLxevrFqD5HA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -339,9 +339,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@lancedb/vectordb-darwin-x64": {
|
"node_modules/@lancedb/vectordb-darwin-x64": {
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-x64/-/vectordb-darwin-x64-0.20.1-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-x64/-/vectordb-darwin-x64-0.21.1-beta.1.tgz",
|
||||||
"integrity": "sha512-m8EYYA8JZIeNsJqQsBDUMu6r31/u7FzpjonJ4Y+CjapVl6UdvI65KUkeL2dYrFao++RuIoaiqcm3e7gRgFZpXQ==",
|
"integrity": "sha512-JnZ41aDOJs6LWfI9t/+MnpqsK/Fj9r/hDdZSOjcQquLOcm2eP3NnvEnDvn+1pqWBN6ceqf1avTatPBGnD/yhNA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -351,9 +351,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@lancedb/vectordb-linux-arm64-gnu": {
|
"node_modules/@lancedb/vectordb-linux-arm64-gnu": {
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-arm64-gnu/-/vectordb-linux-arm64-gnu-0.20.1-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-arm64-gnu/-/vectordb-linux-arm64-gnu-0.21.1-beta.1.tgz",
|
||||||
"integrity": "sha512-3Og2+bk4GlWmMO1Yg2HBfeb5zrOMLaIHD7bEqQ4+6yw4IckAaV+ke05H0tyyqmOVrOQ0LpvtXgD7pPztjm9r9A==",
|
"integrity": "sha512-Xnw0wYtnfzVUr4DzppJCSx+HZdAHr6sqMC8SdaYNQ9XEjBZE20n5SO2AdBYjejbmONJ7lpGs3ydnLIZ6N40dAQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -363,9 +363,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@lancedb/vectordb-linux-x64-gnu": {
|
"node_modules/@lancedb/vectordb-linux-x64-gnu": {
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.20.1-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.21.1-beta.1.tgz",
|
||||||
"integrity": "sha512-mwTQyA/FBoU/FkPuvCNBZG3y83gBN+iYoejehBH2HBkLUIcmlsDgSRZ1OQ+f9ijj12EMBCA11tBUPA9zhHzyrw==",
|
"integrity": "sha512-7S7gV13hv9Ho5W1Jat3FYiaMJOjRAwZOol7lKvOhU+sR/tJMEfZIOWAgymoqhAowbMtf+wwLoeKacfybXGET/w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -375,9 +375,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@lancedb/vectordb-win32-x64-msvc": {
|
"node_modules/@lancedb/vectordb-win32-x64-msvc": {
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-win32-x64-msvc/-/vectordb-win32-x64-msvc-0.20.1-beta.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-win32-x64-msvc/-/vectordb-win32-x64-msvc-0.21.1-beta.1.tgz",
|
||||||
"integrity": "sha512-VkjNpqhK3l3uHLLPmox+HrmKPMaZgV+qsGQWx0nfseGnSOEmXAWZWQFe0APVCQ9y0xTypQB0oH7eSOPZv2t4WQ==",
|
"integrity": "sha512-w6fEQA9IquvJ/GUYfiawRQvvdFD6OU44UW9JWm+FoscUFzdLiV7qmH4QjYEeEXQD7ob83ikFaxXGPTksYXpNOA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vectordb",
|
"name": "vectordb",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"description": " Serverless, low-latency vector database for AI applications",
|
"description": " Serverless, low-latency vector database for AI applications",
|
||||||
"private": false,
|
"private": false,
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
@@ -89,10 +89,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@lancedb/vectordb-darwin-x64": "0.20.1-beta.2",
|
"@lancedb/vectordb-darwin-x64": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-darwin-arm64": "0.20.1-beta.2",
|
"@lancedb/vectordb-darwin-arm64": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-linux-x64-gnu": "0.20.1-beta.2",
|
"@lancedb/vectordb-linux-x64-gnu": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-linux-arm64-gnu": "0.20.1-beta.2",
|
"@lancedb/vectordb-linux-arm64-gnu": "0.21.1-beta.1",
|
||||||
"@lancedb/vectordb-win32-x64-msvc": "0.20.1-beta.2"
|
"@lancedb/vectordb-win32-x64-msvc": "0.21.1-beta.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-nodejs"
|
name = "lancedb-nodejs"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
version = "0.20.1-beta.2"
|
version = "0.21.1-beta.1"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -368,9 +368,9 @@ describe("merge insert", () => {
|
|||||||
{ a: 4, b: "z" },
|
{ a: 4, b: "z" },
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(
|
const result = (await table.toArrow()).toArray().sort((a, b) => a.a - b.a);
|
||||||
JSON.parse(JSON.stringify((await table.toArrow()).toArray())),
|
|
||||||
).toEqual(expected);
|
expect(result.map((row) => ({ ...row }))).toEqual(expected);
|
||||||
});
|
});
|
||||||
test("conditional update", async () => {
|
test("conditional update", async () => {
|
||||||
const newData = [
|
const newData = [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-darwin-arm64",
|
"name": "@lancedb/lancedb-darwin-arm64",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["darwin"],
|
"os": ["darwin"],
|
||||||
"cpu": ["arm64"],
|
"cpu": ["arm64"],
|
||||||
"main": "lancedb.darwin-arm64.node",
|
"main": "lancedb.darwin-arm64.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-darwin-x64",
|
"name": "@lancedb/lancedb-darwin-x64",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["darwin"],
|
"os": ["darwin"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.darwin-x64.node",
|
"main": "lancedb.darwin-x64.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["arm64"],
|
"cpu": ["arm64"],
|
||||||
"main": "lancedb.linux-arm64-gnu.node",
|
"main": "lancedb.linux-arm64-gnu.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-arm64-musl",
|
"name": "@lancedb/lancedb-linux-arm64-musl",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["arm64"],
|
"cpu": ["arm64"],
|
||||||
"main": "lancedb.linux-arm64-musl.node",
|
"main": "lancedb.linux-arm64-musl.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-x64-gnu",
|
"name": "@lancedb/lancedb-linux-x64-gnu",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.linux-x64-gnu.node",
|
"main": "lancedb.linux-x64-gnu.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-x64-musl",
|
"name": "@lancedb/lancedb-linux-x64-musl",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.linux-x64-musl.node",
|
"main": "lancedb.linux-x64-musl.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-win32-arm64-msvc",
|
"name": "@lancedb/lancedb-win32-arm64-msvc",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-win32-x64-msvc",
|
"name": "@lancedb/lancedb-win32-x64-msvc",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"os": ["win32"],
|
"os": ["win32"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.win32-x64-msvc.node",
|
"main": "lancedb.win32-x64-msvc.node",
|
||||||
|
|||||||
4
nodejs/package-lock.json
generated
4
nodejs/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb",
|
"name": "@lancedb/lancedb",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@lancedb/lancedb",
|
"name": "@lancedb/lancedb",
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64",
|
"x64",
|
||||||
"arm64"
|
"arm64"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"ann"
|
"ann"
|
||||||
],
|
],
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.20.1-beta.2",
|
"version": "0.21.1-beta.1",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.js",
|
".": "./dist/index.js",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.24.0"
|
current_version = "0.24.1"
|
||||||
parse = """(?x)
|
parse = """(?x)
|
||||||
(?P<major>0|[1-9]\\d*)\\.
|
(?P<major>0|[1-9]\\d*)\\.
|
||||||
(?P<minor>0|[1-9]\\d*)\\.
|
(?P<minor>0|[1-9]\\d*)\\.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-python"
|
name = "lancedb-python"
|
||||||
version = "0.24.0"
|
version = "0.24.1"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Python bindings for LanceDB"
|
description = "Python bindings for LanceDB"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ embeddings = [
|
|||||||
"boto3>=1.28.57",
|
"boto3>=1.28.57",
|
||||||
"awscli>=1.29.57",
|
"awscli>=1.29.57",
|
||||||
"botocore>=1.31.57",
|
"botocore>=1.31.57",
|
||||||
"ollama",
|
"ollama>=0.3.0",
|
||||||
"ibm-watsonx-ai>=1.1.2",
|
"ibm-watsonx-ai>=1.1.2",
|
||||||
]
|
]
|
||||||
azure = ["adlfs>=2024.2.0"]
|
azure = ["adlfs>=2024.2.0"]
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import TYPE_CHECKING, List, Optional, Union
|
from typing import TYPE_CHECKING, List, Optional, Sequence, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from ..util import attempt_import_or_raise
|
from ..util import attempt_import_or_raise
|
||||||
from .base import TextEmbeddingFunction
|
from .base import TextEmbeddingFunction
|
||||||
from .registry import register
|
from .registry import register
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import numpy as np
|
|
||||||
import ollama
|
import ollama
|
||||||
|
|
||||||
|
|
||||||
@@ -28,23 +29,21 @@ class OllamaEmbeddings(TextEmbeddingFunction):
|
|||||||
keep_alive: Optional[Union[float, str]] = None
|
keep_alive: Optional[Union[float, str]] = None
|
||||||
ollama_client_kwargs: Optional[dict] = {}
|
ollama_client_kwargs: Optional[dict] = {}
|
||||||
|
|
||||||
def ndims(self):
|
def ndims(self) -> int:
|
||||||
return len(self.generate_embeddings(["foo"])[0])
|
return len(self.generate_embeddings(["foo"])[0])
|
||||||
|
|
||||||
def _compute_embedding(self, text) -> Union["np.array", None]:
|
def _compute_embedding(self, text: Sequence[str]) -> Sequence[Sequence[float]]:
|
||||||
return (
|
response = self._ollama_client.embed(
|
||||||
self._ollama_client.embeddings(
|
model=self.name,
|
||||||
model=self.name,
|
input=text,
|
||||||
prompt=text,
|
options=self.options,
|
||||||
options=self.options,
|
keep_alive=self.keep_alive,
|
||||||
keep_alive=self.keep_alive,
|
|
||||||
)["embedding"]
|
|
||||||
or None
|
|
||||||
)
|
)
|
||||||
|
return response.embeddings
|
||||||
|
|
||||||
def generate_embeddings(
|
def generate_embeddings(
|
||||||
self, texts: Union[List[str], "np.ndarray"]
|
self, texts: Union[List[str], np.ndarray]
|
||||||
) -> list[Union["np.array", None]]:
|
) -> list[Union[np.array, None]]:
|
||||||
"""
|
"""
|
||||||
Get the embeddings for the given texts
|
Get the embeddings for the given texts
|
||||||
|
|
||||||
@@ -54,8 +53,8 @@ class OllamaEmbeddings(TextEmbeddingFunction):
|
|||||||
The texts to embed
|
The texts to embed
|
||||||
"""
|
"""
|
||||||
# TODO retry, rate limit, token limit
|
# TODO retry, rate limit, token limit
|
||||||
embeddings = [self._compute_embedding(text) for text in texts]
|
embeddings = self._compute_embedding(texts)
|
||||||
return embeddings
|
return list(embeddings)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def _ollama_client(self) -> "ollama.Client":
|
def _ollama_client(self) -> "ollama.Client":
|
||||||
|
|||||||
@@ -3042,15 +3042,21 @@ class AsyncHybridQuery(AsyncQueryBase, AsyncVectorQueryBase):
|
|||||||
>>> asyncio.run(doctest_example()) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
|
>>> asyncio.run(doctest_example()) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
|
||||||
Vector Search Plan:
|
Vector Search Plan:
|
||||||
ProjectionExec: expr=[vector@0 as vector, text@3 as text, _distance@2 as _distance]
|
ProjectionExec: expr=[vector@0 as vector, text@3 as text, _distance@2 as _distance]
|
||||||
Take: columns="vector, _rowid, _distance, (text)"
|
Take: columns="vector, _rowid, _distance, (text)"
|
||||||
CoalesceBatchesExec: target_batch_size=1024
|
CoalesceBatchesExec: target_batch_size=1024
|
||||||
GlobalLimitExec: skip=0, fetch=10
|
GlobalLimitExec: skip=0, fetch=10
|
||||||
FilterExec: _distance@2 IS NOT NULL
|
FilterExec: _distance@2 IS NOT NULL
|
||||||
SortExec: TopK(fetch=10), expr=[_distance@2 ASC NULLS LAST], preserve_partitioning=[false]
|
SortExec: TopK(fetch=10), expr=[_distance@2 ASC NULLS LAST], preserve_partitioning=[false]
|
||||||
KNNVectorDistance: metric=l2
|
KNNVectorDistance: metric=l2
|
||||||
LanceScan: uri=..., projection=[vector], row_id=true, row_addr=false, ordered=false
|
LanceScan: uri=..., projection=[vector], row_id=true, row_addr=false, ordered=false
|
||||||
|
<BLANKLINE>
|
||||||
FTS Search Plan:
|
FTS Search Plan:
|
||||||
LanceScan: uri=..., projection=[vector, text], row_id=false, row_addr=false, ordered=true
|
ProjectionExec: expr=[vector@2 as vector, text@3 as text, _score@1 as _score]
|
||||||
|
Take: columns="_rowid, _score, (vector), (text)"
|
||||||
|
CoalesceBatchesExec: target_batch_size=1024
|
||||||
|
GlobalLimitExec: skip=0, fetch=10
|
||||||
|
MatchQuery: query=hello
|
||||||
|
<BLANKLINE>
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from lancedb._lancedb import (
|
|||||||
UpdateResult,
|
UpdateResult,
|
||||||
)
|
)
|
||||||
from lancedb.embeddings.base import EmbeddingFunctionConfig
|
from lancedb.embeddings.base import EmbeddingFunctionConfig
|
||||||
from lancedb.index import FTS, BTree, Bitmap, HnswPq, HnswSq, IvfFlat, IvfPq, LabelList
|
from lancedb.index import FTS, BTree, Bitmap, HnswSq, IvfFlat, IvfPq, LabelList
|
||||||
from lancedb.remote.db import LOOP
|
from lancedb.remote.db import LOOP
|
||||||
import pyarrow as pa
|
import pyarrow as pa
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ class RemoteTable(Table):
|
|||||||
|
|
||||||
def to_pandas(self):
|
def to_pandas(self):
|
||||||
"""to_pandas() is not yet supported on LanceDB cloud."""
|
"""to_pandas() is not yet supported on LanceDB cloud."""
|
||||||
return NotImplementedError("to_pandas() is not yet supported on LanceDB cloud.")
|
raise NotImplementedError("to_pandas() is not yet supported on LanceDB cloud.")
|
||||||
|
|
||||||
def checkout(self, version: Union[int, str]):
|
def checkout(self, version: Union[int, str]):
|
||||||
return LOOP.run(self._table.checkout(version))
|
return LOOP.run(self._table.checkout(version))
|
||||||
@@ -186,6 +186,8 @@ class RemoteTable(Table):
|
|||||||
accelerator: Optional[str] = None,
|
accelerator: Optional[str] = None,
|
||||||
index_type="vector",
|
index_type="vector",
|
||||||
wait_timeout: Optional[timedelta] = None,
|
wait_timeout: Optional[timedelta] = None,
|
||||||
|
*,
|
||||||
|
num_bits: int = 8,
|
||||||
):
|
):
|
||||||
"""Create an index on the table.
|
"""Create an index on the table.
|
||||||
Currently, the only parameters that matter are
|
Currently, the only parameters that matter are
|
||||||
@@ -220,11 +222,6 @@ class RemoteTable(Table):
|
|||||||
>>> table.create_index("l2", "vector") # doctest: +SKIP
|
>>> table.create_index("l2", "vector") # doctest: +SKIP
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if num_partitions is not None:
|
|
||||||
logging.warning(
|
|
||||||
"num_partitions is not supported on LanceDB cloud."
|
|
||||||
"This parameter will be tuned automatically."
|
|
||||||
)
|
|
||||||
if num_sub_vectors is not None:
|
if num_sub_vectors is not None:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"num_sub_vectors is not supported on LanceDB cloud."
|
"num_sub_vectors is not supported on LanceDB cloud."
|
||||||
@@ -244,13 +241,21 @@ class RemoteTable(Table):
|
|||||||
|
|
||||||
index_type = index_type.upper()
|
index_type = index_type.upper()
|
||||||
if index_type == "VECTOR" or index_type == "IVF_PQ":
|
if index_type == "VECTOR" or index_type == "IVF_PQ":
|
||||||
config = IvfPq(distance_type=metric)
|
config = IvfPq(
|
||||||
|
distance_type=metric,
|
||||||
|
num_partitions=num_partitions,
|
||||||
|
num_sub_vectors=num_sub_vectors,
|
||||||
|
num_bits=num_bits,
|
||||||
|
)
|
||||||
elif index_type == "IVF_HNSW_PQ":
|
elif index_type == "IVF_HNSW_PQ":
|
||||||
config = HnswPq(distance_type=metric)
|
raise ValueError(
|
||||||
|
"IVF_HNSW_PQ is not supported on LanceDB cloud."
|
||||||
|
"Please use IVF_HNSW_SQ instead."
|
||||||
|
)
|
||||||
elif index_type == "IVF_HNSW_SQ":
|
elif index_type == "IVF_HNSW_SQ":
|
||||||
config = HnswSq(distance_type=metric)
|
config = HnswSq(distance_type=metric, num_partitions=num_partitions)
|
||||||
elif index_type == "IVF_FLAT":
|
elif index_type == "IVF_FLAT":
|
||||||
config = IvfFlat(distance_type=metric)
|
config = IvfFlat(distance_type=metric, num_partitions=num_partitions)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unknown vector index type: {index_type}. Valid options are"
|
f"Unknown vector index type: {index_type}. Valid options are"
|
||||||
|
|||||||
@@ -775,6 +775,82 @@ async def test_explain_plan_async(table_async: AsyncTable):
|
|||||||
assert "KNN" in plan
|
assert "KNN" in plan
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_explain_plan_fts(table_async: AsyncTable):
|
||||||
|
"""Test explain plan for FTS queries"""
|
||||||
|
# Create FTS index
|
||||||
|
from lancedb.index import FTS
|
||||||
|
|
||||||
|
await table_async.create_index("text", config=FTS())
|
||||||
|
|
||||||
|
# Test pure FTS query
|
||||||
|
query = await table_async.search("dog", query_type="fts", fts_columns="text")
|
||||||
|
plan = await query.explain_plan()
|
||||||
|
# Should show FTS details (issue #2465 is now fixed)
|
||||||
|
assert "MatchQuery: query=dog" in plan
|
||||||
|
assert "GlobalLimitExec" in plan # Default limit
|
||||||
|
|
||||||
|
# Test FTS query with limit
|
||||||
|
query_with_limit = await table_async.search(
|
||||||
|
"dog", query_type="fts", fts_columns="text"
|
||||||
|
)
|
||||||
|
plan_with_limit = await query_with_limit.limit(1).explain_plan()
|
||||||
|
assert "MatchQuery: query=dog" in plan_with_limit
|
||||||
|
assert "GlobalLimitExec: skip=0, fetch=1" in plan_with_limit
|
||||||
|
|
||||||
|
# Test FTS query with offset and limit
|
||||||
|
query_with_offset = await table_async.search(
|
||||||
|
"dog", query_type="fts", fts_columns="text"
|
||||||
|
)
|
||||||
|
plan_with_offset = await query_with_offset.offset(1).limit(1).explain_plan()
|
||||||
|
assert "MatchQuery: query=dog" in plan_with_offset
|
||||||
|
assert "GlobalLimitExec: skip=1, fetch=1" in plan_with_offset
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_explain_plan_vector_with_limit_offset(table_async: AsyncTable):
|
||||||
|
"""Test explain plan for vector queries with limit and offset"""
|
||||||
|
# Test vector query with limit
|
||||||
|
plan_with_limit = await (
|
||||||
|
table_async.query().nearest_to(pa.array([1, 2])).limit(1).explain_plan()
|
||||||
|
)
|
||||||
|
assert "KNN" in plan_with_limit
|
||||||
|
assert "GlobalLimitExec: skip=0, fetch=1" in plan_with_limit
|
||||||
|
|
||||||
|
# Test vector query with offset and limit
|
||||||
|
plan_with_offset = await (
|
||||||
|
table_async.query()
|
||||||
|
.nearest_to(pa.array([1, 2]))
|
||||||
|
.offset(1)
|
||||||
|
.limit(1)
|
||||||
|
.explain_plan()
|
||||||
|
)
|
||||||
|
assert "KNN" in plan_with_offset
|
||||||
|
assert "GlobalLimitExec: skip=1, fetch=1" in plan_with_offset
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_explain_plan_with_filters(table_async: AsyncTable):
|
||||||
|
"""Test explain plan for queries with filters"""
|
||||||
|
# Test vector query with filter
|
||||||
|
plan_with_filter = await (
|
||||||
|
table_async.query().nearest_to(pa.array([1, 2])).where("id = 1").explain_plan()
|
||||||
|
)
|
||||||
|
assert "KNN" in plan_with_filter
|
||||||
|
assert "FilterExec" in plan_with_filter
|
||||||
|
|
||||||
|
# Test FTS query with filter
|
||||||
|
from lancedb.index import FTS
|
||||||
|
|
||||||
|
await table_async.create_index("text", config=FTS())
|
||||||
|
query_fts_filter = await table_async.search(
|
||||||
|
"dog", query_type="fts", fts_columns="text"
|
||||||
|
)
|
||||||
|
plan_fts_filter = await query_fts_filter.where("id = 1").explain_plan()
|
||||||
|
assert "MatchQuery: query=dog" in plan_fts_filter
|
||||||
|
assert "FilterExec: id@" in plan_fts_filter # Should show filter details
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_query_camelcase_async(tmp_path):
|
async def test_query_camelcase_async(tmp_path):
|
||||||
db = await lancedb.connect_async(tmp_path)
|
db = await lancedb.connect_async(tmp_path)
|
||||||
|
|||||||
@@ -210,6 +210,25 @@ async def test_retry_error():
|
|||||||
assert cause.status_code == 429
|
assert cause.status_code == 429
|
||||||
|
|
||||||
|
|
||||||
|
def test_table_unimplemented_functions():
|
||||||
|
def handler(request):
|
||||||
|
if request.path == "/v1/table/test/create/?mode=create":
|
||||||
|
request.send_response(200)
|
||||||
|
request.send_header("Content-Type", "application/json")
|
||||||
|
request.end_headers()
|
||||||
|
request.wfile.write(b"{}")
|
||||||
|
else:
|
||||||
|
request.send_response(404)
|
||||||
|
request.end_headers()
|
||||||
|
|
||||||
|
with mock_lancedb_connection(handler) as db:
|
||||||
|
table = db.create_table("test", [{"id": 1}])
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
table.to_arrow()
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
table.to_pandas()
|
||||||
|
|
||||||
|
|
||||||
def test_table_add_in_threadpool():
|
def test_table_add_in_threadpool():
|
||||||
def handler(request):
|
def handler(request):
|
||||||
if request.path == "/v1/table/test/insert/":
|
if request.path == "/v1/table/test/insert/":
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ impl FromPyObject<'_> for PyLanceDB<FtsQuery> {
|
|||||||
let operator = ob.getattr("operator")?.extract::<String>()?;
|
let operator = ob.getattr("operator")?.extract::<String>()?;
|
||||||
let prefix_length = ob.getattr("prefix_length")?.extract()?;
|
let prefix_length = ob.getattr("prefix_length")?.extract()?;
|
||||||
|
|
||||||
Ok(PyLanceDB(
|
Ok(Self(
|
||||||
MatchQuery::new(query)
|
MatchQuery::new(query)
|
||||||
.with_column(Some(column))
|
.with_column(Some(column))
|
||||||
.with_boost(boost)
|
.with_boost(boost)
|
||||||
@@ -70,7 +70,7 @@ impl FromPyObject<'_> for PyLanceDB<FtsQuery> {
|
|||||||
let column = ob.getattr("column")?.extract()?;
|
let column = ob.getattr("column")?.extract()?;
|
||||||
let slop = ob.getattr("slop")?.extract()?;
|
let slop = ob.getattr("slop")?.extract()?;
|
||||||
|
|
||||||
Ok(PyLanceDB(
|
Ok(Self(
|
||||||
PhraseQuery::new(query)
|
PhraseQuery::new(query)
|
||||||
.with_column(Some(column))
|
.with_column(Some(column))
|
||||||
.with_slop(slop)
|
.with_slop(slop)
|
||||||
@@ -78,10 +78,10 @@ impl FromPyObject<'_> for PyLanceDB<FtsQuery> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
"BoostQuery" => {
|
"BoostQuery" => {
|
||||||
let positive: PyLanceDB<FtsQuery> = ob.getattr("positive")?.extract()?;
|
let positive: Self = ob.getattr("positive")?.extract()?;
|
||||||
let negative: PyLanceDB<FtsQuery> = ob.getattr("negative")?.extract()?;
|
let negative: Self = ob.getattr("negative")?.extract()?;
|
||||||
let negative_boost = ob.getattr("negative_boost")?.extract()?;
|
let negative_boost = ob.getattr("negative_boost")?.extract()?;
|
||||||
Ok(PyLanceDB(
|
Ok(Self(
|
||||||
BoostQuery::new(positive.0, negative.0, negative_boost).into(),
|
BoostQuery::new(positive.0, negative.0, negative_boost).into(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -103,18 +103,17 @@ impl FromPyObject<'_> for PyLanceDB<FtsQuery> {
|
|||||||
let op = Operator::try_from(operator.as_str())
|
let op = Operator::try_from(operator.as_str())
|
||||||
.map_err(|e| PyValueError::new_err(format!("Invalid operator: {}", e)))?;
|
.map_err(|e| PyValueError::new_err(format!("Invalid operator: {}", e)))?;
|
||||||
|
|
||||||
Ok(PyLanceDB(q.with_operator(op).into()))
|
Ok(Self(q.with_operator(op).into()))
|
||||||
}
|
}
|
||||||
"BooleanQuery" => {
|
"BooleanQuery" => {
|
||||||
let queries: Vec<(String, PyLanceDB<FtsQuery>)> =
|
let queries: Vec<(String, Self)> = ob.getattr("queries")?.extract()?;
|
||||||
ob.getattr("queries")?.extract()?;
|
|
||||||
let mut sub_queries = Vec::with_capacity(queries.len());
|
let mut sub_queries = Vec::with_capacity(queries.len());
|
||||||
for (occur, q) in queries {
|
for (occur, q) in queries {
|
||||||
let occur = Occur::try_from(occur.as_str())
|
let occur = Occur::try_from(occur.as_str())
|
||||||
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
||||||
sub_queries.push((occur, q.0));
|
sub_queries.push((occur, q.0));
|
||||||
}
|
}
|
||||||
Ok(PyLanceDB(BooleanQuery::new(sub_queries).into()))
|
Ok(Self(BooleanQuery::new(sub_queries).into()))
|
||||||
}
|
}
|
||||||
name => Err(PyValueError::new_err(format!(
|
name => Err(PyValueError::new_err(format!(
|
||||||
"Unsupported FTS query type: {}",
|
"Unsupported FTS query type: {}",
|
||||||
@@ -155,8 +154,8 @@ impl<'py> IntoPyObject<'py> for PyLanceDB<FtsQuery> {
|
|||||||
.call((query.terms, query.column.unwrap()), Some(&kwargs))
|
.call((query.terms, query.column.unwrap()), Some(&kwargs))
|
||||||
}
|
}
|
||||||
FtsQuery::Boost(query) => {
|
FtsQuery::Boost(query) => {
|
||||||
let positive = PyLanceDB(query.positive.as_ref().clone()).into_pyobject(py)?;
|
let positive = Self(query.positive.as_ref().clone()).into_pyobject(py)?;
|
||||||
let negative = PyLanceDB(query.negative.as_ref().clone()).into_pyobject(py)?;
|
let negative = Self(query.negative.as_ref().clone()).into_pyobject(py)?;
|
||||||
let kwargs = PyDict::new(py);
|
let kwargs = PyDict::new(py);
|
||||||
kwargs.set_item("negative_boost", query.negative_boost)?;
|
kwargs.set_item("negative_boost", query.negative_boost)?;
|
||||||
namespace
|
namespace
|
||||||
@@ -182,13 +181,13 @@ impl<'py> IntoPyObject<'py> for PyLanceDB<FtsQuery> {
|
|||||||
query.should.len() + query.must.len() + query.must_not.len(),
|
query.should.len() + query.must.len() + query.must_not.len(),
|
||||||
);
|
);
|
||||||
for q in query.should {
|
for q in query.should {
|
||||||
queries.push((Occur::Should.into(), PyLanceDB(q).into_pyobject(py)?));
|
queries.push((Occur::Should.into(), Self(q).into_pyobject(py)?));
|
||||||
}
|
}
|
||||||
for q in query.must {
|
for q in query.must {
|
||||||
queries.push((Occur::Must.into(), PyLanceDB(q).into_pyobject(py)?));
|
queries.push((Occur::Must.into(), Self(q).into_pyobject(py)?));
|
||||||
}
|
}
|
||||||
for q in query.must_not {
|
for q in query.must_not {
|
||||||
queries.push((Occur::MustNot.into(), PyLanceDB(q).into_pyobject(py)?));
|
queries.push((Occur::MustNot.into(), Self(q).into_pyobject(py)?));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -563,7 +562,10 @@ impl FTSQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn explain_plan(self_: PyRef<'_, Self>, verbose: bool) -> PyResult<Bound<'_, PyAny>> {
|
pub fn explain_plan(self_: PyRef<'_, Self>, verbose: bool) -> PyResult<Bound<'_, PyAny>> {
|
||||||
let inner = self_.inner.clone();
|
let inner = self_
|
||||||
|
.inner
|
||||||
|
.clone()
|
||||||
|
.full_text_search(self_.fts_query.clone());
|
||||||
future_into_py(self_.py(), async move {
|
future_into_py(self_.py(), async move {
|
||||||
inner
|
inner
|
||||||
.explain_plan(verbose)
|
.explain_plan(verbose)
|
||||||
@@ -573,7 +575,10 @@ impl FTSQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyze_plan(self_: PyRef<'_, Self>) -> PyResult<Bound<'_, PyAny>> {
|
pub fn analyze_plan(self_: PyRef<'_, Self>) -> PyResult<Bound<'_, PyAny>> {
|
||||||
let inner = self_.inner.clone();
|
let inner = self_
|
||||||
|
.inner
|
||||||
|
.clone()
|
||||||
|
.full_text_search(self_.fts_query.clone());
|
||||||
future_into_py(self_.py(), async move {
|
future_into_py(self_.py(), async move {
|
||||||
inner
|
inner
|
||||||
.analyze_plan()
|
.analyze_plan()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-node"
|
name = "lancedb-node"
|
||||||
version = "0.20.1-beta.2"
|
version = "0.21.1-beta.1"
|
||||||
description = "Serverless, low-latency vector database for AI applications"
|
description = "Serverless, low-latency vector database for AI applications"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb"
|
name = "lancedb"
|
||||||
version = "0.20.1-beta.2"
|
version = "0.21.1-beta.1"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ impl ListingCatalog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn open_path(path: &str) -> Result<Self> {
|
async fn open_path(path: &str) -> Result<Self> {
|
||||||
let (object_store, base_path) = ObjectStore::from_uri(path).await.unwrap();
|
let (object_store, base_path) = ObjectStore::from_uri(path).await?;
|
||||||
if object_store.is_local() {
|
if object_store.is_local() {
|
||||||
Self::try_create_dir(path).context(CreateDirSnafu { path })?;
|
Self::try_create_dir(path).context(CreateDirSnafu { path })?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ impl ObjectStore for MirroringObjectStore {
|
|||||||
self.primary.delete(location).await
|
self.primary.delete(location).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(&self, prefix: Option<&Path>) -> BoxStream<'_, Result<ObjectMeta>> {
|
fn list(&self, prefix: Option<&Path>) -> BoxStream<'static, Result<ObjectMeta>> {
|
||||||
self.primary.list(prefix)
|
self.primary.list(prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ impl ObjectStore for IoTrackingStore {
|
|||||||
let result = self.target.get(location).await;
|
let result = self.target.get(location).await;
|
||||||
if let Ok(result) = &result {
|
if let Ok(result) = &result {
|
||||||
let num_bytes = result.range.end - result.range.start;
|
let num_bytes = result.range.end - result.range.start;
|
||||||
self.record_read(num_bytes as u64);
|
self.record_read(num_bytes);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -128,12 +128,12 @@ impl ObjectStore for IoTrackingStore {
|
|||||||
let result = self.target.get_opts(location, options).await;
|
let result = self.target.get_opts(location, options).await;
|
||||||
if let Ok(result) = &result {
|
if let Ok(result) = &result {
|
||||||
let num_bytes = result.range.end - result.range.start;
|
let num_bytes = result.range.end - result.range.start;
|
||||||
self.record_read(num_bytes as u64);
|
self.record_read(num_bytes);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_range(&self, location: &Path, range: std::ops::Range<usize>) -> OSResult<Bytes> {
|
async fn get_range(&self, location: &Path, range: std::ops::Range<u64>) -> OSResult<Bytes> {
|
||||||
let result = self.target.get_range(location, range).await;
|
let result = self.target.get_range(location, range).await;
|
||||||
if let Ok(result) = &result {
|
if let Ok(result) = &result {
|
||||||
self.record_read(result.len() as u64);
|
self.record_read(result.len() as u64);
|
||||||
@@ -144,7 +144,7 @@ impl ObjectStore for IoTrackingStore {
|
|||||||
async fn get_ranges(
|
async fn get_ranges(
|
||||||
&self,
|
&self,
|
||||||
location: &Path,
|
location: &Path,
|
||||||
ranges: &[std::ops::Range<usize>],
|
ranges: &[std::ops::Range<u64>],
|
||||||
) -> OSResult<Vec<Bytes>> {
|
) -> OSResult<Vec<Bytes>> {
|
||||||
let result = self.target.get_ranges(location, ranges).await;
|
let result = self.target.get_ranges(location, ranges).await;
|
||||||
if let Ok(result) = &result {
|
if let Ok(result) = &result {
|
||||||
@@ -170,7 +170,7 @@ impl ObjectStore for IoTrackingStore {
|
|||||||
self.target.delete_stream(locations)
|
self.target.delete_stream(locations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list(&self, prefix: Option<&Path>) -> BoxStream<'_, OSResult<ObjectMeta>> {
|
fn list(&self, prefix: Option<&Path>) -> BoxStream<'static, OSResult<ObjectMeta>> {
|
||||||
self.record_read(0);
|
self.record_read(0);
|
||||||
self.target.list(prefix)
|
self.target.list(prefix)
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ impl ObjectStore for IoTrackingStore {
|
|||||||
&self,
|
&self,
|
||||||
prefix: Option<&Path>,
|
prefix: Option<&Path>,
|
||||||
offset: &Path,
|
offset: &Path,
|
||||||
) -> BoxStream<'_, OSResult<ObjectMeta>> {
|
) -> BoxStream<'static, OSResult<ObjectMeta>> {
|
||||||
self.record_read(0);
|
self.record_read(0);
|
||||||
self.target.list_with_offset(prefix, offset)
|
self.target.list_with_offset(prefix, offset)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const REQUEST_TIMEOUT_HEADER: HeaderName = HeaderName::from_static("x-request-timeout-ms");
|
const REQUEST_TIMEOUT_HEADER: HeaderName = HeaderName::from_static("x-request-timeout-ms");
|
||||||
|
const METRIC_TYPE_KEY: &str = "metric_type";
|
||||||
|
const INDEX_TYPE_KEY: &str = "index_type";
|
||||||
|
|
||||||
pub struct RemoteTags<'a, S: HttpSend = Sender> {
|
pub struct RemoteTags<'a, S: HttpSend = Sender> {
|
||||||
inner: &'a RemoteTable<S>,
|
inner: &'a RemoteTable<S>,
|
||||||
@@ -997,23 +999,53 @@ impl<S: HttpSend> BaseTable for RemoteTable<S> {
|
|||||||
"column": column
|
"column": column
|
||||||
});
|
});
|
||||||
|
|
||||||
let (index_type, distance_type) = match index.index {
|
match index.index {
|
||||||
// TODO: Should we pass the actual index parameters? SaaS does not
|
// TODO: Should we pass the actual index parameters? SaaS does not
|
||||||
// yet support them.
|
// yet support them.
|
||||||
Index::IvfFlat(index) => ("IVF_FLAT", Some(index.distance_type)),
|
Index::IvfFlat(index) => {
|
||||||
Index::IvfPq(index) => ("IVF_PQ", Some(index.distance_type)),
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_FLAT".to_string());
|
||||||
Index::IvfHnswSq(index) => ("IVF_HNSW_SQ", Some(index.distance_type)),
|
body[METRIC_TYPE_KEY] =
|
||||||
Index::BTree(_) => ("BTREE", None),
|
serde_json::Value::String(index.distance_type.to_string().to_lowercase());
|
||||||
Index::Bitmap(_) => ("BITMAP", None),
|
if let Some(num_partitions) = index.num_partitions {
|
||||||
Index::LabelList(_) => ("LABEL_LIST", None),
|
body["num_partitions"] = serde_json::Value::Number(num_partitions.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Index::IvfPq(index) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_PQ".to_string());
|
||||||
|
body[METRIC_TYPE_KEY] =
|
||||||
|
serde_json::Value::String(index.distance_type.to_string().to_lowercase());
|
||||||
|
if let Some(num_partitions) = index.num_partitions {
|
||||||
|
body["num_partitions"] = serde_json::Value::Number(num_partitions.into());
|
||||||
|
}
|
||||||
|
if let Some(num_bits) = index.num_bits {
|
||||||
|
body["num_bits"] = serde_json::Value::Number(num_bits.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Index::IvfHnswSq(index) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_HNSW_SQ".to_string());
|
||||||
|
body[METRIC_TYPE_KEY] =
|
||||||
|
serde_json::Value::String(index.distance_type.to_string().to_lowercase());
|
||||||
|
if let Some(num_partitions) = index.num_partitions {
|
||||||
|
body["num_partitions"] = serde_json::Value::Number(num_partitions.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Index::BTree(_) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("BTREE".to_string());
|
||||||
|
}
|
||||||
|
Index::Bitmap(_) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("BITMAP".to_string());
|
||||||
|
}
|
||||||
|
Index::LabelList(_) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("LABEL_LIST".to_string());
|
||||||
|
}
|
||||||
Index::FTS(fts) => {
|
Index::FTS(fts) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("FTS".to_string());
|
||||||
let params = serde_json::to_value(&fts).map_err(|e| Error::InvalidInput {
|
let params = serde_json::to_value(&fts).map_err(|e| Error::InvalidInput {
|
||||||
message: format!("failed to serialize FTS index params {:?}", e),
|
message: format!("failed to serialize FTS index params {:?}", e),
|
||||||
})?;
|
})?;
|
||||||
for (key, value) in params.as_object().unwrap() {
|
for (key, value) in params.as_object().unwrap() {
|
||||||
body[key] = value.clone();
|
body[key] = value.clone();
|
||||||
}
|
}
|
||||||
("FTS", None)
|
|
||||||
}
|
}
|
||||||
Index::Auto => {
|
Index::Auto => {
|
||||||
let schema = self.schema().await?;
|
let schema = self.schema().await?;
|
||||||
@@ -1023,9 +1055,11 @@ impl<S: HttpSend> BaseTable for RemoteTable<S> {
|
|||||||
message: format!("Column {} not found in schema", column),
|
message: format!("Column {} not found in schema", column),
|
||||||
})?;
|
})?;
|
||||||
if supported_vector_data_type(field.data_type()) {
|
if supported_vector_data_type(field.data_type()) {
|
||||||
("IVF_PQ", Some(DistanceType::L2))
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_PQ".to_string());
|
||||||
|
body[METRIC_TYPE_KEY] =
|
||||||
|
serde_json::Value::String(DistanceType::L2.to_string().to_lowercase());
|
||||||
} else if supported_btree_data_type(field.data_type()) {
|
} else if supported_btree_data_type(field.data_type()) {
|
||||||
("BTREE", None)
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("BTREE".to_string());
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::NotSupported {
|
return Err(Error::NotSupported {
|
||||||
message: format!(
|
message: format!(
|
||||||
@@ -1042,12 +1076,6 @@ impl<S: HttpSend> BaseTable for RemoteTable<S> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
body["index_type"] = serde_json::Value::String(index_type.into());
|
|
||||||
if let Some(distance_type) = distance_type {
|
|
||||||
// Phalanx expects this to be lowercase right now.
|
|
||||||
body["metric_type"] =
|
|
||||||
serde_json::Value::String(distance_type.to_string().to_lowercase());
|
|
||||||
}
|
|
||||||
|
|
||||||
let request = request.json(&body);
|
let request = request.json(&body);
|
||||||
|
|
||||||
@@ -1429,11 +1457,12 @@ mod tests {
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use futures::{future::BoxFuture, StreamExt, TryFutureExt};
|
use futures::{future::BoxFuture, StreamExt, TryFutureExt};
|
||||||
use lance_index::scalar::inverted::query::MatchQuery;
|
use lance_index::scalar::inverted::query::MatchQuery;
|
||||||
use lance_index::scalar::FullTextSearchQuery;
|
use lance_index::scalar::{FullTextSearchQuery, InvertedIndexParams};
|
||||||
use reqwest::Body;
|
use reqwest::Body;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::index::vector::IvfFlatIndexBuilder;
|
use crate::index::vector::{IvfFlatIndexBuilder, IvfHnswSqIndexBuilder};
|
||||||
use crate::remote::db::DEFAULT_SERVER_VERSION;
|
use crate::remote::db::DEFAULT_SERVER_VERSION;
|
||||||
use crate::remote::JSON_CONTENT_TYPE;
|
use crate::remote::JSON_CONTENT_TYPE;
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -2433,29 +2462,79 @@ mod tests {
|
|||||||
let cases = [
|
let cases = [
|
||||||
(
|
(
|
||||||
"IVF_FLAT",
|
"IVF_FLAT",
|
||||||
Some("hamming"),
|
json!({
|
||||||
|
"metric_type": "hamming",
|
||||||
|
}),
|
||||||
Index::IvfFlat(IvfFlatIndexBuilder::default().distance_type(DistanceType::Hamming)),
|
Index::IvfFlat(IvfFlatIndexBuilder::default().distance_type(DistanceType::Hamming)),
|
||||||
),
|
),
|
||||||
("IVF_PQ", Some("l2"), Index::IvfPq(Default::default())),
|
(
|
||||||
|
"IVF_FLAT",
|
||||||
|
json!({
|
||||||
|
"metric_type": "hamming",
|
||||||
|
"num_partitions": 128,
|
||||||
|
}),
|
||||||
|
Index::IvfFlat(
|
||||||
|
IvfFlatIndexBuilder::default()
|
||||||
|
.distance_type(DistanceType::Hamming)
|
||||||
|
.num_partitions(128),
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"IVF_PQ",
|
"IVF_PQ",
|
||||||
Some("cosine"),
|
json!({
|
||||||
Index::IvfPq(IvfPqIndexBuilder::default().distance_type(DistanceType::Cosine)),
|
"metric_type": "l2",
|
||||||
|
}),
|
||||||
|
Index::IvfPq(Default::default()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"IVF_PQ",
|
||||||
|
json!({
|
||||||
|
"metric_type": "cosine",
|
||||||
|
"num_partitions": 128,
|
||||||
|
"num_bits": 4,
|
||||||
|
}),
|
||||||
|
Index::IvfPq(
|
||||||
|
IvfPqIndexBuilder::default()
|
||||||
|
.distance_type(DistanceType::Cosine)
|
||||||
|
.num_partitions(128)
|
||||||
|
.num_bits(4),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"IVF_HNSW_SQ",
|
"IVF_HNSW_SQ",
|
||||||
Some("l2"),
|
json!({
|
||||||
|
"metric_type": "l2",
|
||||||
|
}),
|
||||||
Index::IvfHnswSq(Default::default()),
|
Index::IvfHnswSq(Default::default()),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"IVF_HNSW_SQ",
|
||||||
|
json!({
|
||||||
|
"metric_type": "l2",
|
||||||
|
"num_partitions": 128,
|
||||||
|
}),
|
||||||
|
Index::IvfHnswSq(
|
||||||
|
IvfHnswSqIndexBuilder::default()
|
||||||
|
.distance_type(DistanceType::L2)
|
||||||
|
.num_partitions(128),
|
||||||
|
),
|
||||||
|
),
|
||||||
// HNSW_PQ isn't yet supported on SaaS
|
// HNSW_PQ isn't yet supported on SaaS
|
||||||
("BTREE", None, Index::BTree(Default::default())),
|
("BTREE", json!({}), Index::BTree(Default::default())),
|
||||||
("BITMAP", None, Index::Bitmap(Default::default())),
|
("BITMAP", json!({}), Index::Bitmap(Default::default())),
|
||||||
("LABEL_LIST", None, Index::LabelList(Default::default())),
|
(
|
||||||
("FTS", None, Index::FTS(Default::default())),
|
"LABEL_LIST",
|
||||||
|
json!({}),
|
||||||
|
Index::LabelList(Default::default()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"FTS",
|
||||||
|
serde_json::to_value(InvertedIndexParams::default()).unwrap(),
|
||||||
|
Index::FTS(Default::default()),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (index_type, distance_type, index) in cases {
|
for (index_type, expected_body, index) in cases {
|
||||||
let params = index.clone();
|
|
||||||
let table = Table::new_with_handler("my_table", move |request| {
|
let table = Table::new_with_handler("my_table", move |request| {
|
||||||
assert_eq!(request.method(), "POST");
|
assert_eq!(request.method(), "POST");
|
||||||
assert_eq!(request.url().path(), "/v1/table/my_table/create_index/");
|
assert_eq!(request.url().path(), "/v1/table/my_table/create_index/");
|
||||||
@@ -2465,19 +2544,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let body = request.body().unwrap().as_bytes().unwrap();
|
let body = request.body().unwrap().as_bytes().unwrap();
|
||||||
let body: serde_json::Value = serde_json::from_slice(body).unwrap();
|
let body: serde_json::Value = serde_json::from_slice(body).unwrap();
|
||||||
let mut expected_body = serde_json::json!({
|
let mut expected_body = expected_body.clone();
|
||||||
"column": "a",
|
expected_body["column"] = "a".into();
|
||||||
"index_type": index_type,
|
expected_body[INDEX_TYPE_KEY] = index_type.into();
|
||||||
});
|
|
||||||
if let Some(distance_type) = distance_type {
|
|
||||||
expected_body["metric_type"] = distance_type.to_lowercase().into();
|
|
||||||
}
|
|
||||||
if let Index::FTS(fts) = ¶ms {
|
|
||||||
let params = serde_json::to_value(fts).unwrap();
|
|
||||||
for (key, value) in params.as_object().unwrap() {
|
|
||||||
expected_body[key] = value.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(body, expected_body);
|
assert_eq!(body, expected_body);
|
||||||
|
|
||||||
|
|||||||
@@ -392,9 +392,18 @@ pub mod tests {
|
|||||||
} else {
|
} else {
|
||||||
expected_line.trim()
|
expected_line.trim()
|
||||||
};
|
};
|
||||||
assert_eq!(&actual_trimmed[..expected_trimmed.len()], expected_trimmed);
|
assert_eq!(
|
||||||
|
&actual_trimmed[..expected_trimmed.len()],
|
||||||
|
expected_trimmed,
|
||||||
|
"\nactual:\n{physical_plan}\nexpected:\n{expected}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
assert_eq!(lines_checked, expected.lines().count());
|
assert_eq!(
|
||||||
|
lines_checked,
|
||||||
|
expected.lines().count(),
|
||||||
|
"\nlines_checked:\n{lines_checked}\nexpected:\n{}",
|
||||||
|
expected.lines().count()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,9 +486,9 @@ pub mod tests {
|
|||||||
TestFixture::check_plan(
|
TestFixture::check_plan(
|
||||||
plan,
|
plan,
|
||||||
"MetadataEraserExec
|
"MetadataEraserExec
|
||||||
RepartitionExec:...
|
|
||||||
CoalesceBatchesExec:...
|
CoalesceBatchesExec:...
|
||||||
FilterExec: i@0 >= 5
|
FilterExec: i@0 >= 5
|
||||||
|
RepartitionExec:...
|
||||||
ProjectionExec:...
|
ProjectionExec:...
|
||||||
LanceScan:...",
|
LanceScan:...",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ impl DatasetRef {
|
|||||||
dataset: ref mut ds,
|
dataset: ref mut ds,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
*ds = dataset;
|
if dataset.manifest().version > ds.manifest().version {
|
||||||
|
*ds = dataset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!("Dataset should be in latest mode at this point"),
|
_ => unreachable!("Dataset should be in latest mode at this point"),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user