mirror of
https://github.com/lancedb/lancedb.git
synced 2025-12-23 13:29:57 +00:00
Compare commits
19 Commits
python-v0.
...
python-v0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff20d12f20 | ||
|
|
5f3e133470 | ||
|
|
332e722a64 | ||
|
|
3f63c4f8d9 | ||
|
|
39a18baf59 | ||
|
|
0960e19559 | ||
|
|
e5321ba311 | ||
|
|
f523191d21 | ||
|
|
4c3790cde4 | ||
|
|
ff75f2467b | ||
|
|
6f79770248 | ||
|
|
a497db66f9 | ||
|
|
f5076269fe | ||
|
|
a61461331c | ||
|
|
b0170ea86a | ||
|
|
d1efc6ad8a | ||
|
|
9d638cb3c7 | ||
|
|
3cd73f9f5a | ||
|
|
b2d06a3a73 |
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.22.4-beta.2"
|
current_version = "0.23.0-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*)\\.
|
||||||
@@ -72,3 +72,9 @@ search = "\nversion = \"{current_version}\""
|
|||||||
filename = "nodejs/Cargo.toml"
|
filename = "nodejs/Cargo.toml"
|
||||||
replace = "\nversion = \"{new_version}\""
|
replace = "\nversion = \"{new_version}\""
|
||||||
search = "\nversion = \"{current_version}\""
|
search = "\nversion = \"{current_version}\""
|
||||||
|
|
||||||
|
# Java documentation
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "docs/src/java/java.md"
|
||||||
|
replace = "<version>{new_version}</version>"
|
||||||
|
search = "<version>{current_version}</version>"
|
||||||
|
|||||||
107
.github/workflows/java-publish.yml
vendored
107
.github/workflows/java-publish.yml
vendored
@@ -1,76 +1,35 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
name: Build and publish Java packages
|
name: Build and publish Java packages
|
||||||
on:
|
on:
|
||||||
release:
|
push:
|
||||||
types: [released]
|
tags:
|
||||||
|
- "v*"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/java-publish.yml
|
- .github/workflows/java-publish.yml
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
macos-arm64:
|
publish:
|
||||||
name: Build on MacOS Arm64
|
name: Build and Publish
|
||||||
runs-on: macos-14
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 45
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./java/core/lancedb-jni
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
brew install protobuf
|
|
||||||
- name: Build release
|
|
||||||
run: |
|
|
||||||
cargo build --release
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: liblancedb_jni_darwin_aarch64.zip
|
|
||||||
path: target/release/liblancedb_jni.dylib
|
|
||||||
retention-days: 1
|
|
||||||
if-no-files-found: error
|
|
||||||
linux-arm64:
|
|
||||||
name: Build on Linux Arm64
|
|
||||||
runs-on: warp-ubuntu-2204-arm64-8x
|
|
||||||
timeout-minutes: 45
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./java/core/lancedb-jni
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
with:
|
|
||||||
cache-workspaces: "./java/core/lancedb-jni"
|
|
||||||
# Disable full debug symbol generation to speed up CI build and keep memory down
|
|
||||||
# "1" means line tables only, which is useful for panic tracebacks.
|
|
||||||
rustflags: "-C debuginfo=1"
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt -y -qq update
|
|
||||||
sudo apt install -y protobuf-compiler libssl-dev pkg-config
|
|
||||||
- name: Build release
|
|
||||||
run: |
|
|
||||||
cargo build --release
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: liblancedb_jni_linux_aarch64.zip
|
|
||||||
path: target/release/liblancedb_jni.so
|
|
||||||
retention-days: 1
|
|
||||||
if-no-files-found: error
|
|
||||||
linux-x86:
|
|
||||||
runs-on: warp-ubuntu-2204-x64-8x
|
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
needs: [macos-arm64, linux-arm64]
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./java
|
working-directory: ./java
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Set up Java 8
|
- name: Set up Java 8
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
@@ -82,40 +41,30 @@ jobs:
|
|||||||
server-password: SONATYPE_TOKEN
|
server-password: SONATYPE_TOKEN
|
||||||
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
- name: Install dependencies
|
- name: Set git config
|
||||||
run: |
|
run: |
|
||||||
sudo apt -y -qq update
|
git config --global user.email "dev+gha@lancedb.com"
|
||||||
sudo apt install -y protobuf-compiler libssl-dev pkg-config
|
git config --global user.name "LanceDB Github Runner"
|
||||||
- name: Download artifact
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
- name: Copy native libs
|
|
||||||
run: |
|
|
||||||
mkdir -p ./core/target/classes/nativelib/darwin-aarch64 ./core/target/classes/nativelib/linux-aarch64
|
|
||||||
cp ../liblancedb_jni_darwin_aarch64.zip/liblancedb_jni.dylib ./core/target/classes/nativelib/darwin-aarch64/liblancedb_jni.dylib
|
|
||||||
cp ../liblancedb_jni_linux_aarch64.zip/liblancedb_jni.so ./core/target/classes/nativelib/linux-aarch64/liblancedb_jni.so
|
|
||||||
- name: Dry run
|
- name: Dry run
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
mvn --batch-mode -DskipTests -Drust.release.build=true package
|
./mvnw --batch-mode -DskipTests package -pl lancedb-core -am
|
||||||
- name: Set github
|
- name: Publish
|
||||||
run: |
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
git config --global user.email "LanceDB Github Runner"
|
|
||||||
git config --global user.name "dev+gha@lancedb.com"
|
|
||||||
- name: Publish with Java 8
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
run: |
|
||||||
echo "use-agent" >> ~/.gnupg/gpg.conf
|
echo "use-agent" >> ~/.gnupg/gpg.conf
|
||||||
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
|
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
|
||||||
export GPG_TTY=$(tty)
|
export GPG_TTY=$(tty)
|
||||||
mvn --batch-mode -DskipTests -Drust.release.build=true -DpushChanges=false -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} deploy -P deploy-to-ossrh
|
./mvnw --batch-mode -DskipTests -DpushChanges=false -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} deploy -pl lancedb-core -am -P deploy-to-ossrh
|
||||||
env:
|
env:
|
||||||
SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
|
SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
|
||||||
SONATYPE_TOKEN: ${{ secrets.SONATYPE_TOKEN }}
|
SONATYPE_TOKEN: ${{ secrets.SONATYPE_TOKEN }}
|
||||||
|
|
||||||
report-failure:
|
report-failure:
|
||||||
name: Report Workflow Failure
|
name: Report Workflow Failure
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [linux-arm64, linux-x86, macos-arm64]
|
needs: [publish]
|
||||||
if: always() && (github.event_name == 'release' || github.event_name == 'workflow_dispatch')
|
if: always() && failure() && startsWith(github.ref, 'refs/tags/v')
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
issues: write
|
issues: write
|
||||||
|
|||||||
118
.github/workflows/java.yml
vendored
118
.github/workflows/java.yml
vendored
@@ -1,118 +1,46 @@
|
|||||||
name: Build and Run Java JNI Tests
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
name: Build Java LanceDB Core
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- java/**
|
- java/**
|
||||||
|
- .github/workflows/java.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- java/**
|
- java/**
|
||||||
- rust/**
|
|
||||||
- .github/workflows/java.yml
|
- .github/workflows/java.yml
|
||||||
env:
|
|
||||||
# This env var is used by Swatinem/rust-cache@v2 for the cache
|
|
||||||
# key, so we set it to make sure it is always consistent.
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
# Disable full debug symbol generation to speed up CI build and keep memory down
|
|
||||||
# "1" means line tables only, which is useful for panic tracebacks.
|
|
||||||
RUSTFLAGS: "-C debuginfo=1"
|
|
||||||
RUST_BACKTRACE: "1"
|
|
||||||
# according to: https://matklad.github.io/2021/09/04/fast-rust-builds.html
|
|
||||||
# CI builds are faster with incremental disabled.
|
|
||||||
CARGO_INCREMENTAL: "0"
|
|
||||||
CARGO_BUILD_JOBS: "1"
|
|
||||||
jobs:
|
jobs:
|
||||||
linux-build-java-11:
|
build-java:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
name: ubuntu-22.04 + Java 11
|
name: Build
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: ./java
|
working-directory: ./java
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- uses: Swatinem/rust-cache@v2
|
- name: Set up Java 17
|
||||||
with:
|
|
||||||
workspaces: java/core/lancedb-jni
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
with:
|
|
||||||
components: rustfmt
|
|
||||||
- name: Run cargo fmt
|
|
||||||
run: cargo fmt --check
|
|
||||||
working-directory: ./java/core/lancedb-jni
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y protobuf-compiler libssl-dev
|
|
||||||
- name: Install Java 11
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: temurin
|
|
||||||
java-version: 11
|
|
||||||
cache: "maven"
|
|
||||||
- name: Java Style Check
|
|
||||||
run: mvn checkstyle:check
|
|
||||||
# Disable because of issues in lancedb rust core code
|
|
||||||
# - name: Rust Clippy
|
|
||||||
# working-directory: java/core/lancedb-jni
|
|
||||||
# run: cargo clippy --all-targets -- -D warnings
|
|
||||||
- name: Running tests with Java 11
|
|
||||||
run: mvn clean test
|
|
||||||
linux-build-java-17:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
name: ubuntu-22.04 + Java 17
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./java
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
workspaces: java/core/lancedb-jni
|
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
with:
|
|
||||||
components: rustfmt
|
|
||||||
- name: Run cargo fmt
|
|
||||||
run: cargo fmt --check
|
|
||||||
working-directory: ./java/core/lancedb-jni
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y protobuf-compiler libssl-dev
|
|
||||||
- name: Install Java 17
|
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
cache: "maven"
|
cache: "maven"
|
||||||
- run: echo "JAVA_17=$JAVA_HOME" >> $GITHUB_ENV
|
|
||||||
- name: Java Style Check
|
- name: Java Style Check
|
||||||
run: mvn checkstyle:check
|
run: ./mvnw checkstyle:check
|
||||||
# Disable because of issues in lancedb rust core code
|
- name: Build and install
|
||||||
# - name: Rust Clippy
|
run: ./mvnw clean install
|
||||||
# working-directory: java/core/lancedb-jni
|
|
||||||
# run: cargo clippy --all-targets -- -D warnings
|
|
||||||
- name: Running tests with Java 17
|
|
||||||
run: |
|
|
||||||
export JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS \
|
|
||||||
-XX:+IgnoreUnrecognizedVMOptions \
|
|
||||||
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.net=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.nio=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.util=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/sun.nio.cs=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/sun.security.action=ALL-UNNAMED \
|
|
||||||
--add-opens=java.base/sun.util.calendar=ALL-UNNAMED \
|
|
||||||
--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED \
|
|
||||||
-Djdk.reflect.useDirectMethodHandle=false \
|
|
||||||
-Dio.netty.tryReflectionSetAccessible=true"
|
|
||||||
JAVA_HOME=$JAVA_17 mvn clean test
|
|
||||||
|
|||||||
4
.github/workflows/nodejs.yml
vendored
4
.github/workflows/nodejs.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
|||||||
npm install -g @napi-rs/cli
|
npm install -g @napi-rs/cli
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
npm ci --include=optional
|
||||||
npm run build:debug -- --profile ci
|
npm run build:debug -- --profile ci
|
||||||
npm run tsc
|
npm run tsc
|
||||||
- name: Setup localstack
|
- name: Setup localstack
|
||||||
@@ -146,7 +146,7 @@ jobs:
|
|||||||
npm install -g @napi-rs/cli
|
npm install -g @napi-rs/cli
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
npm ci --include=optional
|
||||||
npm run build:debug -- --profile ci
|
npm run build:debug -- --profile ci
|
||||||
npm run tsc
|
npm run tsc
|
||||||
- name: Test
|
- name: Test
|
||||||
|
|||||||
6
.github/workflows/npm-publish.yml
vendored
6
.github/workflows/npm-publish.yml
vendored
@@ -97,12 +97,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
settings:
|
settings:
|
||||||
- target: x86_64-apple-darwin
|
|
||||||
host: macos-latest
|
|
||||||
features: ","
|
|
||||||
pre_build: |-
|
|
||||||
brew install protobuf
|
|
||||||
rustup target add x86_64-apple-darwin
|
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
host: macos-latest
|
host: macos-latest
|
||||||
features: fp16kernels
|
features: fp16kernels
|
||||||
|
|||||||
2
.github/workflows/pypi-publish.yml
vendored
2
.github/workflows/pypi-publish.yml
vendored
@@ -64,8 +64,6 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
- target: x86_64-apple-darwin
|
|
||||||
runner: macos-15-large
|
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
runner: warp-macos-14-arm64-6x
|
runner: warp-macos-14-arm64-6x
|
||||||
env:
|
env:
|
||||||
|
|||||||
17
.github/workflows/python.yml
vendored
17
.github/workflows/python.yml
vendored
@@ -49,8 +49,8 @@ jobs:
|
|||||||
|
|
||||||
type-check:
|
type-check:
|
||||||
name: "Type Check"
|
name: "Type Check"
|
||||||
timeout-minutes: 30
|
timeout-minutes: 60
|
||||||
runs-on: "ubuntu-22.04"
|
runs-on: ubuntu-2404-8x-x64
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
|
|
||||||
doctest:
|
doctest:
|
||||||
name: "Doctest"
|
name: "Doctest"
|
||||||
timeout-minutes: 30
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-2404-8x-x64
|
runs-on: ubuntu-2404-8x-x64
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -143,16 +143,9 @@ jobs:
|
|||||||
- name: Delete wheels
|
- name: Delete wheels
|
||||||
run: rm -rf target/wheels
|
run: rm -rf target/wheels
|
||||||
platform:
|
platform:
|
||||||
name: "Mac: ${{ matrix.config.name }}"
|
name: "Mac"
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
strategy:
|
runs-on: macos-14
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- name: x86
|
|
||||||
runner: macos-15-large
|
|
||||||
- name: Arm
|
|
||||||
runner: macos-14
|
|
||||||
runs-on: "${{ matrix.config.runner }}"
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
195
Cargo.lock
generated
195
Cargo.lock
generated
@@ -1456,12 +1456,6 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0"
|
checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cesu8"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cexpr"
|
name = "cexpr"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -1565,16 +1559,6 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "combine"
|
|
||||||
version = "4.6.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "comfy-table"
|
name = "comfy-table"
|
||||||
version = "7.1.2"
|
version = "7.1.2"
|
||||||
@@ -3102,8 +3086,8 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsst"
|
name = "fsst"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
"rand 0.9.2",
|
"rand 0.9.2",
|
||||||
@@ -4381,28 +4365,6 @@ dependencies = [
|
|||||||
"jiff-tzdb",
|
"jiff-tzdb",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jni"
|
|
||||||
version = "0.21.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
|
||||||
dependencies = [
|
|
||||||
"cesu8",
|
|
||||||
"cfg-if",
|
|
||||||
"combine",
|
|
||||||
"jni-sys",
|
|
||||||
"log",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"walkdir",
|
|
||||||
"windows-sys 0.45.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jni-sys"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.34"
|
version = "0.1.34"
|
||||||
@@ -4460,8 +4422,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance"
|
name = "lance"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-arith",
|
"arrow-arith",
|
||||||
@@ -4526,8 +4488,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-arrow"
|
name = "lance-arrow"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
"arrow-buffer",
|
"arrow-buffer",
|
||||||
@@ -4545,8 +4507,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-bitpacking"
|
name = "lance-bitpacking"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"paste",
|
"paste",
|
||||||
@@ -4555,8 +4517,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-core"
|
name = "lance-core"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
"arrow-buffer",
|
"arrow-buffer",
|
||||||
@@ -4592,8 +4554,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-datafusion"
|
name = "lance-datafusion"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
@@ -4623,8 +4585,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-datagen"
|
name = "lance-datagen"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
@@ -4641,8 +4603,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-encoding"
|
name = "lance-encoding"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-arith",
|
"arrow-arith",
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
@@ -4679,8 +4641,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-file"
|
name = "lance-file"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-arith",
|
"arrow-arith",
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
@@ -4712,8 +4674,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-geo"
|
name = "lance-geo"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"datafusion",
|
"datafusion",
|
||||||
"geo-types",
|
"geo-types",
|
||||||
@@ -4724,8 +4686,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-index"
|
name = "lance-index"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-arith",
|
"arrow-arith",
|
||||||
@@ -4786,8 +4748,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-io"
|
name = "lance-io"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-arith",
|
"arrow-arith",
|
||||||
@@ -4827,8 +4789,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-linalg"
|
name = "lance-linalg"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
"arrow-buffer",
|
"arrow-buffer",
|
||||||
@@ -4844,8 +4806,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-namespace"
|
name = "lance-namespace"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -4857,8 +4819,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-namespace-impls"
|
name = "lance-namespace-impls"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-ipc",
|
"arrow-ipc",
|
||||||
@@ -4896,8 +4858,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-table"
|
name = "lance-table"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
@@ -4936,8 +4898,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lance-testing"
|
name = "lance-testing"
|
||||||
version = "1.0.0-beta.16"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0-beta.16#ff89675ebaca362412c3ec9d62d2de3885a57727"
|
source = "git+https://github.com/lance-format/lance.git?tag=v1.0.0#a0979691926f57afd5d3ac90bf6e5bb11188c0c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
"arrow-schema",
|
"arrow-schema",
|
||||||
@@ -4948,7 +4910,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lancedb"
|
name = "lancedb"
|
||||||
version = "0.22.4-beta.2"
|
version = "0.23.0-beta.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -5025,25 +4987,9 @@ dependencies = [
|
|||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lancedb-jni"
|
|
||||||
version = "0.10.0"
|
|
||||||
dependencies = [
|
|
||||||
"arrow",
|
|
||||||
"arrow-schema",
|
|
||||||
"jni",
|
|
||||||
"lance",
|
|
||||||
"lancedb",
|
|
||||||
"lazy_static",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"snafu",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lancedb-nodejs"
|
name = "lancedb-nodejs"
|
||||||
version = "0.22.4-beta.2"
|
version = "0.23.0-beta.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow-array",
|
"arrow-array",
|
||||||
"arrow-ipc",
|
"arrow-ipc",
|
||||||
@@ -5063,7 +5009,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lancedb-python"
|
name = "lancedb-python"
|
||||||
version = "0.25.4-beta.2"
|
version = "0.26.0-beta.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow",
|
"arrow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -5071,6 +5017,7 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"lance-core",
|
"lance-core",
|
||||||
"lance-io",
|
"lance-io",
|
||||||
|
"lance-namespace",
|
||||||
"lancedb",
|
"lancedb",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"pyo3",
|
"pyo3",
|
||||||
@@ -9505,15 +9452,6 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.45.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.42.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -9550,21 +9488,6 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.42.2",
|
|
||||||
"windows_aarch64_msvc 0.42.2",
|
|
||||||
"windows_i686_gnu 0.42.2",
|
|
||||||
"windows_i686_msvc 0.42.2",
|
|
||||||
"windows_x86_64_gnu 0.42.2",
|
|
||||||
"windows_x86_64_gnullvm 0.42.2",
|
|
||||||
"windows_x86_64_msvc 0.42.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9607,12 +9530,6 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9625,12 +9542,6 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9643,12 +9554,6 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9673,12 +9578,6 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9691,12 +9590,6 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9709,12 +9602,6 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -9727,12 +9614,6 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|||||||
30
Cargo.toml
30
Cargo.toml
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["rust/lancedb", "nodejs", "python", "java/core/lancedb-jni"]
|
members = ["rust/lancedb", "nodejs", "python"]
|
||||||
# Python package needs to be built by maturin.
|
# Python package needs to be built by maturin.
|
||||||
exclude = ["python"]
|
exclude = ["python"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
@@ -15,20 +15,20 @@ categories = ["database-implementations"]
|
|||||||
rust-version = "1.78.0"
|
rust-version = "1.78.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
lance = { "version" = "=1.0.0-beta.16", default-features = false, "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance = { "version" = "=1.0.0", default-features = false, "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-core = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-core = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-datagen = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-datagen = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-file = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-file = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-io = { "version" = "=1.0.0-beta.16", default-features = false, "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-io = { "version" = "=1.0.0", default-features = false, "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-index = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-index = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-linalg = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-linalg = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-namespace = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-namespace = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-namespace-impls = { "version" = "=1.0.0-beta.16", default-features = false, "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-namespace-impls = { "version" = "=1.0.0", default-features = false, "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-table = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-table = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-testing = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-testing = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-datafusion = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-datafusion = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-encoding = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-encoding = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-arrow = { "version" = "=1.0.0-beta.16", "tag" = "v1.0.0-beta.16", "git" = "https://github.com/lance-format/lance.git" }
|
lance-arrow = { "version" = "=1.0.0", "tag" = "v1.0.0", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
ahash = "0.8"
|
ahash = "0.8"
|
||||||
# Note that this one does not include pyarrow
|
# Note that this one does not include pyarrow
|
||||||
arrow = { version = "56.2", optional = false }
|
arrow = { version = "56.2", optional = false }
|
||||||
|
|||||||
@@ -229,6 +229,29 @@ def set_local_version():
|
|||||||
update_cargo_toml(line_updater)
|
update_cargo_toml(line_updater)
|
||||||
|
|
||||||
|
|
||||||
|
def update_lockfiles(version: str, fallback_to_git: bool = False):
|
||||||
|
"""
|
||||||
|
Update Cargo metadata and optionally fall back to using the git tag if the
|
||||||
|
requested crates.io version is unavailable.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print("Updating lockfiles...", file=sys.stderr, end="")
|
||||||
|
run_command("cargo metadata > /dev/null")
|
||||||
|
print(" done.", file=sys.stderr)
|
||||||
|
except Exception as e:
|
||||||
|
if fallback_to_git and "failed to select a version" in str(e):
|
||||||
|
print(
|
||||||
|
f" failed for crates.io v{version}, retrying with git tag...",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
set_preview_version(version)
|
||||||
|
print("Updating lockfiles...", file=sys.stderr, end="")
|
||||||
|
run_command("cargo metadata > /dev/null")
|
||||||
|
print(" done.", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Set the version of the Lance package.")
|
parser = argparse.ArgumentParser(description="Set the version of the Lance package.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"version",
|
"version",
|
||||||
@@ -244,6 +267,7 @@ if args.version == "stable":
|
|||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
set_stable_version(latest_stable_version)
|
set_stable_version(latest_stable_version)
|
||||||
|
update_lockfiles(latest_stable_version)
|
||||||
elif args.version == "preview":
|
elif args.version == "preview":
|
||||||
latest_preview_version = get_latest_preview_version()
|
latest_preview_version = get_latest_preview_version()
|
||||||
print(
|
print(
|
||||||
@@ -251,8 +275,10 @@ elif args.version == "preview":
|
|||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
set_preview_version(latest_preview_version)
|
set_preview_version(latest_preview_version)
|
||||||
|
update_lockfiles(latest_preview_version)
|
||||||
elif args.version == "local":
|
elif args.version == "local":
|
||||||
set_local_version()
|
set_local_version()
|
||||||
|
update_lockfiles("local")
|
||||||
else:
|
else:
|
||||||
# Parse the version number.
|
# Parse the version number.
|
||||||
version = args.version
|
version = args.version
|
||||||
@@ -262,9 +288,7 @@ else:
|
|||||||
|
|
||||||
if "beta" in version:
|
if "beta" in version:
|
||||||
set_preview_version(version)
|
set_preview_version(version)
|
||||||
|
update_lockfiles(version)
|
||||||
else:
|
else:
|
||||||
set_stable_version(version)
|
set_stable_version(version)
|
||||||
|
update_lockfiles(version, fallback_to_git=True)
|
||||||
print("Updating lockfiles...", file=sys.stderr, end="")
|
|
||||||
run_command("cargo metadata > /dev/null")
|
|
||||||
print(" done.", file=sys.stderr)
|
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ nav:
|
|||||||
- Overview: index.md
|
- Overview: index.md
|
||||||
- Python: python/python.md
|
- Python: python/python.md
|
||||||
- Javascript/TypeScript: js/globals.md
|
- Javascript/TypeScript: js/globals.md
|
||||||
|
- Java: java/java.md
|
||||||
- Rust: https://docs.rs/lancedb/latest/lancedb/index.html
|
- Rust: https://docs.rs/lancedb/latest/lancedb/index.html
|
||||||
|
|
||||||
extra_css:
|
extra_css:
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ This page contains the API reference for the SDKs supported by the LanceDB team.
|
|||||||
|
|
||||||
- [Python](python/python.md)
|
- [Python](python/python.md)
|
||||||
- [JavaScript/TypeScript](js/globals.md)
|
- [JavaScript/TypeScript](js/globals.md)
|
||||||
|
- [Java](java/java.md)
|
||||||
- [Rust](https://docs.rs/lancedb/latest/lancedb/index.html)
|
- [Rust](https://docs.rs/lancedb/latest/lancedb/index.html)
|
||||||
499
docs/src/java/java.md
Normal file
499
docs/src/java/java.md
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
# Java SDK
|
||||||
|
|
||||||
|
The LanceDB Java SDK provides a convenient way to interact with LanceDB Cloud and Enterprise deployments using the Lance REST Namespace API.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The Java SDK currently only works for LanceDB remote database that connects to LanceDB Cloud and Enterprise.
|
||||||
|
Local database support is a work in progress. Check [LANCEDB-2848](https://github.com/lancedb/lancedb/issues/2848) for the latest progress.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add the following dependency to your `pom.xml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lancedb</groupId>
|
||||||
|
<artifactId>lancedb-core</artifactId>
|
||||||
|
<version>0.23.0-beta.1</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Connecting to LanceDB Cloud
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.lancedb.LanceDbNamespaceClientBuilder;
|
||||||
|
import org.lance.namespace.LanceNamespace;
|
||||||
|
|
||||||
|
// If your DB url is db://example-db, then your database here is example-db
|
||||||
|
LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
||||||
|
.apiKey("your_lancedb_cloud_api_key")
|
||||||
|
.database("your_database_name")
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connecting to LanceDB Enterprise
|
||||||
|
|
||||||
|
For LanceDB Enterprise deployments with a custom endpoint:
|
||||||
|
|
||||||
|
```java
|
||||||
|
LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
||||||
|
.apiKey("your_lancedb_enterprise_api_key")
|
||||||
|
.database("your_database_name")
|
||||||
|
.endpoint("<your_enterprise_endpoint>")
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Options
|
||||||
|
|
||||||
|
| Method | Description | Required |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| `apiKey(String)` | LanceDB API key | Yes |
|
||||||
|
| `database(String)` | Database name | Yes |
|
||||||
|
| `endpoint(String)` | Custom endpoint URL for Enterprise deployments | No |
|
||||||
|
| `region(String)` | AWS region (default: "us-east-1") | No |
|
||||||
|
| `config(String, String)` | Additional configuration parameters | No |
|
||||||
|
|
||||||
|
## Metadata Operations
|
||||||
|
|
||||||
|
### Creating a Namespace
|
||||||
|
|
||||||
|
Namespaces organize tables hierarchically. Create a namespace before creating tables within it:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.CreateNamespaceRequest;
|
||||||
|
import org.lance.namespace.model.CreateNamespaceResponse;
|
||||||
|
|
||||||
|
// Create a child namespace
|
||||||
|
CreateNamespaceRequest request = new CreateNamespaceRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace"));
|
||||||
|
|
||||||
|
CreateNamespaceResponse response = namespaceClient.createNamespace(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also create nested namespaces:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Create a nested namespace: parent/child
|
||||||
|
CreateNamespaceRequest request = new CreateNamespaceRequest();
|
||||||
|
request.setId(Arrays.asList("parent_namespace", "child_namespace"));
|
||||||
|
|
||||||
|
CreateNamespaceResponse response = namespaceClient.createNamespace(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Describing a Namespace
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.DescribeNamespaceRequest;
|
||||||
|
import org.lance.namespace.model.DescribeNamespaceResponse;
|
||||||
|
|
||||||
|
DescribeNamespaceRequest request = new DescribeNamespaceRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace"));
|
||||||
|
|
||||||
|
DescribeNamespaceResponse response = namespaceClient.describeNamespace(request);
|
||||||
|
System.out.println("Namespace properties: " + response.getProperties());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listing Namespaces
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.ListNamespacesRequest;
|
||||||
|
import org.lance.namespace.model.ListNamespacesResponse;
|
||||||
|
|
||||||
|
// List all namespaces at root level
|
||||||
|
ListNamespacesRequest request = new ListNamespacesRequest();
|
||||||
|
request.setId(Arrays.asList()); // Empty for root
|
||||||
|
|
||||||
|
ListNamespacesResponse response = namespaceClient.listNamespaces(request);
|
||||||
|
for (String ns : response.getNamespaces()) {
|
||||||
|
System.out.println("Namespace: " + ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List child namespaces under a parent
|
||||||
|
ListNamespacesRequest childRequest = new ListNamespacesRequest();
|
||||||
|
childRequest.setId(Arrays.asList("parent_namespace"));
|
||||||
|
|
||||||
|
ListNamespacesResponse childResponse = namespaceClient.listNamespaces(childRequest);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listing Tables
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.ListTablesRequest;
|
||||||
|
import org.lance.namespace.model.ListTablesResponse;
|
||||||
|
|
||||||
|
// List tables in a namespace
|
||||||
|
ListTablesRequest request = new ListTablesRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace"));
|
||||||
|
|
||||||
|
ListTablesResponse response = namespaceClient.listTables(request);
|
||||||
|
for (String table : response.getTables()) {
|
||||||
|
System.out.println("Table: " + table);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dropping a Namespace
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.DropNamespaceRequest;
|
||||||
|
import org.lance.namespace.model.DropNamespaceResponse;
|
||||||
|
|
||||||
|
DropNamespaceRequest request = new DropNamespaceRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace"));
|
||||||
|
|
||||||
|
DropNamespaceResponse response = namespaceClient.dropNamespace(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Describing a Table
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.DescribeTableRequest;
|
||||||
|
import org.lance.namespace.model.DescribeTableResponse;
|
||||||
|
|
||||||
|
DescribeTableRequest request = new DescribeTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
|
||||||
|
DescribeTableResponse response = namespaceClient.describeTable(request);
|
||||||
|
System.out.println("Table version: " + response.getVersion());
|
||||||
|
System.out.println("Schema fields: " + response.getSchema().getFields());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dropping a Table
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.DropTableRequest;
|
||||||
|
import org.lance.namespace.model.DropTableResponse;
|
||||||
|
|
||||||
|
DropTableRequest request = new DropTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
|
||||||
|
DropTableResponse response = namespaceClient.dropTable(request);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing Data
|
||||||
|
|
||||||
|
### Creating a Table
|
||||||
|
|
||||||
|
Tables are created within a namespace by providing data in Apache Arrow IPC format:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.LanceNamespace;
|
||||||
|
import org.lance.namespace.model.CreateTableRequest;
|
||||||
|
import org.lance.namespace.model.CreateTableResponse;
|
||||||
|
import org.apache.arrow.memory.BufferAllocator;
|
||||||
|
import org.apache.arrow.memory.RootAllocator;
|
||||||
|
import org.apache.arrow.vector.IntVector;
|
||||||
|
import org.apache.arrow.vector.VarCharVector;
|
||||||
|
import org.apache.arrow.vector.VectorSchemaRoot;
|
||||||
|
import org.apache.arrow.vector.complex.FixedSizeListVector;
|
||||||
|
import org.apache.arrow.vector.Float4Vector;
|
||||||
|
import org.apache.arrow.vector.ipc.ArrowStreamWriter;
|
||||||
|
import org.apache.arrow.vector.types.FloatingPointPrecision;
|
||||||
|
import org.apache.arrow.vector.types.pojo.ArrowType;
|
||||||
|
import org.apache.arrow.vector.types.pojo.Field;
|
||||||
|
import org.apache.arrow.vector.types.pojo.FieldType;
|
||||||
|
import org.apache.arrow.vector.types.pojo.Schema;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// Create schema with id, name, and embedding fields
|
||||||
|
Schema schema = new Schema(Arrays.asList(
|
||||||
|
new Field("id", FieldType.nullable(new ArrowType.Int(32, true)), null),
|
||||||
|
new Field("name", FieldType.nullable(new ArrowType.Utf8()), null),
|
||||||
|
new Field("embedding",
|
||||||
|
FieldType.nullable(new ArrowType.FixedSizeList(128)),
|
||||||
|
Arrays.asList(new Field("item",
|
||||||
|
FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)),
|
||||||
|
null)))
|
||||||
|
));
|
||||||
|
|
||||||
|
try (BufferAllocator allocator = new RootAllocator();
|
||||||
|
VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) {
|
||||||
|
|
||||||
|
// Populate data
|
||||||
|
root.setRowCount(3);
|
||||||
|
IntVector idVector = (IntVector) root.getVector("id");
|
||||||
|
VarCharVector nameVector = (VarCharVector) root.getVector("name");
|
||||||
|
FixedSizeListVector embeddingVector = (FixedSizeListVector) root.getVector("embedding");
|
||||||
|
Float4Vector embeddingData = (Float4Vector) embeddingVector.getDataVector();
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
idVector.setSafe(i, i + 1);
|
||||||
|
nameVector.setSafe(i, ("item_" + i).getBytes());
|
||||||
|
embeddingVector.setNotNull(i);
|
||||||
|
for (int j = 0; j < 128; j++) {
|
||||||
|
embeddingData.setSafe(i * 128 + j, (float) i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idVector.setValueCount(3);
|
||||||
|
nameVector.setValueCount(3);
|
||||||
|
embeddingData.setValueCount(3 * 128);
|
||||||
|
embeddingVector.setValueCount(3);
|
||||||
|
|
||||||
|
// Serialize to Arrow IPC format
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try (ArrowStreamWriter writer = new ArrowStreamWriter(root, null, Channels.newChannel(out))) {
|
||||||
|
writer.start();
|
||||||
|
writer.writeBatch();
|
||||||
|
writer.end();
|
||||||
|
}
|
||||||
|
byte[] tableData = out.toByteArray();
|
||||||
|
|
||||||
|
// Create table in a namespace
|
||||||
|
CreateTableRequest request = new CreateTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
CreateTableResponse response = namespaceClient.createTable(request, tableData);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.InsertIntoTableRequest;
|
||||||
|
import org.lance.namespace.model.InsertIntoTableResponse;
|
||||||
|
|
||||||
|
// Prepare data in Arrow IPC format (similar to create table example)
|
||||||
|
byte[] insertData = prepareArrowData();
|
||||||
|
|
||||||
|
InsertIntoTableRequest request = new InsertIntoTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
request.setMode(InsertIntoTableRequest.ModeEnum.APPEND);
|
||||||
|
|
||||||
|
InsertIntoTableResponse response = namespaceClient.insertIntoTable(request, insertData);
|
||||||
|
System.out.println("New version: " + response.getVersion());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update
|
||||||
|
|
||||||
|
Update rows matching a predicate condition:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.UpdateTableRequest;
|
||||||
|
import org.lance.namespace.model.UpdateTableResponse;
|
||||||
|
|
||||||
|
UpdateTableRequest request = new UpdateTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
|
||||||
|
// Predicate to select rows to update
|
||||||
|
request.setPredicate("id = 1");
|
||||||
|
|
||||||
|
// Set new values using SQL expressions as [column_name, expression] pairs
|
||||||
|
request.setUpdates(Arrays.asList(
|
||||||
|
Arrays.asList("name", "'updated_name'")
|
||||||
|
));
|
||||||
|
|
||||||
|
UpdateTableResponse response = namespaceClient.updateTable(request);
|
||||||
|
System.out.println("Updated rows: " + response.getUpdatedRows());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete
|
||||||
|
|
||||||
|
Delete rows matching a predicate condition:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.DeleteFromTableRequest;
|
||||||
|
import org.lance.namespace.model.DeleteFromTableResponse;
|
||||||
|
|
||||||
|
DeleteFromTableRequest request = new DeleteFromTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
|
||||||
|
// Predicate to select rows to delete
|
||||||
|
request.setPredicate("id > 100");
|
||||||
|
|
||||||
|
DeleteFromTableResponse response = namespaceClient.deleteFromTable(request);
|
||||||
|
System.out.println("New version: " + response.getVersion());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Merge Insert (Upsert)
|
||||||
|
|
||||||
|
Merge insert allows you to update existing rows and insert new rows in a single operation based on a key column:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.MergeInsertIntoTableRequest;
|
||||||
|
import org.lance.namespace.model.MergeInsertIntoTableResponse;
|
||||||
|
|
||||||
|
// Prepare data with rows to update (id=2,3) and new rows (id=4)
|
||||||
|
byte[] mergeData = prepareArrowData(); // Contains rows with id=2,3,4
|
||||||
|
|
||||||
|
MergeInsertIntoTableRequest request = new MergeInsertIntoTableRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
|
||||||
|
// Match on the "id" column
|
||||||
|
request.setOn("id");
|
||||||
|
|
||||||
|
// Update all columns when a matching row is found
|
||||||
|
request.setWhenMatchedUpdateAll(true);
|
||||||
|
|
||||||
|
// Insert new rows when no match is found
|
||||||
|
request.setWhenNotMatchedInsertAll(true);
|
||||||
|
|
||||||
|
MergeInsertIntoTableResponse response = namespaceClient.mergeInsertIntoTable(request, mergeData);
|
||||||
|
|
||||||
|
System.out.println("Updated rows: " + response.getNumUpdatedRows());
|
||||||
|
System.out.println("Inserted rows: " + response.getNumInsertedRows());
|
||||||
|
```
|
||||||
|
|
||||||
|
## Querying Data
|
||||||
|
|
||||||
|
### Counting Rows
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.CountTableRowsRequest;
|
||||||
|
|
||||||
|
CountTableRowsRequest request = new CountTableRowsRequest();
|
||||||
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
|
||||||
|
Long rowCount = namespaceClient.countTableRows(request);
|
||||||
|
System.out.println("Row count: " + rowCount);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vector Search
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.QueryTableRequest;
|
||||||
|
import org.lance.namespace.model.QueryTableRequestVector;
|
||||||
|
|
||||||
|
QueryTableRequest query = new QueryTableRequest();
|
||||||
|
query.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
query.setK(10); // Return top 10 results
|
||||||
|
|
||||||
|
// Set the query vector
|
||||||
|
List<Float> queryVector = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
queryVector.add(1.0f);
|
||||||
|
}
|
||||||
|
QueryTableRequestVector vector = new QueryTableRequestVector();
|
||||||
|
vector.setSingleVector(queryVector);
|
||||||
|
query.setVector(vector);
|
||||||
|
|
||||||
|
// Specify columns to return
|
||||||
|
query.setColumns(Arrays.asList("id", "name", "embedding"));
|
||||||
|
|
||||||
|
// Execute query - returns Arrow IPC format
|
||||||
|
byte[] result = namespaceClient.queryTable(query);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Full Text Search
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.lance.namespace.model.QueryTableRequest;
|
||||||
|
import org.lance.namespace.model.QueryTableRequestFullTextQuery;
|
||||||
|
import org.lance.namespace.model.StringFtsQuery;
|
||||||
|
|
||||||
|
QueryTableRequest query = new QueryTableRequest();
|
||||||
|
query.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
query.setK(10);
|
||||||
|
|
||||||
|
// Set full text search query
|
||||||
|
StringFtsQuery stringQuery = new StringFtsQuery();
|
||||||
|
stringQuery.setQuery("search terms");
|
||||||
|
stringQuery.setColumns(Arrays.asList("text_column"));
|
||||||
|
|
||||||
|
QueryTableRequestFullTextQuery fts = new QueryTableRequestFullTextQuery();
|
||||||
|
fts.setStringQuery(stringQuery);
|
||||||
|
query.setFullTextQuery(fts);
|
||||||
|
|
||||||
|
// Specify columns to return
|
||||||
|
query.setColumns(Arrays.asList("id", "text_column"));
|
||||||
|
|
||||||
|
byte[] result = namespaceClient.queryTable(query);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Query with Filter
|
||||||
|
|
||||||
|
```java
|
||||||
|
QueryTableRequest query = new QueryTableRequest();
|
||||||
|
query.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
query.setK(10);
|
||||||
|
query.setFilter("id > 50");
|
||||||
|
query.setColumns(Arrays.asList("id", "name"));
|
||||||
|
|
||||||
|
byte[] result = namespaceClient.queryTable(query);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Query with Prefilter
|
||||||
|
|
||||||
|
```java
|
||||||
|
QueryTableRequest query = new QueryTableRequest();
|
||||||
|
query.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
|
query.setK(5);
|
||||||
|
query.setPrefilter(true); // Apply filter before vector search
|
||||||
|
query.setFilter("category = 'electronics'");
|
||||||
|
|
||||||
|
// Set query vector
|
||||||
|
QueryTableRequestVector vector = new QueryTableRequestVector();
|
||||||
|
vector.setSingleVector(queryVector);
|
||||||
|
query.setVector(vector);
|
||||||
|
|
||||||
|
byte[] result = namespaceClient.queryTable(query);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading Query Results
|
||||||
|
|
||||||
|
Query results are returned in Apache Arrow IPC file format. Here's how to read them:
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.apache.arrow.vector.ipc.ArrowFileReader;
|
||||||
|
import org.apache.arrow.vector.VectorSchemaRoot;
|
||||||
|
import org.apache.arrow.memory.BufferAllocator;
|
||||||
|
import org.apache.arrow.memory.RootAllocator;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
|
||||||
|
// Helper class to read Arrow data from byte array
|
||||||
|
class ByteArraySeekableByteChannel implements SeekableByteChannel {
|
||||||
|
private final byte[] data;
|
||||||
|
private long position = 0;
|
||||||
|
private boolean isOpen = true;
|
||||||
|
|
||||||
|
public ByteArraySeekableByteChannel(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(ByteBuffer dst) {
|
||||||
|
int remaining = dst.remaining();
|
||||||
|
int available = (int) (data.length - position);
|
||||||
|
if (available <= 0) return -1;
|
||||||
|
int toRead = Math.min(remaining, available);
|
||||||
|
dst.put(data, (int) position, toRead);
|
||||||
|
position += toRead;
|
||||||
|
return toRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public long position() { return position; }
|
||||||
|
@Override public SeekableByteChannel position(long newPosition) { position = newPosition; return this; }
|
||||||
|
@Override public long size() { return data.length; }
|
||||||
|
@Override public boolean isOpen() { return isOpen; }
|
||||||
|
@Override public void close() { isOpen = false; }
|
||||||
|
@Override public int write(ByteBuffer src) { throw new UnsupportedOperationException(); }
|
||||||
|
@Override public SeekableByteChannel truncate(long size) { throw new UnsupportedOperationException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read query results
|
||||||
|
byte[] queryResult = namespaceClient.queryTable(query);
|
||||||
|
|
||||||
|
try (BufferAllocator allocator = new RootAllocator();
|
||||||
|
ArrowFileReader reader = new ArrowFileReader(
|
||||||
|
new ByteArraySeekableByteChannel(queryResult), allocator)) {
|
||||||
|
|
||||||
|
for (int i = 0; i < reader.getRecordBlocks().size(); i++) {
|
||||||
|
reader.loadRecordBatch(reader.getRecordBlocks().get(i));
|
||||||
|
VectorSchemaRoot root = reader.getVectorSchemaRoot();
|
||||||
|
|
||||||
|
// Access data
|
||||||
|
IntVector idVector = (IntVector) root.getVector("id");
|
||||||
|
VarCharVector nameVector = (VarCharVector) root.getVector("name");
|
||||||
|
|
||||||
|
for (int row = 0; row < root.getRowCount(); row++) {
|
||||||
|
int id = idVector.get(row);
|
||||||
|
String name = new String(nameVector.get(row));
|
||||||
|
System.out.println("Row " + row + ": id=" + id + ", name=" + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
28
java/Makefile
Normal file
28
java/Makefile
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
.PHONY: build-lancedb
|
||||||
|
build-lancedb:
|
||||||
|
./mvnw spotless:apply -pl lancedb-core -am
|
||||||
|
./mvnw install -pl lancedb-core -am
|
||||||
|
|
||||||
|
.PHONY: test-lancedb
|
||||||
|
test-lancedb:
|
||||||
|
# Requires LANCEDB_DB and LANCEDB_API_KEY environment variables
|
||||||
|
./mvnw test -pl lancedb-core -P integration-tests
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
./mvnw clean
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: build-lancedb
|
||||||
@@ -7,10 +7,11 @@
|
|||||||
For LanceDB Cloud, use the simplified builder API:
|
For LanceDB Cloud, use the simplified builder API:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.lancedb.lance.namespace.LanceRestNamespace;
|
import com.lancedb.LanceDbNamespaceClientBuilder;
|
||||||
|
import org.lance.namespace.LanceNamespace;
|
||||||
|
|
||||||
// If your DB url is db://example-db, then your database here is example-db
|
// If your DB url is db://example-db, then your database here is example-db
|
||||||
LanceRestNamespace namespace = LanceDBRestNamespaces.builder()
|
LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
||||||
.apiKey("your_lancedb_cloud_api_key")
|
.apiKey("your_lancedb_cloud_api_key")
|
||||||
.database("your_database_name")
|
.database("your_database_name")
|
||||||
.build();
|
.build();
|
||||||
@@ -18,13 +19,13 @@ LanceRestNamespace namespace = LanceDBRestNamespaces.builder()
|
|||||||
|
|
||||||
### LanceDB Enterprise
|
### LanceDB Enterprise
|
||||||
|
|
||||||
For Enterprise deployments, use your VPC endpoint:
|
For Enterprise deployments, use your custom endpoint:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
LanceRestNamespace namespace = LanceDBRestNamespaces.builder()
|
LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
||||||
.apiKey("your_lancedb_enterprise_api_key")
|
.apiKey("your_lancedb_enterprise_api_key")
|
||||||
.database("your-top-dir") // Your top level folder under your cloud bucket, e.g. s3://your-bucket/your-top-dir/
|
.database("your_database_name")
|
||||||
.hostOverride("http://<vpc_endpoint_dns_name>:80")
|
.endpoint("<your_enterprise_endpoint>")
|
||||||
.build();
|
.build();
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -33,5 +34,11 @@ LanceRestNamespace namespace = LanceDBRestNamespaces.builder()
|
|||||||
Build:
|
Build:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./mvnw install
|
./mvnw install -pl lancedb-core -am
|
||||||
|
```
|
||||||
|
|
||||||
|
Run tests:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./mvnw test -pl lancedb-core
|
||||||
```
|
```
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "lancedb-jni"
|
|
||||||
description = "JNI bindings for LanceDB"
|
|
||||||
# TODO modify lancedb/Cargo.toml for version and dependencies
|
|
||||||
version = "0.10.0"
|
|
||||||
edition.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
readme.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
keywords.workspace = true
|
|
||||||
categories.workspace = true
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
lancedb = { path = "../../../rust/lancedb", default-features = false }
|
|
||||||
lance = { workspace = true }
|
|
||||||
arrow = { workspace = true, features = ["ffi"] }
|
|
||||||
arrow-schema.workspace = true
|
|
||||||
tokio = "1.46"
|
|
||||||
jni = "0.21.1"
|
|
||||||
snafu.workspace = true
|
|
||||||
lazy_static.workspace = true
|
|
||||||
serde = { version = "^1" }
|
|
||||||
serde_json = { version = "1" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["lancedb/default"]
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
||||||
|
|
||||||
use crate::ffi::JNIEnvExt;
|
|
||||||
use crate::traits::IntoJava;
|
|
||||||
use crate::{Error, RT};
|
|
||||||
use jni::objects::{JObject, JString, JValue};
|
|
||||||
use jni::JNIEnv;
|
|
||||||
pub const NATIVE_CONNECTION: &str = "nativeConnectionHandle";
|
|
||||||
use crate::Result;
|
|
||||||
use lancedb::connection::{connect, Connection};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct BlockingConnection {
|
|
||||||
pub(crate) inner: Connection,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockingConnection {
|
|
||||||
pub fn create(dataset_uri: &str) -> Result<Self> {
|
|
||||||
let inner = RT.block_on(connect(dataset_uri).execute())?;
|
|
||||||
Ok(Self { inner })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn table_names(
|
|
||||||
&self,
|
|
||||||
start_after: Option<String>,
|
|
||||||
limit: Option<i32>,
|
|
||||||
) -> Result<Vec<String>> {
|
|
||||||
let mut op = self.inner.table_names();
|
|
||||||
if let Some(start_after) = start_after {
|
|
||||||
op = op.start_after(start_after);
|
|
||||||
}
|
|
||||||
if let Some(limit) = limit {
|
|
||||||
op = op.limit(limit as u32);
|
|
||||||
}
|
|
||||||
Ok(RT.block_on(op.execute())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoJava for BlockingConnection {
|
|
||||||
fn into_java<'a>(self, env: &mut JNIEnv<'a>) -> JObject<'a> {
|
|
||||||
attach_native_connection(env, self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attach_native_connection<'local>(
|
|
||||||
env: &mut JNIEnv<'local>,
|
|
||||||
connection: BlockingConnection,
|
|
||||||
) -> JObject<'local> {
|
|
||||||
let j_connection = create_java_connection_object(env);
|
|
||||||
// This block sets a native Rust object (Connection) as a field in the Java object (j_Connection).
|
|
||||||
// Caution: This creates a potential for memory leaks. The Rust object (Connection) is not
|
|
||||||
// automatically garbage-collected by Java, and its memory will not be freed unless
|
|
||||||
// explicitly handled.
|
|
||||||
//
|
|
||||||
// To prevent memory leaks, ensure the following:
|
|
||||||
// 1. The Java object (`j_Connection`) should implement the `java.io.Closeable` interface.
|
|
||||||
// 2. Users of this Java object should be instructed to always use it within a try-with-resources
|
|
||||||
// statement (or manually call the `close()` method) to ensure that `self.close()` is invoked.
|
|
||||||
match unsafe { env.set_rust_field(&j_connection, NATIVE_CONNECTION, connection) } {
|
|
||||||
Ok(_) => j_connection,
|
|
||||||
Err(err) => {
|
|
||||||
env.throw_new(
|
|
||||||
"java/lang/RuntimeException",
|
|
||||||
format!("Failed to set native handle for Connection: {}", err),
|
|
||||||
)
|
|
||||||
.expect("Error throwing exception");
|
|
||||||
JObject::null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_java_connection_object<'a>(env: &mut JNIEnv<'a>) -> JObject<'a> {
|
|
||||||
env.new_object("com/lancedb/lancedb/Connection", "()V", &[])
|
|
||||||
.expect("Failed to create Java Lance Connection instance")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn Java_com_lancedb_lancedb_Connection_releaseNativeConnection(
|
|
||||||
mut env: JNIEnv,
|
|
||||||
j_connection: JObject,
|
|
||||||
) {
|
|
||||||
let _: BlockingConnection = unsafe {
|
|
||||||
env.take_rust_field(j_connection, NATIVE_CONNECTION)
|
|
||||||
.expect("Failed to take native Connection handle")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn Java_com_lancedb_lancedb_Connection_connect<'local>(
|
|
||||||
mut env: JNIEnv<'local>,
|
|
||||||
_obj: JObject,
|
|
||||||
dataset_uri_object: JString,
|
|
||||||
) -> JObject<'local> {
|
|
||||||
let dataset_uri: String = ok_or_throw!(env, env.get_string(&dataset_uri_object)).into();
|
|
||||||
let blocking_connection = ok_or_throw!(env, BlockingConnection::create(&dataset_uri));
|
|
||||||
blocking_connection.into_java(&mut env)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn Java_com_lancedb_lancedb_Connection_tableNames<'local>(
|
|
||||||
mut env: JNIEnv<'local>,
|
|
||||||
j_connection: JObject,
|
|
||||||
start_after_obj: JObject, // Optional<String>
|
|
||||||
limit_obj: JObject, // Optional<Integer>
|
|
||||||
) -> JObject<'local> {
|
|
||||||
ok_or_throw!(
|
|
||||||
env,
|
|
||||||
inner_table_names(&mut env, j_connection, start_after_obj, limit_obj)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inner_table_names<'local>(
|
|
||||||
env: &mut JNIEnv<'local>,
|
|
||||||
j_connection: JObject,
|
|
||||||
start_after_obj: JObject, // Optional<String>
|
|
||||||
limit_obj: JObject, // Optional<Integer>
|
|
||||||
) -> Result<JObject<'local>> {
|
|
||||||
let start_after = env.get_string_opt(&start_after_obj)?;
|
|
||||||
let limit = env.get_int_opt(&limit_obj)?;
|
|
||||||
let conn =
|
|
||||||
unsafe { env.get_rust_field::<_, _, BlockingConnection>(j_connection, NATIVE_CONNECTION) }?;
|
|
||||||
let table_names = conn.table_names(start_after, limit)?;
|
|
||||||
drop(conn);
|
|
||||||
let j_names = env.new_object("java/util/ArrayList", "()V", &[])?;
|
|
||||||
for item in table_names {
|
|
||||||
let jstr_item = env.new_string(item)?;
|
|
||||||
let item_jobj = JObject::from(jstr_item);
|
|
||||||
let item_gen = JValue::Object(&item_jobj);
|
|
||||||
env.call_method(&j_names, "add", "(Ljava/lang/Object;)Z", &[item_gen])?;
|
|
||||||
}
|
|
||||||
Ok(j_names)
|
|
||||||
}
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
||||||
|
|
||||||
use std::str::Utf8Error;
|
|
||||||
|
|
||||||
use arrow_schema::ArrowError;
|
|
||||||
use jni::errors::Error as JniError;
|
|
||||||
use serde_json::Error as JsonError;
|
|
||||||
use snafu::{Location, Snafu};
|
|
||||||
|
|
||||||
type BoxedError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
|
||||||
|
|
||||||
/// Java Exception types
|
|
||||||
pub enum JavaException {
|
|
||||||
IllegalArgumentException,
|
|
||||||
IOException,
|
|
||||||
RuntimeException,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JavaException {
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::IllegalArgumentException => "java/lang/IllegalArgumentException",
|
|
||||||
Self::IOException => "java/io/IOException",
|
|
||||||
Self::RuntimeException => "java/lang/RuntimeException",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// TODO(lu) change to lancedb-jni
|
|
||||||
#[derive(Debug, Snafu)]
|
|
||||||
#[snafu(visibility(pub))]
|
|
||||||
pub enum Error {
|
|
||||||
#[snafu(display("JNI error: {message}, {location}"))]
|
|
||||||
Jni { message: String, location: Location },
|
|
||||||
#[snafu(display("Invalid argument: {message}, {location}"))]
|
|
||||||
InvalidArgument { message: String, location: Location },
|
|
||||||
#[snafu(display("IO error: {source}, {location}"))]
|
|
||||||
IO {
|
|
||||||
source: BoxedError,
|
|
||||||
location: Location,
|
|
||||||
},
|
|
||||||
#[snafu(display("Arrow error: {message}, {location}"))]
|
|
||||||
Arrow { message: String, location: Location },
|
|
||||||
#[snafu(display("Index error: {message}, {location}"))]
|
|
||||||
Index { message: String, location: Location },
|
|
||||||
#[snafu(display("JSON error: {message}, {location}"))]
|
|
||||||
JSON { message: String, location: Location },
|
|
||||||
#[snafu(display("Dataset at path {path} was not found, {location}"))]
|
|
||||||
DatasetNotFound { path: String, location: Location },
|
|
||||||
#[snafu(display("Dataset already exists: {uri}, {location}"))]
|
|
||||||
DatasetAlreadyExists { uri: String, location: Location },
|
|
||||||
#[snafu(display("Table '{name}' already exists"))]
|
|
||||||
TableAlreadyExists { name: String },
|
|
||||||
#[snafu(display("Table '{name}' was not found: {source}"))]
|
|
||||||
TableNotFound {
|
|
||||||
name: String,
|
|
||||||
source: Box<dyn std::error::Error + Send + Sync>,
|
|
||||||
},
|
|
||||||
#[snafu(display("Invalid table name '{name}': {reason}"))]
|
|
||||||
InvalidTableName { name: String, reason: String },
|
|
||||||
#[snafu(display("Embedding function '{name}' was not found: {reason}, {location}"))]
|
|
||||||
EmbeddingFunctionNotFound {
|
|
||||||
name: String,
|
|
||||||
reason: String,
|
|
||||||
location: Location,
|
|
||||||
},
|
|
||||||
#[snafu(display("Other Lance error: {message}, {location}"))]
|
|
||||||
OtherLance { message: String, location: Location },
|
|
||||||
#[snafu(display("Other LanceDB error: {message}, {location}"))]
|
|
||||||
OtherLanceDB { message: String, location: Location },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
/// Throw as Java Exception
|
|
||||||
pub fn throw(&self, env: &mut jni::JNIEnv) {
|
|
||||||
match self {
|
|
||||||
Self::InvalidArgument { .. }
|
|
||||||
| Self::DatasetNotFound { .. }
|
|
||||||
| Self::DatasetAlreadyExists { .. }
|
|
||||||
| Self::TableAlreadyExists { .. }
|
|
||||||
| Self::TableNotFound { .. }
|
|
||||||
| Self::InvalidTableName { .. }
|
|
||||||
| Self::EmbeddingFunctionNotFound { .. } => {
|
|
||||||
self.throw_as(env, JavaException::IllegalArgumentException)
|
|
||||||
}
|
|
||||||
Self::IO { .. } | Self::Index { .. } => self.throw_as(env, JavaException::IOException),
|
|
||||||
Self::Arrow { .. }
|
|
||||||
| Self::JSON { .. }
|
|
||||||
| Self::OtherLance { .. }
|
|
||||||
| Self::OtherLanceDB { .. }
|
|
||||||
| Self::Jni { .. } => self.throw_as(env, JavaException::RuntimeException),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Throw as an concrete Java Exception
|
|
||||||
pub fn throw_as(&self, env: &mut jni::JNIEnv, exception: JavaException) {
|
|
||||||
let message = &format!(
|
|
||||||
"Error when throwing Java exception: {}:{}",
|
|
||||||
exception.as_str(),
|
|
||||||
self
|
|
||||||
);
|
|
||||||
env.throw_new(exception.as_str(), self.to_string())
|
|
||||||
.expect(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
trait ToSnafuLocation {
|
|
||||||
fn to_snafu_location(&'static self) -> snafu::Location;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToSnafuLocation for std::panic::Location<'static> {
|
|
||||||
fn to_snafu_location(&'static self) -> snafu::Location {
|
|
||||||
snafu::Location::new(self.file(), self.line(), self.column())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<JniError> for Error {
|
|
||||||
#[track_caller]
|
|
||||||
fn from(source: JniError) -> Self {
|
|
||||||
Self::Jni {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Utf8Error> for Error {
|
|
||||||
#[track_caller]
|
|
||||||
fn from(source: Utf8Error) -> Self {
|
|
||||||
Self::InvalidArgument {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ArrowError> for Error {
|
|
||||||
#[track_caller]
|
|
||||||
fn from(source: ArrowError) -> Self {
|
|
||||||
Self::Arrow {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<JsonError> for Error {
|
|
||||||
#[track_caller]
|
|
||||||
fn from(source: JsonError) -> Self {
|
|
||||||
Self::JSON {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<lance::Error> for Error {
|
|
||||||
#[track_caller]
|
|
||||||
fn from(source: lance::Error) -> Self {
|
|
||||||
match source {
|
|
||||||
lance::Error::DatasetNotFound {
|
|
||||||
path,
|
|
||||||
source: _,
|
|
||||||
location,
|
|
||||||
} => Self::DatasetNotFound { path, location },
|
|
||||||
lance::Error::DatasetAlreadyExists { uri, location } => {
|
|
||||||
Self::DatasetAlreadyExists { uri, location }
|
|
||||||
}
|
|
||||||
lance::Error::IO { source, location } => Self::IO { source, location },
|
|
||||||
lance::Error::Arrow { message, location } => Self::Arrow { message, location },
|
|
||||||
lance::Error::Index { message, location } => Self::Index { message, location },
|
|
||||||
lance::Error::InvalidInput { source, location } => Self::InvalidArgument {
|
|
||||||
message: source.to_string(),
|
|
||||||
location,
|
|
||||||
},
|
|
||||||
_ => Self::OtherLance {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<lancedb::Error> for Error {
|
|
||||||
#[track_caller]
|
|
||||||
fn from(source: lancedb::Error) -> Self {
|
|
||||||
match source {
|
|
||||||
lancedb::Error::InvalidTableName { name, reason } => {
|
|
||||||
Self::InvalidTableName { name, reason }
|
|
||||||
}
|
|
||||||
lancedb::Error::InvalidInput { message } => Self::InvalidArgument {
|
|
||||||
message,
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
},
|
|
||||||
lancedb::Error::TableNotFound { name, source } => Self::TableNotFound { name, source },
|
|
||||||
lancedb::Error::TableAlreadyExists { name } => Self::TableAlreadyExists { name },
|
|
||||||
lancedb::Error::EmbeddingFunctionNotFound { name, reason } => {
|
|
||||||
Self::EmbeddingFunctionNotFound {
|
|
||||||
name,
|
|
||||||
reason,
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lancedb::Error::Arrow { source } => Self::Arrow {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
},
|
|
||||||
lancedb::Error::Lance { source } => Self::from(source),
|
|
||||||
_ => Self::OtherLanceDB {
|
|
||||||
message: source.to_string(),
|
|
||||||
location: std::panic::Location::caller().to_snafu_location(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
||||||
|
|
||||||
use core::slice;
|
|
||||||
|
|
||||||
use jni::objects::{JByteBuffer, JObjectArray, JString};
|
|
||||||
use jni::sys::jobjectArray;
|
|
||||||
use jni::{objects::JObject, JNIEnv};
|
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
|
||||||
|
|
||||||
/// TODO(lu) import from lance-jni without duplicate
|
|
||||||
/// Extend JNIEnv with helper functions.
|
|
||||||
pub trait JNIEnvExt {
|
|
||||||
/// Get integers from Java List<Integer> object.
|
|
||||||
fn get_integers(&mut self, obj: &JObject) -> Result<Vec<i32>>;
|
|
||||||
|
|
||||||
/// Get strings from Java List<String> object.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_strings(&mut self, obj: &JObject) -> Result<Vec<String>>;
|
|
||||||
|
|
||||||
/// Get strings from Java String[] object.
|
|
||||||
/// Note that get Option<Vec<String>> from Java Optional<String[]> just doesn't work.
|
|
||||||
#[allow(unused)]
|
|
||||||
fn get_strings_array(&mut self, obj: jobjectArray) -> Result<Vec<String>>;
|
|
||||||
|
|
||||||
/// Get Option<String> from Java Optional<String>.
|
|
||||||
fn get_string_opt(&mut self, obj: &JObject) -> Result<Option<String>>;
|
|
||||||
|
|
||||||
/// Get Option<Vec<String>> from Java Optional<List<String>>.
|
|
||||||
#[allow(unused)]
|
|
||||||
fn get_strings_opt(&mut self, obj: &JObject) -> Result<Option<Vec<String>>>;
|
|
||||||
|
|
||||||
/// Get Option<i32> from Java Optional<Integer>.
|
|
||||||
fn get_int_opt(&mut self, obj: &JObject) -> Result<Option<i32>>;
|
|
||||||
|
|
||||||
/// Get Option<Vec<i32>> from Java Optional<List<Integer>>.
|
|
||||||
fn get_ints_opt(&mut self, obj: &JObject) -> Result<Option<Vec<i32>>>;
|
|
||||||
|
|
||||||
/// Get Option<i64> from Java Optional<Long>.
|
|
||||||
#[allow(unused)]
|
|
||||||
fn get_long_opt(&mut self, obj: &JObject) -> Result<Option<i64>>;
|
|
||||||
|
|
||||||
/// Get Option<u64> from Java Optional<Long>.
|
|
||||||
#[allow(unused)]
|
|
||||||
fn get_u64_opt(&mut self, obj: &JObject) -> Result<Option<u64>>;
|
|
||||||
|
|
||||||
/// Get Option<&[u8]> from Java Optional<ByteBuffer>.
|
|
||||||
#[allow(unused)]
|
|
||||||
fn get_bytes_opt(&mut self, obj: &JObject) -> Result<Option<&[u8]>>;
|
|
||||||
|
|
||||||
fn get_optional<T, F>(&mut self, obj: &JObject, f: F) -> Result<Option<T>>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut JNIEnv, &JObject) -> Result<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JNIEnvExt for JNIEnv<'_> {
|
|
||||||
fn get_integers(&mut self, obj: &JObject) -> Result<Vec<i32>> {
|
|
||||||
let list = self.get_list(obj)?;
|
|
||||||
let mut iter = list.iter(self)?;
|
|
||||||
let mut results = Vec::with_capacity(list.size(self)? as usize);
|
|
||||||
while let Some(elem) = iter.next(self)? {
|
|
||||||
let int_obj = self.call_method(elem, "intValue", "()I", &[])?;
|
|
||||||
let int_value = int_obj.i()?;
|
|
||||||
results.push(int_value);
|
|
||||||
}
|
|
||||||
Ok(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_strings(&mut self, obj: &JObject) -> Result<Vec<String>> {
|
|
||||||
let list = self.get_list(obj)?;
|
|
||||||
let mut iter = list.iter(self)?;
|
|
||||||
let mut results = Vec::with_capacity(list.size(self)? as usize);
|
|
||||||
while let Some(elem) = iter.next(self)? {
|
|
||||||
let jstr = JString::from(elem);
|
|
||||||
let val = self.get_string(&jstr)?;
|
|
||||||
results.push(val.to_str()?.to_string())
|
|
||||||
}
|
|
||||||
Ok(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_strings_array(&mut self, obj: jobjectArray) -> Result<Vec<String>> {
|
|
||||||
let jobject_array = unsafe { JObjectArray::from_raw(obj) };
|
|
||||||
let array_len = self.get_array_length(&jobject_array)?;
|
|
||||||
let mut res: Vec<String> = Vec::new();
|
|
||||||
for i in 0..array_len {
|
|
||||||
let item: JString = self.get_object_array_element(&jobject_array, i)?.into();
|
|
||||||
res.push(self.get_string(&item)?.into());
|
|
||||||
}
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_string_opt(&mut self, obj: &JObject) -> Result<Option<String>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_string_obj = java_obj_gen.l()?;
|
|
||||||
let jstr = JString::from(java_string_obj);
|
|
||||||
let val = env.get_string(&jstr)?;
|
|
||||||
Ok(val.to_str()?.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_strings_opt(&mut self, obj: &JObject) -> Result<Option<Vec<String>>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_list_obj = java_obj_gen.l()?;
|
|
||||||
env.get_strings(&java_list_obj)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_int_opt(&mut self, obj: &JObject) -> Result<Option<i32>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_int_obj = java_obj_gen.l()?;
|
|
||||||
let int_obj = env.call_method(java_int_obj, "intValue", "()I", &[])?;
|
|
||||||
let int_value = int_obj.i()?;
|
|
||||||
Ok(int_value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ints_opt(&mut self, obj: &JObject) -> Result<Option<Vec<i32>>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_list_obj = java_obj_gen.l()?;
|
|
||||||
env.get_integers(&java_list_obj)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_long_opt(&mut self, obj: &JObject) -> Result<Option<i64>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_long_obj = java_obj_gen.l()?;
|
|
||||||
let long_obj = env.call_method(java_long_obj, "longValue", "()J", &[])?;
|
|
||||||
let long_value = long_obj.j()?;
|
|
||||||
Ok(long_value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_u64_opt(&mut self, obj: &JObject) -> Result<Option<u64>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_long_obj = java_obj_gen.l()?;
|
|
||||||
let long_obj = env.call_method(java_long_obj, "longValue", "()J", &[])?;
|
|
||||||
let long_value = long_obj.j()?;
|
|
||||||
Ok(long_value as u64)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_bytes_opt(&mut self, obj: &JObject) -> Result<Option<&[u8]>> {
|
|
||||||
self.get_optional(obj, |env, inner_obj| {
|
|
||||||
let java_obj_gen = env.call_method(inner_obj, "get", "()Ljava/lang/Object;", &[])?;
|
|
||||||
let java_byte_buffer_obj = java_obj_gen.l()?;
|
|
||||||
let j_byte_buffer = JByteBuffer::from(java_byte_buffer_obj);
|
|
||||||
let raw_data = env.get_direct_buffer_address(&j_byte_buffer)?;
|
|
||||||
let capacity = env.get_direct_buffer_capacity(&j_byte_buffer)?;
|
|
||||||
let data = unsafe { slice::from_raw_parts(raw_data, capacity) };
|
|
||||||
Ok(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_optional<T, F>(&mut self, obj: &JObject, f: F) -> Result<Option<T>>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut JNIEnv, &JObject) -> Result<T>,
|
|
||||||
{
|
|
||||||
if obj.is_null() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let is_present = self.call_method(obj, "isPresent", "()Z", &[])?;
|
|
||||||
if !is_present.z()? {
|
|
||||||
// TODO(lu): put get java object into here cuz can only get java Object
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
f(self, obj).map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn Java_com_lancedb_lance_test_JniTestHelper_parseInts(
|
|
||||||
mut env: JNIEnv,
|
|
||||||
_obj: JObject,
|
|
||||||
list_obj: JObject, // List<Integer>
|
|
||||||
) {
|
|
||||||
ok_or_throw_without_return!(env, env.get_integers(&list_obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "system" fn Java_com_lancedb_lance_test_JniTestHelper_parseIntsOpt(
|
|
||||||
mut env: JNIEnv,
|
|
||||||
_obj: JObject,
|
|
||||||
list_obj: JObject, // Optional<List<Integer>>
|
|
||||||
) {
|
|
||||||
ok_or_throw_without_return!(env, env.get_ints_opt(&list_obj));
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
// TODO import from lance-jni without duplicate
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! ok_or_throw {
|
|
||||||
($env:expr, $result:expr) => {
|
|
||||||
match $result {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
Error::from(err).throw(&mut $env);
|
|
||||||
return JObject::null();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! ok_or_throw_without_return {
|
|
||||||
($env:expr, $result:expr) => {
|
|
||||||
match $result {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
Error::from(err).throw(&mut $env);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! ok_or_throw_with_return {
|
|
||||||
($env:expr, $result:expr, $ret:expr) => {
|
|
||||||
match $result {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) => {
|
|
||||||
Error::from(err).throw(&mut $env);
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
mod connection;
|
|
||||||
pub mod error;
|
|
||||||
mod ffi;
|
|
||||||
mod traits;
|
|
||||||
|
|
||||||
pub use error::{Error, Result};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref RT: tokio::runtime::Runtime = tokio::runtime::Builder::new_multi_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.expect("Failed to create tokio runtime");
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
||||||
|
|
||||||
use jni::objects::{JMap, JObject, JString, JValue};
|
|
||||||
use jni::JNIEnv;
|
|
||||||
|
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub trait FromJObject<T> {
|
|
||||||
fn extract(&self) -> Result<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a Rust type into a Java Object.
|
|
||||||
pub trait IntoJava {
|
|
||||||
fn into_java<'a>(self, env: &mut JNIEnv<'a>) -> JObject<'a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromJObject<i32> for JObject<'_> {
|
|
||||||
fn extract(&self) -> Result<i32> {
|
|
||||||
Ok(JValue::from(self).i()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromJObject<i64> for JObject<'_> {
|
|
||||||
fn extract(&self) -> Result<i64> {
|
|
||||||
Ok(JValue::from(self).j()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromJObject<f32> for JObject<'_> {
|
|
||||||
fn extract(&self) -> Result<f32> {
|
|
||||||
Ok(JValue::from(self).f()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromJObject<f64> for JObject<'_> {
|
|
||||||
fn extract(&self) -> Result<f64> {
|
|
||||||
Ok(JValue::from(self).d()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub trait FromJString {
|
|
||||||
fn extract(&self, env: &mut JNIEnv) -> Result<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromJString for JString<'_> {
|
|
||||||
fn extract(&self, env: &mut JNIEnv) -> Result<String> {
|
|
||||||
Ok(env.get_string(self)?.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait JMapExt {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_string(&self, env: &mut JNIEnv, key: &str) -> Result<Option<String>>;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_i32(&self, env: &mut JNIEnv, key: &str) -> Result<Option<i32>>;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_i64(&self, env: &mut JNIEnv, key: &str) -> Result<Option<i64>>;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_f32(&self, env: &mut JNIEnv, key: &str) -> Result<Option<f32>>;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_f64(&self, env: &mut JNIEnv, key: &str) -> Result<Option<f64>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn get_map_value<T>(env: &mut JNIEnv, map: &JMap, key: &str) -> Result<Option<T>>
|
|
||||||
where
|
|
||||||
for<'a> JObject<'a>: FromJObject<T>,
|
|
||||||
{
|
|
||||||
let key_obj: JObject = env.new_string(key)?.into();
|
|
||||||
if let Some(value) = map.get(env, &key_obj)? {
|
|
||||||
if value.is_null() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(value.extract()?))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JMapExt for JMap<'_, '_, '_> {
|
|
||||||
fn get_string(&self, env: &mut JNIEnv, key: &str) -> Result<Option<String>> {
|
|
||||||
let key_obj: JObject = env.new_string(key)?.into();
|
|
||||||
if let Some(value) = self.get(env, &key_obj)? {
|
|
||||||
let value_str: JString = value.into();
|
|
||||||
Ok(Some(value_str.extract(env)?))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_i32(&self, env: &mut JNIEnv, key: &str) -> Result<Option<i32>> {
|
|
||||||
get_map_value(env, self, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_i64(&self, env: &mut JNIEnv, key: &str) -> Result<Option<i64>> {
|
|
||||||
get_map_value(env, self, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_f32(&self, env: &mut JNIEnv, key: &str) -> Result<Option<f32>> {
|
|
||||||
get_map_value(env, self, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_f64(&self, env: &mut JNIEnv, key: &str) -> Result<Option<f64>> {
|
|
||||||
get_map_value(env, self, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.lancedb</groupId>
|
|
||||||
<artifactId>lancedb-parent</artifactId>
|
|
||||||
<version>0.22.4-beta.2</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>lancedb-core</artifactId>
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<description>LanceDB Core</description>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
<properties>
|
|
||||||
<rust.release.build>false</rust.release.build>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.lancedb</groupId>
|
|
||||||
<artifactId>lance-namespace-core</artifactId>
|
|
||||||
<version>0.0.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.arrow</groupId>
|
|
||||||
<artifactId>arrow-vector</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.arrow</groupId>
|
|
||||||
<artifactId>arrow-memory-netty</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.arrow</groupId>
|
|
||||||
<artifactId>arrow-c-data</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.arrow</groupId>
|
|
||||||
<artifactId>arrow-dataset</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.json</groupId>
|
|
||||||
<artifactId>json</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.questdb</groupId>
|
|
||||||
<artifactId>jar-jni</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>build-jni</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.questdb</groupId>
|
|
||||||
<artifactId>rust-maven-plugin</artifactId>
|
|
||||||
<version>1.1.1</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>lancedb-jni</id>
|
|
||||||
<goals>
|
|
||||||
<goal>build</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<path>lancedb-jni</path>
|
|
||||||
<release>${rust.release.build}</release>
|
|
||||||
<!-- Copy native libraries to target/classes for runtime access -->
|
|
||||||
<copyTo>${project.build.directory}/classes/nativelib</copyTo>
|
|
||||||
<copyWithPlatformDir>true</copyWithPlatformDir>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>lancedb-jni-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>test</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<path>lancedb-jni</path>
|
|
||||||
<release>false</release>
|
|
||||||
<verbosity>-v</verbosity>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.lancedb.lancedb;
|
|
||||||
|
|
||||||
import io.questdb.jar.jni.JarJniLoader;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/** Represents LanceDB database. */
|
|
||||||
public class Connection implements Closeable {
|
|
||||||
static {
|
|
||||||
JarJniLoader.loadLib(Connection.class, "/nativelib", "lancedb_jni");
|
|
||||||
}
|
|
||||||
|
|
||||||
private long nativeConnectionHandle;
|
|
||||||
|
|
||||||
/** Connect to a LanceDB instance. */
|
|
||||||
public static native Connection connect(String uri);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of all tables in the database. The names are sorted in ascending order.
|
|
||||||
*
|
|
||||||
* @return the table names
|
|
||||||
*/
|
|
||||||
public List<String> tableNames() {
|
|
||||||
return tableNames(Optional.empty(), Optional.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of filtered tables in the database. The names are sorted in ascending order.
|
|
||||||
*
|
|
||||||
* @param limit The number of results to return.
|
|
||||||
* @return the table names
|
|
||||||
*/
|
|
||||||
public List<String> tableNames(int limit) {
|
|
||||||
return tableNames(Optional.empty(), Optional.of(limit));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of filtered tables in the database. The names are sorted in ascending order.
|
|
||||||
*
|
|
||||||
* @param startAfter If present, only return names that come lexicographically after the supplied
|
|
||||||
* value. This can be combined with limit to implement pagination by setting this to the last
|
|
||||||
* table name from the previous page.
|
|
||||||
* @return the table names
|
|
||||||
*/
|
|
||||||
public List<String> tableNames(String startAfter) {
|
|
||||||
return tableNames(Optional.of(startAfter), Optional.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of filtered tables in the database. The names are sorted in ascending order.
|
|
||||||
*
|
|
||||||
* @param startAfter If present, only return names that come lexicographically after the supplied
|
|
||||||
* value. This can be combined with limit to implement pagination by setting this to the last
|
|
||||||
* table name from the previous page.
|
|
||||||
* @param limit The number of results to return.
|
|
||||||
* @return the table names
|
|
||||||
*/
|
|
||||||
public List<String> tableNames(String startAfter, int limit) {
|
|
||||||
return tableNames(Optional.of(startAfter), Optional.of(limit));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of filtered tables in the database. The names are sorted in ascending order.
|
|
||||||
*
|
|
||||||
* @param startAfter If present, only return names that come lexicographically after the supplied
|
|
||||||
* value. This can be combined with limit to implement pagination by setting this to the last
|
|
||||||
* table name from the previous page.
|
|
||||||
* @param limit The number of results to return.
|
|
||||||
* @return the table names
|
|
||||||
*/
|
|
||||||
public native List<String> tableNames(Optional<String> startAfter, Optional<Integer> limit);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes this connection and releases any system resources associated with it. If the connection
|
|
||||||
* is already closed, then invoking this method has no effect.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (nativeConnectionHandle != 0) {
|
|
||||||
releaseNativeConnection(nativeConnectionHandle);
|
|
||||||
nativeConnectionHandle = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Native method to release the Lance connection resources associated with the given handle.
|
|
||||||
*
|
|
||||||
* @param handle The native handle to the connection resource.
|
|
||||||
*/
|
|
||||||
private native void releaseNativeConnection(long handle);
|
|
||||||
|
|
||||||
private Connection() {}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.lancedb.lancedb;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
public class ConnectionTest {
|
|
||||||
private static final String[] TABLE_NAMES = {
|
|
||||||
"dataset_version", "new_empty_dataset", "test", "write_stream"
|
|
||||||
};
|
|
||||||
|
|
||||||
@TempDir static Path tempDir; // Temporary directory for the tests
|
|
||||||
private static URL lanceDbURL;
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
static void setUp() {
|
|
||||||
ClassLoader classLoader = ConnectionTest.class.getClassLoader();
|
|
||||||
lanceDbURL = classLoader.getResource("example_db");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void emptyDB() {
|
|
||||||
String databaseUri = tempDir.resolve("emptyDB").toString();
|
|
||||||
try (Connection conn = Connection.connect(databaseUri)) {
|
|
||||||
List<String> tableNames = conn.tableNames();
|
|
||||||
assertTrue(tableNames.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void tableNames() {
|
|
||||||
try (Connection conn = Connection.connect(lanceDbURL.toString())) {
|
|
||||||
List<String> tableNames = conn.tableNames();
|
|
||||||
assertEquals(4, tableNames.size());
|
|
||||||
for (int i = 0; i < TABLE_NAMES.length; i++) {
|
|
||||||
assertEquals(TABLE_NAMES[i], tableNames.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void tableNamesStartAfter() {
|
|
||||||
try (Connection conn = Connection.connect(lanceDbURL.toString())) {
|
|
||||||
assertTableNamesStartAfter(
|
|
||||||
conn, TABLE_NAMES[0], 3, TABLE_NAMES[1], TABLE_NAMES[2], TABLE_NAMES[3]);
|
|
||||||
assertTableNamesStartAfter(conn, TABLE_NAMES[1], 2, TABLE_NAMES[2], TABLE_NAMES[3]);
|
|
||||||
assertTableNamesStartAfter(conn, TABLE_NAMES[2], 1, TABLE_NAMES[3]);
|
|
||||||
assertTableNamesStartAfter(conn, TABLE_NAMES[3], 0);
|
|
||||||
assertTableNamesStartAfter(
|
|
||||||
conn, "a_dataset", 4, TABLE_NAMES[0], TABLE_NAMES[1], TABLE_NAMES[2], TABLE_NAMES[3]);
|
|
||||||
assertTableNamesStartAfter(conn, "o_dataset", 2, TABLE_NAMES[2], TABLE_NAMES[3]);
|
|
||||||
assertTableNamesStartAfter(conn, "v_dataset", 1, TABLE_NAMES[3]);
|
|
||||||
assertTableNamesStartAfter(conn, "z_dataset", 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertTableNamesStartAfter(
|
|
||||||
Connection conn, String startAfter, int expectedSize, String... expectedNames) {
|
|
||||||
List<String> tableNames = conn.tableNames(startAfter);
|
|
||||||
assertEquals(expectedSize, tableNames.size());
|
|
||||||
for (int i = 0; i < expectedNames.length; i++) {
|
|
||||||
assertEquals(expectedNames[i], tableNames.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void tableNamesLimit() {
|
|
||||||
try (Connection conn = Connection.connect(lanceDbURL.toString())) {
|
|
||||||
for (int i = 0; i <= TABLE_NAMES.length; i++) {
|
|
||||||
List<String> tableNames = conn.tableNames(i);
|
|
||||||
assertEquals(i, tableNames.size());
|
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
assertEquals(TABLE_NAMES[j], tableNames.get(j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void tableNamesStartAfterLimit() {
|
|
||||||
try (Connection conn = Connection.connect(lanceDbURL.toString())) {
|
|
||||||
List<String> tableNames = conn.tableNames(TABLE_NAMES[0], 2);
|
|
||||||
assertEquals(2, tableNames.size());
|
|
||||||
assertEquals(TABLE_NAMES[1], tableNames.get(0));
|
|
||||||
assertEquals(TABLE_NAMES[2], tableNames.get(1));
|
|
||||||
tableNames = conn.tableNames(TABLE_NAMES[1], 1);
|
|
||||||
assertEquals(1, tableNames.size());
|
|
||||||
assertEquals(TABLE_NAMES[2], tableNames.get(0));
|
|
||||||
tableNames = conn.tableNames(TABLE_NAMES[2], 2);
|
|
||||||
assertEquals(1, tableNames.size());
|
|
||||||
assertEquals(TABLE_NAMES[3], tableNames.get(0));
|
|
||||||
tableNames = conn.tableNames(TABLE_NAMES[3], 2);
|
|
||||||
assertEquals(0, tableNames.size());
|
|
||||||
tableNames = conn.tableNames(TABLE_NAMES[0], 0);
|
|
||||||
assertEquals(0, tableNames.size());
|
|
||||||
|
|
||||||
// Limit larger than the number of remaining tables
|
|
||||||
tableNames = conn.tableNames(TABLE_NAMES[0], 10);
|
|
||||||
assertEquals(3, tableNames.size());
|
|
||||||
assertEquals(TABLE_NAMES[1], tableNames.get(0));
|
|
||||||
assertEquals(TABLE_NAMES[2], tableNames.get(1));
|
|
||||||
assertEquals(TABLE_NAMES[3], tableNames.get(2));
|
|
||||||
|
|
||||||
// Start after a value not in the list
|
|
||||||
tableNames = conn.tableNames("non_existent_table", 2);
|
|
||||||
assertEquals(2, tableNames.size());
|
|
||||||
assertEquals(TABLE_NAMES[2], tableNames.get(0));
|
|
||||||
assertEquals(TABLE_NAMES[3], tableNames.get(1));
|
|
||||||
|
|
||||||
// Start after the last table with a limit
|
|
||||||
tableNames = conn.tableNames(TABLE_NAMES[3], 1);
|
|
||||||
assertEquals(0, tableNames.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
$d51afd07-e3cd-4c76-9b9b-787e13fd55b0<62>=id <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*int3208name <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*string08
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
$15648e72-076f-4ef1-8b90-10d305b95b3b<33>=id <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*int3208name <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*string08
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
$a3689caf-4f6b-4afc-a3c7-97af75661843<34>oitem <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*string8price <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*double80vector <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*fixed_size_list:float:28
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>com.lancedb</groupId>
|
|
||||||
<artifactId>lancedb-parent</artifactId>
|
|
||||||
<version>0.22.4-beta.2</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>lancedb-lance-namespace</artifactId>
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<description>LanceDB Java Integration with Lance Namespace</description>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.lancedb</groupId>
|
|
||||||
<artifactId>lance-namespace-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
||||||
99
java/lancedb-core/pom.xml
Normal file
99
java/lancedb-core/pom.xml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.lancedb</groupId>
|
||||||
|
<artifactId>lancedb-parent</artifactId>
|
||||||
|
<version>0.23.0-beta.1</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>lancedb-core</artifactId>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>Utilities to work with LanceDB Cloud and Enterprise via Lance REST Namespace</description>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lance</groupId>
|
||||||
|
<artifactId>lance-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.arrow</groupId>
|
||||||
|
<artifactId>arrow-vector</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.arrow</groupId>
|
||||||
|
<artifactId>arrow-memory-netty</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<version>5.18.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>2.0.16</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||||
|
<version>2.24.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>2.24.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>2.24.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar-no-fork</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -11,35 +11,58 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package com.lancedb.lancedb;
|
package com.lancedb;
|
||||||
|
|
||||||
import com.lancedb.lance.namespace.LanceRestNamespace;
|
import org.lance.namespace.LanceNamespace;
|
||||||
import com.lancedb.lance.namespace.client.apache.ApiClient;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/** Util class to help construct a {@link LanceRestNamespace} for LanceDB. */
|
/**
|
||||||
public class LanceDbRestNamespaces {
|
* Util class to help construct a {@link LanceNamespace} for LanceDB.
|
||||||
|
*
|
||||||
|
* <p>For LanceDB Cloud, use the simplified builder API:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* import org.lance.namespace.LanceNamespace;
|
||||||
|
*
|
||||||
|
* // If your DB url is db://example-db, then your database here is example-db
|
||||||
|
* LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
||||||
|
* .apiKey("your_lancedb_cloud_api_key")
|
||||||
|
* .database("your_database_name")
|
||||||
|
* .build();
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <p>For LanceDB Enterprise deployments, use your custom endpoint:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
||||||
|
* .apiKey("your_lancedb_enterprise_api_key")
|
||||||
|
* .database("your_database_name")
|
||||||
|
* .endpoint("<your_enterprise_endpoint>")
|
||||||
|
* .build();
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public class LanceDbNamespaceClientBuilder {
|
||||||
private static final String DEFAULT_REGION = "us-east-1";
|
private static final String DEFAULT_REGION = "us-east-1";
|
||||||
private static final String CLOUD_URL_PATTERN = "https://%s.%s.api.lancedb.com";
|
private static final String CLOUD_URL_PATTERN = "https://%s.%s.api.lancedb.com";
|
||||||
|
|
||||||
private String apiKey;
|
private String apiKey;
|
||||||
private String database;
|
private String database;
|
||||||
private Optional<String> hostOverride = Optional.empty();
|
private Optional<String> endpoint = Optional.empty();
|
||||||
private Optional<String> region = Optional.empty();
|
private Optional<String> region = Optional.empty();
|
||||||
private Map<String, String> additionalConfig = new HashMap<>();
|
private Map<String, String> additionalConfig = new HashMap<>();
|
||||||
|
|
||||||
private LanceDbRestNamespaces() {}
|
private LanceDbNamespaceClientBuilder() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new builder instance.
|
* Create a new builder instance.
|
||||||
*
|
*
|
||||||
* @return A new LanceRestNamespaceBuilder
|
* @return A new LanceDbNamespaceClientBuilder
|
||||||
*/
|
*/
|
||||||
public static LanceDbRestNamespaces builder() {
|
public static LanceDbNamespaceClientBuilder newBuilder() {
|
||||||
return new LanceDbRestNamespaces();
|
return new LanceDbNamespaceClientBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +71,7 @@ public class LanceDbRestNamespaces {
|
|||||||
* @param apiKey The LanceDB API key
|
* @param apiKey The LanceDB API key
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public LanceDbRestNamespaces apiKey(String apiKey) {
|
public LanceDbNamespaceClientBuilder apiKey(String apiKey) {
|
||||||
if (apiKey == null || apiKey.trim().isEmpty()) {
|
if (apiKey == null || apiKey.trim().isEmpty()) {
|
||||||
throw new IllegalArgumentException("API key cannot be null or empty");
|
throw new IllegalArgumentException("API key cannot be null or empty");
|
||||||
}
|
}
|
||||||
@@ -62,7 +85,7 @@ public class LanceDbRestNamespaces {
|
|||||||
* @param database The database name
|
* @param database The database name
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public LanceDbRestNamespaces database(String database) {
|
public LanceDbNamespaceClientBuilder database(String database) {
|
||||||
if (database == null || database.trim().isEmpty()) {
|
if (database == null || database.trim().isEmpty()) {
|
||||||
throw new IllegalArgumentException("Database cannot be null or empty");
|
throw new IllegalArgumentException("Database cannot be null or empty");
|
||||||
}
|
}
|
||||||
@@ -71,25 +94,25 @@ public class LanceDbRestNamespaces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a custom host override (optional). When set, this overrides the default LanceDB Cloud URL
|
* Set a custom endpoint URL (optional). When set, this overrides the default LanceDB Cloud URL
|
||||||
* construction. Use this for LanceDB Enterprise deployments.
|
* construction. Use this for LanceDB Enterprise deployments.
|
||||||
*
|
*
|
||||||
* @param hostOverride The complete base URL (e.g., "http://your-vpc-endpoint:80")
|
* @param endpoint The complete base URL for your LanceDB Enterprise deployment
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public LanceDbRestNamespaces hostOverride(String hostOverride) {
|
public LanceDbNamespaceClientBuilder endpoint(String endpoint) {
|
||||||
this.hostOverride = Optional.ofNullable(hostOverride);
|
this.endpoint = Optional.ofNullable(endpoint);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the region for LanceDB Cloud (optional). Defaults to "us-east-1" if not specified. This is
|
* Set the region for LanceDB Cloud (optional). Defaults to "us-east-1" if not specified. This is
|
||||||
* ignored when hostOverride is set.
|
* ignored when endpoint is set.
|
||||||
*
|
*
|
||||||
* @param region The AWS region (e.g., "us-east-1", "eu-west-1")
|
* @param region The AWS region (e.g., "us-east-1", "eu-west-1")
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public LanceDbRestNamespaces region(String region) {
|
public LanceDbNamespaceClientBuilder region(String region) {
|
||||||
this.region = Optional.ofNullable(region);
|
this.region = Optional.ofNullable(region);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -101,18 +124,18 @@ public class LanceDbRestNamespaces {
|
|||||||
* @param value The configuration value
|
* @param value The configuration value
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
public LanceDbRestNamespaces config(String key, String value) {
|
public LanceDbNamespaceClientBuilder config(String key, String value) {
|
||||||
this.additionalConfig.put(key, value);
|
this.additionalConfig.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the LanceRestNamespace instance.
|
* Build the LanceNamespace instance.
|
||||||
*
|
*
|
||||||
* @return A configured LanceRestNamespace
|
* @return A configured LanceNamespace
|
||||||
* @throws IllegalStateException if required parameters are missing
|
* @throws IllegalStateException if required parameters are missing
|
||||||
*/
|
*/
|
||||||
public LanceRestNamespace build() {
|
public LanceNamespace build() {
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if (apiKey == null) {
|
if (apiKey == null) {
|
||||||
throw new IllegalStateException("API key is required");
|
throw new IllegalStateException("API key is required");
|
||||||
@@ -123,24 +146,19 @@ public class LanceDbRestNamespaces {
|
|||||||
|
|
||||||
// Build configuration map
|
// Build configuration map
|
||||||
Map<String, String> config = new HashMap<>(additionalConfig);
|
Map<String, String> config = new HashMap<>(additionalConfig);
|
||||||
config.put("headers.x-lancedb-database", database);
|
config.put("header.x-lancedb-database", database);
|
||||||
config.put("headers.x-api-key", apiKey);
|
config.put("header.x-api-key", apiKey);
|
||||||
|
|
||||||
// Determine base URL
|
// Determine base URL
|
||||||
String baseUrl;
|
String uri;
|
||||||
if (hostOverride.isPresent()) {
|
if (endpoint.isPresent()) {
|
||||||
baseUrl = hostOverride.get();
|
uri = endpoint.get();
|
||||||
config.put("host_override", hostOverride.get());
|
|
||||||
} else {
|
} else {
|
||||||
String effectiveRegion = region.orElse(DEFAULT_REGION);
|
String effectiveRegion = region.orElse(DEFAULT_REGION);
|
||||||
baseUrl = String.format(CLOUD_URL_PATTERN, database, effectiveRegion);
|
uri = String.format(CLOUD_URL_PATTERN, database, effectiveRegion);
|
||||||
config.put("region", effectiveRegion);
|
|
||||||
}
|
}
|
||||||
|
config.put("uri", uri);
|
||||||
|
|
||||||
// Create and configure ApiClient
|
return LanceNamespace.connect("rest", config, null);
|
||||||
ApiClient apiClient = new ApiClient();
|
|
||||||
apiClient.setBasePath(baseUrl);
|
|
||||||
|
|
||||||
return new LanceRestNamespace(apiClient, config);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.lancedb;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/** Unit tests for LanceDbNamespaceClientBuilder. */
|
||||||
|
public class LanceDbNamespaceClientBuilderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilderRequiresApiKey() {
|
||||||
|
LanceDbNamespaceClientBuilder builder =
|
||||||
|
LanceDbNamespaceClientBuilder.newBuilder().database("test-db");
|
||||||
|
|
||||||
|
IllegalStateException exception = assertThrows(IllegalStateException.class, builder::build);
|
||||||
|
assertEquals("API key is required", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilderRequiresDatabase() {
|
||||||
|
LanceDbNamespaceClientBuilder builder =
|
||||||
|
LanceDbNamespaceClientBuilder.newBuilder().apiKey("test-api-key");
|
||||||
|
|
||||||
|
IllegalStateException exception = assertThrows(IllegalStateException.class, builder::build);
|
||||||
|
assertEquals("Database is required", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApiKeyCannotBeNull() {
|
||||||
|
IllegalArgumentException exception =
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> LanceDbNamespaceClientBuilder.newBuilder().apiKey(null));
|
||||||
|
assertEquals("API key cannot be null or empty", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApiKeyCannotBeEmpty() {
|
||||||
|
IllegalArgumentException exception =
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> LanceDbNamespaceClientBuilder.newBuilder().apiKey(" "));
|
||||||
|
assertEquals("API key cannot be null or empty", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDatabaseCannotBeNull() {
|
||||||
|
IllegalArgumentException exception =
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> LanceDbNamespaceClientBuilder.newBuilder().database(null));
|
||||||
|
assertEquals("Database cannot be null or empty", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDatabaseCannotBeEmpty() {
|
||||||
|
IllegalArgumentException exception =
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
() -> LanceDbNamespaceClientBuilder.newBuilder().database(" "));
|
||||||
|
assertEquals("Database cannot be null or empty", exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilderFluentApi() {
|
||||||
|
// Verify the builder returns itself for chaining
|
||||||
|
LanceDbNamespaceClientBuilder builder = LanceDbNamespaceClientBuilder.newBuilder();
|
||||||
|
|
||||||
|
assertSame(builder, builder.apiKey("test-key"));
|
||||||
|
assertSame(builder, builder.database("test-db"));
|
||||||
|
assertSame(builder, builder.endpoint("http://localhost:8080"));
|
||||||
|
assertSame(builder, builder.region("eu-west-1"));
|
||||||
|
assertSame(builder, builder.config("custom-key", "custom-value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNewBuilderCreatesNewInstance() {
|
||||||
|
LanceDbNamespaceClientBuilder builder1 = LanceDbNamespaceClientBuilder.newBuilder();
|
||||||
|
LanceDbNamespaceClientBuilder builder2 = LanceDbNamespaceClientBuilder.newBuilder();
|
||||||
|
|
||||||
|
assertNotSame(builder1, builder2);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
java/lancedb-core/src/test/resources/log4j2.xml
Normal file
32
java/lancedb-core/src/test/resources/log4j2.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<configuration monitorInterval="30">
|
||||||
|
<appenders>
|
||||||
|
<Console name='Console' target='SYSTEM_ERR'>
|
||||||
|
<PatternLayout pattern='%d{HH:mm:ss.SSS} %p [%t] %C{1}.%M: %m%n'/>
|
||||||
|
</Console>
|
||||||
|
</appenders>
|
||||||
|
<loggers>
|
||||||
|
<logger name='com.lancedb' level='DEBUG' additivity='false'>
|
||||||
|
<appender-ref ref='Console'/>
|
||||||
|
</logger>
|
||||||
|
<root level='INFO'>
|
||||||
|
<appender-ref ref='Console'/>
|
||||||
|
</root>
|
||||||
|
</loggers>
|
||||||
|
</configuration>
|
||||||
23
java/pom.xml
23
java/pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.22.4-beta.2</version>
|
<version>0.23.0-beta.1</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
<description>LanceDB Java SDK Parent POM</description>
|
<description>LanceDB Java SDK Parent POM</description>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<arrow.version>15.0.0</arrow.version>
|
<arrow.version>15.0.0</arrow.version>
|
||||||
<lance-namespace.verison>0.0.1</lance-namespace.verison>
|
<lance-core.version>1.0.0-rc.2</lance-core.version>
|
||||||
<spotless.skip>false</spotless.skip>
|
<spotless.skip>false</spotless.skip>
|
||||||
<spotless.version>2.30.0</spotless.version>
|
<spotless.version>2.30.0</spotless.version>
|
||||||
<spotless.java.googlejavaformat.version>1.7</spotless.java.googlejavaformat.version>
|
<spotless.java.googlejavaformat.version>1.7</spotless.java.googlejavaformat.version>
|
||||||
@@ -51,8 +51,7 @@
|
|||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>core</module>
|
<module>lancedb-core</module>
|
||||||
<module>lance-namespace</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
@@ -64,9 +63,9 @@
|
|||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>org.lance</groupId>
|
||||||
<artifactId>lance-namespace-core</artifactId>
|
<artifactId>lance-core</artifactId>
|
||||||
<version>${lance-namespace.verison}</version>
|
<version>${lance-core.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.arrow</groupId>
|
<groupId>org.apache.arrow</groupId>
|
||||||
@@ -88,21 +87,11 @@
|
|||||||
<artifactId>arrow-dataset</artifactId>
|
<artifactId>arrow-dataset</artifactId>
|
||||||
<version>${arrow.version}</version>
|
<version>${arrow.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.questdb</groupId>
|
|
||||||
<artifactId>jar-jni</artifactId>
|
|
||||||
<version>1.1.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
<version>5.10.1</version>
|
<version>5.10.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.json</groupId>
|
|
||||||
<artifactId>json</artifactId>
|
|
||||||
<version>20210307</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-nodejs"
|
name = "lancedb-nodejs"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
version = "0.22.4-beta.2"
|
version = "0.23.0-beta.1"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-darwin-arm64",
|
"name": "@lancedb/lancedb-darwin-arm64",
|
||||||
"version": "0.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-beta.1",
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-win32-x64-msvc",
|
"name": "@lancedb/lancedb-win32-x64-msvc",
|
||||||
"version": "0.22.4-beta.2",
|
"version": "0.23.0-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.22.4-beta.2",
|
"version": "0.23.0-beta.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@lancedb/lancedb",
|
"name": "@lancedb/lancedb",
|
||||||
"version": "0.22.4-beta.2",
|
"version": "0.23.0-beta.1",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64",
|
"x64",
|
||||||
"arm64"
|
"arm64"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"ann"
|
"ann"
|
||||||
],
|
],
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.22.4-beta.2",
|
"version": "0.23.0-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.25.4-beta.3"
|
current_version = "0.26.0"
|
||||||
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.25.4-beta.3"
|
version = "0.26.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Python bindings for LanceDB"
|
description = "Python bindings for LanceDB"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
@@ -18,6 +18,7 @@ arrow = { version = "56.2", features = ["pyarrow"] }
|
|||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
lancedb = { path = "../rust/lancedb", default-features = false }
|
lancedb = { path = "../rust/lancedb", default-features = false }
|
||||||
lance-core.workspace = true
|
lance-core.workspace = true
|
||||||
|
lance-namespace.workspace = true
|
||||||
lance-io.workspace = true
|
lance-io.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
pyo3 = { version = "0.25", features = ["extension-module", "abi3-py39"] }
|
pyo3 = { version = "0.25", features = ["extension-module", "abi3-py39"] }
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ dependencies = [
|
|||||||
"pyarrow>=16",
|
"pyarrow>=16",
|
||||||
"pydantic>=1.10",
|
"pydantic>=1.10",
|
||||||
"tqdm>=4.27.0",
|
"tqdm>=4.27.0",
|
||||||
"lance-namespace>=0.2.1"
|
"lance-namespace>=0.3.2"
|
||||||
]
|
]
|
||||||
description = "lancedb"
|
description = "lancedb"
|
||||||
authors = [{ name = "LanceDB Devs", email = "dev@lancedb.com" }]
|
authors = [{ name = "LanceDB Devs", email = "dev@lancedb.com" }]
|
||||||
|
|||||||
@@ -3,10 +3,30 @@ from typing import Dict, List, Optional, Tuple, Any, TypedDict, Union, Literal
|
|||||||
|
|
||||||
import pyarrow as pa
|
import pyarrow as pa
|
||||||
|
|
||||||
from .index import BTree, IvfFlat, IvfPq, Bitmap, LabelList, HnswPq, HnswSq, FTS
|
from .index import (
|
||||||
|
BTree,
|
||||||
|
IvfFlat,
|
||||||
|
IvfPq,
|
||||||
|
IvfSq,
|
||||||
|
Bitmap,
|
||||||
|
LabelList,
|
||||||
|
HnswPq,
|
||||||
|
HnswSq,
|
||||||
|
FTS,
|
||||||
|
)
|
||||||
from .io import StorageOptionsProvider
|
from .io import StorageOptionsProvider
|
||||||
|
from lance_namespace import (
|
||||||
|
ListNamespacesResponse,
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
|
ListTablesResponse,
|
||||||
|
)
|
||||||
from .remote import ClientConfig
|
from .remote import ClientConfig
|
||||||
|
|
||||||
|
IvfHnswPq: type[HnswPq] = HnswPq
|
||||||
|
IvfHnswSq: type[HnswSq] = HnswSq
|
||||||
|
|
||||||
class Session:
|
class Session:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -26,18 +46,38 @@ class Connection(object):
|
|||||||
async def close(self): ...
|
async def close(self): ...
|
||||||
async def list_namespaces(
|
async def list_namespaces(
|
||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]],
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str],
|
page_token: Optional[str] = None,
|
||||||
limit: Optional[int],
|
limit: Optional[int] = None,
|
||||||
) -> List[str]: ...
|
) -> ListNamespacesResponse: ...
|
||||||
async def create_namespace(self, namespace: List[str]) -> None: ...
|
async def create_namespace(
|
||||||
async def drop_namespace(self, namespace: List[str]) -> None: ...
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse: ...
|
||||||
|
async def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse: ...
|
||||||
|
async def describe_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
) -> DescribeNamespaceResponse: ...
|
||||||
|
async def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse: ...
|
||||||
async def table_names(
|
async def table_names(
|
||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]],
|
namespace: Optional[List[str]],
|
||||||
start_after: Optional[str],
|
start_after: Optional[str],
|
||||||
limit: Optional[int],
|
limit: Optional[int],
|
||||||
) -> list[str]: ...
|
) -> list[str]: ... # Deprecated: Use list_tables instead
|
||||||
async def create_table(
|
async def create_table(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
@@ -104,7 +144,17 @@ class Table:
|
|||||||
async def create_index(
|
async def create_index(
|
||||||
self,
|
self,
|
||||||
column: str,
|
column: str,
|
||||||
index: Union[IvfFlat, IvfPq, HnswPq, HnswSq, BTree, Bitmap, LabelList, FTS],
|
index: Union[
|
||||||
|
IvfFlat,
|
||||||
|
IvfSq,
|
||||||
|
IvfPq,
|
||||||
|
HnswPq,
|
||||||
|
HnswSq,
|
||||||
|
BTree,
|
||||||
|
Bitmap,
|
||||||
|
LabelList,
|
||||||
|
FTS,
|
||||||
|
],
|
||||||
replace: Optional[bool],
|
replace: Optional[bool],
|
||||||
wait_timeout: Optional[object],
|
wait_timeout: Optional[object],
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ from lancedb.embeddings.registry import EmbeddingFunctionRegistry
|
|||||||
|
|
||||||
from lancedb.common import data_to_reader, sanitize_uri, validate_schema
|
from lancedb.common import data_to_reader, sanitize_uri, validate_schema
|
||||||
from lancedb.background_loop import LOOP
|
from lancedb.background_loop import LOOP
|
||||||
|
from lance_namespace import (
|
||||||
|
ListNamespacesResponse,
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
|
ListTablesResponse,
|
||||||
|
)
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from ._lancedb import connect as lancedb_connect # type: ignore
|
from ._lancedb import connect as lancedb_connect # type: ignore
|
||||||
@@ -48,6 +55,12 @@ if TYPE_CHECKING:
|
|||||||
from .io import StorageOptionsProvider
|
from .io import StorageOptionsProvider
|
||||||
from ._lancedb import Session
|
from ._lancedb import Session
|
||||||
|
|
||||||
|
from .namespace_utils import (
|
||||||
|
_normalize_create_namespace_mode,
|
||||||
|
_normalize_drop_namespace_mode,
|
||||||
|
_normalize_drop_namespace_behavior,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DBConnection(EnforceOverrides):
|
class DBConnection(EnforceOverrides):
|
||||||
"""An active LanceDB connection interface."""
|
"""An active LanceDB connection interface."""
|
||||||
@@ -56,8 +69,8 @@ class DBConnection(EnforceOverrides):
|
|||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: Optional[int] = None,
|
||||||
) -> Iterable[str]:
|
) -> ListNamespacesResponse:
|
||||||
"""List immediate child namespace names in the given namespace.
|
"""List immediate child namespace names in the given namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@@ -66,43 +79,119 @@ class DBConnection(EnforceOverrides):
|
|||||||
The parent namespace to list namespaces in.
|
The parent namespace to list namespaces in.
|
||||||
Empty list represents root namespace.
|
Empty list represents root namespace.
|
||||||
page_token: str, optional
|
page_token: str, optional
|
||||||
The token to use for pagination. If not present, start from the beginning.
|
Token for pagination. Use the token from a previous response
|
||||||
limit: int, default 10
|
to get the next page of results.
|
||||||
The size of the page to return.
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Iterable of str
|
ListNamespacesResponse
|
||||||
List of immediate child namespace names
|
Response containing namespace names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
return []
|
return ListNamespacesResponse(namespaces=[], page_token=None)
|
||||||
|
|
||||||
def create_namespace(self, namespace: List[str]) -> None:
|
def create_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse:
|
||||||
"""Create a new namespace.
|
"""Create a new namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to create.
|
The namespace identifier to create.
|
||||||
|
mode: str, optional
|
||||||
|
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
||||||
|
or "overwrite" (replace if exists). Case insensitive.
|
||||||
|
properties: Dict[str, str], optional
|
||||||
|
Properties to set on the namespace.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
CreateNamespaceResponse
|
||||||
|
Response containing the properties of the created namespace.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Namespace operations are not supported for this connection type"
|
"Namespace operations are not supported for this connection type"
|
||||||
)
|
)
|
||||||
|
|
||||||
def drop_namespace(self, namespace: List[str]) -> None:
|
def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse:
|
||||||
"""Drop a namespace.
|
"""Drop a namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to drop.
|
The namespace identifier to drop.
|
||||||
|
mode: str, optional
|
||||||
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
|
behavior: str, optional
|
||||||
|
Whether to restrict drop if not empty ("RESTRICT") or cascade ("CASCADE").
|
||||||
|
Case insensitive.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DropNamespaceResponse
|
||||||
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Namespace operations are not supported for this connection type"
|
"Namespace operations are not supported for this connection type"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def describe_namespace(self, namespace: List[str]) -> DescribeNamespaceResponse:
|
||||||
|
"""Describe a namespace.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str]
|
||||||
|
The namespace identifier to describe.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DescribeNamespaceResponse
|
||||||
|
Response containing the namespace properties.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(
|
||||||
|
"Namespace operations are not supported for this connection type"
|
||||||
|
)
|
||||||
|
|
||||||
|
def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse:
|
||||||
|
"""List all tables in this database with pagination support.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str], optional
|
||||||
|
The namespace to list tables in.
|
||||||
|
None or empty list represents root namespace.
|
||||||
|
page_token: str, optional
|
||||||
|
Token for pagination. Use the token from a previous response
|
||||||
|
to get the next page of results.
|
||||||
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ListTablesResponse
|
||||||
|
Response containing table names and optional page_token for pagination.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(
|
||||||
|
"list_tables is not supported for this connection type"
|
||||||
|
)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def table_names(
|
def table_names(
|
||||||
self,
|
self,
|
||||||
@@ -557,8 +646,8 @@ class LanceDBConnection(DBConnection):
|
|||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: Optional[int] = None,
|
||||||
) -> Iterable[str]:
|
) -> ListNamespacesResponse:
|
||||||
"""List immediate child namespace names in the given namespace.
|
"""List immediate child namespace names in the given namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@@ -567,14 +656,15 @@ class LanceDBConnection(DBConnection):
|
|||||||
The parent namespace to list namespaces in.
|
The parent namespace to list namespaces in.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
page_token: str, optional
|
page_token: str, optional
|
||||||
The token to use for pagination. If not present, start from the beginning.
|
Token for pagination. Use the token from a previous response
|
||||||
limit: int, default 10
|
to get the next page of results.
|
||||||
The size of the page to return.
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Iterable of str
|
ListNamespacesResponse
|
||||||
List of immediate child namespace names
|
Response containing namespace names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
@@ -585,26 +675,111 @@ class LanceDBConnection(DBConnection):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def create_namespace(self, namespace: List[str]) -> None:
|
def create_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse:
|
||||||
"""Create a new namespace.
|
"""Create a new namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to create.
|
The namespace identifier to create.
|
||||||
|
mode: str, optional
|
||||||
|
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
||||||
|
or "overwrite" (replace if exists). Case insensitive.
|
||||||
|
properties: Dict[str, str], optional
|
||||||
|
Properties to set on the namespace.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
CreateNamespaceResponse
|
||||||
|
Response containing the properties of the created namespace.
|
||||||
"""
|
"""
|
||||||
LOOP.run(self._conn.create_namespace(namespace=namespace))
|
return LOOP.run(
|
||||||
|
self._conn.create_namespace(
|
||||||
|
namespace=namespace, mode=mode, properties=properties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def drop_namespace(self, namespace: List[str]) -> None:
|
def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse:
|
||||||
"""Drop a namespace.
|
"""Drop a namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to drop.
|
The namespace identifier to drop.
|
||||||
|
mode: str, optional
|
||||||
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
|
behavior: str, optional
|
||||||
|
Whether to restrict drop if not empty ("RESTRICT") or cascade ("CASCADE").
|
||||||
|
Case insensitive.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DropNamespaceResponse
|
||||||
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
return LOOP.run(self._conn.drop_namespace(namespace=namespace))
|
return LOOP.run(
|
||||||
|
self._conn.drop_namespace(namespace=namespace, mode=mode, behavior=behavior)
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def describe_namespace(self, namespace: List[str]) -> DescribeNamespaceResponse:
|
||||||
|
"""Describe a namespace.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str]
|
||||||
|
The namespace identifier to describe.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DescribeNamespaceResponse
|
||||||
|
Response containing the namespace properties.
|
||||||
|
"""
|
||||||
|
return LOOP.run(self._conn.describe_namespace(namespace=namespace))
|
||||||
|
|
||||||
|
@override
|
||||||
|
def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse:
|
||||||
|
"""List all tables in this database with pagination support.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str], optional
|
||||||
|
The namespace to list tables in.
|
||||||
|
None or empty list represents root namespace.
|
||||||
|
page_token: str, optional
|
||||||
|
Token for pagination. Use the token from a previous response
|
||||||
|
to get the next page of results.
|
||||||
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ListTablesResponse
|
||||||
|
Response containing table names and optional page_token for pagination.
|
||||||
|
"""
|
||||||
|
if namespace is None:
|
||||||
|
namespace = []
|
||||||
|
return LOOP.run(
|
||||||
|
self._conn.list_tables(
|
||||||
|
namespace=namespace, page_token=page_token, limit=limit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def table_names(
|
def table_names(
|
||||||
@@ -616,6 +791,9 @@ class LanceDBConnection(DBConnection):
|
|||||||
) -> Iterable[str]:
|
) -> Iterable[str]:
|
||||||
"""Get the names of all tables in the database. The names are sorted.
|
"""Get the names of all tables in the database. The names are sorted.
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
Use :meth:`list_tables` instead, which provides proper pagination support.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str], optional
|
namespace: List[str], optional
|
||||||
@@ -630,6 +808,13 @@ class LanceDBConnection(DBConnection):
|
|||||||
Iterator of str.
|
Iterator of str.
|
||||||
A list of table names.
|
A list of table names.
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"table_names() is deprecated, use list_tables() instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
@@ -944,8 +1129,8 @@ class AsyncConnection(object):
|
|||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: Optional[int] = None,
|
||||||
) -> Iterable[str]:
|
) -> ListNamespacesResponse:
|
||||||
"""List immediate child namespace names in the given namespace.
|
"""List immediate child namespace names in the given namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@@ -955,39 +1140,128 @@ class AsyncConnection(object):
|
|||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
page_token: str, optional
|
page_token: str, optional
|
||||||
The token to use for pagination. If not present, start from the beginning.
|
The token to use for pagination. If not present, start from the beginning.
|
||||||
limit: int, default 10
|
limit: int, optional
|
||||||
The size of the page to return.
|
The maximum number of results to return.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Iterable of str
|
ListNamespacesResponse
|
||||||
List of immediate child namespace names (not full paths)
|
Response containing namespace names and optional pagination token
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
return await self._inner.list_namespaces(
|
result = await self._inner.list_namespaces(
|
||||||
namespace=namespace, page_token=page_token, limit=limit
|
namespace=namespace, page_token=page_token, limit=limit
|
||||||
)
|
)
|
||||||
|
return ListNamespacesResponse(**result)
|
||||||
|
|
||||||
async def create_namespace(self, namespace: List[str]) -> None:
|
async def create_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse:
|
||||||
"""Create a new namespace.
|
"""Create a new namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to create.
|
The namespace identifier to create.
|
||||||
"""
|
mode: str, optional
|
||||||
await self._inner.create_namespace(namespace)
|
Creation mode - "create", "exist_ok", or "overwrite". Case insensitive.
|
||||||
|
properties: Dict[str, str], optional
|
||||||
|
Properties to associate with the namespace
|
||||||
|
|
||||||
async def drop_namespace(self, namespace: List[str]) -> None:
|
Returns
|
||||||
|
-------
|
||||||
|
CreateNamespaceResponse
|
||||||
|
Response containing namespace properties
|
||||||
|
"""
|
||||||
|
result = await self._inner.create_namespace(
|
||||||
|
namespace,
|
||||||
|
mode=_normalize_create_namespace_mode(mode),
|
||||||
|
properties=properties,
|
||||||
|
)
|
||||||
|
return CreateNamespaceResponse(**result)
|
||||||
|
|
||||||
|
async def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse:
|
||||||
"""Drop a namespace.
|
"""Drop a namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to drop.
|
The namespace identifier to drop.
|
||||||
|
mode: str, optional
|
||||||
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
|
behavior: str, optional
|
||||||
|
Whether to restrict drop if not empty ("RESTRICT") or cascade ("CASCADE").
|
||||||
|
Case insensitive.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DropNamespaceResponse
|
||||||
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
await self._inner.drop_namespace(namespace)
|
result = await self._inner.drop_namespace(
|
||||||
|
namespace,
|
||||||
|
mode=_normalize_drop_namespace_mode(mode),
|
||||||
|
behavior=_normalize_drop_namespace_behavior(behavior),
|
||||||
|
)
|
||||||
|
return DropNamespaceResponse(**result)
|
||||||
|
|
||||||
|
async def describe_namespace(
|
||||||
|
self, namespace: List[str]
|
||||||
|
) -> DescribeNamespaceResponse:
|
||||||
|
"""Describe a namespace.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str]
|
||||||
|
The namespace identifier to describe.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DescribeNamespaceResponse
|
||||||
|
Response containing the namespace properties.
|
||||||
|
"""
|
||||||
|
result = await self._inner.describe_namespace(namespace)
|
||||||
|
return DescribeNamespaceResponse(**result)
|
||||||
|
|
||||||
|
async def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse:
|
||||||
|
"""List all tables in this database with pagination support.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str], optional
|
||||||
|
The namespace to list tables in.
|
||||||
|
None or empty list represents root namespace.
|
||||||
|
page_token: str, optional
|
||||||
|
Token for pagination. Use the token from a previous response
|
||||||
|
to get the next page of results.
|
||||||
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ListTablesResponse
|
||||||
|
Response containing table names and optional page_token for pagination.
|
||||||
|
"""
|
||||||
|
if namespace is None:
|
||||||
|
namespace = []
|
||||||
|
result = await self._inner.list_tables(
|
||||||
|
namespace=namespace, page_token=page_token, limit=limit
|
||||||
|
)
|
||||||
|
return ListTablesResponse(**result)
|
||||||
|
|
||||||
async def table_names(
|
async def table_names(
|
||||||
self,
|
self,
|
||||||
@@ -998,6 +1272,9 @@ class AsyncConnection(object):
|
|||||||
) -> Iterable[str]:
|
) -> Iterable[str]:
|
||||||
"""List all tables in this database, in sorted order
|
"""List all tables in this database, in sorted order
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
Use :meth:`list_tables` instead, which provides proper pagination support.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str], optional
|
namespace: List[str], optional
|
||||||
@@ -1016,6 +1293,13 @@ class AsyncConnection(object):
|
|||||||
-------
|
-------
|
||||||
Iterable of str
|
Iterable of str
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"table_names() is deprecated, use list_tables() instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
return await self._inner.table_names(
|
return await self._inner.table_names(
|
||||||
|
|||||||
@@ -376,6 +376,11 @@ class HnswSq:
|
|||||||
target_partition_size: Optional[int] = None
|
target_partition_size: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards-compatible aliases
|
||||||
|
IvfHnswPq = HnswPq
|
||||||
|
IvfHnswSq = HnswSq
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class IvfFlat:
|
class IvfFlat:
|
||||||
"""Describes an IVF Flat Index
|
"""Describes an IVF Flat Index
|
||||||
@@ -475,6 +480,36 @@ class IvfFlat:
|
|||||||
target_partition_size: Optional[int] = None
|
target_partition_size: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IvfSq:
|
||||||
|
"""Describes an IVF Scalar Quantization (SQ) index.
|
||||||
|
|
||||||
|
This index applies scalar quantization to compress vectors and organizes the
|
||||||
|
quantized vectors into IVF partitions. It offers a balance between search
|
||||||
|
speed and storage efficiency while keeping good recall.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
distance_type: str, default "l2"
|
||||||
|
The distance metric used to train and search the index. Supported values
|
||||||
|
are "l2", "cosine", and "dot".
|
||||||
|
num_partitions: int, default sqrt(num_rows)
|
||||||
|
Number of IVF partitions to create.
|
||||||
|
max_iterations: int, default 50
|
||||||
|
Maximum iterations for kmeans during partition training.
|
||||||
|
sample_rate: int, default 256
|
||||||
|
Controls the number of training vectors: sample_rate * num_partitions.
|
||||||
|
target_partition_size: int, optional
|
||||||
|
Target size for each partition; adjusts the balance between speed and accuracy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
distance_type: Literal["l2", "cosine", "dot"] = "l2"
|
||||||
|
num_partitions: Optional[int] = None
|
||||||
|
max_iterations: int = 50
|
||||||
|
sample_rate: int = 256
|
||||||
|
target_partition_size: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class IvfPq:
|
class IvfPq:
|
||||||
"""Describes an IVF PQ Index
|
"""Describes an IVF PQ Index
|
||||||
@@ -661,6 +696,9 @@ class IvfRq:
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"BTree",
|
"BTree",
|
||||||
"IvfPq",
|
"IvfPq",
|
||||||
|
"IvfHnswPq",
|
||||||
|
"IvfHnswSq",
|
||||||
|
"IvfSq",
|
||||||
"IvfRq",
|
"IvfRq",
|
||||||
"IvfFlat",
|
"IvfFlat",
|
||||||
"HnswPq",
|
"HnswPq",
|
||||||
|
|||||||
@@ -23,7 +23,29 @@ from datetime import timedelta
|
|||||||
import pyarrow as pa
|
import pyarrow as pa
|
||||||
|
|
||||||
from lancedb.db import DBConnection, LanceDBConnection
|
from lancedb.db import DBConnection, LanceDBConnection
|
||||||
|
from lancedb.namespace_utils import (
|
||||||
|
_normalize_create_namespace_mode,
|
||||||
|
_normalize_drop_namespace_mode,
|
||||||
|
_normalize_drop_namespace_behavior,
|
||||||
|
)
|
||||||
from lancedb.io import StorageOptionsProvider
|
from lancedb.io import StorageOptionsProvider
|
||||||
|
from lance_namespace import (
|
||||||
|
LanceNamespace,
|
||||||
|
connect as namespace_connect,
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
ListNamespacesResponse,
|
||||||
|
ListTablesResponse,
|
||||||
|
ListTablesRequest,
|
||||||
|
DescribeTableRequest,
|
||||||
|
DescribeNamespaceRequest,
|
||||||
|
DropTableRequest,
|
||||||
|
ListNamespacesRequest,
|
||||||
|
CreateNamespaceRequest,
|
||||||
|
DropNamespaceRequest,
|
||||||
|
CreateEmptyTableRequest,
|
||||||
|
)
|
||||||
from lancedb.table import AsyncTable, LanceTable, Table
|
from lancedb.table import AsyncTable, LanceTable, Table
|
||||||
from lancedb.util import validate_table_name
|
from lancedb.util import validate_table_name
|
||||||
from lancedb.common import DATA
|
from lancedb.common import DATA
|
||||||
@@ -31,19 +53,9 @@ from lancedb.pydantic import LanceModel
|
|||||||
from lancedb.embeddings import EmbeddingFunctionConfig
|
from lancedb.embeddings import EmbeddingFunctionConfig
|
||||||
from ._lancedb import Session
|
from ._lancedb import Session
|
||||||
|
|
||||||
from lance_namespace import LanceNamespace, connect as namespace_connect
|
from lance_namespace_urllib3_client.models.json_arrow_schema import JsonArrowSchema
|
||||||
from lance_namespace_urllib3_client.models import (
|
from lance_namespace_urllib3_client.models.json_arrow_field import JsonArrowField
|
||||||
ListTablesRequest,
|
from lance_namespace_urllib3_client.models.json_arrow_data_type import JsonArrowDataType
|
||||||
DescribeTableRequest,
|
|
||||||
DropTableRequest,
|
|
||||||
ListNamespacesRequest,
|
|
||||||
CreateNamespaceRequest,
|
|
||||||
DropNamespaceRequest,
|
|
||||||
CreateEmptyTableRequest,
|
|
||||||
JsonArrowSchema,
|
|
||||||
JsonArrowField,
|
|
||||||
JsonArrowDataType,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _convert_pyarrow_type_to_json(arrow_type: pa.DataType) -> JsonArrowDataType:
|
def _convert_pyarrow_type_to_json(arrow_type: pa.DataType) -> JsonArrowDataType:
|
||||||
@@ -241,6 +253,19 @@ class LanceNamespaceDBConnection(DBConnection):
|
|||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
) -> Iterable[str]:
|
) -> Iterable[str]:
|
||||||
|
"""
|
||||||
|
List table names in the database.
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
Use :meth:`list_tables` instead, which provides proper pagination support.
|
||||||
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"table_names() is deprecated, use list_tables() instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit)
|
request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit)
|
||||||
@@ -433,8 +458,8 @@ class LanceNamespaceDBConnection(DBConnection):
|
|||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: Optional[int] = None,
|
||||||
) -> Iterable[str]:
|
) -> ListNamespacesResponse:
|
||||||
"""
|
"""
|
||||||
List child namespaces under the given namespace.
|
List child namespaces under the given namespace.
|
||||||
|
|
||||||
@@ -444,14 +469,15 @@ class LanceNamespaceDBConnection(DBConnection):
|
|||||||
The parent namespace to list children from.
|
The parent namespace to list children from.
|
||||||
If None, lists root-level namespaces.
|
If None, lists root-level namespaces.
|
||||||
page_token : Optional[str]
|
page_token : Optional[str]
|
||||||
Pagination token for listing results.
|
Token for pagination. Use the token from a previous response
|
||||||
limit : int
|
to get the next page of results.
|
||||||
|
limit : int, optional
|
||||||
Maximum number of namespaces to return.
|
Maximum number of namespaces to return.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Iterable[str]
|
ListNamespacesResponse
|
||||||
Names of child namespaces.
|
Response containing namespace names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
@@ -459,10 +485,18 @@ class LanceNamespaceDBConnection(DBConnection):
|
|||||||
id=namespace, page_token=page_token, limit=limit
|
id=namespace, page_token=page_token, limit=limit
|
||||||
)
|
)
|
||||||
response = self._ns.list_namespaces(request)
|
response = self._ns.list_namespaces(request)
|
||||||
return response.namespaces if response.namespaces else []
|
return ListNamespacesResponse(
|
||||||
|
namespaces=response.namespaces if response.namespaces else [],
|
||||||
|
page_token=response.page_token,
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def create_namespace(self, namespace: List[str]) -> None:
|
def create_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse:
|
||||||
"""
|
"""
|
||||||
Create a new namespace.
|
Create a new namespace.
|
||||||
|
|
||||||
@@ -470,12 +504,34 @@ class LanceNamespaceDBConnection(DBConnection):
|
|||||||
----------
|
----------
|
||||||
namespace : List[str]
|
namespace : List[str]
|
||||||
The namespace path to create.
|
The namespace path to create.
|
||||||
|
mode : str, optional
|
||||||
|
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
||||||
|
or "overwrite" (replace if exists). Case insensitive.
|
||||||
|
properties : Dict[str, str], optional
|
||||||
|
Properties to set on the namespace.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
CreateNamespaceResponse
|
||||||
|
Response containing the properties of the created namespace.
|
||||||
"""
|
"""
|
||||||
request = CreateNamespaceRequest(id=namespace)
|
request = CreateNamespaceRequest(
|
||||||
self._ns.create_namespace(request)
|
id=namespace,
|
||||||
|
mode=_normalize_create_namespace_mode(mode),
|
||||||
|
properties=properties,
|
||||||
|
)
|
||||||
|
response = self._ns.create_namespace(request)
|
||||||
|
return CreateNamespaceResponse(
|
||||||
|
properties=response.properties if hasattr(response, "properties") else None
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def drop_namespace(self, namespace: List[str]) -> None:
|
def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse:
|
||||||
"""
|
"""
|
||||||
Drop a namespace.
|
Drop a namespace.
|
||||||
|
|
||||||
@@ -483,9 +539,87 @@ class LanceNamespaceDBConnection(DBConnection):
|
|||||||
----------
|
----------
|
||||||
namespace : List[str]
|
namespace : List[str]
|
||||||
The namespace path to drop.
|
The namespace path to drop.
|
||||||
|
mode : str, optional
|
||||||
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
|
behavior : str, optional
|
||||||
|
Whether to restrict drop if not empty ("RESTRICT") or cascade ("CASCADE").
|
||||||
|
Case insensitive.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DropNamespaceResponse
|
||||||
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
request = DropNamespaceRequest(id=namespace)
|
request = DropNamespaceRequest(
|
||||||
self._ns.drop_namespace(request)
|
id=namespace,
|
||||||
|
mode=_normalize_drop_namespace_mode(mode),
|
||||||
|
behavior=_normalize_drop_namespace_behavior(behavior),
|
||||||
|
)
|
||||||
|
response = self._ns.drop_namespace(request)
|
||||||
|
return DropNamespaceResponse(
|
||||||
|
properties=(
|
||||||
|
response.properties if hasattr(response, "properties") else None
|
||||||
|
),
|
||||||
|
transaction_id=(
|
||||||
|
response.transaction_id if hasattr(response, "transaction_id") else None
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def describe_namespace(self, namespace: List[str]) -> DescribeNamespaceResponse:
|
||||||
|
"""
|
||||||
|
Describe a namespace.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace : List[str]
|
||||||
|
The namespace identifier to describe.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DescribeNamespaceResponse
|
||||||
|
Response containing the namespace properties.
|
||||||
|
"""
|
||||||
|
request = DescribeNamespaceRequest(id=namespace)
|
||||||
|
response = self._ns.describe_namespace(request)
|
||||||
|
return DescribeNamespaceResponse(
|
||||||
|
properties=response.properties if hasattr(response, "properties") else None
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse:
|
||||||
|
"""
|
||||||
|
List all tables in this database with pagination support.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace : List[str], optional
|
||||||
|
The namespace to list tables in.
|
||||||
|
None or empty list represents root namespace.
|
||||||
|
page_token : str, optional
|
||||||
|
Token for pagination. Use the token from a previous response
|
||||||
|
to get the next page of results.
|
||||||
|
limit : int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ListTablesResponse
|
||||||
|
Response containing table names and optional page_token for pagination.
|
||||||
|
"""
|
||||||
|
if namespace is None:
|
||||||
|
namespace = []
|
||||||
|
request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit)
|
||||||
|
response = self._ns.list_tables(request)
|
||||||
|
return ListTablesResponse(
|
||||||
|
tables=response.tables if response.tables else [],
|
||||||
|
page_token=response.page_token,
|
||||||
|
)
|
||||||
|
|
||||||
def _lance_table_from_uri(
|
def _lance_table_from_uri(
|
||||||
self,
|
self,
|
||||||
@@ -563,7 +697,19 @@ class AsyncLanceNamespaceDBConnection:
|
|||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
) -> Iterable[str]:
|
) -> Iterable[str]:
|
||||||
"""List table names in the namespace."""
|
"""
|
||||||
|
List table names in the namespace.
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
Use :meth:`list_tables` instead, which provides proper pagination support.
|
||||||
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"table_names() is deprecated, use list_tables() instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit)
|
request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit)
|
||||||
@@ -771,8 +917,8 @@ class AsyncLanceNamespaceDBConnection:
|
|||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: Optional[int] = None,
|
||||||
) -> Iterable[str]:
|
) -> ListNamespacesResponse:
|
||||||
"""
|
"""
|
||||||
List child namespaces under the given namespace.
|
List child namespaces under the given namespace.
|
||||||
|
|
||||||
@@ -782,14 +928,15 @@ class AsyncLanceNamespaceDBConnection:
|
|||||||
The parent namespace to list children from.
|
The parent namespace to list children from.
|
||||||
If None, lists root-level namespaces.
|
If None, lists root-level namespaces.
|
||||||
page_token : Optional[str]
|
page_token : Optional[str]
|
||||||
Pagination token for listing results.
|
Token for pagination. Use the token from a previous response
|
||||||
limit : int
|
to get the next page of results.
|
||||||
|
limit : int, optional
|
||||||
Maximum number of namespaces to return.
|
Maximum number of namespaces to return.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Iterable[str]
|
ListNamespacesResponse
|
||||||
Names of child namespaces.
|
Response containing namespace names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
@@ -797,9 +944,17 @@ class AsyncLanceNamespaceDBConnection:
|
|||||||
id=namespace, page_token=page_token, limit=limit
|
id=namespace, page_token=page_token, limit=limit
|
||||||
)
|
)
|
||||||
response = self._ns.list_namespaces(request)
|
response = self._ns.list_namespaces(request)
|
||||||
return response.namespaces if response.namespaces else []
|
return ListNamespacesResponse(
|
||||||
|
namespaces=response.namespaces if response.namespaces else [],
|
||||||
|
page_token=response.page_token,
|
||||||
|
)
|
||||||
|
|
||||||
async def create_namespace(self, namespace: List[str]) -> None:
|
async def create_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse:
|
||||||
"""
|
"""
|
||||||
Create a new namespace.
|
Create a new namespace.
|
||||||
|
|
||||||
@@ -807,11 +962,33 @@ class AsyncLanceNamespaceDBConnection:
|
|||||||
----------
|
----------
|
||||||
namespace : List[str]
|
namespace : List[str]
|
||||||
The namespace path to create.
|
The namespace path to create.
|
||||||
"""
|
mode : str, optional
|
||||||
request = CreateNamespaceRequest(id=namespace)
|
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
||||||
self._ns.create_namespace(request)
|
or "overwrite" (replace if exists). Case insensitive.
|
||||||
|
properties : Dict[str, str], optional
|
||||||
|
Properties to set on the namespace.
|
||||||
|
|
||||||
async def drop_namespace(self, namespace: List[str]) -> None:
|
Returns
|
||||||
|
-------
|
||||||
|
CreateNamespaceResponse
|
||||||
|
Response containing the properties of the created namespace.
|
||||||
|
"""
|
||||||
|
request = CreateNamespaceRequest(
|
||||||
|
id=namespace,
|
||||||
|
mode=_normalize_create_namespace_mode(mode),
|
||||||
|
properties=properties,
|
||||||
|
)
|
||||||
|
response = self._ns.create_namespace(request)
|
||||||
|
return CreateNamespaceResponse(
|
||||||
|
properties=response.properties if hasattr(response, "properties") else None
|
||||||
|
)
|
||||||
|
|
||||||
|
async def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse:
|
||||||
"""
|
"""
|
||||||
Drop a namespace.
|
Drop a namespace.
|
||||||
|
|
||||||
@@ -819,9 +996,87 @@ class AsyncLanceNamespaceDBConnection:
|
|||||||
----------
|
----------
|
||||||
namespace : List[str]
|
namespace : List[str]
|
||||||
The namespace path to drop.
|
The namespace path to drop.
|
||||||
|
mode : str, optional
|
||||||
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
|
behavior : str, optional
|
||||||
|
Whether to restrict drop if not empty ("RESTRICT") or cascade ("CASCADE").
|
||||||
|
Case insensitive.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DropNamespaceResponse
|
||||||
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
request = DropNamespaceRequest(id=namespace)
|
request = DropNamespaceRequest(
|
||||||
self._ns.drop_namespace(request)
|
id=namespace,
|
||||||
|
mode=_normalize_drop_namespace_mode(mode),
|
||||||
|
behavior=_normalize_drop_namespace_behavior(behavior),
|
||||||
|
)
|
||||||
|
response = self._ns.drop_namespace(request)
|
||||||
|
return DropNamespaceResponse(
|
||||||
|
properties=(
|
||||||
|
response.properties if hasattr(response, "properties") else None
|
||||||
|
),
|
||||||
|
transaction_id=(
|
||||||
|
response.transaction_id if hasattr(response, "transaction_id") else None
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def describe_namespace(
|
||||||
|
self, namespace: List[str]
|
||||||
|
) -> DescribeNamespaceResponse:
|
||||||
|
"""
|
||||||
|
Describe a namespace.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace : List[str]
|
||||||
|
The namespace identifier to describe.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DescribeNamespaceResponse
|
||||||
|
Response containing the namespace properties.
|
||||||
|
"""
|
||||||
|
request = DescribeNamespaceRequest(id=namespace)
|
||||||
|
response = self._ns.describe_namespace(request)
|
||||||
|
return DescribeNamespaceResponse(
|
||||||
|
properties=response.properties if hasattr(response, "properties") else None
|
||||||
|
)
|
||||||
|
|
||||||
|
async def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse:
|
||||||
|
"""
|
||||||
|
List all tables in this database with pagination support.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace : List[str], optional
|
||||||
|
The namespace to list tables in.
|
||||||
|
None or empty list represents root namespace.
|
||||||
|
page_token : str, optional
|
||||||
|
Token for pagination. Use the token from a previous response
|
||||||
|
to get the next page of results.
|
||||||
|
limit : int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ListTablesResponse
|
||||||
|
Response containing table names and optional page_token for pagination.
|
||||||
|
"""
|
||||||
|
if namespace is None:
|
||||||
|
namespace = []
|
||||||
|
request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit)
|
||||||
|
response = self._ns.list_tables(request)
|
||||||
|
return ListTablesResponse(
|
||||||
|
tables=response.tables if response.tables else [],
|
||||||
|
page_token=response.page_token,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def connect_namespace(
|
def connect_namespace(
|
||||||
|
|||||||
27
python/python/lancedb/namespace_utils.py
Normal file
27
python/python/lancedb/namespace_utils.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
|
"""Utility functions for namespace operations."""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_create_namespace_mode(mode: Optional[str]) -> Optional[str]:
|
||||||
|
"""Normalize create namespace mode to lowercase (API expects lowercase)."""
|
||||||
|
if mode is None:
|
||||||
|
return None
|
||||||
|
return mode.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_drop_namespace_mode(mode: Optional[str]) -> Optional[str]:
|
||||||
|
"""Normalize drop namespace mode to uppercase (API expects uppercase)."""
|
||||||
|
if mode is None:
|
||||||
|
return None
|
||||||
|
return mode.upper()
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_drop_namespace_behavior(behavior: Optional[str]) -> Optional[str]:
|
||||||
|
"""Normalize drop namespace behavior to uppercase (API expects uppercase)."""
|
||||||
|
if behavior is None:
|
||||||
|
return None
|
||||||
|
return behavior.upper()
|
||||||
@@ -23,6 +23,13 @@ import pyarrow as pa
|
|||||||
from ..common import DATA
|
from ..common import DATA
|
||||||
from ..db import DBConnection, LOOP
|
from ..db import DBConnection, LOOP
|
||||||
from ..embeddings import EmbeddingFunctionConfig
|
from ..embeddings import EmbeddingFunctionConfig
|
||||||
|
from lance_namespace import (
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
ListNamespacesResponse,
|
||||||
|
ListTablesResponse,
|
||||||
|
)
|
||||||
from ..pydantic import LanceModel
|
from ..pydantic import LanceModel
|
||||||
from ..table import Table
|
from ..table import Table
|
||||||
from ..util import validate_table_name
|
from ..util import validate_table_name
|
||||||
@@ -106,8 +113,8 @@ class RemoteDBConnection(DBConnection):
|
|||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: Optional[int] = None,
|
||||||
) -> Iterable[str]:
|
) -> ListNamespacesResponse:
|
||||||
"""List immediate child namespace names in the given namespace.
|
"""List immediate child namespace names in the given namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@@ -116,14 +123,15 @@ class RemoteDBConnection(DBConnection):
|
|||||||
The parent namespace to list namespaces in.
|
The parent namespace to list namespaces in.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
page_token: str, optional
|
page_token: str, optional
|
||||||
The token to use for pagination. If not present, start from the beginning.
|
Token for pagination. Use the token from a previous response
|
||||||
limit: int, default 10
|
to get the next page of results.
|
||||||
The size of the page to return.
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Iterable of str
|
ListNamespacesResponse
|
||||||
List of immediate child namespace names
|
Response containing namespace names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
@@ -134,26 +142,111 @@ class RemoteDBConnection(DBConnection):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def create_namespace(self, namespace: List[str]) -> None:
|
def create_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
properties: Optional[Dict[str, str]] = None,
|
||||||
|
) -> CreateNamespaceResponse:
|
||||||
"""Create a new namespace.
|
"""Create a new namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to create.
|
The namespace identifier to create.
|
||||||
|
mode: str, optional
|
||||||
|
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
||||||
|
or "overwrite" (replace if exists). Case insensitive.
|
||||||
|
properties: Dict[str, str], optional
|
||||||
|
Properties to set on the namespace.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
CreateNamespaceResponse
|
||||||
|
Response containing the properties of the created namespace.
|
||||||
"""
|
"""
|
||||||
LOOP.run(self._conn.create_namespace(namespace=namespace))
|
return LOOP.run(
|
||||||
|
self._conn.create_namespace(
|
||||||
|
namespace=namespace, mode=mode, properties=properties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def drop_namespace(self, namespace: List[str]) -> None:
|
def drop_namespace(
|
||||||
|
self,
|
||||||
|
namespace: List[str],
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
behavior: Optional[str] = None,
|
||||||
|
) -> DropNamespaceResponse:
|
||||||
"""Drop a namespace.
|
"""Drop a namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace: List[str]
|
||||||
The namespace identifier to drop.
|
The namespace identifier to drop.
|
||||||
|
mode: str, optional
|
||||||
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
|
behavior: str, optional
|
||||||
|
Whether to restrict drop if not empty ("RESTRICT") or cascade ("CASCADE").
|
||||||
|
Case insensitive.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DropNamespaceResponse
|
||||||
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
return LOOP.run(self._conn.drop_namespace(namespace=namespace))
|
return LOOP.run(
|
||||||
|
self._conn.drop_namespace(namespace=namespace, mode=mode, behavior=behavior)
|
||||||
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def describe_namespace(self, namespace: List[str]) -> DescribeNamespaceResponse:
|
||||||
|
"""Describe a namespace.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str]
|
||||||
|
The namespace identifier to describe.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DescribeNamespaceResponse
|
||||||
|
Response containing the namespace properties.
|
||||||
|
"""
|
||||||
|
return LOOP.run(self._conn.describe_namespace(namespace=namespace))
|
||||||
|
|
||||||
|
@override
|
||||||
|
def list_tables(
|
||||||
|
self,
|
||||||
|
namespace: Optional[List[str]] = None,
|
||||||
|
page_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
) -> ListTablesResponse:
|
||||||
|
"""List all tables in this database with pagination support.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace: List[str], optional
|
||||||
|
The namespace to list tables in.
|
||||||
|
None or empty list represents root namespace.
|
||||||
|
page_token: str, optional
|
||||||
|
Token for pagination. Use the token from a previous response
|
||||||
|
to get the next page of results.
|
||||||
|
limit: int, optional
|
||||||
|
The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
ListTablesResponse
|
||||||
|
Response containing table names and optional page_token for pagination.
|
||||||
|
"""
|
||||||
|
if namespace is None:
|
||||||
|
namespace = []
|
||||||
|
return LOOP.run(
|
||||||
|
self._conn.list_tables(
|
||||||
|
namespace=namespace, page_token=page_token, limit=limit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def table_names(
|
def table_names(
|
||||||
@@ -165,6 +258,9 @@ class RemoteDBConnection(DBConnection):
|
|||||||
) -> Iterable[str]:
|
) -> Iterable[str]:
|
||||||
"""List the names of all tables in the database.
|
"""List the names of all tables in the database.
|
||||||
|
|
||||||
|
.. deprecated::
|
||||||
|
Use :meth:`list_tables` instead, which provides proper pagination support.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str], default []
|
namespace: List[str], default []
|
||||||
@@ -179,6 +275,13 @@ class RemoteDBConnection(DBConnection):
|
|||||||
-------
|
-------
|
||||||
An iterator of table names.
|
An iterator of table names.
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"table_names() is deprecated, use list_tables() instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
if namespace is None:
|
if namespace is None:
|
||||||
namespace = []
|
namespace = []
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
|
|||||||
@@ -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, HnswSq, IvfFlat, IvfPq, LabelList
|
from lancedb.index import FTS, BTree, Bitmap, HnswSq, IvfFlat, IvfPq, IvfSq, LabelList
|
||||||
from lancedb.remote.db import LOOP
|
from lancedb.remote.db import LOOP
|
||||||
import pyarrow as pa
|
import pyarrow as pa
|
||||||
|
|
||||||
@@ -265,6 +265,8 @@ class RemoteTable(Table):
|
|||||||
num_sub_vectors=num_sub_vectors,
|
num_sub_vectors=num_sub_vectors,
|
||||||
num_bits=num_bits,
|
num_bits=num_bits,
|
||||||
)
|
)
|
||||||
|
elif index_type == "IVF_SQ":
|
||||||
|
config = IvfSq(distance_type=metric, num_partitions=num_partitions)
|
||||||
elif index_type == "IVF_HNSW_PQ":
|
elif index_type == "IVF_HNSW_PQ":
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"IVF_HNSW_PQ is not supported on LanceDB cloud."
|
"IVF_HNSW_PQ is not supported on LanceDB cloud."
|
||||||
@@ -277,7 +279,7 @@ class RemoteTable(Table):
|
|||||||
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"
|
||||||
" 'IVF_FLAT', 'IVF_PQ', 'IVF_HNSW_PQ', 'IVF_HNSW_SQ'"
|
" 'IVF_FLAT', 'IVF_SQ', 'IVF_PQ', 'IVF_HNSW_PQ', 'IVF_HNSW_SQ'"
|
||||||
)
|
)
|
||||||
|
|
||||||
LOOP.run(
|
LOOP.run(
|
||||||
|
|||||||
@@ -44,7 +44,18 @@ import numpy as np
|
|||||||
|
|
||||||
from .common import DATA, VEC, VECTOR_COLUMN_NAME
|
from .common import DATA, VEC, VECTOR_COLUMN_NAME
|
||||||
from .embeddings import EmbeddingFunctionConfig, EmbeddingFunctionRegistry
|
from .embeddings import EmbeddingFunctionConfig, EmbeddingFunctionRegistry
|
||||||
from .index import BTree, IvfFlat, IvfPq, Bitmap, IvfRq, LabelList, HnswPq, HnswSq, FTS
|
from .index import (
|
||||||
|
BTree,
|
||||||
|
IvfFlat,
|
||||||
|
IvfPq,
|
||||||
|
IvfSq,
|
||||||
|
Bitmap,
|
||||||
|
IvfRq,
|
||||||
|
LabelList,
|
||||||
|
HnswPq,
|
||||||
|
HnswSq,
|
||||||
|
FTS,
|
||||||
|
)
|
||||||
from .merge import LanceMergeInsertBuilder
|
from .merge import LanceMergeInsertBuilder
|
||||||
from .pydantic import LanceModel, model_to_dict
|
from .pydantic import LanceModel, model_to_dict
|
||||||
from .query import (
|
from .query import (
|
||||||
@@ -2054,7 +2065,7 @@ class LanceTable(Table):
|
|||||||
index_cache_size: Optional[int] = None,
|
index_cache_size: Optional[int] = None,
|
||||||
num_bits: int = 8,
|
num_bits: int = 8,
|
||||||
index_type: Literal[
|
index_type: Literal[
|
||||||
"IVF_FLAT", "IVF_PQ", "IVF_RQ", "IVF_HNSW_SQ", "IVF_HNSW_PQ"
|
"IVF_FLAT", "IVF_SQ", "IVF_PQ", "IVF_RQ", "IVF_HNSW_SQ", "IVF_HNSW_PQ"
|
||||||
] = "IVF_PQ",
|
] = "IVF_PQ",
|
||||||
max_iterations: int = 50,
|
max_iterations: int = 50,
|
||||||
sample_rate: int = 256,
|
sample_rate: int = 256,
|
||||||
@@ -2092,6 +2103,14 @@ class LanceTable(Table):
|
|||||||
sample_rate=sample_rate,
|
sample_rate=sample_rate,
|
||||||
target_partition_size=target_partition_size,
|
target_partition_size=target_partition_size,
|
||||||
)
|
)
|
||||||
|
elif index_type == "IVF_SQ":
|
||||||
|
config = IvfSq(
|
||||||
|
distance_type=metric,
|
||||||
|
num_partitions=num_partitions,
|
||||||
|
max_iterations=max_iterations,
|
||||||
|
sample_rate=sample_rate,
|
||||||
|
target_partition_size=target_partition_size,
|
||||||
|
)
|
||||||
elif index_type == "IVF_PQ":
|
elif index_type == "IVF_PQ":
|
||||||
config = IvfPq(
|
config = IvfPq(
|
||||||
distance_type=metric,
|
distance_type=metric,
|
||||||
@@ -3189,6 +3208,26 @@ def _infer_target_schema(
|
|||||||
if pa.types.is_floating(field.type.value_type):
|
if pa.types.is_floating(field.type.value_type):
|
||||||
target_type = pa.list_(pa.float32(), dim)
|
target_type = pa.list_(pa.float32(), dim)
|
||||||
elif pa.types.is_integer(field.type.value_type):
|
elif pa.types.is_integer(field.type.value_type):
|
||||||
|
values = peeked.column(i)
|
||||||
|
|
||||||
|
if isinstance(values, pa.ChunkedArray):
|
||||||
|
values = values.combine_chunks()
|
||||||
|
|
||||||
|
flattened = values.flatten()
|
||||||
|
valid_count = pc.count(flattened, mode="only_valid").as_py()
|
||||||
|
|
||||||
|
if valid_count == 0:
|
||||||
|
target_type = pa.list_(pa.uint8(), dim)
|
||||||
|
else:
|
||||||
|
min_max = pc.min_max(flattened)
|
||||||
|
min_value = min_max["min"].as_py()
|
||||||
|
max_value = min_max["max"].as_py()
|
||||||
|
|
||||||
|
if (min_value is not None and min_value < 0) or (
|
||||||
|
max_value is not None and max_value > 255
|
||||||
|
):
|
||||||
|
target_type = pa.list_(pa.float32(), dim)
|
||||||
|
else:
|
||||||
target_type = pa.list_(pa.uint8(), dim)
|
target_type = pa.list_(pa.uint8(), dim)
|
||||||
else:
|
else:
|
||||||
continue # Skip non-numeric types
|
continue # Skip non-numeric types
|
||||||
@@ -3456,11 +3495,22 @@ class AsyncTable:
|
|||||||
if config is not None:
|
if config is not None:
|
||||||
if not isinstance(
|
if not isinstance(
|
||||||
config,
|
config,
|
||||||
(IvfFlat, IvfPq, IvfRq, HnswPq, HnswSq, BTree, Bitmap, LabelList, FTS),
|
(
|
||||||
|
IvfFlat,
|
||||||
|
IvfSq,
|
||||||
|
IvfPq,
|
||||||
|
IvfRq,
|
||||||
|
HnswPq,
|
||||||
|
HnswSq,
|
||||||
|
BTree,
|
||||||
|
Bitmap,
|
||||||
|
LabelList,
|
||||||
|
FTS,
|
||||||
|
),
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"config must be an instance of IvfPq, IvfRq, HnswPq, HnswSq, BTree,"
|
"config must be an instance of IvfSq, IvfPq, IvfRq, HnswPq, HnswSq,"
|
||||||
" Bitmap, LabelList, or FTS, but got " + str(type(config))
|
" BTree, Bitmap, LabelList, or FTS, but got " + str(type(config))
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await self._inner.create_index(
|
await self._inner.create_index(
|
||||||
|
|||||||
@@ -18,12 +18,20 @@ AddMode = Literal["append", "overwrite"]
|
|||||||
CreateMode = Literal["create", "overwrite"]
|
CreateMode = Literal["create", "overwrite"]
|
||||||
|
|
||||||
# Index type literals
|
# Index type literals
|
||||||
VectorIndexType = Literal["IVF_FLAT", "IVF_PQ", "IVF_HNSW_SQ", "IVF_HNSW_PQ", "IVF_RQ"]
|
VectorIndexType = Literal[
|
||||||
|
"IVF_FLAT",
|
||||||
|
"IVF_SQ",
|
||||||
|
"IVF_PQ",
|
||||||
|
"IVF_HNSW_SQ",
|
||||||
|
"IVF_HNSW_PQ",
|
||||||
|
"IVF_RQ",
|
||||||
|
]
|
||||||
ScalarIndexType = Literal["BTREE", "BITMAP", "LABEL_LIST"]
|
ScalarIndexType = Literal["BTREE", "BITMAP", "LABEL_LIST"]
|
||||||
IndexType = Literal[
|
IndexType = Literal[
|
||||||
"IVF_PQ",
|
"IVF_PQ",
|
||||||
"IVF_HNSW_PQ",
|
"IVF_HNSW_PQ",
|
||||||
"IVF_HNSW_SQ",
|
"IVF_HNSW_SQ",
|
||||||
|
"IVF_SQ",
|
||||||
"FTS",
|
"FTS",
|
||||||
"BTREE",
|
"BTREE",
|
||||||
"BITMAP",
|
"BITMAP",
|
||||||
|
|||||||
@@ -892,7 +892,7 @@ def test_local_namespace_operations(tmp_path):
|
|||||||
db = lancedb.connect(tmp_path)
|
db = lancedb.connect(tmp_path)
|
||||||
|
|
||||||
# Test list_namespaces returns empty list for root namespace
|
# Test list_namespaces returns empty list for root namespace
|
||||||
namespaces = list(db.list_namespaces())
|
namespaces = db.list_namespaces().namespaces
|
||||||
assert namespaces == []
|
assert namespaces == []
|
||||||
|
|
||||||
# Test list_namespaces with non-empty namespace raises NotImplementedError
|
# Test list_namespaces with non-empty namespace raises NotImplementedError
|
||||||
@@ -900,7 +900,7 @@ def test_local_namespace_operations(tmp_path):
|
|||||||
NotImplementedError,
|
NotImplementedError,
|
||||||
match="Namespace operations are not supported for listing database",
|
match="Namespace operations are not supported for listing database",
|
||||||
):
|
):
|
||||||
list(db.list_namespaces(namespace=["test"]))
|
db.list_namespaces(namespace=["test"])
|
||||||
|
|
||||||
|
|
||||||
def test_local_create_namespace_not_supported(tmp_path):
|
def test_local_create_namespace_not_supported(tmp_path):
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ from lancedb.index import (
|
|||||||
BTree,
|
BTree,
|
||||||
IvfFlat,
|
IvfFlat,
|
||||||
IvfPq,
|
IvfPq,
|
||||||
|
IvfSq,
|
||||||
|
IvfHnswPq,
|
||||||
|
IvfHnswSq,
|
||||||
IvfRq,
|
IvfRq,
|
||||||
Bitmap,
|
Bitmap,
|
||||||
LabelList,
|
LabelList,
|
||||||
@@ -229,6 +232,35 @@ async def test_create_hnswsq_index(some_table: AsyncTable):
|
|||||||
assert len(indices) == 1
|
assert len(indices) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_create_hnswsq_alias_index(some_table: AsyncTable):
|
||||||
|
await some_table.create_index("vector", config=IvfHnswSq(num_partitions=5))
|
||||||
|
indices = await some_table.list_indices()
|
||||||
|
assert len(indices) == 1
|
||||||
|
assert indices[0].index_type in {"HnswSq", "IvfHnswSq"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_create_hnswpq_alias_index(some_table: AsyncTable):
|
||||||
|
await some_table.create_index("vector", config=IvfHnswPq(num_partitions=5))
|
||||||
|
indices = await some_table.list_indices()
|
||||||
|
assert len(indices) == 1
|
||||||
|
assert indices[0].index_type in {"HnswPq", "IvfHnswPq"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_create_ivfsq_index(some_table: AsyncTable):
|
||||||
|
await some_table.create_index("vector", config=IvfSq(num_partitions=10))
|
||||||
|
indices = await some_table.list_indices()
|
||||||
|
assert len(indices) == 1
|
||||||
|
assert indices[0].index_type == "IvfSq"
|
||||||
|
stats = await some_table.index_stats(indices[0].name)
|
||||||
|
assert stats.index_type == "IVF_SQ"
|
||||||
|
assert stats.distance_type == "l2"
|
||||||
|
assert stats.num_indexed_rows == await some_table.count_rows()
|
||||||
|
assert stats.num_unindexed_rows == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_create_index_with_binary_vectors(binary_table: AsyncTable):
|
async def test_create_index_with_binary_vectors(binary_table: AsyncTable):
|
||||||
await binary_table.create_index(
|
await binary_table.create_index(
|
||||||
|
|||||||
@@ -279,13 +279,13 @@ class TestNamespaceConnection:
|
|||||||
db = lancedb.connect_namespace("dir", {"root": self.temp_dir})
|
db = lancedb.connect_namespace("dir", {"root": self.temp_dir})
|
||||||
|
|
||||||
# Initially no namespaces
|
# Initially no namespaces
|
||||||
assert len(list(db.list_namespaces())) == 0
|
assert len(db.list_namespaces().namespaces) == 0
|
||||||
|
|
||||||
# Create a namespace
|
# Create a namespace
|
||||||
db.create_namespace(["test_namespace"])
|
db.create_namespace(["test_namespace"])
|
||||||
|
|
||||||
# Verify namespace exists
|
# Verify namespace exists
|
||||||
namespaces = list(db.list_namespaces())
|
namespaces = db.list_namespaces().namespaces
|
||||||
assert "test_namespace" in namespaces
|
assert "test_namespace" in namespaces
|
||||||
assert len(namespaces) == 1
|
assert len(namespaces) == 1
|
||||||
|
|
||||||
@@ -322,7 +322,7 @@ class TestNamespaceConnection:
|
|||||||
db.drop_namespace(["test_namespace"])
|
db.drop_namespace(["test_namespace"])
|
||||||
|
|
||||||
# Verify namespace no longer exists
|
# Verify namespace no longer exists
|
||||||
namespaces = list(db.list_namespaces())
|
namespaces = db.list_namespaces().namespaces
|
||||||
assert len(namespaces) == 0
|
assert len(namespaces) == 0
|
||||||
|
|
||||||
def test_namespace_with_tables_cannot_be_dropped(self):
|
def test_namespace_with_tables_cannot_be_dropped(self):
|
||||||
@@ -570,13 +570,13 @@ class TestAsyncNamespaceConnection:
|
|||||||
|
|
||||||
# Initially no namespaces
|
# Initially no namespaces
|
||||||
namespaces = await db.list_namespaces()
|
namespaces = await db.list_namespaces()
|
||||||
assert len(list(namespaces)) == 0
|
assert len(namespaces.namespaces) == 0
|
||||||
|
|
||||||
# Create a namespace
|
# Create a namespace
|
||||||
await db.create_namespace(["test_namespace"])
|
await db.create_namespace(["test_namespace"])
|
||||||
|
|
||||||
# Verify namespace exists
|
# Verify namespace exists
|
||||||
namespaces = list(await db.list_namespaces())
|
namespaces = (await db.list_namespaces()).namespaces
|
||||||
assert "test_namespace" in namespaces
|
assert "test_namespace" in namespaces
|
||||||
assert len(namespaces) == 1
|
assert len(namespaces) == 1
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ class TestAsyncNamespaceConnection:
|
|||||||
await db.drop_namespace(["test_namespace"])
|
await db.drop_namespace(["test_namespace"])
|
||||||
|
|
||||||
# Verify namespace no longer exists
|
# Verify namespace no longer exists
|
||||||
namespaces = list(await db.list_namespaces())
|
namespaces = (await db.list_namespaces()).namespaces
|
||||||
assert len(namespaces) == 0
|
assert len(namespaces) == 0
|
||||||
|
|
||||||
async def test_drop_all_tables_async(self):
|
async def test_drop_all_tables_async(self):
|
||||||
|
|||||||
@@ -46,6 +46,39 @@ def test_basic(mem_db: DBConnection):
|
|||||||
assert table.to_arrow() == expected_data
|
assert table.to_arrow() == expected_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_table_infers_large_int_vectors(mem_db: DBConnection):
|
||||||
|
data = [{"vector": [0, 300]}]
|
||||||
|
|
||||||
|
table = mem_db.create_table(
|
||||||
|
"int_vector_overflow", data=data, mode="overwrite", exist_ok=True
|
||||||
|
)
|
||||||
|
|
||||||
|
vector_field = table.schema.field("vector")
|
||||||
|
assert vector_field.type == pa.list_(pa.float32(), 2)
|
||||||
|
|
||||||
|
vector_column = table.to_arrow().column("vector")
|
||||||
|
assert vector_column.type == pa.list_(pa.float32(), 2)
|
||||||
|
assert vector_column.to_pylist() == [[0.0, 300.0]]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_create_table_async_infers_large_int_vectors(
|
||||||
|
mem_db_async: AsyncConnection,
|
||||||
|
):
|
||||||
|
data = [{"vector": [256, 257]}]
|
||||||
|
|
||||||
|
table = await mem_db_async.create_table(
|
||||||
|
"int_vector_overflow_async", data=data, mode="overwrite", exist_ok=True
|
||||||
|
)
|
||||||
|
|
||||||
|
schema = await table.schema()
|
||||||
|
assert schema.field("vector").type == pa.list_(pa.float32(), 2)
|
||||||
|
|
||||||
|
vector_column = (await table.to_arrow()).column("vector")
|
||||||
|
assert vector_column.type == pa.list_(pa.float32(), 2)
|
||||||
|
assert vector_column.to_pylist() == [[256.0, 257.0]]
|
||||||
|
|
||||||
|
|
||||||
def test_input_data_type(mem_db: DBConnection, tmp_path):
|
def test_input_data_type(mem_db: DBConnection, tmp_path):
|
||||||
schema = pa.schema(
|
schema = pa.schema(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ use lancedb::{
|
|||||||
};
|
};
|
||||||
use pyo3::{
|
use pyo3::{
|
||||||
exceptions::{PyRuntimeError, PyValueError},
|
exceptions::{PyRuntimeError, PyValueError},
|
||||||
pyclass, pyfunction, pymethods, Bound, FromPyObject, Py, PyAny, PyObject, PyRef, PyResult,
|
pyclass, pyfunction, pymethods,
|
||||||
Python,
|
types::{PyDict, PyDictMethods},
|
||||||
|
Bound, FromPyObject, Py, PyAny, PyObject, PyRef, PyResult, Python,
|
||||||
};
|
};
|
||||||
use pyo3_async_runtimes::tokio::future_into_py;
|
use pyo3_async_runtimes::tokio::future_into_py;
|
||||||
|
|
||||||
@@ -292,40 +293,155 @@ impl Connection {
|
|||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> PyResult<Bound<'_, PyAny>> {
|
) -> PyResult<Bound<'_, PyAny>> {
|
||||||
let inner = self_.get_inner()?.clone();
|
let inner = self_.get_inner()?.clone();
|
||||||
future_into_py(self_.py(), async move {
|
let py = self_.py();
|
||||||
use lancedb::database::ListNamespacesRequest;
|
future_into_py(py, async move {
|
||||||
|
use lance_namespace::models::ListNamespacesRequest;
|
||||||
let request = ListNamespacesRequest {
|
let request = ListNamespacesRequest {
|
||||||
namespace,
|
id: if namespace.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(namespace)
|
||||||
|
},
|
||||||
page_token,
|
page_token,
|
||||||
limit,
|
limit: limit.map(|l| l as i32),
|
||||||
};
|
};
|
||||||
inner.list_namespaces(request).await.infer_error()
|
let response = inner.list_namespaces(request).await.infer_error()?;
|
||||||
|
Python::with_gil(|py| -> PyResult<Py<PyDict>> {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
dict.set_item("namespaces", response.namespaces)?;
|
||||||
|
dict.set_item("page_token", response.page_token)?;
|
||||||
|
Ok(dict.unbind())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(signature = (namespace,))]
|
#[pyo3(signature = (namespace, mode=None, properties=None))]
|
||||||
pub fn create_namespace(
|
pub fn create_namespace(
|
||||||
self_: PyRef<'_, Self>,
|
self_: PyRef<'_, Self>,
|
||||||
namespace: Vec<String>,
|
namespace: Vec<String>,
|
||||||
|
mode: Option<String>,
|
||||||
|
properties: Option<std::collections::HashMap<String, String>>,
|
||||||
) -> PyResult<Bound<'_, PyAny>> {
|
) -> PyResult<Bound<'_, PyAny>> {
|
||||||
let inner = self_.get_inner()?.clone();
|
let inner = self_.get_inner()?.clone();
|
||||||
future_into_py(self_.py(), async move {
|
let py = self_.py();
|
||||||
use lancedb::database::CreateNamespaceRequest;
|
future_into_py(py, async move {
|
||||||
let request = CreateNamespaceRequest { namespace };
|
use lance_namespace::models::{create_namespace_request, CreateNamespaceRequest};
|
||||||
inner.create_namespace(request).await.infer_error()
|
let mode_enum = mode.and_then(|m| match m.to_lowercase().as_str() {
|
||||||
|
"create" => Some(create_namespace_request::Mode::Create),
|
||||||
|
"exist_ok" => Some(create_namespace_request::Mode::ExistOk),
|
||||||
|
"overwrite" => Some(create_namespace_request::Mode::Overwrite),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
let request = CreateNamespaceRequest {
|
||||||
|
id: if namespace.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(namespace)
|
||||||
|
},
|
||||||
|
mode: mode_enum,
|
||||||
|
properties,
|
||||||
|
};
|
||||||
|
let response = inner.create_namespace(request).await.infer_error()?;
|
||||||
|
Python::with_gil(|py| -> PyResult<Py<PyDict>> {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
dict.set_item("properties", response.properties)?;
|
||||||
|
Ok(dict.unbind())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (namespace, mode=None, behavior=None))]
|
||||||
|
pub fn drop_namespace(
|
||||||
|
self_: PyRef<'_, Self>,
|
||||||
|
namespace: Vec<String>,
|
||||||
|
mode: Option<String>,
|
||||||
|
behavior: Option<String>,
|
||||||
|
) -> PyResult<Bound<'_, PyAny>> {
|
||||||
|
let inner = self_.get_inner()?.clone();
|
||||||
|
let py = self_.py();
|
||||||
|
future_into_py(py, async move {
|
||||||
|
use lance_namespace::models::{drop_namespace_request, DropNamespaceRequest};
|
||||||
|
let mode_enum = mode.and_then(|m| match m.to_uppercase().as_str() {
|
||||||
|
"SKIP" => Some(drop_namespace_request::Mode::Skip),
|
||||||
|
"FAIL" => Some(drop_namespace_request::Mode::Fail),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
let behavior_enum = behavior.and_then(|b| match b.to_uppercase().as_str() {
|
||||||
|
"RESTRICT" => Some(drop_namespace_request::Behavior::Restrict),
|
||||||
|
"CASCADE" => Some(drop_namespace_request::Behavior::Cascade),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
let request = DropNamespaceRequest {
|
||||||
|
id: if namespace.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(namespace)
|
||||||
|
},
|
||||||
|
mode: mode_enum,
|
||||||
|
behavior: behavior_enum,
|
||||||
|
};
|
||||||
|
let response = inner.drop_namespace(request).await.infer_error()?;
|
||||||
|
Python::with_gil(|py| -> PyResult<Py<PyDict>> {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
dict.set_item("properties", response.properties)?;
|
||||||
|
dict.set_item("transaction_id", response.transaction_id)?;
|
||||||
|
Ok(dict.unbind())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3(signature = (namespace,))]
|
#[pyo3(signature = (namespace,))]
|
||||||
pub fn drop_namespace(
|
pub fn describe_namespace(
|
||||||
self_: PyRef<'_, Self>,
|
self_: PyRef<'_, Self>,
|
||||||
namespace: Vec<String>,
|
namespace: Vec<String>,
|
||||||
) -> PyResult<Bound<'_, PyAny>> {
|
) -> PyResult<Bound<'_, PyAny>> {
|
||||||
let inner = self_.get_inner()?.clone();
|
let inner = self_.get_inner()?.clone();
|
||||||
future_into_py(self_.py(), async move {
|
let py = self_.py();
|
||||||
use lancedb::database::DropNamespaceRequest;
|
future_into_py(py, async move {
|
||||||
let request = DropNamespaceRequest { namespace };
|
use lance_namespace::models::DescribeNamespaceRequest;
|
||||||
inner.drop_namespace(request).await.infer_error()
|
let request = DescribeNamespaceRequest {
|
||||||
|
id: if namespace.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(namespace)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let response = inner.describe_namespace(request).await.infer_error()?;
|
||||||
|
Python::with_gil(|py| -> PyResult<Py<PyDict>> {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
dict.set_item("properties", response.properties)?;
|
||||||
|
Ok(dict.unbind())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(signature = (namespace=vec![], page_token=None, limit=None))]
|
||||||
|
pub fn list_tables(
|
||||||
|
self_: PyRef<'_, Self>,
|
||||||
|
namespace: Vec<String>,
|
||||||
|
page_token: Option<String>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
) -> PyResult<Bound<'_, PyAny>> {
|
||||||
|
let inner = self_.get_inner()?.clone();
|
||||||
|
let py = self_.py();
|
||||||
|
future_into_py(py, async move {
|
||||||
|
use lance_namespace::models::ListTablesRequest;
|
||||||
|
let request = ListTablesRequest {
|
||||||
|
id: if namespace.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(namespace)
|
||||||
|
},
|
||||||
|
page_token,
|
||||||
|
limit: limit.map(|l| l as i32),
|
||||||
|
};
|
||||||
|
let response = inner.list_tables(request).await.infer_error()?;
|
||||||
|
Python::with_gil(|py| -> PyResult<Py<PyDict>> {
|
||||||
|
let dict = PyDict::new(py);
|
||||||
|
dict.set_item("tables", response.tables)?;
|
||||||
|
dict.set_item("page_token", response.page_token)?;
|
||||||
|
Ok(dict.unbind())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
use lancedb::index::vector::{IvfFlatIndexBuilder, IvfRqIndexBuilder};
|
use lancedb::index::vector::{IvfFlatIndexBuilder, IvfRqIndexBuilder, IvfSqIndexBuilder};
|
||||||
use lancedb::index::{
|
use lancedb::index::{
|
||||||
scalar::{BTreeIndexBuilder, FtsIndexBuilder},
|
scalar::{BTreeIndexBuilder, FtsIndexBuilder},
|
||||||
vector::{IvfHnswPqIndexBuilder, IvfHnswSqIndexBuilder, IvfPqIndexBuilder},
|
vector::{IvfHnswPqIndexBuilder, IvfHnswSqIndexBuilder, IvfPqIndexBuilder},
|
||||||
@@ -87,6 +87,21 @@ pub fn extract_index_params(source: &Option<Bound<'_, PyAny>>) -> PyResult<Lance
|
|||||||
}
|
}
|
||||||
Ok(LanceDbIndex::IvfPq(ivf_pq_builder))
|
Ok(LanceDbIndex::IvfPq(ivf_pq_builder))
|
||||||
},
|
},
|
||||||
|
"IvfSq" => {
|
||||||
|
let params = source.extract::<IvfSqParams>()?;
|
||||||
|
let distance_type = parse_distance_type(params.distance_type)?;
|
||||||
|
let mut ivf_sq_builder = IvfSqIndexBuilder::default()
|
||||||
|
.distance_type(distance_type)
|
||||||
|
.max_iterations(params.max_iterations)
|
||||||
|
.sample_rate(params.sample_rate);
|
||||||
|
if let Some(num_partitions) = params.num_partitions {
|
||||||
|
ivf_sq_builder = ivf_sq_builder.num_partitions(num_partitions);
|
||||||
|
}
|
||||||
|
if let Some(target_partition_size) = params.target_partition_size {
|
||||||
|
ivf_sq_builder = ivf_sq_builder.target_partition_size(target_partition_size);
|
||||||
|
}
|
||||||
|
Ok(LanceDbIndex::IvfSq(ivf_sq_builder))
|
||||||
|
},
|
||||||
"IvfRq" => {
|
"IvfRq" => {
|
||||||
let params = source.extract::<IvfRqParams>()?;
|
let params = source.extract::<IvfRqParams>()?;
|
||||||
let distance_type = parse_distance_type(params.distance_type)?;
|
let distance_type = parse_distance_type(params.distance_type)?;
|
||||||
@@ -142,7 +157,7 @@ pub fn extract_index_params(source: &Option<Bound<'_, PyAny>>) -> PyResult<Lance
|
|||||||
Ok(LanceDbIndex::IvfHnswSq(hnsw_sq_builder))
|
Ok(LanceDbIndex::IvfHnswSq(hnsw_sq_builder))
|
||||||
},
|
},
|
||||||
not_supported => Err(PyValueError::new_err(format!(
|
not_supported => Err(PyValueError::new_err(format!(
|
||||||
"Invalid index type '{}'. Must be one of BTree, Bitmap, LabelList, FTS, IvfPq, IvfHnswPq, or IvfHnswSq",
|
"Invalid index type '{}'. Must be one of BTree, Bitmap, LabelList, FTS, IvfPq, IvfSq, IvfHnswPq, or IvfHnswSq",
|
||||||
not_supported
|
not_supported
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
@@ -186,6 +201,15 @@ struct IvfPqParams {
|
|||||||
target_partition_size: Option<u32>,
|
target_partition_size: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromPyObject)]
|
||||||
|
struct IvfSqParams {
|
||||||
|
distance_type: String,
|
||||||
|
num_partitions: Option<u32>,
|
||||||
|
max_iterations: u32,
|
||||||
|
sample_rate: u32,
|
||||||
|
target_partition_size: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
#[derive(FromPyObject)]
|
||||||
struct IvfRqParams {
|
struct IvfRqParams {
|
||||||
distance_type: String,
|
distance_type: String,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb"
|
name = "lancedb"
|
||||||
version = "0.22.4-beta.2"
|
version = "0.23.0-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
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ use std::sync::Arc;
|
|||||||
use arrow_array::RecordBatchReader;
|
use arrow_array::RecordBatchReader;
|
||||||
use arrow_schema::{Field, SchemaRef};
|
use arrow_schema::{Field, SchemaRef};
|
||||||
use lance::dataset::ReadParams;
|
use lance::dataset::ReadParams;
|
||||||
|
use lance_namespace::models::{
|
||||||
|
CreateNamespaceRequest, CreateNamespaceResponse, DescribeNamespaceRequest,
|
||||||
|
DescribeNamespaceResponse, DropNamespaceRequest, DropNamespaceResponse, ListNamespacesRequest,
|
||||||
|
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
||||||
|
};
|
||||||
#[cfg(feature = "aws")]
|
#[cfg(feature = "aws")]
|
||||||
use object_store::aws::AwsCredential;
|
use object_store::aws::AwsCredential;
|
||||||
|
|
||||||
@@ -17,9 +22,8 @@ use crate::database::listing::{
|
|||||||
ListingDatabase, OPT_NEW_TABLE_STORAGE_VERSION, OPT_NEW_TABLE_V2_MANIFEST_PATHS,
|
ListingDatabase, OPT_NEW_TABLE_STORAGE_VERSION, OPT_NEW_TABLE_V2_MANIFEST_PATHS,
|
||||||
};
|
};
|
||||||
use crate::database::{
|
use crate::database::{
|
||||||
CloneTableRequest, CreateNamespaceRequest, CreateTableData, CreateTableMode,
|
CloneTableRequest, CreateTableData, CreateTableMode, CreateTableRequest, Database,
|
||||||
CreateTableRequest, Database, DatabaseOptions, DropNamespaceRequest, ListNamespacesRequest,
|
DatabaseOptions, OpenTableRequest, ReadConsistency, TableNamesRequest,
|
||||||
OpenTableRequest, ReadConsistency, TableNamesRequest,
|
|
||||||
};
|
};
|
||||||
use crate::embeddings::{
|
use crate::embeddings::{
|
||||||
EmbeddingDefinition, EmbeddingFunction, EmbeddingRegistry, MemoryRegistry, WithEmbeddings,
|
EmbeddingDefinition, EmbeddingFunction, EmbeddingRegistry, MemoryRegistry, WithEmbeddings,
|
||||||
@@ -74,6 +78,7 @@ impl TableNamesBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the table names operation
|
/// Execute the table names operation
|
||||||
|
#[allow(deprecated)]
|
||||||
pub async fn execute(self) -> Result<Vec<String>> {
|
pub async fn execute(self) -> Result<Vec<String>> {
|
||||||
self.parent.clone().table_names(self.request).await
|
self.parent.clone().table_names(self.request).await
|
||||||
}
|
}
|
||||||
@@ -408,6 +413,7 @@ impl OpenTableBuilder {
|
|||||||
index_cache_size: None,
|
index_cache_size: None,
|
||||||
lance_read_params: None,
|
lance_read_params: None,
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
},
|
},
|
||||||
embedding_registry,
|
embedding_registry,
|
||||||
}
|
}
|
||||||
@@ -767,20 +773,42 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// List immediate child namespace names in the given namespace
|
/// List immediate child namespace names in the given namespace
|
||||||
pub async fn list_namespaces(&self, request: ListNamespacesRequest) -> Result<Vec<String>> {
|
pub async fn list_namespaces(
|
||||||
|
&self,
|
||||||
|
request: ListNamespacesRequest,
|
||||||
|
) -> Result<ListNamespacesResponse> {
|
||||||
self.internal.list_namespaces(request).await
|
self.internal.list_namespaces(request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new namespace
|
/// Create a new namespace
|
||||||
pub async fn create_namespace(&self, request: CreateNamespaceRequest) -> Result<()> {
|
pub async fn create_namespace(
|
||||||
|
&self,
|
||||||
|
request: CreateNamespaceRequest,
|
||||||
|
) -> Result<CreateNamespaceResponse> {
|
||||||
self.internal.create_namespace(request).await
|
self.internal.create_namespace(request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drop a namespace
|
/// Drop a namespace
|
||||||
pub async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<()> {
|
pub async fn drop_namespace(
|
||||||
|
&self,
|
||||||
|
request: DropNamespaceRequest,
|
||||||
|
) -> Result<DropNamespaceResponse> {
|
||||||
self.internal.drop_namespace(request).await
|
self.internal.drop_namespace(request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describe a namespace
|
||||||
|
pub async fn describe_namespace(
|
||||||
|
&self,
|
||||||
|
request: DescribeNamespaceRequest,
|
||||||
|
) -> Result<DescribeNamespaceResponse> {
|
||||||
|
self.internal.describe_namespace(request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List tables with pagination support
|
||||||
|
pub async fn list_tables(&self, request: ListTablesRequest) -> Result<ListTablesResponse> {
|
||||||
|
self.internal.list_tables(request).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the in-memory embedding registry.
|
/// Get the in-memory embedding registry.
|
||||||
/// It's important to note that the embedding registry is not persisted across connections.
|
/// It's important to note that the embedding registry is not persisted across connections.
|
||||||
/// So if a table contains embeddings, you will need to make sure that you are using a connection that has the same embedding functions registered
|
/// So if a table contains embeddings, you will need to make sure that you are using a connection that has the same embedding functions registered
|
||||||
@@ -1086,6 +1114,7 @@ pub struct ConnectNamespaceBuilder {
|
|||||||
read_consistency_interval: Option<std::time::Duration>,
|
read_consistency_interval: Option<std::time::Duration>,
|
||||||
embedding_registry: Option<Arc<dyn EmbeddingRegistry>>,
|
embedding_registry: Option<Arc<dyn EmbeddingRegistry>>,
|
||||||
session: Option<Arc<lance::session::Session>>,
|
session: Option<Arc<lance::session::Session>>,
|
||||||
|
server_side_query_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectNamespaceBuilder {
|
impl ConnectNamespaceBuilder {
|
||||||
@@ -1097,6 +1126,7 @@ impl ConnectNamespaceBuilder {
|
|||||||
read_consistency_interval: None,
|
read_consistency_interval: None,
|
||||||
embedding_registry: None,
|
embedding_registry: None,
|
||||||
session: None,
|
session: None,
|
||||||
|
server_side_query_enabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1151,6 +1181,18 @@ impl ConnectNamespaceBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable server-side query execution.
|
||||||
|
///
|
||||||
|
/// When enabled, queries will be executed on the namespace server instead of
|
||||||
|
/// locally. This can improve performance by reducing data transfer and
|
||||||
|
/// leveraging server-side compute resources.
|
||||||
|
///
|
||||||
|
/// Default is `false` (queries executed locally).
|
||||||
|
pub fn server_side_query(mut self, enabled: bool) -> Self {
|
||||||
|
self.server_side_query_enabled = enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute the connection
|
/// Execute the connection
|
||||||
pub async fn execute(self) -> Result<Connection> {
|
pub async fn execute(self) -> Result<Connection> {
|
||||||
use crate::database::namespace::LanceNamespaceDatabase;
|
use crate::database::namespace::LanceNamespaceDatabase;
|
||||||
@@ -1162,6 +1204,7 @@ impl ConnectNamespaceBuilder {
|
|||||||
self.storage_options,
|
self.storage_options,
|
||||||
self.read_consistency_interval,
|
self.read_consistency_interval,
|
||||||
self.session,
|
self.session,
|
||||||
|
self.server_side_query_enabled,
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ use datafusion_physical_plan::stream::RecordBatchStreamAdapter;
|
|||||||
use futures::stream;
|
use futures::stream;
|
||||||
use lance::dataset::ReadParams;
|
use lance::dataset::ReadParams;
|
||||||
use lance_datafusion::utils::StreamingWriteSource;
|
use lance_datafusion::utils::StreamingWriteSource;
|
||||||
|
use lance_namespace::models::{
|
||||||
|
CreateNamespaceRequest, CreateNamespaceResponse, DescribeNamespaceRequest,
|
||||||
|
DescribeNamespaceResponse, DropNamespaceRequest, DropNamespaceResponse, ListNamespacesRequest,
|
||||||
|
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
||||||
|
};
|
||||||
|
use lance_namespace::LanceNamespace;
|
||||||
|
|
||||||
use crate::arrow::{SendableRecordBatchStream, SendableRecordBatchStreamExt};
|
use crate::arrow::{SendableRecordBatchStream, SendableRecordBatchStreamExt};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
@@ -36,32 +42,7 @@ pub trait DatabaseOptions {
|
|||||||
fn serialize_into_map(&self, map: &mut HashMap<String, String>);
|
fn serialize_into_map(&self, map: &mut HashMap<String, String>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A request to list namespaces in the database
|
/// A request to list names of tables in the database (deprecated, use ListTablesRequest)
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct ListNamespacesRequest {
|
|
||||||
/// The parent namespace to list namespaces in. Empty list represents root namespace.
|
|
||||||
pub namespace: Vec<String>,
|
|
||||||
/// If present, only return names that come lexicographically after the supplied value.
|
|
||||||
pub page_token: Option<String>,
|
|
||||||
/// The maximum number of namespace names to return
|
|
||||||
pub limit: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A request to create a namespace
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CreateNamespaceRequest {
|
|
||||||
/// The namespace identifier to create
|
|
||||||
pub namespace: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A request to drop a namespace
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DropNamespaceRequest {
|
|
||||||
/// The namespace identifier to drop
|
|
||||||
pub namespace: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A request to list names of tables in the database
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct TableNamesRequest {
|
pub struct TableNamesRequest {
|
||||||
/// The namespace to list tables in. Empty list represents root namespace.
|
/// The namespace to list tables in. Empty list represents root namespace.
|
||||||
@@ -77,7 +58,7 @@ pub struct TableNamesRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A request to open a table
|
/// A request to open a table
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct OpenTableRequest {
|
pub struct OpenTableRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// The namespace to open the table from. Empty list represents root namespace.
|
/// The namespace to open the table from. Empty list represents root namespace.
|
||||||
@@ -87,6 +68,22 @@ pub struct OpenTableRequest {
|
|||||||
/// Optional custom location for the table. If not provided, the database will
|
/// Optional custom location for the table. If not provided, the database will
|
||||||
/// derive a location based on its URI and the table name.
|
/// derive a location based on its URI and the table name.
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
|
/// Optional namespace client for server-side query execution.
|
||||||
|
/// When set, queries will be executed on the namespace server instead of locally.
|
||||||
|
pub namespace_client: Option<Arc<dyn LanceNamespace>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for OpenTableRequest {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("OpenTableRequest")
|
||||||
|
.field("name", &self.name)
|
||||||
|
.field("namespace", &self.namespace)
|
||||||
|
.field("index_cache_size", &self.index_cache_size)
|
||||||
|
.field("lance_read_params", &self.lance_read_params)
|
||||||
|
.field("location", &self.location)
|
||||||
|
.field("namespace_client", &self.namespace_client)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TableBuilderCallback = Box<dyn FnOnce(OpenTableRequest) -> OpenTableRequest + Send>;
|
pub type TableBuilderCallback = Box<dyn FnOnce(OpenTableRequest) -> OpenTableRequest + Send>;
|
||||||
@@ -170,6 +167,9 @@ pub struct CreateTableRequest {
|
|||||||
/// Optional custom location for the table. If not provided, the database will
|
/// Optional custom location for the table. If not provided, the database will
|
||||||
/// derive a location based on its URI and the table name.
|
/// derive a location based on its URI and the table name.
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
|
/// Optional namespace client for server-side query execution.
|
||||||
|
/// When set, queries will be executed on the namespace server instead of locally.
|
||||||
|
pub namespace_client: Option<Arc<dyn LanceNamespace>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateTableRequest {
|
impl CreateTableRequest {
|
||||||
@@ -181,6 +181,7 @@ impl CreateTableRequest {
|
|||||||
mode: CreateTableMode::default(),
|
mode: CreateTableMode::default(),
|
||||||
write_options: WriteOptions::default(),
|
write_options: WriteOptions::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,13 +248,30 @@ pub trait Database:
|
|||||||
/// Get the read consistency of the database
|
/// Get the read consistency of the database
|
||||||
async fn read_consistency(&self) -> Result<ReadConsistency>;
|
async fn read_consistency(&self) -> Result<ReadConsistency>;
|
||||||
/// List immediate child namespace names in the given namespace
|
/// List immediate child namespace names in the given namespace
|
||||||
async fn list_namespaces(&self, request: ListNamespacesRequest) -> Result<Vec<String>>;
|
async fn list_namespaces(
|
||||||
|
&self,
|
||||||
|
request: ListNamespacesRequest,
|
||||||
|
) -> Result<ListNamespacesResponse>;
|
||||||
/// Create a new namespace
|
/// Create a new namespace
|
||||||
async fn create_namespace(&self, request: CreateNamespaceRequest) -> Result<()>;
|
async fn create_namespace(
|
||||||
|
&self,
|
||||||
|
request: CreateNamespaceRequest,
|
||||||
|
) -> Result<CreateNamespaceResponse>;
|
||||||
/// Drop a namespace
|
/// Drop a namespace
|
||||||
async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<()>;
|
async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<DropNamespaceResponse>;
|
||||||
|
/// Describe a namespace (get its properties)
|
||||||
|
async fn describe_namespace(
|
||||||
|
&self,
|
||||||
|
request: DescribeNamespaceRequest,
|
||||||
|
) -> Result<DescribeNamespaceResponse>;
|
||||||
/// List the names of tables in the database
|
/// List the names of tables in the database
|
||||||
|
///
|
||||||
|
/// # Deprecated
|
||||||
|
/// Use `list_tables` instead for pagination support
|
||||||
|
#[deprecated(note = "Use list_tables instead")]
|
||||||
async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>>;
|
async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>>;
|
||||||
|
/// List tables in the database with pagination support
|
||||||
|
async fn list_tables(&self, request: ListTablesRequest) -> Result<ListTablesResponse>;
|
||||||
/// Create a table in the database
|
/// Create a table in the database
|
||||||
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>>;
|
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>>;
|
||||||
/// Clone a table in the database.
|
/// Clone a table in the database.
|
||||||
|
|||||||
@@ -24,10 +24,15 @@ use crate::io::object_store::MirroringObjectStoreWrapper;
|
|||||||
use crate::table::NativeTable;
|
use crate::table::NativeTable;
|
||||||
use crate::utils::validate_table_name;
|
use crate::utils::validate_table_name;
|
||||||
|
|
||||||
|
use lance_namespace::models::{
|
||||||
|
CreateNamespaceRequest, CreateNamespaceResponse, DescribeNamespaceRequest,
|
||||||
|
DescribeNamespaceResponse, DropNamespaceRequest, DropNamespaceResponse, ListNamespacesRequest,
|
||||||
|
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
BaseTable, CloneTableRequest, CreateNamespaceRequest, CreateTableMode, CreateTableRequest,
|
BaseTable, CloneTableRequest, CreateTableMode, CreateTableRequest, Database, DatabaseOptions,
|
||||||
Database, DatabaseOptions, DropNamespaceRequest, ListNamespacesRequest, OpenTableRequest,
|
OpenTableRequest, TableNamesRequest,
|
||||||
TableNamesRequest,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// File extension to indicate a lance table
|
/// File extension to indicate a lance table
|
||||||
@@ -641,6 +646,7 @@ impl ListingDatabase {
|
|||||||
index_cache_size: None,
|
index_cache_size: None,
|
||||||
lance_read_params: None,
|
lance_read_params: None,
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
};
|
};
|
||||||
let req = (callback)(req);
|
let req = (callback)(req);
|
||||||
let table = self.open_table(req).await?;
|
let table = self.open_table(req).await?;
|
||||||
@@ -662,14 +668,20 @@ impl ListingDatabase {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl Database for ListingDatabase {
|
impl Database for ListingDatabase {
|
||||||
async fn list_namespaces(&self, request: ListNamespacesRequest) -> Result<Vec<String>> {
|
async fn list_namespaces(
|
||||||
if !request.namespace.is_empty() {
|
&self,
|
||||||
|
request: ListNamespacesRequest,
|
||||||
|
) -> Result<ListNamespacesResponse> {
|
||||||
|
if request.id.as_ref().map(|v| !v.is_empty()).unwrap_or(false) {
|
||||||
return Err(Error::NotSupported {
|
return Err(Error::NotSupported {
|
||||||
message: "Namespace operations are not supported for listing database".into(),
|
message: "Namespace operations are not supported for listing database".into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Vec::new())
|
Ok(ListNamespacesResponse {
|
||||||
|
namespaces: Vec::new(),
|
||||||
|
page_token: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uri(&self) -> &str {
|
fn uri(&self) -> &str {
|
||||||
@@ -688,13 +700,28 @@ impl Database for ListingDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_namespace(&self, _request: CreateNamespaceRequest) -> Result<()> {
|
async fn create_namespace(
|
||||||
|
&self,
|
||||||
|
_request: CreateNamespaceRequest,
|
||||||
|
) -> Result<CreateNamespaceResponse> {
|
||||||
Err(Error::NotSupported {
|
Err(Error::NotSupported {
|
||||||
message: "Namespace operations are not supported for listing database".into(),
|
message: "Namespace operations are not supported for listing database".into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn drop_namespace(&self, _request: DropNamespaceRequest) -> Result<()> {
|
async fn drop_namespace(
|
||||||
|
&self,
|
||||||
|
_request: DropNamespaceRequest,
|
||||||
|
) -> Result<DropNamespaceResponse> {
|
||||||
|
Err(Error::NotSupported {
|
||||||
|
message: "Namespace operations are not supported for listing database".into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn describe_namespace(
|
||||||
|
&self,
|
||||||
|
_request: DescribeNamespaceRequest,
|
||||||
|
) -> Result<DescribeNamespaceResponse> {
|
||||||
Err(Error::NotSupported {
|
Err(Error::NotSupported {
|
||||||
message: "Namespace operations are not supported for listing database".into(),
|
message: "Namespace operations are not supported for listing database".into(),
|
||||||
})
|
})
|
||||||
@@ -735,6 +762,57 @@ impl Database for ListingDatabase {
|
|||||||
Ok(f)
|
Ok(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_tables(&self, request: ListTablesRequest) -> Result<ListTablesResponse> {
|
||||||
|
if request.id.as_ref().map(|v| !v.is_empty()).unwrap_or(false) {
|
||||||
|
return Err(Error::NotSupported {
|
||||||
|
message: "Namespace parameter is not supported for listing database. Only root namespace is supported.".into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut f = self
|
||||||
|
.object_store
|
||||||
|
.read_dir(self.base_path.clone())
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(Path::new)
|
||||||
|
.filter(|path| {
|
||||||
|
let is_lance = path
|
||||||
|
.extension()
|
||||||
|
.and_then(|e| e.to_str())
|
||||||
|
.map(|e| e == LANCE_EXTENSION);
|
||||||
|
is_lance.unwrap_or(false)
|
||||||
|
})
|
||||||
|
.filter_map(|p| p.file_stem().and_then(|s| s.to_str().map(String::from)))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
f.sort();
|
||||||
|
|
||||||
|
// Handle pagination with page_token
|
||||||
|
if let Some(ref page_token) = request.page_token {
|
||||||
|
let index = f
|
||||||
|
.iter()
|
||||||
|
.position(|name| name.as_str() > page_token.as_str())
|
||||||
|
.unwrap_or(f.len());
|
||||||
|
f.drain(0..index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if there's a next page
|
||||||
|
let next_page_token = if let Some(limit) = request.limit {
|
||||||
|
if f.len() > limit as usize {
|
||||||
|
let token = f[limit as usize].clone();
|
||||||
|
f.truncate(limit as usize);
|
||||||
|
Some(token)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ListTablesResponse {
|
||||||
|
tables: f,
|
||||||
|
page_token: next_page_token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>> {
|
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>> {
|
||||||
// When namespace is not empty, location must be provided
|
// When namespace is not empty, location must be provided
|
||||||
if !request.namespace.is_empty() && request.location.is_none() {
|
if !request.namespace.is_empty() && request.location.is_none() {
|
||||||
@@ -768,6 +846,7 @@ impl Database for ListingDatabase {
|
|||||||
self.store_wrapper.clone(),
|
self.store_wrapper.clone(),
|
||||||
Some(write_params),
|
Some(write_params),
|
||||||
self.read_consistency_interval,
|
self.read_consistency_interval,
|
||||||
|
request.namespace_client,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -839,6 +918,7 @@ impl Database for ListingDatabase {
|
|||||||
self.store_wrapper.clone(),
|
self.store_wrapper.clone(),
|
||||||
None,
|
None,
|
||||||
self.read_consistency_interval,
|
self.read_consistency_interval,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -910,6 +990,7 @@ impl Database for ListingDatabase {
|
|||||||
self.store_wrapper.clone(),
|
self.store_wrapper.clone(),
|
||||||
Some(read_params),
|
Some(read_params),
|
||||||
self.read_consistency_interval,
|
self.read_consistency_interval,
|
||||||
|
request.namespace_client,
|
||||||
)
|
)
|
||||||
.await?,
|
.await?,
|
||||||
);
|
);
|
||||||
@@ -947,6 +1028,7 @@ impl Database for ListingDatabase {
|
|||||||
self.drop_tables(vec![name.to_string()]).await
|
self.drop_tables(vec![name.to_string()]).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
async fn drop_all_tables(&self, namespace: &[String]) -> Result<()> {
|
async fn drop_all_tables(&self, namespace: &[String]) -> Result<()> {
|
||||||
// Check if namespace parameter is provided
|
// Check if namespace parameter is provided
|
||||||
if !namespace.is_empty() {
|
if !namespace.is_empty() {
|
||||||
@@ -1011,6 +1093,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1032,6 +1115,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Verify both tables exist
|
// Verify both tables exist
|
||||||
|
#[allow(deprecated)]
|
||||||
let table_names = db.table_names(TableNamesRequest::default()).await.unwrap();
|
let table_names = db.table_names(TableNamesRequest::default()).await.unwrap();
|
||||||
assert!(table_names.contains(&"source_table".to_string()));
|
assert!(table_names.contains(&"source_table".to_string()));
|
||||||
assert!(table_names.contains(&"cloned_table".to_string()));
|
assert!(table_names.contains(&"cloned_table".to_string()));
|
||||||
@@ -1075,6 +1159,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1133,6 +1218,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1168,6 +1254,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1207,6 +1294,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1246,6 +1334,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1300,6 +1389,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1357,6 +1447,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1442,6 +1533,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1528,6 +1620,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1621,6 +1714,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1718,6 +1812,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options: Default::default(),
|
write_options: Default::default(),
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1771,6 +1866,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options,
|
write_options,
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1844,6 +1940,7 @@ mod tests {
|
|||||||
mode: CreateTableMode::Create,
|
mode: CreateTableMode::Create,
|
||||||
write_options,
|
write_options,
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ use async_trait::async_trait;
|
|||||||
use lance_io::object_store::{LanceNamespaceStorageOptionsProvider, StorageOptionsProvider};
|
use lance_io::object_store::{LanceNamespaceStorageOptionsProvider, StorageOptionsProvider};
|
||||||
use lance_namespace::{
|
use lance_namespace::{
|
||||||
models::{
|
models::{
|
||||||
CreateEmptyTableRequest, CreateNamespaceRequest, DescribeTableRequest,
|
CreateEmptyTableRequest, CreateNamespaceRequest, CreateNamespaceResponse,
|
||||||
DropNamespaceRequest, DropTableRequest, ListNamespacesRequest, ListTablesRequest,
|
DescribeNamespaceRequest, DescribeNamespaceResponse, DescribeTableRequest,
|
||||||
|
DropNamespaceRequest, DropNamespaceResponse, DropTableRequest, ListNamespacesRequest,
|
||||||
|
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
||||||
},
|
},
|
||||||
LanceNamespace,
|
LanceNamespace,
|
||||||
};
|
};
|
||||||
@@ -22,11 +24,8 @@ use crate::database::ReadConsistency;
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
listing::ListingDatabase, BaseTable, CloneTableRequest,
|
listing::ListingDatabase, BaseTable, CloneTableRequest, CreateTableMode,
|
||||||
CreateNamespaceRequest as DbCreateNamespaceRequest, CreateTableMode,
|
CreateTableRequest as DbCreateTableRequest, Database, OpenTableRequest, TableNamesRequest,
|
||||||
CreateTableRequest as DbCreateTableRequest, Database,
|
|
||||||
DropNamespaceRequest as DbDropNamespaceRequest,
|
|
||||||
ListNamespacesRequest as DbListNamespacesRequest, OpenTableRequest, TableNamesRequest,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A database implementation that uses lance-namespace for table management
|
/// A database implementation that uses lance-namespace for table management
|
||||||
@@ -40,6 +39,8 @@ pub struct LanceNamespaceDatabase {
|
|||||||
session: Option<Arc<lance::session::Session>>,
|
session: Option<Arc<lance::session::Session>>,
|
||||||
// database URI
|
// database URI
|
||||||
uri: String,
|
uri: String,
|
||||||
|
// Whether to enable server-side query execution
|
||||||
|
server_side_query_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanceNamespaceDatabase {
|
impl LanceNamespaceDatabase {
|
||||||
@@ -49,6 +50,7 @@ impl LanceNamespaceDatabase {
|
|||||||
storage_options: HashMap<String, String>,
|
storage_options: HashMap<String, String>,
|
||||||
read_consistency_interval: Option<std::time::Duration>,
|
read_consistency_interval: Option<std::time::Duration>,
|
||||||
session: Option<Arc<lance::session::Session>>,
|
session: Option<Arc<lance::session::Session>>,
|
||||||
|
server_side_query_enabled: bool,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let mut builder = ConnectBuilder::new(ns_impl);
|
let mut builder = ConnectBuilder::new(ns_impl);
|
||||||
for (key, value) in ns_properties.clone() {
|
for (key, value) in ns_properties.clone() {
|
||||||
@@ -67,6 +69,7 @@ impl LanceNamespaceDatabase {
|
|||||||
read_consistency_interval,
|
read_consistency_interval,
|
||||||
session,
|
session,
|
||||||
uri: format!("namespace://{}", ns_impl),
|
uri: format!("namespace://{}", ns_impl),
|
||||||
|
server_side_query_enabled,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +79,7 @@ impl std::fmt::Debug for LanceNamespaceDatabase {
|
|||||||
f.debug_struct("LanceNamespaceDatabase")
|
f.debug_struct("LanceNamespaceDatabase")
|
||||||
.field("storage_options", &self.storage_options)
|
.field("storage_options", &self.storage_options)
|
||||||
.field("read_consistency_interval", &self.read_consistency_interval)
|
.field("read_consistency_interval", &self.read_consistency_interval)
|
||||||
|
.field("server_side_query_enabled", &self.server_side_query_enabled)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,92 +153,47 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_namespaces(&self, request: DbListNamespacesRequest) -> Result<Vec<String>> {
|
async fn list_namespaces(
|
||||||
let ns_request = ListNamespacesRequest {
|
&self,
|
||||||
id: if request.namespace.is_empty() {
|
request: ListNamespacesRequest,
|
||||||
None
|
) -> Result<ListNamespacesResponse> {
|
||||||
} else {
|
Ok(self.namespace.list_namespaces(request).await?)
|
||||||
Some(request.namespace)
|
|
||||||
},
|
|
||||||
page_token: request.page_token,
|
|
||||||
limit: request.limit.map(|l| l as i32),
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = self
|
|
||||||
.namespace
|
|
||||||
.list_namespaces(ns_request)
|
|
||||||
.await
|
|
||||||
.map_err(|e| Error::Runtime {
|
|
||||||
message: format!("Failed to list namespaces: {}", e),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(response.namespaces)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_namespace(&self, request: DbCreateNamespaceRequest) -> Result<()> {
|
async fn create_namespace(
|
||||||
let ns_request = CreateNamespaceRequest {
|
&self,
|
||||||
id: if request.namespace.is_empty() {
|
request: CreateNamespaceRequest,
|
||||||
None
|
) -> Result<CreateNamespaceResponse> {
|
||||||
} else {
|
Ok(self.namespace.create_namespace(request).await?)
|
||||||
Some(request.namespace)
|
|
||||||
},
|
|
||||||
mode: None,
|
|
||||||
properties: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.namespace
|
|
||||||
.create_namespace(ns_request)
|
|
||||||
.await
|
|
||||||
.map_err(|e| Error::Runtime {
|
|
||||||
message: format!("Failed to create namespace: {}", e),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn drop_namespace(&self, request: DbDropNamespaceRequest) -> Result<()> {
|
async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<DropNamespaceResponse> {
|
||||||
let ns_request = DropNamespaceRequest {
|
Ok(self.namespace.drop_namespace(request).await?)
|
||||||
id: if request.namespace.is_empty() {
|
}
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(request.namespace)
|
|
||||||
},
|
|
||||||
mode: None,
|
|
||||||
behavior: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.namespace
|
async fn describe_namespace(
|
||||||
.drop_namespace(ns_request)
|
&self,
|
||||||
.await
|
request: DescribeNamespaceRequest,
|
||||||
.map_err(|e| Error::Runtime {
|
) -> Result<DescribeNamespaceResponse> {
|
||||||
message: format!("Failed to drop namespace: {}", e),
|
Ok(self.namespace.describe_namespace(request).await?)
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>> {
|
async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>> {
|
||||||
let ns_request = ListTablesRequest {
|
let ns_request = ListTablesRequest {
|
||||||
id: if request.namespace.is_empty() {
|
id: Some(request.namespace),
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(request.namespace)
|
|
||||||
},
|
|
||||||
page_token: request.start_after,
|
page_token: request.start_after,
|
||||||
limit: request.limit.map(|l| l as i32),
|
limit: request.limit.map(|l| l as i32),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response =
|
let response = self.namespace.list_tables(ns_request).await?;
|
||||||
self.namespace
|
|
||||||
.list_tables(ns_request)
|
|
||||||
.await
|
|
||||||
.map_err(|e| Error::Runtime {
|
|
||||||
message: format!("Failed to list tables: {}", e),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(response.tables)
|
Ok(response.tables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_tables(&self, request: ListTablesRequest) -> Result<ListTablesResponse> {
|
||||||
|
Ok(self.namespace.list_tables(request).await?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_table(&self, request: DbCreateTableRequest) -> Result<Arc<dyn BaseTable>> {
|
async fn create_table(&self, request: DbCreateTableRequest) -> Result<Arc<dyn BaseTable>> {
|
||||||
// Extract user-provided storage options from request
|
// Extract user-provided storage options from request
|
||||||
let user_storage_options = request
|
let user_storage_options = request
|
||||||
@@ -290,6 +249,10 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let namespace_client = self
|
||||||
|
.server_side_query_enabled
|
||||||
|
.then(|| self.namespace.clone());
|
||||||
|
|
||||||
return listing_db
|
return listing_db
|
||||||
.open_table(OpenTableRequest {
|
.open_table(OpenTableRequest {
|
||||||
name: request.name.clone(),
|
name: request.name.clone(),
|
||||||
@@ -297,6 +260,7 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
index_cache_size: None,
|
index_cache_size: None,
|
||||||
lance_read_params: None,
|
lance_read_params: None,
|
||||||
location: Some(location),
|
location: Some(location),
|
||||||
|
namespace_client,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -333,12 +297,16 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
let listing_db = self
|
let listing_db = self
|
||||||
.create_listing_database(
|
.create_listing_database(
|
||||||
&location,
|
&location,
|
||||||
table_id,
|
table_id.clone(),
|
||||||
user_storage_options,
|
user_storage_options,
|
||||||
create_empty_response.storage_options.as_ref(),
|
create_empty_response.storage_options.as_ref(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let namespace_client = self
|
||||||
|
.server_side_query_enabled
|
||||||
|
.then(|| self.namespace.clone());
|
||||||
|
|
||||||
let create_request = DbCreateTableRequest {
|
let create_request = DbCreateTableRequest {
|
||||||
name: request.name,
|
name: request.name,
|
||||||
namespace: request.namespace,
|
namespace: request.namespace,
|
||||||
@@ -346,7 +314,9 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
mode: request.mode,
|
mode: request.mode,
|
||||||
write_options: request.write_options,
|
write_options: request.write_options,
|
||||||
location: Some(location),
|
location: Some(location),
|
||||||
|
namespace_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
listing_db.create_table(create_request).await
|
listing_db.create_table(create_request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,19 +350,25 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
let listing_db = self
|
let listing_db = self
|
||||||
.create_listing_database(
|
.create_listing_database(
|
||||||
&location,
|
&location,
|
||||||
table_id,
|
table_id.clone(),
|
||||||
user_storage_options,
|
user_storage_options,
|
||||||
response.storage_options.as_ref(),
|
response.storage_options.as_ref(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let namespace_client = self
|
||||||
|
.server_side_query_enabled
|
||||||
|
.then(|| self.namespace.clone());
|
||||||
|
|
||||||
let open_request = OpenTableRequest {
|
let open_request = OpenTableRequest {
|
||||||
name: request.name.clone(),
|
name: request.name.clone(),
|
||||||
namespace: request.namespace.clone(),
|
namespace: request.namespace.clone(),
|
||||||
index_cache_size: request.index_cache_size,
|
index_cache_size: request.index_cache_size,
|
||||||
lance_read_params: request.lance_read_params,
|
lance_read_params: request.lance_read_params,
|
||||||
location: Some(location),
|
location: Some(location),
|
||||||
|
namespace_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
listing_db.open_table(open_request).await
|
listing_db.open_table(open_request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,6 +405,7 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
async fn drop_all_tables(&self, namespace: &[String]) -> Result<()> {
|
async fn drop_all_tables(&self, namespace: &[String]) -> Result<()> {
|
||||||
let tables = self
|
let tables = self
|
||||||
.table_names(TableNamesRequest {
|
.table_names(TableNamesRequest {
|
||||||
@@ -455,7 +432,6 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::connect_namespace;
|
use crate::connect_namespace;
|
||||||
use crate::database::CreateNamespaceRequest;
|
|
||||||
use crate::query::ExecutableQuery;
|
use crate::query::ExecutableQuery;
|
||||||
use arrow_array::{Int32Array, RecordBatch, RecordBatchIterator, StringArray};
|
use arrow_array::{Int32Array, RecordBatch, RecordBatchIterator, StringArray};
|
||||||
use arrow_schema::{DataType, Field, Schema};
|
use arrow_schema::{DataType, Field, Schema};
|
||||||
@@ -568,7 +544,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -627,7 +605,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -689,7 +669,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -771,7 +753,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -825,7 +809,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -904,7 +890,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -936,7 +924,9 @@ mod tests {
|
|||||||
|
|
||||||
// Create a child namespace first
|
// Create a child namespace first
|
||||||
conn.create_namespace(CreateNamespaceRequest {
|
conn.create_namespace(CreateNamespaceRequest {
|
||||||
namespace: vec!["test_ns".into()],
|
id: Some(vec!["test_ns".into()]),
|
||||||
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -977,4 +967,46 @@ mod tests {
|
|||||||
let open_result = conn.open_table("drop_test").execute().await;
|
let open_result = conn.open_table("drop_test").execute().await;
|
||||||
assert!(open_result.is_err());
|
assert!(open_result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_table_names_at_root() {
|
||||||
|
// Test that table_names at root (empty namespace) works correctly
|
||||||
|
// This is a regression test for a bug where empty namespace was converted to None
|
||||||
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let root_path = tmp_dir.path().to_str().unwrap().to_string();
|
||||||
|
|
||||||
|
let mut properties = HashMap::new();
|
||||||
|
properties.insert("root".to_string(), root_path);
|
||||||
|
|
||||||
|
let conn = connect_namespace("dir", properties)
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.expect("Failed to connect to namespace");
|
||||||
|
|
||||||
|
// Create multiple tables at root namespace
|
||||||
|
let test_data1 = create_test_data();
|
||||||
|
let _table1 = conn
|
||||||
|
.create_table("table1", test_data1)
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.expect("Failed to create table1 at root");
|
||||||
|
|
||||||
|
let test_data2 = create_test_data();
|
||||||
|
let _table2 = conn
|
||||||
|
.create_table("table2", test_data2)
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.expect("Failed to create table2 at root");
|
||||||
|
|
||||||
|
// List tables at root using table_names (empty namespace means root)
|
||||||
|
let table_names = conn
|
||||||
|
.table_names()
|
||||||
|
.execute()
|
||||||
|
.await
|
||||||
|
.expect("Failed to list tables at root");
|
||||||
|
|
||||||
|
assert!(table_names.contains(&"table1".to_string()));
|
||||||
|
assert!(table_names.contains(&"table2".to_string()));
|
||||||
|
assert_eq!(table_names.len(), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::{table::BaseTable, DistanceType, Error, Result};
|
|||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
scalar::{BTreeIndexBuilder, BitmapIndexBuilder, LabelListIndexBuilder},
|
scalar::{BTreeIndexBuilder, BitmapIndexBuilder, LabelListIndexBuilder},
|
||||||
vector::{IvfHnswPqIndexBuilder, IvfHnswSqIndexBuilder, IvfPqIndexBuilder},
|
vector::{IvfHnswPqIndexBuilder, IvfHnswSqIndexBuilder, IvfPqIndexBuilder, IvfSqIndexBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod scalar;
|
pub mod scalar;
|
||||||
@@ -54,6 +54,9 @@ pub enum Index {
|
|||||||
/// IVF index with Product Quantization
|
/// IVF index with Product Quantization
|
||||||
IvfPq(IvfPqIndexBuilder),
|
IvfPq(IvfPqIndexBuilder),
|
||||||
|
|
||||||
|
/// IVF index with Scalar Quantization
|
||||||
|
IvfSq(IvfSqIndexBuilder),
|
||||||
|
|
||||||
/// IVF index with RabitQ Quantization
|
/// IVF index with RabitQ Quantization
|
||||||
IvfRq(IvfRqIndexBuilder),
|
IvfRq(IvfRqIndexBuilder),
|
||||||
|
|
||||||
@@ -277,6 +280,8 @@ pub enum IndexType {
|
|||||||
// Vector
|
// Vector
|
||||||
#[serde(alias = "IVF_FLAT")]
|
#[serde(alias = "IVF_FLAT")]
|
||||||
IvfFlat,
|
IvfFlat,
|
||||||
|
#[serde(alias = "IVF_SQ")]
|
||||||
|
IvfSq,
|
||||||
#[serde(alias = "IVF_PQ")]
|
#[serde(alias = "IVF_PQ")]
|
||||||
IvfPq,
|
IvfPq,
|
||||||
#[serde(alias = "IVF_RQ")]
|
#[serde(alias = "IVF_RQ")]
|
||||||
@@ -301,6 +306,7 @@ impl std::fmt::Display for IndexType {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::IvfFlat => write!(f, "IVF_FLAT"),
|
Self::IvfFlat => write!(f, "IVF_FLAT"),
|
||||||
|
Self::IvfSq => write!(f, "IVF_SQ"),
|
||||||
Self::IvfPq => write!(f, "IVF_PQ"),
|
Self::IvfPq => write!(f, "IVF_PQ"),
|
||||||
Self::IvfRq => write!(f, "IVF_RQ"),
|
Self::IvfRq => write!(f, "IVF_RQ"),
|
||||||
Self::IvfHnswPq => write!(f, "IVF_HNSW_PQ"),
|
Self::IvfHnswPq => write!(f, "IVF_HNSW_PQ"),
|
||||||
@@ -323,6 +329,7 @@ impl std::str::FromStr for IndexType {
|
|||||||
"LABEL_LIST" | "LABELLIST" => Ok(Self::LabelList),
|
"LABEL_LIST" | "LABELLIST" => Ok(Self::LabelList),
|
||||||
"FTS" | "INVERTED" => Ok(Self::FTS),
|
"FTS" | "INVERTED" => Ok(Self::FTS),
|
||||||
"IVF_FLAT" => Ok(Self::IvfFlat),
|
"IVF_FLAT" => Ok(Self::IvfFlat),
|
||||||
|
"IVF_SQ" => Ok(Self::IvfSq),
|
||||||
"IVF_PQ" => Ok(Self::IvfPq),
|
"IVF_PQ" => Ok(Self::IvfPq),
|
||||||
"IVF_RQ" => Ok(Self::IvfRq),
|
"IVF_RQ" => Ok(Self::IvfRq),
|
||||||
"IVF_HNSW_PQ" => Ok(Self::IvfHnswPq),
|
"IVF_HNSW_PQ" => Ok(Self::IvfHnswPq),
|
||||||
|
|||||||
@@ -209,6 +209,38 @@ impl IvfFlatIndexBuilder {
|
|||||||
impl_ivf_params_setter!();
|
impl_ivf_params_setter!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builder for an IVF SQ index.
|
||||||
|
///
|
||||||
|
/// This index compresses vectors using scalar quantization and groups them into IVF partitions.
|
||||||
|
/// It offers a balance between search performance and storage footprint.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct IvfSqIndexBuilder {
|
||||||
|
pub(crate) distance_type: DistanceType,
|
||||||
|
|
||||||
|
// IVF
|
||||||
|
pub(crate) num_partitions: Option<u32>,
|
||||||
|
pub(crate) sample_rate: u32,
|
||||||
|
pub(crate) max_iterations: u32,
|
||||||
|
pub(crate) target_partition_size: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IvfSqIndexBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
distance_type: DistanceType::L2,
|
||||||
|
num_partitions: None,
|
||||||
|
sample_rate: 256,
|
||||||
|
max_iterations: 50,
|
||||||
|
target_partition_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IvfSqIndexBuilder {
|
||||||
|
impl_distance_type_setter!();
|
||||||
|
impl_ivf_params_setter!();
|
||||||
|
}
|
||||||
|
|
||||||
/// Builder for an IVF PQ index.
|
/// Builder for an IVF PQ index.
|
||||||
///
|
///
|
||||||
/// This index stores a compressed (quantized) copy of every vector. These vectors
|
/// This index stores a compressed (quantized) copy of every vector. These vectors
|
||||||
|
|||||||
@@ -10,13 +10,17 @@ use http::StatusCode;
|
|||||||
use lance_io::object_store::StorageOptions;
|
use lance_io::object_store::StorageOptions;
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use reqwest::header::CONTENT_TYPE;
|
use reqwest::header::CONTENT_TYPE;
|
||||||
use serde::Deserialize;
|
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
|
use lance_namespace::models::{
|
||||||
|
CreateNamespaceRequest, CreateNamespaceResponse, DescribeNamespaceRequest,
|
||||||
|
DescribeNamespaceResponse, DropNamespaceRequest, DropNamespaceResponse, ListNamespacesRequest,
|
||||||
|
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::database::{
|
use crate::database::{
|
||||||
CloneTableRequest, CreateNamespaceRequest, CreateTableData, CreateTableMode,
|
CloneTableRequest, CreateTableData, CreateTableMode, CreateTableRequest, Database,
|
||||||
CreateTableRequest, Database, DatabaseOptions, DropNamespaceRequest, ListNamespacesRequest,
|
DatabaseOptions, OpenTableRequest, ReadConsistency, TableNamesRequest,
|
||||||
OpenTableRequest, ReadConsistency, TableNamesRequest,
|
|
||||||
};
|
};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::table::BaseTable;
|
use crate::table::BaseTable;
|
||||||
@@ -180,11 +184,6 @@ impl RemoteDatabaseOptionsBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ListTablesResponse {
|
|
||||||
tables: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RemoteDatabase<S: HttpSend = Sender> {
|
pub struct RemoteDatabase<S: HttpSend = Sender> {
|
||||||
client: RestfulLanceDbClient<S>,
|
client: RestfulLanceDbClient<S>,
|
||||||
@@ -337,7 +336,6 @@ impl<S: HttpSend> Database for RemoteDatabase<S> {
|
|||||||
self.client
|
self.client
|
||||||
.get(&format!("/v1/namespace/{}/table/list", namespace_id))
|
.get(&format!("/v1/namespace/{}/table/list", namespace_id))
|
||||||
} else {
|
} else {
|
||||||
// TODO: use new API for all listing operations once stable
|
|
||||||
self.client.get("/v1/table/")
|
self.client.get("/v1/table/")
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -371,6 +369,44 @@ impl<S: HttpSend> Database for RemoteDatabase<S> {
|
|||||||
Ok(tables)
|
Ok(tables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_tables(&self, request: ListTablesRequest) -> Result<ListTablesResponse> {
|
||||||
|
let namespace_parts = request.id.as_deref().unwrap_or(&[]);
|
||||||
|
let namespace_id = build_namespace_identifier(namespace_parts, &self.client.id_delimiter);
|
||||||
|
let mut req = self
|
||||||
|
.client
|
||||||
|
.get(&format!("/v1/namespace/{}/table/list", namespace_id));
|
||||||
|
|
||||||
|
if let Some(limit) = request.limit {
|
||||||
|
req = req.query(&[("limit", limit)]);
|
||||||
|
}
|
||||||
|
if let Some(ref page_token) = request.page_token {
|
||||||
|
req = req.query(&[("page_token", page_token)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (request_id, rsp) = self.client.send_with_retry(req, None, true).await?;
|
||||||
|
let rsp = self.client.check_response(&request_id, rsp).await?;
|
||||||
|
let version = parse_server_version(&request_id, &rsp)?;
|
||||||
|
let response: ListTablesResponse = rsp.json().await.err_to_http(request_id)?;
|
||||||
|
|
||||||
|
// Cache the tables for future use
|
||||||
|
let namespace_vec = namespace_parts.to_vec();
|
||||||
|
for table in &response.tables {
|
||||||
|
let table_identifier =
|
||||||
|
build_table_identifier(table, &namespace_vec, &self.client.id_delimiter);
|
||||||
|
let cache_key = build_cache_key(table, &namespace_vec);
|
||||||
|
let remote_table = Arc::new(RemoteTable::new(
|
||||||
|
self.client.clone(),
|
||||||
|
table.clone(),
|
||||||
|
namespace_vec.clone(),
|
||||||
|
table_identifier.clone(),
|
||||||
|
version.clone(),
|
||||||
|
));
|
||||||
|
self.table_cache.insert(cache_key, remote_table).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>> {
|
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>> {
|
||||||
let data = match request.data {
|
let data = match request.data {
|
||||||
CreateTableData::Data(data) => data,
|
CreateTableData::Data(data) => data,
|
||||||
@@ -417,6 +453,7 @@ impl<S: HttpSend> Database for RemoteDatabase<S> {
|
|||||||
index_cache_size: None,
|
index_cache_size: None,
|
||||||
lance_read_params: None,
|
lance_read_params: None,
|
||||||
location: None,
|
location: None,
|
||||||
|
namespace_client: None,
|
||||||
};
|
};
|
||||||
let req = (callback)(req);
|
let req = (callback)(req);
|
||||||
self.open_table(req).await
|
self.open_table(req).await
|
||||||
@@ -590,53 +627,101 @@ impl<S: HttpSend> Database for RemoteDatabase<S> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_namespaces(&self, request: ListNamespacesRequest) -> Result<Vec<String>> {
|
async fn list_namespaces(
|
||||||
let namespace_id =
|
&self,
|
||||||
build_namespace_identifier(request.namespace.as_slice(), &self.client.id_delimiter);
|
request: ListNamespacesRequest,
|
||||||
|
) -> Result<ListNamespacesResponse> {
|
||||||
|
let namespace_parts = request.id.as_deref().unwrap_or(&[]);
|
||||||
|
let namespace_id = build_namespace_identifier(namespace_parts, &self.client.id_delimiter);
|
||||||
let mut req = self
|
let mut req = self
|
||||||
.client
|
.client
|
||||||
.get(&format!("/v1/namespace/{}/list", namespace_id));
|
.get(&format!("/v1/namespace/{}/list", namespace_id));
|
||||||
if let Some(limit) = request.limit {
|
if let Some(limit) = request.limit {
|
||||||
req = req.query(&[("limit", limit)]);
|
req = req.query(&[("limit", limit)]);
|
||||||
}
|
}
|
||||||
if let Some(page_token) = request.page_token {
|
if let Some(ref page_token) = request.page_token {
|
||||||
req = req.query(&[("page_token", page_token)]);
|
req = req.query(&[("page_token", page_token)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (request_id, resp) = self.client.send(req).await?;
|
let (request_id, resp) = self.client.send(req).await?;
|
||||||
let resp = self.client.check_response(&request_id, resp).await?;
|
let resp = self.client.check_response(&request_id, resp).await?;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
resp.json().await.err_to_http(request_id)
|
||||||
struct ListNamespacesResponse {
|
|
||||||
namespaces: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed: ListNamespacesResponse = resp.json().await.map_err(|e| Error::Runtime {
|
async fn create_namespace(
|
||||||
message: format!("Failed to parse namespace response: {}", e),
|
&self,
|
||||||
})?;
|
request: CreateNamespaceRequest,
|
||||||
Ok(parsed.namespaces)
|
) -> Result<CreateNamespaceResponse> {
|
||||||
}
|
let namespace_parts = request.id.as_deref().unwrap_or(&[]);
|
||||||
|
let namespace_id = build_namespace_identifier(namespace_parts, &self.client.id_delimiter);
|
||||||
async fn create_namespace(&self, request: CreateNamespaceRequest) -> Result<()> {
|
let mut req = self
|
||||||
let namespace_id =
|
|
||||||
build_namespace_identifier(request.namespace.as_slice(), &self.client.id_delimiter);
|
|
||||||
let req = self
|
|
||||||
.client
|
.client
|
||||||
.post(&format!("/v1/namespace/{}/create", namespace_id));
|
.post(&format!("/v1/namespace/{}/create", namespace_id));
|
||||||
let (request_id, resp) = self.client.send(req).await?;
|
|
||||||
self.client.check_response(&request_id, resp).await?;
|
// Build request body with mode and properties if present
|
||||||
Ok(())
|
#[derive(serde::Serialize)]
|
||||||
|
struct CreateNamespaceRequestBody {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
mode: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
properties: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<()> {
|
let body = CreateNamespaceRequestBody {
|
||||||
let namespace_id =
|
mode: request.mode.as_ref().map(|m| format!("{:?}", m)),
|
||||||
build_namespace_identifier(request.namespace.as_slice(), &self.client.id_delimiter);
|
properties: request.properties,
|
||||||
let req = self
|
};
|
||||||
|
|
||||||
|
req = req.json(&body);
|
||||||
|
let (request_id, resp) = self.client.send(req).await?;
|
||||||
|
let resp = self.client.check_response(&request_id, resp).await?;
|
||||||
|
|
||||||
|
resp.json().await.err_to_http(request_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<DropNamespaceResponse> {
|
||||||
|
let namespace_parts = request.id.as_deref().unwrap_or(&[]);
|
||||||
|
let namespace_id = build_namespace_identifier(namespace_parts, &self.client.id_delimiter);
|
||||||
|
let mut req = self
|
||||||
.client
|
.client
|
||||||
.post(&format!("/v1/namespace/{}/drop", namespace_id));
|
.post(&format!("/v1/namespace/{}/drop", namespace_id));
|
||||||
|
|
||||||
|
// Build request body with mode and behavior if present
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
struct DropNamespaceRequestBody {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
mode: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
behavior: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = DropNamespaceRequestBody {
|
||||||
|
mode: request.mode.as_ref().map(|m| format!("{:?}", m)),
|
||||||
|
behavior: request.behavior.as_ref().map(|b| format!("{:?}", b)),
|
||||||
|
};
|
||||||
|
|
||||||
|
req = req.json(&body);
|
||||||
let (request_id, resp) = self.client.send(req).await?;
|
let (request_id, resp) = self.client.send(req).await?;
|
||||||
self.client.check_response(&request_id, resp).await?;
|
let resp = self.client.check_response(&request_id, resp).await?;
|
||||||
Ok(())
|
|
||||||
|
resp.json().await.err_to_http(request_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn describe_namespace(
|
||||||
|
&self,
|
||||||
|
request: DescribeNamespaceRequest,
|
||||||
|
) -> Result<DescribeNamespaceResponse> {
|
||||||
|
let namespace_parts = request.id.as_deref().unwrap_or(&[]);
|
||||||
|
let namespace_id = build_namespace_identifier(namespace_parts, &self.client.id_delimiter);
|
||||||
|
let req = self
|
||||||
|
.client
|
||||||
|
.get(&format!("/v1/namespace/{}/describe", namespace_id));
|
||||||
|
|
||||||
|
let (request_id, resp) = self.client.send(req).await?;
|
||||||
|
let resp = self.client.check_response(&request_id, resp).await?;
|
||||||
|
|
||||||
|
resp.json().await.err_to_http(request_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
|||||||
@@ -1072,6 +1072,14 @@ impl<S: HttpSend> BaseTable for RemoteTable<S> {
|
|||||||
body["num_bits"] = serde_json::Value::Number(num_bits.into());
|
body["num_bits"] = serde_json::Value::Number(num_bits.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Index::IvfSq(index) => {
|
||||||
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_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::IvfHnswSq(index) => {
|
Index::IvfHnswSq(index) => {
|
||||||
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_HNSW_SQ".to_string());
|
body[INDEX_TYPE_KEY] = serde_json::Value::String("IVF_HNSW_SQ".to_string());
|
||||||
body[METRIC_TYPE_KEY] =
|
body[METRIC_TYPE_KEY] =
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ use lance_index::vector::pq::PQBuildParams;
|
|||||||
use lance_index::vector::sq::builder::SQBuildParams;
|
use lance_index::vector::sq::builder::SQBuildParams;
|
||||||
use lance_index::DatasetIndexExt;
|
use lance_index::DatasetIndexExt;
|
||||||
use lance_index::IndexType;
|
use lance_index::IndexType;
|
||||||
|
use lance_namespace::models::{
|
||||||
|
QueryTableRequest as NsQueryTableRequest, QueryTableRequestFullTextQuery,
|
||||||
|
QueryTableRequestVector, StringFtsQuery,
|
||||||
|
};
|
||||||
|
use lance_namespace::LanceNamespace;
|
||||||
use lance_table::format::Manifest;
|
use lance_table::format::Manifest;
|
||||||
use lance_table::io::commit::ManifestNamingScheme;
|
use lance_table::io::commit::ManifestNamingScheme;
|
||||||
use log::info;
|
use log::info;
|
||||||
@@ -1480,7 +1485,7 @@ impl NativeTableExt for Arc<dyn BaseTable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A table in a LanceDB database.
|
/// A table in a LanceDB database.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NativeTable {
|
pub struct NativeTable {
|
||||||
name: String,
|
name: String,
|
||||||
namespace: Vec<String>,
|
namespace: Vec<String>,
|
||||||
@@ -1490,6 +1495,22 @@ pub struct NativeTable {
|
|||||||
// This comes from the connection options. We store here so we can pass down
|
// This comes from the connection options. We store here so we can pass down
|
||||||
// to the dataset when we recreate it (for example, in checkout_latest).
|
// to the dataset when we recreate it (for example, in checkout_latest).
|
||||||
read_consistency_interval: Option<std::time::Duration>,
|
read_consistency_interval: Option<std::time::Duration>,
|
||||||
|
// Optional namespace client for server-side query execution.
|
||||||
|
// When set, queries will be executed on the namespace server instead of locally.
|
||||||
|
namespace_client: Option<Arc<dyn LanceNamespace>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for NativeTable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("NativeTable")
|
||||||
|
.field("name", &self.name)
|
||||||
|
.field("namespace", &self.namespace)
|
||||||
|
.field("id", &self.id)
|
||||||
|
.field("uri", &self.uri)
|
||||||
|
.field("read_consistency_interval", &self.read_consistency_interval)
|
||||||
|
.field("namespace_client", &self.namespace_client)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for NativeTable {
|
impl std::fmt::Display for NativeTable {
|
||||||
@@ -1524,7 +1545,7 @@ impl NativeTable {
|
|||||||
/// * A [NativeTable] object.
|
/// * A [NativeTable] object.
|
||||||
pub async fn open(uri: &str) -> Result<Self> {
|
pub async fn open(uri: &str) -> Result<Self> {
|
||||||
let name = Self::get_table_name(uri)?;
|
let name = Self::get_table_name(uri)?;
|
||||||
Self::open_with_params(uri, &name, vec![], None, None, None).await
|
Self::open_with_params(uri, &name, vec![], None, None, None, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens an existing Table
|
/// Opens an existing Table
|
||||||
@@ -1534,10 +1555,12 @@ impl NativeTable {
|
|||||||
/// * `base_path` - The base path where the table is located
|
/// * `base_path` - The base path where the table is located
|
||||||
/// * `name` The Table name
|
/// * `name` The Table name
|
||||||
/// * `params` The [ReadParams] to use when opening the table
|
/// * `params` The [ReadParams] to use when opening the table
|
||||||
|
/// * `namespace_client` - Optional namespace client for server-side query execution
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// * A [NativeTable] object.
|
/// * A [NativeTable] object.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn open_with_params(
|
pub async fn open_with_params(
|
||||||
uri: &str,
|
uri: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
@@ -1545,6 +1568,7 @@ impl NativeTable {
|
|||||||
write_store_wrapper: Option<Arc<dyn WrappingObjectStore>>,
|
write_store_wrapper: Option<Arc<dyn WrappingObjectStore>>,
|
||||||
params: Option<ReadParams>,
|
params: Option<ReadParams>,
|
||||||
read_consistency_interval: Option<std::time::Duration>,
|
read_consistency_interval: Option<std::time::Duration>,
|
||||||
|
namespace_client: Option<Arc<dyn LanceNamespace>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let params = params.unwrap_or_default();
|
let params = params.unwrap_or_default();
|
||||||
// patch the params if we have a write store wrapper
|
// patch the params if we have a write store wrapper
|
||||||
@@ -1575,9 +1599,18 @@ impl NativeTable {
|
|||||||
uri: uri.to_string(),
|
uri: uri.to_string(),
|
||||||
dataset,
|
dataset,
|
||||||
read_consistency_interval,
|
read_consistency_interval,
|
||||||
|
namespace_client,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the namespace client for server-side query execution.
|
||||||
|
///
|
||||||
|
/// When set, queries will be executed on the namespace server instead of locally.
|
||||||
|
pub fn with_namespace_client(mut self, namespace_client: Arc<dyn LanceNamespace>) -> Self {
|
||||||
|
self.namespace_client = Some(namespace_client);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn get_table_name(uri: &str) -> Result<String> {
|
fn get_table_name(uri: &str) -> Result<String> {
|
||||||
let path = Path::new(uri);
|
let path = Path::new(uri);
|
||||||
let name = path
|
let name = path
|
||||||
@@ -1614,10 +1647,12 @@ impl NativeTable {
|
|||||||
/// * `namespace` - The namespace path. When non-empty, an explicit URI must be provided.
|
/// * `namespace` - The namespace path. When non-empty, an explicit URI must be provided.
|
||||||
/// * `batches` RecordBatch to be saved in the database.
|
/// * `batches` RecordBatch to be saved in the database.
|
||||||
/// * `params` - Write parameters.
|
/// * `params` - Write parameters.
|
||||||
|
/// * `namespace_client` - Optional namespace client for server-side query execution
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// * A [TableImpl] object.
|
/// * A [TableImpl] object.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
uri: &str,
|
uri: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
@@ -1626,6 +1661,7 @@ impl NativeTable {
|
|||||||
write_store_wrapper: Option<Arc<dyn WrappingObjectStore>>,
|
write_store_wrapper: Option<Arc<dyn WrappingObjectStore>>,
|
||||||
params: Option<WriteParams>,
|
params: Option<WriteParams>,
|
||||||
read_consistency_interval: Option<std::time::Duration>,
|
read_consistency_interval: Option<std::time::Duration>,
|
||||||
|
namespace_client: Option<Arc<dyn LanceNamespace>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
// Default params uses format v1.
|
// Default params uses format v1.
|
||||||
let params = params.unwrap_or(WriteParams {
|
let params = params.unwrap_or(WriteParams {
|
||||||
@@ -1657,9 +1693,11 @@ impl NativeTable {
|
|||||||
uri: uri.to_string(),
|
uri: uri.to_string(),
|
||||||
dataset: DatasetConsistencyWrapper::new_latest(dataset, read_consistency_interval),
|
dataset: DatasetConsistencyWrapper::new_latest(dataset, read_consistency_interval),
|
||||||
read_consistency_interval,
|
read_consistency_interval,
|
||||||
|
namespace_client,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn create_empty(
|
pub async fn create_empty(
|
||||||
uri: &str,
|
uri: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
@@ -1668,6 +1706,7 @@ impl NativeTable {
|
|||||||
write_store_wrapper: Option<Arc<dyn WrappingObjectStore>>,
|
write_store_wrapper: Option<Arc<dyn WrappingObjectStore>>,
|
||||||
params: Option<WriteParams>,
|
params: Option<WriteParams>,
|
||||||
read_consistency_interval: Option<std::time::Duration>,
|
read_consistency_interval: Option<std::time::Duration>,
|
||||||
|
namespace_client: Option<Arc<dyn LanceNamespace>>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let batches = RecordBatchIterator::new(vec![], schema);
|
let batches = RecordBatchIterator::new(vec![], schema);
|
||||||
Self::create(
|
Self::create(
|
||||||
@@ -1678,6 +1717,7 @@ impl NativeTable {
|
|||||||
write_store_wrapper,
|
write_store_wrapper,
|
||||||
params,
|
params,
|
||||||
read_consistency_interval,
|
read_consistency_interval,
|
||||||
|
namespace_client,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -1906,6 +1946,25 @@ impl NativeTable {
|
|||||||
VectorIndexParams::with_ivf_flat_params(index.distance_type.into(), ivf_params);
|
VectorIndexParams::with_ivf_flat_params(index.distance_type.into(), ivf_params);
|
||||||
Ok(Box::new(lance_idx_params))
|
Ok(Box::new(lance_idx_params))
|
||||||
}
|
}
|
||||||
|
Index::IvfSq(index) => {
|
||||||
|
Self::validate_index_type(field, "IVF SQ", supported_vector_data_type)?;
|
||||||
|
let ivf_params = Self::build_ivf_params(
|
||||||
|
index.num_partitions,
|
||||||
|
index.target_partition_size,
|
||||||
|
index.sample_rate,
|
||||||
|
index.max_iterations,
|
||||||
|
);
|
||||||
|
let sq_params = SQBuildParams {
|
||||||
|
sample_rate: index.sample_rate as usize,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let lance_idx_params = VectorIndexParams::with_ivf_sq_params(
|
||||||
|
index.distance_type.into(),
|
||||||
|
ivf_params,
|
||||||
|
sq_params,
|
||||||
|
);
|
||||||
|
Ok(Box::new(lance_idx_params))
|
||||||
|
}
|
||||||
Index::IvfPq(index) => {
|
Index::IvfPq(index) => {
|
||||||
Self::validate_index_type(field, "IVF PQ", supported_vector_data_type)?;
|
Self::validate_index_type(field, "IVF PQ", supported_vector_data_type)?;
|
||||||
let dim = Self::get_vector_dimension(field)?;
|
let dim = Self::get_vector_dimension(field)?;
|
||||||
@@ -2013,6 +2072,7 @@ impl NativeTable {
|
|||||||
Index::LabelList(_) => IndexType::LabelList,
|
Index::LabelList(_) => IndexType::LabelList,
|
||||||
Index::FTS(_) => IndexType::Inverted,
|
Index::FTS(_) => IndexType::Inverted,
|
||||||
Index::IvfFlat(_)
|
Index::IvfFlat(_)
|
||||||
|
| Index::IvfSq(_)
|
||||||
| Index::IvfPq(_)
|
| Index::IvfPq(_)
|
||||||
| Index::IvfRq(_)
|
| Index::IvfRq(_)
|
||||||
| Index::IvfHnswPq(_)
|
| Index::IvfHnswPq(_)
|
||||||
@@ -2035,6 +2095,278 @@ impl NativeTable {
|
|||||||
Ok(DatasetRecordBatchStream::new(inner))
|
Ok(DatasetRecordBatchStream::new(inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute a query on the namespace server instead of locally.
|
||||||
|
async fn namespace_query(
|
||||||
|
&self,
|
||||||
|
namespace_client: Arc<dyn LanceNamespace>,
|
||||||
|
query: &AnyQuery,
|
||||||
|
_options: QueryExecutionOptions,
|
||||||
|
) -> Result<DatasetRecordBatchStream> {
|
||||||
|
// Build table_id from namespace + table name
|
||||||
|
let mut table_id = self.namespace.clone();
|
||||||
|
table_id.push(self.name.clone());
|
||||||
|
|
||||||
|
// Convert AnyQuery to namespace QueryTableRequest
|
||||||
|
let mut ns_request = self.convert_to_namespace_query(query)?;
|
||||||
|
// Set the table ID on the request
|
||||||
|
ns_request.id = Some(table_id);
|
||||||
|
|
||||||
|
// Call the namespace query_table API
|
||||||
|
let response_bytes = namespace_client
|
||||||
|
.query_table(ns_request)
|
||||||
|
.await
|
||||||
|
.map_err(|e| Error::Runtime {
|
||||||
|
message: format!("Failed to execute server-side query: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Parse the Arrow IPC response into a RecordBatchStream
|
||||||
|
self.parse_arrow_ipc_response(response_bytes).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a QueryFilter to a SQL string for the namespace API.
|
||||||
|
fn filter_to_sql(&self, filter: &QueryFilter) -> Result<String> {
|
||||||
|
match filter {
|
||||||
|
QueryFilter::Sql(sql) => Ok(sql.clone()),
|
||||||
|
QueryFilter::Substrait(_) => Err(Error::NotSupported {
|
||||||
|
message: "Substrait filters are not supported for server-side queries".to_string(),
|
||||||
|
}),
|
||||||
|
QueryFilter::Datafusion(_) => Err(Error::NotSupported {
|
||||||
|
message: "Datafusion expression filters are not supported for server-side queries. Use SQL filter instead.".to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert an AnyQuery to the namespace QueryTableRequest format.
|
||||||
|
fn convert_to_namespace_query(&self, query: &AnyQuery) -> Result<NsQueryTableRequest> {
|
||||||
|
match query {
|
||||||
|
AnyQuery::VectorQuery(vq) => {
|
||||||
|
// Extract the query vector(s)
|
||||||
|
let vector = self.extract_query_vector(&vq.query_vector)?;
|
||||||
|
|
||||||
|
// Convert filter to SQL string
|
||||||
|
let filter = match &vq.base.filter {
|
||||||
|
Some(f) => Some(self.filter_to_sql(f)?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert select to columns list
|
||||||
|
let columns = match &vq.base.select {
|
||||||
|
Select::All => None,
|
||||||
|
Select::Columns(cols) => Some(cols.clone()),
|
||||||
|
Select::Dynamic(_) => {
|
||||||
|
return Err(Error::NotSupported {
|
||||||
|
message:
|
||||||
|
"Dynamic column selection is not supported for server-side queries"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for unsupported features
|
||||||
|
if vq.base.reranker.is_some() {
|
||||||
|
return Err(Error::NotSupported {
|
||||||
|
message: "Reranker is not supported for server-side queries".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert FTS query if present
|
||||||
|
let full_text_query = vq.base.full_text_search.as_ref().map(|fts| {
|
||||||
|
let columns = fts.columns();
|
||||||
|
let columns_vec = if columns.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(columns.into_iter().collect())
|
||||||
|
};
|
||||||
|
Box::new(QueryTableRequestFullTextQuery {
|
||||||
|
string_query: Some(Box::new(StringFtsQuery {
|
||||||
|
query: fts.query.to_string(),
|
||||||
|
columns: columns_vec,
|
||||||
|
})),
|
||||||
|
structured_query: None,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(NsQueryTableRequest {
|
||||||
|
id: None, // Will be set in namespace_query
|
||||||
|
k: vq.base.limit.unwrap_or(10) as i32,
|
||||||
|
vector: Box::new(vector),
|
||||||
|
vector_column: vq.column.clone(),
|
||||||
|
filter,
|
||||||
|
columns,
|
||||||
|
offset: vq.base.offset.map(|o| o as i32),
|
||||||
|
distance_type: vq.distance_type.map(|dt| dt.to_string()),
|
||||||
|
nprobes: Some(vq.minimum_nprobes as i32),
|
||||||
|
ef: vq.ef.map(|e| e as i32),
|
||||||
|
refine_factor: vq.refine_factor.map(|r| r as i32),
|
||||||
|
lower_bound: vq.lower_bound,
|
||||||
|
upper_bound: vq.upper_bound,
|
||||||
|
prefilter: Some(vq.base.prefilter),
|
||||||
|
fast_search: Some(vq.base.fast_search),
|
||||||
|
with_row_id: Some(vq.base.with_row_id),
|
||||||
|
bypass_vector_index: Some(!vq.use_index),
|
||||||
|
full_text_query,
|
||||||
|
version: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
AnyQuery::Query(q) => {
|
||||||
|
// For non-vector queries, pass an empty vector (similar to remote table implementation)
|
||||||
|
if q.reranker.is_some() {
|
||||||
|
return Err(Error::NotSupported {
|
||||||
|
message: "Reranker is not supported for server-side query execution"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter = q
|
||||||
|
.filter
|
||||||
|
.as_ref()
|
||||||
|
.map(|f| self.filter_to_sql(f))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
let columns = match &q.select {
|
||||||
|
Select::All => None,
|
||||||
|
Select::Columns(cols) => Some(cols.clone()),
|
||||||
|
Select::Dynamic(_) => {
|
||||||
|
return Err(Error::NotSupported {
|
||||||
|
message: "Dynamic columns are not supported for server-side query"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle full text search if present
|
||||||
|
let full_text_query = q.full_text_search.as_ref().map(|fts| {
|
||||||
|
let columns_vec = if fts.columns().is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(fts.columns().iter().cloned().collect())
|
||||||
|
};
|
||||||
|
Box::new(QueryTableRequestFullTextQuery {
|
||||||
|
string_query: Some(Box::new(StringFtsQuery {
|
||||||
|
query: fts.query.to_string(),
|
||||||
|
columns: columns_vec,
|
||||||
|
})),
|
||||||
|
structured_query: None,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// Empty vector for non-vector queries
|
||||||
|
let vector = Box::new(QueryTableRequestVector {
|
||||||
|
single_vector: Some(vec![]),
|
||||||
|
multi_vector: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(NsQueryTableRequest {
|
||||||
|
id: None, // Will be set by caller
|
||||||
|
vector,
|
||||||
|
k: q.limit.unwrap_or(10) as i32,
|
||||||
|
filter,
|
||||||
|
columns,
|
||||||
|
prefilter: Some(q.prefilter),
|
||||||
|
offset: q.offset.map(|o| o as i32),
|
||||||
|
ef: None,
|
||||||
|
refine_factor: None,
|
||||||
|
distance_type: None,
|
||||||
|
nprobes: None,
|
||||||
|
vector_column: None, // No vector column for plain queries
|
||||||
|
with_row_id: Some(q.with_row_id),
|
||||||
|
bypass_vector_index: Some(true), // No vector index for plain queries
|
||||||
|
full_text_query,
|
||||||
|
version: None,
|
||||||
|
fast_search: None,
|
||||||
|
lower_bound: None,
|
||||||
|
upper_bound: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract query vector(s) from Arrow arrays into the namespace format.
|
||||||
|
fn extract_query_vector(
|
||||||
|
&self,
|
||||||
|
query_vectors: &[Arc<dyn arrow_array::Array>],
|
||||||
|
) -> Result<QueryTableRequestVector> {
|
||||||
|
if query_vectors.is_empty() {
|
||||||
|
return Err(Error::InvalidInput {
|
||||||
|
message: "Query vector is required for vector search".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle single vector case
|
||||||
|
if query_vectors.len() == 1 {
|
||||||
|
let arr = &query_vectors[0];
|
||||||
|
let single_vector = self.array_to_f32_vec(arr)?;
|
||||||
|
Ok(QueryTableRequestVector {
|
||||||
|
single_vector: Some(single_vector),
|
||||||
|
multi_vector: None,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Handle multi-vector case
|
||||||
|
let multi_vector: Result<Vec<Vec<f32>>> = query_vectors
|
||||||
|
.iter()
|
||||||
|
.map(|arr| self.array_to_f32_vec(arr))
|
||||||
|
.collect();
|
||||||
|
Ok(QueryTableRequestVector {
|
||||||
|
single_vector: None,
|
||||||
|
multi_vector: Some(multi_vector?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert an Arrow array to a Vec<f32>.
|
||||||
|
fn array_to_f32_vec(&self, arr: &Arc<dyn arrow_array::Array>) -> Result<Vec<f32>> {
|
||||||
|
// Handle FixedSizeList (common for vectors)
|
||||||
|
if let Some(fsl) = arr
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<arrow_array::FixedSizeListArray>()
|
||||||
|
{
|
||||||
|
let values = fsl.values();
|
||||||
|
if let Some(f32_arr) = values.as_any().downcast_ref::<arrow_array::Float32Array>() {
|
||||||
|
return Ok(f32_arr.values().to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle direct Float32Array
|
||||||
|
if let Some(f32_arr) = arr.as_any().downcast_ref::<arrow_array::Float32Array>() {
|
||||||
|
return Ok(f32_arr.values().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::InvalidInput {
|
||||||
|
message: "Query vector must be Float32 type".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse Arrow IPC response from the namespace server.
|
||||||
|
async fn parse_arrow_ipc_response(
|
||||||
|
&self,
|
||||||
|
bytes: bytes::Bytes,
|
||||||
|
) -> Result<DatasetRecordBatchStream> {
|
||||||
|
use arrow_ipc::reader::StreamReader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
let cursor = Cursor::new(bytes);
|
||||||
|
let reader = StreamReader::try_new(cursor, None).map_err(|e| Error::Runtime {
|
||||||
|
message: format!("Failed to parse Arrow IPC response: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Collect all record batches
|
||||||
|
let schema = reader.schema();
|
||||||
|
let batches: Vec<_> = reader
|
||||||
|
.into_iter()
|
||||||
|
.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
|
.map_err(|e| Error::Runtime {
|
||||||
|
message: format!("Failed to read Arrow IPC batches: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Create a stream from the batches
|
||||||
|
let stream = futures::stream::iter(batches.into_iter().map(Ok));
|
||||||
|
let record_batch_stream = Box::pin(
|
||||||
|
datafusion_physical_plan::stream::RecordBatchStreamAdapter::new(schema, stream),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(DatasetRecordBatchStream::new(record_batch_stream))
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether the table uses V2 manifest paths.
|
/// Check whether the table uses V2 manifest paths.
|
||||||
///
|
///
|
||||||
/// See [Self::migrate_manifest_paths_v2] and [ManifestNamingScheme] for
|
/// See [Self::migrate_manifest_paths_v2] and [ManifestNamingScheme] for
|
||||||
@@ -2466,6 +2798,12 @@ impl BaseTable for NativeTable {
|
|||||||
query: &AnyQuery,
|
query: &AnyQuery,
|
||||||
options: QueryExecutionOptions,
|
options: QueryExecutionOptions,
|
||||||
) -> Result<DatasetRecordBatchStream> {
|
) -> Result<DatasetRecordBatchStream> {
|
||||||
|
// If namespace client is configured, use server-side query execution
|
||||||
|
if let Some(ref namespace_client) = self.namespace_client {
|
||||||
|
return self
|
||||||
|
.namespace_query(namespace_client.clone(), query, options)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
self.generic_query(query, options).await
|
self.generic_query(query, options).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2934,7 +3272,7 @@ mod tests {
|
|||||||
|
|
||||||
let batches = make_test_batches();
|
let batches = make_test_batches();
|
||||||
let batches = Box::new(batches) as Box<dyn RecordBatchReader + Send>;
|
let batches = Box::new(batches) as Box<dyn RecordBatchReader + Send>;
|
||||||
let table = NativeTable::create(uri, "test", vec![], batches, None, None, None)
|
let table = NativeTable::create(uri, "test", vec![], batches, None, None, None, None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -4574,4 +4912,91 @@ mod tests {
|
|||||||
assert_eq!(result.len(), 1);
|
assert_eq!(result.len(), 1);
|
||||||
assert_eq!(result[0].index_type, crate::index::IndexType::Bitmap);
|
assert_eq!(result[0].index_type, crate::index::IndexType::Bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_convert_to_namespace_query_vector() {
|
||||||
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let dataset_path = tmp_dir.path().join("test_ns_query.lance");
|
||||||
|
|
||||||
|
let batches = make_test_batches();
|
||||||
|
Dataset::write(batches, dataset_path.to_str().unwrap(), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let table = NativeTable::open(dataset_path.to_str().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Create a vector query
|
||||||
|
let query_vector = Arc::new(Float32Array::from(vec![1.0, 2.0, 3.0, 4.0]));
|
||||||
|
let vq = VectorQueryRequest {
|
||||||
|
base: QueryRequest {
|
||||||
|
limit: Some(10),
|
||||||
|
offset: Some(5),
|
||||||
|
filter: Some(QueryFilter::Sql("id > 0".to_string())),
|
||||||
|
select: Select::Columns(vec!["id".to_string()]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
column: Some("vector".to_string()),
|
||||||
|
query_vector: vec![query_vector as Arc<dyn Array>],
|
||||||
|
minimum_nprobes: 20,
|
||||||
|
distance_type: Some(crate::DistanceType::L2),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let any_query = AnyQuery::VectorQuery(vq);
|
||||||
|
let ns_request = table.convert_to_namespace_query(&any_query).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ns_request.k, 10);
|
||||||
|
assert_eq!(ns_request.offset, Some(5));
|
||||||
|
assert_eq!(ns_request.filter, Some("id > 0".to_string()));
|
||||||
|
assert_eq!(ns_request.columns, Some(vec!["id".to_string()]));
|
||||||
|
assert_eq!(ns_request.vector_column, Some("vector".to_string()));
|
||||||
|
assert_eq!(ns_request.distance_type, Some("l2".to_string()));
|
||||||
|
assert!(ns_request.vector.single_vector.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
ns_request.vector.single_vector.as_ref().unwrap(),
|
||||||
|
&vec![1.0, 2.0, 3.0, 4.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_convert_to_namespace_query_plain_query() {
|
||||||
|
let tmp_dir = tempdir().unwrap();
|
||||||
|
let dataset_path = tmp_dir.path().join("test_ns_plain.lance");
|
||||||
|
|
||||||
|
let batches = make_test_batches();
|
||||||
|
Dataset::write(batches, dataset_path.to_str().unwrap(), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let table = NativeTable::open(dataset_path.to_str().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Create a plain (non-vector) query with filter and select
|
||||||
|
let q = QueryRequest {
|
||||||
|
limit: Some(20),
|
||||||
|
offset: Some(5),
|
||||||
|
filter: Some(QueryFilter::Sql("id > 5".to_string())),
|
||||||
|
select: Select::Columns(vec!["id".to_string()]),
|
||||||
|
with_row_id: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let any_query = AnyQuery::Query(q);
|
||||||
|
let ns_request = table.convert_to_namespace_query(&any_query).unwrap();
|
||||||
|
|
||||||
|
// Plain queries should pass an empty vector
|
||||||
|
assert_eq!(ns_request.k, 20);
|
||||||
|
assert_eq!(ns_request.offset, Some(5));
|
||||||
|
assert_eq!(ns_request.filter, Some("id > 5".to_string()));
|
||||||
|
assert_eq!(ns_request.columns, Some(vec!["id".to_string()]));
|
||||||
|
assert_eq!(ns_request.with_row_id, Some(true));
|
||||||
|
assert_eq!(ns_request.bypass_vector_index, Some(true));
|
||||||
|
assert!(ns_request.vector_column.is_none()); // No vector column for plain queries
|
||||||
|
|
||||||
|
// Should have an empty vector
|
||||||
|
assert!(ns_request.vector.single_vector.as_ref().unwrap().is_empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user