mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-29 09:28:27 +00:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32c77879c9 | |||
| 9330a9b851 | |||
| 02de07576e | |||
| 81617fd3d9 | |||
| 011fdd5c94 | |||
| 650f173236 | |||
| 9b21c136c6 | |||
| 694aa48e19 | |||
| 455ba5abbf | |||
| 5338aeb006 | |||
| 47a34f5cca | |||
| a17c241e86 | |||
| 1fc23e5473 | |||
| 87b831bcae | |||
| 59db036118 | |||
| c091243d5b | |||
| a2aea7b4e5 | |||
| 4a5341edb1 | |||
| 25dfe2cfd4 | |||
| 4dcd7f4314 | |||
| 2e36cd9dad | |||
| f31e27768a | |||
| b84150a53e | |||
| d135c18db6 | |||
| ef399de092 | |||
| 0d767abd0e | |||
| a92ae0ded5 | |||
| c54888a83a | |||
| ba6c44abc9 | |||
| 75b0a8e0a3 | |||
| 2a886141f7 | |||
| 2a1df8edcf | |||
| fd98b845ea | |||
| be48ada352 | |||
| 9ad2dfe601 | |||
| f909df3e87 | |||
| d715bbb588 | |||
| 5ce3d8d141 | |||
| 5eaac178b1 | |||
| 11af763fcd | |||
| 2ed5452e1c | |||
| b7c0b5987c | |||
| 97a4b38f19 | |||
| 10879d99b8 | |||
| 4e6a1d5dce | |||
| 13d2759356 | |||
| 7f52ec8c36 | |||
| c6ae0de3ee | |||
| 231f0655ce | |||
| 8c52977c59 | |||
| 359710a0bf | |||
| 1f1726369d | |||
| df354abae4 | |||
| 11bc674548 | |||
| 5593460823 | |||
| 2807ad6854 | |||
| 4761fa9bcb | |||
| 4c2939d66e | |||
| a813ce2f71 | |||
| a898dc81c2 | |||
| de3f8097e7 | |||
| 0ac59de5f1 | |||
| d082c2d2ac | |||
| 9d8699f99e | |||
| aa2c7b3591 | |||
| 590c0c1e77 | |||
| 382ecd65e3 | |||
| e26b22bcca | |||
| 3ba46135a5 | |||
| f903d07887 |
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.27.2-beta.1"
|
current_version = "0.28.0-beta.11"
|
||||||
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*)\\.
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ body:
|
|||||||
label: Link
|
label: Link
|
||||||
description: >
|
description: >
|
||||||
Provide a link to the existing documentation, if applicable.
|
Provide a link to the existing documentation, if applicable.
|
||||||
placeholder: ex. https://lancedb.com/docs/tables/...
|
placeholder: ex. https://docs.lancedb.com/tables/...
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
# Scope: the root Cargo workspace, which produces the Rust binaries we
|
||||||
|
# ship to users (the Node.js and Python native extensions). The
|
||||||
|
# `rust/lancedb` library crate shares the same lockfile; its consumers
|
||||||
|
# pick their own dependency versions, but bumping transitive deps here
|
||||||
|
# keeps the binaries we ship current.
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: cargo
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
groups:
|
||||||
|
rust-minor-patch:
|
||||||
|
update-types:
|
||||||
|
- minor
|
||||||
|
- patch
|
||||||
@@ -45,7 +45,9 @@ jobs:
|
|||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
# pnpm 11 (used by the nodejs install step below) requires
|
||||||
|
# Node >= 22.13; use 24 since 22 hits EOL in October.
|
||||||
|
node-version: 24
|
||||||
|
|
||||||
- name: Install Codex CLI
|
- name: Install Codex CLI
|
||||||
run: npm install -g @openai/codex
|
run: npm install -g @openai/codex
|
||||||
@@ -79,10 +81,14 @@ jobs:
|
|||||||
java-version: '11'
|
java-version: '11'
|
||||||
cache: maven
|
cache: maven
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.1.1
|
||||||
- name: Install Node.js dependencies for TypeScript bindings
|
- name: Install Node.js dependencies for TypeScript bindings
|
||||||
run: |
|
run: |
|
||||||
cd nodejs
|
cd nodejs
|
||||||
npm ci
|
pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Configure git user
|
- name: Configure git user
|
||||||
run: |
|
run: |
|
||||||
@@ -137,7 +143,7 @@ jobs:
|
|||||||
- For Rust test failures: Run the specific test with "cargo test -p <crate> <test_name>"
|
- For Rust test failures: Run the specific test with "cargo test -p <crate> <test_name>"
|
||||||
- For Python test failures: Build with "cd python && maturin develop" then run "pytest <specific_test_file>::<test_name>"
|
- For Python test failures: Build with "cd python && maturin develop" then run "pytest <specific_test_file>::<test_name>"
|
||||||
- For Java test failures: Run "cd java && mvn test -Dtest=<TestClass>#<testMethod>"
|
- For Java test failures: Run "cd java && mvn test -Dtest=<TestClass>#<testMethod>"
|
||||||
- For TypeScript test failures: Run "cd nodejs && npm run build && npm test -- --testNamePattern='<test_name>'"
|
- For TypeScript test failures: Run "cd nodejs && pnpm build && pnpm test -- --testNamePattern='<test_name>'"
|
||||||
- Do NOT run the full test suite - only run the tests that were failing
|
- Do NOT run the full test suite - only run the tests that were failing
|
||||||
|
|
||||||
7. If the additional guidelines are provided, follow them as well.
|
7. If the additional guidelines are provided, follow them as well.
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
labeler:
|
labeler:
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- .github/workflows/java-publish.yml
|
- .github/workflows/java-publish.yml
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
name: Build and Publish
|
name: Build and Publish
|
||||||
@@ -40,7 +43,7 @@ jobs:
|
|||||||
server-username: SONATYPE_USER
|
server-username: SONATYPE_USER
|
||||||
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: MAVEN_GPG_PASSPHRASE
|
||||||
- name: Set git config
|
- name: Set git config
|
||||||
run: |
|
run: |
|
||||||
git config --global user.email "dev+gha@lancedb.com"
|
git config --global user.email "dev+gha@lancedb.com"
|
||||||
@@ -55,10 +58,11 @@ jobs:
|
|||||||
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)
|
||||||
./mvnw --batch-mode -DskipTests -DpushChanges=false -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} deploy -pl lancedb-core -am -P deploy-to-ossrh
|
./mvnw --batch-mode -DskipTests -DpushChanges=false 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 }}
|
||||||
|
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
|
|
||||||
report-failure:
|
report-failure:
|
||||||
name: Report Workflow Failure
|
name: Report Workflow Failure
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ on:
|
|||||||
- java/**
|
- java/**
|
||||||
- .github/workflows/java.yml
|
- .github/workflows/java.yml
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-java:
|
build-java:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ on:
|
|||||||
- nodejs/**
|
- nodejs/**
|
||||||
- java/**
|
- java/**
|
||||||
- .github/workflows/license-header-check.yml
|
- .github/workflows/license-header-check.yml
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-licenses:
|
check-licenses:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- Cargo.toml
|
- Cargo.toml
|
||||||
- Cargo.lock
|
- Cargo.lock
|
||||||
|
- rust-toolchain.toml
|
||||||
- nodejs/**
|
- nodejs/**
|
||||||
- rust/**
|
- rust/**
|
||||||
- docs/src/js/**
|
- docs/src/js/**
|
||||||
- .github/workflows/nodejs.yml
|
- .github/workflows/nodejs.yml
|
||||||
- docker-compose.yml
|
- docker-compose.yml
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -38,11 +42,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.1.1
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
||||||
cache: 'npm'
|
# in October. The library itself still supports Node >= 18
|
||||||
cache-dependency-path: nodejs/package-lock.json
|
# (see test matrix below).
|
||||||
|
node-version: 24
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
@@ -57,11 +67,13 @@ jobs:
|
|||||||
run: cargo clippy --profile ci --all --all-features -- -D warnings
|
run: cargo clippy --profile ci --all --all-features -- -D warnings
|
||||||
- name: Lint Typescript
|
- name: Lint Typescript
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
pnpm install --frozen-lockfile
|
||||||
npm run lint-ci
|
pnpm lint-ci
|
||||||
- name: Lint examples
|
- name: Lint examples
|
||||||
working-directory: nodejs/examples
|
working-directory: nodejs/examples
|
||||||
run: npm ci && npm run lint-ci
|
# The `@lancedb/lancedb` dep points at file:../dist; pnpm errors if
|
||||||
|
# that dir is missing, so create an empty one for lint-only runs.
|
||||||
|
run: mkdir -p ../dist && pnpm install --frozen-lockfile && pnpm lint-ci
|
||||||
linux:
|
linux:
|
||||||
name: Linux (NodeJS ${{ matrix.node-version }})
|
name: Linux (NodeJS ${{ matrix.node-version }})
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
@@ -78,14 +90,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
- uses: actions/setup-node@v4
|
- uses: pnpm/action-setup@v4
|
||||||
name: Setup Node.js 20 for build
|
|
||||||
with:
|
with:
|
||||||
# @napi-rs/cli v3 requires Node >= 20.12 (via @inquirer/prompts@8).
|
version: 11.1.1
|
||||||
# Build always on Node 20; tests run on the matrix version below.
|
- uses: actions/setup-node@v4
|
||||||
node-version: 20
|
name: Setup Node.js 24 for build
|
||||||
cache: 'npm'
|
with:
|
||||||
cache-dependency-path: nodejs/package-lock.json
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
||||||
|
# in October. Build/install runs on Node 24; tests run on the
|
||||||
|
# matrix version below using direct jest invocation.
|
||||||
|
node-version: 24
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -93,45 +109,52 @@ jobs:
|
|||||||
sudo apt install -y protobuf-compiler libssl-dev
|
sudo apt install -y protobuf-compiler libssl-dev
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npm ci --include=optional
|
pnpm install --frozen-lockfile
|
||||||
npm run build:debug -- --profile ci
|
# No `--` separator: pnpm forwards it literally, which would
|
||||||
|
# make napi-rs treat `--profile ci` as a cargo passthrough arg.
|
||||||
|
pnpm build:debug --profile ci
|
||||||
|
pnpm tsc
|
||||||
|
- name: Setup examples
|
||||||
|
working-directory: nodejs/examples
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
- name: Check docs
|
||||||
|
run: |
|
||||||
|
# We run this as part of the job because the binary needs to be built
|
||||||
|
# first to export the types of the native code.
|
||||||
|
set -e
|
||||||
|
# `pnpm docs` would invoke pnpm's built-in `docs` command, not
|
||||||
|
# the script — use `pnpm run docs`.
|
||||||
|
pnpm run docs
|
||||||
|
if ! git diff --exit-code -- ../ ':(exclude)Cargo.lock'; then
|
||||||
|
echo "Docs need to be updated"
|
||||||
|
echo "Run 'pnpm run docs', fix any warnings, and commit the changes."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
name: Setup Node.js ${{ matrix.node-version }} for test
|
name: Setup Node.js ${{ matrix.node-version }} for test
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- name: Compile TypeScript
|
|
||||||
run: npm run tsc
|
|
||||||
- name: Setup localstack
|
- name: Setup localstack
|
||||||
working-directory: .
|
working-directory: .
|
||||||
run: docker compose up --detach --wait
|
run: docker compose up --detach --wait
|
||||||
- name: Test
|
- name: Test
|
||||||
env:
|
env:
|
||||||
S3_TEST: "1"
|
S3_TEST: "1"
|
||||||
run: npm run test
|
# Newer @smithy/core uses dynamic ESM imports.
|
||||||
- name: Setup examples
|
NODE_OPTIONS: "--experimental-vm-modules"
|
||||||
working-directory: nodejs/examples
|
# Invoke jest directly because pnpm 11 itself requires Node 22+
|
||||||
run: npm ci
|
# while the matrix tests on older Node versions.
|
||||||
|
run: npx jest --verbose
|
||||||
- name: Test examples
|
- name: Test examples
|
||||||
working-directory: ./
|
working-directory: ./
|
||||||
env:
|
env:
|
||||||
OPENAI_API_KEY: test
|
OPENAI_API_KEY: test
|
||||||
OPENAI_BASE_URL: http://0.0.0.0:8000
|
OPENAI_BASE_URL: http://0.0.0.0:8000
|
||||||
|
NODE_OPTIONS: "--experimental-vm-modules"
|
||||||
run: |
|
run: |
|
||||||
python ci/mock_openai.py &
|
python ci/mock_openai.py &
|
||||||
cd nodejs/examples
|
cd nodejs/examples
|
||||||
npm test
|
npx jest --testEnvironment jest-environment-node-single-context --verbose
|
||||||
- name: Check docs
|
|
||||||
run: |
|
|
||||||
# We run this as part of the job because the binary needs to be built
|
|
||||||
# first to export the types of the native code.
|
|
||||||
set -e
|
|
||||||
npm ci
|
|
||||||
npm run docs
|
|
||||||
if ! git diff --exit-code -- ../ ':(exclude)Cargo.lock'; then
|
|
||||||
echo "Docs need to be updated"
|
|
||||||
echo "Run 'npm run docs', fix any warnings, and commit the changes."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
macos:
|
macos:
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
runs-on: "macos-14"
|
runs-on: "macos-14"
|
||||||
@@ -144,20 +167,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
lfs: true
|
lfs: true
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.1.1
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
||||||
cache: 'npm'
|
# in October.
|
||||||
cache-dependency-path: nodejs/package-lock.json
|
node-version: 24
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install protobuf
|
brew install protobuf
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npm ci --include=optional
|
pnpm install --frozen-lockfile
|
||||||
npm run build:debug -- --profile ci
|
# No `--` separator: pnpm forwards it literally, which would
|
||||||
npm run tsc
|
# make napi-rs treat `--profile ci` as a cargo passthrough arg.
|
||||||
|
pnpm build:debug --profile ci
|
||||||
|
pnpm tsc
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
npm run test
|
pnpm test
|
||||||
|
|||||||
@@ -171,13 +171,18 @@ jobs:
|
|||||||
working-directory: nodejs
|
working-directory: nodejs
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.1.1
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
if: ${{ !matrix.settings.docker }}
|
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
||||||
cache: npm
|
# in October.
|
||||||
cache-dependency-path: nodejs/package-lock.json
|
node-version: 24
|
||||||
|
cache: pnpm
|
||||||
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
||||||
- name: Install
|
- name: Install
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
if: ${{ !matrix.settings.docker }}
|
if: ${{ !matrix.settings.docker }}
|
||||||
@@ -195,7 +200,7 @@ jobs:
|
|||||||
target/
|
target/
|
||||||
key: nodejs-${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }}
|
key: nodejs-${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: pnpm install --frozen-lockfile
|
||||||
- name: Install Zig
|
- name: Install Zig
|
||||||
uses: mlugg/setup-zig@v2
|
uses: mlugg/setup-zig@v2
|
||||||
if: ${{ contains(matrix.settings.target, 'musl') }}
|
if: ${{ contains(matrix.settings.target, 'musl') }}
|
||||||
@@ -248,7 +253,7 @@ jobs:
|
|||||||
# one to do the upload.
|
# one to do the upload.
|
||||||
- name: Make generic artifacts
|
- name: Make generic artifacts
|
||||||
if: ${{ matrix.settings.target == 'aarch64-apple-darwin' }}
|
if: ${{ matrix.settings.target == 'aarch64-apple-darwin' }}
|
||||||
run: npm run tsc
|
run: pnpm tsc
|
||||||
- name: Upload Generic Artifacts
|
- name: Upload Generic Artifacts
|
||||||
if: ${{ matrix.settings.target == 'aarch64-apple-darwin' }}
|
if: ${{ matrix.settings.target == 'aarch64-apple-darwin' }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -283,14 +288,24 @@ jobs:
|
|||||||
working-directory: nodejs
|
working-directory: nodejs
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup node
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.1.1
|
||||||
|
- name: Setup Node.js 24 for install
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
||||||
|
# in October.
|
||||||
|
node-version: 24
|
||||||
|
cache: pnpm
|
||||||
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
- name: Setup Node.js ${{ matrix.node }} for test
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: nodejs/package-lock.json
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -311,7 +326,9 @@ jobs:
|
|||||||
- name: Move built files
|
- name: Move built files
|
||||||
run: cp dist/native.d.ts dist/native.js dist/*.node lancedb/
|
run: cp dist/native.d.ts dist/native.js dist/*.node lancedb/
|
||||||
- name: Test bindings
|
- name: Test bindings
|
||||||
run: npm test
|
# Invoke jest directly because pnpm 11 itself requires Node 22+
|
||||||
|
# while the matrix tests on older Node versions.
|
||||||
|
run: npx jest --verbose
|
||||||
publish:
|
publish:
|
||||||
name: Publish
|
name: Publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -323,15 +340,19 @@ jobs:
|
|||||||
- test-lancedb
|
- test-lancedb
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 11.1.1
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
cache: npm
|
cache: pnpm
|
||||||
cache-dependency-path: nodejs/package-lock.json
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: pnpm install --frozen-lockfile
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nodejs-dist
|
name: nodejs-dist
|
||||||
@@ -351,7 +372,7 @@ jobs:
|
|||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
run: find dist && find nodejs-artifacts
|
run: find dist && find nodejs-artifacts
|
||||||
- name: Move artifacts
|
- name: Move artifacts
|
||||||
run: npx napi artifacts -d nodejs-artifacts
|
run: pnpm exec napi artifacts -d nodejs-artifacts
|
||||||
- name: List packages
|
- name: List packages
|
||||||
run: find npm
|
run: find npm
|
||||||
- name: Publish
|
- name: Publish
|
||||||
|
|||||||
@@ -14,10 +14,16 @@ on:
|
|||||||
env:
|
env:
|
||||||
PIP_EXTRA_INDEX_URL: "https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/"
|
PIP_EXTRA_INDEX_URL: "https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
name: Python ${{ matrix.config.platform }} manylinux${{ matrix.config.manylinux }}
|
name: Python ${{ matrix.config.platform }} manylinux${{ matrix.config.manylinux }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
@@ -57,10 +63,12 @@ jobs:
|
|||||||
- uses: ./.github/workflows/upload_wheel
|
- uses: ./.github/workflows/upload_wheel
|
||||||
if: startsWith(github.ref, 'refs/tags/python-v')
|
if: startsWith(github.ref, 'refs/tags/python-v')
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.LANCEDB_PYPI_API_TOKEN }}
|
|
||||||
fury_token: ${{ secrets.FURY_TOKEN }}
|
fury_token: ${{ secrets.FURY_TOKEN }}
|
||||||
mac:
|
mac:
|
||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
runs-on: ${{ matrix.config.runner }}
|
runs-on: ${{ matrix.config.runner }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -85,10 +93,12 @@ jobs:
|
|||||||
- uses: ./.github/workflows/upload_wheel
|
- uses: ./.github/workflows/upload_wheel
|
||||||
if: startsWith(github.ref, 'refs/tags/python-v')
|
if: startsWith(github.ref, 'refs/tags/python-v')
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.LANCEDB_PYPI_API_TOKEN }}
|
|
||||||
fury_token: ${{ secrets.FURY_TOKEN }}
|
fury_token: ${{ secrets.FURY_TOKEN }}
|
||||||
windows:
|
windows:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -107,7 +117,6 @@ jobs:
|
|||||||
- uses: ./.github/workflows/upload_wheel
|
- uses: ./.github/workflows/upload_wheel
|
||||||
if: startsWith(github.ref, 'refs/tags/python-v')
|
if: startsWith(github.ref, 'refs/tags/python-v')
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.LANCEDB_PYPI_API_TOKEN }}
|
|
||||||
fury_token: ${{ secrets.FURY_TOKEN }}
|
fury_token: ${{ secrets.FURY_TOKEN }}
|
||||||
gh-release:
|
gh-release:
|
||||||
if: startsWith(github.ref, 'refs/tags/python-v')
|
if: startsWith(github.ref, 'refs/tags/python-v')
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- Cargo.toml
|
- Cargo.toml
|
||||||
- Cargo.lock
|
- Cargo.lock
|
||||||
|
- rust-toolchain.toml
|
||||||
- python/**
|
- python/**
|
||||||
- rust/**
|
- rust/**
|
||||||
- .github/workflows/python.yml
|
- .github/workflows/python.yml
|
||||||
@@ -16,6 +17,9 @@ on:
|
|||||||
- .github/workflows/build_windows_wheel/**
|
- .github/workflows/build_windows_wheel/**
|
||||||
- .github/workflows/run_tests/**
|
- .github/workflows/run_tests/**
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -107,7 +111,6 @@ jobs:
|
|||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
pip install --extra-index-url https://pypi.fury.io/lance-format/ --extra-index-url https://pypi.fury.io/lancedb/ -e .[tests,dev,embeddings]
|
pip install --extra-index-url https://pypi.fury.io/lance-format/ --extra-index-url https://pypi.fury.io/lancedb/ -e .[tests,dev,embeddings]
|
||||||
pip install tantivy
|
|
||||||
pip install mlx
|
pip install mlx
|
||||||
- name: Doctest
|
- name: Doctest
|
||||||
run: pytest --doctest-modules python/lancedb
|
run: pytest --doctest-modules python/lancedb
|
||||||
@@ -226,6 +229,5 @@ jobs:
|
|||||||
pip install "pydantic<2"
|
pip install "pydantic<2"
|
||||||
pip install pyarrow==16
|
pip install pyarrow==16
|
||||||
pip install --extra-index-url https://pypi.fury.io/lance-format/ --extra-index-url https://pypi.fury.io/lancedb/ -e .[tests]
|
pip install --extra-index-url https://pypi.fury.io/lance-format/ --extra-index-url https://pypi.fury.io/lancedb/ -e .[tests]
|
||||||
pip install tantivy
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: pytest -m "not slow and not s3_test" -x -v --durations=30 python/tests
|
run: pytest -m "not slow and not s3_test" -x -v --durations=30 python/tests
|
||||||
|
|||||||
@@ -8,9 +8,16 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- Cargo.toml
|
- Cargo.toml
|
||||||
- Cargo.lock
|
- Cargo.lock
|
||||||
|
- rust-toolchain.toml
|
||||||
|
- deny.toml
|
||||||
- rust/**
|
- rust/**
|
||||||
|
- nodejs/Cargo.toml
|
||||||
|
- python/Cargo.toml
|
||||||
- .github/workflows/rust.yml
|
- .github/workflows/rust.yml
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
@@ -52,6 +59,17 @@ jobs:
|
|||||||
- name: Run clippy (without remote feature)
|
- name: Run clippy (without remote feature)
|
||||||
run: cargo clippy --profile ci --workspace --tests -- -D warnings
|
run: cargo clippy --profile ci --workspace --tests -- -D warnings
|
||||||
|
|
||||||
|
deny:
|
||||||
|
# Supply-chain checks: advisories, licenses, banned crates, and source
|
||||||
|
# restrictions. Configuration lives in `deny.toml` at the workspace root.
|
||||||
|
timeout-minutes: 10
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||||
|
with:
|
||||||
|
command: check advisories bans licenses sources
|
||||||
|
|
||||||
build-no-lock:
|
build-no-lock:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ name: Update package-lock.json
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ name: Update NodeJs package-lock.json
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ name: upload-wheel
|
|||||||
|
|
||||||
description: "Upload wheels to Pypi"
|
description: "Upload wheels to Pypi"
|
||||||
inputs:
|
inputs:
|
||||||
pypi_token:
|
|
||||||
required: true
|
|
||||||
description: "release token for the repo"
|
|
||||||
fury_token:
|
fury_token:
|
||||||
required: true
|
required: true
|
||||||
description: "release token for the fury repo"
|
description: "release token for the fury repo"
|
||||||
@@ -12,12 +9,6 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install twine
|
|
||||||
python3 -m pip install --upgrade pkginfo
|
|
||||||
- name: Choose repo
|
- name: Choose repo
|
||||||
shell: bash
|
shell: bash
|
||||||
id: choose_repo
|
id: choose_repo
|
||||||
@@ -27,19 +18,17 @@ runs:
|
|||||||
else
|
else
|
||||||
echo "repo=pypi" >> $GITHUB_OUTPUT
|
echo "repo=pypi" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
- name: Publish to PyPI
|
- name: Publish to Fury
|
||||||
|
if: steps.choose_repo.outputs.repo == 'fury'
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
FURY_TOKEN: ${{ inputs.fury_token }}
|
FURY_TOKEN: ${{ inputs.fury_token }}
|
||||||
PYPI_TOKEN: ${{ inputs.pypi_token }}
|
|
||||||
run: |
|
run: |
|
||||||
if [[ ${{ steps.choose_repo.outputs.repo }} == fury ]]; then
|
WHEEL=$(ls target/wheels/lancedb-*.whl 2> /dev/null | head -n 1)
|
||||||
WHEEL=$(ls target/wheels/lancedb-*.whl 2> /dev/null | head -n 1)
|
echo "Uploading $WHEEL to Fury"
|
||||||
echo "Uploading $WHEEL to Fury"
|
curl -f -F package=@$WHEEL https://$FURY_TOKEN@push.fury.io/lancedb/
|
||||||
curl -f -F package=@$WHEEL https://$FURY_TOKEN@push.fury.io/lancedb/
|
- name: Publish to PyPI
|
||||||
else
|
if: steps.choose_repo.outputs.repo == 'pypi'
|
||||||
twine upload --repository ${{ steps.choose_repo.outputs.repo }} \
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
--username __token__ \
|
with:
|
||||||
--password $PYPI_TOKEN \
|
packages-dir: target/wheels/
|
||||||
target/wheels/lancedb-*.whl
|
|
||||||
fi
|
|
||||||
|
|||||||
Generated
+1842
-628
File diff suppressed because it is too large
Load Diff
+32
-34
@@ -1,7 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["rust/lancedb", "nodejs", "python"]
|
members = ["rust/lancedb", "nodejs", "python"]
|
||||||
# Python package needs to be built by maturin.
|
|
||||||
exclude = ["python"]
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
@@ -15,40 +13,40 @@ categories = ["database-implementations"]
|
|||||||
rust-version = "1.91.0"
|
rust-version = "1.91.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
lance = { version = "=4.0.0", default-features = false }
|
lance = { "version" = "=7.0.0-beta.7", default-features = false, "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-core = { version = "=4.0.0" }
|
lance-core = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-datagen = { version = "=4.0.0" }
|
lance-datagen = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-file = { version = "=4.0.0" }
|
lance-file = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-io = { version = "=4.0.0", default-features = false }
|
lance-io = { "version" = "=7.0.0-beta.7", default-features = false, "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-index = { version = "=4.0.0" }
|
lance-index = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-linalg = { version = "=4.0.0" }
|
lance-linalg = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-namespace = { version = "=4.0.0" }
|
lance-namespace = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-namespace-impls = { version = "=4.0.0", default-features = false }
|
lance-namespace-impls = { "version" = "=7.0.0-beta.7", default-features = false, "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-table = { version = "=4.0.0" }
|
lance-table = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-testing = { version = "=4.0.0" }
|
lance-testing = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-datafusion = { version = "=4.0.0" }
|
lance-datafusion = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-encoding = { version = "=4.0.0" }
|
lance-encoding = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "git" = "https://github.com/lance-format/lance.git" }
|
||||||
lance-arrow = { version = "=4.0.0" }
|
lance-arrow = { "version" = "=7.0.0-beta.7", "tag" = "v7.0.0-beta.7", "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 = "57.2", optional = false }
|
arrow = { version = "58.0.0", optional = false }
|
||||||
arrow-array = "57.2"
|
arrow-array = "58.0.0"
|
||||||
arrow-data = "57.2"
|
arrow-data = "58.0.0"
|
||||||
arrow-ipc = "57.2"
|
arrow-ipc = "58.0.0"
|
||||||
arrow-ord = "57.2"
|
arrow-ord = "58.0.0"
|
||||||
arrow-schema = "57.2"
|
arrow-schema = "58.0.0"
|
||||||
arrow-select = "57.2"
|
arrow-select = "58.0.0"
|
||||||
arrow-cast = "57.2"
|
arrow-cast = "58.0.0"
|
||||||
async-trait = "0"
|
async-trait = "0"
|
||||||
datafusion = { version = "52.1", default-features = false }
|
datafusion = { version = "53.0.0", default-features = false }
|
||||||
datafusion-catalog = "52.1"
|
datafusion-catalog = "53.0.0"
|
||||||
datafusion-common = { version = "52.1", default-features = false }
|
datafusion-common = { version = "53.0.0", default-features = false }
|
||||||
datafusion-execution = "52.1"
|
datafusion-execution = "53.0.0"
|
||||||
datafusion-expr = "52.1"
|
datafusion-expr = "53.0.0"
|
||||||
datafusion-functions = "52.1"
|
datafusion-functions = "53.0.0"
|
||||||
datafusion-physical-plan = "52.1"
|
datafusion-physical-plan = "53.0.0"
|
||||||
datafusion-physical-expr = "52.1"
|
datafusion-physical-expr = "53.0.0"
|
||||||
datafusion-sql = "52.1"
|
datafusion-sql = "53.0.0"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
half = { "version" = "2.7.1", default-features = false, features = [
|
half = { "version" = "2.7.1", default-features = false, features = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -56,7 +54,7 @@ half = { "version" = "2.7.1", default-features = false, features = [
|
|||||||
futures = "0"
|
futures = "0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
moka = { version = "0.12", features = ["future"] }
|
moka = { version = "0.12", features = ["future"] }
|
||||||
object_store = "0.12.0"
|
object_store = "0.13.2"
|
||||||
pin-project = "1.0.7"
|
pin-project = "1.0.7"
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
snafu = "0.8"
|
snafu = "0.8"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
# **The Multimodal AI Lakehouse**
|
# **The Multimodal AI Lakehouse**
|
||||||
|
|
||||||
[**How to Install** ](#how-to-install) ✦ [**Detailed Documentation**](https://lancedb.com/docs) ✦ [**Tutorials and Recipes**](https://github.com/lancedb/vectordb-recipes/tree/main) ✦ [**Contributors**](#contributors)
|
[**How to Install** ](#how-to-install) ✦ [**Detailed Documentation**](https://docs.lancedb.com) ✦ [**Tutorials and Recipes**](https://github.com/lancedb/vectordb-recipes/tree/main) ✦ [**Contributors**](#contributors)
|
||||||
|
|
||||||
**The ultimate multimodal data platform for AI/ML applications.**
|
**The ultimate multimodal data platform for AI/ML applications.**
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ LanceDB is a central location where developers can build, train and analyze thei
|
|||||||
|
|
||||||
## **How to Install**:
|
## **How to Install**:
|
||||||
|
|
||||||
Follow the [Quickstart](https://lancedb.com/docs/quickstart/) doc to set up LanceDB locally.
|
Follow the [Quickstart](https://docs.lancedb.com/quickstart) doc to set up LanceDB locally.
|
||||||
|
|
||||||
**API & SDK:** We also support Python, Typescript and Rust SDKs
|
**API & SDK:** We also support Python, Typescript and Rust SDKs
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# cargo-deny configuration for LanceDB.
|
||||||
|
#
|
||||||
|
# Run locally with `cargo deny check`. See
|
||||||
|
# https://embarkstudios.github.io/cargo-deny/ for the full reference.
|
||||||
|
|
||||||
|
# The set of target triples we care about. cargo-deny will only consider
|
||||||
|
# dependencies that are used on at least one of these targets. Keeping this
|
||||||
|
# explicit avoids noise from platform-specific crates (e.g. wasm, android,
|
||||||
|
# ios) that we never actually ship.
|
||||||
|
[graph]
|
||||||
|
targets = [
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"x86_64-apple-darwin",
|
||||||
|
"aarch64-apple-darwin",
|
||||||
|
"x86_64-pc-windows-msvc",
|
||||||
|
"aarch64-pc-windows-msvc",
|
||||||
|
]
|
||||||
|
all-features = true
|
||||||
|
|
||||||
|
[output]
|
||||||
|
feature-depth = 1
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Advisories: security vulnerabilities and yanked crates.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[advisories]
|
||||||
|
version = 2
|
||||||
|
# Fail the check if any crate in the lockfile has been yanked from crates.io.
|
||||||
|
# Yanked crates are a signal the author retracted the release (often due to
|
||||||
|
# bugs or security issues) and should not be depended on.
|
||||||
|
yanked = "deny"
|
||||||
|
# Advisory IDs we have explicitly reviewed and chosen to accept. Every
|
||||||
|
# entry must include a rationale and, where possible, an upstream issue
|
||||||
|
# pointing to a fix. Revisit this list whenever dependencies are updated.
|
||||||
|
ignore = [
|
||||||
|
# rsa: Marvin Attack timing side-channel in PKCS#1 v1.5 decryption.
|
||||||
|
# Reached only through opendal → reqsign → rsa. We do not use RSA
|
||||||
|
# decryption in LanceDB ourselves; this is dormant in the signing path.
|
||||||
|
# No fixed release exists upstream as of this writing.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2023-0071
|
||||||
|
{ id = "RUSTSEC-2023-0071", reason = "rsa crate via opendal/reqsign; no fixed upstream release" },
|
||||||
|
|
||||||
|
# instant: unmaintained. Pulled in via backoff → instant. Upstream
|
||||||
|
# recommends switching to `web-time`; fix has to come from backoff.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2024-0384
|
||||||
|
{ id = "RUSTSEC-2024-0384", reason = "transitive via backoff; waiting on backoff replacement" },
|
||||||
|
|
||||||
|
# paste: unmaintained (author archived the repo). Used transitively by
|
||||||
|
# datafusion and the arrow ecosystem; widespread, no drop-in replacement.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2024-0436
|
||||||
|
{ id = "RUSTSEC-2024-0436", reason = "transitive via datafusion; awaiting ecosystem migration" },
|
||||||
|
|
||||||
|
# encoding: unmaintained. Reached through lindera-dictionary, which is
|
||||||
|
# required by the native Lindera tokenizer path. Lindera has not migrated
|
||||||
|
# off this crate yet.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2021-0153
|
||||||
|
{ id = "RUSTSEC-2021-0153", reason = "transitive via lindera-dictionary for native Lindera tokenizer" },
|
||||||
|
|
||||||
|
# fast-float: unsound and unmaintained. Reached only through polars-arrow
|
||||||
|
# from the optional Polars integration; replacement requires a Polars
|
||||||
|
# dependency upgrade.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2024-0379
|
||||||
|
{ id = "RUSTSEC-2024-0379", reason = "transitive via polars-arrow; waiting on Polars migration" },
|
||||||
|
|
||||||
|
# tantivy: segfault on malformed input due to missing bounds check.
|
||||||
|
# Pulled in via lance for full-text search. We only feed tantivy
|
||||||
|
# documents we construct ourselves, not attacker-controlled bytes.
|
||||||
|
# Tracked for a lance dependency bump.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2025-0003
|
||||||
|
{ id = "RUSTSEC-2025-0003", reason = "tantivy via lance; inputs are internally produced, not user-supplied bytes" },
|
||||||
|
|
||||||
|
# backoff: unmaintained. Reached only via async-openai. Replacement
|
||||||
|
# requires async-openai to migrate (or us to drop async-openai).
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2025-0012
|
||||||
|
{ id = "RUSTSEC-2025-0012", reason = "transitive via async-openai; waiting on upstream migration" },
|
||||||
|
|
||||||
|
# number_prefix: unmaintained. Transitive via indicatif → hf-hub.
|
||||||
|
# No security impact, just maintenance status.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2025-0119
|
||||||
|
{ id = "RUSTSEC-2025-0119", reason = "transitive via hf-hub/indicatif; cosmetic formatting crate" },
|
||||||
|
|
||||||
|
# bincode: unmaintained. Reached through lindera and lindera-dictionary,
|
||||||
|
# which are required by the native Lindera tokenizer path. Lindera has not
|
||||||
|
# migrated to another serialization format yet.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2025-0141
|
||||||
|
{ id = "RUSTSEC-2025-0141", reason = "transitive via lindera/lindera-dictionary for native Lindera tokenizer" },
|
||||||
|
|
||||||
|
# lru: soundness issue in IterMut. Reached only through aws-sdk-s3 in
|
||||||
|
# LanceDB's dev-dependency graph; LanceDB does not use that iterator
|
||||||
|
# directly. Clearing this requires the AWS SDK chain to update lru.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2026-0002
|
||||||
|
{ id = "RUSTSEC-2026-0002", reason = "transitive via aws-sdk-s3 dev-dependency; waiting on AWS SDK lru upgrade" },
|
||||||
|
|
||||||
|
# rustls-webpki 0.101.7 (old major line): name-constraint checks for
|
||||||
|
# URI / wildcard names. Pulled in only via the legacy rustls 0.21 chain
|
||||||
|
# from aws-smithy-http-client. The 0.103 line we actively use is patched.
|
||||||
|
# Clearing the 0.101 copy requires the aws-sdk chain to migrate off
|
||||||
|
# rustls 0.21.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2026-0098
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2026-0099
|
||||||
|
{ id = "RUSTSEC-2026-0098", reason = "only affects rustls-webpki 0.101 from legacy aws-smithy/rustls 0.21 chain" },
|
||||||
|
{ id = "RUSTSEC-2026-0099", reason = "only affects rustls-webpki 0.101 from legacy aws-smithy/rustls 0.21 chain" },
|
||||||
|
|
||||||
|
# rustls-webpki 0.101.7: reachable panic in CRL parsing. Same legacy
|
||||||
|
# rustls 0.21 chain from aws-smithy-http-client as above. The 0.103 line
|
||||||
|
# we actively use is upgraded to 0.103.13 which contains the fix.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2026-0104
|
||||||
|
{ id = "RUSTSEC-2026-0104", reason = "only affects rustls-webpki 0.101 from legacy aws-smithy/rustls 0.21 chain" },
|
||||||
|
|
||||||
|
# rand 0.8.5: soundness issue only when ThreadRng reseeds inside a custom
|
||||||
|
# logger. Reached through several transitive chains. LanceDB does not use
|
||||||
|
# rand from a custom logger; upgrade once all pinned chains accept 0.8.6+.
|
||||||
|
# https://rustsec.org/advisories/RUSTSEC-2026-0097
|
||||||
|
{ id = "RUSTSEC-2026-0097", reason = "transitive rand 0.8.5; LanceDB does not call ThreadRng from custom logging" },
|
||||||
|
]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Licenses: only allow licenses we've reviewed as compatible with Apache-2.0.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[licenses]
|
||||||
|
version = 2
|
||||||
|
# SPDX identifiers for licenses that are compatible with our Apache-2.0
|
||||||
|
# distribution. Additions require legal review.
|
||||||
|
allow = [
|
||||||
|
"Apache-2.0",
|
||||||
|
"Apache-2.0 WITH LLVM-exception",
|
||||||
|
"MIT",
|
||||||
|
"BSD-2-Clause",
|
||||||
|
"BSD-3-Clause",
|
||||||
|
"ISC",
|
||||||
|
"Unicode-3.0",
|
||||||
|
"Unicode-DFS-2016",
|
||||||
|
"Zlib",
|
||||||
|
"CC0-1.0",
|
||||||
|
"MPL-2.0",
|
||||||
|
"BSL-1.0",
|
||||||
|
"OpenSSL",
|
||||||
|
# 0BSD ("BSD Zero Clause") is effectively public domain — no attribution
|
||||||
|
# required. Pulled in by `mock_instant`.
|
||||||
|
"0BSD",
|
||||||
|
# bzip2-1.0.6 is the permissive upstream bzip2 license (BSD-like). Pulled
|
||||||
|
# in by `libbz2-rs-sys`, the pure-Rust bzip2 implementation.
|
||||||
|
"bzip2-1.0.6",
|
||||||
|
# CDLA-Permissive-2.0 is a permissive data license used by `webpki-roots`
|
||||||
|
# for the Mozilla CA root bundle. Data-only, distribution-compatible.
|
||||||
|
"CDLA-Permissive-2.0",
|
||||||
|
]
|
||||||
|
confidence-threshold = 0.8
|
||||||
|
# Crates whose license cannot be determined from Cargo metadata but whose
|
||||||
|
# license we've manually confirmed from upstream. Keep this list minimal.
|
||||||
|
[[licenses.clarify]]
|
||||||
|
# polars-arrow-format omits the `license` field in its Cargo.toml, but the
|
||||||
|
# upstream repo (pola-rs/polars-arrow-format) is dual-licensed Apache-2.0 OR
|
||||||
|
# MIT. See https://github.com/pola-rs/polars-arrow-format/blob/main/LICENSE
|
||||||
|
crate = "polars-arrow-format"
|
||||||
|
expression = "Apache-2.0 OR MIT"
|
||||||
|
license-files = []
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Bans: disallow specific crates and flag dependency hygiene issues.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[bans]
|
||||||
|
# Warn (not deny) on duplicate versions of the same crate. In a large
|
||||||
|
# workspace like this one, duplicates are common and often unavoidable
|
||||||
|
# transitively. We surface them to discourage growth, but don't fail CI.
|
||||||
|
multiple-versions = "warn"
|
||||||
|
# Wildcard version requirements (`foo = "*"`) are a footgun — they let any
|
||||||
|
# future release in without review. Ban them outright.
|
||||||
|
wildcards = "deny"
|
||||||
|
# Internal workspace crates reference each other via `path = "..."`, which
|
||||||
|
# cargo-deny sees as a wildcard version. That's fine for private workspace
|
||||||
|
# members (not published to crates.io), so allow it specifically for paths.
|
||||||
|
allow-wildcard-paths = true
|
||||||
|
# Features that, if enabled, should cause the check to fail.
|
||||||
|
deny = []
|
||||||
|
# Crates to skip when checking for duplicate versions.
|
||||||
|
skip = []
|
||||||
|
# Similar to `skip`, but also skips the entire transitive subtree.
|
||||||
|
skip-tree = []
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Sources: restrict where crates can come from.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[sources]
|
||||||
|
# Deny any registry other than the ones explicitly listed below.
|
||||||
|
unknown-registry = "deny"
|
||||||
|
# Deny any git dependency whose host isn't in the allow-list below. This
|
||||||
|
# prevents accidental pulls from arbitrary forks.
|
||||||
|
unknown-git = "deny"
|
||||||
|
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||||
|
# Lance is developed in a sibling repo and pulled as a git dependency until
|
||||||
|
# releases are cut to crates.io. Allow that specific host.
|
||||||
|
allow-git = [
|
||||||
|
"https://github.com/lance-format/lance",
|
||||||
|
]
|
||||||
@@ -24,4 +24,4 @@ RUN python --version && \
|
|||||||
rustc --version && \
|
rustc --version && \
|
||||||
protoc --version
|
protoc --version
|
||||||
|
|
||||||
RUN pip install --no-cache-dir tantivy lancedb
|
RUN pip install --no-cache-dir lancedb
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# LanceDB Documentation
|
# LanceDB Documentation
|
||||||
|
|
||||||
LanceDB docs are available at [lancedb.com/docs](https://lancedb.com/docs).
|
LanceDB docs are available at [docs.lancedb.com](https://docs.lancedb.com).
|
||||||
|
|
||||||
The SDK docs are built and deployed automatically by [Github Actions](../.github/workflows/docs.yml)
|
The SDK docs are built and deployed automatically by [Github Actions](../.github/workflows/docs.yml)
|
||||||
whenever a commit is pushed to the `main` branch. So it is possible for the docs to show
|
whenever a commit is pushed to the `main` branch. So it is possible for the docs to show
|
||||||
|
|||||||
+15
-15
@@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`:
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-core</artifactId>
|
<artifactId>lancedb-core</artifactId>
|
||||||
<version>0.27.2-beta.1</version>
|
<version>0.28.0-beta.11</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -57,32 +57,32 @@ LanceNamespace namespaceClient = LanceDbNamespaceClientBuilder.newBuilder()
|
|||||||
|
|
||||||
## Metadata Operations
|
## Metadata Operations
|
||||||
|
|
||||||
### Creating a Namespace
|
### Creating a Namespace Path
|
||||||
|
|
||||||
Namespaces organize tables hierarchically. Create a namespace before creating tables within it:
|
Namespace paths organize tables hierarchically. Create the desired namespace path before creating tables within it:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import org.lance.namespace.model.CreateNamespaceRequest;
|
import org.lance.namespace.model.CreateNamespaceRequest;
|
||||||
import org.lance.namespace.model.CreateNamespaceResponse;
|
import org.lance.namespace.model.CreateNamespaceResponse;
|
||||||
|
|
||||||
// Create a child namespace
|
// Create a child namespace path
|
||||||
CreateNamespaceRequest request = new CreateNamespaceRequest();
|
CreateNamespaceRequest request = new CreateNamespaceRequest();
|
||||||
request.setId(Arrays.asList("my_namespace"));
|
request.setId(Arrays.asList("my_namespace"));
|
||||||
|
|
||||||
CreateNamespaceResponse response = namespaceClient.createNamespace(request);
|
CreateNamespaceResponse response = namespaceClient.createNamespace(request);
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also create nested namespaces:
|
You can also create nested namespace paths:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// Create a nested namespace: parent/child
|
// Create a nested namespace path: parent/child
|
||||||
CreateNamespaceRequest request = new CreateNamespaceRequest();
|
CreateNamespaceRequest request = new CreateNamespaceRequest();
|
||||||
request.setId(Arrays.asList("parent_namespace", "child_namespace"));
|
request.setId(Arrays.asList("parent_namespace", "child_namespace"));
|
||||||
|
|
||||||
CreateNamespaceResponse response = namespaceClient.createNamespace(request);
|
CreateNamespaceResponse response = namespaceClient.createNamespace(request);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Describing a Namespace
|
### Describing a Namespace Path
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import org.lance.namespace.model.DescribeNamespaceRequest;
|
import org.lance.namespace.model.DescribeNamespaceRequest;
|
||||||
@@ -95,22 +95,22 @@ DescribeNamespaceResponse response = namespaceClient.describeNamespace(request);
|
|||||||
System.out.println("Namespace properties: " + response.getProperties());
|
System.out.println("Namespace properties: " + response.getProperties());
|
||||||
```
|
```
|
||||||
|
|
||||||
### Listing Namespaces
|
### Listing Namespace Paths
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import org.lance.namespace.model.ListNamespacesRequest;
|
import org.lance.namespace.model.ListNamespacesRequest;
|
||||||
import org.lance.namespace.model.ListNamespacesResponse;
|
import org.lance.namespace.model.ListNamespacesResponse;
|
||||||
|
|
||||||
// List all namespaces at root level
|
// List all namespace paths at the root level
|
||||||
ListNamespacesRequest request = new ListNamespacesRequest();
|
ListNamespacesRequest request = new ListNamespacesRequest();
|
||||||
request.setId(Arrays.asList()); // Empty for root
|
request.setId(Arrays.asList()); // Empty for root
|
||||||
|
|
||||||
ListNamespacesResponse response = namespaceClient.listNamespaces(request);
|
ListNamespacesResponse response = namespaceClient.listNamespaces(request);
|
||||||
for (String ns : response.getNamespaces()) {
|
for (String ns : response.getNamespaces()) {
|
||||||
System.out.println("Namespace: " + ns);
|
System.out.println("Namespace path: " + ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List child namespaces under a parent
|
// List child namespace paths under a parent path
|
||||||
ListNamespacesRequest childRequest = new ListNamespacesRequest();
|
ListNamespacesRequest childRequest = new ListNamespacesRequest();
|
||||||
childRequest.setId(Arrays.asList("parent_namespace"));
|
childRequest.setId(Arrays.asList("parent_namespace"));
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ ListNamespacesResponse childResponse = namespaceClient.listNamespaces(childReque
|
|||||||
import org.lance.namespace.model.ListTablesRequest;
|
import org.lance.namespace.model.ListTablesRequest;
|
||||||
import org.lance.namespace.model.ListTablesResponse;
|
import org.lance.namespace.model.ListTablesResponse;
|
||||||
|
|
||||||
// List tables in a namespace
|
// List tables in a namespace path
|
||||||
ListTablesRequest request = new ListTablesRequest();
|
ListTablesRequest request = new ListTablesRequest();
|
||||||
request.setId(Arrays.asList("my_namespace"));
|
request.setId(Arrays.asList("my_namespace"));
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ for (String table : response.getTables()) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dropping a Namespace
|
### Dropping a Namespace Path
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import org.lance.namespace.model.DropNamespaceRequest;
|
import org.lance.namespace.model.DropNamespaceRequest;
|
||||||
@@ -175,7 +175,7 @@ DropTableResponse response = namespaceClient.dropTable(request);
|
|||||||
|
|
||||||
### Creating a Table
|
### Creating a Table
|
||||||
|
|
||||||
Tables are created within a namespace by providing data in Apache Arrow IPC format:
|
Tables are created within a namespace path by providing data in Apache Arrow IPC format:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import org.lance.namespace.LanceNamespace;
|
import org.lance.namespace.LanceNamespace;
|
||||||
@@ -242,7 +242,7 @@ try (BufferAllocator allocator = new RootAllocator();
|
|||||||
}
|
}
|
||||||
byte[] tableData = out.toByteArray();
|
byte[] tableData = out.toByteArray();
|
||||||
|
|
||||||
// Create table in a namespace
|
// Create a table in a namespace path
|
||||||
CreateTableRequest request = new CreateTableRequest();
|
CreateTableRequest request = new CreateTableRequest();
|
||||||
request.setId(Arrays.asList("my_namespace", "my_table"));
|
request.setId(Arrays.asList("my_namespace", "my_table"));
|
||||||
CreateTableResponse response = namespaceClient.createTable(request, tableData);
|
CreateTableResponse response = namespaceClient.createTable(request, tableData);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const results = await table.vectorSearch([0.1, 0.3]).limit(20).toArray();
|
|||||||
console.log(results);
|
console.log(results);
|
||||||
```
|
```
|
||||||
|
|
||||||
The [quickstart](https://lancedb.com/docs/quickstart/basic-usage/) contains more complete examples.
|
The [quickstart](https://docs.lancedb.com/quickstart/) contains more complete examples.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|||||||
@@ -12,20 +12,22 @@ Typescript.
|
|||||||
* `src/`: Rust bindings source code
|
* `src/`: Rust bindings source code
|
||||||
* `lancedb/`: Typescript package source code
|
* `lancedb/`: Typescript package source code
|
||||||
* `__test__/`: Unit tests
|
* `__test__/`: Unit tests
|
||||||
* `examples/`: An npm package with the examples shown in the documentation
|
* `examples/`: A pnpm package with the examples shown in the documentation
|
||||||
|
|
||||||
## Development environment
|
## Development environment
|
||||||
|
|
||||||
To set up your development environment, you will need to install the following:
|
To set up your development environment, you will need to install the following:
|
||||||
|
|
||||||
1. Node.js 14 or later
|
1. Node.js 22 or later (required by pnpm 11)
|
||||||
2. Rust's package manager, Cargo. Use [rustup](https://rustup.rs/) to install.
|
2. [pnpm](https://pnpm.io/installation) 11 or later (or run via `corepack enable`,
|
||||||
3. [protoc](https://grpc.io/docs/protoc-installation/) (Protocol Buffers compiler)
|
which uses the `packageManager` field in `package.json`)
|
||||||
|
3. Rust's package manager, Cargo. Use [rustup](https://rustup.rs/) to install.
|
||||||
|
4. [protoc](https://grpc.io/docs/protoc-installation/) (Protocol Buffers compiler)
|
||||||
|
|
||||||
Initial setup:
|
Initial setup:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Commit Hooks
|
### Commit Hooks
|
||||||
@@ -39,38 +41,38 @@ pre-commit install
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Most common development commands can be run using the npm scripts.
|
Most common development commands can be run using the pnpm scripts.
|
||||||
|
|
||||||
Build the package
|
Build the package
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install
|
pnpm install
|
||||||
npm run build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
Lint:
|
Lint:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm run lint
|
pnpm lint
|
||||||
```
|
```
|
||||||
|
|
||||||
Format and fix lints:
|
Format and fix lints:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm run lint-fix
|
pnpm lint-fix
|
||||||
```
|
```
|
||||||
|
|
||||||
Run tests:
|
Run tests:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm test
|
pnpm test
|
||||||
```
|
```
|
||||||
|
|
||||||
To run a single test:
|
To run a single test:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Single file: table.test.ts
|
# Single file: table.test.ts
|
||||||
npm test -- table.test.ts
|
pnpm test -- table.test.ts
|
||||||
# Single test: 'merge insert' in table.test.ts
|
# Single test: 'merge insert' in table.test.ts
|
||||||
npm test -- table.test.ts --testNamePattern=merge\ insert
|
pnpm test -- table.test.ts --testNamePattern=merge\ insert
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ sharing the same data, deletion, and index files.
|
|||||||
* **options.sourceVersion?**: `number`
|
* **options.sourceVersion?**: `number`
|
||||||
The version of the source table to clone.
|
The version of the source table to clone.
|
||||||
|
|
||||||
* **options.targetNamespace?**: `string`[]
|
* **options.targetNamespacePath?**: `string`[]
|
||||||
The namespace for the target table (defaults to root namespace).
|
The namespace path for the target table (defaults to root namespace).
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
@@ -116,13 +116,13 @@ Creates a new empty Table
|
|||||||
|
|
||||||
`Promise`<[`Table`](Table.md)>
|
`Promise`<[`Table`](Table.md)>
|
||||||
|
|
||||||
#### createEmptyTable(name, schema, namespace, options)
|
#### createEmptyTable(name, schema, namespacePath, options)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract createEmptyTable(
|
abstract createEmptyTable(
|
||||||
name,
|
name,
|
||||||
schema,
|
schema,
|
||||||
namespace?,
|
namespacePath?,
|
||||||
options?): Promise<Table>
|
options?): Promise<Table>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -136,8 +136,8 @@ Creates a new empty Table
|
|||||||
* **schema**: [`SchemaLike`](../type-aliases/SchemaLike.md)
|
* **schema**: [`SchemaLike`](../type-aliases/SchemaLike.md)
|
||||||
The schema of the table
|
The schema of the table
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace to create the table in (defaults to root namespace)
|
The namespace path to create the table in (defaults to root namespace)
|
||||||
|
|
||||||
* **options?**: `Partial`<[`CreateTableOptions`](../interfaces/CreateTableOptions.md)>
|
* **options?**: `Partial`<[`CreateTableOptions`](../interfaces/CreateTableOptions.md)>
|
||||||
Additional options
|
Additional options
|
||||||
@@ -148,12 +148,39 @@ Creates a new empty Table
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
### createTable()
|
### createNamespace()
|
||||||
|
|
||||||
#### createTable(options, namespace)
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract createTable(options, namespace?): Promise<Table>
|
abstract createNamespace(namespacePath, options?): Promise<CreateNamespaceResponse>
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new namespace at the given path.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* **namespacePath**: `string`[]
|
||||||
|
The namespace path to create.
|
||||||
|
|
||||||
|
* **options?**: `Partial`<[`CreateNamespaceOptions`](../interfaces/CreateNamespaceOptions.md)>
|
||||||
|
Creation `mode`
|
||||||
|
("create" | "exist_ok" | "overwrite") and optional `properties`
|
||||||
|
to attach to the namespace.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Promise`<[`CreateNamespaceResponse`](../interfaces/CreateNamespaceResponse.md)>
|
||||||
|
|
||||||
|
The properties of the
|
||||||
|
created namespace and an optional transaction id.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### createTable()
|
||||||
|
|
||||||
|
#### createTable(options, namespacePath)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
abstract createTable(options, namespacePath?): Promise<Table>
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates a new Table and initialize it with new data.
|
Creates a new Table and initialize it with new data.
|
||||||
@@ -163,8 +190,8 @@ Creates a new Table and initialize it with new data.
|
|||||||
* **options**: `object` & `Partial`<[`CreateTableOptions`](../interfaces/CreateTableOptions.md)>
|
* **options**: `object` & `Partial`<[`CreateTableOptions`](../interfaces/CreateTableOptions.md)>
|
||||||
The options object.
|
The options object.
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace to create the table in (defaults to root namespace)
|
The namespace path to create the table in (defaults to root namespace)
|
||||||
|
|
||||||
##### Returns
|
##### Returns
|
||||||
|
|
||||||
@@ -197,13 +224,13 @@ Creates a new Table and initialize it with new data.
|
|||||||
|
|
||||||
`Promise`<[`Table`](Table.md)>
|
`Promise`<[`Table`](Table.md)>
|
||||||
|
|
||||||
#### createTable(name, data, namespace, options)
|
#### createTable(name, data, namespacePath, options)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract createTable(
|
abstract createTable(
|
||||||
name,
|
name,
|
||||||
data,
|
data,
|
||||||
namespace?,
|
namespacePath?,
|
||||||
options?): Promise<Table>
|
options?): Promise<Table>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -218,8 +245,8 @@ Creates a new Table and initialize it with new data.
|
|||||||
Non-empty Array of Records
|
Non-empty Array of Records
|
||||||
to be inserted into the table
|
to be inserted into the table
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace to create the table in (defaults to root namespace)
|
The namespace path to create the table in (defaults to root namespace)
|
||||||
|
|
||||||
* **options?**: `Partial`<[`CreateTableOptions`](../interfaces/CreateTableOptions.md)>
|
* **options?**: `Partial`<[`CreateTableOptions`](../interfaces/CreateTableOptions.md)>
|
||||||
Additional options
|
Additional options
|
||||||
@@ -230,6 +257,29 @@ Creates a new Table and initialize it with new data.
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
### describeNamespace()
|
||||||
|
|
||||||
|
```ts
|
||||||
|
abstract describeNamespace(namespacePath): Promise<DescribeNamespaceResponse>
|
||||||
|
```
|
||||||
|
|
||||||
|
Describe a namespace, returning its properties.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* **namespacePath**: `string`[]
|
||||||
|
The namespace path to describe, in
|
||||||
|
parent → child order, e.g. `["analytics", "sales"]`.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Promise`<[`DescribeNamespaceResponse`](../interfaces/DescribeNamespaceResponse.md)>
|
||||||
|
|
||||||
|
The namespace's properties
|
||||||
|
(may be undefined if the namespace has none).
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
### display()
|
### display()
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@@ -247,15 +297,15 @@ Return a brief description of the connection
|
|||||||
### dropAllTables()
|
### dropAllTables()
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract dropAllTables(namespace?): Promise<void>
|
abstract dropAllTables(namespacePath?): Promise<void>
|
||||||
```
|
```
|
||||||
|
|
||||||
Drop all tables in the database.
|
Drop all tables in the database.
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace to drop tables from (defaults to root namespace).
|
The namespace path to drop tables from (defaults to root namespace).
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
@@ -263,10 +313,40 @@ Drop all tables in the database.
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
### dropNamespace()
|
||||||
|
|
||||||
|
```ts
|
||||||
|
abstract dropNamespace(namespacePath, options?): Promise<DropNamespaceResponse>
|
||||||
|
```
|
||||||
|
|
||||||
|
Drop a namespace.
|
||||||
|
|
||||||
|
Use `behavior: "cascade"` to also drop everything contained in the
|
||||||
|
namespace (sub-namespaces and tables). The default `"restrict"`
|
||||||
|
behavior refuses to drop a non-empty namespace.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* **namespacePath**: `string`[]
|
||||||
|
The namespace path to drop.
|
||||||
|
|
||||||
|
* **options?**: `Partial`<[`DropNamespaceOptions`](../interfaces/DropNamespaceOptions.md)>
|
||||||
|
`mode` ("skip" | "fail"
|
||||||
|
for missing-namespace handling) and `behavior` ("restrict" | "cascade").
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Promise`<[`DropNamespaceResponse`](../interfaces/DropNamespaceResponse.md)>
|
||||||
|
|
||||||
|
Any properties returned by
|
||||||
|
the server and an optional transaction id.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
### dropTable()
|
### dropTable()
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract dropTable(name, namespace?): Promise<void>
|
abstract dropTable(name, namespacePath?): Promise<void>
|
||||||
```
|
```
|
||||||
|
|
||||||
Drop an existing table.
|
Drop an existing table.
|
||||||
@@ -276,8 +356,8 @@ Drop an existing table.
|
|||||||
* **name**: `string`
|
* **name**: `string`
|
||||||
The name of the table to drop.
|
The name of the table to drop.
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace of the table (defaults to root namespace).
|
The namespace path of the table (defaults to root namespace).
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
@@ -299,12 +379,42 @@ Return true if the connection has not been closed
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
### listNamespaces()
|
||||||
|
|
||||||
|
```ts
|
||||||
|
abstract listNamespaces(namespacePath?, options?): Promise<ListNamespacesResponse>
|
||||||
|
```
|
||||||
|
|
||||||
|
List the immediate child namespaces under the given parent.
|
||||||
|
|
||||||
|
Results may be paginated. To retrieve subsequent pages, pass the
|
||||||
|
`pageToken` returned by a previous call.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* **namespacePath?**: `string`[]
|
||||||
|
The parent namespace path. Defaults
|
||||||
|
to the root namespace if omitted.
|
||||||
|
|
||||||
|
* **options?**: `Partial`<[`ListNamespacesOptions`](../interfaces/ListNamespacesOptions.md)>
|
||||||
|
Pagination options
|
||||||
|
(`pageToken`, `limit`).
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Promise`<[`ListNamespacesResponse`](../interfaces/ListNamespacesResponse.md)>
|
||||||
|
|
||||||
|
Child namespace names and
|
||||||
|
an optional token for fetching the next page.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
### openTable()
|
### openTable()
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract openTable(
|
abstract openTable(
|
||||||
name,
|
name,
|
||||||
namespace?,
|
namespacePath?,
|
||||||
options?): Promise<Table>
|
options?): Promise<Table>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -315,8 +425,8 @@ Open a table in the database.
|
|||||||
* **name**: `string`
|
* **name**: `string`
|
||||||
The name of the table
|
The name of the table
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace of the table (defaults to root namespace)
|
The namespace path of the table (defaults to root namespace)
|
||||||
|
|
||||||
* **options?**: `Partial`<[`OpenTableOptions`](../interfaces/OpenTableOptions.md)>
|
* **options?**: `Partial`<[`OpenTableOptions`](../interfaces/OpenTableOptions.md)>
|
||||||
Additional options
|
Additional options
|
||||||
@@ -349,10 +459,10 @@ Tables will be returned in lexicographical order.
|
|||||||
|
|
||||||
`Promise`<`string`[]>
|
`Promise`<`string`[]>
|
||||||
|
|
||||||
#### tableNames(namespace, options)
|
#### tableNames(namespacePath, options)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
abstract tableNames(namespace?, options?): Promise<string[]>
|
abstract tableNames(namespacePath?, options?): Promise<string[]>
|
||||||
```
|
```
|
||||||
|
|
||||||
List all the table names in this database.
|
List all the table names in this database.
|
||||||
@@ -361,8 +471,8 @@ Tables will be returned in lexicographical order.
|
|||||||
|
|
||||||
##### Parameters
|
##### Parameters
|
||||||
|
|
||||||
* **namespace?**: `string`[]
|
* **namespacePath?**: `string`[]
|
||||||
The namespace to list tables from (defaults to root namespace)
|
The namespace path to list tables from (defaults to root namespace)
|
||||||
|
|
||||||
* **options?**: `Partial`<[`TableNamesOptions`](../interfaces/TableNamesOptions.md)>
|
* **options?**: `Partial`<[`TableNamesOptions`](../interfaces/TableNamesOptions.md)>
|
||||||
options to control the
|
options to control the
|
||||||
|
|||||||
@@ -501,6 +501,34 @@ Modeled after ``VACUUM`` in PostgreSQL.
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
### prewarmData()
|
||||||
|
|
||||||
|
```ts
|
||||||
|
abstract prewarmData(columns?): Promise<void>
|
||||||
|
```
|
||||||
|
|
||||||
|
Prewarm one or more columns of data in the table.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
* **columns?**: `string`[]
|
||||||
|
The columns to prewarm. If undefined, all columns are prewarmed.
|
||||||
|
This will load the column data into the page cache so that future queries that
|
||||||
|
read those columns avoid the initial cold-start latency. This call initiates
|
||||||
|
prewarming and returns once the request is accepted; the warming itself may
|
||||||
|
continue in the background. Calling it on already-prewarmed columns is a
|
||||||
|
no-op on the server.
|
||||||
|
Prewarming is generally useful for columns used in filters or projections.
|
||||||
|
Large columns (e.g. high-dimensional vectors or binary data) may not be
|
||||||
|
practical to prewarm.
|
||||||
|
This feature is currently only supported on remote tables.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
`Promise`<`void`>
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
### prewarmIndex()
|
### prewarmIndex()
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / connectNamespace
|
||||||
|
|
||||||
|
# Function: connectNamespace()
|
||||||
|
|
||||||
|
## connectNamespace(implName, config, options)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function connectNamespace(
|
||||||
|
implName,
|
||||||
|
config,
|
||||||
|
options?): Promise<Connection>
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect to a LanceDB database through a namespace.
|
||||||
|
|
||||||
|
Unlike [connect](connect.md), which routes by URI scheme (local path vs.
|
||||||
|
`db://` cloud), `connectNamespace` always returns a namespace-backed
|
||||||
|
connection. The `implName` selects the namespace implementation:
|
||||||
|
|
||||||
|
- `"dir"` — directory namespace, configured with [DirNamespaceConfig](../interfaces/DirNamespaceConfig.md).
|
||||||
|
- `"rest"` — remote REST catalog, configured with [RestNamespaceConfig](../interfaces/RestNamespaceConfig.md).
|
||||||
|
- Any other string — full module path for a custom implementation,
|
||||||
|
configured with a free-form string-keyed `properties` map.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* **implName**: `"dir"`
|
||||||
|
|
||||||
|
* **config**: [`DirNamespaceConfig`](../interfaces/DirNamespaceConfig.md)
|
||||||
|
|
||||||
|
* **options?**: `Partial`<[`ConnectNamespaceOptions`](../interfaces/ConnectNamespaceOptions.md)>
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
`Promise`<[`Connection`](../classes/Connection.md)>
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const db = await connectNamespace("dir", { root: "/path/to/db" });
|
||||||
|
await db.createTable("users", [{ id: 1 }]);
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const db = await connectNamespace("rest", {
|
||||||
|
uri: "https://catalog.example.com",
|
||||||
|
headers: { "x-api-key": process.env.CATALOG_KEY ?? "" },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const db = await connectNamespace("my.custom.Namespace", {
|
||||||
|
endpoint: "...",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## connectNamespace(implName, config, options)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function connectNamespace(
|
||||||
|
implName,
|
||||||
|
config,
|
||||||
|
options?): Promise<Connection>
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect through the built-in REST namespace.
|
||||||
|
|
||||||
|
Configured with [RestNamespaceConfig](../interfaces/RestNamespaceConfig.md). See the function-level
|
||||||
|
documentation above for the full surface, examples, and how this
|
||||||
|
relates to [connect](connect.md).
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* **implName**: `"rest"`
|
||||||
|
|
||||||
|
* **config**: [`RestNamespaceConfig`](../interfaces/RestNamespaceConfig.md)
|
||||||
|
|
||||||
|
* **options?**: `Partial`<[`ConnectNamespaceOptions`](../interfaces/ConnectNamespaceOptions.md)>
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
`Promise`<[`Connection`](../classes/Connection.md)>
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const db = await connectNamespace("rest", {
|
||||||
|
uri: "https://catalog.example.com",
|
||||||
|
headers: { "x-api-key": process.env.CATALOG_KEY ?? "" },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## connectNamespace(implName, properties, options)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function connectNamespace(
|
||||||
|
implName,
|
||||||
|
properties,
|
||||||
|
options?): Promise<Connection>
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect through a custom namespace implementation by full module path,
|
||||||
|
configured with a free-form string-keyed `properties` map. Use the
|
||||||
|
typed overloads above for the built-in `"dir"` and `"rest"` impls.
|
||||||
|
|
||||||
|
See the function-level documentation above for examples and how this
|
||||||
|
relates to [connect](connect.md).
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
* **implName**: `string`
|
||||||
|
|
||||||
|
* **properties**: `Record`<`string`, `string`>
|
||||||
|
|
||||||
|
* **options?**: `Partial`<[`ConnectNamespaceOptions`](../interfaces/ConnectNamespaceOptions.md)>
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
`Promise`<[`Connection`](../classes/Connection.md)>
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const db = await connectNamespace("my.custom.Namespace", {
|
||||||
|
endpoint: "...",
|
||||||
|
});
|
||||||
|
```
|
||||||
@@ -51,10 +51,17 @@
|
|||||||
- [ClientConfig](interfaces/ClientConfig.md)
|
- [ClientConfig](interfaces/ClientConfig.md)
|
||||||
- [ColumnAlteration](interfaces/ColumnAlteration.md)
|
- [ColumnAlteration](interfaces/ColumnAlteration.md)
|
||||||
- [CompactionStats](interfaces/CompactionStats.md)
|
- [CompactionStats](interfaces/CompactionStats.md)
|
||||||
|
- [ConnectNamespaceOptions](interfaces/ConnectNamespaceOptions.md)
|
||||||
- [ConnectionOptions](interfaces/ConnectionOptions.md)
|
- [ConnectionOptions](interfaces/ConnectionOptions.md)
|
||||||
|
- [CreateNamespaceOptions](interfaces/CreateNamespaceOptions.md)
|
||||||
|
- [CreateNamespaceResponse](interfaces/CreateNamespaceResponse.md)
|
||||||
- [CreateTableOptions](interfaces/CreateTableOptions.md)
|
- [CreateTableOptions](interfaces/CreateTableOptions.md)
|
||||||
- [DeleteResult](interfaces/DeleteResult.md)
|
- [DeleteResult](interfaces/DeleteResult.md)
|
||||||
|
- [DescribeNamespaceResponse](interfaces/DescribeNamespaceResponse.md)
|
||||||
|
- [DirNamespaceConfig](interfaces/DirNamespaceConfig.md)
|
||||||
- [DropColumnsResult](interfaces/DropColumnsResult.md)
|
- [DropColumnsResult](interfaces/DropColumnsResult.md)
|
||||||
|
- [DropNamespaceOptions](interfaces/DropNamespaceOptions.md)
|
||||||
|
- [DropNamespaceResponse](interfaces/DropNamespaceResponse.md)
|
||||||
- [ExecutableQuery](interfaces/ExecutableQuery.md)
|
- [ExecutableQuery](interfaces/ExecutableQuery.md)
|
||||||
- [FragmentStatistics](interfaces/FragmentStatistics.md)
|
- [FragmentStatistics](interfaces/FragmentStatistics.md)
|
||||||
- [FragmentSummaryStats](interfaces/FragmentSummaryStats.md)
|
- [FragmentSummaryStats](interfaces/FragmentSummaryStats.md)
|
||||||
@@ -69,12 +76,15 @@
|
|||||||
- [IvfFlatOptions](interfaces/IvfFlatOptions.md)
|
- [IvfFlatOptions](interfaces/IvfFlatOptions.md)
|
||||||
- [IvfPqOptions](interfaces/IvfPqOptions.md)
|
- [IvfPqOptions](interfaces/IvfPqOptions.md)
|
||||||
- [IvfRqOptions](interfaces/IvfRqOptions.md)
|
- [IvfRqOptions](interfaces/IvfRqOptions.md)
|
||||||
|
- [ListNamespacesOptions](interfaces/ListNamespacesOptions.md)
|
||||||
|
- [ListNamespacesResponse](interfaces/ListNamespacesResponse.md)
|
||||||
- [MergeResult](interfaces/MergeResult.md)
|
- [MergeResult](interfaces/MergeResult.md)
|
||||||
- [OpenTableOptions](interfaces/OpenTableOptions.md)
|
- [OpenTableOptions](interfaces/OpenTableOptions.md)
|
||||||
- [OptimizeOptions](interfaces/OptimizeOptions.md)
|
- [OptimizeOptions](interfaces/OptimizeOptions.md)
|
||||||
- [OptimizeStats](interfaces/OptimizeStats.md)
|
- [OptimizeStats](interfaces/OptimizeStats.md)
|
||||||
- [QueryExecutionOptions](interfaces/QueryExecutionOptions.md)
|
- [QueryExecutionOptions](interfaces/QueryExecutionOptions.md)
|
||||||
- [RemovalStats](interfaces/RemovalStats.md)
|
- [RemovalStats](interfaces/RemovalStats.md)
|
||||||
|
- [RestNamespaceConfig](interfaces/RestNamespaceConfig.md)
|
||||||
- [RetryConfig](interfaces/RetryConfig.md)
|
- [RetryConfig](interfaces/RetryConfig.md)
|
||||||
- [ShuffleOptions](interfaces/ShuffleOptions.md)
|
- [ShuffleOptions](interfaces/ShuffleOptions.md)
|
||||||
- [SplitCalculatedOptions](interfaces/SplitCalculatedOptions.md)
|
- [SplitCalculatedOptions](interfaces/SplitCalculatedOptions.md)
|
||||||
@@ -107,6 +117,7 @@
|
|||||||
|
|
||||||
- [RecordBatchIterator](functions/RecordBatchIterator.md)
|
- [RecordBatchIterator](functions/RecordBatchIterator.md)
|
||||||
- [connect](functions/connect.md)
|
- [connect](functions/connect.md)
|
||||||
|
- [connectNamespace](functions/connectNamespace.md)
|
||||||
- [makeArrowTable](functions/makeArrowTable.md)
|
- [makeArrowTable](functions/makeArrowTable.md)
|
||||||
- [packBits](functions/packBits.md)
|
- [packBits](functions/packBits.md)
|
||||||
- [permutationBuilder](functions/permutationBuilder.md)
|
- [permutationBuilder](functions/permutationBuilder.md)
|
||||||
|
|||||||
@@ -53,3 +53,18 @@ optional tlsConfig: TlsConfig;
|
|||||||
```ts
|
```ts
|
||||||
optional userAgent: string;
|
optional userAgent: string;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### userId?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional userId: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
User identifier for tracking purposes.
|
||||||
|
|
||||||
|
This is sent as the `x-lancedb-user-id` header in requests to LanceDB Cloud/Enterprise.
|
||||||
|
It can be set directly, or via the `LANCEDB_USER_ID` environment variable.
|
||||||
|
Alternatively, set `LANCEDB_USER_ID_ENV_KEY` to specify another environment
|
||||||
|
variable that contains the user ID value.
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / ConnectNamespaceOptions
|
||||||
|
|
||||||
|
# Interface: ConnectNamespaceOptions
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### namespaceClientProperties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional namespaceClientProperties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Extra properties for the backing namespace client.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### readConsistencyInterval?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional readConsistencyInterval: number;
|
||||||
|
```
|
||||||
|
|
||||||
|
The interval, in seconds, at which to check for updates to the table
|
||||||
|
from other processes. If None, then consistency is not checked. For
|
||||||
|
performance reasons, this is the default. For strong consistency, set
|
||||||
|
this to zero seconds. Then every read will check for updates from other
|
||||||
|
processes. As a compromise, you can set this to a non-zero value for
|
||||||
|
eventual consistency.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### session?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional session: Session;
|
||||||
|
```
|
||||||
|
|
||||||
|
The session to use for this connection. Holds shared caches and other
|
||||||
|
session-specific state.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### storageOptions?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional storageOptions: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration for object storage. The available options are described
|
||||||
|
at https://docs.lancedb.com/storage/
|
||||||
@@ -41,6 +41,29 @@ for testing purposes.
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
### manifestEnabled?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional manifestEnabled: boolean;
|
||||||
|
```
|
||||||
|
|
||||||
|
(For LanceDB OSS only): use directory namespace manifests as the source
|
||||||
|
of truth for table metadata. Existing directory-listed root tables are
|
||||||
|
migrated into the manifest on access.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### namespaceClientProperties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional namespaceClientProperties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
(For LanceDB OSS only): extra properties for the backing namespace
|
||||||
|
client used by manifest-enabled native connections.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
### readConsistencyInterval?
|
### readConsistencyInterval?
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@@ -89,4 +112,4 @@ optional storageOptions: Record<string, string>;
|
|||||||
|
|
||||||
(For LanceDB OSS only): configuration for object storage.
|
(For LanceDB OSS only): configuration for object storage.
|
||||||
|
|
||||||
The available options are described at https://lancedb.com/docs/storage/
|
The available options are described at https://docs.lancedb.com/storage/
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / CreateNamespaceOptions
|
||||||
|
|
||||||
|
# Interface: CreateNamespaceOptions
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### mode?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional mode: "overwrite" | "create" | "exist_ok";
|
||||||
|
```
|
||||||
|
|
||||||
|
Creation mode.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### properties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional properties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Properties to set on the new namespace.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / CreateNamespaceResponse
|
||||||
|
|
||||||
|
# Interface: CreateNamespaceResponse
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### properties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional properties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### transactionId?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional transactionId: string;
|
||||||
|
```
|
||||||
@@ -97,4 +97,4 @@ Configuration for object storage.
|
|||||||
Options already set on the connection will be inherited by the table,
|
Options already set on the connection will be inherited by the table,
|
||||||
but can be overridden here.
|
but can be overridden here.
|
||||||
|
|
||||||
The available options are described at https://lancedb.com/docs/storage/
|
The available options are described at https://docs.lancedb.com/storage/
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / DescribeNamespaceResponse
|
||||||
|
|
||||||
|
# Interface: DescribeNamespaceResponse
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### properties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional properties: Record<string, string>;
|
||||||
|
```
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / DirNamespaceConfig
|
||||||
|
|
||||||
|
# Interface: DirNamespaceConfig
|
||||||
|
|
||||||
|
Configuration for the built-in directory namespace (`"dir"`).
|
||||||
|
|
||||||
|
The directory namespace stores tables under a single root path (local
|
||||||
|
filesystem or object storage URI). See
|
||||||
|
[https://docs.lancedb.com/namespaces](https://docs.lancedb.com/namespaces) for the documented surface;
|
||||||
|
less-common knobs live under [DirNamespaceConfig.extraProperties](DirNamespaceConfig.md#extraproperties).
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### extraProperties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional extraProperties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional raw properties passed verbatim to the namespace
|
||||||
|
implementation (e.g. `storage.*`, `credential_vendor.*`). Typed
|
||||||
|
fields above take precedence on key collision.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### manifestEnabled?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional manifestEnabled: boolean;
|
||||||
|
```
|
||||||
|
|
||||||
|
Whether to maintain a namespace manifest at the root. Required for
|
||||||
|
child namespaces. Defaults to true on the impl side.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### root
|
||||||
|
|
||||||
|
```ts
|
||||||
|
root: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
Root path or URI containing the LanceDB tables.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / DropNamespaceOptions
|
||||||
|
|
||||||
|
# Interface: DropNamespaceOptions
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### behavior?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional behavior: "restrict" | "cascade";
|
||||||
|
```
|
||||||
|
|
||||||
|
Refuse to drop if non-empty (restrict) or drop recursively (cascade).
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### mode?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional mode: "fail" | "skip";
|
||||||
|
```
|
||||||
|
|
||||||
|
Whether to skip if the namespace doesn't exist, or fail.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / DropNamespaceResponse
|
||||||
|
|
||||||
|
# Interface: DropNamespaceResponse
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### properties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional properties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### transactionId?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional transactionId: string[];
|
||||||
|
```
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / ListNamespacesOptions
|
||||||
|
|
||||||
|
# Interface: ListNamespacesOptions
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### limit?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional limit: number;
|
||||||
|
```
|
||||||
|
|
||||||
|
An optional limit to the number of results to return.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### pageToken?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional pageToken: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
Token from a previous response for pagination.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / ListNamespacesResponse
|
||||||
|
|
||||||
|
# Interface: ListNamespacesResponse
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### namespaces
|
||||||
|
|
||||||
|
```ts
|
||||||
|
namespaces: string[];
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### pageToken?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional pageToken: string;
|
||||||
|
```
|
||||||
@@ -42,4 +42,4 @@ Configuration for object storage.
|
|||||||
Options already set on the connection will be inherited by the table,
|
Options already set on the connection will be inherited by the table,
|
||||||
but can be overridden here.
|
but can be overridden here.
|
||||||
|
|
||||||
The available options are described at https://lancedb.com/docs/storage/
|
The available options are described at https://docs.lancedb.com/storage/
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
[**@lancedb/lancedb**](../README.md) • **Docs**
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
[@lancedb/lancedb](../globals.md) / RestNamespaceConfig
|
||||||
|
|
||||||
|
# Interface: RestNamespaceConfig
|
||||||
|
|
||||||
|
Configuration for the built-in REST namespace (`"rest"`).
|
||||||
|
|
||||||
|
The REST namespace talks to a remote catalog server over HTTP. See
|
||||||
|
[https://docs.lancedb.com/namespaces](https://docs.lancedb.com/namespaces) for the documented surface;
|
||||||
|
less-common knobs (TLS, metrics) live under
|
||||||
|
[RestNamespaceConfig.extraProperties](RestNamespaceConfig.md#extraproperties).
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### extraProperties?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional extraProperties: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional raw properties passed verbatim to the namespace
|
||||||
|
implementation (e.g. `tls.*`, `ops_metrics_enabled`, `delimiter`).
|
||||||
|
Typed fields above take precedence on key collision.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### headers?
|
||||||
|
|
||||||
|
```ts
|
||||||
|
optional headers: Record<string, string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP headers forwarded with each request. Keys are passed through
|
||||||
|
as-is (e.g. `"x-api-key"`, `"Authorization"`).
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
### uri
|
||||||
|
|
||||||
|
```ts
|
||||||
|
uri: string;
|
||||||
|
```
|
||||||
|
|
||||||
|
Catalog endpoint URL.
|
||||||
@@ -94,11 +94,11 @@ of raw SQL strings with [where][lancedb.query.LanceQueryBuilder.where] and
|
|||||||
|
|
||||||
## Full text search
|
## Full text search
|
||||||
|
|
||||||
::: lancedb.fts.create_index
|
Use [lancedb.table.Table.create_fts_index][] for the synchronous API or
|
||||||
|
[lancedb.table.AsyncTable.create_index][] with [lancedb.index.FTS][] for the
|
||||||
|
asynchronous API.
|
||||||
|
|
||||||
::: lancedb.fts.populate_index
|
::: lancedb.index.FTS
|
||||||
|
|
||||||
::: lancedb.fts.search_index
|
|
||||||
|
|
||||||
## Utilities
|
## Utilities
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.27.2-beta.1</version>
|
<version>0.28.0-beta.11</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.27.2-beta.1</version>
|
<version>0.28.0-beta.11</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-core.version>3.0.1</lance-core.version>
|
<lance-core.version>7.0.0-beta.7</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>
|
||||||
|
|||||||
+6
-6
@@ -3,11 +3,11 @@ The core Rust library is in the `../rust/lancedb` directory, the rust binding
|
|||||||
code is in the `src/` directory and the typescript bindings are in
|
code is in the `src/` directory and the typescript bindings are in
|
||||||
the `lancedb/` directory.
|
the `lancedb/` directory.
|
||||||
|
|
||||||
Whenever you change the Rust code, you will need to recompile: `npm run build`.
|
Whenever you change the Rust code, you will need to recompile: `pnpm build`.
|
||||||
|
|
||||||
Common commands:
|
Common commands:
|
||||||
* Build: `npm run build`
|
* Build: `pnpm build`
|
||||||
* Lint: `npm run lint`
|
* Lint: `pnpm lint`
|
||||||
* Fix lints: `npm run lint-fix`
|
* Fix lints: `pnpm lint-fix`
|
||||||
* Test: `npm test`
|
* Test: `pnpm test`
|
||||||
* Run single test file: `npm test __test__/arrow.test.ts`
|
* Run single test file: `pnpm test __test__/arrow.test.ts`
|
||||||
|
|||||||
+15
-13
@@ -12,20 +12,22 @@ Typescript.
|
|||||||
* `src/`: Rust bindings source code
|
* `src/`: Rust bindings source code
|
||||||
* `lancedb/`: Typescript package source code
|
* `lancedb/`: Typescript package source code
|
||||||
* `__test__/`: Unit tests
|
* `__test__/`: Unit tests
|
||||||
* `examples/`: An npm package with the examples shown in the documentation
|
* `examples/`: A pnpm package with the examples shown in the documentation
|
||||||
|
|
||||||
## Development environment
|
## Development environment
|
||||||
|
|
||||||
To set up your development environment, you will need to install the following:
|
To set up your development environment, you will need to install the following:
|
||||||
|
|
||||||
1. Node.js 14 or later
|
1. Node.js 22 or later (required by pnpm 11)
|
||||||
2. Rust's package manager, Cargo. Use [rustup](https://rustup.rs/) to install.
|
2. [pnpm](https://pnpm.io/installation) 11 or later (or run via `corepack enable`,
|
||||||
3. [protoc](https://grpc.io/docs/protoc-installation/) (Protocol Buffers compiler)
|
which uses the `packageManager` field in `package.json`)
|
||||||
|
3. Rust's package manager, Cargo. Use [rustup](https://rustup.rs/) to install.
|
||||||
|
4. [protoc](https://grpc.io/docs/protoc-installation/) (Protocol Buffers compiler)
|
||||||
|
|
||||||
Initial setup:
|
Initial setup:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Commit Hooks
|
### Commit Hooks
|
||||||
@@ -39,38 +41,38 @@ pre-commit install
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Most common development commands can be run using the npm scripts.
|
Most common development commands can be run using the pnpm scripts.
|
||||||
|
|
||||||
Build the package
|
Build the package
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install
|
pnpm install
|
||||||
npm run build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
Lint:
|
Lint:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm run lint
|
pnpm lint
|
||||||
```
|
```
|
||||||
|
|
||||||
Format and fix lints:
|
Format and fix lints:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm run lint-fix
|
pnpm lint-fix
|
||||||
```
|
```
|
||||||
|
|
||||||
Run tests:
|
Run tests:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm test
|
pnpm test
|
||||||
```
|
```
|
||||||
|
|
||||||
To run a single test:
|
To run a single test:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# Single file: table.test.ts
|
# Single file: table.test.ts
|
||||||
npm test -- table.test.ts
|
pnpm test -- table.test.ts
|
||||||
# Single test: 'merge insert' in table.test.ts
|
# Single test: 'merge insert' in table.test.ts
|
||||||
npm test -- table.test.ts --testNamePattern=merge\ insert
|
pnpm test -- table.test.ts --testNamePattern=merge\ insert
|
||||||
```
|
```
|
||||||
|
|||||||
+6
-4
@@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-nodejs"
|
name = "lancedb-nodejs"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
version = "0.27.2-beta.1"
|
version = "0.28.0-beta.11"
|
||||||
|
publish = false
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -15,12 +16,13 @@ crate-type = ["cdylib"]
|
|||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
arrow-ipc.workspace = true
|
arrow-ipc.workspace = true
|
||||||
arrow-array.workspace = true
|
arrow-array.workspace = true
|
||||||
arrow-buffer = "57.2"
|
arrow-buffer = "58.0.0"
|
||||||
half.workspace = true
|
half.workspace = true
|
||||||
arrow-schema.workspace = true
|
arrow-schema.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
lancedb = { path = "../rust/lancedb", default-features = false }
|
lancedb = { path = "../rust/lancedb", default-features = false }
|
||||||
|
lance-namespace.workspace = true
|
||||||
napi = { version = "3.8.3", default-features = false, features = [
|
napi = { version = "3.8.3", default-features = false, features = [
|
||||||
"napi9",
|
"napi9",
|
||||||
"async"
|
"async"
|
||||||
@@ -31,8 +33,8 @@ lzma-sys = { version = "0.1", features = ["static"] }
|
|||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
|
||||||
# Pin to resolve build failures; update periodically for security patches.
|
# Pin to resolve build failures; update periodically for security patches.
|
||||||
aws-lc-sys = "=0.38.0"
|
aws-lc-sys = "=0.40.0"
|
||||||
aws-lc-rs = "=1.16.1"
|
aws-lc-rs = "=1.16.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
napi-build = "2.3.1"
|
napi-build = "2.3.1"
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ const results = await table.vectorSearch([0.1, 0.3]).limit(20).toArray();
|
|||||||
console.log(results);
|
console.log(results);
|
||||||
```
|
```
|
||||||
|
|
||||||
The [quickstart](https://lancedb.com/docs/quickstart/basic-usage/) contains more complete examples.
|
The [quickstart](https://docs.lancedb.com/quickstart/) contains more complete examples.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import { readdirSync } from "fs";
|
import { readdirSync } from "fs";
|
||||||
import { Field, Float64, Schema } from "apache-arrow";
|
import { Field, Float64, Schema } from "apache-arrow";
|
||||||
import * as tmp from "tmp";
|
import * as tmp from "tmp";
|
||||||
import { Connection, Table, connect } from "../lancedb";
|
import { Connection, Table, connect, connectNamespace } from "../lancedb";
|
||||||
import { LocalTable } from "../lancedb/table";
|
import { LocalTable } from "../lancedb/table";
|
||||||
|
|
||||||
describe("when connecting", () => {
|
describe("when connecting", () => {
|
||||||
@@ -306,3 +306,186 @@ describe("clone table functionality", () => {
|
|||||||
).rejects.toThrow("Deep clone is not yet implemented");
|
).rejects.toThrow("Deep clone is not yet implemented");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("namespaces", () => {
|
||||||
|
let tmpDir: tmp.DirResult;
|
||||||
|
let db: Connection;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||||
|
// The local DirectoryNamespace backend only supports child namespaces
|
||||||
|
// when manifest mode is enabled (see lance-namespace-impls/src/dir.rs).
|
||||||
|
db = await connect(tmpDir.name, {
|
||||||
|
// biome-ignore lint/style/useNamingConvention: opaque backend property key, must match Rust
|
||||||
|
namespaceClientProperties: { manifest_enabled: "true" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterEach(() => tmpDir.removeCallback());
|
||||||
|
|
||||||
|
it("should create and describe a namespace", async () => {
|
||||||
|
await db.createNamespace(["myns"]);
|
||||||
|
const desc = await db.describeNamespace(["myns"]);
|
||||||
|
expect(desc).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should list namespaces created at the root", async () => {
|
||||||
|
await db.createNamespace(["alpha"]);
|
||||||
|
await db.createNamespace(["beta"]);
|
||||||
|
const list = await db.listNamespaces();
|
||||||
|
expect(list.namespaces).toEqual(expect.arrayContaining(["alpha", "beta"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should list child namespaces under a parent", async () => {
|
||||||
|
await db.createNamespace(["parent"]);
|
||||||
|
await db.createNamespace(["parent", "child"]);
|
||||||
|
const list = await db.listNamespaces(["parent"]);
|
||||||
|
expect(list.namespaces).toContain("child");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should drop a namespace", async () => {
|
||||||
|
await db.createNamespace(["ephemeral"]);
|
||||||
|
await db.dropNamespace(["ephemeral"]);
|
||||||
|
const list = await db.listNamespaces();
|
||||||
|
expect(list.namespaces).not.toContain("ephemeral");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should raise an error on any namespace op after close", async () => {
|
||||||
|
await db.close();
|
||||||
|
await expect(db.describeNamespace(["foo"])).rejects.toThrow(
|
||||||
|
"Connection is closed",
|
||||||
|
);
|
||||||
|
await expect(db.listNamespaces()).rejects.toThrow("Connection is closed");
|
||||||
|
await expect(db.createNamespace(["foo"])).rejects.toThrow(
|
||||||
|
"Connection is closed",
|
||||||
|
);
|
||||||
|
await expect(db.dropNamespace(["foo"])).rejects.toThrow(
|
||||||
|
"Connection is closed",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should raise an understandable error when describing a non-existent namespace", async () => {
|
||||||
|
await expect(db.describeNamespace(["does-not-exist"])).rejects.toThrow(
|
||||||
|
/not found/i,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should raise an error when creating a namespace that already exists", async () => {
|
||||||
|
await db.createNamespace(["dup"]);
|
||||||
|
await expect(db.createNamespace(["dup"])).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reject an unrecognized createNamespace mode with a clear error", async () => {
|
||||||
|
await expect(
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: deliberately bypass TS to test runtime validation
|
||||||
|
db.createNamespace(["x"], { mode: "frobnicate" as any }),
|
||||||
|
).rejects.toThrow(/Invalid mode 'frobnicate'/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reject an unrecognized dropNamespace mode with a clear error", async () => {
|
||||||
|
await db.createNamespace(["x"]);
|
||||||
|
await expect(
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: deliberately bypass TS to test runtime validation
|
||||||
|
db.dropNamespace(["x"], { mode: "frobnicate" as any }),
|
||||||
|
).rejects.toThrow(/Invalid mode 'frobnicate'/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reject an unrecognized dropNamespace behavior with a clear error", async () => {
|
||||||
|
await db.createNamespace(["x"]);
|
||||||
|
await expect(
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: deliberately bypass TS to test runtime validation
|
||||||
|
db.dropNamespace(["x"], { behavior: "frobnicate" as any }),
|
||||||
|
).rejects.toThrow(/Invalid behavior 'frobnicate'/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("connectNamespace", () => {
|
||||||
|
let tmpDir: tmp.DirResult;
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
||||||
|
});
|
||||||
|
afterEach(() => tmpDir.removeCallback());
|
||||||
|
|
||||||
|
it("connects via the dir implementation and supports table ops", async () => {
|
||||||
|
const db = await connectNamespace("dir", { root: tmpDir.name });
|
||||||
|
await db.createTable("users", [{ id: 1 }, { id: 2 }]);
|
||||||
|
await expect(db.tableNames()).resolves.toContain("users");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws a clear error when implName is empty", async () => {
|
||||||
|
await expect(connectNamespace("", {})).rejects.toThrow(
|
||||||
|
"implName must be a non-empty string",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws when the namespace implementation is unknown", async () => {
|
||||||
|
await expect(connectNamespace("not-a-real-impl", {})).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes storage options through to the namespace", async () => {
|
||||||
|
const db = await connectNamespace(
|
||||||
|
"dir",
|
||||||
|
{ root: tmpDir.name },
|
||||||
|
{ storageOptions: { newTableDataStorageVersion: "stable" } },
|
||||||
|
);
|
||||||
|
await db.createTable("plumbing", [{ id: 1 }]);
|
||||||
|
await expect(db.tableNames()).resolves.toContain("plumbing");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports child namespaces when manifestEnabled is true on the dir config", async () => {
|
||||||
|
const writer = await connectNamespace("dir", {
|
||||||
|
root: tmpDir.name,
|
||||||
|
manifestEnabled: true,
|
||||||
|
});
|
||||||
|
await writer.createNamespace(["analytics"]);
|
||||||
|
await writer.createTable("orders", [{ id: 1 }, { id: 2 }], ["analytics"]);
|
||||||
|
await writer.close();
|
||||||
|
|
||||||
|
const reader = await connectNamespace("dir", {
|
||||||
|
root: tmpDir.name,
|
||||||
|
manifestEnabled: true,
|
||||||
|
});
|
||||||
|
await expect(reader.tableNames(["analytics"])).resolves.toContain("orders");
|
||||||
|
const orders = await reader.openTable("orders", ["analytics"]);
|
||||||
|
await expect(orders.countRows()).resolves.toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("merges extraProperties into the dir config and is overridden by typed fields", async () => {
|
||||||
|
// Two observable assertions:
|
||||||
|
// - Typed `root` overrides extraProperties.root: createTable would fail
|
||||||
|
// under the bogus path if the override didn't happen.
|
||||||
|
// - extraProperties.manifest_enabled="false" is honored end-to-end. Child
|
||||||
|
// namespaces require manifest mode (default true), so explicitly
|
||||||
|
// disabling it via extraProperties must make createNamespace reject. If
|
||||||
|
// extraProperties pass-through were silently broken, the default would
|
||||||
|
// let createNamespace succeed.
|
||||||
|
const db = await connectNamespace("dir", {
|
||||||
|
root: tmpDir.name,
|
||||||
|
extraProperties: {
|
||||||
|
root: "/should/be/overridden",
|
||||||
|
// biome-ignore lint/style/useNamingConvention: backend property key
|
||||||
|
manifest_enabled: "false",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await db.createTable("base", [{ id: 1 }]);
|
||||||
|
await expect(db.tableNames()).resolves.toContain("base");
|
||||||
|
await expect(db.createNamespace(["analytics"])).rejects.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("flows unknown top-level keys through when implName is dynamic (no silent drop)", async () => {
|
||||||
|
// Routes via the third overload because `impl` is `string`, not the
|
||||||
|
// literal `"dir"`. The dispatcher still notices the runtime value is
|
||||||
|
// "dir", but unknown keys like `manifest_enabled` must not be silently
|
||||||
|
// dropped during the conversion.
|
||||||
|
//
|
||||||
|
// Asserting a *negative* outcome (manifest disabled -> createNamespace
|
||||||
|
// rejects) is required for observability, since the backend default for
|
||||||
|
// `manifest_enabled` is true.
|
||||||
|
const impl: string = "dir";
|
||||||
|
const db = await connectNamespace(impl, {
|
||||||
|
root: tmpDir.name,
|
||||||
|
// biome-ignore lint/style/useNamingConvention: backend property key
|
||||||
|
manifest_enabled: "false",
|
||||||
|
});
|
||||||
|
await expect(db.createNamespace(["mixed"])).rejects.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
|
import * as path from "node:path";
|
||||||
import { RecordBatch } from "apache-arrow";
|
import { RecordBatch } from "apache-arrow";
|
||||||
import * as tmp from "tmp";
|
import * as tmp from "tmp";
|
||||||
import { Connection, Index, Table, connect, makeArrowTable } from "../lancedb";
|
import { Connection, Index, Table, connect, makeArrowTable } from "../lancedb";
|
||||||
@@ -76,4 +78,91 @@ describe("rerankers", function () {
|
|||||||
|
|
||||||
expect(result).toHaveLength(2);
|
expect(result).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not keep process alive after rerank query", async function () {
|
||||||
|
const script = `
|
||||||
|
import * as lancedb from "./dist/index.js";
|
||||||
|
import * as os from "node:os";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import * as fs from "node:fs/promises";
|
||||||
|
|
||||||
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "lancedb-rerank-exit-"));
|
||||||
|
const db = await lancedb.connect(dir);
|
||||||
|
const table = await db.createTable("test", [{ text: "hello", vector: [1, 2, 3] }], {
|
||||||
|
mode: "overwrite",
|
||||||
|
});
|
||||||
|
await table.createIndex("text", { config: lancedb.Index.fts() });
|
||||||
|
await table.waitForIndex(["text_idx"], 30);
|
||||||
|
|
||||||
|
const reranker = await lancedb.rerankers.RRFReranker.create();
|
||||||
|
await table
|
||||||
|
.query()
|
||||||
|
.nearestTo([1, 2, 3])
|
||||||
|
.fullTextSearch("hello")
|
||||||
|
.rerank(reranker)
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
table.close();
|
||||||
|
db.close();
|
||||||
|
`;
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
const child = spawn(
|
||||||
|
process.execPath,
|
||||||
|
["--input-type=module", "-e", script],
|
||||||
|
{
|
||||||
|
cwd: path.resolve(__dirname, ".."),
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let stdout = "";
|
||||||
|
let stderr = "";
|
||||||
|
|
||||||
|
child.stdout.on("data", (chunk) => {
|
||||||
|
stdout += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on("data", (chunk) => {
|
||||||
|
stderr += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
child.kill();
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`child process did not exit in time\nstdout:\n${stdout}\nstderr:\n${stderr}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}, 20_000);
|
||||||
|
|
||||||
|
child.on("error", (err) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("exit", (code, signal) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
if (signal !== null) {
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`child process exited with signal ${signal}\nstdout:\n${stdout}\nstderr:\n${stderr}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code !== 0) {
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`child process exited with code ${code}\nstdout:\n${stdout}\nstderr:\n${stderr}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
|
|||||||
},
|
},
|
||||||
numIndices: 0,
|
numIndices: 0,
|
||||||
numRows: 3,
|
numRows: 3,
|
||||||
totalBytes: 24,
|
totalBytes: 44,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,6 +115,12 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
|
|||||||
await expect(table.countRows()).resolves.toBe(1);
|
await expect(table.countRows()).resolves.toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should accept skipAutoCleanup on add()", async () => {
|
||||||
|
await table.add([{ id: 1 }], { skipAutoCleanup: true });
|
||||||
|
await table.add([{ id: 2 }], { skipAutoCleanup: true });
|
||||||
|
await expect(table.countRows()).resolves.toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
it("should let me close the table", async () => {
|
it("should let me close the table", async () => {
|
||||||
expect(table.isOpen()).toBe(true);
|
expect(table.isOpen()).toBe(true);
|
||||||
table.close();
|
table.close();
|
||||||
@@ -1870,6 +1876,25 @@ describe.each([arrow15, arrow16, arrow17, arrow18])(
|
|||||||
expect(results.length).toBe(3);
|
expect(results.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("prewarmData errors on local tables", async () => {
|
||||||
|
const db = await connect(tmpDir.name);
|
||||||
|
const data = [
|
||||||
|
{ text: "alpha", vector: [0.1, 0.2, 0.3] },
|
||||||
|
{ text: "beta", vector: [0.4, 0.5, 0.6] },
|
||||||
|
];
|
||||||
|
const table = await db.createTable("prewarm_data_test", data);
|
||||||
|
|
||||||
|
// prewarmData is only supported on remote tables. We verify the call
|
||||||
|
// is wired through napi and surfaces the expected error for both
|
||||||
|
// arg shapes (undefined and string[]).
|
||||||
|
await expect(table.prewarmData()).rejects.toThrow(
|
||||||
|
"prewarm_data is currently only supported on remote tables",
|
||||||
|
);
|
||||||
|
await expect(table.prewarmData(["text"])).rejects.toThrow(
|
||||||
|
"prewarm_data is currently only supported on remote tables",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("full text index on list", async () => {
|
test("full text index on list", async () => {
|
||||||
const db = await connect(tmpDir.name);
|
const db = await connect(tmpDir.name);
|
||||||
const data = [
|
const data = [
|
||||||
|
|||||||
Generated
-4810
File diff suppressed because it is too large
Load Diff
@@ -11,16 +11,17 @@
|
|||||||
"test": "node --experimental-vm-modules node_modules/.bin/jest --testEnvironment jest-environment-node-single-context --verbose",
|
"test": "node --experimental-vm-modules node_modules/.bin/jest --testEnvironment jest-environment-node-single-context --verbose",
|
||||||
"lint": "biome check *.ts && biome format *.ts",
|
"lint": "biome check *.ts && biome format *.ts",
|
||||||
"lint-ci": "biome ci .",
|
"lint-ci": "biome ci .",
|
||||||
"lint-fix": "biome check --write *.ts && npm run format",
|
"lint-fix": "biome check --write *.ts && pnpm format",
|
||||||
"format": "biome format --write *.ts"
|
"format": "biome format --write *.ts"
|
||||||
},
|
},
|
||||||
"author": "Lance Devs",
|
"author": "Lance Devs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"packageManager": "pnpm@11.1.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@huggingface/transformers": "^3.0.2",
|
"@huggingface/transformers": "3.0.2",
|
||||||
"@lancedb/lancedb": "file:../dist",
|
"@lancedb/lancedb": "file:../dist",
|
||||||
"openai": "^4.29.2",
|
"openai": "4.29.2",
|
||||||
"sharp": "^0.33.5"
|
"sharp": "0.33.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.7.3",
|
"@biomejs/biome": "^1.7.3",
|
||||||
|
|||||||
Generated
+3466
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
# Block resolution of versions less than 24h old (Shai-Hulud window).
|
||||||
|
# This is the pnpm 11 default but pinned here so it's visible to
|
||||||
|
# reviewers and survives a future pnpm major flipping the default.
|
||||||
|
minimumReleaseAge: 1440
|
||||||
|
|
||||||
|
# Fail install if a transitive dep tries to run an unapproved script.
|
||||||
|
strictDepBuilds: true
|
||||||
|
|
||||||
|
allowBuilds:
|
||||||
|
'@biomejs/biome': true
|
||||||
|
onnxruntime-node: true
|
||||||
|
protobufjs: true
|
||||||
|
sharp: true
|
||||||
+197
-61
@@ -16,6 +16,18 @@ import {
|
|||||||
} from "./arrow";
|
} from "./arrow";
|
||||||
import { EmbeddingFunctionConfig, getRegistry } from "./embedding/registry";
|
import { EmbeddingFunctionConfig, getRegistry } from "./embedding/registry";
|
||||||
import { Connection as LanceDbConnection } from "./native";
|
import { Connection as LanceDbConnection } from "./native";
|
||||||
|
import type {
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
ListNamespacesResponse,
|
||||||
|
} from "./native";
|
||||||
|
export type {
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
ListNamespacesResponse,
|
||||||
|
};
|
||||||
import { sanitizeTable } from "./sanitize";
|
import { sanitizeTable } from "./sanitize";
|
||||||
import { LocalTable, Table } from "./table";
|
import { LocalTable, Table } from "./table";
|
||||||
|
|
||||||
@@ -42,7 +54,7 @@ export interface CreateTableOptions {
|
|||||||
* Options already set on the connection will be inherited by the table,
|
* Options already set on the connection will be inherited by the table,
|
||||||
* but can be overridden here.
|
* but can be overridden here.
|
||||||
*
|
*
|
||||||
* The available options are described at https://lancedb.com/docs/storage/
|
* The available options are described at https://docs.lancedb.com/storage/
|
||||||
*/
|
*/
|
||||||
storageOptions?: Record<string, string>;
|
storageOptions?: Record<string, string>;
|
||||||
|
|
||||||
@@ -78,7 +90,7 @@ export interface OpenTableOptions {
|
|||||||
* Options already set on the connection will be inherited by the table,
|
* Options already set on the connection will be inherited by the table,
|
||||||
* but can be overridden here.
|
* but can be overridden here.
|
||||||
*
|
*
|
||||||
* The available options are described at https://lancedb.com/docs/storage/
|
* The available options are described at https://docs.lancedb.com/storage/
|
||||||
*/
|
*/
|
||||||
storageOptions?: Record<string, string>;
|
storageOptions?: Record<string, string>;
|
||||||
/**
|
/**
|
||||||
@@ -110,6 +122,28 @@ export interface TableNamesOptions {
|
|||||||
/** An optional limit to the number of results to return. */
|
/** An optional limit to the number of results to return. */
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ListNamespacesOptions {
|
||||||
|
/** Token from a previous response for pagination. */
|
||||||
|
pageToken?: string;
|
||||||
|
/** An optional limit to the number of results to return. */
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateNamespaceOptions {
|
||||||
|
/** Creation mode. */
|
||||||
|
mode?: "create" | "exist_ok" | "overwrite";
|
||||||
|
/** Properties to set on the new namespace. */
|
||||||
|
properties?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DropNamespaceOptions {
|
||||||
|
/** Whether to skip if the namespace doesn't exist, or fail. */
|
||||||
|
mode?: "skip" | "fail";
|
||||||
|
/** Refuse to drop if non-empty (restrict) or drop recursively (cascade). */
|
||||||
|
behavior?: "restrict" | "cascade";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A LanceDB Connection that allows you to open tables and create new ones.
|
* A LanceDB Connection that allows you to open tables and create new ones.
|
||||||
*
|
*
|
||||||
@@ -166,25 +200,25 @@ export abstract class Connection {
|
|||||||
* List all the table names in this database.
|
* List all the table names in this database.
|
||||||
*
|
*
|
||||||
* Tables will be returned in lexicographical order.
|
* Tables will be returned in lexicographical order.
|
||||||
* @param {string[]} namespace - The namespace to list tables from (defaults to root namespace)
|
* @param {string[]} namespacePath - The namespace path to list tables from (defaults to root namespace)
|
||||||
* @param {Partial<TableNamesOptions>} options - options to control the
|
* @param {Partial<TableNamesOptions>} options - options to control the
|
||||||
* paging / start point
|
* paging / start point
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
abstract tableNames(
|
abstract tableNames(
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
options?: Partial<TableNamesOptions>,
|
options?: Partial<TableNamesOptions>,
|
||||||
): Promise<string[]>;
|
): Promise<string[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a table in the database.
|
* Open a table in the database.
|
||||||
* @param {string} name - The name of the table
|
* @param {string} name - The name of the table
|
||||||
* @param {string[]} namespace - The namespace of the table (defaults to root namespace)
|
* @param {string[]} namespacePath - The namespace path of the table (defaults to root namespace)
|
||||||
* @param {Partial<OpenTableOptions>} options - Additional options
|
* @param {Partial<OpenTableOptions>} options - Additional options
|
||||||
*/
|
*/
|
||||||
abstract openTable(
|
abstract openTable(
|
||||||
name: string,
|
name: string,
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
options?: Partial<OpenTableOptions>,
|
options?: Partial<OpenTableOptions>,
|
||||||
): Promise<Table>;
|
): Promise<Table>;
|
||||||
|
|
||||||
@@ -193,7 +227,7 @@ export abstract class Connection {
|
|||||||
* @param {object} options - The options object.
|
* @param {object} options - The options object.
|
||||||
* @param {string} options.name - The name of the table.
|
* @param {string} options.name - The name of the table.
|
||||||
* @param {Data} options.data - Non-empty Array of Records to be inserted into the table
|
* @param {Data} options.data - Non-empty Array of Records to be inserted into the table
|
||||||
* @param {string[]} namespace - The namespace to create the table in (defaults to root namespace)
|
* @param {string[]} namespacePath - The namespace path to create the table in (defaults to root namespace)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
abstract createTable(
|
abstract createTable(
|
||||||
@@ -201,7 +235,7 @@ export abstract class Connection {
|
|||||||
name: string;
|
name: string;
|
||||||
data: Data;
|
data: Data;
|
||||||
} & Partial<CreateTableOptions>,
|
} & Partial<CreateTableOptions>,
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
): Promise<Table>;
|
): Promise<Table>;
|
||||||
/**
|
/**
|
||||||
* Creates a new Table and initialize it with new data.
|
* Creates a new Table and initialize it with new data.
|
||||||
@@ -220,13 +254,13 @@ export abstract class Connection {
|
|||||||
* @param {string} name - The name of the table.
|
* @param {string} name - The name of the table.
|
||||||
* @param {Record<string, unknown>[] | TableLike} data - Non-empty Array of Records
|
* @param {Record<string, unknown>[] | TableLike} data - Non-empty Array of Records
|
||||||
* to be inserted into the table
|
* to be inserted into the table
|
||||||
* @param {string[]} namespace - The namespace to create the table in (defaults to root namespace)
|
* @param {string[]} namespacePath - The namespace path to create the table in (defaults to root namespace)
|
||||||
* @param {Partial<CreateTableOptions>} options - Additional options
|
* @param {Partial<CreateTableOptions>} options - Additional options
|
||||||
*/
|
*/
|
||||||
abstract createTable(
|
abstract createTable(
|
||||||
name: string,
|
name: string,
|
||||||
data: Record<string, unknown>[] | TableLike,
|
data: Record<string, unknown>[] | TableLike,
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
options?: Partial<CreateTableOptions>,
|
options?: Partial<CreateTableOptions>,
|
||||||
): Promise<Table>;
|
): Promise<Table>;
|
||||||
|
|
||||||
@@ -245,28 +279,91 @@ export abstract class Connection {
|
|||||||
* Creates a new empty Table
|
* Creates a new empty Table
|
||||||
* @param {string} name - The name of the table.
|
* @param {string} name - The name of the table.
|
||||||
* @param {Schema} schema - The schema of the table
|
* @param {Schema} schema - The schema of the table
|
||||||
* @param {string[]} namespace - The namespace to create the table in (defaults to root namespace)
|
* @param {string[]} namespacePath - The namespace path to create the table in (defaults to root namespace)
|
||||||
* @param {Partial<CreateTableOptions>} options - Additional options
|
* @param {Partial<CreateTableOptions>} options - Additional options
|
||||||
*/
|
*/
|
||||||
abstract createEmptyTable(
|
abstract createEmptyTable(
|
||||||
name: string,
|
name: string,
|
||||||
schema: import("./arrow").SchemaLike,
|
schema: import("./arrow").SchemaLike,
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
options?: Partial<CreateTableOptions>,
|
options?: Partial<CreateTableOptions>,
|
||||||
): Promise<Table>;
|
): Promise<Table>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop an existing table.
|
* Drop an existing table.
|
||||||
* @param {string} name The name of the table to drop.
|
* @param {string} name The name of the table to drop.
|
||||||
* @param {string[]} namespace The namespace of the table (defaults to root namespace).
|
* @param {string[]} namespacePath The namespace path of the table (defaults to root namespace).
|
||||||
*/
|
*/
|
||||||
abstract dropTable(name: string, namespace?: string[]): Promise<void>;
|
abstract dropTable(name: string, namespacePath?: string[]): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop all tables in the database.
|
* Drop all tables in the database.
|
||||||
* @param {string[]} namespace The namespace to drop tables from (defaults to root namespace).
|
* @param {string[]} namespacePath The namespace path to drop tables from (defaults to root namespace).
|
||||||
*/
|
*/
|
||||||
abstract dropAllTables(namespace?: string[]): Promise<void>;
|
abstract dropAllTables(namespacePath?: string[]): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describe a namespace, returning its properties.
|
||||||
|
*
|
||||||
|
* @param {string[]} namespacePath - The namespace path to describe, in
|
||||||
|
* parent → child order, e.g. `["analytics", "sales"]`.
|
||||||
|
* @returns {Promise<DescribeNamespaceResponse>} The namespace's properties
|
||||||
|
* (may be undefined if the namespace has none).
|
||||||
|
*/
|
||||||
|
abstract describeNamespace(
|
||||||
|
namespacePath: string[],
|
||||||
|
): Promise<DescribeNamespaceResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the immediate child namespaces under the given parent.
|
||||||
|
*
|
||||||
|
* Results may be paginated. To retrieve subsequent pages, pass the
|
||||||
|
* `pageToken` returned by a previous call.
|
||||||
|
*
|
||||||
|
* @param {string[]} namespacePath - The parent namespace path. Defaults
|
||||||
|
* to the root namespace if omitted.
|
||||||
|
* @param {Partial<ListNamespacesOptions>} options - Pagination options
|
||||||
|
* (`pageToken`, `limit`).
|
||||||
|
* @returns {Promise<ListNamespacesResponse>} Child namespace names and
|
||||||
|
* an optional token for fetching the next page.
|
||||||
|
*/
|
||||||
|
abstract listNamespaces(
|
||||||
|
namespacePath?: string[],
|
||||||
|
options?: Partial<ListNamespacesOptions>,
|
||||||
|
): Promise<ListNamespacesResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new namespace at the given path.
|
||||||
|
*
|
||||||
|
* @param {string[]} namespacePath - The namespace path to create.
|
||||||
|
* @param {Partial<CreateNamespaceOptions>} options - Creation `mode`
|
||||||
|
* ("create" | "exist_ok" | "overwrite") and optional `properties`
|
||||||
|
* to attach to the namespace.
|
||||||
|
* @returns {Promise<CreateNamespaceResponse>} The properties of the
|
||||||
|
* created namespace and an optional transaction id.
|
||||||
|
*/
|
||||||
|
abstract createNamespace(
|
||||||
|
namespacePath: string[],
|
||||||
|
options?: Partial<CreateNamespaceOptions>,
|
||||||
|
): Promise<CreateNamespaceResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop a namespace.
|
||||||
|
*
|
||||||
|
* Use `behavior: "cascade"` to also drop everything contained in the
|
||||||
|
* namespace (sub-namespaces and tables). The default `"restrict"`
|
||||||
|
* behavior refuses to drop a non-empty namespace.
|
||||||
|
*
|
||||||
|
* @param {string[]} namespacePath - The namespace path to drop.
|
||||||
|
* @param {Partial<DropNamespaceOptions>} options - `mode` ("skip" | "fail"
|
||||||
|
* for missing-namespace handling) and `behavior` ("restrict" | "cascade").
|
||||||
|
* @returns {Promise<DropNamespaceResponse>} Any properties returned by
|
||||||
|
* the server and an optional transaction id.
|
||||||
|
*/
|
||||||
|
abstract dropNamespace(
|
||||||
|
namespacePath: string[],
|
||||||
|
options?: Partial<DropNamespaceOptions>,
|
||||||
|
): Promise<DropNamespaceResponse>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clone a table from a source table.
|
* Clone a table from a source table.
|
||||||
@@ -279,7 +376,7 @@ export abstract class Connection {
|
|||||||
* @param {string} targetTableName - The name of the target table to create.
|
* @param {string} targetTableName - The name of the target table to create.
|
||||||
* @param {string} sourceUri - The URI of the source table to clone from.
|
* @param {string} sourceUri - The URI of the source table to clone from.
|
||||||
* @param {object} options - Clone options.
|
* @param {object} options - Clone options.
|
||||||
* @param {string[]} options.targetNamespace - The namespace for the target table (defaults to root namespace).
|
* @param {string[]} options.targetNamespacePath - The namespace path for the target table (defaults to root namespace).
|
||||||
* @param {number} options.sourceVersion - The version of the source table to clone.
|
* @param {number} options.sourceVersion - The version of the source table to clone.
|
||||||
* @param {string} options.sourceTag - The tag of the source table to clone.
|
* @param {string} options.sourceTag - The tag of the source table to clone.
|
||||||
* @param {boolean} options.isShallow - Whether to perform a shallow clone (defaults to true).
|
* @param {boolean} options.isShallow - Whether to perform a shallow clone (defaults to true).
|
||||||
@@ -288,7 +385,7 @@ export abstract class Connection {
|
|||||||
targetTableName: string,
|
targetTableName: string,
|
||||||
sourceUri: string,
|
sourceUri: string,
|
||||||
options?: {
|
options?: {
|
||||||
targetNamespace?: string[];
|
targetNamespacePath?: string[];
|
||||||
sourceVersion?: number;
|
sourceVersion?: number;
|
||||||
sourceTag?: string;
|
sourceTag?: string;
|
||||||
isShallow?: boolean;
|
isShallow?: boolean;
|
||||||
@@ -319,25 +416,25 @@ export class LocalConnection extends Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async tableNames(
|
async tableNames(
|
||||||
namespaceOrOptions?: string[] | Partial<TableNamesOptions>,
|
namespacePathOrOptions?: string[] | Partial<TableNamesOptions>,
|
||||||
options?: Partial<TableNamesOptions>,
|
options?: Partial<TableNamesOptions>,
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
// Detect if first argument is namespace array or options object
|
// Detect if first argument is namespacePath array or options object
|
||||||
let namespace: string[] | undefined;
|
let namespacePath: string[] | undefined;
|
||||||
let tableNamesOptions: Partial<TableNamesOptions> | undefined;
|
let tableNamesOptions: Partial<TableNamesOptions> | undefined;
|
||||||
|
|
||||||
if (Array.isArray(namespaceOrOptions)) {
|
if (Array.isArray(namespacePathOrOptions)) {
|
||||||
// First argument is namespace array
|
// First argument is namespacePath array
|
||||||
namespace = namespaceOrOptions;
|
namespacePath = namespacePathOrOptions;
|
||||||
tableNamesOptions = options;
|
tableNamesOptions = options;
|
||||||
} else {
|
} else {
|
||||||
// First argument is options object (backwards compatibility)
|
// First argument is options object (backwards compatibility)
|
||||||
namespace = undefined;
|
namespacePath = undefined;
|
||||||
tableNamesOptions = namespaceOrOptions;
|
tableNamesOptions = namespacePathOrOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.inner.tableNames(
|
return this.inner.tableNames(
|
||||||
namespace ?? [],
|
namespacePath ?? [],
|
||||||
tableNamesOptions?.startAfter,
|
tableNamesOptions?.startAfter,
|
||||||
tableNamesOptions?.limit,
|
tableNamesOptions?.limit,
|
||||||
);
|
);
|
||||||
@@ -345,12 +442,12 @@ export class LocalConnection extends Connection {
|
|||||||
|
|
||||||
async openTable(
|
async openTable(
|
||||||
name: string,
|
name: string,
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
options?: Partial<OpenTableOptions>,
|
options?: Partial<OpenTableOptions>,
|
||||||
): Promise<Table> {
|
): Promise<Table> {
|
||||||
const innerTable = await this.inner.openTable(
|
const innerTable = await this.inner.openTable(
|
||||||
name,
|
name,
|
||||||
namespace ?? [],
|
namespacePath ?? [],
|
||||||
cleanseStorageOptions(options?.storageOptions),
|
cleanseStorageOptions(options?.storageOptions),
|
||||||
options?.indexCacheSize,
|
options?.indexCacheSize,
|
||||||
);
|
);
|
||||||
@@ -362,7 +459,7 @@ export class LocalConnection extends Connection {
|
|||||||
targetTableName: string,
|
targetTableName: string,
|
||||||
sourceUri: string,
|
sourceUri: string,
|
||||||
options?: {
|
options?: {
|
||||||
targetNamespace?: string[];
|
targetNamespacePath?: string[];
|
||||||
sourceVersion?: number;
|
sourceVersion?: number;
|
||||||
sourceTag?: string;
|
sourceTag?: string;
|
||||||
isShallow?: boolean;
|
isShallow?: boolean;
|
||||||
@@ -371,7 +468,7 @@ export class LocalConnection extends Connection {
|
|||||||
const innerTable = await this.inner.cloneTable(
|
const innerTable = await this.inner.cloneTable(
|
||||||
targetTableName,
|
targetTableName,
|
||||||
sourceUri,
|
sourceUri,
|
||||||
options?.targetNamespace ?? [],
|
options?.targetNamespacePath ?? [],
|
||||||
options?.sourceVersion ?? null,
|
options?.sourceVersion ?? null,
|
||||||
options?.sourceTag ?? null,
|
options?.sourceTag ?? null,
|
||||||
options?.isShallow ?? true,
|
options?.isShallow ?? true,
|
||||||
@@ -406,42 +503,42 @@ export class LocalConnection extends Connection {
|
|||||||
nameOrOptions:
|
nameOrOptions:
|
||||||
| string
|
| string
|
||||||
| ({ name: string; data: Data } & Partial<CreateTableOptions>),
|
| ({ name: string; data: Data } & Partial<CreateTableOptions>),
|
||||||
dataOrNamespace?: Record<string, unknown>[] | TableLike | string[],
|
dataOrNamespacePath?: Record<string, unknown>[] | TableLike | string[],
|
||||||
namespaceOrOptions?: string[] | Partial<CreateTableOptions>,
|
namespacePathOrOptions?: string[] | Partial<CreateTableOptions>,
|
||||||
options?: Partial<CreateTableOptions>,
|
options?: Partial<CreateTableOptions>,
|
||||||
): Promise<Table> {
|
): Promise<Table> {
|
||||||
if (typeof nameOrOptions !== "string" && "name" in nameOrOptions) {
|
if (typeof nameOrOptions !== "string" && "name" in nameOrOptions) {
|
||||||
// First overload: createTable(options, namespace?)
|
// First overload: createTable(options, namespacePath?)
|
||||||
const { name, data, ...createOptions } = nameOrOptions;
|
const { name, data, ...createOptions } = nameOrOptions;
|
||||||
const namespace = dataOrNamespace as string[] | undefined;
|
const namespacePath = dataOrNamespacePath as string[] | undefined;
|
||||||
return this._createTableImpl(name, data, namespace, createOptions);
|
return this._createTableImpl(name, data, namespacePath, createOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second overload: createTable(name, data, namespace?, options?)
|
// Second overload: createTable(name, data, namespacePath?, options?)
|
||||||
const name = nameOrOptions;
|
const name = nameOrOptions;
|
||||||
const data = dataOrNamespace as Record<string, unknown>[] | TableLike;
|
const data = dataOrNamespacePath as Record<string, unknown>[] | TableLike;
|
||||||
|
|
||||||
// Detect if third argument is namespace array or options object
|
// Detect if third argument is namespacePath array or options object
|
||||||
let namespace: string[] | undefined;
|
let namespacePath: string[] | undefined;
|
||||||
let createOptions: Partial<CreateTableOptions> | undefined;
|
let createOptions: Partial<CreateTableOptions> | undefined;
|
||||||
|
|
||||||
if (Array.isArray(namespaceOrOptions)) {
|
if (Array.isArray(namespacePathOrOptions)) {
|
||||||
// Third argument is namespace array
|
// Third argument is namespacePath array
|
||||||
namespace = namespaceOrOptions;
|
namespacePath = namespacePathOrOptions;
|
||||||
createOptions = options;
|
createOptions = options;
|
||||||
} else {
|
} else {
|
||||||
// Third argument is options object (backwards compatibility)
|
// Third argument is options object (backwards compatibility)
|
||||||
namespace = undefined;
|
namespacePath = undefined;
|
||||||
createOptions = namespaceOrOptions;
|
createOptions = namespacePathOrOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._createTableImpl(name, data, namespace, createOptions);
|
return this._createTableImpl(name, data, namespacePath, createOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createTableImpl(
|
private async _createTableImpl(
|
||||||
name: string,
|
name: string,
|
||||||
data: Data,
|
data: Data,
|
||||||
namespace?: string[],
|
namespacePath?: string[],
|
||||||
options?: Partial<CreateTableOptions>,
|
options?: Partial<CreateTableOptions>,
|
||||||
): Promise<Table> {
|
): Promise<Table> {
|
||||||
if (data === undefined) {
|
if (data === undefined) {
|
||||||
@@ -455,7 +552,7 @@ export class LocalConnection extends Connection {
|
|||||||
name,
|
name,
|
||||||
buf,
|
buf,
|
||||||
mode,
|
mode,
|
||||||
namespace ?? [],
|
namespacePath ?? [],
|
||||||
storageOptions,
|
storageOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -465,21 +562,21 @@ export class LocalConnection extends Connection {
|
|||||||
async createEmptyTable(
|
async createEmptyTable(
|
||||||
name: string,
|
name: string,
|
||||||
schema: import("./arrow").SchemaLike,
|
schema: import("./arrow").SchemaLike,
|
||||||
namespaceOrOptions?: string[] | Partial<CreateTableOptions>,
|
namespacePathOrOptions?: string[] | Partial<CreateTableOptions>,
|
||||||
options?: Partial<CreateTableOptions>,
|
options?: Partial<CreateTableOptions>,
|
||||||
): Promise<Table> {
|
): Promise<Table> {
|
||||||
// Detect if third argument is namespace array or options object
|
// Detect if third argument is namespacePath array or options object
|
||||||
let namespace: string[] | undefined;
|
let namespacePath: string[] | undefined;
|
||||||
let createOptions: Partial<CreateTableOptions> | undefined;
|
let createOptions: Partial<CreateTableOptions> | undefined;
|
||||||
|
|
||||||
if (Array.isArray(namespaceOrOptions)) {
|
if (Array.isArray(namespacePathOrOptions)) {
|
||||||
// Third argument is namespace array
|
// Third argument is namespacePath array
|
||||||
namespace = namespaceOrOptions;
|
namespacePath = namespacePathOrOptions;
|
||||||
createOptions = options;
|
createOptions = options;
|
||||||
} else {
|
} else {
|
||||||
// Third argument is options object (backwards compatibility)
|
// Third argument is options object (backwards compatibility)
|
||||||
namespace = undefined;
|
namespacePath = undefined;
|
||||||
createOptions = namespaceOrOptions;
|
createOptions = namespacePathOrOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mode: string = createOptions?.mode ?? "create";
|
let mode: string = createOptions?.mode ?? "create";
|
||||||
@@ -502,18 +599,57 @@ export class LocalConnection extends Connection {
|
|||||||
name,
|
name,
|
||||||
buf,
|
buf,
|
||||||
mode,
|
mode,
|
||||||
namespace ?? [],
|
namespacePath ?? [],
|
||||||
storageOptions,
|
storageOptions,
|
||||||
);
|
);
|
||||||
return new LocalTable(innerTable);
|
return new LocalTable(innerTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
async dropTable(name: string, namespace?: string[]): Promise<void> {
|
async dropTable(name: string, namespacePath?: string[]): Promise<void> {
|
||||||
return this.inner.dropTable(name, namespace ?? []);
|
return this.inner.dropTable(name, namespacePath ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
async dropAllTables(namespace?: string[]): Promise<void> {
|
async dropAllTables(namespacePath?: string[]): Promise<void> {
|
||||||
return this.inner.dropAllTables(namespace ?? []);
|
return this.inner.dropAllTables(namespacePath ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
describeNamespace(
|
||||||
|
namespacePath: string[],
|
||||||
|
): Promise<DescribeNamespaceResponse> {
|
||||||
|
return this.inner.describeNamespace(namespacePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
listNamespaces(
|
||||||
|
namespacePath?: string[],
|
||||||
|
options?: Partial<ListNamespacesOptions>,
|
||||||
|
): Promise<ListNamespacesResponse> {
|
||||||
|
return this.inner.listNamespaces(
|
||||||
|
namespacePath ?? [],
|
||||||
|
options?.pageToken,
|
||||||
|
options?.limit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNamespace(
|
||||||
|
namespacePath: string[],
|
||||||
|
options?: Partial<CreateNamespaceOptions>,
|
||||||
|
): Promise<CreateNamespaceResponse> {
|
||||||
|
return this.inner.createNamespace(
|
||||||
|
namespacePath,
|
||||||
|
options?.mode,
|
||||||
|
options?.properties,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dropNamespace(
|
||||||
|
namespacePath: string[],
|
||||||
|
options?: Partial<DropNamespaceOptions>,
|
||||||
|
): Promise<DropNamespaceResponse> {
|
||||||
|
return this.inner.dropNamespace(
|
||||||
|
namespacePath,
|
||||||
|
options?.mode,
|
||||||
|
options?.behavior,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from "./connection";
|
} from "./connection";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ConnectNamespaceOptions,
|
||||||
ConnectionOptions,
|
ConnectionOptions,
|
||||||
Connection as LanceDbConnection,
|
Connection as LanceDbConnection,
|
||||||
JsHeaderProvider as NativeJsHeaderProvider,
|
JsHeaderProvider as NativeJsHeaderProvider,
|
||||||
@@ -22,6 +23,7 @@ export { JsHeaderProvider as NativeJsHeaderProvider } from "./native.js";
|
|||||||
export {
|
export {
|
||||||
AddColumnsSql,
|
AddColumnsSql,
|
||||||
ConnectionOptions,
|
ConnectionOptions,
|
||||||
|
ConnectNamespaceOptions,
|
||||||
IndexStatistics,
|
IndexStatistics,
|
||||||
IndexConfig,
|
IndexConfig,
|
||||||
ClientConfig,
|
ClientConfig,
|
||||||
@@ -62,6 +64,13 @@ export {
|
|||||||
CreateTableOptions,
|
CreateTableOptions,
|
||||||
TableNamesOptions,
|
TableNamesOptions,
|
||||||
OpenTableOptions,
|
OpenTableOptions,
|
||||||
|
ListNamespacesOptions,
|
||||||
|
CreateNamespaceOptions,
|
||||||
|
DropNamespaceOptions,
|
||||||
|
ListNamespacesResponse,
|
||||||
|
CreateNamespaceResponse,
|
||||||
|
DropNamespaceResponse,
|
||||||
|
DescribeNamespaceResponse,
|
||||||
} from "./connection";
|
} from "./connection";
|
||||||
|
|
||||||
export { Session } from "./native.js";
|
export { Session } from "./native.js";
|
||||||
@@ -293,3 +302,197 @@ export async function connect(
|
|||||||
);
|
);
|
||||||
return new LocalConnection(nativeConn);
|
return new LocalConnection(nativeConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the built-in directory namespace (`"dir"`).
|
||||||
|
*
|
||||||
|
* The directory namespace stores tables under a single root path (local
|
||||||
|
* filesystem or object storage URI). See
|
||||||
|
* {@link https://docs.lancedb.com/namespaces} for the documented surface;
|
||||||
|
* less-common knobs live under {@link DirNamespaceConfig.extraProperties}.
|
||||||
|
*/
|
||||||
|
export interface DirNamespaceConfig {
|
||||||
|
/** Root path or URI containing the LanceDB tables. */
|
||||||
|
root: string;
|
||||||
|
/**
|
||||||
|
* Whether to maintain a namespace manifest at the root. Required for
|
||||||
|
* child namespaces. Defaults to true on the impl side.
|
||||||
|
*/
|
||||||
|
manifestEnabled?: boolean;
|
||||||
|
/**
|
||||||
|
* Additional raw properties passed verbatim to the namespace
|
||||||
|
* implementation (e.g. `storage.*`, `credential_vendor.*`). Typed
|
||||||
|
* fields above take precedence on key collision.
|
||||||
|
*/
|
||||||
|
extraProperties?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for the built-in REST namespace (`"rest"`).
|
||||||
|
*
|
||||||
|
* The REST namespace talks to a remote catalog server over HTTP. See
|
||||||
|
* {@link https://docs.lancedb.com/namespaces} for the documented surface;
|
||||||
|
* less-common knobs (TLS, metrics) live under
|
||||||
|
* {@link RestNamespaceConfig.extraProperties}.
|
||||||
|
*/
|
||||||
|
export interface RestNamespaceConfig {
|
||||||
|
/** Catalog endpoint URL. */
|
||||||
|
uri: string;
|
||||||
|
/**
|
||||||
|
* HTTP headers forwarded with each request. Keys are passed through
|
||||||
|
* as-is (e.g. `"x-api-key"`, `"Authorization"`).
|
||||||
|
*/
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
/**
|
||||||
|
* Additional raw properties passed verbatim to the namespace
|
||||||
|
* implementation (e.g. `tls.*`, `ops_metrics_enabled`, `delimiter`).
|
||||||
|
* Typed fields above take precedence on key collision.
|
||||||
|
*/
|
||||||
|
extraProperties?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dirConfigToProperties(
|
||||||
|
config: DirNamespaceConfig,
|
||||||
|
): Record<string, string> {
|
||||||
|
// Spread the whole input so that unknown keys (e.g. a raw `manifest_enabled`
|
||||||
|
// passed via the dynamic-impl path) flow through instead of being dropped.
|
||||||
|
// Typed transformations layer on top.
|
||||||
|
const { manifestEnabled, extraProperties, ...rest } = config;
|
||||||
|
const properties: Record<string, string> = {
|
||||||
|
...(extraProperties ?? {}),
|
||||||
|
...(rest as Record<string, string>),
|
||||||
|
};
|
||||||
|
if (manifestEnabled !== undefined) {
|
||||||
|
properties.manifest_enabled = String(manifestEnabled);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restConfigToProperties(
|
||||||
|
config: RestNamespaceConfig,
|
||||||
|
): Record<string, string> {
|
||||||
|
const { headers, extraProperties, ...rest } = config;
|
||||||
|
const properties: Record<string, string> = {
|
||||||
|
...(extraProperties ?? {}),
|
||||||
|
...(rest as Record<string, string>),
|
||||||
|
};
|
||||||
|
if (headers) {
|
||||||
|
for (const [name, value] of Object.entries(headers)) {
|
||||||
|
properties[`headers.${name}`] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a LanceDB database through a namespace.
|
||||||
|
*
|
||||||
|
* Unlike {@link connect}, which routes by URI scheme (local path vs.
|
||||||
|
* `db://` cloud), `connectNamespace` always returns a namespace-backed
|
||||||
|
* connection. The `implName` selects the namespace implementation:
|
||||||
|
*
|
||||||
|
* - `"dir"` — directory namespace, configured with {@link DirNamespaceConfig}.
|
||||||
|
* - `"rest"` — remote REST catalog, configured with {@link RestNamespaceConfig}.
|
||||||
|
* - Any other string — full module path for a custom implementation,
|
||||||
|
* configured with a free-form string-keyed `properties` map.
|
||||||
|
*
|
||||||
|
* @example Typed dir namespace
|
||||||
|
* ```ts
|
||||||
|
* const db = await connectNamespace("dir", { root: "/path/to/db" });
|
||||||
|
* await db.createTable("users", [{ id: 1 }]);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Typed REST namespace with auth headers
|
||||||
|
* ```ts
|
||||||
|
* const db = await connectNamespace("rest", {
|
||||||
|
* uri: "https://catalog.example.com",
|
||||||
|
* headers: { "x-api-key": process.env.CATALOG_KEY ?? "" },
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Custom implementation with raw properties
|
||||||
|
* ```ts
|
||||||
|
* const db = await connectNamespace("my.custom.Namespace", {
|
||||||
|
* endpoint: "...",
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function connectNamespace(
|
||||||
|
implName: "dir",
|
||||||
|
config: DirNamespaceConfig,
|
||||||
|
options?: Partial<ConnectNamespaceOptions>,
|
||||||
|
): Promise<Connection>;
|
||||||
|
/**
|
||||||
|
* Connect through the built-in REST namespace.
|
||||||
|
*
|
||||||
|
* Configured with {@link RestNamespaceConfig}. See the function-level
|
||||||
|
* documentation above for the full surface, examples, and how this
|
||||||
|
* relates to {@link connect}.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const db = await connectNamespace("rest", {
|
||||||
|
* uri: "https://catalog.example.com",
|
||||||
|
* headers: { "x-api-key": process.env.CATALOG_KEY ?? "" },
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function connectNamespace(
|
||||||
|
implName: "rest",
|
||||||
|
config: RestNamespaceConfig,
|
||||||
|
options?: Partial<ConnectNamespaceOptions>,
|
||||||
|
): Promise<Connection>;
|
||||||
|
/**
|
||||||
|
* Connect through a custom namespace implementation by full module path,
|
||||||
|
* configured with a free-form string-keyed `properties` map. Use the
|
||||||
|
* typed overloads above for the built-in `"dir"` and `"rest"` impls.
|
||||||
|
*
|
||||||
|
* See the function-level documentation above for examples and how this
|
||||||
|
* relates to {@link connect}.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const db = await connectNamespace("my.custom.Namespace", {
|
||||||
|
* endpoint: "...",
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function connectNamespace(
|
||||||
|
implName: string,
|
||||||
|
properties: Record<string, string>,
|
||||||
|
options?: Partial<ConnectNamespaceOptions>,
|
||||||
|
): Promise<Connection>;
|
||||||
|
export async function connectNamespace(
|
||||||
|
implName: string,
|
||||||
|
configOrProperties:
|
||||||
|
| DirNamespaceConfig
|
||||||
|
| RestNamespaceConfig
|
||||||
|
| Record<string, string>,
|
||||||
|
options?: Partial<ConnectNamespaceOptions>,
|
||||||
|
): Promise<Connection> {
|
||||||
|
let properties: Record<string, string>;
|
||||||
|
if (implName === "dir") {
|
||||||
|
properties = dirConfigToProperties(
|
||||||
|
configOrProperties as DirNamespaceConfig,
|
||||||
|
);
|
||||||
|
} else if (implName === "rest") {
|
||||||
|
properties = restConfigToProperties(
|
||||||
|
configOrProperties as RestNamespaceConfig,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
properties = configOrProperties as Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalOptions: ConnectNamespaceOptions = (options ??
|
||||||
|
{}) as ConnectNamespaceOptions;
|
||||||
|
finalOptions.storageOptions = cleanseStorageOptions(
|
||||||
|
finalOptions.storageOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
const nativeConn = await LanceDbConnection.newWithNamespace(
|
||||||
|
implName,
|
||||||
|
properties,
|
||||||
|
finalOptions,
|
||||||
|
);
|
||||||
|
return new LocalConnection(nativeConn);
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,6 +87,23 @@ export class MergeInsertBuilder {
|
|||||||
this.#schema,
|
this.#schema,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip the automatic cleanup of old dataset versions that would otherwise
|
||||||
|
* run as part of this merge insert's commit. Forwards to
|
||||||
|
* `MergeInsertBuilder::skip_auto_cleanup` in lance-core.
|
||||||
|
*
|
||||||
|
* Useful for high-frequency writers that prefer to manage version cleanup
|
||||||
|
* themselves, or writers without delete permissions on the underlying storage.
|
||||||
|
*
|
||||||
|
* @param skip - If true, the auto-cleanup step is skipped at commit time.
|
||||||
|
*/
|
||||||
|
skipAutoCleanup(skip: boolean): MergeInsertBuilder {
|
||||||
|
return new MergeInsertBuilder(
|
||||||
|
this.#native.skipAutoCleanup(skip),
|
||||||
|
this.#schema,
|
||||||
|
);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Executes the merge insert operation
|
* Executes the merge insert operation
|
||||||
*
|
*
|
||||||
|
|||||||
+36
-1
@@ -56,6 +56,18 @@ export interface AddDataOptions {
|
|||||||
* If "overwrite" then the new data will replace the existing data in the table.
|
* If "overwrite" then the new data will replace the existing data in the table.
|
||||||
*/
|
*/
|
||||||
mode: "append" | "overwrite";
|
mode: "append" | "overwrite";
|
||||||
|
/**
|
||||||
|
* If true, skip the automatic cleanup of old dataset versions that would
|
||||||
|
* otherwise run as part of this write's commit. Forwards to
|
||||||
|
* `WriteParams.skip_auto_cleanup` in lance-core.
|
||||||
|
*
|
||||||
|
* Useful for high-frequency writers that prefer to manage version cleanup
|
||||||
|
* themselves (for example, via a separate periodic optimize job), or for
|
||||||
|
* writers that don't have delete permissions on the underlying storage.
|
||||||
|
*
|
||||||
|
* Defaults to false.
|
||||||
|
*/
|
||||||
|
skipAutoCleanup?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateOptions {
|
export interface UpdateOptions {
|
||||||
@@ -285,6 +297,25 @@ export abstract class Table {
|
|||||||
*/
|
*/
|
||||||
abstract prewarmIndex(name: string): Promise<void>;
|
abstract prewarmIndex(name: string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prewarm one or more columns of data in the table.
|
||||||
|
*
|
||||||
|
* @param columns The columns to prewarm. If undefined, all columns are prewarmed.
|
||||||
|
*
|
||||||
|
* This will load the column data into the page cache so that future queries that
|
||||||
|
* read those columns avoid the initial cold-start latency. This call initiates
|
||||||
|
* prewarming and returns once the request is accepted; the warming itself may
|
||||||
|
* continue in the background. Calling it on already-prewarmed columns is a
|
||||||
|
* no-op on the server.
|
||||||
|
*
|
||||||
|
* Prewarming is generally useful for columns used in filters or projections.
|
||||||
|
* Large columns (e.g. high-dimensional vectors or binary data) may not be
|
||||||
|
* practical to prewarm.
|
||||||
|
*
|
||||||
|
* This feature is currently only supported on remote tables.
|
||||||
|
*/
|
||||||
|
abstract prewarmData(columns?: string[]): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for asynchronous indexing to complete on the table.
|
* Waits for asynchronous indexing to complete on the table.
|
||||||
*
|
*
|
||||||
@@ -617,7 +648,7 @@ export class LocalTable extends Table {
|
|||||||
const schema = await this.schema();
|
const schema = await this.schema();
|
||||||
|
|
||||||
const buffer = await fromDataToBuffer(data, undefined, schema);
|
const buffer = await fromDataToBuffer(data, undefined, schema);
|
||||||
return await this.inner.add(buffer, mode);
|
return await this.inner.add(buffer, mode, options?.skipAutoCleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
@@ -710,6 +741,10 @@ export class LocalTable extends Table {
|
|||||||
await this.inner.prewarmIndex(name);
|
await this.inner.prewarmIndex(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async prewarmData(columns?: string[]): Promise<void> {
|
||||||
|
await this.inner.prewarmData(columns);
|
||||||
|
}
|
||||||
|
|
||||||
async waitForIndex(
|
async waitForIndex(
|
||||||
indexNames: string[],
|
indexNames: string[],
|
||||||
timeoutSeconds: number,
|
timeoutSeconds: number,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-darwin-arm64",
|
"name": "@lancedb/lancedb-darwin-arm64",
|
||||||
"version": "0.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"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-linux-arm64-gnu",
|
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
||||||
"version": "0.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"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.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"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.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"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.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"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.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-win32-x64-msvc",
|
"name": "@lancedb/lancedb-win32-x64-msvc",
|
||||||
"version": "0.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"os": ["win32"],
|
"os": ["win32"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.win32-x64-msvc.node",
|
"main": "lancedb.win32-x64-msvc.node",
|
||||||
|
|||||||
Generated
-10452
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -11,7 +11,7 @@
|
|||||||
"ann"
|
"ann"
|
||||||
],
|
],
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.27.2-beta.1",
|
"version": "0.28.0-beta.11",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.js",
|
".": "./dist/index.js",
|
||||||
@@ -38,15 +38,15 @@
|
|||||||
"url": "https://github.com/lancedb/lancedb"
|
"url": "https://github.com/lancedb/lancedb"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@aws-sdk/client-dynamodb": "^3.33.0",
|
"@aws-sdk/client-dynamodb": "3.1003.0",
|
||||||
"@aws-sdk/client-kms": "^3.33.0",
|
"@aws-sdk/client-kms": "3.1003.0",
|
||||||
"@aws-sdk/client-s3": "^3.33.0",
|
"@aws-sdk/client-s3": "3.1003.0",
|
||||||
"@biomejs/biome": "^1.7.3",
|
"@biomejs/biome": "^1.7.3",
|
||||||
"@jest/globals": "^29.7.0",
|
"@jest/globals": "^29.7.0",
|
||||||
"@napi-rs/cli": "^3.5.1",
|
"@napi-rs/cli": "3.5.1",
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"@types/jest": "^29.1.2",
|
"@types/jest": "^29.1.2",
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "22.7.4",
|
||||||
"@types/tmp": "^0.2.6",
|
"@types/tmp": "^0.2.6",
|
||||||
"apache-arrow-15": "npm:apache-arrow@15.0.0",
|
"apache-arrow-15": "npm:apache-arrow@15.0.0",
|
||||||
"apache-arrow-16": "npm:apache-arrow@16.0.0",
|
"apache-arrow-16": "npm:apache-arrow@16.0.0",
|
||||||
@@ -57,9 +57,9 @@
|
|||||||
"shx": "^0.3.4",
|
"shx": "^0.3.4",
|
||||||
"tmp": "^0.2.3",
|
"tmp": "^0.2.3",
|
||||||
"ts-jest": "^29.1.2",
|
"ts-jest": "^29.1.2",
|
||||||
"typedoc": "^0.26.4",
|
"typedoc": "0.26.4",
|
||||||
"typedoc-plugin-markdown": "^4.2.1",
|
"typedoc-plugin-markdown": "4.2.1",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "5.5.4",
|
||||||
"typescript-eslint": "^7.1.0"
|
"typescript-eslint": "^7.1.0"
|
||||||
},
|
},
|
||||||
"ava": {
|
"ava": {
|
||||||
@@ -68,16 +68,16 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
},
|
},
|
||||||
|
"packageManager": "pnpm@11.1.1",
|
||||||
"cpu": ["x64", "arm64"],
|
"cpu": ["x64", "arm64"],
|
||||||
"os": ["darwin", "linux", "win32"],
|
"os": ["darwin", "linux", "win32"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"artifacts": "napi artifacts",
|
"artifacts": "napi artifacts",
|
||||||
"build:debug": "napi build --platform --dts ../lancedb/native.d.ts --js ../lancedb/native.js --output-dir lancedb",
|
"build:debug": "napi build --platform --dts ../lancedb/native.d.ts --js ../lancedb/native.js --output-dir lancedb",
|
||||||
"postbuild:debug": "shx mkdir -p dist && shx cp lancedb/*.node dist/",
|
"postbuild:debug": "shx mkdir -p dist && shx cp lancedb/*.node dist/ && node -e \"require('fs').writeFileSync('dist/package.json', JSON.stringify({name:'@lancedb/lancedb',type:'commonjs'}))\"",
|
||||||
"build:release": "napi build --platform --release --dts ../lancedb/native.d.ts --js ../lancedb/native.js --output-dir dist",
|
"build:release": "napi build --platform --release --dts ../lancedb/native.d.ts --js ../lancedb/native.js --output-dir dist",
|
||||||
"postbuild:release": "shx mkdir -p dist && shx cp lancedb/*.node dist/",
|
"build": "pnpm build:debug && pnpm tsc",
|
||||||
"build": "npm run build:debug && npm run tsc",
|
"build-release": "pnpm build:release && pnpm tsc",
|
||||||
"build-release": "npm run build:release && npm run tsc",
|
|
||||||
"tsc": "tsc -b",
|
"tsc": "tsc -b",
|
||||||
"posttsc": "shx cp lancedb/native.d.ts dist/native.d.ts",
|
"posttsc": "shx cp lancedb/native.d.ts dist/native.d.ts",
|
||||||
"lint-ci": "biome ci .",
|
"lint-ci": "biome ci .",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"lint-fix": "biome check --write . && biome format --write .",
|
"lint-fix": "biome check --write . && biome format --write .",
|
||||||
"prepublishOnly": "napi prepublish -t npm",
|
"prepublishOnly": "napi prepublish -t npm",
|
||||||
"test": "jest --verbose",
|
"test": "jest --verbose",
|
||||||
"integration": "S3_TEST=1 npm run test",
|
"integration": "S3_TEST=1 pnpm test",
|
||||||
"universal": "napi universalize",
|
"universal": "napi universalize",
|
||||||
"version": "napi version"
|
"version": "napi version"
|
||||||
},
|
},
|
||||||
@@ -95,8 +95,8 @@
|
|||||||
"reflect-metadata": "^0.2.2"
|
"reflect-metadata": "^0.2.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@huggingface/transformers": "^3.0.2",
|
"@huggingface/transformers": "3.0.2",
|
||||||
"openai": "^4.29.2"
|
"openai": "4.29.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"apache-arrow": ">=15.0.0 <=18.1.0"
|
"apache-arrow": ">=15.0.0 <=18.1.0"
|
||||||
|
|||||||
Generated
+7317
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
|||||||
|
# Flat node_modules layout. The @napi-rs/cli build step fails to locate
|
||||||
|
# the cdylib artifact under pnpm's isolated layout; the hoisted linker
|
||||||
|
# mirrors npm's structure and unblocks the native build.
|
||||||
|
nodeLinker: hoisted
|
||||||
|
|
||||||
|
# Block resolution of versions less than 24h old (Shai-Hulud window).
|
||||||
|
# This is the pnpm 11 default but pinned here so it's visible to
|
||||||
|
# reviewers and survives a future pnpm major flipping the default.
|
||||||
|
minimumReleaseAge: 1440
|
||||||
|
|
||||||
|
# Fail install if a transitive dep tries to run an unapproved script.
|
||||||
|
strictDepBuilds: true
|
||||||
|
|
||||||
|
allowBuilds:
|
||||||
|
'@biomejs/biome': true
|
||||||
|
onnxruntime-node: true
|
||||||
|
protobufjs: true
|
||||||
|
sharp: true
|
||||||
+212
-17
@@ -8,12 +8,16 @@ use lancedb::database::{CreateTableMode, Database};
|
|||||||
use napi::bindgen_prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
use napi_derive::*;
|
use napi_derive::*;
|
||||||
|
|
||||||
|
use crate::ConnectNamespaceOptions;
|
||||||
use crate::ConnectionOptions;
|
use crate::ConnectionOptions;
|
||||||
use crate::error::NapiErrorExt;
|
use crate::error::NapiErrorExt;
|
||||||
use crate::header::JsHeaderProvider;
|
use crate::header::JsHeaderProvider;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use lancedb::connection::{ConnectBuilder, Connection as LanceDBConnection};
|
use lancedb::connection::{ConnectBuilder, Connection as LanceDBConnection, connect_namespace};
|
||||||
|
|
||||||
|
use lance_namespace::models::{
|
||||||
|
CreateNamespaceRequest, DescribeNamespaceRequest, DropNamespaceRequest, ListNamespacesRequest,
|
||||||
|
};
|
||||||
use lancedb::ipc::{ipc_file_to_batches, ipc_file_to_schema};
|
use lancedb::ipc::{ipc_file_to_batches, ipc_file_to_schema};
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
@@ -21,6 +25,29 @@ pub struct Connection {
|
|||||||
inner: Option<LanceDBConnection>,
|
inner: Option<LanceDBConnection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
pub struct DescribeNamespaceResponse {
|
||||||
|
pub properties: Option<HashMap<String, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
pub struct ListNamespacesResponse {
|
||||||
|
pub namespaces: Vec<String>,
|
||||||
|
pub page_token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
pub struct CreateNamespaceResponse {
|
||||||
|
pub properties: Option<HashMap<String, String>>,
|
||||||
|
pub transaction_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
pub struct DropNamespaceResponse {
|
||||||
|
pub properties: Option<HashMap<String, String>>,
|
||||||
|
pub transaction_id: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
pub(crate) fn inner_new(inner: LanceDBConnection) -> Self {
|
pub(crate) fn inner_new(inner: LanceDBConnection) -> Self {
|
||||||
Self { inner: Some(inner) }
|
Self { inner: Some(inner) }
|
||||||
@@ -67,6 +94,12 @@ impl Connection {
|
|||||||
builder = builder.storage_option(key, value);
|
builder = builder.storage_option(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(manifest_enabled) = options.manifest_enabled {
|
||||||
|
builder = builder.manifest_enabled(manifest_enabled);
|
||||||
|
}
|
||||||
|
if let Some(namespace_client_properties) = options.namespace_client_properties {
|
||||||
|
builder = builder.namespace_client_properties(namespace_client_properties);
|
||||||
|
}
|
||||||
|
|
||||||
// Create client config, optionally with header provider
|
// Create client config, optionally with header provider
|
||||||
let client_config = options.client_config.unwrap_or_default();
|
let client_config = options.client_config.unwrap_or_default();
|
||||||
@@ -100,6 +133,39 @@ impl Connection {
|
|||||||
Ok(Self::inner_new(builder.execute().await.default_error()?))
|
Ok(Self::inner_new(builder.execute().await.default_error()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new Connection instance backed by a namespace implementation.
|
||||||
|
#[napi(factory)]
|
||||||
|
pub async fn new_with_namespace(
|
||||||
|
impl_name: String,
|
||||||
|
properties: HashMap<String, String>,
|
||||||
|
options: ConnectNamespaceOptions,
|
||||||
|
) -> napi::Result<Self> {
|
||||||
|
if impl_name.is_empty() {
|
||||||
|
return Err(napi::Error::from_reason(
|
||||||
|
"implName must be a non-empty string",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder = connect_namespace(&impl_name, properties);
|
||||||
|
if let Some(interval) = options.read_consistency_interval {
|
||||||
|
builder =
|
||||||
|
builder.read_consistency_interval(std::time::Duration::from_secs_f64(interval));
|
||||||
|
}
|
||||||
|
if let Some(storage_options) = options.storage_options {
|
||||||
|
for (key, value) in storage_options {
|
||||||
|
builder = builder.storage_option(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(namespace_client_properties) = options.namespace_client_properties {
|
||||||
|
builder = builder.namespace_client_properties(namespace_client_properties);
|
||||||
|
}
|
||||||
|
if let Some(session) = options.session {
|
||||||
|
builder = builder.session(session.inner.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::inner_new(builder.execute().await.default_error()?))
|
||||||
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn display(&self) -> napi::Result<String> {
|
pub fn display(&self) -> napi::Result<String> {
|
||||||
Ok(self.get_inner()?.to_string())
|
Ok(self.get_inner()?.to_string())
|
||||||
@@ -119,12 +185,12 @@ impl Connection {
|
|||||||
#[napi(catch_unwind)]
|
#[napi(catch_unwind)]
|
||||||
pub async fn table_names(
|
pub async fn table_names(
|
||||||
&self,
|
&self,
|
||||||
namespace: Vec<String>,
|
namespace_path: Option<Vec<String>>,
|
||||||
start_after: Option<String>,
|
start_after: Option<String>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> napi::Result<Vec<String>> {
|
) -> napi::Result<Vec<String>> {
|
||||||
let mut op = self.get_inner()?.table_names();
|
let mut op = self.get_inner()?.table_names();
|
||||||
op = op.namespace(namespace);
|
op = op.namespace(namespace_path.unwrap_or_default());
|
||||||
if let Some(start_after) = start_after {
|
if let Some(start_after) = start_after {
|
||||||
op = op.start_after(start_after);
|
op = op.start_after(start_after);
|
||||||
}
|
}
|
||||||
@@ -146,7 +212,7 @@ impl Connection {
|
|||||||
name: String,
|
name: String,
|
||||||
buf: Buffer,
|
buf: Buffer,
|
||||||
mode: String,
|
mode: String,
|
||||||
namespace: Vec<String>,
|
namespace_path: Option<Vec<String>>,
|
||||||
storage_options: Option<HashMap<String, String>>,
|
storage_options: Option<HashMap<String, String>>,
|
||||||
) -> napi::Result<Table> {
|
) -> napi::Result<Table> {
|
||||||
let batches = ipc_file_to_batches(buf.to_vec())
|
let batches = ipc_file_to_batches(buf.to_vec())
|
||||||
@@ -154,7 +220,7 @@ impl Connection {
|
|||||||
let mode = Self::parse_create_mode_str(&mode)?;
|
let mode = Self::parse_create_mode_str(&mode)?;
|
||||||
let mut builder = self.get_inner()?.create_table(&name, batches).mode(mode);
|
let mut builder = self.get_inner()?.create_table(&name, batches).mode(mode);
|
||||||
|
|
||||||
builder = builder.namespace(namespace);
|
builder = builder.namespace(namespace_path.unwrap_or_default());
|
||||||
|
|
||||||
if let Some(storage_options) = storage_options {
|
if let Some(storage_options) = storage_options {
|
||||||
for (key, value) in storage_options {
|
for (key, value) in storage_options {
|
||||||
@@ -171,7 +237,7 @@ impl Connection {
|
|||||||
name: String,
|
name: String,
|
||||||
schema_buf: Buffer,
|
schema_buf: Buffer,
|
||||||
mode: String,
|
mode: String,
|
||||||
namespace: Vec<String>,
|
namespace_path: Option<Vec<String>>,
|
||||||
storage_options: Option<HashMap<String, String>>,
|
storage_options: Option<HashMap<String, String>>,
|
||||||
) -> napi::Result<Table> {
|
) -> napi::Result<Table> {
|
||||||
let schema = ipc_file_to_schema(schema_buf.to_vec()).map_err(|e| {
|
let schema = ipc_file_to_schema(schema_buf.to_vec()).map_err(|e| {
|
||||||
@@ -183,7 +249,7 @@ impl Connection {
|
|||||||
.create_empty_table(&name, schema)
|
.create_empty_table(&name, schema)
|
||||||
.mode(mode);
|
.mode(mode);
|
||||||
|
|
||||||
builder = builder.namespace(namespace);
|
builder = builder.namespace(namespace_path.unwrap_or_default());
|
||||||
|
|
||||||
if let Some(storage_options) = storage_options {
|
if let Some(storage_options) = storage_options {
|
||||||
for (key, value) in storage_options {
|
for (key, value) in storage_options {
|
||||||
@@ -198,13 +264,13 @@ impl Connection {
|
|||||||
pub async fn open_table(
|
pub async fn open_table(
|
||||||
&self,
|
&self,
|
||||||
name: String,
|
name: String,
|
||||||
namespace: Vec<String>,
|
namespace_path: Option<Vec<String>>,
|
||||||
storage_options: Option<HashMap<String, String>>,
|
storage_options: Option<HashMap<String, String>>,
|
||||||
index_cache_size: Option<u32>,
|
index_cache_size: Option<u32>,
|
||||||
) -> napi::Result<Table> {
|
) -> napi::Result<Table> {
|
||||||
let mut builder = self.get_inner()?.open_table(&name);
|
let mut builder = self.get_inner()?.open_table(&name);
|
||||||
|
|
||||||
builder = builder.namespace(namespace);
|
builder = builder.namespace(namespace_path.unwrap_or_default());
|
||||||
|
|
||||||
if let Some(storage_options) = storage_options {
|
if let Some(storage_options) = storage_options {
|
||||||
for (key, value) in storage_options {
|
for (key, value) in storage_options {
|
||||||
@@ -223,7 +289,7 @@ impl Connection {
|
|||||||
&self,
|
&self,
|
||||||
target_table_name: String,
|
target_table_name: String,
|
||||||
source_uri: String,
|
source_uri: String,
|
||||||
target_namespace: Vec<String>,
|
target_namespace_path: Option<Vec<String>>,
|
||||||
source_version: Option<i64>,
|
source_version: Option<i64>,
|
||||||
source_tag: Option<String>,
|
source_tag: Option<String>,
|
||||||
is_shallow: bool,
|
is_shallow: bool,
|
||||||
@@ -232,7 +298,7 @@ impl Connection {
|
|||||||
.get_inner()?
|
.get_inner()?
|
||||||
.clone_table(&target_table_name, &source_uri);
|
.clone_table(&target_table_name, &source_uri);
|
||||||
|
|
||||||
builder = builder.target_namespace(target_namespace);
|
builder = builder.target_namespace(target_namespace_path.unwrap_or_default());
|
||||||
|
|
||||||
if let Some(version) = source_version {
|
if let Some(version) = source_version {
|
||||||
builder = builder.source_version(version as u64);
|
builder = builder.source_version(version as u64);
|
||||||
@@ -250,18 +316,147 @@ impl Connection {
|
|||||||
|
|
||||||
/// Drop table with the name. Or raise an error if the table does not exist.
|
/// Drop table with the name. Or raise an error if the table does not exist.
|
||||||
#[napi(catch_unwind)]
|
#[napi(catch_unwind)]
|
||||||
pub async fn drop_table(&self, name: String, namespace: Vec<String>) -> napi::Result<()> {
|
pub async fn drop_table(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
namespace_path: Option<Vec<String>>,
|
||||||
|
) -> napi::Result<()> {
|
||||||
|
let ns = namespace_path.unwrap_or_default();
|
||||||
self.get_inner()?
|
self.get_inner()?
|
||||||
.drop_table(&name, &namespace)
|
.drop_table(&name, &ns)
|
||||||
.await
|
.await
|
||||||
.default_error()
|
.default_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[napi(catch_unwind)]
|
#[napi(catch_unwind)]
|
||||||
pub async fn drop_all_tables(&self, namespace: Vec<String>) -> napi::Result<()> {
|
pub async fn drop_all_tables(&self, namespace_path: Option<Vec<String>>) -> napi::Result<()> {
|
||||||
self.get_inner()?
|
let ns = namespace_path.unwrap_or_default();
|
||||||
.drop_all_tables(&namespace)
|
self.get_inner()?.drop_all_tables(&ns).await.default_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(catch_unwind)]
|
||||||
|
/// Describe a namespace and return its properties.
|
||||||
|
pub async fn describe_namespace(
|
||||||
|
&self,
|
||||||
|
namespace_path: Vec<String>,
|
||||||
|
) -> napi::Result<DescribeNamespaceResponse> {
|
||||||
|
let req = DescribeNamespaceRequest {
|
||||||
|
id: Some(namespace_path),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let resp = self
|
||||||
|
.get_inner()?
|
||||||
|
.describe_namespace(req)
|
||||||
.await
|
.await
|
||||||
.default_error()
|
.default_error()?;
|
||||||
|
Ok(DescribeNamespaceResponse {
|
||||||
|
properties: resp.properties,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(catch_unwind)]
|
||||||
|
/// List child namespaces under the given namespace path
|
||||||
|
pub async fn list_namespaces(
|
||||||
|
&self,
|
||||||
|
namespace_path: Option<Vec<String>>,
|
||||||
|
page_token: Option<String>,
|
||||||
|
limit: Option<u32>,
|
||||||
|
) -> napi::Result<ListNamespacesResponse> {
|
||||||
|
let req = ListNamespacesRequest {
|
||||||
|
id: namespace_path,
|
||||||
|
page_token,
|
||||||
|
limit: limit.map(|l| l as i32),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let resp = self
|
||||||
|
.get_inner()?
|
||||||
|
.list_namespaces(req)
|
||||||
|
.await
|
||||||
|
.default_error()?;
|
||||||
|
Ok(ListNamespacesResponse {
|
||||||
|
namespaces: resp.namespaces,
|
||||||
|
page_token: resp.page_token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(catch_unwind)]
|
||||||
|
/// Create a new namespace with optional properties.
|
||||||
|
pub async fn create_namespace(
|
||||||
|
&self,
|
||||||
|
namespace_path: Vec<String>,
|
||||||
|
mode: Option<String>,
|
||||||
|
properties: Option<HashMap<String, String>>,
|
||||||
|
) -> napi::Result<CreateNamespaceResponse> {
|
||||||
|
let mode_str = mode
|
||||||
|
.map(|m| match m.to_lowercase().as_str() {
|
||||||
|
"create" => Ok("Create".to_string()),
|
||||||
|
"exist_ok" => Ok("ExistOk".to_string()),
|
||||||
|
"overwrite" => Ok("Overwrite".to_string()),
|
||||||
|
_ => Err(napi::Error::from_reason(format!(
|
||||||
|
"Invalid mode '{}': expected one of 'create', 'exist_ok', 'overwrite'",
|
||||||
|
m
|
||||||
|
))),
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let req = CreateNamespaceRequest {
|
||||||
|
id: Some(namespace_path),
|
||||||
|
mode: mode_str,
|
||||||
|
properties,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let resp = self
|
||||||
|
.get_inner()?
|
||||||
|
.create_namespace(req)
|
||||||
|
.await
|
||||||
|
.default_error()?;
|
||||||
|
Ok(CreateNamespaceResponse {
|
||||||
|
properties: resp.properties,
|
||||||
|
transaction_id: resp.transaction_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(catch_unwind)]
|
||||||
|
/// Drop a namespace.
|
||||||
|
pub async fn drop_namespace(
|
||||||
|
&self,
|
||||||
|
namespace_path: Vec<String>,
|
||||||
|
mode: Option<String>,
|
||||||
|
behavior: Option<String>,
|
||||||
|
) -> napi::Result<DropNamespaceResponse> {
|
||||||
|
let mode_str = mode
|
||||||
|
.map(|m| match m.to_lowercase().as_str() {
|
||||||
|
"skip" => Ok("Skip".to_string()),
|
||||||
|
"fail" => Ok("Fail".to_string()),
|
||||||
|
_ => Err(napi::Error::from_reason(format!(
|
||||||
|
"Invalid mode '{}': expected one of 'skip', 'fail'",
|
||||||
|
m
|
||||||
|
))),
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let behavior_str = behavior
|
||||||
|
.map(|b| match b.to_lowercase().as_str() {
|
||||||
|
"restrict" => Ok("Restrict".to_string()),
|
||||||
|
"cascade" => Ok("Cascade".to_string()),
|
||||||
|
_ => Err(napi::Error::from_reason(format!(
|
||||||
|
"Invalid behavior '{}': expected one of 'restrict', 'cascade'",
|
||||||
|
b
|
||||||
|
))),
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let req = DropNamespaceRequest {
|
||||||
|
id: Some(namespace_path),
|
||||||
|
mode: mode_str,
|
||||||
|
behavior: behavior_str,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let resp = self
|
||||||
|
.get_inner()?
|
||||||
|
.drop_namespace(req)
|
||||||
|
.await
|
||||||
|
.default_error()?;
|
||||||
|
Ok(DropNamespaceResponse {
|
||||||
|
properties: resp.properties,
|
||||||
|
transaction_id: resp.transaction_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-1
@@ -35,8 +35,15 @@ pub struct ConnectionOptions {
|
|||||||
pub read_consistency_interval: Option<f64>,
|
pub read_consistency_interval: Option<f64>,
|
||||||
/// (For LanceDB OSS only): configuration for object storage.
|
/// (For LanceDB OSS only): configuration for object storage.
|
||||||
///
|
///
|
||||||
/// The available options are described at https://lancedb.com/docs/storage/
|
/// The available options are described at https://docs.lancedb.com/storage/
|
||||||
pub storage_options: Option<HashMap<String, String>>,
|
pub storage_options: Option<HashMap<String, String>>,
|
||||||
|
/// (For LanceDB OSS only): use directory namespace manifests as the source
|
||||||
|
/// of truth for table metadata. Existing directory-listed root tables are
|
||||||
|
/// migrated into the manifest on access.
|
||||||
|
pub manifest_enabled: Option<bool>,
|
||||||
|
/// (For LanceDB OSS only): extra properties for the backing namespace
|
||||||
|
/// client used by manifest-enabled native connections.
|
||||||
|
pub namespace_client_properties: Option<HashMap<String, String>>,
|
||||||
/// (For LanceDB OSS only): the session to use for this connection. Holds
|
/// (For LanceDB OSS only): the session to use for this connection. Holds
|
||||||
/// shared caches and other session-specific state.
|
/// shared caches and other session-specific state.
|
||||||
pub session: Option<session::Session>,
|
pub session: Option<session::Session>,
|
||||||
@@ -60,6 +67,26 @@ pub struct OpenTableOptions {
|
|||||||
pub storage_options: Option<HashMap<String, String>>,
|
pub storage_options: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConnectNamespaceOptions {
|
||||||
|
/// The interval, in seconds, at which to check for updates to the table
|
||||||
|
/// from other processes. If None, then consistency is not checked. For
|
||||||
|
/// performance reasons, this is the default. For strong consistency, set
|
||||||
|
/// this to zero seconds. Then every read will check for updates from other
|
||||||
|
/// processes. As a compromise, you can set this to a non-zero value for
|
||||||
|
/// eventual consistency.
|
||||||
|
pub read_consistency_interval: Option<f64>,
|
||||||
|
/// Configuration for object storage. The available options are described
|
||||||
|
/// at https://docs.lancedb.com/storage/
|
||||||
|
pub storage_options: Option<HashMap<String, String>>,
|
||||||
|
/// Extra properties for the backing namespace client.
|
||||||
|
pub namespace_client_properties: Option<HashMap<String, String>>,
|
||||||
|
/// The session to use for this connection. Holds shared caches and other
|
||||||
|
/// session-specific state.
|
||||||
|
pub session: Option<session::Session>,
|
||||||
|
}
|
||||||
|
|
||||||
#[napi_derive::module_init]
|
#[napi_derive::module_init]
|
||||||
fn init() {
|
fn init() {
|
||||||
let env = Env::new()
|
let env = Env::new()
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ impl NativeMergeInsertBuilder {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn skip_auto_cleanup(&self, skip: bool) -> Self {
|
||||||
|
let mut this = self.clone();
|
||||||
|
this.inner.skip_auto_cleanup(skip);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
#[napi(catch_unwind)]
|
#[napi(catch_unwind)]
|
||||||
pub async fn execute(&self, buf: Buffer) -> napi::Result<MergeResult> {
|
pub async fn execute(&self, buf: Buffer) -> napi::Result<MergeResult> {
|
||||||
let data = ipc_file_to_batches(buf.to_vec())
|
let data = ipc_file_to_batches(buf.to_vec())
|
||||||
|
|||||||
@@ -92,6 +92,13 @@ pub struct ClientConfig {
|
|||||||
pub extra_headers: Option<HashMap<String, String>>,
|
pub extra_headers: Option<HashMap<String, String>>,
|
||||||
pub id_delimiter: Option<String>,
|
pub id_delimiter: Option<String>,
|
||||||
pub tls_config: Option<TlsConfig>,
|
pub tls_config: Option<TlsConfig>,
|
||||||
|
/// User identifier for tracking purposes.
|
||||||
|
///
|
||||||
|
/// This is sent as the `x-lancedb-user-id` header in requests to LanceDB Cloud/Enterprise.
|
||||||
|
/// It can be set directly, or via the `LANCEDB_USER_ID` environment variable.
|
||||||
|
/// Alternatively, set `LANCEDB_USER_ID_ENV_KEY` to specify another environment
|
||||||
|
/// variable that contains the user ID value.
|
||||||
|
pub user_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TimeoutConfig> for lancedb::remote::TimeoutConfig {
|
impl From<TimeoutConfig> for lancedb::remote::TimeoutConfig {
|
||||||
@@ -145,6 +152,7 @@ impl From<ClientConfig> for lancedb::remote::ClientConfig {
|
|||||||
id_delimiter: config.id_delimiter,
|
id_delimiter: config.id_delimiter,
|
||||||
tls_config: config.tls_config.map(Into::into),
|
tls_config: config.tls_config.map(Into::into),
|
||||||
header_provider: None, // the header provider is set separately later
|
header_provider: None, // the header provider is set separately later
|
||||||
|
user_id: config.user_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type RerankHybridFn = ThreadsafeFunction<
|
|||||||
RerankHybridCallbackArgs,
|
RerankHybridCallbackArgs,
|
||||||
Status,
|
Status,
|
||||||
false,
|
false,
|
||||||
|
true,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Reranker implementation that "wraps" a NodeJS Reranker implementation.
|
/// Reranker implementation that "wraps" a NodeJS Reranker implementation.
|
||||||
@@ -32,7 +33,10 @@ impl Reranker {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
rerank_hybrid: Function<RerankHybridCallbackArgs, Promise<Buffer>>,
|
rerank_hybrid: Function<RerankHybridCallbackArgs, Promise<Buffer>>,
|
||||||
) -> napi::Result<Self> {
|
) -> napi::Result<Self> {
|
||||||
let rerank_hybrid = rerank_hybrid.build_threadsafe_function().build()?;
|
let rerank_hybrid = rerank_hybrid
|
||||||
|
.build_threadsafe_function()
|
||||||
|
.weak::<true>()
|
||||||
|
.build()?;
|
||||||
Ok(Self { rerank_hybrid })
|
Ok(Self { rerank_hybrid })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-2
@@ -6,7 +6,7 @@ use std::collections::HashMap;
|
|||||||
use lancedb::ipc::{ipc_file_to_batches, ipc_file_to_schema};
|
use lancedb::ipc::{ipc_file_to_batches, ipc_file_to_schema};
|
||||||
use lancedb::table::{
|
use lancedb::table::{
|
||||||
AddDataMode, ColumnAlteration as LanceColumnAlteration, Duration, NewColumnTransform,
|
AddDataMode, ColumnAlteration as LanceColumnAlteration, Duration, NewColumnTransform,
|
||||||
OptimizeAction, OptimizeOptions, Table as LanceDbTable,
|
OptimizeAction, OptimizeOptions, Table as LanceDbTable, WriteOptions,
|
||||||
};
|
};
|
||||||
use napi::bindgen_prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
use napi_derive::napi;
|
use napi_derive::napi;
|
||||||
@@ -68,7 +68,12 @@ impl Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[napi(catch_unwind)]
|
#[napi(catch_unwind)]
|
||||||
pub async fn add(&self, buf: Buffer, mode: String) -> napi::Result<AddResult> {
|
pub async fn add(
|
||||||
|
&self,
|
||||||
|
buf: Buffer,
|
||||||
|
mode: String,
|
||||||
|
skip_auto_cleanup: Option<bool>,
|
||||||
|
) -> napi::Result<AddResult> {
|
||||||
let batches = ipc_file_to_batches(buf.to_vec())
|
let batches = ipc_file_to_batches(buf.to_vec())
|
||||||
.map_err(|e| napi::Error::from_reason(format!("Failed to read IPC file: {}", e)))?;
|
.map_err(|e| napi::Error::from_reason(format!("Failed to read IPC file: {}", e)))?;
|
||||||
let batches = batches
|
let batches = batches
|
||||||
@@ -92,6 +97,13 @@ impl Table {
|
|||||||
return Err(napi::Error::from_reason(format!("Invalid mode: {}", mode)));
|
return Err(napi::Error::from_reason(format!("Invalid mode: {}", mode)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if skip_auto_cleanup.unwrap_or(false) {
|
||||||
|
op = op.write_options(WriteOptions {
|
||||||
|
skip_auto_cleanup: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let res = op.execute().await.default_error()?;
|
let res = op.execute().await.default_error()?;
|
||||||
Ok(res.into())
|
Ok(res.into())
|
||||||
}
|
}
|
||||||
@@ -159,6 +171,14 @@ impl Table {
|
|||||||
.default_error()
|
.default_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi(catch_unwind)]
|
||||||
|
pub async fn prewarm_data(&self, columns: Option<Vec<String>>) -> napi::Result<()> {
|
||||||
|
self.inner_ref()?
|
||||||
|
.prewarm_data(columns)
|
||||||
|
.await
|
||||||
|
.default_error()
|
||||||
|
}
|
||||||
|
|
||||||
#[napi(catch_unwind)]
|
#[napi(catch_unwind)]
|
||||||
pub async fn wait_for_index(&self, index_names: Vec<String>, timeout_s: i64) -> Result<()> {
|
pub async fn wait_for_index(&self, index_names: Vec<String>, timeout_s: i64) -> Result<()> {
|
||||||
let timeout = std::time::Duration::from_secs(timeout_s.try_into().unwrap());
|
let timeout = std::time::Duration::from_secs(timeout_s.try_into().unwrap());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.30.2"
|
current_version = "0.31.0-beta.11"
|
||||||
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*)\\.
|
||||||
|
|||||||
+8
-6
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-python"
|
name = "lancedb-python"
|
||||||
version = "0.30.2"
|
version = "0.31.0-beta.11"
|
||||||
|
publish = false
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Python bindings for LanceDB"
|
description = "Python bindings for LanceDB"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
@@ -14,7 +15,7 @@ name = "_lancedb"
|
|||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arrow = { version = "57.2", features = ["pyarrow"] }
|
arrow = { version = "58.0.0", features = ["pyarrow"] }
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
lancedb = { path = "../rust/lancedb", default-features = false }
|
lancedb = { path = "../rust/lancedb", default-features = false }
|
||||||
@@ -24,8 +25,8 @@ lance-namespace-impls.workspace = true
|
|||||||
lance-io.workspace = true
|
lance-io.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
pyo3 = { version = "0.26", features = ["extension-module", "abi3-py39"] }
|
pyo3 = { version = "0.28", features = ["extension-module", "abi3-py39"] }
|
||||||
pyo3-async-runtimes = { version = "0.26", features = [
|
pyo3-async-runtimes = { version = "0.28", features = [
|
||||||
"attributes",
|
"attributes",
|
||||||
"tokio-runtime",
|
"tokio-runtime",
|
||||||
] }
|
] }
|
||||||
@@ -34,10 +35,11 @@ futures.workspace = true
|
|||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
tokio = { version = "1.40", features = ["sync"] }
|
tokio = { version = "1.40", features = ["sync", "rt-multi-thread"] }
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { version = "0.26", features = [
|
pyo3-build-config = { version = "0.28", features = [
|
||||||
"extension-module",
|
"extension-module",
|
||||||
"abi3-py39",
|
"abi3-py39",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -183,7 +183,6 @@
|
|||||||
| stack-data | 0.6.3 | MIT License | http://github.com/alexmojaki/stack_data |
|
| stack-data | 0.6.3 | MIT License | http://github.com/alexmojaki/stack_data |
|
||||||
| sympy | 1.14.0 | BSD License | https://sympy.org |
|
| sympy | 1.14.0 | BSD License | https://sympy.org |
|
||||||
| tabulate | 0.9.0 | MIT License | https://github.com/astanin/python-tabulate |
|
| tabulate | 0.9.0 | MIT License | https://github.com/astanin/python-tabulate |
|
||||||
| tantivy | 0.25.1 | UNKNOWN | UNKNOWN |
|
|
||||||
| threadpoolctl | 3.6.0 | BSD License | https://github.com/joblib/threadpoolctl |
|
| threadpoolctl | 3.6.0 | BSD License | https://github.com/joblib/threadpoolctl |
|
||||||
| timm | 1.0.24 | Apache Software License | https://github.com/huggingface/pytorch-image-models |
|
| timm | 1.0.24 | Apache Software License | https://github.com/huggingface/pytorch-image-models |
|
||||||
| tinycss2 | 1.4.0 | BSD License | https://www.courtbouillon.org/tinycss2 |
|
| tinycss2 | 1.4.0 | BSD License | https://www.courtbouillon.org/tinycss2 |
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ repository = "https://github.com/lancedb/lancedb"
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
pylance = [
|
pylance = [
|
||||||
"pylance>=4.0.0b7",
|
"pylance>=5.0.0b5",
|
||||||
]
|
]
|
||||||
tests = [
|
tests = [
|
||||||
"aiohttp>=3.9.0",
|
"aiohttp>=3.9.0",
|
||||||
@@ -57,9 +57,8 @@ tests = [
|
|||||||
"duckdb>=0.9.0",
|
"duckdb>=0.9.0",
|
||||||
"pytz>=2023.3",
|
"pytz>=2023.3",
|
||||||
"polars>=0.19, <=1.3.0",
|
"polars>=0.19, <=1.3.0",
|
||||||
"tantivy>=0.20.0",
|
|
||||||
"pyarrow-stubs>=16.0",
|
"pyarrow-stubs>=16.0",
|
||||||
"pylance>=4.0.0b7",
|
"pylance>=5.0.0b5",
|
||||||
"requests>=2.31.0",
|
"requests>=2.31.0",
|
||||||
"datafusion>=52,<53",
|
"datafusion>=52,<53",
|
||||||
]
|
]
|
||||||
@@ -83,7 +82,7 @@ embeddings = [
|
|||||||
"colpali-engine>=0.3.10",
|
"colpali-engine>=0.3.10",
|
||||||
"huggingface_hub>=0.19.0",
|
"huggingface_hub>=0.19.0",
|
||||||
"InstructorEmbedding>=1.0.1",
|
"InstructorEmbedding>=1.0.1",
|
||||||
"google.generativeai>=0.3.0",
|
"google-genai>=1.0.0",
|
||||||
"boto3>=1.28.57",
|
"boto3>=1.28.57",
|
||||||
"awscli>=1.44.38",
|
"awscli>=1.44.38",
|
||||||
"botocore>=1.31.57",
|
"botocore>=1.31.57",
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import importlib.metadata
|
|||||||
import os
|
import os
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Dict, Optional, Union, Any
|
from typing import Dict, Optional, Union, Any, List
|
||||||
import warnings
|
|
||||||
|
|
||||||
__version__ = importlib.metadata.version("lancedb")
|
__version__ = importlib.metadata.version("lancedb")
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ from ._lancedb import connect as lancedb_connect
|
|||||||
from .common import URI, sanitize_uri
|
from .common import URI, sanitize_uri
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from .db import AsyncConnection, DBConnection, LanceDBConnection
|
from .db import AsyncConnection, DBConnection, LanceDBConnection
|
||||||
from .io import StorageOptionsProvider
|
|
||||||
from .remote import ClientConfig
|
from .remote import ClientConfig
|
||||||
from .remote.db import RemoteDBConnection
|
from .remote.db import RemoteDBConnection
|
||||||
from .expr import Expr, col, lit, func
|
from .expr import Expr, col, lit, func
|
||||||
@@ -64,7 +62,7 @@ def _check_s3_bucket_with_dots(
|
|||||||
|
|
||||||
|
|
||||||
def connect(
|
def connect(
|
||||||
uri: URI,
|
uri: Optional[URI] = None,
|
||||||
*,
|
*,
|
||||||
api_key: Optional[str] = None,
|
api_key: Optional[str] = None,
|
||||||
region: str = "us-east-1",
|
region: str = "us-east-1",
|
||||||
@@ -74,14 +72,19 @@ def connect(
|
|||||||
client_config: Union[ClientConfig, Dict[str, Any], None] = None,
|
client_config: Union[ClientConfig, Dict[str, Any], None] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
session: Optional[Session] = None,
|
session: Optional[Session] = None,
|
||||||
|
manifest_enabled: bool = False,
|
||||||
|
namespace_client_impl: Optional[str] = None,
|
||||||
|
namespace_client_properties: Optional[Dict[str, str]] = None,
|
||||||
|
namespace_client_pushdown_operations: Optional[List[str]] = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> DBConnection:
|
) -> DBConnection:
|
||||||
"""Connect to a LanceDB database.
|
"""Connect to a LanceDB database.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
uri: str or Path
|
uri: str or Path, optional
|
||||||
The uri of the database.
|
The uri of the database. When ``namespace_client_impl`` is provided you may
|
||||||
|
omit ``uri`` and connect through a namespace client instead.
|
||||||
api_key: str, optional
|
api_key: str, optional
|
||||||
If presented, connect to LanceDB cloud.
|
If presented, connect to LanceDB cloud.
|
||||||
Otherwise, connect to a database on file system or cloud storage.
|
Otherwise, connect to a database on file system or cloud storage.
|
||||||
@@ -107,13 +110,29 @@ def connect(
|
|||||||
default configuration is used.
|
default configuration is used.
|
||||||
storage_options: dict, optional
|
storage_options: dict, optional
|
||||||
Additional options for the storage backend. See available options at
|
Additional options for the storage backend. See available options at
|
||||||
<https://lancedb.com/docs/storage/>
|
<https://docs.lancedb.com/storage/>
|
||||||
|
manifest_enabled : bool, default False
|
||||||
|
When true for local/native connections, use directory namespace
|
||||||
|
manifests as the source of truth for table metadata. Existing
|
||||||
|
directory-listed root tables are migrated into the manifest on access.
|
||||||
session: Session, optional
|
session: Session, optional
|
||||||
(For LanceDB OSS only)
|
(For LanceDB OSS only)
|
||||||
A session to use for this connection. Sessions allow you to configure
|
A session to use for this connection. Sessions allow you to configure
|
||||||
cache sizes for index and metadata caches, which can significantly
|
cache sizes for index and metadata caches, which can significantly
|
||||||
impact memory use and performance. They can also be re-used across
|
impact memory use and performance. They can also be re-used across
|
||||||
multiple connections to share the same cache state.
|
multiple connections to share the same cache state.
|
||||||
|
namespace_client_impl : str, optional
|
||||||
|
When provided along with ``namespace_client_properties``, ``connect``
|
||||||
|
returns a namespace-backed connection by delegating to
|
||||||
|
:func:`connect_namespace`. The value identifies which namespace
|
||||||
|
implementation to load (e.g., ``"dir"`` or ``"rest"``).
|
||||||
|
namespace_client_properties : dict, optional
|
||||||
|
Configuration to pass to the namespace client implementation. Required
|
||||||
|
when ``namespace_client_impl`` is set.
|
||||||
|
namespace_client_pushdown_operations : list[str], optional
|
||||||
|
Only used when ``namespace_client_properties`` is provided. Forwards to
|
||||||
|
:func:`connect_namespace` to control which operations are executed on the
|
||||||
|
namespace service (e.g., ``["QueryTable", "CreateTable"]``).
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
@@ -133,11 +152,48 @@ def connect(
|
|||||||
>>> db = lancedb.connect("db://my_database", api_key="ldb_...",
|
>>> db = lancedb.connect("db://my_database", api_key="ldb_...",
|
||||||
... client_config={"retry_config": {"retries": 5}})
|
... client_config={"retry_config": {"retries": 5}})
|
||||||
|
|
||||||
|
Connect to a namespace-backed database:
|
||||||
|
|
||||||
|
>>> db = lancedb.connect(namespace_client_impl="dir",
|
||||||
|
... namespace_client_properties={"root": "/tmp/ns"})
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
conn : DBConnection
|
conn : DBConnection
|
||||||
A connection to a LanceDB database.
|
A connection to a LanceDB database.
|
||||||
"""
|
"""
|
||||||
|
if namespace_client_impl is not None:
|
||||||
|
if namespace_client_properties is None:
|
||||||
|
raise ValueError(
|
||||||
|
"namespace_client_properties must be provided when "
|
||||||
|
"namespace_client_impl is set"
|
||||||
|
)
|
||||||
|
if kwargs:
|
||||||
|
raise ValueError(f"Unknown keyword arguments: {kwargs}")
|
||||||
|
return connect_namespace(
|
||||||
|
namespace_client_impl,
|
||||||
|
namespace_client_properties,
|
||||||
|
read_consistency_interval=read_consistency_interval,
|
||||||
|
storage_options=storage_options,
|
||||||
|
session=session,
|
||||||
|
namespace_client_pushdown_operations=namespace_client_pushdown_operations,
|
||||||
|
)
|
||||||
|
|
||||||
|
if namespace_client_properties is not None and not manifest_enabled:
|
||||||
|
raise ValueError(
|
||||||
|
"namespace_client_impl must be provided when using "
|
||||||
|
"namespace_client_properties unless manifest_enabled=True"
|
||||||
|
)
|
||||||
|
|
||||||
|
if namespace_client_pushdown_operations is not None:
|
||||||
|
raise ValueError(
|
||||||
|
"namespace_client_pushdown_operations is only valid when "
|
||||||
|
"connecting through a namespace"
|
||||||
|
)
|
||||||
|
if uri is None:
|
||||||
|
raise ValueError(
|
||||||
|
"uri is required when not connecting through a namespace client"
|
||||||
|
)
|
||||||
if isinstance(uri, str) and uri.startswith("db://"):
|
if isinstance(uri, str) and uri.startswith("db://"):
|
||||||
if api_key is None:
|
if api_key is None:
|
||||||
api_key = os.environ.get("LANCEDB_API_KEY")
|
api_key = os.environ.get("LANCEDB_API_KEY")
|
||||||
@@ -166,9 +222,92 @@ def connect(
|
|||||||
read_consistency_interval=read_consistency_interval,
|
read_consistency_interval=read_consistency_interval,
|
||||||
storage_options=storage_options,
|
storage_options=storage_options,
|
||||||
session=session,
|
session=session,
|
||||||
|
manifest_enabled=manifest_enabled,
|
||||||
|
namespace_client_properties=namespace_client_properties,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
WORKER_PROPERTY_PREFIX = "_lancedb_worker_"
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_worker_overrides(props: dict[str, str]) -> dict[str, str]:
|
||||||
|
"""Apply worker property overrides.
|
||||||
|
|
||||||
|
Any key starting with ``_lancedb_worker_`` is extracted, the prefix
|
||||||
|
is stripped, and the resulting key-value pair is put back into the
|
||||||
|
map (overriding the existing value if present). The original
|
||||||
|
prefixed key is removed.
|
||||||
|
"""
|
||||||
|
worker_keys = [k for k in props if k.startswith(WORKER_PROPERTY_PREFIX)]
|
||||||
|
if not worker_keys:
|
||||||
|
return props
|
||||||
|
result = dict(props)
|
||||||
|
for key in worker_keys:
|
||||||
|
value = result.pop(key)
|
||||||
|
real_key = key[len(WORKER_PROPERTY_PREFIX) :]
|
||||||
|
result[real_key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_conn(
|
||||||
|
data: str,
|
||||||
|
*,
|
||||||
|
for_worker: bool = False,
|
||||||
|
) -> DBConnection:
|
||||||
|
"""Reconstruct a DBConnection from a serialized string.
|
||||||
|
|
||||||
|
The string must have been produced by
|
||||||
|
:meth:`DBConnection.serialize`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
data : str
|
||||||
|
String produced by ``serialize()``.
|
||||||
|
for_worker : bool, default False
|
||||||
|
When ``True``, any namespace client property whose key starts
|
||||||
|
with ``_lancedb_worker_`` has that prefix stripped and the
|
||||||
|
value overrides the corresponding property. For example,
|
||||||
|
``_lancedb_worker_uri`` replaces ``uri``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
DBConnection
|
||||||
|
A new connection matching the serialized state.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
parsed = json.loads(data)
|
||||||
|
connection_type = parsed.get("connection_type")
|
||||||
|
|
||||||
|
rci_secs = parsed.get("read_consistency_interval_seconds")
|
||||||
|
rci = timedelta(seconds=rci_secs) if rci_secs is not None else None
|
||||||
|
storage_options = parsed.get("storage_options")
|
||||||
|
|
||||||
|
if connection_type == "namespace":
|
||||||
|
props = dict(parsed.get("namespace_client_properties") or {})
|
||||||
|
if for_worker:
|
||||||
|
props = _apply_worker_overrides(props)
|
||||||
|
return connect_namespace(
|
||||||
|
namespace_client_impl=parsed["namespace_client_impl"],
|
||||||
|
namespace_client_properties=props,
|
||||||
|
read_consistency_interval=rci,
|
||||||
|
storage_options=storage_options,
|
||||||
|
namespace_client_pushdown_operations=parsed.get(
|
||||||
|
"namespace_client_pushdown_operations"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
elif connection_type == "local":
|
||||||
|
return LanceDBConnection(
|
||||||
|
parsed["uri"],
|
||||||
|
read_consistency_interval=rci,
|
||||||
|
storage_options=storage_options,
|
||||||
|
manifest_enabled=parsed.get("manifest_enabled", False),
|
||||||
|
namespace_client_properties=parsed.get("namespace_client_properties"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown connection_type: {connection_type}")
|
||||||
|
|
||||||
|
|
||||||
async def connect_async(
|
async def connect_async(
|
||||||
uri: URI,
|
uri: URI,
|
||||||
*,
|
*,
|
||||||
@@ -179,6 +318,8 @@ async def connect_async(
|
|||||||
client_config: Optional[Union[ClientConfig, Dict[str, Any]]] = None,
|
client_config: Optional[Union[ClientConfig, Dict[str, Any]]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
session: Optional[Session] = None,
|
session: Optional[Session] = None,
|
||||||
|
manifest_enabled: bool = False,
|
||||||
|
namespace_client_properties: Optional[Dict[str, str]] = None,
|
||||||
) -> AsyncConnection:
|
) -> AsyncConnection:
|
||||||
"""Connect to a LanceDB database.
|
"""Connect to a LanceDB database.
|
||||||
|
|
||||||
@@ -211,13 +352,20 @@ async def connect_async(
|
|||||||
default configuration is used.
|
default configuration is used.
|
||||||
storage_options: dict, optional
|
storage_options: dict, optional
|
||||||
Additional options for the storage backend. See available options at
|
Additional options for the storage backend. See available options at
|
||||||
<https://lancedb.com/docs/storage/>
|
<https://docs.lancedb.com/storage/>
|
||||||
session: Session, optional
|
session: Session, optional
|
||||||
(For LanceDB OSS only)
|
(For LanceDB OSS only)
|
||||||
A session to use for this connection. Sessions allow you to configure
|
A session to use for this connection. Sessions allow you to configure
|
||||||
cache sizes for index and metadata caches, which can significantly
|
cache sizes for index and metadata caches, which can significantly
|
||||||
impact memory use and performance. They can also be re-used across
|
impact memory use and performance. They can also be re-used across
|
||||||
multiple connections to share the same cache state.
|
multiple connections to share the same cache state.
|
||||||
|
manifest_enabled : bool, default False
|
||||||
|
When true for local/native connections, use directory namespace
|
||||||
|
manifests as the source of truth for table metadata. Existing
|
||||||
|
directory-listed root tables are migrated into the manifest on access.
|
||||||
|
namespace_client_properties : dict, optional
|
||||||
|
Additional directory namespace client properties to use with
|
||||||
|
``manifest_enabled=True``.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
@@ -260,6 +408,8 @@ async def connect_async(
|
|||||||
client_config,
|
client_config,
|
||||||
storage_options,
|
storage_options,
|
||||||
session,
|
session,
|
||||||
|
manifest_enabled,
|
||||||
|
namespace_client_properties,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -284,17 +434,6 @@ __all__ = [
|
|||||||
"LanceNamespaceDBConnection",
|
"LanceNamespaceDBConnection",
|
||||||
"RemoteDBConnection",
|
"RemoteDBConnection",
|
||||||
"Session",
|
"Session",
|
||||||
"StorageOptionsProvider",
|
|
||||||
"Table",
|
"Table",
|
||||||
"__version__",
|
"__version__",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def __warn_on_fork():
|
|
||||||
warnings.warn(
|
|
||||||
"lance is not fork-safe. If you are using multiprocessing, use spawn instead.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if hasattr(os, "register_at_fork"):
|
|
||||||
os.register_at_fork(before=__warn_on_fork) # type: ignore[attr-defined]
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ from .index import (
|
|||||||
LabelList,
|
LabelList,
|
||||||
HnswPq,
|
HnswPq,
|
||||||
HnswSq,
|
HnswSq,
|
||||||
|
HnswFlat,
|
||||||
FTS,
|
FTS,
|
||||||
)
|
)
|
||||||
from .io import StorageOptionsProvider
|
|
||||||
from lance_namespace import (
|
from lance_namespace import (
|
||||||
ListNamespacesResponse,
|
ListNamespacesResponse,
|
||||||
CreateNamespaceResponse,
|
CreateNamespaceResponse,
|
||||||
@@ -26,6 +26,7 @@ from .remote import ClientConfig
|
|||||||
|
|
||||||
IvfHnswPq: type[HnswPq] = HnswPq
|
IvfHnswPq: type[HnswPq] = HnswPq
|
||||||
IvfHnswSq: type[HnswSq] = HnswSq
|
IvfHnswSq: type[HnswSq] = HnswSq
|
||||||
|
IvfHnswFlat: type[HnswFlat] = HnswFlat
|
||||||
|
|
||||||
class PyExpr:
|
class PyExpr:
|
||||||
"""A type-safe DataFusion expression node (Rust-side handle)."""
|
"""A type-safe DataFusion expression node (Rust-side handle)."""
|
||||||
@@ -72,35 +73,35 @@ 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]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
) -> ListNamespacesResponse: ...
|
) -> ListNamespacesResponse: ...
|
||||||
async def create_namespace(
|
async def create_namespace(
|
||||||
self,
|
self,
|
||||||
namespace: List[str],
|
namespace_path: List[str],
|
||||||
mode: Optional[str] = None,
|
mode: Optional[str] = None,
|
||||||
properties: Optional[Dict[str, str]] = None,
|
properties: Optional[Dict[str, str]] = None,
|
||||||
) -> CreateNamespaceResponse: ...
|
) -> CreateNamespaceResponse: ...
|
||||||
async def drop_namespace(
|
async def drop_namespace(
|
||||||
self,
|
self,
|
||||||
namespace: List[str],
|
namespace_path: List[str],
|
||||||
mode: Optional[str] = None,
|
mode: Optional[str] = None,
|
||||||
behavior: Optional[str] = None,
|
behavior: Optional[str] = None,
|
||||||
) -> DropNamespaceResponse: ...
|
) -> DropNamespaceResponse: ...
|
||||||
async def describe_namespace(
|
async def describe_namespace(
|
||||||
self,
|
self,
|
||||||
namespace: List[str],
|
namespace_path: List[str],
|
||||||
) -> DescribeNamespaceResponse: ...
|
) -> DescribeNamespaceResponse: ...
|
||||||
async def list_tables(
|
async def list_tables(
|
||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
) -> ListTablesResponse: ...
|
) -> ListTablesResponse: ...
|
||||||
async def table_names(
|
async def table_names(
|
||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]],
|
namespace_path: Optional[List[str]],
|
||||||
start_after: Optional[str],
|
start_after: Optional[str],
|
||||||
limit: Optional[int],
|
limit: Optional[int],
|
||||||
) -> list[str]: ... # Deprecated: Use list_tables instead
|
) -> list[str]: ... # Deprecated: Use list_tables instead
|
||||||
@@ -109,9 +110,8 @@ class Connection(object):
|
|||||||
name: str,
|
name: str,
|
||||||
mode: str,
|
mode: str,
|
||||||
data: pa.RecordBatchReader,
|
data: pa.RecordBatchReader,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
storage_options_provider: Optional[StorageOptionsProvider] = None,
|
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
) -> Table: ...
|
) -> Table: ...
|
||||||
async def create_empty_table(
|
async def create_empty_table(
|
||||||
@@ -119,17 +119,15 @@ class Connection(object):
|
|||||||
name: str,
|
name: str,
|
||||||
mode: str,
|
mode: str,
|
||||||
schema: pa.Schema,
|
schema: pa.Schema,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
storage_options_provider: Optional[StorageOptionsProvider] = None,
|
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
) -> Table: ...
|
) -> Table: ...
|
||||||
async def open_table(
|
async def open_table(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
storage_options_provider: Optional[StorageOptionsProvider] = None,
|
|
||||||
index_cache_size: Optional[int] = None,
|
index_cache_size: Optional[int] = None,
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
) -> Table: ...
|
) -> Table: ...
|
||||||
@@ -137,7 +135,7 @@ class Connection(object):
|
|||||||
self,
|
self,
|
||||||
target_table_name: str,
|
target_table_name: str,
|
||||||
source_uri: str,
|
source_uri: str,
|
||||||
target_namespace: Optional[List[str]] = None,
|
target_namespace_path: Optional[List[str]] = None,
|
||||||
source_version: Optional[int] = None,
|
source_version: Optional[int] = None,
|
||||||
source_tag: Optional[str] = None,
|
source_tag: Optional[str] = None,
|
||||||
is_shallow: bool = True,
|
is_shallow: bool = True,
|
||||||
@@ -146,13 +144,18 @@ class Connection(object):
|
|||||||
self,
|
self,
|
||||||
cur_name: str,
|
cur_name: str,
|
||||||
new_name: str,
|
new_name: str,
|
||||||
cur_namespace: Optional[List[str]] = None,
|
cur_namespace_path: Optional[List[str]] = None,
|
||||||
new_namespace: Optional[List[str]] = None,
|
new_namespace_path: Optional[List[str]] = None,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
async def drop_table(
|
async def drop_table(
|
||||||
self, name: str, namespace: Optional[List[str]] = None
|
self, name: str, namespace_path: Optional[List[str]] = None
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
async def drop_all_tables(self, namespace: Optional[List[str]] = None) -> None: ...
|
async def drop_all_tables(
|
||||||
|
self, namespace_path: Optional[List[str]] = None
|
||||||
|
) -> None: ...
|
||||||
|
async def namespace_client_config(
|
||||||
|
self,
|
||||||
|
) -> Dict[str, Any]: ...
|
||||||
|
|
||||||
class Table:
|
class Table:
|
||||||
def name(self) -> str: ...
|
def name(self) -> str: ...
|
||||||
@@ -179,6 +182,7 @@ class Table:
|
|||||||
IvfPq,
|
IvfPq,
|
||||||
HnswPq,
|
HnswPq,
|
||||||
HnswSq,
|
HnswSq,
|
||||||
|
HnswFlat,
|
||||||
BTree,
|
BTree,
|
||||||
Bitmap,
|
Bitmap,
|
||||||
LabelList,
|
LabelList,
|
||||||
@@ -241,6 +245,8 @@ async def connect(
|
|||||||
client_config: Optional[Union[ClientConfig, Dict[str, Any]]],
|
client_config: Optional[Union[ClientConfig, Dict[str, Any]]],
|
||||||
storage_options: Optional[Dict[str, str]],
|
storage_options: Optional[Dict[str, str]],
|
||||||
session: Optional[Session],
|
session: Optional[Session],
|
||||||
|
manifest_enabled: bool = False,
|
||||||
|
namespace_client_properties: Optional[Dict[str, str]] = None,
|
||||||
) -> Connection: ...
|
) -> Connection: ...
|
||||||
|
|
||||||
class RecordBatchStream:
|
class RecordBatchStream:
|
||||||
@@ -439,7 +445,7 @@ class AsyncPermutationBuilder:
|
|||||||
async def execute(self) -> Table: ...
|
async def execute(self) -> Table: ...
|
||||||
|
|
||||||
def async_permutation_builder(
|
def async_permutation_builder(
|
||||||
table: Table, dest_table_name: str
|
table: Table,
|
||||||
) -> AsyncPermutationBuilder: ...
|
) -> AsyncPermutationBuilder: ...
|
||||||
def fts_query_to_json(query: Any) -> str: ...
|
def fts_query_to_json(query: Any) -> str: ...
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
class BackgroundEventLoop:
|
class BackgroundEventLoop:
|
||||||
@@ -13,6 +15,9 @@ class BackgroundEventLoop:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self._start()
|
||||||
|
|
||||||
|
def _start(self):
|
||||||
self.loop = asyncio.new_event_loop()
|
self.loop = asyncio.new_event_loop()
|
||||||
self.thread = threading.Thread(
|
self.thread = threading.Thread(
|
||||||
target=self.loop.run_forever,
|
target=self.loop.run_forever,
|
||||||
@@ -31,3 +36,30 @@ class BackgroundEventLoop:
|
|||||||
|
|
||||||
|
|
||||||
LOOP = BackgroundEventLoop()
|
LOOP = BackgroundEventLoop()
|
||||||
|
|
||||||
|
_FORK_WARNED = False
|
||||||
|
|
||||||
|
|
||||||
|
def _reset_after_fork():
|
||||||
|
# Threads do not survive fork(), so the asyncio loop in LOOP.thread is
|
||||||
|
# dead in the child. Re-initialize the singleton in place so existing
|
||||||
|
# `from .background_loop import LOOP` references in other modules see
|
||||||
|
# the new state. The Rust-side tokio runtime is reset analogously by a
|
||||||
|
# pthread_atfork hook installed in the _lancedb extension.
|
||||||
|
LOOP._start()
|
||||||
|
global _FORK_WARNED
|
||||||
|
if not _FORK_WARNED:
|
||||||
|
_FORK_WARNED = True
|
||||||
|
warnings.warn(
|
||||||
|
"lancedb fork support is experimental: the internal async "
|
||||||
|
"runtime has been reset in the forked child, but a small chance "
|
||||||
|
"of deadlock remains if other state was mid-operation at fork "
|
||||||
|
"time. The 'forkserver' or 'spawn' multiprocessing start method "
|
||||||
|
"is likely a safer alternative.",
|
||||||
|
RuntimeWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(os, "register_at_fork"):
|
||||||
|
os.register_at_fork(after_in_child=_reset_after_fork)
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ def data_to_reader(
|
|||||||
f"Unknown data type {type(data)}. "
|
f"Unknown data type {type(data)}. "
|
||||||
"Supported types: list of dicts, pandas DataFrame, polars DataFrame, "
|
"Supported types: list of dicts, pandas DataFrame, polars DataFrame, "
|
||||||
"pyarrow Table/RecordBatch, or Pydantic models. "
|
"pyarrow Table/RecordBatch, or Pydantic models. "
|
||||||
"See https://lancedb.com/docs/tables/ for examples."
|
"See https://docs.lancedb.com/tables/ for examples."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+320
-220
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,10 @@ from .utils import TEXT, api_key_not_found_help
|
|||||||
@register("gemini-text")
|
@register("gemini-text")
|
||||||
class GeminiText(TextEmbeddingFunction):
|
class GeminiText(TextEmbeddingFunction):
|
||||||
"""
|
"""
|
||||||
An embedding function that uses the Google's Gemini API. Requires GOOGLE_API_KEY to
|
An embedding function that uses Google's Gemini API. Requires GOOGLE_API_KEY to
|
||||||
be set.
|
be set.
|
||||||
|
|
||||||
https://ai.google.dev/docs/embeddings_guide
|
https://ai.google.dev/gemini-api/docs/embeddings
|
||||||
|
|
||||||
Supports various tasks types:
|
Supports various tasks types:
|
||||||
| Task Type | Description |
|
| Task Type | Description |
|
||||||
@@ -46,9 +46,12 @@ class GeminiText(TextEmbeddingFunction):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
name: str, default "models/embedding-001"
|
name: str, default "gemini-embedding-001"
|
||||||
The name of the model to use. See the Gemini documentation for a list of
|
The name of the model to use. Supported models include:
|
||||||
available models.
|
- "gemini-embedding-001" (768 dimensions)
|
||||||
|
|
||||||
|
Note: The legacy "models/embedding-001" format is also supported but
|
||||||
|
"gemini-embedding-001" is recommended.
|
||||||
|
|
||||||
query_task_type: str, default "retrieval_query"
|
query_task_type: str, default "retrieval_query"
|
||||||
Sets the task type for the queries.
|
Sets the task type for the queries.
|
||||||
@@ -77,7 +80,7 @@ class GeminiText(TextEmbeddingFunction):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str = "models/embedding-001"
|
name: str = "gemini-embedding-001"
|
||||||
query_task_type: str = "retrieval_query"
|
query_task_type: str = "retrieval_query"
|
||||||
source_task_type: str = "retrieval_document"
|
source_task_type: str = "retrieval_document"
|
||||||
|
|
||||||
@@ -114,23 +117,48 @@ class GeminiText(TextEmbeddingFunction):
|
|||||||
texts: list[str] or np.ndarray (of str)
|
texts: list[str] or np.ndarray (of str)
|
||||||
The texts to embed
|
The texts to embed
|
||||||
"""
|
"""
|
||||||
if (
|
from google.genai import types
|
||||||
kwargs.get("task_type") == "retrieval_document"
|
|
||||||
): # Provide a title to use existing API design
|
|
||||||
title = "Embedding of a document"
|
|
||||||
kwargs["title"] = title
|
|
||||||
|
|
||||||
return [
|
task_type = kwargs.get("task_type")
|
||||||
self.client.embed_content(model=self.name, content=text, **kwargs)[
|
|
||||||
"embedding"
|
# Build content objects for embed_content
|
||||||
]
|
contents = []
|
||||||
for text in texts
|
for text in texts:
|
||||||
]
|
if task_type == "retrieval_document":
|
||||||
|
# Provide a title for retrieval_document task
|
||||||
|
contents.append(
|
||||||
|
{"parts": [{"text": "Embedding of a document"}, {"text": text}]}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
contents.append({"parts": [{"text": text}]})
|
||||||
|
|
||||||
|
# Build config
|
||||||
|
config_kwargs = {}
|
||||||
|
if task_type:
|
||||||
|
config_kwargs["task_type"] = task_type.upper() # API expects uppercase
|
||||||
|
|
||||||
|
# Call embed_content for each content
|
||||||
|
embeddings = []
|
||||||
|
for content in contents:
|
||||||
|
config = (
|
||||||
|
types.EmbedContentConfig(**config_kwargs) if config_kwargs else None
|
||||||
|
)
|
||||||
|
response = self.client.models.embed_content(
|
||||||
|
model=self.name,
|
||||||
|
contents=content,
|
||||||
|
config=config,
|
||||||
|
)
|
||||||
|
embeddings.append(response.embeddings[0].values)
|
||||||
|
|
||||||
|
return embeddings
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def client(self):
|
def client(self):
|
||||||
genai = attempt_import_or_raise("google.generativeai", "google.generativeai")
|
attempt_import_or_raise("google.genai", "google-genai")
|
||||||
|
|
||||||
if not os.environ.get("GOOGLE_API_KEY"):
|
if not os.environ.get("GOOGLE_API_KEY"):
|
||||||
api_key_not_found_help("google")
|
api_key_not_found_help("google")
|
||||||
return genai
|
|
||||||
|
from google import genai as genai_module
|
||||||
|
|
||||||
|
return genai_module.Client(api_key=os.environ.get("GOOGLE_API_KEY"))
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
|
||||||
|
|
||||||
"""Full text search index using tantivy-py"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
from typing import List, Tuple, Optional
|
|
||||||
|
|
||||||
import pyarrow as pa
|
|
||||||
|
|
||||||
try:
|
|
||||||
import tantivy
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"Please install tantivy-py `pip install tantivy` to use the full text search feature." # noqa: E501
|
|
||||||
)
|
|
||||||
|
|
||||||
from .table import LanceTable
|
|
||||||
|
|
||||||
|
|
||||||
def create_index(
|
|
||||||
index_path: str,
|
|
||||||
text_fields: List[str],
|
|
||||||
ordering_fields: Optional[List[str]] = None,
|
|
||||||
tokenizer_name: str = "default",
|
|
||||||
) -> tantivy.Index:
|
|
||||||
"""
|
|
||||||
Create a new Index (not populated)
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
index_path : str
|
|
||||||
Path to the index directory
|
|
||||||
text_fields : List[str]
|
|
||||||
List of text fields to index
|
|
||||||
ordering_fields: List[str]
|
|
||||||
List of unsigned type fields to order by at search time
|
|
||||||
tokenizer_name : str, default "default"
|
|
||||||
The tokenizer to use
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
index : tantivy.Index
|
|
||||||
The index object (not yet populated)
|
|
||||||
"""
|
|
||||||
if ordering_fields is None:
|
|
||||||
ordering_fields = []
|
|
||||||
# Declaring our schema.
|
|
||||||
schema_builder = tantivy.SchemaBuilder()
|
|
||||||
# special field that we'll populate with row_id
|
|
||||||
schema_builder.add_integer_field("doc_id", stored=True)
|
|
||||||
# data fields
|
|
||||||
for name in text_fields:
|
|
||||||
schema_builder.add_text_field(name, stored=True, tokenizer_name=tokenizer_name)
|
|
||||||
if ordering_fields:
|
|
||||||
for name in ordering_fields:
|
|
||||||
schema_builder.add_unsigned_field(name, fast=True)
|
|
||||||
schema = schema_builder.build()
|
|
||||||
os.makedirs(index_path, exist_ok=True)
|
|
||||||
index = tantivy.Index(schema, path=index_path)
|
|
||||||
return index
|
|
||||||
|
|
||||||
|
|
||||||
def populate_index(
|
|
||||||
index: tantivy.Index,
|
|
||||||
table: LanceTable,
|
|
||||||
fields: List[str],
|
|
||||||
writer_heap_size: Optional[int] = None,
|
|
||||||
ordering_fields: Optional[List[str]] = None,
|
|
||||||
) -> int:
|
|
||||||
"""
|
|
||||||
Populate an index with data from a LanceTable
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
index : tantivy.Index
|
|
||||||
The index object
|
|
||||||
table : LanceTable
|
|
||||||
The table to index
|
|
||||||
fields : List[str]
|
|
||||||
List of fields to index
|
|
||||||
writer_heap_size : int
|
|
||||||
The writer heap size in bytes, defaults to 1GB
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
int
|
|
||||||
The number of rows indexed
|
|
||||||
"""
|
|
||||||
if ordering_fields is None:
|
|
||||||
ordering_fields = []
|
|
||||||
writer_heap_size = writer_heap_size or 1024 * 1024 * 1024
|
|
||||||
# first check the fields exist and are string or large string type
|
|
||||||
nested = []
|
|
||||||
|
|
||||||
for name in fields:
|
|
||||||
try:
|
|
||||||
f = table.schema.field(name) # raises KeyError if not found
|
|
||||||
except KeyError:
|
|
||||||
f = resolve_path(table.schema, name)
|
|
||||||
nested.append(name)
|
|
||||||
|
|
||||||
if not pa.types.is_string(f.type) and not pa.types.is_large_string(f.type):
|
|
||||||
raise TypeError(f"Field {name} is not a string type")
|
|
||||||
|
|
||||||
# create a tantivy writer
|
|
||||||
writer = index.writer(heap_size=writer_heap_size)
|
|
||||||
# write data into index
|
|
||||||
dataset = table.to_lance()
|
|
||||||
row_id = 0
|
|
||||||
|
|
||||||
max_nested_level = 0
|
|
||||||
if len(nested) > 0:
|
|
||||||
max_nested_level = max([len(name.split(".")) for name in nested])
|
|
||||||
|
|
||||||
for b in dataset.to_batches(columns=fields + ordering_fields):
|
|
||||||
if max_nested_level > 0:
|
|
||||||
b = pa.Table.from_batches([b])
|
|
||||||
for _ in range(max_nested_level - 1):
|
|
||||||
b = b.flatten()
|
|
||||||
for i in range(b.num_rows):
|
|
||||||
doc = tantivy.Document()
|
|
||||||
for name in fields:
|
|
||||||
value = b[name][i].as_py()
|
|
||||||
if value is not None:
|
|
||||||
doc.add_text(name, value)
|
|
||||||
for name in ordering_fields:
|
|
||||||
value = b[name][i].as_py()
|
|
||||||
if value is not None:
|
|
||||||
doc.add_unsigned(name, value)
|
|
||||||
if not doc.is_empty:
|
|
||||||
doc.add_integer("doc_id", row_id)
|
|
||||||
writer.add_document(doc)
|
|
||||||
row_id += 1
|
|
||||||
# commit changes
|
|
||||||
writer.commit()
|
|
||||||
return row_id
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_path(schema, field_name: str) -> pa.Field:
|
|
||||||
"""
|
|
||||||
Resolve a nested field path to a list of field names
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
field_name : str
|
|
||||||
The field name to resolve
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
List[str]
|
|
||||||
The resolved path
|
|
||||||
"""
|
|
||||||
path = field_name.split(".")
|
|
||||||
field = schema.field(path.pop(0))
|
|
||||||
for segment in path:
|
|
||||||
if pa.types.is_struct(field.type):
|
|
||||||
field = field.type.field(segment)
|
|
||||||
else:
|
|
||||||
raise KeyError(f"field {field_name} not found in schema {schema}")
|
|
||||||
return field
|
|
||||||
|
|
||||||
|
|
||||||
def search_index(
|
|
||||||
index: tantivy.Index, query: str, limit: int = 10, ordering_field=None
|
|
||||||
) -> Tuple[Tuple[int], Tuple[float]]:
|
|
||||||
"""
|
|
||||||
Search an index for a query
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
index : tantivy.Index
|
|
||||||
The index object
|
|
||||||
query : str
|
|
||||||
The query string
|
|
||||||
limit : int
|
|
||||||
The maximum number of results to return
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
ids_and_score: list[tuple[int], tuple[float]]
|
|
||||||
A tuple of two tuples, the first containing the document ids
|
|
||||||
and the second containing the scores
|
|
||||||
"""
|
|
||||||
searcher = index.searcher()
|
|
||||||
query = index.parse_query(query)
|
|
||||||
# get top results
|
|
||||||
if ordering_field:
|
|
||||||
results = searcher.search(query, limit, order_by_field=ordering_field)
|
|
||||||
else:
|
|
||||||
results = searcher.search(query, limit)
|
|
||||||
if results.count == 0:
|
|
||||||
return tuple(), tuple()
|
|
||||||
return tuple(
|
|
||||||
zip(
|
|
||||||
*[
|
|
||||||
(searcher.doc(doc_address)["doc_id"][0], score)
|
|
||||||
for score, doc_address in results.hits
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -7,6 +7,7 @@ from typing import Literal, Optional
|
|||||||
from ._lancedb import (
|
from ._lancedb import (
|
||||||
IndexConfig,
|
IndexConfig,
|
||||||
)
|
)
|
||||||
|
from .types import BaseTokenizerType
|
||||||
|
|
||||||
lang_mapping = {
|
lang_mapping = {
|
||||||
"ar": "Arabic",
|
"ar": "Arabic",
|
||||||
@@ -111,8 +112,12 @@ class FTS:
|
|||||||
- "simple": Splits text by whitespace and punctuation.
|
- "simple": Splits text by whitespace and punctuation.
|
||||||
- "whitespace": Split text by whitespace, but not punctuation.
|
- "whitespace": Split text by whitespace, but not punctuation.
|
||||||
- "raw": No tokenization. The entire text is treated as a single token.
|
- "raw": No tokenization. The entire text is treated as a single token.
|
||||||
|
- "ngram": N-gram tokenizer for substring-style matching.
|
||||||
|
- "jieba/*": Jieba tokenizer loaded from Lance's language model home.
|
||||||
|
- "lindera/*": Lindera tokenizer loaded from Lance's language model home.
|
||||||
language : str, default "English"
|
language : str, default "English"
|
||||||
The language to use for tokenization.
|
The language to use for stemming and stop-word removal. This is not the
|
||||||
|
primary way to enable CJK tokenization.
|
||||||
max_token_length : int, default 40
|
max_token_length : int, default 40
|
||||||
The maximum token length to index. Tokens longer than this length will be
|
The maximum token length to index. Tokens longer than this length will be
|
||||||
ignored.
|
ignored.
|
||||||
@@ -127,10 +132,17 @@ class FTS:
|
|||||||
ascii_folding : bool, default True
|
ascii_folding : bool, default True
|
||||||
Whether to fold ASCII characters. This converts accented characters to
|
Whether to fold ASCII characters. This converts accented characters to
|
||||||
their ASCII equivalent. For example, "café" would be converted to "cafe".
|
their ASCII equivalent. For example, "café" would be converted to "cafe".
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
Model-backed tokenizers such as ``jieba/default`` and ``lindera/ipadic``
|
||||||
|
require tokenizer models in Lance's language model home. Set
|
||||||
|
``LANCE_LANGUAGE_MODEL_HOME`` to override the default platform data
|
||||||
|
directory under ``lance/language_models``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with_position: bool = False
|
with_position: bool = False
|
||||||
base_tokenizer: Literal["simple", "raw", "whitespace"] = "simple"
|
base_tokenizer: BaseTokenizerType = "simple"
|
||||||
language: str = "English"
|
language: str = "English"
|
||||||
max_token_length: Optional[int] = 40
|
max_token_length: Optional[int] = 40
|
||||||
lower_case: bool = True
|
lower_case: bool = True
|
||||||
@@ -376,9 +388,98 @@ class HnswSq:
|
|||||||
target_partition_size: Optional[int] = None
|
target_partition_size: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HnswFlat:
|
||||||
|
"""Describe a HNSW-FLAT index configuration.
|
||||||
|
|
||||||
|
HNSW-FLAT stands for Hierarchical Navigable Small World without quantization.
|
||||||
|
It stores raw vectors in the HNSW graph, providing the highest recall among
|
||||||
|
the IVF_HNSW family at the cost of more memory and disk space compared to
|
||||||
|
:class:`HnswSq` or :class:`HnswPq`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
|
||||||
|
distance_type: str, default "l2"
|
||||||
|
|
||||||
|
The distance metric used to train the index.
|
||||||
|
|
||||||
|
The following distance types are available:
|
||||||
|
|
||||||
|
"l2" - Euclidean distance. This is a very common distance metric that
|
||||||
|
accounts for both magnitude and direction when determining the distance
|
||||||
|
between vectors. l2 distance has a range of [0, ∞).
|
||||||
|
|
||||||
|
"cosine" - Cosine distance. Cosine distance is a distance metric
|
||||||
|
calculated from the cosine similarity between two vectors. Cosine
|
||||||
|
similarity is a measure of similarity between two non-zero vectors of an
|
||||||
|
inner product space. It is defined to equal the cosine of the angle
|
||||||
|
between them. Unlike l2, the cosine distance is not affected by the
|
||||||
|
magnitude of the vectors. Cosine distance has a range of [0, 2].
|
||||||
|
|
||||||
|
"dot" - Dot product. Dot distance is the dot product of two vectors. Dot
|
||||||
|
distance has a range of (-∞, ∞). If the vectors are normalized (i.e. their
|
||||||
|
l2 norm is 1), then dot distance is equivalent to the cosine distance.
|
||||||
|
|
||||||
|
num_partitions, default sqrt(num_rows)
|
||||||
|
|
||||||
|
The number of IVF partitions to create.
|
||||||
|
|
||||||
|
For HNSW, we recommend a small number of partitions. Setting this to 1
|
||||||
|
works well for most tables. For very large tables, training just one HNSW
|
||||||
|
graph will require too much memory. Each partition becomes its own HNSW
|
||||||
|
graph, so setting this value higher reduces the peak memory use of
|
||||||
|
training.
|
||||||
|
|
||||||
|
max_iterations, default 50
|
||||||
|
|
||||||
|
Max iterations to train kmeans.
|
||||||
|
|
||||||
|
When training an IVF index we use kmeans to calculate the partitions.
|
||||||
|
This parameter controls how many iterations of kmeans to run.
|
||||||
|
|
||||||
|
sample_rate, default 256
|
||||||
|
|
||||||
|
The rate used to calculate the number of training vectors for kmeans.
|
||||||
|
|
||||||
|
m, default 20
|
||||||
|
|
||||||
|
The number of neighbors to select for each vector in the HNSW graph.
|
||||||
|
|
||||||
|
This value controls the tradeoff between search speed and accuracy.
|
||||||
|
The higher the value the more accurate the search but the slower it
|
||||||
|
will be.
|
||||||
|
|
||||||
|
ef_construction, default 300
|
||||||
|
|
||||||
|
The number of candidates to evaluate during the construction of the HNSW
|
||||||
|
graph.
|
||||||
|
|
||||||
|
This value controls the tradeoff between build speed and accuracy.
|
||||||
|
The higher the value the more accurate the build but the slower it will
|
||||||
|
be. 150 to 300 is the typical range. 100 is a minimum for good quality
|
||||||
|
search results. In most cases, there is no benefit to setting this higher
|
||||||
|
than 500. This value should be set to a value that is not less than `ef`
|
||||||
|
in the search phase.
|
||||||
|
|
||||||
|
target_partition_size, default is 1,048,576
|
||||||
|
|
||||||
|
The target size of each partition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
distance_type: Literal["l2", "cosine", "dot"] = "l2"
|
||||||
|
num_partitions: Optional[int] = None
|
||||||
|
max_iterations: int = 50
|
||||||
|
sample_rate: int = 256
|
||||||
|
m: int = 20
|
||||||
|
ef_construction: int = 300
|
||||||
|
target_partition_size: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
# Backwards-compatible aliases
|
# Backwards-compatible aliases
|
||||||
IvfHnswPq = HnswPq
|
IvfHnswPq = HnswPq
|
||||||
IvfHnswSq = HnswSq
|
IvfHnswSq = HnswSq
|
||||||
|
IvfHnswFlat = HnswFlat
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -698,11 +799,13 @@ __all__ = [
|
|||||||
"IvfPq",
|
"IvfPq",
|
||||||
"IvfHnswPq",
|
"IvfHnswPq",
|
||||||
"IvfHnswSq",
|
"IvfHnswSq",
|
||||||
|
"IvfHnswFlat",
|
||||||
"IvfSq",
|
"IvfSq",
|
||||||
"IvfRq",
|
"IvfRq",
|
||||||
"IvfFlat",
|
"IvfFlat",
|
||||||
"HnswPq",
|
"HnswPq",
|
||||||
"HnswSq",
|
"HnswSq",
|
||||||
|
"HnswFlat",
|
||||||
"IndexConfig",
|
"IndexConfig",
|
||||||
"FTS",
|
"FTS",
|
||||||
"Bitmap",
|
"Bitmap",
|
||||||
|
|||||||
@@ -2,70 +2,3 @@
|
|||||||
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
"""I/O utilities and interfaces for LanceDB."""
|
"""I/O utilities and interfaces for LanceDB."""
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from typing import Dict
|
|
||||||
|
|
||||||
|
|
||||||
class StorageOptionsProvider(ABC):
|
|
||||||
"""Abstract base class for providing storage options to LanceDB tables.
|
|
||||||
|
|
||||||
Storage options providers enable automatic credential refresh for cloud
|
|
||||||
storage backends (e.g., AWS S3, Azure Blob Storage, GCS). When credentials
|
|
||||||
have an expiration time, the provider's fetch_storage_options() method will
|
|
||||||
be called periodically to get fresh credentials before they expire.
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
>>> class MyProvider(StorageOptionsProvider):
|
|
||||||
... def fetch_storage_options(self) -> Dict[str, str]:
|
|
||||||
... # Fetch fresh credentials from your credential manager
|
|
||||||
... return {
|
|
||||||
... "aws_access_key_id": "...",
|
|
||||||
... "aws_secret_access_key": "...",
|
|
||||||
... "expires_at_millis": "1234567890000" # Optional
|
|
||||||
... }
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def fetch_storage_options(self) -> Dict[str, str]:
|
|
||||||
"""Fetch fresh storage credentials.
|
|
||||||
|
|
||||||
This method is called by LanceDB when credentials need to be refreshed.
|
|
||||||
If the returned dictionary contains an "expires_at_millis" key with a
|
|
||||||
Unix timestamp in milliseconds, LanceDB will automatically refresh the
|
|
||||||
credentials before that time. If the key is not present, credentials
|
|
||||||
are assumed to not expire.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Dict[str, str]
|
|
||||||
Dictionary containing cloud storage credentials and optionally an
|
|
||||||
expiration time:
|
|
||||||
- "expires_at_millis" (optional): Unix timestamp in milliseconds when
|
|
||||||
credentials expire
|
|
||||||
- Provider-specific credential keys (e.g., aws_access_key_id,
|
|
||||||
aws_secret_access_key, etc.)
|
|
||||||
|
|
||||||
Raises
|
|
||||||
------
|
|
||||||
RuntimeError
|
|
||||||
If credentials cannot be fetched or are invalid
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def provider_id(self) -> str:
|
|
||||||
"""Return a human-readable unique identifier for this provider instance.
|
|
||||||
|
|
||||||
This identifier is used for caching and equality comparison. Two providers
|
|
||||||
with the same ID will share the same cached object store connection.
|
|
||||||
|
|
||||||
The default implementation uses the class name and string representation.
|
|
||||||
Override this method if you need custom identification logic.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
A unique identifier for this provider instance
|
|
||||||
"""
|
|
||||||
return f"{self.__class__.__name__} {{ repr: {str(self)!r} }}"
|
|
||||||
|
|||||||
+607
-470
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
from deprecation import deprecated
|
import copy
|
||||||
from lancedb import AsyncConnection, DBConnection
|
|
||||||
import pyarrow as pa
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from deprecation import deprecated
|
||||||
|
import pyarrow as pa
|
||||||
|
|
||||||
from ._lancedb import async_permutation_builder, PermutationReader
|
from ._lancedb import async_permutation_builder, PermutationReader
|
||||||
from .table import LanceTable
|
from .table import LanceTable
|
||||||
from .background_loop import LOOP
|
from .background_loop import LOOP
|
||||||
@@ -36,10 +37,7 @@ class PermutationBuilder:
|
|||||||
be referenced by name in the future. If names are not provided then they can only
|
be referenced by name in the future. If names are not provided then they can only
|
||||||
be referenced by their ordinal index. There is no requirement to name every split.
|
be referenced by their ordinal index. There is no requirement to name every split.
|
||||||
|
|
||||||
By default, the permutation will be stored in memory and will be lost when the
|
The permutation is stored in memory and will be lost when the program exits.
|
||||||
program exits. To persist the permutation (for very large datasets or to share
|
|
||||||
the permutation across multiple workers) use the [persist](#persist) method to
|
|
||||||
create a permanent table.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, table: LanceTable):
|
def __init__(self, table: LanceTable):
|
||||||
@@ -51,15 +49,6 @@ class PermutationBuilder:
|
|||||||
"""
|
"""
|
||||||
self._async = async_permutation_builder(table)
|
self._async = async_permutation_builder(table)
|
||||||
|
|
||||||
def persist(
|
|
||||||
self, database: Union[DBConnection, AsyncConnection], table_name: str
|
|
||||||
) -> "PermutationBuilder":
|
|
||||||
"""
|
|
||||||
Persist the permutation to the given database.
|
|
||||||
"""
|
|
||||||
self._async.persist(database, table_name)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def split_random(
|
def split_random(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -284,9 +273,8 @@ class Permutations:
|
|||||||
self.permutation_table = permutation_table
|
self.permutation_table = permutation_table
|
||||||
|
|
||||||
if permutation_table.schema.metadata is not None:
|
if permutation_table.schema.metadata is not None:
|
||||||
split_names = permutation_table.schema.metadata.get(
|
raw = permutation_table.schema.metadata.get(b"split_names")
|
||||||
b"split_names", None
|
split_names = raw.decode("utf-8") if raw is not None else None
|
||||||
).decode("utf-8")
|
|
||||||
if split_names is not None:
|
if split_names is not None:
|
||||||
self.split_names = json.loads(split_names)
|
self.split_names = json.loads(split_names)
|
||||||
self.split_dict = {
|
self.split_dict = {
|
||||||
@@ -381,20 +369,44 @@ class Permutation:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
reader: PermutationReader,
|
base_table: LanceTable,
|
||||||
|
permutation_table: Optional[LanceTable],
|
||||||
|
split: int,
|
||||||
selection: dict[str, str],
|
selection: dict[str, str],
|
||||||
batch_size: int,
|
batch_size: int,
|
||||||
transform_fn: Callable[pa.RecordBatch, Any],
|
transform_fn: Callable[pa.RecordBatch, Any],
|
||||||
|
offset: Optional[int] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
connection_factory: Optional[Callable[[str], LanceTable]] = None,
|
||||||
|
_reader: Optional[PermutationReader] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Internal constructor. Use [from_tables](#from_tables) instead.
|
Internal constructor. Use [from_tables](#from_tables) instead.
|
||||||
"""
|
"""
|
||||||
assert reader is not None, "reader is required"
|
assert base_table is not None, "base_table is required"
|
||||||
assert selection is not None, "selection is required"
|
assert selection is not None, "selection is required"
|
||||||
self.reader = reader
|
self.base_table = base_table
|
||||||
|
self.permutation_table = permutation_table
|
||||||
|
self.split = split
|
||||||
self.selection = selection
|
self.selection = selection
|
||||||
self.transform_fn = transform_fn
|
self.transform_fn = transform_fn
|
||||||
self.batch_size = batch_size
|
self.batch_size = batch_size
|
||||||
|
self.offset = offset
|
||||||
|
self.limit = limit
|
||||||
|
self.connection_factory = connection_factory
|
||||||
|
if _reader is None:
|
||||||
|
_reader = LOOP.run(self._build_reader())
|
||||||
|
self.reader: PermutationReader = _reader
|
||||||
|
|
||||||
|
async def _build_reader(self) -> PermutationReader:
|
||||||
|
reader = await PermutationReader.from_tables(
|
||||||
|
self.base_table, self.permutation_table, self.split
|
||||||
|
)
|
||||||
|
if self.offset is not None:
|
||||||
|
reader = await reader.with_offset(self.offset)
|
||||||
|
if self.limit is not None:
|
||||||
|
reader = await reader.with_limit(self.limit)
|
||||||
|
return reader
|
||||||
|
|
||||||
def _with_selection(self, selection: dict[str, str]) -> "Permutation":
|
def _with_selection(self, selection: dict[str, str]) -> "Permutation":
|
||||||
"""
|
"""
|
||||||
@@ -403,21 +415,97 @@ class Permutation:
|
|||||||
Does not validation of the selection and it replaces it entirely. This is not
|
Does not validation of the selection and it replaces it entirely. This is not
|
||||||
intended for public use.
|
intended for public use.
|
||||||
"""
|
"""
|
||||||
return Permutation(self.reader, selection, self.batch_size, self.transform_fn)
|
new = copy.copy(self)
|
||||||
|
new.selection = selection
|
||||||
def _with_reader(self, reader: PermutationReader) -> "Permutation":
|
return new
|
||||||
"""
|
|
||||||
Creates a new permutation with the given reader
|
|
||||||
|
|
||||||
This is an internal method and should not be used directly.
|
|
||||||
"""
|
|
||||||
return Permutation(reader, self.selection, self.batch_size, self.transform_fn)
|
|
||||||
|
|
||||||
def with_batch_size(self, batch_size: int) -> "Permutation":
|
def with_batch_size(self, batch_size: int) -> "Permutation":
|
||||||
"""
|
"""
|
||||||
Creates a new permutation with the given batch size
|
Creates a new permutation with the given batch size
|
||||||
"""
|
"""
|
||||||
return Permutation(self.reader, self.selection, batch_size, self.transform_fn)
|
new = copy.copy(self)
|
||||||
|
new.batch_size = batch_size
|
||||||
|
return new
|
||||||
|
|
||||||
|
def with_connection_factory(
|
||||||
|
self, connection_factory: Callable[[str], LanceTable]
|
||||||
|
) -> "Permutation":
|
||||||
|
"""
|
||||||
|
Creates a new permutation that will use ``connection_factory`` to reopen
|
||||||
|
the base table when this permutation is unpickled in a worker process.
|
||||||
|
|
||||||
|
The factory is a callable that takes a single argument — the base table
|
||||||
|
name — and returns a [LanceTable]. It must be picklable; the worker
|
||||||
|
will pickle it via standard ``pickle`` and call it to recover the base
|
||||||
|
table. Picklable callables in practice means top-level (module-level)
|
||||||
|
functions, ``functools.partial`` of such functions, or instances of
|
||||||
|
picklable classes implementing ``__call__``. Lambdas and closures over
|
||||||
|
local variables don't pickle with the default protocol.
|
||||||
|
|
||||||
|
Setting a factory is necessary when the URI alone is not enough to
|
||||||
|
re-open the connection — most importantly for LanceDB Cloud (``db://``)
|
||||||
|
connections, where ``api_key`` and ``region`` aren't recoverable from
|
||||||
|
the connection object after construction.
|
||||||
|
|
||||||
|
For local file or cloud-storage paths the factory is optional: if not
|
||||||
|
set, ``__getstate__`` falls back to capturing
|
||||||
|
``(uri, storage_options, namespace_path)`` and re-opening via
|
||||||
|
``lancedb.connect(uri, storage_options=...)``.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
Basic native (file-system path), parameterized via ``functools.partial``::
|
||||||
|
|
||||||
|
import functools, lancedb
|
||||||
|
from lancedb.permutation import Permutation
|
||||||
|
|
||||||
|
def open_native_table(uri: str, table_name: str):
|
||||||
|
return lancedb.connect(uri).open_table(table_name)
|
||||||
|
|
||||||
|
factory = functools.partial(open_native_table, "/data/lance_db")
|
||||||
|
permutation = Permutation.identity(
|
||||||
|
factory("training")
|
||||||
|
).with_connection_factory(factory)
|
||||||
|
|
||||||
|
Native via :func:`lancedb.connect_namespace` (e.g. a directory- or
|
||||||
|
REST-backed namespace client). The factory takes the
|
||||||
|
implementation name and properties dict as partial-bound args so
|
||||||
|
the worker can rebuild the same namespace connection::
|
||||||
|
|
||||||
|
def open_via_namespace(
|
||||||
|
impl: str, properties: dict[str, str], table_name: str,
|
||||||
|
):
|
||||||
|
return lancedb.connect_namespace(impl, properties).open_table(
|
||||||
|
table_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
factory = functools.partial(
|
||||||
|
open_via_namespace,
|
||||||
|
"dir",
|
||||||
|
{"root": "/data/lance_db"},
|
||||||
|
)
|
||||||
|
|
||||||
|
LanceDB Cloud, reading credentials from env vars at worker startup
|
||||||
|
so secrets aren't pickled into the dataset::
|
||||||
|
|
||||||
|
import os, lancedb
|
||||||
|
|
||||||
|
def open_remote_table(table_name: str):
|
||||||
|
db = lancedb.connect(
|
||||||
|
"db://my-database",
|
||||||
|
api_key=os.environ["LANCEDB_API_KEY"],
|
||||||
|
region=os.environ.get("LANCEDB_REGION", "us-east-1"),
|
||||||
|
)
|
||||||
|
return db.open_table(table_name)
|
||||||
|
|
||||||
|
permutation = Permutation.identity(
|
||||||
|
open_remote_table("training")
|
||||||
|
).with_connection_factory(open_remote_table)
|
||||||
|
"""
|
||||||
|
assert connection_factory is not None, "connection_factory is required"
|
||||||
|
new = copy.copy(self)
|
||||||
|
new.connection_factory = connection_factory
|
||||||
|
return new
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def identity(cls, table: LanceTable) -> "Permutation":
|
def identity(cls, table: LanceTable) -> "Permutation":
|
||||||
@@ -460,9 +548,8 @@ class Permutation:
|
|||||||
f"Cannot create a permutation on split `{split}`"
|
f"Cannot create a permutation on split `{split}`"
|
||||||
" because no split names are defined in the permutation table"
|
" because no split names are defined in the permutation table"
|
||||||
)
|
)
|
||||||
split_names = permutation_table.schema.metadata.get(
|
raw = permutation_table.schema.metadata.get(b"split_names")
|
||||||
b"split_names", None
|
split_names = raw.decode("utf-8") if raw is not None else None
|
||||||
).decode("utf-8")
|
|
||||||
if split_names is None:
|
if split_names is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Cannot create a permutation on split `{split}`"
|
f"Cannot create a permutation on split `{split}`"
|
||||||
@@ -491,11 +578,126 @@ class Permutation:
|
|||||||
schema = await reader.output_schema(None)
|
schema = await reader.output_schema(None)
|
||||||
initial_selection = {name: name for name in schema.names}
|
initial_selection = {name: name for name in schema.names}
|
||||||
return cls(
|
return cls(
|
||||||
reader, initial_selection, DEFAULT_BATCH_SIZE, Transforms.arrow2python
|
base_table,
|
||||||
|
permutation_table,
|
||||||
|
split,
|
||||||
|
initial_selection,
|
||||||
|
DEFAULT_BATCH_SIZE,
|
||||||
|
Transforms.arrow2python,
|
||||||
|
_reader=reader,
|
||||||
)
|
)
|
||||||
|
|
||||||
return LOOP.run(do_from_tables())
|
return LOOP.run(do_from_tables())
|
||||||
|
|
||||||
|
def __getstate__(self) -> dict[str, Any]:
|
||||||
|
"""Build a picklable state dict for this permutation.
|
||||||
|
|
||||||
|
The base table is captured either via a user-supplied
|
||||||
|
``connection_factory`` (see [with_connection_factory]) or, as a
|
||||||
|
fallback, by introspecting ``(uri, storage_options, namespace_path)``
|
||||||
|
on the connection. The permutation table — always an in-memory
|
||||||
|
LanceDB table — is captured as a pyarrow Table (which pickles via
|
||||||
|
Arrow IPC natively). The reader is dropped from the wire format;
|
||||||
|
``__setstate__`` rebuilds it from the restored tables.
|
||||||
|
"""
|
||||||
|
permutation_data: Optional[pa.Table] = None
|
||||||
|
if self.permutation_table is not None:
|
||||||
|
permutation_data = self.permutation_table.to_arrow()
|
||||||
|
|
||||||
|
common = {
|
||||||
|
"base_table_name": self.base_table.name,
|
||||||
|
"permutation_data": permutation_data,
|
||||||
|
"split": self.split,
|
||||||
|
"selection": self.selection,
|
||||||
|
"batch_size": self.batch_size,
|
||||||
|
"transform_fn": self.transform_fn,
|
||||||
|
"offset": self.offset,
|
||||||
|
"limit": self.limit,
|
||||||
|
"connection_factory": self.connection_factory,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.connection_factory is not None:
|
||||||
|
# The factory carries enough state to recover the base table on
|
||||||
|
# its own; we don't need to capture the URI / storage options /
|
||||||
|
# namespace from the existing connection.
|
||||||
|
return common
|
||||||
|
|
||||||
|
# URI-introspection fallback: only viable for native (OSS) connections
|
||||||
|
# where (uri, storage_options) is enough to reopen. Remote / cloud
|
||||||
|
# connections don't expose recoverable api_key / region — those users
|
||||||
|
# must call with_connection_factory().
|
||||||
|
try:
|
||||||
|
base_uri = self.base_table._conn.uri
|
||||||
|
storage_options = self.base_table._conn.storage_options
|
||||||
|
except AttributeError as e:
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot pickle this Permutation: the base table's connection "
|
||||||
|
"does not expose a uri/storage_options, which usually means it "
|
||||||
|
"is a remote (LanceDB Cloud) connection. Call "
|
||||||
|
"Permutation.with_connection_factory(...) first to provide a "
|
||||||
|
"picklable callable that re-opens the base table from a worker "
|
||||||
|
"process."
|
||||||
|
) from e
|
||||||
|
|
||||||
|
if base_uri.startswith("memory://"):
|
||||||
|
# In-memory base tables don't exist in any worker process by
|
||||||
|
# default, so dump the entire base table into the pickle. This
|
||||||
|
# can be expensive for large datasets — users with large
|
||||||
|
# in-memory base tables should either persist them or set a
|
||||||
|
# connection_factory.
|
||||||
|
return {
|
||||||
|
**common,
|
||||||
|
"base_table_data": self.base_table.to_arrow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
**common,
|
||||||
|
"base_table_uri": base_uri,
|
||||||
|
"base_table_namespace": self.base_table._namespace_path,
|
||||||
|
"base_table_storage_options": storage_options,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||||
|
from . import connect
|
||||||
|
|
||||||
|
connection_factory = state["connection_factory"]
|
||||||
|
if connection_factory is not None:
|
||||||
|
base_table = connection_factory(state["base_table_name"])
|
||||||
|
elif "base_table_data" in state:
|
||||||
|
# In-memory base table inlined into the pickle; rebuild the same
|
||||||
|
# way we rebuild the in-memory permutation table.
|
||||||
|
mem_db = connect("memory://")
|
||||||
|
base_table = mem_db.create_table(
|
||||||
|
state["base_table_name"], state["base_table_data"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
base_db = connect(
|
||||||
|
state["base_table_uri"],
|
||||||
|
storage_options=state["base_table_storage_options"],
|
||||||
|
)
|
||||||
|
base_table = base_db.open_table(
|
||||||
|
state["base_table_name"],
|
||||||
|
namespace_path=state["base_table_namespace"] or None,
|
||||||
|
)
|
||||||
|
|
||||||
|
permutation_table: Optional[LanceTable] = None
|
||||||
|
if state["permutation_data"] is not None:
|
||||||
|
mem_db = connect("memory://")
|
||||||
|
permutation_table = mem_db.create_table(
|
||||||
|
"permutation", state["permutation_data"]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.base_table = base_table
|
||||||
|
self.permutation_table = permutation_table
|
||||||
|
self.split = state["split"]
|
||||||
|
self.selection = state["selection"]
|
||||||
|
self.batch_size = state["batch_size"]
|
||||||
|
self.transform_fn = state["transform_fn"]
|
||||||
|
self.offset = state["offset"]
|
||||||
|
self.limit = state["limit"]
|
||||||
|
self.connection_factory = connection_factory
|
||||||
|
self.reader = LOOP.run(self._build_reader())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def schema(self) -> pa.Schema:
|
def schema(self) -> pa.Schema:
|
||||||
async def do_output_schema():
|
async def do_output_schema():
|
||||||
@@ -762,7 +964,9 @@ class Permutation:
|
|||||||
for expensive operations such as image decoding.
|
for expensive operations such as image decoding.
|
||||||
"""
|
"""
|
||||||
assert transform is not None, "transform is required"
|
assert transform is not None, "transform is required"
|
||||||
return Permutation(self.reader, self.selection, self.batch_size, transform)
|
new = copy.copy(self)
|
||||||
|
new.transform_fn = transform
|
||||||
|
return new
|
||||||
|
|
||||||
def __getitem__(self, index: int) -> Any:
|
def __getitem__(self, index: int) -> Any:
|
||||||
"""
|
"""
|
||||||
@@ -797,12 +1001,10 @@ class Permutation:
|
|||||||
"""
|
"""
|
||||||
Skip the first `skip` rows of the permutation
|
Skip the first `skip` rows of the permutation
|
||||||
"""
|
"""
|
||||||
|
new = copy.copy(self)
|
||||||
async def do_with_skip():
|
new.offset = skip
|
||||||
reader = await self.reader.with_offset(skip)
|
new.reader = LOOP.run(new._build_reader())
|
||||||
return self._with_reader(reader)
|
return new
|
||||||
|
|
||||||
return LOOP.run(do_with_skip())
|
|
||||||
|
|
||||||
@deprecated(details="Use with_take instead")
|
@deprecated(details="Use with_take instead")
|
||||||
def take(self, limit: int) -> "Permutation":
|
def take(self, limit: int) -> "Permutation":
|
||||||
@@ -820,12 +1022,10 @@ class Permutation:
|
|||||||
"""
|
"""
|
||||||
Limit the permutation to `limit` rows (following any `skip`)
|
Limit the permutation to `limit` rows (following any `skip`)
|
||||||
"""
|
"""
|
||||||
|
new = copy.copy(self)
|
||||||
async def do_with_take():
|
new.limit = limit
|
||||||
reader = await self.reader.with_limit(limit)
|
new.reader = LOOP.run(new._build_reader())
|
||||||
return self._with_reader(reader)
|
return new
|
||||||
|
|
||||||
return LOOP.run(do_with_take())
|
|
||||||
|
|
||||||
@deprecated(details="Use with_repeat instead")
|
@deprecated(details="Use with_repeat instead")
|
||||||
def repeat(self, times: int) -> "Permutation":
|
def repeat(self, times: int) -> "Permutation":
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import sys
|
|||||||
import types
|
import types
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
|
from enum import Enum
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
@@ -314,6 +315,19 @@ def _pydantic_type_to_arrow_type(tp: Any, field: FieldInfo) -> pa.DataType:
|
|||||||
return pa.list_(pa.list_(tp.value_arrow_type(), tp.dim()))
|
return pa.list_(pa.list_(tp.value_arrow_type(), tp.dim()))
|
||||||
# For regular Vector
|
# For regular Vector
|
||||||
return pa.list_(tp.value_arrow_type(), tp.dim())
|
return pa.list_(tp.value_arrow_type(), tp.dim())
|
||||||
|
if _safe_issubclass(tp, Enum):
|
||||||
|
# Map Enum to the Arrow type of its value.
|
||||||
|
# For string-valued enums, use dictionary encoding for efficiency.
|
||||||
|
# For integer enums, use the native type.
|
||||||
|
# Fall back to utf8 for mixed-type or empty enums.
|
||||||
|
value_types = {type(m.value) for m in tp}
|
||||||
|
if len(value_types) == 1:
|
||||||
|
value_type = value_types.pop()
|
||||||
|
if value_type is str:
|
||||||
|
# Use dictionary encoding for string enums
|
||||||
|
return pa.dictionary(pa.int32(), pa.utf8())
|
||||||
|
return _py_type_to_arrow_type(value_type, field)
|
||||||
|
return pa.utf8()
|
||||||
return _py_type_to_arrow_type(tp, field)
|
return _py_type_to_arrow_type(tp, field)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import deprecation
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pyarrow as pa
|
import pyarrow as pa
|
||||||
import pyarrow.compute as pc
|
import pyarrow.compute as pc
|
||||||
import pyarrow.fs as pa_fs
|
|
||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
from lancedb.pydantic import PYDANTIC_VERSION
|
from lancedb.pydantic import PYDANTIC_VERSION
|
||||||
@@ -1526,9 +1525,7 @@ class LanceFtsQueryBuilder(LanceQueryBuilder):
|
|||||||
return self._table._output_schema(self.to_query_object())
|
return self._table._output_schema(self.to_query_object())
|
||||||
|
|
||||||
def to_arrow(self, *, timeout: Optional[timedelta] = None) -> pa.Table:
|
def to_arrow(self, *, timeout: Optional[timedelta] = None) -> pa.Table:
|
||||||
path, fs, exist = self._table._get_fts_index_path()
|
self._table._ensure_no_legacy_fts_index()
|
||||||
if exist:
|
|
||||||
return self.tantivy_to_arrow()
|
|
||||||
|
|
||||||
query = self._query
|
query = self._query
|
||||||
if self._phrase_query:
|
if self._phrase_query:
|
||||||
@@ -1552,90 +1549,6 @@ class LanceFtsQueryBuilder(LanceQueryBuilder):
|
|||||||
):
|
):
|
||||||
raise NotImplementedError("to_batches on an FTS query")
|
raise NotImplementedError("to_batches on an FTS query")
|
||||||
|
|
||||||
def tantivy_to_arrow(self) -> pa.Table:
|
|
||||||
try:
|
|
||||||
import tantivy
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError(
|
|
||||||
"Please install tantivy-py `pip install tantivy` to use the full text search feature." # noqa: E501
|
|
||||||
)
|
|
||||||
|
|
||||||
from .fts import search_index
|
|
||||||
|
|
||||||
# get the index path
|
|
||||||
path, fs, exist = self._table._get_fts_index_path()
|
|
||||||
|
|
||||||
# check if the index exist
|
|
||||||
if not exist:
|
|
||||||
raise FileNotFoundError(
|
|
||||||
"Fts index does not exist. "
|
|
||||||
"Please first call table.create_fts_index(['<field_names>']) to "
|
|
||||||
"create the fts index."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check that we are on local filesystem
|
|
||||||
if not isinstance(fs, pa_fs.LocalFileSystem):
|
|
||||||
raise NotImplementedError(
|
|
||||||
"Tantivy-based full text search "
|
|
||||||
"is only supported on the local filesystem"
|
|
||||||
)
|
|
||||||
# open the index
|
|
||||||
index = tantivy.Index.open(path)
|
|
||||||
# get the scores and doc ids
|
|
||||||
query = self._query
|
|
||||||
if self._phrase_query:
|
|
||||||
query = query.replace('"', "'")
|
|
||||||
query = f'"{query}"'
|
|
||||||
limit = self._limit if self._limit is not None else 10
|
|
||||||
row_ids, scores = search_index(
|
|
||||||
index, query, limit, ordering_field=self.ordering_field_name
|
|
||||||
)
|
|
||||||
if len(row_ids) == 0:
|
|
||||||
empty_schema = pa.schema([pa.field("_score", pa.float32())])
|
|
||||||
return pa.Table.from_batches([], schema=empty_schema)
|
|
||||||
scores = pa.array(scores)
|
|
||||||
output_tbl = self._table.to_lance().take(row_ids, columns=self._columns)
|
|
||||||
output_tbl = output_tbl.append_column("_score", scores)
|
|
||||||
# this needs to match vector search results which are uint64
|
|
||||||
row_ids = pa.array(row_ids, type=pa.uint64())
|
|
||||||
|
|
||||||
if self._where is not None:
|
|
||||||
tmp_name = "__lancedb__duckdb__indexer__"
|
|
||||||
output_tbl = output_tbl.append_column(
|
|
||||||
tmp_name, pa.array(range(len(output_tbl)))
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
# TODO would be great to have Substrait generate pyarrow compute
|
|
||||||
# expressions or conversely have pyarrow support SQL expressions
|
|
||||||
# using Substrait
|
|
||||||
import duckdb
|
|
||||||
|
|
||||||
indexer = duckdb.sql(
|
|
||||||
f"SELECT {tmp_name} FROM output_tbl WHERE {self._where}"
|
|
||||||
).to_arrow_table()[tmp_name]
|
|
||||||
output_tbl = output_tbl.take(indexer).drop([tmp_name])
|
|
||||||
row_ids = row_ids.take(indexer)
|
|
||||||
|
|
||||||
except ImportError:
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import lance
|
|
||||||
|
|
||||||
# TODO Use "memory://" instead once that's supported
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
|
||||||
ds = lance.write_dataset(output_tbl, tmp)
|
|
||||||
output_tbl = ds.to_table(filter=self._where)
|
|
||||||
indexer = output_tbl[tmp_name]
|
|
||||||
row_ids = row_ids.take(indexer)
|
|
||||||
output_tbl = output_tbl.drop([tmp_name])
|
|
||||||
|
|
||||||
if self._with_row_id:
|
|
||||||
output_tbl = output_tbl.append_column("_rowid", row_ids)
|
|
||||||
|
|
||||||
if self._reranker is not None:
|
|
||||||
output_tbl = self._reranker.rerank_fts(self._query, output_tbl)
|
|
||||||
return output_tbl
|
|
||||||
|
|
||||||
def rerank(self, reranker: Reranker) -> LanceFtsQueryBuilder:
|
def rerank(self, reranker: Reranker) -> LanceFtsQueryBuilder:
|
||||||
"""Rerank the results using the specified reranker.
|
"""Rerank the results using the specified reranker.
|
||||||
|
|
||||||
@@ -1730,7 +1643,7 @@ class LanceHybridQueryBuilder(LanceQueryBuilder):
|
|||||||
def _validate_query(self, query, vector=None, text=None):
|
def _validate_query(self, query, vector=None, text=None):
|
||||||
if query is not None and (vector is not None or text is not None):
|
if query is not None and (vector is not None or text is not None):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"You can either provide a string query in search() method"
|
"You can either provide a string query in search() method "
|
||||||
"or set `vector()` and `text()` explicitly for hybrid search."
|
"or set `vector()` and `text()` explicitly for hybrid search."
|
||||||
"But not both."
|
"But not both."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -145,6 +145,33 @@ class TlsConfig:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ClientConfig:
|
class ClientConfig:
|
||||||
|
"""Configuration for the LanceDB Cloud HTTP client.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
user_agent: str
|
||||||
|
User agent string sent with requests.
|
||||||
|
retry_config: RetryConfig
|
||||||
|
Configuration for retrying failed requests.
|
||||||
|
timeout_config: Optional[TimeoutConfig]
|
||||||
|
Configuration for request timeouts.
|
||||||
|
extra_headers: Optional[dict]
|
||||||
|
Additional headers to include in requests.
|
||||||
|
id_delimiter: Optional[str]
|
||||||
|
The delimiter to use when constructing object identifiers.
|
||||||
|
tls_config: Optional[TlsConfig]
|
||||||
|
TLS/mTLS configuration for secure connections.
|
||||||
|
header_provider: Optional[HeaderProvider]
|
||||||
|
Provider for dynamic headers to be added to each request.
|
||||||
|
user_id: Optional[str]
|
||||||
|
User identifier for tracking purposes. This is sent as the
|
||||||
|
`x-lancedb-user-id` header in requests to LanceDB Cloud/Enterprise.
|
||||||
|
|
||||||
|
This can also be set via the `LANCEDB_USER_ID` environment variable.
|
||||||
|
Alternatively, set `LANCEDB_USER_ID_ENV_KEY` to specify another
|
||||||
|
environment variable that contains the user ID value.
|
||||||
|
"""
|
||||||
|
|
||||||
user_agent: str = f"LanceDB-Python-Client/{__version__}"
|
user_agent: str = f"LanceDB-Python-Client/{__version__}"
|
||||||
retry_config: RetryConfig = field(default_factory=RetryConfig)
|
retry_config: RetryConfig = field(default_factory=RetryConfig)
|
||||||
timeout_config: Optional[TimeoutConfig] = field(default_factory=TimeoutConfig)
|
timeout_config: Optional[TimeoutConfig] = field(default_factory=TimeoutConfig)
|
||||||
@@ -152,6 +179,7 @@ class ClientConfig:
|
|||||||
id_delimiter: Optional[str] = None
|
id_delimiter: Optional[str] = None
|
||||||
tls_config: Optional[TlsConfig] = None
|
tls_config: Optional[TlsConfig] = None
|
||||||
header_provider: Optional["HeaderProvider"] = None
|
header_provider: Optional["HeaderProvider"] = None
|
||||||
|
user_id: Optional[str] = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if isinstance(self.retry_config, dict):
|
if isinstance(self.retry_config, dict):
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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 (
|
from lance_namespace import (
|
||||||
|
LanceNamespace,
|
||||||
CreateNamespaceResponse,
|
CreateNamespaceResponse,
|
||||||
DescribeNamespaceResponse,
|
DescribeNamespaceResponse,
|
||||||
DropNamespaceResponse,
|
DropNamespaceResponse,
|
||||||
@@ -111,7 +112,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
@override
|
@override
|
||||||
def list_namespaces(
|
def list_namespaces(
|
||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
) -> ListNamespacesResponse:
|
) -> ListNamespacesResponse:
|
||||||
@@ -119,7 +120,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str], optional
|
namespace_path: List[str], optional
|
||||||
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
|
||||||
@@ -133,18 +134,18 @@ class RemoteDBConnection(DBConnection):
|
|||||||
ListNamespacesResponse
|
ListNamespacesResponse
|
||||||
Response containing namespace names and optional page_token for pagination.
|
Response containing namespace names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
self._conn.list_namespaces(
|
self._conn.list_namespaces(
|
||||||
namespace=namespace, page_token=page_token, limit=limit
|
namespace_path=namespace_path, page_token=page_token, limit=limit
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def create_namespace(
|
def create_namespace(
|
||||||
self,
|
self,
|
||||||
namespace: List[str],
|
namespace_path: List[str],
|
||||||
mode: Optional[str] = None,
|
mode: Optional[str] = None,
|
||||||
properties: Optional[Dict[str, str]] = None,
|
properties: Optional[Dict[str, str]] = None,
|
||||||
) -> CreateNamespaceResponse:
|
) -> CreateNamespaceResponse:
|
||||||
@@ -152,7 +153,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace_path: List[str]
|
||||||
The namespace identifier to create.
|
The namespace identifier to create.
|
||||||
mode: str, optional
|
mode: str, optional
|
||||||
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
Creation mode - "create" (fail if exists), "exist_ok" (skip if exists),
|
||||||
@@ -167,14 +168,14 @@ class RemoteDBConnection(DBConnection):
|
|||||||
"""
|
"""
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
self._conn.create_namespace(
|
self._conn.create_namespace(
|
||||||
namespace=namespace, mode=mode, properties=properties
|
namespace_path=namespace_path, mode=mode, properties=properties
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def drop_namespace(
|
def drop_namespace(
|
||||||
self,
|
self,
|
||||||
namespace: List[str],
|
namespace_path: List[str],
|
||||||
mode: Optional[str] = None,
|
mode: Optional[str] = None,
|
||||||
behavior: Optional[str] = None,
|
behavior: Optional[str] = None,
|
||||||
) -> DropNamespaceResponse:
|
) -> DropNamespaceResponse:
|
||||||
@@ -182,7 +183,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace_path: List[str]
|
||||||
The namespace identifier to drop.
|
The namespace identifier to drop.
|
||||||
mode: str, optional
|
mode: str, optional
|
||||||
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
Whether to skip if not exists ("SKIP") or fail ("FAIL"). Case insensitive.
|
||||||
@@ -196,16 +197,20 @@ class RemoteDBConnection(DBConnection):
|
|||||||
Response containing properties and transaction_id if applicable.
|
Response containing properties and transaction_id if applicable.
|
||||||
"""
|
"""
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
self._conn.drop_namespace(namespace=namespace, mode=mode, behavior=behavior)
|
self._conn.drop_namespace(
|
||||||
|
namespace_path=namespace_path, mode=mode, behavior=behavior
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def describe_namespace(self, namespace: List[str]) -> DescribeNamespaceResponse:
|
def describe_namespace(
|
||||||
|
self, namespace_path: List[str]
|
||||||
|
) -> DescribeNamespaceResponse:
|
||||||
"""Describe a namespace.
|
"""Describe a namespace.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str]
|
namespace_path: List[str]
|
||||||
The namespace identifier to describe.
|
The namespace identifier to describe.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@@ -213,12 +218,12 @@ class RemoteDBConnection(DBConnection):
|
|||||||
DescribeNamespaceResponse
|
DescribeNamespaceResponse
|
||||||
Response containing the namespace properties.
|
Response containing the namespace properties.
|
||||||
"""
|
"""
|
||||||
return LOOP.run(self._conn.describe_namespace(namespace=namespace))
|
return LOOP.run(self._conn.describe_namespace(namespace_path=namespace_path))
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def list_tables(
|
def list_tables(
|
||||||
self,
|
self,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
) -> ListTablesResponse:
|
) -> ListTablesResponse:
|
||||||
@@ -226,7 +231,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str], optional
|
namespace_path: List[str], optional
|
||||||
The namespace to list tables in.
|
The namespace to list tables in.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
page_token: str, optional
|
page_token: str, optional
|
||||||
@@ -240,11 +245,11 @@ class RemoteDBConnection(DBConnection):
|
|||||||
ListTablesResponse
|
ListTablesResponse
|
||||||
Response containing table names and optional page_token for pagination.
|
Response containing table names and optional page_token for pagination.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
self._conn.list_tables(
|
self._conn.list_tables(
|
||||||
namespace=namespace, page_token=page_token, limit=limit
|
namespace_path=namespace_path, page_token=page_token, limit=limit
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -254,7 +259,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
page_token: Optional[str] = None,
|
page_token: Optional[str] = None,
|
||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
) -> Iterable[str]:
|
) -> Iterable[str]:
|
||||||
"""List the names of all tables in the database.
|
"""List the names of all tables in the database.
|
||||||
|
|
||||||
@@ -263,7 +268,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
namespace: List[str], default []
|
namespace_path: List[str], default []
|
||||||
The namespace to list tables in.
|
The namespace to list tables in.
|
||||||
Empty list represents root namespace.
|
Empty list represents root namespace.
|
||||||
page_token: str
|
page_token: str
|
||||||
@@ -282,11 +287,11 @@ class RemoteDBConnection(DBConnection):
|
|||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
return LOOP.run(
|
return LOOP.run(
|
||||||
self._conn.table_names(
|
self._conn.table_names(
|
||||||
namespace=namespace, start_after=page_token, limit=limit
|
namespace_path=namespace_path, start_after=page_token, limit=limit
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -295,7 +300,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
index_cache_size: Optional[int] = None,
|
index_cache_size: Optional[int] = None,
|
||||||
) -> Table:
|
) -> Table:
|
||||||
@@ -305,7 +310,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
----------
|
----------
|
||||||
name: str
|
name: str
|
||||||
The name of the table.
|
The name of the table.
|
||||||
namespace: List[str], optional
|
namespace_path: List[str], optional
|
||||||
The namespace to open the table from.
|
The namespace to open the table from.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
|
|
||||||
@@ -315,15 +320,15 @@ class RemoteDBConnection(DBConnection):
|
|||||||
"""
|
"""
|
||||||
from .table import RemoteTable
|
from .table import RemoteTable
|
||||||
|
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
if index_cache_size is not None:
|
if index_cache_size is not None:
|
||||||
logging.info(
|
logging.info(
|
||||||
"index_cache_size is ignored in LanceDb Cloud"
|
"index_cache_size is ignored in LanceDb Cloud"
|
||||||
" (there is no local cache to configure)"
|
" (there is no local cache to configure)"
|
||||||
)
|
)
|
||||||
|
|
||||||
table = LOOP.run(self._conn.open_table(name, namespace=namespace))
|
table = LOOP.run(self._conn.open_table(name, namespace_path=namespace_path))
|
||||||
return RemoteTable(table, self.db_name)
|
return RemoteTable(table, self.db_name)
|
||||||
|
|
||||||
def clone_table(
|
def clone_table(
|
||||||
@@ -331,7 +336,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
target_table_name: str,
|
target_table_name: str,
|
||||||
source_uri: str,
|
source_uri: str,
|
||||||
*,
|
*,
|
||||||
target_namespace: Optional[List[str]] = None,
|
target_namespace_path: Optional[List[str]] = None,
|
||||||
source_version: Optional[int] = None,
|
source_version: Optional[int] = None,
|
||||||
source_tag: Optional[str] = None,
|
source_tag: Optional[str] = None,
|
||||||
is_shallow: bool = True,
|
is_shallow: bool = True,
|
||||||
@@ -344,7 +349,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
The name of the target table to create.
|
The name of the target table to create.
|
||||||
source_uri: str
|
source_uri: str
|
||||||
The URI of the source table to clone from.
|
The URI of the source table to clone from.
|
||||||
target_namespace: List[str], optional
|
target_namespace_path: List[str], optional
|
||||||
The namespace for the target table.
|
The namespace for the target table.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
source_version: int, optional
|
source_version: int, optional
|
||||||
@@ -361,13 +366,13 @@ class RemoteDBConnection(DBConnection):
|
|||||||
"""
|
"""
|
||||||
from .table import RemoteTable
|
from .table import RemoteTable
|
||||||
|
|
||||||
if target_namespace is None:
|
if target_namespace_path is None:
|
||||||
target_namespace = []
|
target_namespace_path = []
|
||||||
table = LOOP.run(
|
table = LOOP.run(
|
||||||
self._conn.clone_table(
|
self._conn.clone_table(
|
||||||
target_table_name,
|
target_table_name,
|
||||||
source_uri,
|
source_uri,
|
||||||
target_namespace=target_namespace,
|
target_namespace_path=target_namespace_path,
|
||||||
source_version=source_version,
|
source_version=source_version,
|
||||||
source_tag=source_tag,
|
source_tag=source_tag,
|
||||||
is_shallow=is_shallow,
|
is_shallow=is_shallow,
|
||||||
@@ -387,7 +392,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
exist_ok: bool = False,
|
exist_ok: bool = False,
|
||||||
embedding_functions: Optional[List[EmbeddingFunctionConfig]] = None,
|
embedding_functions: Optional[List[EmbeddingFunctionConfig]] = None,
|
||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
) -> Table:
|
) -> Table:
|
||||||
"""Create a [Table][lancedb.table.Table] in the database.
|
"""Create a [Table][lancedb.table.Table] in the database.
|
||||||
|
|
||||||
@@ -395,7 +400,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
----------
|
----------
|
||||||
name: str
|
name: str
|
||||||
The name of the table.
|
The name of the table.
|
||||||
namespace: List[str], optional
|
namespace_path: List[str], optional
|
||||||
The namespace to create the table in.
|
The namespace to create the table in.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
data: The data to initialize the table, *optional*
|
data: The data to initialize the table, *optional*
|
||||||
@@ -495,8 +500,8 @@ class RemoteDBConnection(DBConnection):
|
|||||||
mode = "exist_ok"
|
mode = "exist_ok"
|
||||||
elif not mode:
|
elif not mode:
|
||||||
mode = "exist_ok"
|
mode = "exist_ok"
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
validate_table_name(name)
|
validate_table_name(name)
|
||||||
if embedding_functions is not None:
|
if embedding_functions is not None:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
@@ -511,7 +516,7 @@ class RemoteDBConnection(DBConnection):
|
|||||||
self._conn.create_table(
|
self._conn.create_table(
|
||||||
name,
|
name,
|
||||||
data,
|
data,
|
||||||
namespace=namespace,
|
namespace_path=namespace_path,
|
||||||
mode=mode,
|
mode=mode,
|
||||||
schema=schema,
|
schema=schema,
|
||||||
on_bad_vectors=on_bad_vectors,
|
on_bad_vectors=on_bad_vectors,
|
||||||
@@ -521,28 +526,28 @@ class RemoteDBConnection(DBConnection):
|
|||||||
return RemoteTable(table, self.db_name)
|
return RemoteTable(table, self.db_name)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def drop_table(self, name: str, namespace: Optional[List[str]] = None):
|
def drop_table(self, name: str, namespace_path: Optional[List[str]] = None):
|
||||||
"""Drop a table from the database.
|
"""Drop a table from the database.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
name: str
|
name: str
|
||||||
The name of the table.
|
The name of the table.
|
||||||
namespace: List[str], optional
|
namespace_path: List[str], optional
|
||||||
The namespace to drop the table from.
|
The namespace to drop the table from.
|
||||||
None or empty list represents root namespace.
|
None or empty list represents root namespace.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
LOOP.run(self._conn.drop_table(name, namespace=namespace))
|
LOOP.run(self._conn.drop_table(name, namespace_path=namespace_path))
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def rename_table(
|
def rename_table(
|
||||||
self,
|
self,
|
||||||
cur_name: str,
|
cur_name: str,
|
||||||
new_name: str,
|
new_name: str,
|
||||||
cur_namespace: Optional[List[str]] = None,
|
cur_namespace_path: Optional[List[str]] = None,
|
||||||
new_namespace: Optional[List[str]] = None,
|
new_namespace_path: Optional[List[str]] = None,
|
||||||
):
|
):
|
||||||
"""Rename a table in the database.
|
"""Rename a table in the database.
|
||||||
|
|
||||||
@@ -553,19 +558,32 @@ class RemoteDBConnection(DBConnection):
|
|||||||
new_name: str
|
new_name: str
|
||||||
The new name of the table.
|
The new name of the table.
|
||||||
"""
|
"""
|
||||||
if cur_namespace is None:
|
if cur_namespace_path is None:
|
||||||
cur_namespace = []
|
cur_namespace_path = []
|
||||||
if new_namespace is None:
|
if new_namespace_path is None:
|
||||||
new_namespace = []
|
new_namespace_path = []
|
||||||
LOOP.run(
|
LOOP.run(
|
||||||
self._conn.rename_table(
|
self._conn.rename_table(
|
||||||
cur_name,
|
cur_name,
|
||||||
new_name,
|
new_name,
|
||||||
cur_namespace=cur_namespace,
|
cur_namespace_path=cur_namespace_path,
|
||||||
new_namespace=new_namespace,
|
new_namespace_path=new_namespace_path,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def namespace_client(self) -> LanceNamespace:
|
||||||
|
"""Get the equivalent namespace client for this connection.
|
||||||
|
|
||||||
|
Returns a RestNamespace with the same URI and authentication headers.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
LanceNamespace
|
||||||
|
The namespace client for this connection.
|
||||||
|
"""
|
||||||
|
return LOOP.run(self._conn.namespace_client())
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
"""Close the connection to the database."""
|
"""Close the connection to the database."""
|
||||||
self._conn.close()
|
self._conn.close()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from lancedb.index import (
|
|||||||
FTS,
|
FTS,
|
||||||
BTree,
|
BTree,
|
||||||
Bitmap,
|
Bitmap,
|
||||||
|
HnswFlat,
|
||||||
HnswSq,
|
HnswSq,
|
||||||
IvfFlat,
|
IvfFlat,
|
||||||
IvfPq,
|
IvfPq,
|
||||||
@@ -39,6 +40,7 @@ from lancedb.table import _normalize_progress
|
|||||||
|
|
||||||
from ..query import LanceVectorQueryBuilder, LanceQueryBuilder, LanceTakeQueryBuilder
|
from ..query import LanceVectorQueryBuilder, LanceQueryBuilder, LanceTakeQueryBuilder
|
||||||
from ..table import AsyncTable, IndexStatistics, Query, Table, Tags
|
from ..table import AsyncTable, IndexStatistics, Query, Table, Tags
|
||||||
|
from ..types import BaseTokenizerType
|
||||||
|
|
||||||
|
|
||||||
class RemoteTable(Table):
|
class RemoteTable(Table):
|
||||||
@@ -167,7 +169,7 @@ class RemoteTable(Table):
|
|||||||
wait_timeout: Optional[timedelta] = None,
|
wait_timeout: Optional[timedelta] = None,
|
||||||
with_position: bool = False,
|
with_position: bool = False,
|
||||||
# tokenizer configs:
|
# tokenizer configs:
|
||||||
base_tokenizer: str = "simple",
|
base_tokenizer: BaseTokenizerType = "simple",
|
||||||
language: str = "English",
|
language: str = "English",
|
||||||
max_token_length: Optional[int] = 40,
|
max_token_length: Optional[int] = 40,
|
||||||
lower_case: bool = True,
|
lower_case: bool = True,
|
||||||
@@ -284,13 +286,15 @@ class RemoteTable(Table):
|
|||||||
)
|
)
|
||||||
elif index_type == "IVF_HNSW_SQ":
|
elif index_type == "IVF_HNSW_SQ":
|
||||||
config = HnswSq(distance_type=metric, num_partitions=num_partitions)
|
config = HnswSq(distance_type=metric, num_partitions=num_partitions)
|
||||||
|
elif index_type == "IVF_HNSW_FLAT":
|
||||||
|
config = HnswFlat(distance_type=metric, num_partitions=num_partitions)
|
||||||
elif index_type == "IVF_FLAT":
|
elif index_type == "IVF_FLAT":
|
||||||
config = IvfFlat(distance_type=metric, num_partitions=num_partitions)
|
config = IvfFlat(distance_type=metric, num_partitions=num_partitions)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unknown vector index type: {index_type}. Valid options are"
|
f"Unknown vector index type: {index_type}. Valid options are"
|
||||||
" 'IVF_FLAT', 'IVF_PQ', 'IVF_RQ', 'IVF_SQ',"
|
" 'IVF_FLAT', 'IVF_PQ', 'IVF_RQ', 'IVF_SQ',"
|
||||||
" 'IVF_HNSW_PQ', 'IVF_HNSW_SQ'"
|
" 'IVF_HNSW_PQ', 'IVF_HNSW_SQ', 'IVF_HNSW_FLAT'"
|
||||||
)
|
)
|
||||||
|
|
||||||
LOOP.run(
|
LOOP.run(
|
||||||
|
|||||||
+474
-178
@@ -57,6 +57,7 @@ from .index import (
|
|||||||
LabelList,
|
LabelList,
|
||||||
HnswPq,
|
HnswPq,
|
||||||
HnswSq,
|
HnswSq,
|
||||||
|
HnswFlat,
|
||||||
FTS,
|
FTS,
|
||||||
)
|
)
|
||||||
from .merge import LanceMergeInsertBuilder
|
from .merge import LanceMergeInsertBuilder
|
||||||
@@ -86,10 +87,62 @@ from .util import (
|
|||||||
)
|
)
|
||||||
from .index import lang_mapping
|
from .index import lang_mapping
|
||||||
|
|
||||||
|
_MODEL_BACKED_TOKENIZER_PREFIXES = ("jieba", "lindera")
|
||||||
|
_MODEL_BACKED_TOKENIZER_ERRORS = (
|
||||||
|
"unknown base tokenizer",
|
||||||
|
"Invalid directory path:",
|
||||||
|
"Failed to load Jieba",
|
||||||
|
"Failed to load tokenizer config",
|
||||||
|
"Failed to initialize default tokenizer",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _add_unique_note(exception: BaseException, note: str) -> None:
|
||||||
|
existing_notes = getattr(exception, "__notes__", ()) or ()
|
||||||
|
message = (
|
||||||
|
exception.args[0]
|
||||||
|
if exception.args and isinstance(exception.args[0], str)
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
if note not in existing_notes and note not in message:
|
||||||
|
add_note(exception, note)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_model_backed_tokenizer(base_tokenizer: str) -> bool:
|
||||||
|
return any(
|
||||||
|
base_tokenizer == prefix or base_tokenizer.startswith(f"{prefix}/")
|
||||||
|
for prefix in _MODEL_BACKED_TOKENIZER_PREFIXES
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _maybe_add_fts_error_note(
|
||||||
|
exception: BaseException, *, base_tokenizer: str, language: Optional[str] = None
|
||||||
|
) -> None:
|
||||||
|
message = str(exception)
|
||||||
|
if language is not None and "not support the requested language" in message:
|
||||||
|
supported_langs = ", ".join(lang_mapping.values())
|
||||||
|
_add_unique_note(exception, f"Supported languages: {supported_langs}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not _is_model_backed_tokenizer(base_tokenizer):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not any(marker in message for marker in _MODEL_BACKED_TOKENIZER_ERRORS):
|
||||||
|
return
|
||||||
|
|
||||||
|
_add_unique_note(
|
||||||
|
exception,
|
||||||
|
"Model-backed tokenizers such as 'jieba/default' and 'lindera/ipadic' "
|
||||||
|
"require tokenizer models in Lance's language model home. Set "
|
||||||
|
"LANCE_LANGUAGE_MODEL_HOME to override the default platform data "
|
||||||
|
"directory under 'lance/language_models'. Expected layouts include "
|
||||||
|
"'<model-home>/jieba/default/...' and "
|
||||||
|
"'<model-home>/lindera/ipadic/...'.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .db import LanceDBConnection
|
from .db import LanceDBConnection
|
||||||
from .io import StorageOptionsProvider
|
|
||||||
from ._lancedb import (
|
from ._lancedb import (
|
||||||
Table as LanceDBTable,
|
Table as LanceDBTable,
|
||||||
OptimizeStats,
|
OptimizeStats,
|
||||||
@@ -192,7 +245,7 @@ def _into_pyarrow_reader(
|
|||||||
f"Unknown data type {type(data)}. "
|
f"Unknown data type {type(data)}. "
|
||||||
"Supported types: list of dicts, pandas DataFrame, polars DataFrame, "
|
"Supported types: list of dicts, pandas DataFrame, polars DataFrame, "
|
||||||
"pyarrow Table/RecordBatch, or Pydantic models. "
|
"pyarrow Table/RecordBatch, or Pydantic models. "
|
||||||
"See https://lancedb.com/docs/tables/ for examples."
|
"See https://docs.lancedb.com/tables/ for examples."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -271,15 +324,17 @@ def _sanitize_data(
|
|||||||
reader,
|
reader,
|
||||||
on_bad_vectors=on_bad_vectors,
|
on_bad_vectors=on_bad_vectors,
|
||||||
fill_value=fill_value,
|
fill_value=fill_value,
|
||||||
|
target_schema=target_schema,
|
||||||
|
metadata=metadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
if target_schema is None:
|
if target_schema is None:
|
||||||
target_schema, reader = _infer_target_schema(reader)
|
target_schema, reader = _infer_target_schema(reader)
|
||||||
|
|
||||||
if metadata:
|
if metadata:
|
||||||
new_metadata = target_schema.metadata or {}
|
target_schema = target_schema.with_metadata(
|
||||||
new_metadata.update(metadata)
|
_merge_metadata(target_schema.metadata, metadata)
|
||||||
target_schema = target_schema.with_metadata(new_metadata)
|
)
|
||||||
|
|
||||||
_validate_schema(target_schema)
|
_validate_schema(target_schema)
|
||||||
reader = _cast_to_target_schema(reader, target_schema, allow_subschema)
|
reader = _cast_to_target_schema(reader, target_schema, allow_subschema)
|
||||||
@@ -295,7 +350,7 @@ def _cast_to_target_schema(
|
|||||||
# pa.Table.cast expects field order not to be changed.
|
# pa.Table.cast expects field order not to be changed.
|
||||||
# Lance doesn't care about field order, so we don't need to rearrange fields
|
# Lance doesn't care about field order, so we don't need to rearrange fields
|
||||||
# to match the target schema. We just need to correctly cast the fields.
|
# to match the target schema. We just need to correctly cast the fields.
|
||||||
if reader.schema == target_schema:
|
if reader.schema.equals(target_schema, check_metadata=True):
|
||||||
# Fast path when the schemas are already the same
|
# Fast path when the schemas are already the same
|
||||||
return reader
|
return reader
|
||||||
|
|
||||||
@@ -315,7 +370,13 @@ def _cast_to_target_schema(
|
|||||||
def gen():
|
def gen():
|
||||||
for batch in reader:
|
for batch in reader:
|
||||||
# Table but not RecordBatch has cast.
|
# Table but not RecordBatch has cast.
|
||||||
yield pa.Table.from_batches([batch]).cast(reordered_schema).to_batches()[0]
|
cast_batches = (
|
||||||
|
pa.Table.from_batches([batch]).cast(reordered_schema).to_batches()
|
||||||
|
)
|
||||||
|
if cast_batches:
|
||||||
|
yield pa.RecordBatch.from_arrays(
|
||||||
|
cast_batches[0].columns, schema=reordered_schema
|
||||||
|
)
|
||||||
|
|
||||||
return pa.RecordBatchReader.from_batches(reordered_schema, gen())
|
return pa.RecordBatchReader.from_batches(reordered_schema, gen())
|
||||||
|
|
||||||
@@ -333,37 +394,51 @@ def _align_field_types(
|
|||||||
if target_field is None:
|
if target_field is None:
|
||||||
raise ValueError(f"Field '{field.name}' not found in target schema")
|
raise ValueError(f"Field '{field.name}' not found in target schema")
|
||||||
if pa.types.is_struct(target_field.type):
|
if pa.types.is_struct(target_field.type):
|
||||||
new_type = pa.struct(
|
if pa.types.is_struct(field.type):
|
||||||
_align_field_types(
|
new_type = pa.struct(
|
||||||
field.type.fields,
|
_align_field_types(
|
||||||
target_field.type.fields,
|
field.type.fields,
|
||||||
|
target_field.type.fields,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
new_type = target_field.type
|
||||||
elif pa.types.is_list(target_field.type):
|
elif pa.types.is_list(target_field.type):
|
||||||
new_type = pa.list_(
|
if _is_list_like(field.type):
|
||||||
_align_field_types(
|
new_type = pa.list_(
|
||||||
[field.type.value_field],
|
_align_field_types(
|
||||||
[target_field.type.value_field],
|
[field.type.value_field],
|
||||||
)[0]
|
[target_field.type.value_field],
|
||||||
)
|
)[0]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_type = target_field.type
|
||||||
elif pa.types.is_large_list(target_field.type):
|
elif pa.types.is_large_list(target_field.type):
|
||||||
new_type = pa.large_list(
|
if _is_list_like(field.type):
|
||||||
_align_field_types(
|
new_type = pa.large_list(
|
||||||
[field.type.value_field],
|
_align_field_types(
|
||||||
[target_field.type.value_field],
|
[field.type.value_field],
|
||||||
)[0]
|
[target_field.type.value_field],
|
||||||
)
|
)[0]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_type = target_field.type
|
||||||
elif pa.types.is_fixed_size_list(target_field.type):
|
elif pa.types.is_fixed_size_list(target_field.type):
|
||||||
new_type = pa.list_(
|
if _is_list_like(field.type):
|
||||||
_align_field_types(
|
new_type = pa.list_(
|
||||||
[field.type.value_field],
|
_align_field_types(
|
||||||
[target_field.type.value_field],
|
[field.type.value_field],
|
||||||
)[0],
|
[target_field.type.value_field],
|
||||||
target_field.type.list_size,
|
)[0],
|
||||||
)
|
target_field.type.list_size,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_type = target_field.type
|
||||||
else:
|
else:
|
||||||
new_type = target_field.type
|
new_type = target_field.type
|
||||||
new_fields.append(pa.field(field.name, new_type, field.nullable))
|
new_fields.append(
|
||||||
|
pa.field(field.name, new_type, field.nullable, target_field.metadata)
|
||||||
|
)
|
||||||
return new_fields
|
return new_fields
|
||||||
|
|
||||||
|
|
||||||
@@ -441,6 +516,7 @@ def sanitize_create_table(
|
|||||||
schema = data.schema
|
schema = data.schema
|
||||||
|
|
||||||
if metadata:
|
if metadata:
|
||||||
|
metadata = _merge_metadata(schema.metadata, metadata)
|
||||||
schema = schema.with_metadata(metadata)
|
schema = schema.with_metadata(metadata)
|
||||||
# Need to apply metadata to the data as well
|
# Need to apply metadata to the data as well
|
||||||
if isinstance(data, pa.Table):
|
if isinstance(data, pa.Table):
|
||||||
@@ -493,9 +569,9 @@ def _append_vector_columns(
|
|||||||
vector columns to the table.
|
vector columns to the table.
|
||||||
"""
|
"""
|
||||||
if schema is None:
|
if schema is None:
|
||||||
metadata = metadata or {}
|
metadata = _merge_metadata(metadata)
|
||||||
else:
|
else:
|
||||||
metadata = schema.metadata or metadata or {}
|
metadata = _merge_metadata(schema.metadata, metadata)
|
||||||
functions = EmbeddingFunctionRegistry.get_instance().parse_functions(metadata)
|
functions = EmbeddingFunctionRegistry.get_instance().parse_functions(metadata)
|
||||||
|
|
||||||
if not functions:
|
if not functions:
|
||||||
@@ -921,29 +997,29 @@ class Table(ABC):
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
field_names: str or list of str
|
field_names: str or list of str
|
||||||
The name(s) of the field to index.
|
The name of the field to index. Native FTS indexes can only be
|
||||||
If ``use_tantivy`` is False (default), only a single field name
|
created on a single field at a time. To search over multiple text
|
||||||
(str) is supported. To index multiple fields, create a separate
|
fields, create a separate FTS index for each field.
|
||||||
FTS index for each field.
|
|
||||||
replace: bool, default False
|
replace: bool, default False
|
||||||
If True, replace the existing index if it exists. Note that this is
|
If True, replace the existing index if it exists. Note that this is
|
||||||
not yet an atomic operation; the index will be temporarily
|
not yet an atomic operation; the index will be temporarily
|
||||||
unavailable while the new index is being created.
|
unavailable while the new index is being created.
|
||||||
writer_heap_size: int, default 1GB
|
writer_heap_size: int, default 1GB
|
||||||
Only available with use_tantivy=True
|
Deprecated legacy Tantivy parameter. Any value other than the
|
||||||
|
default raises an error.
|
||||||
ordering_field_names:
|
ordering_field_names:
|
||||||
A list of unsigned type fields to index to optionally order
|
Deprecated legacy Tantivy parameter. Setting this raises an error.
|
||||||
results on at search time.
|
|
||||||
only available with use_tantivy=True
|
|
||||||
tokenizer_name: str, default "default"
|
tokenizer_name: str, default "default"
|
||||||
The tokenizer to use for the index. Can be "raw", "default" or the 2 letter
|
A compatibility alias for native tokenizer configs. Can be "raw",
|
||||||
language code followed by "_stem". So for english it would be "en_stem".
|
"default" or the 2 letter language code followed by "_stem". So
|
||||||
For available languages see: https://docs.rs/tantivy/latest/tantivy/tokenizer/enum.Language.html
|
for english it would be "en_stem". For new native FTS indexes, use
|
||||||
|
``base_tokenizer`` directly; ``tokenizer_name`` is a legacy
|
||||||
|
compatibility alias and does not expose model-backed tokenizer names
|
||||||
|
such as ``jieba/default`` or ``lindera/ipadic``.
|
||||||
use_tantivy: bool, default False
|
use_tantivy: bool, default False
|
||||||
If True, use the legacy full-text search implementation based on tantivy.
|
Deprecated legacy Tantivy parameter. Setting this to True raises an
|
||||||
If False, use the new full-text search implementation based on lance-index.
|
error.
|
||||||
with_position: bool, default False
|
with_position: bool, default False
|
||||||
Only available with use_tantivy=False
|
|
||||||
If False, do not store the positions of the terms in the text.
|
If False, do not store the positions of the terms in the text.
|
||||||
This can reduce the size of the index and improve indexing speed.
|
This can reduce the size of the index and improve indexing speed.
|
||||||
But it will raise an exception for phrase queries.
|
But it will raise an exception for phrase queries.
|
||||||
@@ -953,8 +1029,11 @@ class Table(ABC):
|
|||||||
- "whitespace": Split text by whitespace, but not punctuation.
|
- "whitespace": Split text by whitespace, but not punctuation.
|
||||||
- "raw": No tokenization. The entire text is treated as a single token.
|
- "raw": No tokenization. The entire text is treated as a single token.
|
||||||
- "ngram": N-Gram tokenizer.
|
- "ngram": N-Gram tokenizer.
|
||||||
|
- "jieba/*": Jieba tokenizer loaded from Lance's language model home.
|
||||||
|
- "lindera/*": Lindera tokenizer loaded from Lance's language model home.
|
||||||
language : str, default "English"
|
language : str, default "English"
|
||||||
The language to use for tokenization.
|
The language to use for stemming and stop-word removal. This is not
|
||||||
|
the primary way to enable CJK tokenization.
|
||||||
max_token_length : int, default 40
|
max_token_length : int, default 40
|
||||||
The maximum token length to index. Tokens longer than this length will be
|
The maximum token length to index. Tokens longer than this length will be
|
||||||
ignored.
|
ignored.
|
||||||
@@ -980,6 +1059,13 @@ class Table(ABC):
|
|||||||
The timeout to wait if indexing is asynchronous.
|
The timeout to wait if indexing is asynchronous.
|
||||||
name: str, optional
|
name: str, optional
|
||||||
The name of the index. If not provided, a default name will be generated.
|
The name of the index. If not provided, a default name will be generated.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
Model-backed tokenizers such as ``jieba/default`` and ``lindera/ipadic``
|
||||||
|
require tokenizer models in Lance's language model home. Set
|
||||||
|
``LANCE_LANGUAGE_MODEL_HOME`` to override the default platform data
|
||||||
|
directory under ``lance/language_models``.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@@ -1724,6 +1810,16 @@ class Table(ABC):
|
|||||||
index_exists = fs.get_file_info(path).type != pa_fs.FileType.NotFound
|
index_exists = fs.get_file_info(path).type != pa_fs.FileType.NotFound
|
||||||
return (path, fs, index_exists)
|
return (path, fs, index_exists)
|
||||||
|
|
||||||
|
def _ensure_no_legacy_fts_index(self):
|
||||||
|
path, _, exists = self._get_fts_index_path()
|
||||||
|
if exists:
|
||||||
|
raise ValueError(
|
||||||
|
"Legacy Tantivy FTS index detected at "
|
||||||
|
f"{path}. Tantivy-based FTS has been removed. "
|
||||||
|
"Delete the legacy index and recreate it with "
|
||||||
|
"table.create_fts_index(...)."
|
||||||
|
)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def uses_v2_manifest_paths(self) -> bool:
|
def uses_v2_manifest_paths(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -1776,30 +1872,30 @@ class LanceTable(Table):
|
|||||||
connection: "LanceDBConnection",
|
connection: "LanceDBConnection",
|
||||||
name: str,
|
name: str,
|
||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
storage_options_provider: Optional["StorageOptionsProvider"] = None,
|
|
||||||
index_cache_size: Optional[int] = None,
|
index_cache_size: Optional[int] = None,
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
namespace_client: Optional[Any] = None,
|
namespace_client: Optional[Any] = None,
|
||||||
managed_versioning: Optional[bool] = None,
|
managed_versioning: Optional[bool] = None,
|
||||||
|
pushdown_operations: Optional[set] = None,
|
||||||
_async: AsyncTable = None,
|
_async: AsyncTable = None,
|
||||||
):
|
):
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
self._conn = connection
|
self._conn = connection
|
||||||
self._namespace = namespace
|
self._namespace_path = namespace_path
|
||||||
self._location = location # Store location for use in _dataset_path
|
self._location = location # Store location for use in _dataset_path
|
||||||
self._namespace_client = namespace_client
|
self._namespace_client = namespace_client
|
||||||
|
self._pushdown_operations = pushdown_operations or set()
|
||||||
if _async is not None:
|
if _async is not None:
|
||||||
self._table = _async
|
self._table = _async
|
||||||
else:
|
else:
|
||||||
self._table = LOOP.run(
|
self._table = LOOP.run(
|
||||||
connection._conn.open_table(
|
connection._conn.open_table(
|
||||||
name,
|
name,
|
||||||
namespace=namespace,
|
namespace_path=namespace_path,
|
||||||
storage_options=storage_options,
|
storage_options=storage_options,
|
||||||
storage_options_provider=storage_options_provider,
|
|
||||||
index_cache_size=index_cache_size,
|
index_cache_size=index_cache_size,
|
||||||
location=location,
|
location=location,
|
||||||
namespace_client=namespace_client,
|
namespace_client=namespace_client,
|
||||||
@@ -1814,13 +1910,13 @@ class LanceTable(Table):
|
|||||||
@property
|
@property
|
||||||
def namespace(self) -> List[str]:
|
def namespace(self) -> List[str]:
|
||||||
"""Return the namespace path of the table."""
|
"""Return the namespace path of the table."""
|
||||||
return self._namespace
|
return self._namespace_path
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self) -> str:
|
def id(self) -> str:
|
||||||
"""Return the full identifier of the table (namespace$name)."""
|
"""Return the full identifier of the table (namespace$name)."""
|
||||||
if self._namespace:
|
if self._namespace_path:
|
||||||
return "$".join(self._namespace + [self.name])
|
return "$".join(self._namespace_path + [self.name])
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -1841,26 +1937,26 @@ class LanceTable(Table):
|
|||||||
db,
|
db,
|
||||||
name,
|
name,
|
||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str]] = None,
|
storage_options: Optional[Dict[str, str]] = None,
|
||||||
storage_options_provider: Optional["StorageOptionsProvider"] = None,
|
|
||||||
index_cache_size: Optional[int] = None,
|
index_cache_size: Optional[int] = None,
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
namespace_client: Optional[Any] = None,
|
namespace_client: Optional[Any] = None,
|
||||||
managed_versioning: Optional[bool] = None,
|
managed_versioning: Optional[bool] = None,
|
||||||
|
pushdown_operations: Optional[set] = None,
|
||||||
):
|
):
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
tbl = cls(
|
tbl = cls(
|
||||||
db,
|
db,
|
||||||
name,
|
name,
|
||||||
namespace=namespace,
|
namespace_path=namespace_path,
|
||||||
storage_options=storage_options,
|
storage_options=storage_options,
|
||||||
storage_options_provider=storage_options_provider,
|
|
||||||
index_cache_size=index_cache_size,
|
index_cache_size=index_cache_size,
|
||||||
location=location,
|
location=location,
|
||||||
namespace_client=namespace_client,
|
namespace_client=namespace_client,
|
||||||
managed_versioning=managed_versioning,
|
managed_versioning=managed_versioning,
|
||||||
|
pushdown_operations=pushdown_operations,
|
||||||
)
|
)
|
||||||
|
|
||||||
# check the dataset exists
|
# check the dataset exists
|
||||||
@@ -1893,11 +1989,11 @@ class LanceTable(Table):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self._namespace_client is not None:
|
if self._namespace_client is not None:
|
||||||
table_id = self._namespace + [self.name]
|
table_id = self._namespace_path + [self.name]
|
||||||
return lance.dataset(
|
return lance.dataset(
|
||||||
version=self.version,
|
version=self.version,
|
||||||
storage_options=self._conn.storage_options,
|
storage_options=self._conn.storage_options,
|
||||||
namespace=self._namespace_client,
|
namespace_client=self._namespace_client,
|
||||||
table_id=table_id,
|
table_id=table_id,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
@@ -2141,7 +2237,13 @@ 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_SQ", "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_HNSW_FLAT",
|
||||||
] = "IVF_PQ",
|
] = "IVF_PQ",
|
||||||
max_iterations: int = 50,
|
max_iterations: int = 50,
|
||||||
sample_rate: int = 256,
|
sample_rate: int = 256,
|
||||||
@@ -2228,6 +2330,16 @@ class LanceTable(Table):
|
|||||||
ef_construction=ef_construction,
|
ef_construction=ef_construction,
|
||||||
target_partition_size=target_partition_size,
|
target_partition_size=target_partition_size,
|
||||||
)
|
)
|
||||||
|
elif index_type == "IVF_HNSW_FLAT":
|
||||||
|
config = HnswFlat(
|
||||||
|
distance_type=metric,
|
||||||
|
num_partitions=num_partitions,
|
||||||
|
max_iterations=max_iterations,
|
||||||
|
sample_rate=sample_rate,
|
||||||
|
m=m,
|
||||||
|
ef_construction=ef_construction,
|
||||||
|
target_partition_size=target_partition_size,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown index type {index_type}")
|
raise ValueError(f"Unknown index type {index_type}")
|
||||||
|
|
||||||
@@ -2383,41 +2495,57 @@ class LanceTable(Table):
|
|||||||
prefix_only: bool = False,
|
prefix_only: bool = False,
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
):
|
):
|
||||||
if not use_tantivy:
|
self._ensure_no_legacy_fts_index()
|
||||||
if not isinstance(field_names, str):
|
|
||||||
raise ValueError(
|
|
||||||
"Native FTS indexes can only be created on a single field "
|
|
||||||
"at a time. To search over multiple text fields, create a "
|
|
||||||
"separate FTS index for each field."
|
|
||||||
)
|
|
||||||
|
|
||||||
if tokenizer_name is None:
|
if use_tantivy:
|
||||||
tokenizer_configs = {
|
raise ValueError(
|
||||||
"base_tokenizer": base_tokenizer,
|
"Tantivy-based FTS has been removed. "
|
||||||
"language": language,
|
"Remove use_tantivy and recreate the index with native FTS."
|
||||||
"with_position": with_position,
|
)
|
||||||
"max_token_length": max_token_length,
|
if ordering_field_names is not None:
|
||||||
"lower_case": lower_case,
|
raise ValueError(
|
||||||
"stem": stem,
|
"ordering_field_names was only supported by the removed "
|
||||||
"remove_stop_words": remove_stop_words,
|
"Tantivy-based FTS implementation."
|
||||||
"ascii_folding": ascii_folding,
|
)
|
||||||
"ngram_min_length": ngram_min_length,
|
if writer_heap_size != 1024 * 1024 * 1024:
|
||||||
"ngram_max_length": ngram_max_length,
|
raise ValueError(
|
||||||
"prefix_only": prefix_only,
|
"writer_heap_size was only supported by the removed "
|
||||||
}
|
"Tantivy-based FTS implementation."
|
||||||
else:
|
)
|
||||||
tokenizer_configs = self.infer_tokenizer_configs(tokenizer_name)
|
if not isinstance(field_names, str):
|
||||||
|
raise ValueError(
|
||||||
config = FTS(
|
"Native FTS indexes can only be created on a single field "
|
||||||
**tokenizer_configs,
|
"at a time. To search over multiple text fields, create a "
|
||||||
|
"separate FTS index for each field."
|
||||||
|
)
|
||||||
|
if "." in field_names:
|
||||||
|
raise ValueError(
|
||||||
|
"Native FTS indexes can only be created on top-level fields. "
|
||||||
|
f"Received nested field path: {field_names!r}."
|
||||||
)
|
)
|
||||||
|
|
||||||
# delete the existing legacy index if it exists
|
if tokenizer_name is None:
|
||||||
if replace:
|
tokenizer_configs = {
|
||||||
path, fs, exist = self._get_fts_index_path()
|
"base_tokenizer": base_tokenizer,
|
||||||
if exist:
|
"language": language,
|
||||||
fs.delete_dir(path)
|
"with_position": with_position,
|
||||||
|
"max_token_length": max_token_length,
|
||||||
|
"lower_case": lower_case,
|
||||||
|
"stem": stem,
|
||||||
|
"remove_stop_words": remove_stop_words,
|
||||||
|
"ascii_folding": ascii_folding,
|
||||||
|
"ngram_min_length": ngram_min_length,
|
||||||
|
"ngram_max_length": ngram_max_length,
|
||||||
|
"prefix_only": prefix_only,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
tokenizer_configs = self.infer_tokenizer_configs(tokenizer_name)
|
||||||
|
|
||||||
|
config = FTS(
|
||||||
|
**tokenizer_configs,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
LOOP.run(
|
LOOP.run(
|
||||||
self._table.create_index(
|
self._table.create_index(
|
||||||
field_names,
|
field_names,
|
||||||
@@ -2426,42 +2554,13 @@ class LanceTable(Table):
|
|||||||
name=name,
|
name=name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
except (ValueError, RuntimeError) as e:
|
||||||
|
_maybe_add_fts_error_note(
|
||||||
from .fts import create_index, populate_index
|
e,
|
||||||
|
base_tokenizer=config.base_tokenizer,
|
||||||
if isinstance(field_names, str):
|
language=config.language,
|
||||||
field_names = [field_names]
|
|
||||||
|
|
||||||
if isinstance(ordering_field_names, str):
|
|
||||||
ordering_field_names = [ordering_field_names]
|
|
||||||
|
|
||||||
path, fs, exist = self._get_fts_index_path()
|
|
||||||
if exist:
|
|
||||||
if not replace:
|
|
||||||
raise ValueError("Index already exists. Use replace=True to overwrite.")
|
|
||||||
fs.delete_dir(path)
|
|
||||||
|
|
||||||
if not isinstance(fs, pa_fs.LocalFileSystem):
|
|
||||||
raise NotImplementedError(
|
|
||||||
"Full-text search is only supported on the local filesystem"
|
|
||||||
)
|
)
|
||||||
|
raise e
|
||||||
if tokenizer_name is None:
|
|
||||||
tokenizer_name = "default"
|
|
||||||
index = create_index(
|
|
||||||
path,
|
|
||||||
field_names,
|
|
||||||
ordering_fields=ordering_field_names,
|
|
||||||
tokenizer_name=tokenizer_name,
|
|
||||||
)
|
|
||||||
populate_index(
|
|
||||||
index,
|
|
||||||
self,
|
|
||||||
field_names,
|
|
||||||
ordering_fields=ordering_field_names,
|
|
||||||
writer_heap_size=writer_heap_size,
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def infer_tokenizer_configs(tokenizer_name: str) -> dict:
|
def infer_tokenizer_configs(tokenizer_name: str) -> dict:
|
||||||
@@ -2803,13 +2902,13 @@ class LanceTable(Table):
|
|||||||
fill_value: float = 0.0,
|
fill_value: float = 0.0,
|
||||||
embedding_functions: Optional[List[EmbeddingFunctionConfig]] = None,
|
embedding_functions: Optional[List[EmbeddingFunctionConfig]] = None,
|
||||||
*,
|
*,
|
||||||
namespace: Optional[List[str]] = None,
|
namespace_path: Optional[List[str]] = None,
|
||||||
storage_options: Optional[Dict[str, str | bool]] = None,
|
storage_options: Optional[Dict[str, str | bool]] = None,
|
||||||
storage_options_provider: Optional["StorageOptionsProvider"] = None,
|
|
||||||
data_storage_version: Optional[str] = None,
|
data_storage_version: Optional[str] = None,
|
||||||
enable_v2_manifest_paths: Optional[bool] = None,
|
enable_v2_manifest_paths: Optional[bool] = None,
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
namespace_client: Optional[Any] = None,
|
namespace_client: Optional[Any] = None,
|
||||||
|
pushdown_operations: Optional[set] = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Create a new table.
|
Create a new table.
|
||||||
@@ -2864,13 +2963,14 @@ class LanceTable(Table):
|
|||||||
Deprecated. Set `storage_options` when connecting to the database and set
|
Deprecated. Set `storage_options` when connecting to the database and set
|
||||||
`new_table_enable_v2_manifest_paths` in the options.
|
`new_table_enable_v2_manifest_paths` in the options.
|
||||||
"""
|
"""
|
||||||
if namespace is None:
|
if namespace_path is None:
|
||||||
namespace = []
|
namespace_path = []
|
||||||
self = cls.__new__(cls)
|
self = cls.__new__(cls)
|
||||||
self._conn = db
|
self._conn = db
|
||||||
self._namespace = namespace
|
self._namespace_path = namespace_path
|
||||||
self._location = location
|
self._location = location
|
||||||
self._namespace_client = namespace_client
|
self._namespace_client = namespace_client
|
||||||
|
self._pushdown_operations = pushdown_operations or set()
|
||||||
|
|
||||||
if data_storage_version is not None:
|
if data_storage_version is not None:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
@@ -2903,10 +3003,10 @@ class LanceTable(Table):
|
|||||||
on_bad_vectors=on_bad_vectors,
|
on_bad_vectors=on_bad_vectors,
|
||||||
fill_value=fill_value,
|
fill_value=fill_value,
|
||||||
embedding_functions=embedding_functions,
|
embedding_functions=embedding_functions,
|
||||||
namespace=namespace,
|
namespace_path=namespace_path,
|
||||||
storage_options=storage_options,
|
storage_options=storage_options,
|
||||||
storage_options_provider=storage_options_provider,
|
|
||||||
location=location,
|
location=location,
|
||||||
|
namespace_client=namespace_client,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
@@ -2974,6 +3074,15 @@ class LanceTable(Table):
|
|||||||
batch_size: Optional[int] = None,
|
batch_size: Optional[int] = None,
|
||||||
timeout: Optional[timedelta] = None,
|
timeout: Optional[timedelta] = None,
|
||||||
) -> pa.RecordBatchReader:
|
) -> pa.RecordBatchReader:
|
||||||
|
if (
|
||||||
|
"QueryTable" in self._pushdown_operations
|
||||||
|
and self._namespace_client is not None
|
||||||
|
):
|
||||||
|
from lancedb.namespace import _execute_server_side_query
|
||||||
|
|
||||||
|
table_id = self._namespace_path + [self.name]
|
||||||
|
return _execute_server_side_query(self._namespace_client, table_id, query)
|
||||||
|
|
||||||
async_iter = LOOP.run(
|
async_iter = LOOP.run(
|
||||||
self._table._execute_query(query, batch_size=batch_size, timeout=timeout)
|
self._table._execute_query(query, batch_size=batch_size, timeout=timeout)
|
||||||
)
|
)
|
||||||
@@ -3203,43 +3312,157 @@ def _handle_bad_vectors(
|
|||||||
reader: pa.RecordBatchReader,
|
reader: pa.RecordBatchReader,
|
||||||
on_bad_vectors: Literal["error", "drop", "fill", "null"] = "error",
|
on_bad_vectors: Literal["error", "drop", "fill", "null"] = "error",
|
||||||
fill_value: float = 0.0,
|
fill_value: float = 0.0,
|
||||||
|
target_schema: Optional[pa.Schema] = None,
|
||||||
|
metadata: Optional[dict] = None,
|
||||||
) -> pa.RecordBatchReader:
|
) -> pa.RecordBatchReader:
|
||||||
vector_columns = []
|
vector_columns = _find_vector_columns(reader.schema, target_schema, metadata)
|
||||||
|
if not vector_columns:
|
||||||
|
return reader
|
||||||
|
|
||||||
for field in reader.schema:
|
output_schema = _vector_output_schema(reader.schema, vector_columns)
|
||||||
# They can provide a 'vector' column that isn't yet a FSL
|
|
||||||
named_vector_col = (
|
|
||||||
(
|
|
||||||
pa.types.is_list(field.type)
|
|
||||||
or pa.types.is_large_list(field.type)
|
|
||||||
or pa.types.is_fixed_size_list(field.type)
|
|
||||||
)
|
|
||||||
and pa.types.is_floating(field.type.value_type)
|
|
||||||
and field.name == VECTOR_COLUMN_NAME
|
|
||||||
)
|
|
||||||
# TODO: we're making an assumption that fixed size list of 10 or more
|
|
||||||
# is a vector column. This is definitely a bit hacky.
|
|
||||||
likely_vector_col = (
|
|
||||||
pa.types.is_fixed_size_list(field.type)
|
|
||||||
and pa.types.is_floating(field.type.value_type)
|
|
||||||
and (field.type.list_size >= 10)
|
|
||||||
)
|
|
||||||
|
|
||||||
if named_vector_col or likely_vector_col:
|
|
||||||
vector_columns.append(field.name)
|
|
||||||
|
|
||||||
def gen():
|
def gen():
|
||||||
for batch in reader:
|
for batch in reader:
|
||||||
for name in vector_columns:
|
pending_dims = []
|
||||||
|
for vector_column in vector_columns:
|
||||||
|
dim = vector_column["expected_dim"]
|
||||||
|
if target_schema is not None and dim is None:
|
||||||
|
dim = _infer_vector_dim(batch[vector_column["name"]])
|
||||||
|
pending_dims.append(vector_column)
|
||||||
batch = _handle_bad_vector_column(
|
batch = _handle_bad_vector_column(
|
||||||
batch,
|
batch,
|
||||||
vector_column_name=name,
|
vector_column_name=vector_column["name"],
|
||||||
on_bad_vectors=on_bad_vectors,
|
on_bad_vectors=on_bad_vectors,
|
||||||
fill_value=fill_value,
|
fill_value=fill_value,
|
||||||
|
expected_dim=dim,
|
||||||
|
expected_value_type=vector_column["expected_value_type"],
|
||||||
)
|
)
|
||||||
yield batch
|
for vector_column in pending_dims:
|
||||||
|
if vector_column["expected_dim"] is None:
|
||||||
|
vector_column["expected_dim"] = _infer_vector_dim(
|
||||||
|
batch[vector_column["name"]]
|
||||||
|
)
|
||||||
|
if batch.schema.equals(output_schema, check_metadata=True):
|
||||||
|
yield batch
|
||||||
|
continue
|
||||||
|
|
||||||
return pa.RecordBatchReader.from_batches(reader.schema, gen())
|
cast_batches = (
|
||||||
|
pa.Table.from_batches([batch]).cast(output_schema).to_batches()
|
||||||
|
)
|
||||||
|
if cast_batches:
|
||||||
|
yield pa.RecordBatch.from_arrays(
|
||||||
|
cast_batches[0].columns,
|
||||||
|
schema=output_schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
return pa.RecordBatchReader.from_batches(output_schema, gen())
|
||||||
|
|
||||||
|
|
||||||
|
def _find_vector_columns(
|
||||||
|
reader_schema: pa.Schema,
|
||||||
|
target_schema: Optional[pa.Schema],
|
||||||
|
metadata: Optional[dict],
|
||||||
|
) -> List[dict]:
|
||||||
|
if target_schema is None:
|
||||||
|
vector_columns = []
|
||||||
|
for field in reader_schema:
|
||||||
|
named_vector_col = (
|
||||||
|
_is_list_like(field.type)
|
||||||
|
and pa.types.is_floating(field.type.value_type)
|
||||||
|
and field.name == VECTOR_COLUMN_NAME
|
||||||
|
)
|
||||||
|
likely_vector_col = (
|
||||||
|
pa.types.is_fixed_size_list(field.type)
|
||||||
|
and pa.types.is_floating(field.type.value_type)
|
||||||
|
and (field.type.list_size >= 10)
|
||||||
|
)
|
||||||
|
if named_vector_col or likely_vector_col:
|
||||||
|
vector_columns.append(
|
||||||
|
{
|
||||||
|
"name": field.name,
|
||||||
|
"expected_dim": None,
|
||||||
|
"expected_value_type": None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return vector_columns
|
||||||
|
|
||||||
|
reader_column_names = set(reader_schema.names)
|
||||||
|
active_metadata = _merge_metadata(target_schema.metadata, metadata)
|
||||||
|
embedding_function_columns = set(
|
||||||
|
EmbeddingFunctionRegistry.get_instance().parse_functions(active_metadata).keys()
|
||||||
|
)
|
||||||
|
vector_columns = []
|
||||||
|
for field in target_schema:
|
||||||
|
if field.name not in reader_column_names:
|
||||||
|
continue
|
||||||
|
if not _is_list_like(field.type) or not pa.types.is_floating(
|
||||||
|
field.type.value_type
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
reader_field = reader_schema.field(field.name)
|
||||||
|
named_vector_col = (
|
||||||
|
field.name in embedding_function_columns
|
||||||
|
or field.name == VECTOR_COLUMN_NAME
|
||||||
|
or (field.name == "embedding" and pa.types.is_fixed_size_list(field.type))
|
||||||
|
)
|
||||||
|
typed_fixed_vector_col = (
|
||||||
|
pa.types.is_fixed_size_list(reader_field.type)
|
||||||
|
and pa.types.is_floating(reader_field.type.value_type)
|
||||||
|
and reader_field.type.list_size >= 10
|
||||||
|
)
|
||||||
|
|
||||||
|
if named_vector_col or typed_fixed_vector_col:
|
||||||
|
vector_columns.append(
|
||||||
|
{
|
||||||
|
"name": field.name,
|
||||||
|
"expected_dim": (
|
||||||
|
field.type.list_size
|
||||||
|
if pa.types.is_fixed_size_list(field.type)
|
||||||
|
else None
|
||||||
|
),
|
||||||
|
"expected_value_type": field.type.value_type,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return vector_columns
|
||||||
|
|
||||||
|
|
||||||
|
def _vector_output_schema(
|
||||||
|
reader_schema: pa.Schema,
|
||||||
|
vector_columns: List[dict],
|
||||||
|
) -> pa.Schema:
|
||||||
|
columns_by_name = {column["name"]: column for column in vector_columns}
|
||||||
|
fields = []
|
||||||
|
for field in reader_schema:
|
||||||
|
column = columns_by_name.get(field.name)
|
||||||
|
if column is None:
|
||||||
|
output_type = field.type
|
||||||
|
else:
|
||||||
|
output_type = _vector_output_type(field, column)
|
||||||
|
fields.append(pa.field(field.name, output_type, field.nullable, field.metadata))
|
||||||
|
return pa.schema(fields, metadata=reader_schema.metadata)
|
||||||
|
|
||||||
|
|
||||||
|
def _vector_output_type(field: pa.Field, vector_column: dict) -> pa.DataType:
|
||||||
|
if not _is_list_like(field.type):
|
||||||
|
return field.type
|
||||||
|
|
||||||
|
if vector_column["expected_value_type"] is not None and (
|
||||||
|
pa.types.is_null(field.type.value_type)
|
||||||
|
or pa.types.is_integer(field.type.value_type)
|
||||||
|
or pa.types.is_unsigned_integer(field.type.value_type)
|
||||||
|
):
|
||||||
|
return pa.list_(vector_column["expected_value_type"])
|
||||||
|
|
||||||
|
if (
|
||||||
|
vector_column["expected_dim"] is not None
|
||||||
|
and pa.types.is_fixed_size_list(field.type)
|
||||||
|
and field.type.list_size != vector_column["expected_dim"]
|
||||||
|
):
|
||||||
|
return pa.list_(field.type.value_type)
|
||||||
|
|
||||||
|
return field.type
|
||||||
|
|
||||||
|
|
||||||
def _handle_bad_vector_column(
|
def _handle_bad_vector_column(
|
||||||
@@ -3247,6 +3470,8 @@ def _handle_bad_vector_column(
|
|||||||
vector_column_name: str,
|
vector_column_name: str,
|
||||||
on_bad_vectors: str = "error",
|
on_bad_vectors: str = "error",
|
||||||
fill_value: float = 0.0,
|
fill_value: float = 0.0,
|
||||||
|
expected_dim: Optional[int] = None,
|
||||||
|
expected_value_type: Optional[pa.DataType] = None,
|
||||||
) -> pa.RecordBatch:
|
) -> pa.RecordBatch:
|
||||||
"""
|
"""
|
||||||
Ensure that the vector column exists and has type fixed_size_list(float)
|
Ensure that the vector column exists and has type fixed_size_list(float)
|
||||||
@@ -3263,14 +3488,39 @@ def _handle_bad_vector_column(
|
|||||||
fill_value: float, default 0.0
|
fill_value: float, default 0.0
|
||||||
The value to use when filling vectors. Only used if on_bad_vectors="fill".
|
The value to use when filling vectors. Only used if on_bad_vectors="fill".
|
||||||
"""
|
"""
|
||||||
|
position = data.column_names.index(vector_column_name)
|
||||||
vec_arr = data[vector_column_name]
|
vec_arr = data[vector_column_name]
|
||||||
|
if not _is_list_like(vec_arr.type):
|
||||||
|
return data
|
||||||
|
|
||||||
has_nan = has_nan_values(vec_arr)
|
if (
|
||||||
|
expected_dim is not None
|
||||||
|
and pa.types.is_fixed_size_list(vec_arr.type)
|
||||||
|
and vec_arr.type.list_size != expected_dim
|
||||||
|
):
|
||||||
|
vec_arr = pa.array(vec_arr.to_pylist(), type=pa.list_(vec_arr.type.value_type))
|
||||||
|
data = data.set_column(position, vector_column_name, vec_arr)
|
||||||
|
|
||||||
if pa.types.is_fixed_size_list(vec_arr.type):
|
if expected_value_type is not None and (
|
||||||
|
pa.types.is_integer(vec_arr.type.value_type)
|
||||||
|
or pa.types.is_unsigned_integer(vec_arr.type.value_type)
|
||||||
|
):
|
||||||
|
vec_arr = pa.array(vec_arr.to_pylist(), type=pa.list_(expected_value_type))
|
||||||
|
data = data.set_column(position, vector_column_name, vec_arr)
|
||||||
|
|
||||||
|
if pa.types.is_floating(vec_arr.type.value_type):
|
||||||
|
has_nan = has_nan_values(vec_arr)
|
||||||
|
else:
|
||||||
|
has_nan = pa.array([False] * len(vec_arr))
|
||||||
|
|
||||||
|
if expected_dim is not None:
|
||||||
|
dim = expected_dim
|
||||||
|
elif pa.types.is_fixed_size_list(vec_arr.type):
|
||||||
dim = vec_arr.type.list_size
|
dim = vec_arr.type.list_size
|
||||||
else:
|
else:
|
||||||
dim = _modal_list_size(vec_arr)
|
dim = _infer_vector_dim(vec_arr)
|
||||||
|
if dim is None:
|
||||||
|
return data
|
||||||
has_wrong_dim = pc.not_equal(pc.list_value_length(vec_arr), dim)
|
has_wrong_dim = pc.not_equal(pc.list_value_length(vec_arr), dim)
|
||||||
|
|
||||||
has_bad_vectors = pc.any(has_nan).as_py() or pc.any(has_wrong_dim).as_py()
|
has_bad_vectors = pc.any(has_nan).as_py() or pc.any(has_wrong_dim).as_py()
|
||||||
@@ -3308,13 +3558,12 @@ def _handle_bad_vector_column(
|
|||||||
)
|
)
|
||||||
vec_arr = pc.if_else(
|
vec_arr = pc.if_else(
|
||||||
is_bad,
|
is_bad,
|
||||||
pa.scalar([fill_value] * dim),
|
pa.scalar([fill_value] * dim, type=vec_arr.type),
|
||||||
vec_arr,
|
vec_arr,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid value for on_bad_vectors: {on_bad_vectors}")
|
raise ValueError(f"Invalid value for on_bad_vectors: {on_bad_vectors}")
|
||||||
|
|
||||||
position = data.column_names.index(vector_column_name)
|
|
||||||
return data.set_column(position, vector_column_name, vec_arr)
|
return data.set_column(position, vector_column_name, vec_arr)
|
||||||
|
|
||||||
|
|
||||||
@@ -3335,6 +3584,28 @@ def has_nan_values(arr: Union[pa.ListArray, pa.ChunkedArray]) -> pa.BooleanArray
|
|||||||
return pc.is_in(indices, has_nan_indices)
|
return pc.is_in(indices, has_nan_indices)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_list_like(data_type: pa.DataType) -> bool:
|
||||||
|
return (
|
||||||
|
pa.types.is_list(data_type)
|
||||||
|
or pa.types.is_large_list(data_type)
|
||||||
|
or pa.types.is_fixed_size_list(data_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_metadata(*metadata_dicts: Optional[dict]) -> dict:
|
||||||
|
merged = {}
|
||||||
|
for metadata in metadata_dicts:
|
||||||
|
if metadata is None:
|
||||||
|
continue
|
||||||
|
for key, value in metadata.items():
|
||||||
|
if isinstance(key, str):
|
||||||
|
key = key.encode("utf-8")
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = value.encode("utf-8")
|
||||||
|
merged[key] = value
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
def _name_suggests_vector_column(field_name: str) -> bool:
|
def _name_suggests_vector_column(field_name: str) -> bool:
|
||||||
"""Check if a field name indicates a vector column."""
|
"""Check if a field name indicates a vector column."""
|
||||||
name_lower = field_name.lower()
|
name_lower = field_name.lower()
|
||||||
@@ -3402,6 +3673,16 @@ def _modal_list_size(arr: Union[pa.ListArray, pa.ChunkedArray]) -> int:
|
|||||||
return pc.mode(pc.list_value_length(arr))[0].as_py()["mode"]
|
return pc.mode(pc.list_value_length(arr))[0].as_py()["mode"]
|
||||||
|
|
||||||
|
|
||||||
|
def _infer_vector_dim(arr: Union[pa.Array, pa.ChunkedArray]) -> Optional[int]:
|
||||||
|
if not _is_list_like(arr.type):
|
||||||
|
return None
|
||||||
|
lengths = pc.list_value_length(arr)
|
||||||
|
lengths = pc.filter(lengths, pc.greater(lengths, 0))
|
||||||
|
if len(lengths) == 0:
|
||||||
|
return None
|
||||||
|
return pc.mode(lengths)[0].as_py()["mode"]
|
||||||
|
|
||||||
|
|
||||||
def _validate_schema(schema: pa.Schema):
|
def _validate_schema(schema: pa.Schema):
|
||||||
"""
|
"""
|
||||||
Make sure the metadata is valid utf8
|
Make sure the metadata is valid utf8
|
||||||
@@ -3609,7 +3890,18 @@ class AsyncTable:
|
|||||||
*,
|
*,
|
||||||
replace: Optional[bool] = None,
|
replace: Optional[bool] = None,
|
||||||
config: Optional[
|
config: Optional[
|
||||||
Union[IvfFlat, IvfPq, IvfRq, HnswPq, HnswSq, BTree, Bitmap, LabelList, FTS]
|
Union[
|
||||||
|
IvfFlat,
|
||||||
|
IvfPq,
|
||||||
|
IvfRq,
|
||||||
|
HnswPq,
|
||||||
|
HnswSq,
|
||||||
|
HnswFlat,
|
||||||
|
BTree,
|
||||||
|
Bitmap,
|
||||||
|
LabelList,
|
||||||
|
FTS,
|
||||||
|
]
|
||||||
] = None,
|
] = None,
|
||||||
wait_timeout: Optional[timedelta] = None,
|
wait_timeout: Optional[timedelta] = None,
|
||||||
name: Optional[str] = None,
|
name: Optional[str] = None,
|
||||||
@@ -3656,6 +3948,7 @@ class AsyncTable:
|
|||||||
IvfRq,
|
IvfRq,
|
||||||
HnswPq,
|
HnswPq,
|
||||||
HnswSq,
|
HnswSq,
|
||||||
|
HnswFlat,
|
||||||
BTree,
|
BTree,
|
||||||
Bitmap,
|
Bitmap,
|
||||||
LabelList,
|
LabelList,
|
||||||
@@ -3675,11 +3968,13 @@ class AsyncTable:
|
|||||||
name=name,
|
name=name,
|
||||||
train=train,
|
train=train,
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except (ValueError, RuntimeError) as e:
|
||||||
if "not support the requested language" in str(e):
|
if isinstance(config, FTS):
|
||||||
supported_langs = ", ".join(lang_mapping.values())
|
_maybe_add_fts_error_note(
|
||||||
help_msg = f"Supported languages: {supported_langs}"
|
e,
|
||||||
add_note(e, help_msg)
|
base_tokenizer=config.base_tokenizer,
|
||||||
|
language=config.language,
|
||||||
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
async def drop_index(self, name: str) -> None:
|
async def drop_index(self, name: str) -> None:
|
||||||
@@ -4824,6 +5119,7 @@ class IndexStatistics:
|
|||||||
"IVF_RQ",
|
"IVF_RQ",
|
||||||
"IVF_HNSW_SQ",
|
"IVF_HNSW_SQ",
|
||||||
"IVF_HNSW_PQ",
|
"IVF_HNSW_PQ",
|
||||||
|
"IVF_HNSW_FLAT",
|
||||||
"FTS",
|
"FTS",
|
||||||
"BTREE",
|
"BTREE",
|
||||||
"BITMAP",
|
"BITMAP",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ VectorIndexType = Literal[
|
|||||||
"IVF_PQ",
|
"IVF_PQ",
|
||||||
"IVF_HNSW_SQ",
|
"IVF_HNSW_SQ",
|
||||||
"IVF_HNSW_PQ",
|
"IVF_HNSW_PQ",
|
||||||
|
"IVF_HNSW_FLAT",
|
||||||
"IVF_RQ",
|
"IVF_RQ",
|
||||||
]
|
]
|
||||||
ScalarIndexType = Literal["BTREE", "BITMAP", "LABEL_LIST"]
|
ScalarIndexType = Literal["BTREE", "BITMAP", "LABEL_LIST"]
|
||||||
@@ -31,6 +32,7 @@ IndexType = Literal[
|
|||||||
"IVF_PQ",
|
"IVF_PQ",
|
||||||
"IVF_HNSW_PQ",
|
"IVF_HNSW_PQ",
|
||||||
"IVF_HNSW_SQ",
|
"IVF_HNSW_SQ",
|
||||||
|
"IVF_HNSW_FLAT",
|
||||||
"IVF_SQ",
|
"IVF_SQ",
|
||||||
"FTS",
|
"FTS",
|
||||||
"BTREE",
|
"BTREE",
|
||||||
@@ -40,4 +42,5 @@ IndexType = Literal[
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Tokenizer literals
|
# Tokenizer literals
|
||||||
BaseTokenizerType = Literal["simple", "raw", "whitespace", "ngram"]
|
BuiltinTokenizerType = Literal["simple", "raw", "whitespace", "ngram"]
|
||||||
|
BaseTokenizerType = BuiltinTokenizerType | str
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user