mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-12-14 00:16:57 +00:00
Compare commits
182 Commits
post-updat
...
v1.29.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dd7f29edf | ||
|
|
ff680d29a8 | ||
|
|
00420dfca0 | ||
|
|
a3a86ac629 | ||
|
|
f6210b8e5e | ||
|
|
fe46af7ded | ||
|
|
57b94b411f | ||
|
|
a7b6f65851 | ||
|
|
1ec6646d8c | ||
|
|
2dccacf273 | ||
|
|
ce0f04e9ee | ||
|
|
9ba5c6d371 | ||
|
|
56673fee56 | ||
|
|
b30bcbb931 | ||
|
|
5fbe4436c8 | ||
|
|
8fa253c293 | ||
|
|
4833da9edb | ||
|
|
c0e31a4f01 | ||
|
|
c06ffb31d1 | ||
|
|
3097314b9d | ||
|
|
786a978237 | ||
|
|
03e53aaf6d | ||
|
|
2206f045a4 | ||
|
|
246cf8b2d1 | ||
|
|
82adabc5a0 | ||
|
|
c9a22247d2 | ||
|
|
c535b8ddef | ||
|
|
8e89619aed | ||
|
|
f617ca8e38 | ||
|
|
959175ad2a | ||
|
|
341ffbf5ef | ||
|
|
542f3073f4 | ||
|
|
0f134b079f | ||
|
|
9e7ae47355 | ||
|
|
1edf07df29 | ||
|
|
88aa3cddde | ||
|
|
e6846cb55a | ||
|
|
29b715e2f9 | ||
|
|
f28dc5bd2b | ||
|
|
56d0b8ea54 | ||
|
|
514edb1b79 | ||
|
|
cfb609d41d | ||
|
|
11cb062067 | ||
|
|
2ca4926ac5 | ||
|
|
834bd9b879 | ||
|
|
cac7e00983 | ||
|
|
e9300bac64 | ||
|
|
b0da7864a4 | ||
|
|
2b9d379feb | ||
|
|
8d585a04d4 | ||
|
|
0095a72fba | ||
|
|
651339648c | ||
|
|
a489f4c172 | ||
|
|
3b875ea00e | ||
|
|
9d269c499c | ||
|
|
da35ae0a6e | ||
|
|
61945b235d | ||
|
|
e936ac172d | ||
|
|
162a84cdbf | ||
|
|
92c63cf351 | ||
|
|
fca35b7476 | ||
|
|
4056657a55 | ||
|
|
685d227597 | ||
|
|
49b9f6ff38 | ||
|
|
79d0a3fb97 | ||
|
|
313ef7e79b | ||
|
|
256407be61 | ||
|
|
8b3943bd32 | ||
|
|
87b972d29a | ||
|
|
09ab61b360 | ||
|
|
2459f381b4 | ||
|
|
6442f02de4 | ||
|
|
91c4d9ea79 | ||
|
|
92a4091da3 | ||
|
|
29a337f0f9 | ||
|
|
8c3cebadaa | ||
|
|
b566458aa2 | ||
|
|
ae4344e359 | ||
|
|
b6cb384650 | ||
|
|
2c3e3d856c | ||
|
|
93e97f814c | ||
|
|
e9350f033d | ||
|
|
54c92fd6c0 | ||
|
|
4f4df83a51 | ||
|
|
a51021cab7 | ||
|
|
e33f4fdeae | ||
|
|
e407bca196 | ||
|
|
cd24ea11b4 | ||
|
|
ba578e7ab5 | ||
|
|
05a74d1e68 | ||
|
|
41d61deb97 | ||
|
|
bba292b01a | ||
|
|
96923dff33 | ||
|
|
8f9c9305da | ||
|
|
a9f309e1d1 | ||
|
|
e456a9acd8 | ||
|
|
9b7d29466c | ||
|
|
b0ef14b6f0 | ||
|
|
36febe2068 | ||
|
|
6f14a6ec18 | ||
|
|
1a45b19e7e | ||
|
|
bd7525b166 | ||
|
|
359757d939 | ||
|
|
1c6eea596c | ||
|
|
693b6f483e | ||
|
|
818a4aa6d9 | ||
|
|
ddadeb99e9 | ||
|
|
b8d8be934a | ||
|
|
7175d70b8f | ||
|
|
8a3e65ab6f | ||
|
|
4737e1a2a5 | ||
|
|
36522e951b | ||
|
|
fce046d84d | ||
|
|
3fc507bb44 | ||
|
|
fdbcd033fb | ||
|
|
aaab49baca | ||
|
|
0d0d6e8099 | ||
|
|
c1e351c92b | ||
|
|
67cab4cc9d | ||
|
|
f30a37b0fe | ||
|
|
a78a9f80dd | ||
|
|
439fee5434 | ||
|
|
9e858590e0 | ||
|
|
29eebd5f93 | ||
|
|
07da6edbdf | ||
|
|
22b83042e6 | ||
|
|
52ab13906a | ||
|
|
29bec8efd4 | ||
|
|
6947a8990b | ||
|
|
fbb2bb0c73 | ||
|
|
15918f53a9 | ||
|
|
d7f5f3a0a3 | ||
|
|
1afbf35f27 | ||
|
|
d7675233d5 | ||
|
|
c63c1ac32b | ||
|
|
6171dcde0d | ||
|
|
04bc134324 | ||
|
|
8ff39d927d | ||
|
|
ffd461c800 | ||
|
|
9134d27980 | ||
|
|
f60242979f | ||
|
|
d347417cfd | ||
|
|
55d54afd69 | ||
|
|
dca7679c47 | ||
|
|
a34b692396 | ||
|
|
63829b62e9 | ||
|
|
44c8252ad5 | ||
|
|
19ae428890 | ||
|
|
7adcb657ae | ||
|
|
9624768976 | ||
|
|
5025acfd2a | ||
|
|
4bbfdccc3e | ||
|
|
a5b24b54b8 | ||
|
|
461e69c143 | ||
|
|
915aeafefe | ||
|
|
408529d8b2 | ||
|
|
1724ab6d94 | ||
|
|
49a500a342 | ||
|
|
f26eabcfa1 | ||
|
|
b468c090f3 | ||
|
|
c14114840e | ||
|
|
7933d1f9ea | ||
|
|
6f1d3f337b | ||
|
|
9640706c5a | ||
|
|
01cd273a52 | ||
|
|
ae87d1cab9 | ||
|
|
d5a5372aba | ||
|
|
cf62af13e8 | ||
|
|
0d5e176dc2 | ||
|
|
d6f36a773d | ||
|
|
91cf94c196 | ||
|
|
753ba39199 | ||
|
|
3944c25853 | ||
|
|
925bce5fbd | ||
|
|
62065ed30d | ||
|
|
a8d55562e9 | ||
|
|
40d649ec9e | ||
|
|
c272ac8204 | ||
|
|
e18c677f0e | ||
|
|
84a288da57 | ||
|
|
cbfc325b56 | ||
|
|
ea640b076e |
5
.github/ISSUE_TEMPLATE/new_feature_issue.md
vendored
5
.github/ISSUE_TEMPLATE/new_feature_issue.md
vendored
@@ -24,6 +24,11 @@ TBD
|
|||||||
- [ ] If not, add the `no db change` label to your PR, and you're good to merge.
|
- [ ] If not, add the `no db change` label to your PR, and you're good to merge.
|
||||||
- [ ] If yes, add the `db change` label to your PR. You'll receive a message explaining you what to do.
|
- [ ] If yes, add the `db change` label to your PR. You'll receive a message explaining you what to do.
|
||||||
|
|
||||||
|
### Reminders when adding features
|
||||||
|
|
||||||
|
- [ ] Write unit tests using insta
|
||||||
|
- [ ] Write declarative integration tests in [workloads/tests](https://github.com/meilisearch/meilisearch/tree/main/workloads/test). Specify the routes to call and then call `cargo xtask test workloads/tests/YOUR_TEST.json --update-responses` so that responses are automatically filled.
|
||||||
|
|
||||||
### Reminders when modifying the API
|
### Reminders when modifying the API
|
||||||
|
|
||||||
- [ ] Update the openAPI file with utoipa:
|
- [ ] Update the openAPI file with utoipa:
|
||||||
|
|||||||
2
.github/workflows/bench-pr.yml
vendored
2
.github/workflows/bench-pr.yml
vendored
@@ -67,8 +67,6 @@ jobs:
|
|||||||
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
|
|
||||||
- name: Run benchmarks on PR ${{ github.event.issue.id }}
|
- name: Run benchmarks on PR ${{ github.event.issue.id }}
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/bench-push-indexing.yml
vendored
2
.github/workflows/bench-push-indexing.yml
vendored
@@ -13,8 +13,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
|
|
||||||
# Run benchmarks
|
# Run benchmarks
|
||||||
- name: Run benchmarks - Dataset ${BENCH_NAME} - Branch main - Commit ${{ github.sha }}
|
- name: Run benchmarks - Dataset ${BENCH_NAME} - Branch main - Commit ${{ github.sha }}
|
||||||
|
|||||||
4
.github/workflows/db-change-comments.yml
vendored
4
.github/workflows/db-change-comments.yml
vendored
@@ -19,6 +19,7 @@ env:
|
|||||||
|
|
||||||
- [ ] Detail the change to the DB format and why they are forward compatible
|
- [ ] Detail the change to the DB format and why they are forward compatible
|
||||||
- [ ] Forward-compatibility: A database created before this PR and using the features touched by this PR was able to be opened by a Meilisearch produced by the code of this PR.
|
- [ ] Forward-compatibility: A database created before this PR and using the features touched by this PR was able to be opened by a Meilisearch produced by the code of this PR.
|
||||||
|
- [ ] Declarative test: add a [declarative test containing a dumpless upgrade](https://github.com/meilisearch/meilisearch/blob/main/TESTING.md#typical-usage)
|
||||||
|
|
||||||
|
|
||||||
## This PR makes breaking changes
|
## This PR makes breaking changes
|
||||||
@@ -35,8 +36,7 @@ env:
|
|||||||
- [ ] Write the code to go from the old database to the new one
|
- [ ] Write the code to go from the old database to the new one
|
||||||
- If the change happened in milli, the upgrade function should be written and called [here](https://github.com/meilisearch/meilisearch/blob/3fd86e8d76d7d468b0095d679adb09211ca3b6c0/crates/milli/src/update/upgrade/mod.rs#L24-L47)
|
- If the change happened in milli, the upgrade function should be written and called [here](https://github.com/meilisearch/meilisearch/blob/3fd86e8d76d7d468b0095d679adb09211ca3b6c0/crates/milli/src/update/upgrade/mod.rs#L24-L47)
|
||||||
- If the change happened in the index-scheduler, we've never done it yet, but the right place to do it should be [here](https://github.com/meilisearch/meilisearch/blob/3fd86e8d76d7d468b0095d679adb09211ca3b6c0/crates/index-scheduler/src/scheduler/process_upgrade/mod.rs#L13)
|
- If the change happened in the index-scheduler, we've never done it yet, but the right place to do it should be [here](https://github.com/meilisearch/meilisearch/blob/3fd86e8d76d7d468b0095d679adb09211ca3b6c0/crates/index-scheduler/src/scheduler/process_upgrade/mod.rs#L13)
|
||||||
- [ ] Write an integration test [here](https://github.com/meilisearch/meilisearch/blob/main/crates/meilisearch/tests/upgrade/mod.rs) ensuring you can read the old database, upgrade to the new database, and read the new database as expected
|
- [ ] Declarative test: add a [declarative test containing a dumpless upgrade](https://github.com/meilisearch/meilisearch/blob/main/TESTING.md#typical-usage)
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
add-comment:
|
add-comment:
|
||||||
|
|||||||
6
.github/workflows/flaky-tests.yml
vendored
6
.github/workflows/flaky-tests.yml
vendored
@@ -13,6 +13,12 @@ jobs:
|
|||||||
image: ubuntu:22.04
|
image: ubuntu:22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- name: Install needed dependencies
|
- name: Install needed dependencies
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
|
|||||||
2
.github/workflows/fuzzer-indexing.yml
vendored
2
.github/workflows/fuzzer-indexing.yml
vendored
@@ -13,8 +13,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
|
|
||||||
# Run benchmarks
|
# Run benchmarks
|
||||||
- name: Run the fuzzer
|
- name: Run the fuzzer
|
||||||
|
|||||||
6
.github/workflows/publish-apt-brew-pkg.yml
vendored
6
.github/workflows/publish-apt-brew-pkg.yml
vendored
@@ -25,6 +25,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
apt-get install build-essential -y
|
apt-get install build-essential -y
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Install cargo-deb
|
- name: Install cargo-deb
|
||||||
run: cargo install cargo-deb
|
run: cargo install cargo-deb
|
||||||
|
|||||||
175
.github/workflows/publish-docker-images.yml
vendored
175
.github/workflows/publish-docker-images.yml
vendored
@@ -14,10 +14,105 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
build:
|
||||||
runs-on: docker
|
runs-on: ${{ matrix.runner }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform: [amd64, arm64]
|
||||||
|
edition: [community, enterprise]
|
||||||
|
include:
|
||||||
|
- platform: amd64
|
||||||
|
runner: ubuntu-24.04
|
||||||
|
- platform: arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
- edition: community
|
||||||
|
registry: getmeili/meilisearch
|
||||||
|
feature-flag: ""
|
||||||
|
- edition: enterprise
|
||||||
|
registry: getmeili/meilisearch-enterprise
|
||||||
|
feature-flag: "--features enterprise"
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=linux/${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
platforms: linux/${{ matrix.platform }}
|
||||||
|
install: true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ matrix.registry }}
|
||||||
|
# Prevent `latest` to be updated for each new tag pushed.
|
||||||
|
# We need latest and `vX.Y` tags to only be pushed for the stable Meilisearch releases.
|
||||||
|
flavor: latest=false
|
||||||
|
tags: |
|
||||||
|
type=ref,event=tag
|
||||||
|
type=raw,value=nightly,enable=${{ github.event_name != 'push' }}
|
||||||
|
type=semver,pattern=v{{major}}.{{minor}},enable=${{ steps.check-tag-format.outputs.stable == 'true' }}
|
||||||
|
type=semver,pattern=v{{major}},enable=${{ steps.check-tag-format.outputs.stable == 'true' }}
|
||||||
|
type=raw,value=latest,enable=${{ steps.check-tag-format.outputs.stable == 'true' && steps.check-tag-format.outputs.latest == 'true' }}
|
||||||
|
|
||||||
|
- name: Build and push by digest
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
id: build-and-push
|
||||||
|
with:
|
||||||
|
platforms: linux/${{ matrix.platform }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
tags: ${{ matrix.registry }}
|
||||||
|
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
||||||
|
build-args: |
|
||||||
|
COMMIT_SHA=${{ github.sha }}
|
||||||
|
COMMIT_DATE=${{ steps.build-metadata.outputs.date }}
|
||||||
|
GIT_TAG=${{ github.ref_name }}
|
||||||
|
EXTRA_ARGS=${{ matrix.feature-flag }}
|
||||||
|
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ runner.temp }}/digests
|
||||||
|
digest="${{ steps.build-and-push.outputs.digest }}"
|
||||||
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests-${{ matrix.edition }}-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: ${{ runner.temp }}/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
edition: [community, enterprise]
|
||||||
|
include:
|
||||||
|
- edition: community
|
||||||
|
registry: getmeili/meilisearch
|
||||||
|
- edition: enterprise
|
||||||
|
registry: getmeili/meilisearch-enterprise
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write # This is needed to use Cosign in keyless mode
|
id-token: write # This is needed to use Cosign in keyless mode
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
@@ -58,26 +153,30 @@ jobs:
|
|||||||
|
|
||||||
echo "date=$commit_date" >> $GITHUB_OUTPUT
|
echo "date=$commit_date" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # tag=v3.10.0
|
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # tag=v3.10.0
|
||||||
|
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ${{ runner.temp }}/digests
|
||||||
|
pattern: digests-${{ matrix.edition }}-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: getmeili/meilisearch
|
images: ${{ matrix.registry }}
|
||||||
# Prevent `latest` to be updated for each new tag pushed.
|
# Prevent `latest` to be updated for each new tag pushed.
|
||||||
# We need latest and `vX.Y` tags to only be pushed for the stable Meilisearch releases.
|
# We need latest and `vX.Y` tags to only be pushed for the stable Meilisearch releases.
|
||||||
flavor: latest=false
|
flavor: latest=false
|
||||||
@@ -88,33 +187,31 @@ jobs:
|
|||||||
type=semver,pattern=v{{major}},enable=${{ steps.check-tag-format.outputs.stable == 'true' }}
|
type=semver,pattern=v{{major}},enable=${{ steps.check-tag-format.outputs.stable == 'true' }}
|
||||||
type=raw,value=latest,enable=${{ steps.check-tag-format.outputs.stable == 'true' && steps.check-tag-format.outputs.latest == 'true' }}
|
type=raw,value=latest,enable=${{ steps.check-tag-format.outputs.stable == 'true' && steps.check-tag-format.outputs.latest == 'true' }}
|
||||||
|
|
||||||
- name: Build and push
|
- name: Create manifest list and push
|
||||||
uses: docker/build-push-action@v6
|
working-directory: ${{ runner.temp }}/digests
|
||||||
id: build-and-push
|
run: |
|
||||||
with:
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
push: true
|
$(printf '${{ matrix.registry }}@sha256:%s ' *)
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
- name: Inspect image to fetch digest to sign
|
||||||
build-args: |
|
run: |
|
||||||
COMMIT_SHA=${{ github.sha }}
|
digest=$(docker buildx imagetools inspect --format='{{ json .Manifest }}' ${{ matrix.registry }}:${{ steps.meta.outputs.version }} | jq -r '.digest')
|
||||||
COMMIT_DATE=${{ steps.build-metadata.outputs.date }}
|
echo "DIGEST=${digest}" >> $GITHUB_ENV
|
||||||
GIT_TAG=${{ github.ref_name }}
|
|
||||||
|
|
||||||
- name: Sign the images with GitHub OIDC Token
|
- name: Sign the images with GitHub OIDC Token
|
||||||
env:
|
env:
|
||||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
|
||||||
TAGS: ${{ steps.meta.outputs.tags }}
|
TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
run: |
|
run: |
|
||||||
images=""
|
images=""
|
||||||
for tag in ${TAGS}; do
|
for tag in ${TAGS}; do
|
||||||
images+="${tag}@${DIGEST} "
|
images+="${tag}@${{ env.DIGEST }} "
|
||||||
done
|
done
|
||||||
cosign sign --yes ${images}
|
cosign sign --yes ${images}
|
||||||
|
|
||||||
# /!\ Don't touch this without checking with Cloud team
|
# /!\ Don't touch this without checking with engineers working on the Cloud code base on #discussion-engineering Slack channel
|
||||||
- name: Send CI information to Cloud team
|
- name: Notify meilisearch-cloud
|
||||||
# Do not send if nightly build (i.e. 'schedule' or 'workflow_dispatch' event)
|
# Do not send if nightly build (i.e. 'schedule' or 'workflow_dispatch' event)
|
||||||
if: github.event_name == 'push'
|
if: ${{ (github.event_name == 'push') && (matrix.edition == 'enterprise') }}
|
||||||
uses: peter-evans/repository-dispatch@v3
|
uses: peter-evans/repository-dispatch@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
@@ -122,21 +219,13 @@ jobs:
|
|||||||
event-type: cloud-docker-build
|
event-type: cloud-docker-build
|
||||||
client-payload: '{ "meilisearch_version": "${{ github.ref_name }}", "stable": "${{ steps.check-tag-format.outputs.stable }}" }'
|
client-payload: '{ "meilisearch_version": "${{ github.ref_name }}", "stable": "${{ steps.check-tag-format.outputs.stable }}" }'
|
||||||
|
|
||||||
# Send notification to Swarmia to notify of a deployment: https://app.swarmia.com
|
# /!\ Don't touch this without checking with integration team members on #discussion-integrations Slack channel
|
||||||
# - name: 'Setup jq'
|
- name: Notify meilisearch-kubernetes
|
||||||
# uses: dcarbone/install-jq-action
|
# Do not send if nightly build (i.e. 'schedule' or 'workflow_dispatch' event), or if not stable
|
||||||
# - name: Send deployment to Swarmia
|
if: ${{ github.event_name == 'push' && matrix.edition == 'community' && steps.check-tag-format.outputs.stable == 'true' }}
|
||||||
# if: github.event_name == 'push' && success()
|
uses: peter-evans/repository-dispatch@v3
|
||||||
# run: |
|
with:
|
||||||
# JSON_STRING=$( jq --null-input --compact-output \
|
token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
# --arg version "${{ github.ref_name }}" \
|
repository: meilisearch/meilisearch-kubernetes
|
||||||
# --arg appName "meilisearch" \
|
event-type: meilisearch-release
|
||||||
# --arg environment "production" \
|
client-payload: '{ "version": "${{ github.ref_name }}" }'
|
||||||
# --arg commitSha "${{ github.sha }}" \
|
|
||||||
# --arg repositoryFullName "${{ github.repository }}" \
|
|
||||||
# '{"version": $version, "appName": $appName, "environment": $environment, "commitSha": $commitSha, "repositoryFullName": $repositoryFullName}' )
|
|
||||||
|
|
||||||
# curl -H "Authorization: ${{ secrets.SWARMIA_DEPLOYMENTS_AUTHORIZATION }}" \
|
|
||||||
# -H "Content-Type: application/json" \
|
|
||||||
# -d "$JSON_STRING" \
|
|
||||||
# https://hook.swarmia.com/deployments
|
|
||||||
|
|||||||
176
.github/workflows/publish-release-assets.yml
vendored
176
.github/workflows/publish-release-assets.yml
vendored
@@ -32,157 +32,61 @@ jobs:
|
|||||||
if: github.event_name == 'release' && steps.check-tag-format.outputs.stable == 'true'
|
if: github.event_name == 'release' && steps.check-tag-format.outputs.stable == 'true'
|
||||||
run: bash .github/scripts/check-release.sh
|
run: bash .github/scripts/check-release.sh
|
||||||
|
|
||||||
publish-linux:
|
publish-binaries:
|
||||||
name: Publish binary for Linux
|
name: Publish binary for ${{ matrix.release }} ${{ matrix.edition }} edition
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: check-version
|
|
||||||
container:
|
|
||||||
# Use ubuntu-22.04 to compile with glibc 2.35
|
|
||||||
image: ubuntu:22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v5
|
|
||||||
- name: Install needed dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update && apt-get install -y curl
|
|
||||||
apt-get install build-essential -y
|
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
|
||||||
- name: Build
|
|
||||||
run: cargo build --release --locked
|
|
||||||
# No need to upload binaries for dry run (cron or workflow_dispatch)
|
|
||||||
- name: Upload binaries to release
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: svenstaro/upload-release-action@2.11.2
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
|
||||||
file: target/release/meilisearch
|
|
||||||
asset_name: meilisearch-linux-amd64
|
|
||||||
tag: ${{ github.ref }}
|
|
||||||
|
|
||||||
publish-macos-windows:
|
|
||||||
name: Publish binary for ${{ matrix.os }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: check-version
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-14, windows-2022]
|
edition: [community, enterprise]
|
||||||
|
release:
|
||||||
|
[macos-amd64, macos-aarch64, windows, linux-amd64, linux-aarch64]
|
||||||
include:
|
include:
|
||||||
- os: macos-14
|
- edition: "community"
|
||||||
artifact_name: meilisearch
|
feature-flag: ""
|
||||||
asset_name: meilisearch-macos-amd64
|
edition-suffix: ""
|
||||||
- os: windows-2022
|
- edition: "enterprise"
|
||||||
artifact_name: meilisearch.exe
|
feature-flag: "--features enterprise"
|
||||||
asset_name: meilisearch-windows-amd64.exe
|
edition-suffix: "enterprise-"
|
||||||
|
- release: macos-amd64
|
||||||
|
os: macos-15-intel
|
||||||
|
binary_path: release/meilisearch
|
||||||
|
asset_name: macos-amd64
|
||||||
|
extra-args: ""
|
||||||
|
- release: macos-aarch64
|
||||||
|
os: macos-14
|
||||||
|
binary_path: aarch64-apple-darwin/release/meilisearch
|
||||||
|
asset_name: macos-apple-silicon
|
||||||
|
extra-args: "--target aarch64-apple-darwin"
|
||||||
|
- release: windows
|
||||||
|
os: windows-2022
|
||||||
|
binary_path: release/meilisearch.exe
|
||||||
|
asset_name: windows-amd64.exe
|
||||||
|
extra-args: ""
|
||||||
|
- release: linux-amd64
|
||||||
|
os: ubuntu-22.04
|
||||||
|
binary_path: x86_64-unknown-linux-gnu/release/meilisearch
|
||||||
|
asset_name: linux-amd64
|
||||||
|
extra-args: "--target x86_64-unknown-linux-gnu"
|
||||||
|
- release: linux-aarch64
|
||||||
|
os: ubuntu-22.04-arm
|
||||||
|
binary_path: aarch64-unknown-linux-gnu/release/meilisearch
|
||||||
|
asset_name: linux-aarch64
|
||||||
|
extra-args: "--target aarch64-unknown-linux-gnu"
|
||||||
|
needs: check-version
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release --locked
|
run: cargo build --release --locked ${{ matrix.feature-flag }} ${{ matrix.extra-args }}
|
||||||
# No need to upload binaries for dry run (cron or workflow_dispatch)
|
# No need to upload binaries for dry run (cron or workflow_dispatch)
|
||||||
- name: Upload binaries to release
|
- name: Upload binaries to release
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: svenstaro/upload-release-action@2.11.2
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
file: target/release/${{ matrix.artifact_name }}
|
file: target/${{ matrix.binary_path }}
|
||||||
asset_name: ${{ matrix.asset_name }}
|
asset_name: meilisearch-${{ matrix.edition-suffix }}${{ matrix.asset_name }}
|
||||||
tag: ${{ github.ref }}
|
|
||||||
|
|
||||||
publish-macos-apple-silicon:
|
|
||||||
name: Publish binary for macOS silicon
|
|
||||||
runs-on: macos-14
|
|
||||||
needs: check-version
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-apple-darwin
|
|
||||||
asset_name: meilisearch-macos-apple-silicon
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
- name: Installing Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@1.89
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
- name: Cargo build
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: --release --target ${{ matrix.target }}
|
|
||||||
- name: Upload the binary to release
|
|
||||||
# No need to upload binaries for dry run (cron or workflow_dispatch)
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: svenstaro/upload-release-action@2.11.2
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
|
||||||
file: target/${{ matrix.target }}/release/meilisearch
|
|
||||||
asset_name: ${{ matrix.asset_name }}
|
|
||||||
tag: ${{ github.ref }}
|
|
||||||
|
|
||||||
publish-aarch64:
|
|
||||||
name: Publish binary for aarch64
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: check-version
|
|
||||||
env:
|
|
||||||
DEBIAN_FRONTEND: noninteractive
|
|
||||||
container:
|
|
||||||
# Use ubuntu-22.04 to compile with glibc 2.35
|
|
||||||
image: ubuntu:22.04
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-unknown-linux-gnu
|
|
||||||
asset_name: meilisearch-linux-aarch64
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
- name: Install needed dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update -y && apt upgrade -y
|
|
||||||
apt-get install -y curl build-essential gcc-aarch64-linux-gnu
|
|
||||||
- name: Set up Docker for cross compilation
|
|
||||||
run: |
|
|
||||||
apt-get install -y curl apt-transport-https ca-certificates software-properties-common
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
|
||||||
add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
|
||||||
apt-get update -y && apt-get install -y docker-ce
|
|
||||||
- name: Installing Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@1.89
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
- name: Configure target aarch64 GNU
|
|
||||||
## Environment variable is not passed using env:
|
|
||||||
## LD gold won't work with MUSL
|
|
||||||
# env:
|
|
||||||
# JEMALLOC_SYS_WITH_LG_PAGE: 16
|
|
||||||
# RUSTFLAGS: '-Clink-arg=-fuse-ld=gold'
|
|
||||||
run: |
|
|
||||||
echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config
|
|
||||||
echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config
|
|
||||||
echo 'JEMALLOC_SYS_WITH_LG_PAGE=16' >> $GITHUB_ENV
|
|
||||||
- name: Install a default toolchain that will be used to build cargo cross
|
|
||||||
run: |
|
|
||||||
rustup default stable
|
|
||||||
- name: Cargo build
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
use-cross: true
|
|
||||||
args: --release --target ${{ matrix.target }}
|
|
||||||
env:
|
|
||||||
CROSS_DOCKER_IN_DOCKER: true
|
|
||||||
- name: List target output files
|
|
||||||
run: ls -lR ./target
|
|
||||||
- name: Upload the binary to release
|
|
||||||
# No need to upload binaries for dry run (cron or workflow_dispatch)
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: svenstaro/upload-release-action@2.11.2
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
|
||||||
file: target/${{ matrix.target }}/release/meilisearch
|
|
||||||
asset_name: ${{ matrix.asset_name }}
|
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
|
|
||||||
publish-openapi-file:
|
publish-openapi-file:
|
||||||
|
|||||||
24
.github/workflows/sdks-tests.yml
vendored
24
.github/workflows/sdks-tests.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -122,7 +122,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -149,7 +149,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -213,7 +213,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -238,7 +238,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -263,7 +263,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -284,7 +284,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -307,7 +307,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -338,7 +338,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
@@ -370,7 +370,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:${{ needs.define-docker-image.outputs.docker-image }}
|
image: getmeili/meilisearch-enterprise:${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
MEILI_MASTER_KEY: ${{ env.MEILI_MASTER_KEY }}
|
||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
|
|||||||
148
.github/workflows/test-suite.yml
vendored
148
.github/workflows/test-suite.yml
vendored
@@ -15,31 +15,40 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-linux:
|
test-linux:
|
||||||
name: Tests on ubuntu-22.04
|
name: Tests on Ubuntu
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.runner }}
|
||||||
container:
|
strategy:
|
||||||
# Use ubuntu-22.04 to compile with glibc 2.35
|
matrix:
|
||||||
image: ubuntu:22.04
|
runner: [ubuntu-22.04, ubuntu-22.04-arm]
|
||||||
|
features: ["", "--features enterprise"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Install needed dependencies
|
- name: check free space before
|
||||||
|
run: df -h
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
sudo rm -rf "/opt/ghc" || true
|
||||||
apt-get install build-essential -y
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
|
- name: check free space after
|
||||||
|
run: df -h
|
||||||
- name: Setup test with Rust stable
|
- name: Setup test with Rust stable
|
||||||
uses: dtolnay/rust-toolchain@1.89
|
uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.8.0
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- name: Run cargo check without any default features
|
with:
|
||||||
|
key: ${{ matrix.features }}
|
||||||
|
- name: Run cargo build without any default features
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --locked --release --no-default-features --all
|
args: --locked --no-default-features --all
|
||||||
- name: Run cargo test
|
- name: Run cargo test
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --locked --release --all
|
args: --locked --all ${{ matrix.features }}
|
||||||
|
|
||||||
test-others:
|
test-others:
|
||||||
name: Tests on ${{ matrix.os }}
|
name: Tests on ${{ matrix.os }}
|
||||||
@@ -48,50 +57,57 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-14, windows-2022]
|
os: [macos-14, windows-2022]
|
||||||
|
features: ["", "--features enterprise"]
|
||||||
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.8.0
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Run cargo check without any default features
|
- name: Run cargo build without any default features
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --locked --release --no-default-features --all
|
args: --locked --no-default-features --all
|
||||||
- name: Run cargo test
|
- name: Run cargo test
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --locked --release --all
|
args: --locked --all ${{ matrix.features }}
|
||||||
|
|
||||||
test-all-features:
|
test-all-features:
|
||||||
name: Tests almost all features
|
name: Tests almost all features
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
container:
|
|
||||||
# Use ubuntu-22.04 to compile with glibc 2.35
|
|
||||||
image: ubuntu:22.04
|
|
||||||
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Install needed dependencies
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
sudo rm -rf "/opt/ghc" || true
|
||||||
apt-get install --assume-yes build-essential curl
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Run cargo build with almost all features
|
- name: Run cargo build with almost all features
|
||||||
run: |
|
run: |
|
||||||
cargo build --workspace --locked --release --features "$(cargo xtask list-features --exclude-feature cuda,test-ollama)"
|
cargo build --workspace --locked --features "$(cargo xtask list-features --exclude-feature cuda,test-ollama)"
|
||||||
- name: Run cargo test with almost all features
|
- name: Run cargo test with almost all features
|
||||||
run: |
|
run: |
|
||||||
cargo test --workspace --locked --release --features "$(cargo xtask list-features --exclude-feature cuda,test-ollama)"
|
cargo test --workspace --locked --features "$(cargo xtask list-features --exclude-feature cuda,test-ollama)"
|
||||||
|
|
||||||
ollama-ubuntu:
|
ollama-ubuntu:
|
||||||
name: Test with Ollama
|
name: Test with Ollama
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
env:
|
env:
|
||||||
MEILI_TEST_OLLAMA_SERVER: "http://localhost:11434"
|
MEILI_TEST_OLLAMA_SERVER: "http://localhost:11434"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- name: Install Ollama
|
- name: Install Ollama
|
||||||
run: |
|
run: |
|
||||||
curl -fsSL https://ollama.com/install.sh | sudo -E sh
|
curl -fsSL https://ollama.com/install.sh | sudo -E sh
|
||||||
@@ -115,20 +131,20 @@ jobs:
|
|||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --locked --release --all --features test-ollama ollama
|
args: --locked -p meilisearch --features test-ollama ollama
|
||||||
|
|
||||||
test-disabled-tokenization:
|
test-disabled-tokenization:
|
||||||
name: Test disabled tokenization
|
name: Test disabled tokenization
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
container:
|
|
||||||
image: ubuntu:22.04
|
|
||||||
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Install needed dependencies
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
sudo rm -rf "/opt/ghc" || true
|
||||||
apt-get install --assume-yes build-essential curl
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Run cargo tree without default features and check lindera is not present
|
- name: Run cargo tree without default features and check lindera is not present
|
||||||
run: |
|
run: |
|
||||||
@@ -140,36 +156,39 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cargo tree -f '{p} {f}' -e normal | grep lindera -qz
|
cargo tree -f '{p} {f}' -e normal | grep lindera -qz
|
||||||
|
|
||||||
# We run tests in debug also, to make sure that the debug_assertions are hit
|
build:
|
||||||
test-debug:
|
name: Build in release
|
||||||
name: Run tests in debug
|
runs-on: ubuntu-22.04
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
# Use ubuntu-22.04 to compile with glibc 2.35
|
|
||||||
image: ubuntu:22.04
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- name: Install needed dependencies
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
sudo rm -rf "/opt/ghc" || true
|
||||||
apt-get install build-essential -y
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.8.0
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- name: Run tests in debug
|
- name: Build
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo build --release --locked --target x86_64-unknown-linux-gnu
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --locked --all
|
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
name: Run Clippy
|
name: Run Clippy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
features: ["", "--features enterprise"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
components: clippy
|
components: clippy
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.8.0
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
@@ -177,18 +196,21 @@ jobs:
|
|||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --all-targets -- --deny warnings
|
args: --all-targets ${{ matrix.features }} -- --deny warnings
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Run Rustfmt
|
name: Run Rustfmt
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2024-07-09
|
|
||||||
override: true
|
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.8.0
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
@@ -199,3 +221,23 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo -ne "\n" > crates/benchmarks/benches/datasets_paths.rs
|
echo -ne "\n" > crates/benchmarks/benches/datasets_paths.rs
|
||||||
cargo fmt --all -- --check
|
cargo fmt --all -- --check
|
||||||
|
|
||||||
|
declarative-tests:
|
||||||
|
name: Run declarative tests
|
||||||
|
runs-on: ubuntu-22.04-arm
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
|
- name: Run declarative tests
|
||||||
|
run: |
|
||||||
|
cargo xtask test workloads/tests/*.json
|
||||||
|
|||||||
@@ -18,9 +18,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
- name: Clean space as per https://github.com/actions/virtual-environments/issues/709
|
||||||
|
run: |
|
||||||
|
sudo rm -rf "/opt/ghc" || true
|
||||||
|
sudo rm -rf "/usr/share/dotnet" || true
|
||||||
|
sudo rm -rf "/usr/local/lib/android" || true
|
||||||
|
sudo rm -rf "/usr/local/share/boost" || true
|
||||||
- uses: dtolnay/rust-toolchain@1.89
|
- uses: dtolnay/rust-toolchain@1.89
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
- name: Install sd
|
- name: Install sd
|
||||||
run: cargo install sd
|
run: cargo install sd
|
||||||
- name: Update Cargo.toml file
|
- name: Update Cargo.toml file
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ They are JSON files with the following structure (comments are not actually supp
|
|||||||
{
|
{
|
||||||
// Name of the workload. Must be unique to the workload, as it will be used to group results on the dashboard.
|
// Name of the workload. Must be unique to the workload, as it will be used to group results on the dashboard.
|
||||||
"name": "hackernews.ndjson_1M,no-threads",
|
"name": "hackernews.ndjson_1M,no-threads",
|
||||||
|
"type": "bench",
|
||||||
// Number of consecutive runs of the commands that should be performed.
|
// Number of consecutive runs of the commands that should be performed.
|
||||||
// Each run uses a fresh instance of Meilisearch and a fresh database.
|
// Each run uses a fresh instance of Meilisearch and a fresh database.
|
||||||
// Each run produces its own report file.
|
// Each run produces its own report file.
|
||||||
|
|||||||
1166
Cargo.lock
generated
1166
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "1.26.0"
|
version = "1.29.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Quentin de Quelen <quentin@dequelen.me>",
|
"Quentin de Quelen <quentin@dequelen.me>",
|
||||||
"Clément Renault <clement@meilisearch.com>",
|
"Clément Renault <clement@meilisearch.com>",
|
||||||
@@ -50,3 +50,5 @@ opt-level = 3
|
|||||||
opt-level = 3
|
opt-level = 3
|
||||||
[profile.dev.package.roaring]
|
[profile.dev.package.roaring]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
[profile.dev.package.gemm-f16]
|
||||||
|
opt-level = 3
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
[build.env]
|
|
||||||
passthrough = [
|
|
||||||
"RUST_BACKTRACE",
|
|
||||||
"CARGO_TERM_COLOR",
|
|
||||||
"RUSTFLAGS",
|
|
||||||
"JEMALLOC_SYS_WITH_LG_PAGE"
|
|
||||||
]
|
|
||||||
@@ -8,16 +8,14 @@ WORKDIR /
|
|||||||
ARG COMMIT_SHA
|
ARG COMMIT_SHA
|
||||||
ARG COMMIT_DATE
|
ARG COMMIT_DATE
|
||||||
ARG GIT_TAG
|
ARG GIT_TAG
|
||||||
|
ARG EXTRA_ARGS
|
||||||
ENV VERGEN_GIT_SHA=${COMMIT_SHA} VERGEN_GIT_COMMIT_TIMESTAMP=${COMMIT_DATE} VERGEN_GIT_DESCRIBE=${GIT_TAG}
|
ENV VERGEN_GIT_SHA=${COMMIT_SHA} VERGEN_GIT_COMMIT_TIMESTAMP=${COMMIT_DATE} VERGEN_GIT_DESCRIBE=${GIT_TAG}
|
||||||
ENV RUSTFLAGS="-C target-feature=-crt-static"
|
ENV RUSTFLAGS="-C target-feature=-crt-static"
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN set -eux; \
|
RUN set -eux; \
|
||||||
apkArch="$(apk --print-arch)"; \
|
apkArch="$(apk --print-arch)"; \
|
||||||
if [ "$apkArch" = "aarch64" ]; then \
|
cargo build --release -p meilisearch -p meilitool ${EXTRA_ARGS}
|
||||||
export JEMALLOC_SYS_WITH_LG_PAGE=16; \
|
|
||||||
fi && \
|
|
||||||
cargo build --release -p meilisearch -p meilitool
|
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
FROM alpine:3.22
|
FROM alpine:3.22
|
||||||
|
|||||||
326
TESTING.md
Normal file
326
TESTING.md
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
# Declarative tests
|
||||||
|
|
||||||
|
Declarative tests ensure that Meilisearch features remain stable across versions.
|
||||||
|
|
||||||
|
While we already have unit tests, those are run against **temporary databases** that are created fresh each time and therefore never risk corruption.
|
||||||
|
|
||||||
|
Declarative tests instead **simulate the lifetime of a database**: they chain together commands and requests to change the binary, verifying that database state and API responses remain consistent.
|
||||||
|
|
||||||
|
## Basic example
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"name": "api-keys",
|
||||||
|
"binary": { // the first command will run on the binary following this specification.
|
||||||
|
"source": "release", // get the binary as a release from GitHub
|
||||||
|
"version": "1.19.0", // version to fetch
|
||||||
|
"edition": "community" // edition to fetch
|
||||||
|
},
|
||||||
|
"commands": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This example defines a no-op test (it does nothing).
|
||||||
|
|
||||||
|
If the file is saved at `workloads/tests/example.json`, you can run it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo xtask test workloads/tests/example.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
Commands represent API requests sent to Meilisearch endpoints during a test.
|
||||||
|
|
||||||
|
They are executed sequentially, and their responses can be validated to ensure consistent behavior across upgrades.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
|
||||||
|
{
|
||||||
|
"route": "keys",
|
||||||
|
"method": "POST",
|
||||||
|
"body": {
|
||||||
|
"inline": {
|
||||||
|
"actions": [
|
||||||
|
"search",
|
||||||
|
"documents.add"
|
||||||
|
],
|
||||||
|
"description": "Test API Key",
|
||||||
|
"expiresAt": null,
|
||||||
|
"indexes": [ "movies" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This command issues a `POST /keys` request, creating an API key with permissions to search and add documents in the `movies` index.
|
||||||
|
|
||||||
|
### Using assets in commands
|
||||||
|
|
||||||
|
To keep tests concise and reusable, you can define **assets** at the root of the workload file.
|
||||||
|
|
||||||
|
Assets are external data sources (such as datasets) that are cached between runs, making tests faster and easier to read.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"name": "movies",
|
||||||
|
"binary": {
|
||||||
|
"source": "release",
|
||||||
|
"version": "1.19.0",
|
||||||
|
"edition": "community"
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"movies.json": {
|
||||||
|
"local_location": null,
|
||||||
|
"remote_location": "https://milli-benchmarks.fra1.digitaloceanspaces.com/bench/datasets/movies.json",
|
||||||
|
"sha256": "5b6e4cb660bc20327776e8a33ea197b43d9ec84856710ead1cc87ab24df77de1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"route": "indexes/movies/documents",
|
||||||
|
"method": "POST",
|
||||||
|
"body": {
|
||||||
|
"asset": "movies.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example:
|
||||||
|
- The `movies.json` dataset is defined as an asset, pointing to a remote URL.
|
||||||
|
- The SHA-256 checksum ensures integrity.
|
||||||
|
- The `POST /indexes/movies/documents` command uses this asset as the request body.
|
||||||
|
|
||||||
|
This makes the test much cleaner than inlining a large dataset directly into the command.
|
||||||
|
|
||||||
|
For asset handling, please refer to the [declarative benchmarks documentation](/BENCHMARKS.md#adding-new-assets).
|
||||||
|
|
||||||
|
### Asserting responses
|
||||||
|
|
||||||
|
Commands can specify both the **expected status code** and the **expected response body**.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"route": "indexes/movies/documents",
|
||||||
|
"method": "POST",
|
||||||
|
"body": {
|
||||||
|
"asset": "movies.json"
|
||||||
|
},
|
||||||
|
"expectedStatus": 202,
|
||||||
|
"expectedResponse": {
|
||||||
|
"enqueuedAt": "[timestamp]", // Set to a bracketed string to ignore the value
|
||||||
|
"indexUid": "movies",
|
||||||
|
"status": "enqueued",
|
||||||
|
"taskUid": 1,
|
||||||
|
"type": "documentAdditionOrUpdate"
|
||||||
|
},
|
||||||
|
"synchronous": "WaitForTask"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Manually writing `expectedResponse` fields can be tedious.
|
||||||
|
|
||||||
|
Instead, you can let the test runner populate them automatically:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the workload to populate expected fields. Only adds the missing ones, doesn't change existing data
|
||||||
|
cargo xtask test workloads/tests/example.json --add-missing-responses
|
||||||
|
|
||||||
|
# OR
|
||||||
|
|
||||||
|
# Run the workload to populate expected fields. Updates all fields including existing ones
|
||||||
|
cargo xtask test workloads/tests/example.json --update-responses
|
||||||
|
```
|
||||||
|
|
||||||
|
This workflow is recommended:
|
||||||
|
|
||||||
|
1. Write the test without expected fields.
|
||||||
|
2. Run it with `--add-missing-responses` to capture the actual responses.
|
||||||
|
3. Review and commit the generated expectations.
|
||||||
|
|
||||||
|
## Changing binary
|
||||||
|
|
||||||
|
It is possible to insert an instruction to change the current Meilisearch instance from one binary specification to another during a test.
|
||||||
|
|
||||||
|
When executed, such an instruction will:
|
||||||
|
1. Stop the current Meilisearch instance.
|
||||||
|
2. Fetch the binary specified by the instruction.
|
||||||
|
3. Restart the server with the specified binary on the same database.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"name": "movies",
|
||||||
|
"binary": {
|
||||||
|
"source": "release",
|
||||||
|
"version": "1.19.0", // start with version v1.19.0
|
||||||
|
"edition": "community"
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"movies.json": {
|
||||||
|
"local_location": null,
|
||||||
|
"remote_location": "https://milli-benchmarks.fra1.digitaloceanspaces.com/bench/datasets/movies.json",
|
||||||
|
"sha256": "5b6e4cb660bc20327776e8a33ea197b43d9ec84856710ead1cc87ab24df77de1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": [
|
||||||
|
// setup some data
|
||||||
|
{
|
||||||
|
"route": "indexes/movies/documents",
|
||||||
|
"method": "POST",
|
||||||
|
"body": {
|
||||||
|
"asset": "movies.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// switch binary to v1.24.0
|
||||||
|
{
|
||||||
|
"binary": {
|
||||||
|
"source": "release",
|
||||||
|
"version": "1.24.0",
|
||||||
|
"edition": "community"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Typical Usage
|
||||||
|
|
||||||
|
In most cases, the change binary instruction will be used to update a database.
|
||||||
|
|
||||||
|
- **Set up** some data using commands on an older version.
|
||||||
|
- **Upgrade** to the latest version.
|
||||||
|
- **Assert** that the data and API behavior remain correct after the upgrade.
|
||||||
|
|
||||||
|
To properly test the dumpless upgrade, one should typically:
|
||||||
|
|
||||||
|
1. Open the database without processing the update task: Use a `binary` instruction to switch to the desired version, passing `--experimental-dumpless-upgrade` and `--experimental-max-number-of-batched-tasks=0` as extra CLI arguments
|
||||||
|
2. Check that the search, stats and task queue still work.
|
||||||
|
3. Open the database and process the update task: Use a `binary` instruction to switch to the desired version, passing `--experimental-dumpless-upgrade` as the extra CLI argument. Use a `health` command to wait for the upgrade task to finish.
|
||||||
|
4. Check that the indexing, search, stats, and task queue still work.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"name": "movies",
|
||||||
|
"binary": {
|
||||||
|
"source": "release",
|
||||||
|
"version": "1.12.0",
|
||||||
|
"edition": "community"
|
||||||
|
},
|
||||||
|
"commands": [
|
||||||
|
// 0. Run commands to populate the database
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
},
|
||||||
|
// 1. Open the database with new MS without processing the update task
|
||||||
|
{
|
||||||
|
"binary": {
|
||||||
|
"source": "build", // build the binary from the sources in the current git repository
|
||||||
|
"edition": "community",
|
||||||
|
"extraCliArgs": [
|
||||||
|
"--experimental-dumpless-upgrade", // allows to open with a newer MS
|
||||||
|
"--experimental-max-number-of-batched-tasks=0" // prevent processing of the update task
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 2. Check the search etc.
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
},
|
||||||
|
// 3. Open the database with new MS and processing the update task
|
||||||
|
{
|
||||||
|
"binary": {
|
||||||
|
"source": "build", // build the binary from the sources in the current git repository
|
||||||
|
"edition": "community",
|
||||||
|
"extraCliArgs": [
|
||||||
|
"--experimental-dumpless-upgrade" // allows to open with a newer MS
|
||||||
|
// no `--experimental-max-number-of-batched-tasks=0`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 4. Check the indexing, search, etc.
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures backward compatibility: databases created with older Meilisearch versions should remain functional and consistent after an upgrade.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
Sometimes a command needs to use a value returned by a **previous response**.
|
||||||
|
These values can be captured and reused using the register field.
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"route": "keys",
|
||||||
|
"method": "POST",
|
||||||
|
"body": {
|
||||||
|
"inline": {
|
||||||
|
"actions": [
|
||||||
|
"search",
|
||||||
|
"documents.add"
|
||||||
|
],
|
||||||
|
"description": "Test API Key",
|
||||||
|
"expiresAt": null,
|
||||||
|
"indexes": [ "movies" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expectedResponse": {
|
||||||
|
"key": "c6f64630bad2996b1f675007c8800168e14adf5d6a7bb1a400a6d2b158050eaf",
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"key": "/key"
|
||||||
|
},
|
||||||
|
"synchronous": "WaitForResponse"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `register` field captures the value at the JSON path `/key` from the response.
|
||||||
|
Paths follow the **JavaScript Object Notation Pointer (RFC 6901)** format.
|
||||||
|
Registered variables are available for all subsequent commands.
|
||||||
|
|
||||||
|
Registered variables can be referenced by wrapping their name in double curly braces:
|
||||||
|
|
||||||
|
In the route/path:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"route": "tasks/{{ task_id }}",
|
||||||
|
"method": "GET"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the request body:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"route": "indexes/movies/documents",
|
||||||
|
"method": "PATCH",
|
||||||
|
"body": {
|
||||||
|
"inline": {
|
||||||
|
"id": "{{ document_id }}",
|
||||||
|
"overview": "Shazam turns evil and the world is in danger.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or they can be referenced by their name (**without curly braces**) as an API key:
|
||||||
|
|
||||||
|
```jsonc
|
||||||
|
{
|
||||||
|
"route": "indexes/movies/documents",
|
||||||
|
"method": "POST",
|
||||||
|
"body": { /* ... */ },
|
||||||
|
"apiKeyVariable": "key" // The **content** of the key variable will be used as an API key
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -11,27 +11,27 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
bumpalo = "3.18.1"
|
bumpalo = "3.19.0"
|
||||||
csv = "1.3.1"
|
csv = "1.4.0"
|
||||||
memmap2 = "0.9.7"
|
memmap2 = "0.9.9"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
mimalloc = { version = "0.1.47", default-features = false }
|
mimalloc = { version = "0.1.48", default-features = false }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.6.0", features = ["html_reports"] }
|
criterion = { version = "0.7.0", features = ["html_reports"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rand_chacha = "0.3.1"
|
rand_chacha = "0.3.1"
|
||||||
roaring = "0.10.12"
|
roaring = "0.10.12"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
bytes = "1.10.1"
|
bytes = "1.11.0"
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.9.0"
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.5"
|
||||||
reqwest = { version = "0.12.20", features = ["blocking", "rustls-tls"], default-features = false }
|
reqwest = { version = "0.12.24", features = ["blocking", "rustls-tls"], default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["milli/all-tokenizations"]
|
default = ["milli/all-tokenizations"]
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ use roaring::RoaringBitmap;
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
fn no_cancel() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
const BENCHMARK_ITERATION: usize = 10;
|
const BENCHMARK_ITERATION: usize = 10;
|
||||||
|
|
||||||
fn setup_dir(path: impl AsRef<Path>) {
|
fn setup_dir(path: impl AsRef<Path>) {
|
||||||
@@ -65,7 +69,7 @@ fn setup_settings<'t>(
|
|||||||
let sortable_fields = sortable_fields.iter().map(|s| s.to_string()).collect();
|
let sortable_fields = sortable_fields.iter().map(|s| s.to_string()).collect();
|
||||||
builder.set_sortable_fields(sortable_fields);
|
builder.set_sortable_fields(sortable_fields);
|
||||||
|
|
||||||
builder.execute(&|| false, &Progress::default(), Default::default()).unwrap();
|
builder.execute(&no_cancel, &Progress::default(), Default::default()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_index_with_settings(
|
fn setup_index_with_settings(
|
||||||
@@ -152,7 +156,7 @@ fn indexing_songs_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -168,7 +172,7 @@ fn indexing_songs_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -220,7 +224,7 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -236,7 +240,7 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -266,7 +270,7 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -282,7 +286,7 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -336,7 +340,7 @@ fn deleting_songs_in_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -352,7 +356,7 @@ fn deleting_songs_in_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -414,7 +418,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -430,7 +434,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -460,7 +464,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -476,7 +480,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -502,7 +506,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -518,7 +522,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -571,7 +575,7 @@ fn indexing_songs_without_faceted_numbers(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -587,7 +591,7 @@ fn indexing_songs_without_faceted_numbers(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -639,7 +643,7 @@ fn indexing_songs_without_faceted_fields(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -655,7 +659,7 @@ fn indexing_songs_without_faceted_fields(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -707,7 +711,7 @@ fn indexing_wiki(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -723,7 +727,7 @@ fn indexing_wiki(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -774,7 +778,7 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -790,7 +794,7 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -820,7 +824,7 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -836,7 +840,7 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -889,7 +893,7 @@ fn deleting_wiki_in_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -905,7 +909,7 @@ fn deleting_wiki_in_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -967,7 +971,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -983,7 +987,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1014,7 +1018,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1030,7 +1034,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1057,7 +1061,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1073,7 +1077,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1125,7 +1129,7 @@ fn indexing_movies_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1141,7 +1145,7 @@ fn indexing_movies_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1192,7 +1196,7 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1208,7 +1212,7 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1238,7 +1242,7 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1254,7 +1258,7 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1307,7 +1311,7 @@ fn deleting_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1323,7 +1327,7 @@ fn deleting_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1372,7 +1376,7 @@ fn delete_documents_from_ids(index: Index, document_ids_to_delete: Vec<RoaringBi
|
|||||||
Some(primary_key),
|
Some(primary_key),
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1422,7 +1426,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1438,7 +1442,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1468,7 +1472,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1484,7 +1488,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1510,7 +1514,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1526,7 +1530,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1601,7 +1605,7 @@ fn indexing_nested_movies_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1617,7 +1621,7 @@ fn indexing_nested_movies_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1693,7 +1697,7 @@ fn deleting_nested_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1709,7 +1713,7 @@ fn deleting_nested_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1777,7 +1781,7 @@ fn indexing_nested_movies_without_faceted_fields(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1793,7 +1797,7 @@ fn indexing_nested_movies_without_faceted_fields(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1845,7 +1849,7 @@ fn indexing_geo(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1861,7 +1865,7 @@ fn indexing_geo(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1912,7 +1916,7 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1928,7 +1932,7 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -1958,7 +1962,7 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1974,7 +1978,7 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2027,7 +2031,7 @@ fn deleting_geo_in_batches_default(c: &mut Criterion) {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2043,7 +2047,7 @@ fn deleting_geo_in_batches_default(c: &mut Criterion) {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ license.workspace = true
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = { version = "0.3.41", features = ["parsing"] }
|
time = { version = "0.3.44", features = ["parsing"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
vergen-git2 = "1.0.7"
|
vergen-gitcl = "1.0.8"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ fn emit_git_variables() -> anyhow::Result<()> {
|
|||||||
// Note: any code that needs VERGEN_ environment variables should take care to define them manually in the Dockerfile and pass them
|
// Note: any code that needs VERGEN_ environment variables should take care to define them manually in the Dockerfile and pass them
|
||||||
// in the corresponding GitHub workflow (publish_docker.yml).
|
// in the corresponding GitHub workflow (publish_docker.yml).
|
||||||
// This is due to the Dockerfile building the binary outside of the git directory.
|
// This is due to the Dockerfile building the binary outside of the git directory.
|
||||||
let mut builder = vergen_git2::Git2Builder::default();
|
let mut builder = vergen_gitcl::GitclBuilder::default();
|
||||||
|
|
||||||
builder.branch(true);
|
builder.branch(true);
|
||||||
builder.commit_timestamp(true);
|
builder.commit_timestamp(true);
|
||||||
@@ -25,5 +25,5 @@ fn emit_git_variables() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let git2 = builder.build()?;
|
let git2 = builder.build()?;
|
||||||
|
|
||||||
vergen_git2::Emitter::default().fail_on_error().add_instructions(&git2)?.emit()
|
vergen_gitcl::Emitter::default().fail_on_error().add_instructions(&git2)?.emit()
|
||||||
}
|
}
|
||||||
|
|||||||
6
crates/build-info/src/main.rs
Normal file
6
crates/build-info/src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
use build_info::BuildInfo;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let info = BuildInfo::from_build();
|
||||||
|
dbg!(info);
|
||||||
|
}
|
||||||
@@ -11,24 +11,27 @@ readme.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.5"
|
||||||
http = "1.3.1"
|
http = "1.3.1"
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
regex = "1.11.1"
|
regex = "1.12.2"
|
||||||
roaring = { version = "0.10.12", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
tar = "0.4.44"
|
tar = "0.4.44"
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
time = { version = "0.3.41", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
time = { version = "0.3.44", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
meili-snap = { path = "../meili-snap" }
|
meili-snap = { path = "../meili-snap" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
enterprise = ["meilisearch-types/enterprise"]
|
||||||
@@ -262,13 +262,13 @@ pub(crate) mod test {
|
|||||||
use big_s::S;
|
use big_s::S;
|
||||||
use maplit::{btreemap, btreeset};
|
use maplit::{btreemap, btreeset};
|
||||||
use meilisearch_types::batches::{Batch, BatchEnqueuedAt, BatchStats};
|
use meilisearch_types::batches::{Batch, BatchEnqueuedAt, BatchStats};
|
||||||
use meilisearch_types::enterprise_edition::network::{Network, Remote};
|
|
||||||
use meilisearch_types::facet_values_sort::FacetValuesSort;
|
use meilisearch_types::facet_values_sort::FacetValuesSort;
|
||||||
use meilisearch_types::features::RuntimeTogglableFeatures;
|
use meilisearch_types::features::RuntimeTogglableFeatures;
|
||||||
use meilisearch_types::index_uid_pattern::IndexUidPattern;
|
use meilisearch_types::index_uid_pattern::IndexUidPattern;
|
||||||
use meilisearch_types::keys::{Action, Key};
|
use meilisearch_types::keys::{Action, Key};
|
||||||
use meilisearch_types::milli::update::Setting;
|
use meilisearch_types::milli::update::Setting;
|
||||||
use meilisearch_types::milli::{self, FilterableAttributesRule};
|
use meilisearch_types::milli::{self, FilterableAttributesRule};
|
||||||
|
use meilisearch_types::network::{Network, Remote};
|
||||||
use meilisearch_types::settings::{Checked, FacetingSettings, Settings};
|
use meilisearch_types::settings::{Checked, FacetingSettings, Settings};
|
||||||
use meilisearch_types::task_view::DetailsView;
|
use meilisearch_types::task_view::DetailsView;
|
||||||
use meilisearch_types::tasks::{BatchStopReason, Details, Kind, Status};
|
use meilisearch_types::tasks::{BatchStopReason, Details, Kind, Status};
|
||||||
@@ -341,7 +341,6 @@ pub(crate) mod test {
|
|||||||
prefix_search: Setting::NotSet,
|
prefix_search: Setting::NotSet,
|
||||||
chat: Setting::NotSet,
|
chat: Setting::NotSet,
|
||||||
vector_store: Setting::NotSet,
|
vector_store: Setting::NotSet,
|
||||||
execute_after_update: Setting::NotSet,
|
|
||||||
_kind: std::marker::PhantomData,
|
_kind: std::marker::PhantomData,
|
||||||
};
|
};
|
||||||
settings.check()
|
settings.check()
|
||||||
|
|||||||
@@ -423,7 +423,6 @@ impl<T> From<v5::Settings<T>> for v6::Settings<v6::Unchecked> {
|
|||||||
prefix_search: v6::Setting::NotSet,
|
prefix_search: v6::Setting::NotSet,
|
||||||
chat: v6::Setting::NotSet,
|
chat: v6::Setting::NotSet,
|
||||||
vector_store: v6::Setting::NotSet,
|
vector_store: v6::Setting::NotSet,
|
||||||
execute_after_update: v6::Setting::NotSet,
|
|
||||||
_kind: std::marker::PhantomData,
|
_kind: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub type Batch = meilisearch_types::batches::Batch;
|
|||||||
pub type Key = meilisearch_types::keys::Key;
|
pub type Key = meilisearch_types::keys::Key;
|
||||||
pub type ChatCompletionSettings = meilisearch_types::features::ChatCompletionSettings;
|
pub type ChatCompletionSettings = meilisearch_types::features::ChatCompletionSettings;
|
||||||
pub type RuntimeTogglableFeatures = meilisearch_types::features::RuntimeTogglableFeatures;
|
pub type RuntimeTogglableFeatures = meilisearch_types::features::RuntimeTogglableFeatures;
|
||||||
pub type Network = meilisearch_types::enterprise_edition::network::Network;
|
pub type Network = meilisearch_types::network::Network;
|
||||||
pub type Webhooks = meilisearch_types::webhooks::WebhooksDumpView;
|
pub type Webhooks = meilisearch_types::webhooks::WebhooksDumpView;
|
||||||
|
|
||||||
// ===== Other types to clarify the code of the compat module
|
// ===== Other types to clarify the code of the compat module
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ use std::path::PathBuf;
|
|||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use meilisearch_types::batches::Batch;
|
use meilisearch_types::batches::Batch;
|
||||||
use meilisearch_types::enterprise_edition::network::Network;
|
|
||||||
use meilisearch_types::features::{ChatCompletionSettings, RuntimeTogglableFeatures};
|
use meilisearch_types::features::{ChatCompletionSettings, RuntimeTogglableFeatures};
|
||||||
use meilisearch_types::keys::Key;
|
use meilisearch_types::keys::Key;
|
||||||
|
use meilisearch_types::network::Network;
|
||||||
use meilisearch_types::settings::{Checked, Settings};
|
use meilisearch_types::settings::{Checked, Settings};
|
||||||
use meilisearch_types::webhooks::WebhooksDumpView;
|
use meilisearch_types::webhooks::WebhooksDumpView;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4"] }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ license.workspace = true
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.6.0", features = ["html_reports"] }
|
criterion = { version = "0.7.0", features = ["html_reports"] }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmarks"
|
name = "benchmarks"
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arbitrary = { version = "1.4.1", features = ["derive"] }
|
arbitrary = { version = "1.4.2", features = ["derive"] }
|
||||||
bumpalo = "3.18.1"
|
bumpalo = "3.19.0"
|
||||||
clap = { version = "4.5.40", features = ["derive"] }
|
clap = { version = "4.5.52", features = ["derive"] }
|
||||||
either = "1.15.0"
|
either = "1.15.0"
|
||||||
fastrand = "2.3.0"
|
fastrand = "2.3.0"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
|
|||||||
@@ -11,33 +11,33 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
byte-unit = "5.1.6"
|
byte-unit = "5.1.6"
|
||||||
bytes = "1.10.1"
|
bytes = "1.11.0"
|
||||||
bumpalo = "3.18.1"
|
bumpalo = "3.19.0"
|
||||||
bumparaw-collections = "0.1.4"
|
bumparaw-collections = "0.1.4"
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.9.0"
|
||||||
csv = "1.3.1"
|
csv = "1.4.0"
|
||||||
derive_builder = "0.20.2"
|
derive_builder = "0.20.2"
|
||||||
dump = { path = "../dump" }
|
dump = { path = "../dump" }
|
||||||
enum-iterator = "2.1.0"
|
enum-iterator = "2.3.0"
|
||||||
file-store = { path = "../file-store" }
|
file-store = { path = "../file-store" }
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.5"
|
||||||
indexmap = "2.9.0"
|
indexmap = "2.12.0"
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
memmap2 = "0.9.7"
|
memmap2 = "0.9.9"
|
||||||
page_size = "0.6.0"
|
page_size = "0.6.0"
|
||||||
rayon = "1.10.0"
|
rayon = "1.11.0"
|
||||||
roaring = { version = "0.10.12", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
tar = "0.4.44"
|
tar = "0.4.44"
|
||||||
synchronoise = "1.0.1"
|
synchronoise = "1.0.1"
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
time = { version = "0.3.41", features = [
|
time = { version = "0.3.44", features = [
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
"formatting",
|
"formatting",
|
||||||
"parsing",
|
"parsing",
|
||||||
@@ -45,11 +45,11 @@ time = { version = "0.3.41", features = [
|
|||||||
] }
|
] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
ureq = "2.12.1"
|
ureq = "2.12.1"
|
||||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4"] }
|
||||||
backoff = "0.4.0"
|
backoff = "0.4.0"
|
||||||
reqwest = { version = "0.12.23", features = ["rustls-tls", "http2"], default-features = false }
|
reqwest = { version = "0.12.24", features = ["rustls-tls", "http2"], default-features = false }
|
||||||
rusty-s3 = "0.8.1"
|
rusty-s3 = "0.8.1"
|
||||||
tokio = { version = "1.47.1", features = ["full"] }
|
tokio = { version = "1.48.0", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use meilisearch_types::enterprise_edition::network::Network;
|
|
||||||
use meilisearch_types::features::{InstanceTogglableFeatures, RuntimeTogglableFeatures};
|
use meilisearch_types::features::{InstanceTogglableFeatures, RuntimeTogglableFeatures};
|
||||||
use meilisearch_types::heed::types::{SerdeJson, Str};
|
use meilisearch_types::heed::types::{SerdeJson, Str};
|
||||||
use meilisearch_types::heed::{Database, Env, RwTxn, WithoutTls};
|
use meilisearch_types::heed::{Database, Env, RwTxn, WithoutTls};
|
||||||
|
use meilisearch_types::network::Network;
|
||||||
|
|
||||||
use crate::error::FeatureNotEnabledError;
|
use crate::error::FeatureNotEnabledError;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|||||||
@@ -306,18 +306,6 @@ fn create_or_open_index(
|
|||||||
) -> Result<Index> {
|
) -> Result<Index> {
|
||||||
let options = EnvOpenOptions::new();
|
let options = EnvOpenOptions::new();
|
||||||
let mut options = options.read_txn_without_tls();
|
let mut options = options.read_txn_without_tls();
|
||||||
|
|
||||||
let map_size = match std::env::var("MEILI_MAX_INDEX_SIZE") {
|
|
||||||
Ok(max_size) => {
|
|
||||||
let max_size = max_size.parse().unwrap();
|
|
||||||
map_size.min(max_size)
|
|
||||||
}
|
|
||||||
Err(VarError::NotPresent) => map_size,
|
|
||||||
Err(VarError::NotUnicode(e)) => {
|
|
||||||
panic!("Non unicode max index size in `MEILI_MAX_INDEX_SIZE`: {e:?}")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
options.map_size(clamp_to_page_size(map_size));
|
options.map_size(clamp_to_page_size(map_size));
|
||||||
|
|
||||||
// You can find more details about this experimental
|
// You can find more details about this experimental
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use meilisearch_types::heed::types::{SerdeBincode, SerdeJson, Str};
|
|||||||
use meilisearch_types::heed::{Database, RoTxn};
|
use meilisearch_types::heed::{Database, RoTxn};
|
||||||
use meilisearch_types::milli::{CboRoaringBitmapCodec, RoaringBitmapCodec, BEU32};
|
use meilisearch_types::milli::{CboRoaringBitmapCodec, RoaringBitmapCodec, BEU32};
|
||||||
use meilisearch_types::tasks::{Details, Kind, Status, Task};
|
use meilisearch_types::tasks::{Details, Kind, Status, Task};
|
||||||
use meilisearch_types::versioning;
|
use meilisearch_types::versioning::{self, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH};
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
|
|
||||||
use crate::index_mapper::IndexMapper;
|
use crate::index_mapper::IndexMapper;
|
||||||
@@ -320,8 +320,12 @@ fn snapshot_details(d: &Details) -> String {
|
|||||||
format!("{{ url: {url:?}, api_key: {api_key:?}, payload_size: {payload_size:?}, indexes: {indexes:?} }}")
|
format!("{{ url: {url:?}, api_key: {api_key:?}, payload_size: {payload_size:?}, indexes: {indexes:?} }}")
|
||||||
}
|
}
|
||||||
Details::UpgradeDatabase { from, to } => {
|
Details::UpgradeDatabase { from, to } => {
|
||||||
|
if to == &(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) {
|
||||||
|
format!("{{ from: {from:?}, to: [current version] }}")
|
||||||
|
} else {
|
||||||
format!("{{ from: {from:?}, to: {to:?} }}")
|
format!("{{ from: {from:?}, to: {to:?} }}")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Details::IndexCompaction { index_uid, pre_compaction_size, post_compaction_size } => {
|
Details::IndexCompaction { index_uid, pre_compaction_size, post_compaction_size } => {
|
||||||
format!("{{ index_uid: {index_uid:?}, pre_compaction_size: {pre_compaction_size:?}, post_compaction_size: {post_compaction_size:?} }}")
|
format!("{{ index_uid: {index_uid:?}, pre_compaction_size: {pre_compaction_size:?}, post_compaction_size: {post_compaction_size:?} }}")
|
||||||
}
|
}
|
||||||
@@ -400,7 +404,21 @@ pub fn snapshot_batch(batch: &Batch) -> String {
|
|||||||
|
|
||||||
snap.push('{');
|
snap.push('{');
|
||||||
snap.push_str(&format!("uid: {uid}, "));
|
snap.push_str(&format!("uid: {uid}, "));
|
||||||
snap.push_str(&format!("details: {}, ", serde_json::to_string(details).unwrap()));
|
let details = if let Some(upgrade_to) = &details.upgrade_to {
|
||||||
|
if upgrade_to.as_str()
|
||||||
|
== format!("v{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_PATCH}").as_str()
|
||||||
|
{
|
||||||
|
let mut details = details.clone();
|
||||||
|
|
||||||
|
details.upgrade_to = Some("[current version]".into());
|
||||||
|
serde_json::to_string(&details).unwrap()
|
||||||
|
} else {
|
||||||
|
serde_json::to_string(details).unwrap()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serde_json::to_string(details).unwrap()
|
||||||
|
};
|
||||||
|
snap.push_str(&format!("details: {details}, "));
|
||||||
snap.push_str(&format!("stats: {}, ", serde_json::to_string(&stats).unwrap()));
|
snap.push_str(&format!("stats: {}, ", serde_json::to_string(&stats).unwrap()));
|
||||||
if !embedder_stats.skip_serializing() {
|
if !embedder_stats.skip_serializing() {
|
||||||
snap.push_str(&format!(
|
snap.push_str(&format!(
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ pub use features::RoFeatures;
|
|||||||
use flate2::bufread::GzEncoder;
|
use flate2::bufread::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use meilisearch_types::batches::Batch;
|
use meilisearch_types::batches::Batch;
|
||||||
use meilisearch_types::enterprise_edition::network::Network;
|
|
||||||
use meilisearch_types::features::{
|
use meilisearch_types::features::{
|
||||||
ChatCompletionSettings, InstanceTogglableFeatures, RuntimeTogglableFeatures,
|
ChatCompletionSettings, InstanceTogglableFeatures, RuntimeTogglableFeatures,
|
||||||
};
|
};
|
||||||
@@ -67,6 +66,7 @@ use meilisearch_types::milli::vector::{
|
|||||||
Embedder, EmbedderOptions, RuntimeEmbedder, RuntimeEmbedders, RuntimeFragment,
|
Embedder, EmbedderOptions, RuntimeEmbedder, RuntimeEmbedders, RuntimeFragment,
|
||||||
};
|
};
|
||||||
use meilisearch_types::milli::{self, Index};
|
use meilisearch_types::milli::{self, Index};
|
||||||
|
use meilisearch_types::network::Network;
|
||||||
use meilisearch_types::task_view::TaskView;
|
use meilisearch_types::task_view::TaskView;
|
||||||
use meilisearch_types::tasks::{KindWithContent, Task, TaskNetwork};
|
use meilisearch_types::tasks::{KindWithContent, Task, TaskNetwork};
|
||||||
use meilisearch_types::webhooks::{Webhook, WebhooksDumpView, WebhooksView};
|
use meilisearch_types::webhooks::{Webhook, WebhooksDumpView, WebhooksView};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ source: crates/index-scheduler/src/scheduler/test_failure.rs
|
|||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { from: (1, 12, 0), to: (1, 26, 0) }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { from: (1, 12, 0), to: [current version] }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
2 {uid: 2, batch_uid: 2, status: succeeded, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
2 {uid: 2, batch_uid: 2, status: succeeded, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
3 {uid: 3, batch_uid: 3, status: failed, error: ResponseError { code: 200, message: "Index `doggo` already exists.", error_code: "index_already_exists", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#index_already_exists" }, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
3 {uid: 3, batch_uid: 3, status: failed, error: ResponseError { code: 200, message: "Index `doggo` already exists.", error_code: "index_already_exists", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#index_already_exists" }, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
@@ -57,7 +57,7 @@ girafo: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [4,]
|
[timestamp] [4,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"v1.26.0"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"[current version]"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
1 {uid: 1, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
2 {uid: 2, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 2 of type `indexCreation` that cannot be batched with any other task.", }
|
2 {uid: 2, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 2 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
3 {uid: 3, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 3 of type `indexCreation` that cannot be batched with any other task.", }
|
3 {uid: 3, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 3 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ source: crates/index-scheduler/src/scheduler/test_failure.rs
|
|||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { from: (1, 12, 0), to: (1, 26, 0) }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
0 {uid: 0, status: enqueued, details: { from: (1, 12, 0), to: [current version] }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,]
|
enqueued [0,]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ source: crates/index-scheduler/src/scheduler/test_failure.rs
|
|||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { from: (1, 12, 0), to: (1, 26, 0) }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
0 {uid: 0, status: enqueued, details: { from: (1, 12, 0), to: [current version] }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ source: crates/index-scheduler/src/scheduler/test_failure.rs
|
|||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { from: (1, 12, 0), to: (1, 26, 0) }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
0 {uid: 0, batch_uid: 0, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { from: (1, 12, 0), to: [current version] }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
@@ -37,7 +37,7 @@ catto [1,]
|
|||||||
[timestamp] [0,]
|
[timestamp] [0,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"v1.26.0"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"[current version]"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ source: crates/index-scheduler/src/scheduler/test_failure.rs
|
|||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { from: (1, 12, 0), to: (1, 26, 0) }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
0 {uid: 0, batch_uid: 0, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { from: (1, 12, 0), to: [current version] }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
2 {uid: 2, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
2 {uid: 2, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
@@ -40,7 +40,7 @@ doggo [2,]
|
|||||||
[timestamp] [0,]
|
[timestamp] [0,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"v1.26.0"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"[current version]"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ source: crates/index-scheduler/src/scheduler/test_failure.rs
|
|||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { from: (1, 12, 0), to: (1, 26, 0) }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { from: (1, 12, 0), to: [current version] }, kind: UpgradeDatabase { from: (1, 12, 0) }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
2 {uid: 2, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
2 {uid: 2, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
3 {uid: 3, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
3 {uid: 3, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
@@ -43,7 +43,7 @@ doggo [2,3,]
|
|||||||
[timestamp] [0,]
|
[timestamp] [0,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"v1.26.0"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
0 {uid: 0, details: {"upgradeFrom":"v1.12.0","upgradeTo":"[current version]"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"upgradeDatabase":1},"indexUids":{}}, stop reason: "stopped after the last task of type `upgradeDatabase` because they cannot be batched with tasks of any other type.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use meilisearch_types::heed::{Env, RwTxn, WithoutTls};
|
use meilisearch_types::heed::{Env, RwTxn, WithoutTls};
|
||||||
use meilisearch_types::tasks::{Details, KindWithContent, Status, Task};
|
use meilisearch_types::tasks::{Details, KindWithContent, Status, Task};
|
||||||
use meilisearch_types::versioning::{VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH};
|
use meilisearch_types::versioning;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
@@ -9,81 +9,82 @@ use crate::queue::TaskQueue;
|
|||||||
use crate::versioning::Versioning;
|
use crate::versioning::Versioning;
|
||||||
|
|
||||||
trait UpgradeIndexScheduler {
|
trait UpgradeIndexScheduler {
|
||||||
fn upgrade(
|
fn upgrade(&self, env: &Env<WithoutTls>, wtxn: &mut RwTxn) -> anyhow::Result<()>;
|
||||||
&self,
|
/// Whether the migration should be applied, depending on the initial version of the index scheduler before
|
||||||
env: &Env<WithoutTls>,
|
/// any migration was applied
|
||||||
wtxn: &mut RwTxn,
|
fn must_upgrade(&self, initial_version: (u32, u32, u32)) -> bool;
|
||||||
original: (u32, u32, u32),
|
/// A progress-centric description of the migration
|
||||||
) -> anyhow::Result<()>;
|
fn description(&self) -> &'static str;
|
||||||
fn target_version(&self) -> (u32, u32, u32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Upgrade the index scheduler to the binary version.
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// The current implementation uses a single wtxn to the index scheduler for the whole duration of the upgrade.
|
||||||
|
/// If migrations start taking take a long time, it might prevent tasks from being registered.
|
||||||
|
/// If this issue manifests, then it can be mitigated by adding a `fn target_version` to `UpgradeIndexScheduler`,
|
||||||
|
/// to be able to write intermediate versions and drop the wtxn between applying migrations.
|
||||||
pub fn upgrade_index_scheduler(
|
pub fn upgrade_index_scheduler(
|
||||||
env: &Env<WithoutTls>,
|
env: &Env<WithoutTls>,
|
||||||
versioning: &Versioning,
|
versioning: &Versioning,
|
||||||
from: (u32, u32, u32),
|
initial_version: (u32, u32, u32),
|
||||||
to: (u32, u32, u32),
|
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let current_major = to.0;
|
let target_major: u32 = versioning::VERSION_MAJOR;
|
||||||
let current_minor = to.1;
|
let target_minor: u32 = versioning::VERSION_MINOR;
|
||||||
let current_patch = to.2;
|
let target_patch: u32 = versioning::VERSION_PATCH;
|
||||||
|
let target_version = (target_major, target_minor, target_patch);
|
||||||
|
|
||||||
|
if initial_version == target_version {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let upgrade_functions: &[&dyn UpgradeIndexScheduler] = &[
|
let upgrade_functions: &[&dyn UpgradeIndexScheduler] = &[
|
||||||
// This is the last upgrade function, it will be called when the index is up to date.
|
// List all upgrade functions to apply in order here.
|
||||||
// any other upgrade function should be added before this one.
|
|
||||||
&ToCurrentNoOp {},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let start = match from {
|
let (initial_major, initial_minor, initial_patch) = initial_version;
|
||||||
(1, 12, _) => 0,
|
|
||||||
(1, 13, _) => 0,
|
if initial_version > target_version {
|
||||||
(1, 14, _) => 0,
|
|
||||||
(1, 15, _) => 0,
|
|
||||||
(1, 16, _) => 0,
|
|
||||||
(1, 17, _) => 0,
|
|
||||||
(1, 18, _) => 0,
|
|
||||||
(1, 19, _) => 0,
|
|
||||||
(1, 20, _) => 0,
|
|
||||||
(1, 21, _) => 0,
|
|
||||||
(1, 22, _) => 0,
|
|
||||||
(1, 23, _) => 0,
|
|
||||||
(1, 24, _) => 0,
|
|
||||||
(1, 25, _) => 0,
|
|
||||||
(1, 26, _) => 0,
|
|
||||||
(major, minor, patch) => {
|
|
||||||
if major > current_major
|
|
||||||
|| (major == current_major && minor > current_minor)
|
|
||||||
|| (major == current_major && minor == current_minor && patch > current_patch)
|
|
||||||
{
|
|
||||||
bail!(
|
bail!(
|
||||||
"Database version {major}.{minor}.{patch} is higher than the Meilisearch version {current_major}.{current_minor}.{current_patch}. Downgrade is not supported",
|
"Database version {initial_major}.{initial_minor}.{initial_patch} is higher than the Meilisearch version {target_major}.{target_minor}.{target_patch}. Downgrade is not supported",
|
||||||
);
|
);
|
||||||
} else if major < 1 || (major == current_major && minor < 12) {
|
}
|
||||||
|
|
||||||
|
if initial_version < (1, 12, 0) {
|
||||||
bail!(
|
bail!(
|
||||||
"Database version {major}.{minor}.{patch} is too old for the experimental dumpless upgrade feature. Please generate a dump using the v{major}.{minor}.{patch} and import it in the v{current_major}.{current_minor}.{current_patch}",
|
"Database version {initial_major}.{initial_minor}.{initial_patch} is too old for the experimental dumpless upgrade feature. Please generate a dump using the v{initial_major}.{initial_minor}.{initial_patch} and import it in the v{target_major}.{target_minor}.{target_patch}",
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
bail!("Unknown database version: v{major}.{minor}.{patch}");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("Upgrading the task queue");
|
info!("Upgrading the task queue");
|
||||||
let mut local_from = from;
|
|
||||||
for upgrade in upgrade_functions[start..].iter() {
|
|
||||||
let target = upgrade.target_version();
|
|
||||||
info!(
|
|
||||||
"Upgrading from v{}.{}.{} to v{}.{}.{}",
|
|
||||||
local_from.0, local_from.1, local_from.2, target.0, target.1, target.2
|
|
||||||
);
|
|
||||||
let mut wtxn = env.write_txn()?;
|
let mut wtxn = env.write_txn()?;
|
||||||
upgrade.upgrade(env, &mut wtxn, local_from)?;
|
let migration_count = upgrade_functions.len();
|
||||||
versioning.set_version(&mut wtxn, target)?;
|
for (migration_index, upgrade) in upgrade_functions.iter().enumerate() {
|
||||||
wtxn.commit()?;
|
if upgrade.must_upgrade(initial_version) {
|
||||||
local_from = target;
|
info!(
|
||||||
|
"[{migration_index}/{migration_count}]Applying migration: {}",
|
||||||
|
upgrade.description()
|
||||||
|
);
|
||||||
|
|
||||||
|
upgrade.upgrade(env, &mut wtxn)?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"[{}/{migration_count}]Migration applied: {}",
|
||||||
|
migration_index + 1,
|
||||||
|
upgrade.description()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"[{migration_index}/{migration_count}]Skipping unnecessary migration: {}",
|
||||||
|
upgrade.description()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut wtxn = env.write_txn()?;
|
versioning.set_version(&mut wtxn, target_version)?;
|
||||||
|
info!("Task queue upgraded, spawning the upgrade database task");
|
||||||
|
|
||||||
let queue = TaskQueue::new(env, &mut wtxn)?;
|
let queue = TaskQueue::new(env, &mut wtxn)?;
|
||||||
let uid = queue.next_task_id(&wtxn)?;
|
let uid = queue.next_task_id(&wtxn)?;
|
||||||
queue.register(
|
queue.register(
|
||||||
@@ -96,9 +97,9 @@ pub fn upgrade_index_scheduler(
|
|||||||
finished_at: None,
|
finished_at: None,
|
||||||
error: None,
|
error: None,
|
||||||
canceled_by: None,
|
canceled_by: None,
|
||||||
details: Some(Details::UpgradeDatabase { from, to }),
|
details: Some(Details::UpgradeDatabase { from: initial_version, to: target_version }),
|
||||||
status: Status::Enqueued,
|
status: Status::Enqueued,
|
||||||
kind: KindWithContent::UpgradeDatabase { from },
|
kind: KindWithContent::UpgradeDatabase { from: initial_version },
|
||||||
network: None,
|
network: None,
|
||||||
custom_metadata: None,
|
custom_metadata: None,
|
||||||
},
|
},
|
||||||
@@ -107,21 +108,3 @@ pub fn upgrade_index_scheduler(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
struct ToCurrentNoOp {}
|
|
||||||
|
|
||||||
impl UpgradeIndexScheduler for ToCurrentNoOp {
|
|
||||||
fn upgrade(
|
|
||||||
&self,
|
|
||||||
_env: &Env<WithoutTls>,
|
|
||||||
_wtxn: &mut RwTxn,
|
|
||||||
_original: (u32, u32, u32),
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn target_version(&self) -> (u32, u32, u32) {
|
|
||||||
(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -64,14 +64,7 @@ impl Versioning {
|
|||||||
};
|
};
|
||||||
wtxn.commit()?;
|
wtxn.commit()?;
|
||||||
|
|
||||||
let bin_major: u32 = versioning::VERSION_MAJOR;
|
upgrade_index_scheduler(env, &this, from)?;
|
||||||
let bin_minor: u32 = versioning::VERSION_MINOR;
|
|
||||||
let bin_patch: u32 = versioning::VERSION_PATCH;
|
|
||||||
let to = (bin_major, bin_minor, bin_patch);
|
|
||||||
|
|
||||||
if from != to {
|
|
||||||
upgrade_index_scheduler(env, &this, from, to)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once we reach this point it means the upgrade process, if there was one is entirely finished
|
// Once we reach this point it means the upgrade process, if there was one is entirely finished
|
||||||
// we can safely say we reached the latest version of the index scheduler
|
// we can safely say we reached the latest version of the index scheduler
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ license.workspace = true
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.6.0"
|
criterion = "0.7.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "depth"
|
name = "depth"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ license.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# fixed version due to format breakages in v1.40
|
# fixed version due to format breakages in v1.40
|
||||||
insta = { version = "=1.39.0", features = ["json", "redactions"] }
|
insta = { version = "=1.39.0", features = ["json", "redactions"] }
|
||||||
md5 = "0.7.0"
|
md5 = "0.8.0"
|
||||||
once_cell = "1.21"
|
once_cell = "1.21"
|
||||||
regex-lite = "0.1.6"
|
regex-lite = "0.1.8"
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
uuid = { version = "1.18.1", features = ["v4"] }
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
enum-iterator = "2.1.0"
|
enum-iterator = "2.3.0"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
roaring = { version = "0.10.12", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
time = { version = "0.3.41", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
time = { version = "0.3.44", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
||||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4"] }
|
||||||
|
|||||||
@@ -11,38 +11,38 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.11.0", default-features = false }
|
actix-web = { version = "4.12.0", default-features = false }
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
bumpalo = "3.18.1"
|
bumpalo = "3.19.0"
|
||||||
bumparaw-collections = "0.1.4"
|
bumparaw-collections = "0.1.4"
|
||||||
byte-unit = { version = "5.1.6", features = ["serde"] }
|
byte-unit = { version = "5.1.6", features = ["serde"] }
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.9.0"
|
||||||
csv = "1.3.1"
|
csv = "1.4.0"
|
||||||
deserr = { version = "0.6.3", features = ["actix-web"] }
|
deserr = { version = "0.6.4", features = ["actix-web"] }
|
||||||
either = { version = "1.15.0", features = ["serde"] }
|
either = { version = "1.15.0", features = ["serde"] }
|
||||||
enum-iterator = "2.1.0"
|
enum-iterator = "2.3.0"
|
||||||
file-store = { path = "../file-store" }
|
file-store = { path = "../file-store" }
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.5"
|
||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
memmap2 = "0.9.7"
|
memmap2 = "0.9.9"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
roaring = { version = "0.10.12", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
rustc-hash = "2.1.1"
|
rustc-hash = "2.1.1"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde-cs = "0.2.4"
|
serde-cs = "0.2.4"
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
tar = "0.4.44"
|
tar = "0.4.44"
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
time = { version = "0.3.41", features = [
|
time = { version = "0.3.44", features = [
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
"formatting",
|
"formatting",
|
||||||
"parsing",
|
"parsing",
|
||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
tokio = "1.45"
|
tokio = "1.48"
|
||||||
utoipa = { version = "5.4.0", features = ["macros"] }
|
utoipa = { version = "5.4.0", features = ["macros"] }
|
||||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# fixed version due to format breakages in v1.40
|
# fixed version due to format breakages in v1.40
|
||||||
@@ -56,6 +56,9 @@ all-tokenizations = ["milli/all-tokenizations"]
|
|||||||
# chinese specialized tokenization
|
# chinese specialized tokenization
|
||||||
chinese = ["milli/chinese"]
|
chinese = ["milli/chinese"]
|
||||||
chinese-pinyin = ["milli/chinese-pinyin"]
|
chinese-pinyin = ["milli/chinese-pinyin"]
|
||||||
|
|
||||||
|
enterprise = ["milli/enterprise"]
|
||||||
|
|
||||||
# hebrew specialized tokenization
|
# hebrew specialized tokenization
|
||||||
hebrew = ["milli/hebrew"]
|
hebrew = ["milli/hebrew"]
|
||||||
# japanese specialized tokenization
|
# japanese specialized tokenization
|
||||||
|
|||||||
16
crates/meilisearch-types/src/community_edition.rs
Normal file
16
crates/meilisearch-types/src/community_edition.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod network {
|
||||||
|
use milli::update::new::indexer::current_edition::sharding::Shards;
|
||||||
|
|
||||||
|
use crate::network::Network;
|
||||||
|
|
||||||
|
impl Network {
|
||||||
|
pub fn shards(&self) -> Option<Shards> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sharding(&self) -> bool {
|
||||||
|
// always false in CE
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,21 +3,9 @@
|
|||||||
// Use of this source code is governed by the Business Source License 1.1,
|
// Use of this source code is governed by the Business Source License 1.1,
|
||||||
// as found in the LICENSE-EE file or at <https://mariadb.com/bsl11>
|
// as found in the LICENSE-EE file or at <https://mariadb.com/bsl11>
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use milli::update::new::indexer::enterprise_edition::sharding::Shards;
|
use milli::update::new::indexer::enterprise_edition::sharding::Shards;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
|
use crate::network::Network;
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Network {
|
|
||||||
#[serde(default, rename = "self")]
|
|
||||||
pub local: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub remotes: BTreeMap<String, Remote>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub sharding: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Network {
|
impl Network {
|
||||||
pub fn shards(&self) -> Option<Shards> {
|
pub fn shards(&self) -> Option<Shards> {
|
||||||
@@ -34,14 +22,8 @@ impl Network {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
pub fn sharding(&self) -> bool {
|
||||||
#[serde(rename_all = "camelCase")]
|
self.sharding
|
||||||
pub struct Remote {
|
}
|
||||||
pub url: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub search_api_key: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub write_api_key: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ InvalidSettingsDisplayedAttributes , InvalidRequest , BAD_REQU
|
|||||||
InvalidSettingsDistinctAttribute , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsDistinctAttribute , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsProximityPrecision , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsProximityPrecision , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsFacetSearch , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsFacetSearch , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsexecuteAfterUpdate , InvalidRequest , BAD_REQUEST ;
|
|
||||||
InvalidSettingsPrefixSearch , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsPrefixSearch , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsFaceting , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsFaceting , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsFilterableAttributes , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsFilterableAttributes , InvalidRequest , BAD_REQUEST ;
|
||||||
@@ -434,6 +433,7 @@ InvalidChatCompletionSearchQueryParamPrompt , InvalidRequest , BAD_REQU
|
|||||||
InvalidChatCompletionSearchFilterParamPrompt , InvalidRequest , BAD_REQUEST ;
|
InvalidChatCompletionSearchFilterParamPrompt , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidChatCompletionSearchIndexUidParamPrompt , InvalidRequest , BAD_REQUEST ;
|
InvalidChatCompletionSearchIndexUidParamPrompt , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidChatCompletionPreQueryPrompt , InvalidRequest , BAD_REQUEST ;
|
InvalidChatCompletionPreQueryPrompt , InvalidRequest , BAD_REQUEST ;
|
||||||
|
RequiresEnterpriseEdition , InvalidRequest , UNAVAILABLE_FOR_LEGAL_REASONS ;
|
||||||
// Webhooks
|
// Webhooks
|
||||||
InvalidWebhooks , InvalidRequest , BAD_REQUEST ;
|
InvalidWebhooks , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidWebhookUrl , InvalidRequest , BAD_REQUEST ;
|
InvalidWebhookUrl , InvalidRequest , BAD_REQUEST ;
|
||||||
|
|||||||
@@ -2,10 +2,17 @@
|
|||||||
|
|
||||||
pub mod batch_view;
|
pub mod batch_view;
|
||||||
pub mod batches;
|
pub mod batches;
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
pub mod community_edition;
|
||||||
pub mod compression;
|
pub mod compression;
|
||||||
pub mod deserr;
|
pub mod deserr;
|
||||||
pub mod document_formats;
|
pub mod document_formats;
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
pub mod enterprise_edition;
|
pub mod enterprise_edition;
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
pub use community_edition as current_edition;
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
pub use enterprise_edition as current_edition;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod facet_values_sort;
|
pub mod facet_values_sort;
|
||||||
pub mod features;
|
pub mod features;
|
||||||
@@ -13,6 +20,7 @@ pub mod index_uid;
|
|||||||
pub mod index_uid_pattern;
|
pub mod index_uid_pattern;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
pub mod locales;
|
pub mod locales;
|
||||||
|
pub mod network;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
pub mod star_or;
|
pub mod star_or;
|
||||||
pub mod task_view;
|
pub mod task_view;
|
||||||
|
|||||||
24
crates/meilisearch-types/src/network.rs
Normal file
24
crates/meilisearch-types/src/network.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Network {
|
||||||
|
#[serde(default, rename = "self")]
|
||||||
|
pub local: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub remotes: BTreeMap<String, Remote>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub sharding: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Remote {
|
||||||
|
pub url: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub search_api_key: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub write_api_key: Option<String>,
|
||||||
|
}
|
||||||
@@ -326,12 +326,6 @@ pub struct Settings<T> {
|
|||||||
#[schema(value_type = Option<VectorStoreBackend>)]
|
#[schema(value_type = Option<VectorStoreBackend>)]
|
||||||
pub vector_store: Setting<VectorStoreBackend>,
|
pub vector_store: Setting<VectorStoreBackend>,
|
||||||
|
|
||||||
/// Function to execute after an update
|
|
||||||
#[serde(default, skip_serializing_if = "Setting::is_not_set")]
|
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSettingsexecuteAfterUpdate>)]
|
|
||||||
#[schema(value_type = Option<String>, example = json!("doc.likes += 1"))]
|
|
||||||
pub execute_after_update: Setting<String>,
|
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[deserr(skip)]
|
#[deserr(skip)]
|
||||||
pub _kind: PhantomData<T>,
|
pub _kind: PhantomData<T>,
|
||||||
@@ -401,7 +395,6 @@ impl Settings<Checked> {
|
|||||||
prefix_search: Setting::Reset,
|
prefix_search: Setting::Reset,
|
||||||
chat: Setting::Reset,
|
chat: Setting::Reset,
|
||||||
vector_store: Setting::Reset,
|
vector_store: Setting::Reset,
|
||||||
execute_after_update: Setting::Reset,
|
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -430,7 +423,6 @@ impl Settings<Checked> {
|
|||||||
prefix_search,
|
prefix_search,
|
||||||
chat,
|
chat,
|
||||||
vector_store,
|
vector_store,
|
||||||
execute_after_update,
|
|
||||||
_kind,
|
_kind,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
@@ -457,7 +449,6 @@ impl Settings<Checked> {
|
|||||||
prefix_search,
|
prefix_search,
|
||||||
vector_store,
|
vector_store,
|
||||||
chat,
|
chat,
|
||||||
execute_after_update,
|
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,7 +501,6 @@ impl Settings<Unchecked> {
|
|||||||
prefix_search: self.prefix_search,
|
prefix_search: self.prefix_search,
|
||||||
chat: self.chat,
|
chat: self.chat,
|
||||||
vector_store: self.vector_store,
|
vector_store: self.vector_store,
|
||||||
execute_after_update: self.execute_after_update,
|
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -592,10 +582,6 @@ impl Settings<Unchecked> {
|
|||||||
prefix_search: other.prefix_search.or(self.prefix_search),
|
prefix_search: other.prefix_search.or(self.prefix_search),
|
||||||
chat: other.chat.clone().or(self.chat.clone()),
|
chat: other.chat.clone().or(self.chat.clone()),
|
||||||
vector_store: other.vector_store.or(self.vector_store),
|
vector_store: other.vector_store.or(self.vector_store),
|
||||||
execute_after_update: other
|
|
||||||
.execute_after_update
|
|
||||||
.clone()
|
|
||||||
.or(self.execute_after_update.clone()),
|
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -636,7 +622,6 @@ pub fn apply_settings_to_builder(
|
|||||||
prefix_search,
|
prefix_search,
|
||||||
chat,
|
chat,
|
||||||
vector_store,
|
vector_store,
|
||||||
execute_after_update,
|
|
||||||
_kind,
|
_kind,
|
||||||
} = settings;
|
} = settings;
|
||||||
|
|
||||||
@@ -860,14 +845,6 @@ pub fn apply_settings_to_builder(
|
|||||||
Setting::Reset => builder.reset_vector_store(),
|
Setting::Reset => builder.reset_vector_store(),
|
||||||
Setting::NotSet => (),
|
Setting::NotSet => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
match execute_after_update {
|
|
||||||
Setting::Set(execute_after_update) => {
|
|
||||||
builder.set_execute_after_update(execute_after_update.clone())
|
|
||||||
}
|
|
||||||
Setting::Reset => builder.reset_execute_after_update(),
|
|
||||||
Setting::NotSet => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SecretPolicy {
|
pub enum SecretPolicy {
|
||||||
@@ -967,13 +944,13 @@ pub fn settings(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let vector_store = index.get_vector_store(rtxn)?;
|
let vector_store = index.get_vector_store(rtxn)?;
|
||||||
|
|
||||||
let embedders = Setting::Set(embedders);
|
let embedders = Setting::Set(embedders);
|
||||||
let search_cutoff_ms = index.search_cutoff(rtxn)?;
|
let search_cutoff_ms = index.search_cutoff(rtxn)?;
|
||||||
let localized_attributes_rules = index.localized_attributes_rules(rtxn)?;
|
let localized_attributes_rules = index.localized_attributes_rules(rtxn)?;
|
||||||
let prefix_search = index.prefix_search(rtxn)?.map(PrefixSearchSettings::from);
|
let prefix_search = index.prefix_search(rtxn)?.map(PrefixSearchSettings::from);
|
||||||
let facet_search = index.facet_search(rtxn)?;
|
let facet_search = index.facet_search(rtxn)?;
|
||||||
let chat = index.chat_config(rtxn).map(ChatSettings::from)?;
|
let chat = index.chat_config(rtxn).map(ChatSettings::from)?;
|
||||||
let execute_after_update = index.execute_after_update(rtxn)?;
|
|
||||||
|
|
||||||
let mut settings = Settings {
|
let mut settings = Settings {
|
||||||
displayed_attributes: match displayed_attributes {
|
displayed_attributes: match displayed_attributes {
|
||||||
@@ -1018,10 +995,6 @@ pub fn settings(
|
|||||||
Some(vector_store) => Setting::Set(vector_store),
|
Some(vector_store) => Setting::Set(vector_store),
|
||||||
None => Setting::Reset,
|
None => Setting::Reset,
|
||||||
},
|
},
|
||||||
execute_after_update: match execute_after_update {
|
|
||||||
Some(function) => Setting::Set(function.to_string()),
|
|
||||||
None => Setting::NotSet,
|
|
||||||
},
|
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1252,7 +1225,6 @@ pub(crate) mod test {
|
|||||||
prefix_search: Setting::NotSet,
|
prefix_search: Setting::NotSet,
|
||||||
chat: Setting::NotSet,
|
chat: Setting::NotSet,
|
||||||
vector_store: Setting::NotSet,
|
vector_store: Setting::NotSet,
|
||||||
execute_after_update: Setting::NotSet,
|
|
||||||
_kind: PhantomData::<Unchecked>,
|
_kind: PhantomData::<Unchecked>,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1286,7 +1258,7 @@ pub(crate) mod test {
|
|||||||
prefix_search: Setting::NotSet,
|
prefix_search: Setting::NotSet,
|
||||||
chat: Setting::NotSet,
|
chat: Setting::NotSet,
|
||||||
vector_store: Setting::NotSet,
|
vector_store: Setting::NotSet,
|
||||||
execute_after_update: Setting::NotSet,
|
|
||||||
_kind: PhantomData::<Unchecked>,
|
_kind: PhantomData::<Unchecked>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,91 +14,91 @@ default-run = "meilisearch"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-cors = "0.7.1"
|
actix-cors = "0.7.1"
|
||||||
actix-http = { version = "3.11.0", default-features = false, features = [
|
actix-http = { version = "3.11.2", default-features = false, features = [
|
||||||
"compress-brotli",
|
"compress-brotli",
|
||||||
"compress-gzip",
|
"compress-gzip",
|
||||||
"rustls-0_23",
|
"rustls-0_23",
|
||||||
] }
|
] }
|
||||||
actix-utils = "3.0.1"
|
actix-utils = "3.0.1"
|
||||||
actix-web = { version = "4.11.0", default-features = false, features = [
|
actix-web = { version = "4.12.0", default-features = false, features = [
|
||||||
"macros",
|
"macros",
|
||||||
"compress-brotli",
|
"compress-brotli",
|
||||||
"compress-gzip",
|
"compress-gzip",
|
||||||
"cookies",
|
"cookies",
|
||||||
"rustls-0_23",
|
"rustls-0_23",
|
||||||
] }
|
] }
|
||||||
anyhow = { version = "1.0.98", features = ["backtrace"] }
|
anyhow = { version = "1.0.100", features = ["backtrace"] }
|
||||||
bstr = "1.12.0"
|
bstr = "1.12.1"
|
||||||
byte-unit = { version = "5.1.6", features = ["serde"] }
|
byte-unit = { version = "5.1.6", features = ["serde"] }
|
||||||
bytes = "1.10.1"
|
bytes = "1.11.0"
|
||||||
bumpalo = "3.18.1"
|
bumpalo = "3.19.0"
|
||||||
clap = { version = "4.5.40", features = ["derive", "env"] }
|
clap = { version = "4.5.52", features = ["derive", "env"] }
|
||||||
crossbeam-channel = "0.5.15"
|
crossbeam-channel = "0.5.15"
|
||||||
deserr = { version = "0.6.3", features = ["actix-web"] }
|
deserr = { version = "0.6.4", features = ["actix-web"] }
|
||||||
dump = { path = "../dump" }
|
dump = { path = "../dump" }
|
||||||
either = "1.15.0"
|
either = "1.15.0"
|
||||||
file-store = { path = "../file-store" }
|
file-store = { path = "../file-store" }
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.5"
|
||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
index-scheduler = { path = "../index-scheduler" }
|
index-scheduler = { path = "../index-scheduler" }
|
||||||
indexmap = { version = "2.9.0", features = ["serde"] }
|
indexmap = { version = "2.12.0", features = ["serde"] }
|
||||||
is-terminal = "0.4.16"
|
is-terminal = "0.4.17"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
jsonwebtoken = "9.3.1"
|
jsonwebtoken = "9.3.1"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
memmap2 = "0.9.7"
|
memmap2 = "0.9.9"
|
||||||
mimalloc = { version = "0.1.47", default-features = false }
|
mimalloc = { version = "0.1.48", default-features = false }
|
||||||
mime = "0.3.17"
|
mime = "0.3.17"
|
||||||
num_cpus = "1.17.0"
|
num_cpus = "1.17.0"
|
||||||
obkv = "0.3.0"
|
obkv = "0.3.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
ordered-float = "5.0.0"
|
ordered-float = "5.1.0"
|
||||||
parking_lot = "0.12.4"
|
parking_lot = "0.12.5"
|
||||||
permissive-json-pointer = { path = "../permissive-json-pointer" }
|
permissive-json-pointer = { path = "../permissive-json-pointer" }
|
||||||
pin-project-lite = "0.2.16"
|
pin-project-lite = "0.2.16"
|
||||||
platform-dirs = "0.3.0"
|
platform-dirs = "0.3.0"
|
||||||
prometheus = { version = "0.14.0", features = ["process"] }
|
prometheus = { version = "0.14.0", features = ["process"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rayon = "1.10.0"
|
rayon = "1.11.0"
|
||||||
regex = "1.11.1"
|
regex = "1.12.2"
|
||||||
reqwest = { version = "0.12.20", features = [
|
reqwest = { version = "0.12.24", features = [
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
"json",
|
"json",
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
rustls = { version = "0.23.28", features = ["ring"], default-features = false }
|
rustls = { version = "0.23.35", features = ["ring"], default-features = false }
|
||||||
rustls-pki-types = { version = "1.12.0", features = ["alloc"] }
|
rustls-pki-types = { version = "1.13.0", features = ["alloc"] }
|
||||||
rustls-pemfile = "2.2.0"
|
rustls-pemfile = "2.2.0"
|
||||||
segment = { version = "0.2.6" }
|
segment = { version = "0.2.6" }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
siphasher = "1.0.1"
|
siphasher = "1.0.1"
|
||||||
slice-group-by = "0.3.1"
|
slice-group-by = "0.3.1"
|
||||||
static-files = { version = "0.2.5", optional = true }
|
static-files = { version = "0.3.1", optional = true }
|
||||||
sysinfo = "0.35.2"
|
sysinfo = "0.37.2"
|
||||||
tar = "0.4.44"
|
tar = "0.4.44"
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
time = { version = "0.3.41", features = [
|
time = { version = "0.3.44", features = [
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
"formatting",
|
"formatting",
|
||||||
"parsing",
|
"parsing",
|
||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
tokio = { version = "1.45.1", features = ["full"] }
|
tokio = { version = "1.48.0", features = ["full"] }
|
||||||
toml = "0.8.23"
|
toml = "0.9.8"
|
||||||
uuid = { version = "1.18.0", features = ["serde", "v4", "v7"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4", "v7"] }
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
termcolor = "1.4.1"
|
termcolor = "1.4.1"
|
||||||
url = { version = "2.5.4", features = ["serde"] }
|
url = { version = "2.5.7", features = ["serde"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.20", features = ["json"] }
|
tracing-subscriber = { version = "0.3.20", features = ["json"] }
|
||||||
tracing-trace = { version = "0.1.0", path = "../tracing-trace" }
|
tracing-trace = { version = "0.1.0", path = "../tracing-trace" }
|
||||||
tracing-actix-web = "0.7.18"
|
tracing-actix-web = "0.7.19"
|
||||||
build-info = { version = "1.7.0", path = "../build-info" }
|
build-info = { version = "1.7.0", path = "../build-info" }
|
||||||
roaring = "0.10.12"
|
roaring = "0.10.12"
|
||||||
mopa-maintained = "0.2.3"
|
mopa-maintained = "0.2.3"
|
||||||
@@ -114,35 +114,35 @@ utoipa = { version = "5.4.0", features = [
|
|||||||
utoipa-scalar = { version = "0.3.0", optional = true, features = ["actix-web"] }
|
utoipa-scalar = { version = "0.3.0", optional = true, features = ["actix-web"] }
|
||||||
async-openai = { git = "https://github.com/meilisearch/async-openai", branch = "better-error-handling" }
|
async-openai = { git = "https://github.com/meilisearch/async-openai", branch = "better-error-handling" }
|
||||||
secrecy = "0.10.3"
|
secrecy = "0.10.3"
|
||||||
actix-web-lab = { version = "0.24.1", default-features = false }
|
actix-web-lab = { version = "0.24.3", default-features = false }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
backoff = { version = "0.4.0", features = ["tokio"] }
|
backoff = { version = "0.4.0", features = ["tokio"] }
|
||||||
humantime = { version = "2.3.0", default-features = false }
|
humantime = { version = "2.3.0", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.10.0"
|
actix-rt = "2.11.0"
|
||||||
brotli = "8.0.1"
|
brotli = "8.0.2"
|
||||||
# fixed version due to format breakages in v1.40
|
# fixed version due to format breakages in v1.40
|
||||||
insta = { version = "=1.39.0", features = ["redactions"] }
|
insta = { version = "=1.39.0", features = ["redactions"] }
|
||||||
manifest-dir-macros = "0.1.18"
|
manifest-dir-macros = "0.1.18"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
meili-snap = { path = "../meili-snap" }
|
meili-snap = { path = "../meili-snap" }
|
||||||
temp-env = "0.3.6"
|
temp-env = "0.3.6"
|
||||||
wiremock = "0.6.3"
|
wiremock = "0.6.5"
|
||||||
yaup = "0.3.1"
|
yaup = "0.3.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = { version = "1.0.98", optional = true }
|
anyhow = { version = "1.0.100", optional = true }
|
||||||
cargo_toml = { version = "0.22.1", optional = true }
|
cargo_toml = { version = "0.22.3", optional = true }
|
||||||
hex = { version = "0.4.3", optional = true }
|
hex = { version = "0.4.3", optional = true }
|
||||||
reqwest = { version = "0.12.20", features = [
|
reqwest = { version = "0.12.24", features = [
|
||||||
"blocking",
|
"blocking",
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
], default-features = false, optional = true }
|
], default-features = false, optional = true }
|
||||||
sha-1 = { version = "0.10.1", optional = true }
|
sha-1 = { version = "0.10.1", optional = true }
|
||||||
static-files = { version = "0.2.5", optional = true }
|
static-files = { version = "0.3.1", optional = true }
|
||||||
tempfile = { version = "3.20.0", optional = true }
|
tempfile = { version = "3.23.0", optional = true }
|
||||||
zip = { version = "4.1.0", optional = true }
|
zip = { version = "6.0.0", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["meilisearch-types/all-tokenizations", "mini-dashboard"]
|
default = ["meilisearch-types/all-tokenizations", "mini-dashboard"]
|
||||||
@@ -160,6 +160,7 @@ mini-dashboard = [
|
|||||||
]
|
]
|
||||||
chinese = ["meilisearch-types/chinese"]
|
chinese = ["meilisearch-types/chinese"]
|
||||||
chinese-pinyin = ["meilisearch-types/chinese-pinyin"]
|
chinese-pinyin = ["meilisearch-types/chinese-pinyin"]
|
||||||
|
enterprise = ["meilisearch-types/enterprise"]
|
||||||
hebrew = ["meilisearch-types/hebrew"]
|
hebrew = ["meilisearch-types/hebrew"]
|
||||||
japanese = ["meilisearch-types/japanese"]
|
japanese = ["meilisearch-types/japanese"]
|
||||||
korean = ["meilisearch-types/korean"]
|
korean = ["meilisearch-types/korean"]
|
||||||
|
|||||||
@@ -231,8 +231,14 @@ pub fn setup_meilisearch(
|
|||||||
max_number_of_tasks: 1_000_000,
|
max_number_of_tasks: 1_000_000,
|
||||||
max_number_of_batched_tasks: opt.experimental_max_number_of_batched_tasks,
|
max_number_of_batched_tasks: opt.experimental_max_number_of_batched_tasks,
|
||||||
batched_tasks_size_limit: opt.experimental_limit_batched_tasks_total_size.map_or_else(
|
batched_tasks_size_limit: opt.experimental_limit_batched_tasks_total_size.map_or_else(
|
||||||
|
|| {
|
||||||
|
opt.indexer_options
|
||||||
|
.max_indexing_memory
|
||||||
// By default, we use half of the available memory to determine the size of batched tasks
|
// By default, we use half of the available memory to determine the size of batched tasks
|
||||||
|| opt.indexer_options.max_indexing_memory.map_or(u64::MAX, |mem| mem.as_u64() / 2),
|
.map_or(u64::MAX, |mem| mem.as_u64() / 2)
|
||||||
|
// And never exceed 10 GiB when we infer the limit
|
||||||
|
.min(10 * 1024 * 1024 * 1024)
|
||||||
|
},
|
||||||
|size| size.as_u64(),
|
|size| size.as_u64(),
|
||||||
),
|
),
|
||||||
index_growth_amount: byte_unit::Byte::from_str("10GiB").unwrap().as_u64() as usize,
|
index_growth_amount: byte_unit::Byte::from_str("10GiB").unwrap().as_u64() as usize,
|
||||||
|
|||||||
@@ -474,7 +474,8 @@ pub struct Opt {
|
|||||||
pub experimental_max_number_of_batched_tasks: usize,
|
pub experimental_max_number_of_batched_tasks: usize,
|
||||||
|
|
||||||
/// Experimentally controls the maximum total size, in bytes, of tasks that will be processed
|
/// Experimentally controls the maximum total size, in bytes, of tasks that will be processed
|
||||||
/// simultaneously. When unspecified, defaults to half of the maximum indexing memory.
|
/// simultaneously. When unspecified, defaults to half of the maximum indexing memory and
|
||||||
|
/// clamped to 10 GiB.
|
||||||
///
|
///
|
||||||
/// See: <https://github.com/orgs/meilisearch/discussions/801>
|
/// See: <https://github.com/orgs/meilisearch/discussions/801>
|
||||||
#[clap(long, env = MEILI_EXPERIMENTAL_LIMIT_BATCHED_TASKS_TOTAL_SIZE)]
|
#[clap(long, env = MEILI_EXPERIMENTAL_LIMIT_BATCHED_TASKS_TOTAL_SIZE)]
|
||||||
|
|||||||
39
crates/meilisearch/src/routes/indexes/community_edition.rs
Normal file
39
crates/meilisearch/src/routes/indexes/community_edition.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
pub mod proxy {
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use actix_web::HttpRequest;
|
||||||
|
use index_scheduler::IndexScheduler;
|
||||||
|
|
||||||
|
use crate::error::MeilisearchHttpError;
|
||||||
|
|
||||||
|
pub enum Body<T: serde::Serialize> {
|
||||||
|
NdJsonPayload,
|
||||||
|
Inline(T),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body<()> {
|
||||||
|
pub fn with_ndjson_payload(_file: File) -> Self {
|
||||||
|
Self::NdJsonPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const PROXY_ORIGIN_REMOTE_HEADER: &str = "Meili-Proxy-Origin-Remote";
|
||||||
|
pub const PROXY_ORIGIN_TASK_UID_HEADER: &str = "Meili-Proxy-Origin-TaskUid";
|
||||||
|
|
||||||
|
pub async fn proxy<T: serde::Serialize>(
|
||||||
|
_index_scheduler: &IndexScheduler,
|
||||||
|
_index_uid: &str,
|
||||||
|
_req: &HttpRequest,
|
||||||
|
_network: meilisearch_types::network::Network,
|
||||||
|
_body: Body<T>,
|
||||||
|
_task: &meilisearch_types::tasks::Task,
|
||||||
|
) -> Result<(), MeilisearchHttpError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ use crate::extractors::authentication::policies::*;
|
|||||||
use crate::extractors::authentication::GuardedData;
|
use crate::extractors::authentication::GuardedData;
|
||||||
use crate::extractors::payload::Payload;
|
use crate::extractors::payload::Payload;
|
||||||
use crate::extractors::sequential_extractor::SeqHandler;
|
use crate::extractors::sequential_extractor::SeqHandler;
|
||||||
use crate::routes::indexes::enterprise_edition::proxy::{proxy, Body};
|
use crate::routes::indexes::current_edition::proxy::{proxy, Body};
|
||||||
use crate::routes::indexes::search::fix_sort_query_parameters;
|
use crate::routes::indexes::search::fix_sort_query_parameters;
|
||||||
use crate::routes::{
|
use crate::routes::{
|
||||||
get_task_id, is_dry_run, PaginationView, SummarizedTaskView, PAGINATION_DEFAULT_LIMIT,
|
get_task_id, is_dry_run, PaginationView, SummarizedTaskView, PAGINATION_DEFAULT_LIMIT,
|
||||||
@@ -367,7 +367,7 @@ pub async fn delete_document(
|
|||||||
.await??
|
.await??
|
||||||
};
|
};
|
||||||
|
|
||||||
if network.sharding && !dry_run {
|
if network.sharding() && !dry_run {
|
||||||
proxy(&index_scheduler, &index_uid, &req, network, Body::none(), &task).await?;
|
proxy(&index_scheduler, &index_uid, &req, network, Body::none(), &task).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1098,7 +1098,7 @@ async fn document_addition(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if network.sharding {
|
if network.sharding() {
|
||||||
if let Some(file) = file {
|
if let Some(file) = file {
|
||||||
proxy(
|
proxy(
|
||||||
&index_scheduler,
|
&index_scheduler,
|
||||||
@@ -1222,7 +1222,7 @@ pub async fn delete_documents_batch(
|
|||||||
.await??
|
.await??
|
||||||
};
|
};
|
||||||
|
|
||||||
if network.sharding && !dry_run {
|
if network.sharding() && !dry_run {
|
||||||
proxy(&index_scheduler, &index_uid, &req, network, Body::Inline(body), &task).await?;
|
proxy(&index_scheduler, &index_uid, &req, network, Body::Inline(body), &task).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1320,7 +1320,7 @@ pub async fn delete_documents_by_filter(
|
|||||||
.await??
|
.await??
|
||||||
};
|
};
|
||||||
|
|
||||||
if network.sharding && !dry_run {
|
if network.sharding() && !dry_run {
|
||||||
proxy(&index_scheduler, &index_uid, &req, network, Body::Inline(filter), &task).await?;
|
proxy(&index_scheduler, &index_uid, &req, network, Body::Inline(filter), &task).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1475,7 +1475,7 @@ pub async fn edit_documents_by_function(
|
|||||||
.await??
|
.await??
|
||||||
};
|
};
|
||||||
|
|
||||||
if network.sharding && !dry_run {
|
if network.sharding() && !dry_run {
|
||||||
proxy(&index_scheduler, &index_uid, &req, network, Body::Inline(body), &task).await?;
|
proxy(&index_scheduler, &index_uid, &req, network, Body::Inline(body), &task).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1549,7 +1549,7 @@ pub async fn clear_all_documents(
|
|||||||
.await??
|
.await??
|
||||||
};
|
};
|
||||||
|
|
||||||
if network.sharding && !dry_run {
|
if network.sharding() && !dry_run {
|
||||||
proxy(&index_scheduler, &index_uid, &req, network, Body::none(), &task).await?;
|
proxy(&index_scheduler, &index_uid, &req, network, Body::none(), &task).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub async fn proxy<T: serde::Serialize>(
|
|||||||
index_scheduler: &IndexScheduler,
|
index_scheduler: &IndexScheduler,
|
||||||
index_uid: &str,
|
index_uid: &str,
|
||||||
req: &HttpRequest,
|
req: &HttpRequest,
|
||||||
network: meilisearch_types::enterprise_edition::network::Network,
|
network: meilisearch_types::network::Network,
|
||||||
body: Body<T>,
|
body: Body<T>,
|
||||||
task: &meilisearch_types::tasks::Task,
|
task: &meilisearch_types::tasks::Task,
|
||||||
) -> Result<(), MeilisearchHttpError> {
|
) -> Result<(), MeilisearchHttpError> {
|
||||||
|
|||||||
@@ -30,7 +30,16 @@ use crate::Opt;
|
|||||||
|
|
||||||
pub mod compact;
|
pub mod compact;
|
||||||
pub mod documents;
|
pub mod documents;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
mod community_edition;
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
mod enterprise_edition;
|
mod enterprise_edition;
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
use community_edition as current_edition;
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
use enterprise_edition as current_edition;
|
||||||
|
|
||||||
pub mod facet_search;
|
pub mod facet_search;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
mod search_analytics;
|
mod search_analytics;
|
||||||
@@ -41,7 +50,7 @@ mod settings_analytics;
|
|||||||
pub mod similar;
|
pub mod similar;
|
||||||
mod similar_analytics;
|
mod similar_analytics;
|
||||||
|
|
||||||
pub use enterprise_edition::proxy::{PROXY_ORIGIN_REMOTE_HEADER, PROXY_ORIGIN_TASK_UID_HEADER};
|
pub use current_edition::proxy::{PROXY_ORIGIN_REMOTE_HEADER, PROXY_ORIGIN_TASK_UID_HEADER};
|
||||||
|
|
||||||
#[derive(OpenApi)]
|
#[derive(OpenApi)]
|
||||||
#[openapi(
|
#[openapi(
|
||||||
|
|||||||
@@ -498,17 +498,6 @@ make_setting_routes!(
|
|||||||
camelcase_attr: "facetSearch",
|
camelcase_attr: "facetSearch",
|
||||||
analytics: FacetSearchAnalytics
|
analytics: FacetSearchAnalytics
|
||||||
},
|
},
|
||||||
{
|
|
||||||
route: "/execute-after-update",
|
|
||||||
update_verb: put,
|
|
||||||
value_type: String,
|
|
||||||
err_type: meilisearch_types::deserr::DeserrJsonError<
|
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsexecuteAfterUpdate,
|
|
||||||
>,
|
|
||||||
attr: execute_after_update,
|
|
||||||
camelcase_attr: "executeAfterUpdate",
|
|
||||||
analytics: ExecuteAfterUpdateAnalytics
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
route: "/prefix-search",
|
route: "/prefix-search",
|
||||||
update_verb: put,
|
update_verb: put,
|
||||||
@@ -630,9 +619,6 @@ pub async fn update_all(
|
|||||||
new_settings.non_separator_tokens.as_ref().set(),
|
new_settings.non_separator_tokens.as_ref().set(),
|
||||||
),
|
),
|
||||||
facet_search: FacetSearchAnalytics::new(new_settings.facet_search.as_ref().set()),
|
facet_search: FacetSearchAnalytics::new(new_settings.facet_search.as_ref().set()),
|
||||||
execute_after_update: ExecuteAfterUpdateAnalytics::new(
|
|
||||||
new_settings.execute_after_update.as_ref().set(),
|
|
||||||
),
|
|
||||||
prefix_search: PrefixSearchAnalytics::new(new_settings.prefix_search.as_ref().set()),
|
prefix_search: PrefixSearchAnalytics::new(new_settings.prefix_search.as_ref().set()),
|
||||||
chat: ChatAnalytics::new(new_settings.chat.as_ref().set()),
|
chat: ChatAnalytics::new(new_settings.chat.as_ref().set()),
|
||||||
vector_store: VectorStoreAnalytics::new(new_settings.vector_store.as_ref().set()),
|
vector_store: VectorStoreAnalytics::new(new_settings.vector_store.as_ref().set()),
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ pub struct SettingsAnalytics {
|
|||||||
pub prefix_search: PrefixSearchAnalytics,
|
pub prefix_search: PrefixSearchAnalytics,
|
||||||
pub chat: ChatAnalytics,
|
pub chat: ChatAnalytics,
|
||||||
pub vector_store: VectorStoreAnalytics,
|
pub vector_store: VectorStoreAnalytics,
|
||||||
pub execute_after_update: ExecuteAfterUpdateAnalytics,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aggregate for SettingsAnalytics {
|
impl Aggregate for SettingsAnalytics {
|
||||||
@@ -198,9 +197,6 @@ impl Aggregate for SettingsAnalytics {
|
|||||||
set: new.facet_search.set | self.facet_search.set,
|
set: new.facet_search.set | self.facet_search.set,
|
||||||
value: new.facet_search.value.or(self.facet_search.value),
|
value: new.facet_search.value.or(self.facet_search.value),
|
||||||
},
|
},
|
||||||
execute_after_update: ExecuteAfterUpdateAnalytics {
|
|
||||||
set: new.execute_after_update.set | self.execute_after_update.set,
|
|
||||||
},
|
|
||||||
prefix_search: PrefixSearchAnalytics {
|
prefix_search: PrefixSearchAnalytics {
|
||||||
set: new.prefix_search.set | self.prefix_search.set,
|
set: new.prefix_search.set | self.prefix_search.set,
|
||||||
value: new.prefix_search.value.or(self.prefix_search.value),
|
value: new.prefix_search.value.or(self.prefix_search.value),
|
||||||
@@ -673,21 +669,6 @@ impl FacetSearchAnalytics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Default)]
|
|
||||||
pub struct ExecuteAfterUpdateAnalytics {
|
|
||||||
pub set: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExecuteAfterUpdateAnalytics {
|
|
||||||
pub fn new(distinct: Option<&String>) -> Self {
|
|
||||||
Self { set: distinct.is_some() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_settings(self) -> SettingsAnalytics {
|
|
||||||
SettingsAnalytics { execute_after_update: self, ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Default)]
|
#[derive(Serialize, Default)]
|
||||||
pub struct PrefixSearchAnalytics {
|
pub struct PrefixSearchAnalytics {
|
||||||
pub set: bool,
|
pub set: bool,
|
||||||
|
|||||||
@@ -183,7 +183,11 @@ pub async fn get_metrics(
|
|||||||
crate::metrics::MEILISEARCH_LAST_FINISHED_BATCHES_PROGRESS_TRACE_MS.reset();
|
crate::metrics::MEILISEARCH_LAST_FINISHED_BATCHES_PROGRESS_TRACE_MS.reset();
|
||||||
let (batches, _total) = index_scheduler.get_batches_from_authorized_indexes(
|
let (batches, _total) = index_scheduler.get_batches_from_authorized_indexes(
|
||||||
// Fetch the finished batches...
|
// Fetch the finished batches...
|
||||||
&Query { statuses: Some(vec![Status::Succeeded, Status::Failed]), ..Query::default() },
|
&Query {
|
||||||
|
statuses: Some(vec![Status::Succeeded, Status::Failed]),
|
||||||
|
limit: Some(1),
|
||||||
|
..Query::default()
|
||||||
|
},
|
||||||
auth_filters,
|
auth_filters,
|
||||||
)?;
|
)?;
|
||||||
// ...and get the last batch only.
|
// ...and get the last batch only.
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use deserr::Deserr;
|
|||||||
use index_scheduler::IndexScheduler;
|
use index_scheduler::IndexScheduler;
|
||||||
use itertools::{EitherOrBoth, Itertools};
|
use itertools::{EitherOrBoth, Itertools};
|
||||||
use meilisearch_types::deserr::DeserrJsonError;
|
use meilisearch_types::deserr::DeserrJsonError;
|
||||||
use meilisearch_types::enterprise_edition::network::{Network as DbNetwork, Remote as DbRemote};
|
|
||||||
use meilisearch_types::error::deserr_codes::{
|
use meilisearch_types::error::deserr_codes::{
|
||||||
InvalidNetworkRemotes, InvalidNetworkSearchApiKey, InvalidNetworkSelf, InvalidNetworkSharding,
|
InvalidNetworkRemotes, InvalidNetworkSearchApiKey, InvalidNetworkSelf, InvalidNetworkSharding,
|
||||||
InvalidNetworkUrl, InvalidNetworkWriteApiKey,
|
InvalidNetworkUrl, InvalidNetworkWriteApiKey,
|
||||||
@@ -15,6 +14,7 @@ use meilisearch_types::error::deserr_codes::{
|
|||||||
use meilisearch_types::error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
use meilisearch_types::keys::actions;
|
use meilisearch_types::keys::actions;
|
||||||
use meilisearch_types::milli::update::Setting;
|
use meilisearch_types::milli::update::Setting;
|
||||||
|
use meilisearch_types::network::{Network as DbNetwork, Remote as DbRemote};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use utoipa::{OpenApi, ToSchema};
|
use utoipa::{OpenApi, ToSchema};
|
||||||
@@ -211,6 +211,16 @@ async fn patch_network(
|
|||||||
let old_network = index_scheduler.network();
|
let old_network = index_scheduler.network();
|
||||||
debug!(parameters = ?new_network, "Patch network");
|
debug!(parameters = ?new_network, "Patch network");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
if new_network.sharding.set().is_some() {
|
||||||
|
use meilisearch_types::error::Code;
|
||||||
|
|
||||||
|
return Err(ResponseError::from_msg(
|
||||||
|
"Meilisearch Enterprise Edition is required to set `network.sharding`".into(),
|
||||||
|
Code::RequiresEnterpriseEdition,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let merged_self = match new_network.local {
|
let merged_self = match new_network.local {
|
||||||
Setting::Set(new_self) => Some(new_self),
|
Setting::Set(new_self) => Some(new_self),
|
||||||
Setting::Reset => None,
|
Setting::Reset => None,
|
||||||
@@ -312,6 +322,7 @@ async fn patch_network(
|
|||||||
|
|
||||||
let merged_network =
|
let merged_network =
|
||||||
DbNetwork { local: merged_self, remotes: merged_remotes, sharding: merged_sharding };
|
DbNetwork { local: merged_self, remotes: merged_remotes, sharding: merged_sharding };
|
||||||
|
|
||||||
index_scheduler.put_network(merged_network.clone())?;
|
index_scheduler.put_network(merged_network.clone())?;
|
||||||
debug!(returns = ?merged_network, "Patch network");
|
debug!(returns = ?merged_network, "Patch network");
|
||||||
Ok(HttpResponse::Ok().json(merged_network))
|
Ok(HttpResponse::Ok().json(merged_network))
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ use std::vec::{IntoIter, Vec};
|
|||||||
use actix_http::StatusCode;
|
use actix_http::StatusCode;
|
||||||
use index_scheduler::{IndexScheduler, RoFeatures};
|
use index_scheduler::{IndexScheduler, RoFeatures};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use meilisearch_types::enterprise_edition::network::{Network, Remote};
|
|
||||||
use meilisearch_types::error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
use meilisearch_types::milli::order_by_map::OrderByMap;
|
use meilisearch_types::milli::order_by_map::OrderByMap;
|
||||||
use meilisearch_types::milli::score_details::{ScoreDetails, WeightedScoreValue};
|
use meilisearch_types::milli::score_details::{ScoreDetails, WeightedScoreValue};
|
||||||
use meilisearch_types::milli::vector::Embedding;
|
use meilisearch_types::milli::vector::Embedding;
|
||||||
use meilisearch_types::milli::{self, DocumentId, OrderBy, TimeBudget, DEFAULT_VALUES_PER_FACET};
|
use meilisearch_types::milli::{self, DocumentId, OrderBy, TimeBudget, DEFAULT_VALUES_PER_FACET};
|
||||||
|
use meilisearch_types::network::{Network, Remote};
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
pub use error::ProxySearchError;
|
pub use error::ProxySearchError;
|
||||||
use error::ReqwestErrorWithoutUrl;
|
use error::ReqwestErrorWithoutUrl;
|
||||||
use meilisearch_types::enterprise_edition::network::Remote;
|
use meilisearch_types::network::Remote;
|
||||||
use rand::Rng as _;
|
use rand::Rng as _;
|
||||||
use reqwest::{Client, Response, StatusCode};
|
use reqwest::{Client, Response, StatusCode};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|||||||
@@ -1339,3 +1339,266 @@ async fn get_document_with_vectors() {
|
|||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_fetch_documents_pagination_with_sorting() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
let (task, _code) = index.create(None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Set name as sortable attribute
|
||||||
|
let (task, code) = index.update_settings_sortable_attributes(json!(["name"])).await;
|
||||||
|
assert_eq!(code, 202);
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let documents = json!((0..50)
|
||||||
|
.map(|i| json!({"id": i, "name": format!("doc_{:05}", std::cmp::min(i, 5))}))
|
||||||
|
.collect::<Vec<_>>());
|
||||||
|
|
||||||
|
// Add documents as described in the bug report
|
||||||
|
let (task, code) = index.add_documents(documents, None).await;
|
||||||
|
assert_eq!(code, 202);
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Request 1 (first page): offset 0, limit 2
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 2,
|
||||||
|
"sort": ["name:asc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(code, 200);
|
||||||
|
let results = response["results"].as_array().unwrap();
|
||||||
|
snapshot!(json_string!(results), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "doc_00000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "doc_00001"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Request 2 (second page): offset 2, limit 2
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"offset": 2,
|
||||||
|
"limit": 2,
|
||||||
|
"sort": ["name:asc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(code, 200);
|
||||||
|
let results = response["results"].as_array().unwrap();
|
||||||
|
snapshot!(json_string!(results), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "doc_00002"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "doc_00003"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Request 3 (third page): offset 4, limit 2
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"offset": 4,
|
||||||
|
"limit": 2,
|
||||||
|
"sort": ["name:asc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(code, 200);
|
||||||
|
let results = response["results"].as_array().unwrap();
|
||||||
|
snapshot!(json_string!(results), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "doc_00004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "doc_00005"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Request 4 (fourth page): offset 6, limit 2
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"offset": 6,
|
||||||
|
"limit": 2,
|
||||||
|
"sort": ["name:asc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!(code, 200);
|
||||||
|
let results = response["results"].as_array().unwrap();
|
||||||
|
snapshot!(json_string!(results), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "doc_00005"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "doc_00005"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
// <https://github.com/meilisearch/meilisearch/issues/5998>
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn get_document_sort_field_not_in_any_document() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
let (task, _code) = index.create(None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (task, _code) = index.update_settings_sortable_attributes(json!(["created_at"])).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let documents = json!([
|
||||||
|
{ "id": 1, "name": "Document 1" },
|
||||||
|
{ "id": 2, "name": "Document 2" }
|
||||||
|
]);
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"sort": ["created_at:asc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response), @r###"
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Document 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Document 2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 20,
|
||||||
|
"total": 2
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn get_document_sort_includes_docs_without_field() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
let (task, _code) = index.create(None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (task, _code) = index.update_settings_sortable_attributes(json!(["created_at"])).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let documents = json!([
|
||||||
|
{ "id": 1, "name": "Doc without created_at" },
|
||||||
|
{ "id": 2, "name": "Doc with created_at", "created_at": "2025-01-15" },
|
||||||
|
{ "id": 3, "name": "Another doc without created_at" },
|
||||||
|
{ "id": 4, "name": "Another doc with created_at", "created_at": "2025-01-10" }
|
||||||
|
]);
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"sort": ["created_at:asc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response), @r###"
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "Another doc with created_at",
|
||||||
|
"created_at": "2025-01-10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Doc with created_at",
|
||||||
|
"created_at": "2025-01-15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Doc without created_at"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Another doc without created_at"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 20,
|
||||||
|
"total": 4
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn get_document_sort_desc_includes_docs_without_field() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
let (task, _code) = index.create(None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (task, _code) = index.update_settings_sortable_attributes(json!(["priority"])).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let documents = json!([
|
||||||
|
{ "id": 1, "name": "Low priority", "priority": 1 },
|
||||||
|
{ "id": 2, "name": "No priority" },
|
||||||
|
{ "id": 3, "name": "High priority", "priority": 10 }
|
||||||
|
]);
|
||||||
|
let (task, _code) = index.add_documents(documents, None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.fetch_documents(json!({
|
||||||
|
"sort": ["priority:desc"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response), @r###"
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "High priority",
|
||||||
|
"priority": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Low priority",
|
||||||
|
"priority": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "No priority"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 20,
|
||||||
|
"total": 3
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3142,6 +3142,7 @@ fn fail(override_response_body: Option<&str>) -> ResponseTemplate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn remote_auto_sharding() {
|
async fn remote_auto_sharding() {
|
||||||
let ms0 = Server::new().await;
|
let ms0 = Server::new().await;
|
||||||
@@ -3161,7 +3162,6 @@ async fn remote_auto_sharding() {
|
|||||||
snapshot!(json_string!(response["network"]), @"true");
|
snapshot!(json_string!(response["network"]), @"true");
|
||||||
|
|
||||||
// set self & sharding
|
// set self & sharding
|
||||||
|
|
||||||
let (response, code) = ms0.set_network(json!({"self": "ms0", "sharding": true})).await;
|
let (response, code) = ms0.set_network(json!({"self": "ms0", "sharding": true})).await;
|
||||||
snapshot!(code, @"200 OK");
|
snapshot!(code, @"200 OK");
|
||||||
snapshot!(json_string!(response), @r###"
|
snapshot!(json_string!(response), @r###"
|
||||||
@@ -3462,6 +3462,30 @@ async fn remote_auto_sharding() {
|
|||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn sharding_not_enterprise() {
|
||||||
|
let ms0 = Server::new().await;
|
||||||
|
|
||||||
|
// enable feature
|
||||||
|
|
||||||
|
let (response, code) = ms0.set_features(json!({"network": true})).await;
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["network"]), @"true");
|
||||||
|
|
||||||
|
let (response, code) = ms0.set_network(json!({"self": "ms0", "sharding": true})).await;
|
||||||
|
snapshot!(code, @"451 Unavailable For Legal Reasons");
|
||||||
|
snapshot!(json_string!(response), @r###"
|
||||||
|
{
|
||||||
|
"message": "Meilisearch Enterprise Edition is required to set `network.sharding`",
|
||||||
|
"code": "requires_enterprise_edition",
|
||||||
|
"type": "invalid_request",
|
||||||
|
"link": "https://docs.meilisearch.com/errors#requires_enterprise_edition"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn remote_auto_sharding_with_custom_metadata() {
|
async fn remote_auto_sharding_with_custom_metadata() {
|
||||||
let ms0 = Server::new().await;
|
let ms0 = Server::new().await;
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ test_setting_routes!(
|
|||||||
{
|
{
|
||||||
setting: vector_store,
|
setting: vector_store,
|
||||||
update_verb: patch,
|
update_verb: patch,
|
||||||
default_value: null
|
default_value: "experimental"
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ mod chat;
|
|||||||
mod distinct;
|
mod distinct;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod get_settings;
|
mod get_settings;
|
||||||
|
mod parent_seachable_fields;
|
||||||
mod prefix_search_settings;
|
mod prefix_search_settings;
|
||||||
mod proximity_settings;
|
mod proximity_settings;
|
||||||
mod tokenizer_customization;
|
mod tokenizer_customization;
|
||||||
|
|||||||
114
crates/meilisearch/tests/settings/parent_seachable_fields.rs
Normal file
114
crates/meilisearch/tests/settings/parent_seachable_fields.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use meili_snap::{json_string, snapshot};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::common::Server;
|
||||||
|
use crate::json;
|
||||||
|
|
||||||
|
static DOCUMENTS: Lazy<crate::common::Value> = Lazy::new(|| {
|
||||||
|
json!([
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"meta": {
|
||||||
|
"title": "Soup of the day",
|
||||||
|
"description": "many the fish",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"meta": {
|
||||||
|
"title": "Soup of day",
|
||||||
|
"description": "many the lazy fish",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"meta": {
|
||||||
|
"title": "the Soup of day",
|
||||||
|
"description": "many the fish",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn nested_field_becomes_searchable() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let (task, _status_code) = index.add_documents(DOCUMENTS.clone(), None).await;
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"searchableAttributes": ["meta.title"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{response:?}");
|
||||||
|
server.wait_task(response.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// We expect no documents when searching for
|
||||||
|
// a nested non-searchable field
|
||||||
|
index
|
||||||
|
.search(json!({"q": "many fish"}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"[]"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"searchableAttributes": ["meta.title", "meta.description"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{response:?}");
|
||||||
|
server.wait_task(response.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// We expect all the documents when the nested field becomes searchable
|
||||||
|
index
|
||||||
|
.search(json!({"q": "many fish"}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"meta": {
|
||||||
|
"title": "Soup of the day",
|
||||||
|
"description": "many the fish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"meta": {
|
||||||
|
"title": "the Soup of day",
|
||||||
|
"description": "many the fish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"meta": {
|
||||||
|
"title": "Soup of day",
|
||||||
|
"description": "many the lazy fish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"searchableAttributes": ["meta.title"]
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{response:?}");
|
||||||
|
server.wait_task(response.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// We expect no documents when searching for
|
||||||
|
// a nested non-searchable field
|
||||||
|
index
|
||||||
|
.search(json!({"q": "many fish"}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"[]"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
@@ -42,8 +42,16 @@ async fn version_too_old() {
|
|||||||
std::fs::create_dir_all(&db_path).unwrap();
|
std::fs::create_dir_all(&db_path).unwrap();
|
||||||
std::fs::write(db_path.join("VERSION"), "1.11.9999").unwrap();
|
std::fs::write(db_path.join("VERSION"), "1.11.9999").unwrap();
|
||||||
let options = Opt { experimental_dumpless_upgrade: true, ..default_settings };
|
let options = Opt { experimental_dumpless_upgrade: true, ..default_settings };
|
||||||
let err = Server::new_with_options(options).await.map(|_| ()).unwrap_err();
|
let err = Server::new_with_options(options).await.map(|_| ()).unwrap_err().to_string();
|
||||||
snapshot!(err, @"Database version 1.11.9999 is too old for the experimental dumpless upgrade feature. Please generate a dump using the v1.11.9999 and import it in the v1.26.0");
|
|
||||||
|
let major = meilisearch_types::versioning::VERSION_MAJOR;
|
||||||
|
let minor = meilisearch_types::versioning::VERSION_MINOR;
|
||||||
|
let patch = meilisearch_types::versioning::VERSION_PATCH;
|
||||||
|
|
||||||
|
let current_version = format!("{major}.{minor}.{patch}");
|
||||||
|
let err = err.replace(¤t_version, "[current version]");
|
||||||
|
|
||||||
|
snapshot!(err, @"Database version 1.11.9999 is too old for the experimental dumpless upgrade feature. Please generate a dump using the v1.11.9999 and import it in the v[current version]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
@@ -54,11 +62,21 @@ async fn version_requires_downgrade() {
|
|||||||
std::fs::create_dir_all(&db_path).unwrap();
|
std::fs::create_dir_all(&db_path).unwrap();
|
||||||
let major = meilisearch_types::versioning::VERSION_MAJOR;
|
let major = meilisearch_types::versioning::VERSION_MAJOR;
|
||||||
let minor = meilisearch_types::versioning::VERSION_MINOR;
|
let minor = meilisearch_types::versioning::VERSION_MINOR;
|
||||||
let patch = meilisearch_types::versioning::VERSION_PATCH + 1;
|
let mut patch = meilisearch_types::versioning::VERSION_PATCH;
|
||||||
std::fs::write(db_path.join("VERSION"), format!("{major}.{minor}.{patch}")).unwrap();
|
|
||||||
|
let current_version = format!("{major}.{minor}.{patch}");
|
||||||
|
patch += 1;
|
||||||
|
let future_version = format!("{major}.{minor}.{patch}");
|
||||||
|
|
||||||
|
std::fs::write(db_path.join("VERSION"), &future_version).unwrap();
|
||||||
let options = Opt { experimental_dumpless_upgrade: true, ..default_settings };
|
let options = Opt { experimental_dumpless_upgrade: true, ..default_settings };
|
||||||
let err = Server::new_with_options(options).await.map(|_| ()).unwrap_err();
|
let err = Server::new_with_options(options).await.map(|_| ()).unwrap_err();
|
||||||
snapshot!(err, @"Database version 1.26.1 is higher than the Meilisearch version 1.26.0. Downgrade is not supported");
|
|
||||||
|
let err = err.to_string();
|
||||||
|
let err = err.replace(¤t_version, "[current version]");
|
||||||
|
let err = err.replace(&future_version, "[future version]");
|
||||||
|
|
||||||
|
snapshot!(err, @"Database version [future version] is higher than the Meilisearch version [current version]. Downgrade is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"progress": null,
|
"progress": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"totalNbTasks": 1,
|
"totalNbTasks": 1,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"progress": null,
|
"progress": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"totalNbTasks": 1,
|
"totalNbTasks": 1,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"progress": null,
|
"progress": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"totalNbTasks": 1,
|
"totalNbTasks": 1,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"canceledBy": null,
|
"canceledBy": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"error": null,
|
"error": null,
|
||||||
"duration": "[duration]",
|
"duration": "[duration]",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"canceledBy": null,
|
"canceledBy": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"error": null,
|
"error": null,
|
||||||
"duration": "[duration]",
|
"duration": "[duration]",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"canceledBy": null,
|
"canceledBy": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"error": null,
|
"error": null,
|
||||||
"duration": "[duration]",
|
"duration": "[duration]",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"progress": null,
|
"progress": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"totalNbTasks": 1,
|
"totalNbTasks": 1,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ source: crates/meilisearch/tests/upgrade/v1_12/v1_12_0.rs
|
|||||||
"canceledBy": null,
|
"canceledBy": null,
|
||||||
"details": {
|
"details": {
|
||||||
"upgradeFrom": "v1.12.0",
|
"upgradeFrom": "v1.12.0",
|
||||||
"upgradeTo": "v1.26.0"
|
"upgradeTo": "[current version]"
|
||||||
},
|
},
|
||||||
"error": null,
|
"error": null,
|
||||||
"duration": "[duration]",
|
"duration": "[duration]",
|
||||||
|
|||||||
@@ -166,55 +166,55 @@ async fn check_the_index_scheduler(server: &Server) {
|
|||||||
// We rewrite the first task for all calls because it may be the upgrade database with unknown dates and duration.
|
// We rewrite the first task for all calls because it may be the upgrade database with unknown dates and duration.
|
||||||
// The other tasks should NOT change
|
// The other tasks should NOT change
|
||||||
let (tasks, _) = server.tasks_filter("limit=1000").await;
|
let (tasks, _) = server.tasks_filter("limit=1000").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "the_whole_task_queue_once_everything_has_been_processed");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "the_whole_task_queue_once_everything_has_been_processed");
|
||||||
let (batches, _) = server.batches_filter("limit=1000").await;
|
let (batches, _) = server.batches_filter("limit=1000").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "the_whole_batch_queue_once_everything_has_been_processed");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "the_whole_batch_queue_once_everything_has_been_processed");
|
||||||
|
|
||||||
// Tests all the tasks query parameters
|
// Tests all the tasks query parameters
|
||||||
let (tasks, _) = server.tasks_filter("uids=10").await;
|
let (tasks, _) = server.tasks_filter("uids=10").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_uids_equal_10");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_uids_equal_10");
|
||||||
let (tasks, _) = server.tasks_filter("batchUids=10").await;
|
let (tasks, _) = server.tasks_filter("batchUids=10").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_batchUids_equal_10");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_batchUids_equal_10");
|
||||||
let (tasks, _) = server.tasks_filter("statuses=canceled").await;
|
let (tasks, _) = server.tasks_filter("statuses=canceled").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_statuses_equal_canceled");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_statuses_equal_canceled");
|
||||||
// types has already been tested above to retrieve the upgrade database
|
// types has already been tested above to retrieve the upgrade database
|
||||||
let (tasks, _) = server.tasks_filter("canceledBy=19").await;
|
let (tasks, _) = server.tasks_filter("canceledBy=19").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_canceledBy_equal_19");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_canceledBy_equal_19");
|
||||||
let (tasks, _) = server.tasks_filter("beforeEnqueuedAt=2025-01-16T16:47:41Z").await;
|
let (tasks, _) = server.tasks_filter("beforeEnqueuedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_beforeEnqueuedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_beforeEnqueuedAt_equal_2025-01-16T16_47_41");
|
||||||
let (tasks, _) = server.tasks_filter("afterEnqueuedAt=2025-01-16T16:47:41Z").await;
|
let (tasks, _) = server.tasks_filter("afterEnqueuedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_afterEnqueuedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_afterEnqueuedAt_equal_2025-01-16T16_47_41");
|
||||||
let (tasks, _) = server.tasks_filter("beforeStartedAt=2025-01-16T16:47:41Z").await;
|
let (tasks, _) = server.tasks_filter("beforeStartedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_beforeStartedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_beforeStartedAt_equal_2025-01-16T16_47_41");
|
||||||
let (tasks, _) = server.tasks_filter("afterStartedAt=2025-01-16T16:47:41Z").await;
|
let (tasks, _) = server.tasks_filter("afterStartedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_afterStartedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_afterStartedAt_equal_2025-01-16T16_47_41");
|
||||||
let (tasks, _) = server.tasks_filter("beforeFinishedAt=2025-01-16T16:47:41Z").await;
|
let (tasks, _) = server.tasks_filter("beforeFinishedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_beforeFinishedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_beforeFinishedAt_equal_2025-01-16T16_47_41");
|
||||||
let (tasks, _) = server.tasks_filter("afterFinishedAt=2025-01-16T16:47:41Z").await;
|
let (tasks, _) = server.tasks_filter("afterFinishedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(tasks, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_afterFinishedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(tasks, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]" }), name: "tasks_filter_afterFinishedAt_equal_2025-01-16T16_47_41");
|
||||||
|
|
||||||
// Tests all the batches query parameters
|
// Tests all the batches query parameters
|
||||||
let (batches, _) = server.batches_filter("uids=10").await;
|
let (batches, _) = server.batches_filter("uids=10").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_uids_equal_10");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_uids_equal_10");
|
||||||
let (batches, _) = server.batches_filter("batchUids=10").await;
|
let (batches, _) = server.batches_filter("batchUids=10").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_batchUids_equal_10");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_batchUids_equal_10");
|
||||||
let (batches, _) = server.batches_filter("statuses=canceled").await;
|
let (batches, _) = server.batches_filter("statuses=canceled").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_statuses_equal_canceled");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_statuses_equal_canceled");
|
||||||
// types has already been tested above to retrieve the upgrade database
|
// types has already been tested above to retrieve the upgrade database
|
||||||
let (batches, _) = server.batches_filter("canceledBy=19").await;
|
let (batches, _) = server.batches_filter("canceledBy=19").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_canceledBy_equal_19");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_canceledBy_equal_19");
|
||||||
let (batches, _) = server.batches_filter("beforeEnqueuedAt=2025-01-16T16:47:41Z").await;
|
let (batches, _) = server.batches_filter("beforeEnqueuedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_beforeEnqueuedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_beforeEnqueuedAt_equal_2025-01-16T16_47_41");
|
||||||
let (batches, _) = server.batches_filter("afterEnqueuedAt=2025-01-16T16:47:41Z").await;
|
let (batches, _) = server.batches_filter("afterEnqueuedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_afterEnqueuedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_afterEnqueuedAt_equal_2025-01-16T16_47_41");
|
||||||
let (batches, _) = server.batches_filter("beforeStartedAt=2025-01-16T16:47:41Z").await;
|
let (batches, _) = server.batches_filter("beforeStartedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_beforeStartedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_beforeStartedAt_equal_2025-01-16T16_47_41");
|
||||||
let (batches, _) = server.batches_filter("afterStartedAt=2025-01-16T16:47:41Z").await;
|
let (batches, _) = server.batches_filter("afterStartedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_afterStartedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_afterStartedAt_equal_2025-01-16T16_47_41");
|
||||||
let (batches, _) = server.batches_filter("beforeFinishedAt=2025-01-16T16:47:41Z").await;
|
let (batches, _) = server.batches_filter("beforeFinishedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_beforeFinishedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_beforeFinishedAt_equal_2025-01-16T16_47_41");
|
||||||
let (batches, _) = server.batches_filter("afterFinishedAt=2025-01-16T16:47:41Z").await;
|
let (batches, _) = server.batches_filter("afterFinishedAt=2025-01-16T16:47:41Z").await;
|
||||||
snapshot!(json_string!(batches, { ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_afterFinishedAt_equal_2025-01-16T16_47_41");
|
snapshot!(json_string!(batches, { ".results[0].details.upgradeTo" => "[current version]", ".results[0].duration" => "[duration]", ".results[0].enqueuedAt" => "[date]", ".results[0].startedAt" => "[date]", ".results[0].finishedAt" => "[date]", ".results[0].stats.progressTrace" => "[progressTrace]", ".results[0].stats.internalDatabaseSizes" => "[internalDatabaseSizes]", ".results[0].stats.writeChannelCongestion" => "[writeChannelCongestion]" }), name: "batches_filter_afterFinishedAt_equal_2025-01-16T16_47_41");
|
||||||
|
|
||||||
let (stats, _) = server.stats().await;
|
let (stats, _) = server.stats().await;
|
||||||
assert_json_snapshot!(stats, {
|
assert_json_snapshot!(stats, {
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ async fn binary_quantize_before_sending_documents() {
|
|||||||
"manual": {
|
"manual": {
|
||||||
"embeddings": [
|
"embeddings": [
|
||||||
[
|
[
|
||||||
-1.0,
|
0.0,
|
||||||
-1.0,
|
0.0,
|
||||||
1.0
|
1.0
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -122,7 +122,7 @@ async fn binary_quantize_before_sending_documents() {
|
|||||||
[
|
[
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
-1.0
|
0.0
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"regenerate": false
|
"regenerate": false
|
||||||
@@ -191,8 +191,8 @@ async fn binary_quantize_after_sending_documents() {
|
|||||||
"manual": {
|
"manual": {
|
||||||
"embeddings": [
|
"embeddings": [
|
||||||
[
|
[
|
||||||
-1.0,
|
0.0,
|
||||||
-1.0,
|
0.0,
|
||||||
1.0
|
1.0
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -209,7 +209,7 @@ async fn binary_quantize_after_sending_documents() {
|
|||||||
[
|
[
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
-1.0
|
0.0
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"regenerate": false
|
"regenerate": false
|
||||||
|
|||||||
43
crates/meilisearch/tests/vector/huggingface.rs
Normal file
43
crates/meilisearch/tests/vector/huggingface.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use meili_snap::snapshot;
|
||||||
|
|
||||||
|
use crate::common::{GetAllDocumentsOptions, Server};
|
||||||
|
use crate::json;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn hf_bge_m3_force_cls_settings() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"embedders": {
|
||||||
|
"default": {
|
||||||
|
"source": "huggingFace",
|
||||||
|
"model": "baai/bge-m3",
|
||||||
|
"revision": "5617a9f61b028005a4858fdac845db406aefb181",
|
||||||
|
"pooling": "forceCls",
|
||||||
|
// minimal template to allow potential document embedding if used later
|
||||||
|
"documentTemplate": "{{doc.title}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
snapshot!(code, @"202 Accepted");
|
||||||
|
server.wait_task(response.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Try to embed one simple document
|
||||||
|
let (task, code) =
|
||||||
|
index.add_documents(json!([{ "id": 1, "title": "Hello world" }]), None).await;
|
||||||
|
snapshot!(code, @"202 Accepted");
|
||||||
|
server.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
|
// Retrieve the document with vectors and assert embeddings were produced
|
||||||
|
let (documents, _code) = index
|
||||||
|
.get_all_documents(GetAllDocumentsOptions { retrieve_vectors: true, ..Default::default() })
|
||||||
|
.await;
|
||||||
|
let has_vectors = documents["results"][0]["_vectors"]["default"]["embeddings"]
|
||||||
|
.as_array()
|
||||||
|
.map(|a| !a.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
snapshot!(has_vectors, @"true");
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
mod binary_quantized;
|
mod binary_quantized;
|
||||||
mod fragments;
|
mod fragments;
|
||||||
|
mod huggingface;
|
||||||
#[cfg(feature = "test-ollama")]
|
#[cfg(feature = "test-ollama")]
|
||||||
mod ollama;
|
mod ollama;
|
||||||
mod openai;
|
mod openai;
|
||||||
|
|||||||
@@ -500,13 +500,6 @@ async fn test_both_apis() {
|
|||||||
snapshot!(code, @"200 OK");
|
snapshot!(code, @"200 OK");
|
||||||
snapshot!(json_string!(response["hits"]), @r###"
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"name": "kefir",
|
|
||||||
"gender": "M",
|
|
||||||
"birthyear": 2023,
|
|
||||||
"breed": "Patou"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Vénus",
|
"name": "Vénus",
|
||||||
@@ -527,6 +520,13 @@ async fn test_both_apis() {
|
|||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 1995,
|
"birthyear": 1995,
|
||||||
"breed": "Labrador Retriever"
|
"breed": "Labrador Retriever"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
@@ -540,13 +540,6 @@ async fn test_both_apis() {
|
|||||||
snapshot!(code, @"200 OK");
|
snapshot!(code, @"200 OK");
|
||||||
snapshot!(json_string!(response["hits"]), @r###"
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"name": "kefir",
|
|
||||||
"gender": "M",
|
|
||||||
"birthyear": 2023,
|
|
||||||
"breed": "Patou"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Vénus",
|
"name": "Vénus",
|
||||||
@@ -567,6 +560,13 @@ async fn test_both_apis() {
|
|||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 1995,
|
"birthyear": 1995,
|
||||||
"breed": "Labrador Retriever"
|
"breed": "Labrador Retriever"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
@@ -581,18 +581,11 @@ async fn test_both_apis() {
|
|||||||
snapshot!(json_string!(response["hits"]), @r###"
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 1,
|
||||||
"name": "kefir",
|
"name": "Intel",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2023,
|
"birthyear": 2011,
|
||||||
"breed": "Patou"
|
"breed": "Beagle"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Max",
|
|
||||||
"gender": "M",
|
|
||||||
"birthyear": 1995,
|
|
||||||
"breed": "Labrador Retriever"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@@ -602,11 +595,18 @@ async fn test_both_apis() {
|
|||||||
"breed": "Jack Russel Terrier"
|
"breed": "Jack Russel Terrier"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 3,
|
||||||
"name": "Intel",
|
"name": "Max",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2011,
|
"birthyear": 1995,
|
||||||
"breed": "Beagle"
|
"breed": "Labrador Retriever"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
@@ -621,18 +621,11 @@ async fn test_both_apis() {
|
|||||||
snapshot!(json_string!(response["hits"]), @r###"
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 1,
|
||||||
"name": "kefir",
|
"name": "Intel",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2023,
|
"birthyear": 2011,
|
||||||
"breed": "Patou"
|
"breed": "Beagle"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Max",
|
|
||||||
"gender": "M",
|
|
||||||
"birthyear": 1995,
|
|
||||||
"breed": "Labrador Retriever"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@@ -642,11 +635,18 @@ async fn test_both_apis() {
|
|||||||
"breed": "Jack Russel Terrier"
|
"breed": "Jack Russel Terrier"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 3,
|
||||||
"name": "Intel",
|
"name": "Max",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2011,
|
"birthyear": 1995,
|
||||||
"breed": "Beagle"
|
"breed": "Labrador Retriever"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
@@ -661,18 +661,11 @@ async fn test_both_apis() {
|
|||||||
snapshot!(json_string!(response["hits"]), @r###"
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 1,
|
||||||
"name": "kefir",
|
"name": "Intel",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2023,
|
"birthyear": 2011,
|
||||||
"breed": "Patou"
|
"breed": "Beagle"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Max",
|
|
||||||
"gender": "M",
|
|
||||||
"birthyear": 1995,
|
|
||||||
"breed": "Labrador Retriever"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@@ -682,11 +675,18 @@ async fn test_both_apis() {
|
|||||||
"breed": "Jack Russel Terrier"
|
"breed": "Jack Russel Terrier"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 3,
|
||||||
"name": "Intel",
|
"name": "Max",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2011,
|
"birthyear": 1995,
|
||||||
"breed": "Beagle"
|
"breed": "Labrador Retriever"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
@@ -701,18 +701,11 @@ async fn test_both_apis() {
|
|||||||
snapshot!(json_string!(response["hits"]), @r###"
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 1,
|
||||||
"name": "kefir",
|
"name": "Intel",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2023,
|
"birthyear": 2011,
|
||||||
"breed": "Patou"
|
"breed": "Beagle"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Max",
|
|
||||||
"gender": "M",
|
|
||||||
"birthyear": 1995,
|
|
||||||
"breed": "Labrador Retriever"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@@ -722,11 +715,18 @@ async fn test_both_apis() {
|
|||||||
"breed": "Jack Russel Terrier"
|
"breed": "Jack Russel Terrier"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 3,
|
||||||
"name": "Intel",
|
"name": "Max",
|
||||||
"gender": "M",
|
"gender": "M",
|
||||||
"birthyear": 2011,
|
"birthyear": 1995,
|
||||||
"breed": "Beagle"
|
"breed": "Labrador Retriever"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.100"
|
||||||
clap = { version = "4.5.40", features = ["derive"] }
|
clap = { version = "4.5.52", features = ["derive"] }
|
||||||
dump = { path = "../dump" }
|
dump = { path = "../dump" }
|
||||||
file-store = { path = "../file-store" }
|
file-store = { path = "../file-store" }
|
||||||
indexmap = { version = "2.9.0", features = ["serde"] }
|
indexmap = { version = "2.12.0", features = ["serde"] }
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order"] }
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
time = { version = "0.3.41", features = ["formatting", "parsing", "alloc"] }
|
time = { version = "0.3.44", features = ["formatting", "parsing", "alloc"] }
|
||||||
uuid = { version = "1.17.0", features = ["v4"], default-features = false }
|
uuid = { version = "1.18.1", features = ["v4"], default-features = false }
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ license.workspace = true
|
|||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
bimap = { version = "0.6.3", features = ["serde"] }
|
bimap = { version = "0.6.3", features = ["serde"] }
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
bstr = "1.12.0"
|
bstr = "1.12.1"
|
||||||
bytemuck = { version = "1.23.1", features = ["extern_crate_alloc"] }
|
bytemuck = { version = "1.24.0", features = ["extern_crate_alloc"] }
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
charabia = { version = "0.9.8", default-features = false }
|
charabia = { version = "0.9.9", default-features = false }
|
||||||
cellulite = "0.3.1-nested-rtxns-2"
|
cellulite = "0.3.1-nested-rtxns-2"
|
||||||
concat-arrays = "0.1.2"
|
concat-arrays = "0.1.2"
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.9.0"
|
||||||
crossbeam-channel = "0.5.15"
|
crossbeam-channel = "0.5.15"
|
||||||
deserr = "0.6.3"
|
deserr = "0.6.4"
|
||||||
either = { version = "1.15.0", features = ["serde"] }
|
either = { version = "1.15.0", features = ["serde"] }
|
||||||
flatten-serde-json = { path = "../flatten-serde-json" }
|
flatten-serde-json = { path = "../flatten-serde-json" }
|
||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
@@ -38,39 +38,39 @@ heed = { version = "0.22.1-nested-rtxns-6", default-features = false, features =
|
|||||||
"serde-json",
|
"serde-json",
|
||||||
"serde-bincode",
|
"serde-bincode",
|
||||||
] }
|
] }
|
||||||
indexmap = { version = "2.9.0", features = ["serde"] }
|
indexmap = { version = "2.12.0", features = ["serde"] }
|
||||||
json-depth-checker = { path = "../json-depth-checker" }
|
json-depth-checker = { path = "../json-depth-checker" }
|
||||||
levenshtein_automata = { version = "0.2.1", features = ["fst_automaton"] }
|
levenshtein_automata = { version = "0.2.1", features = ["fst_automaton"] }
|
||||||
memchr = "2.7.5"
|
memchr = "2.7.6"
|
||||||
memmap2 = "0.9.7"
|
memmap2 = "0.9.9"
|
||||||
obkv = "0.3.0"
|
obkv = "0.3.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
ordered-float = "5.0.0"
|
ordered-float = "5.1.0"
|
||||||
rayon = "1.10.0"
|
rayon = "1.11.0"
|
||||||
roaring = { version = "0.10.12", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
rstar = { version = "0.12.2", features = ["serde"] }
|
rstar = { version = "0.12.2", features = ["serde"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.140", features = ["preserve_order", "raw_value"] }
|
serde_json = { version = "1.0.145", features = ["preserve_order", "raw_value"] }
|
||||||
slice-group-by = "0.3.1"
|
slice-group-by = "0.3.1"
|
||||||
smallstr = { version = "0.3.0", features = ["serde"] }
|
smallstr = { version = "0.3.1", features = ["serde"] }
|
||||||
smallvec = "1.15.1"
|
smallvec = "1.15.1"
|
||||||
smartstring = "1.0.1"
|
smartstring = "1.0.1"
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.23.0"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.17"
|
||||||
time = { version = "0.3.41", features = [
|
time = { version = "0.3.44", features = [
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
"formatting",
|
"formatting",
|
||||||
"parsing",
|
"parsing",
|
||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
uuid = { version = "1.18.1", features = ["v4"] }
|
||||||
|
|
||||||
filter-parser = { path = "../filter-parser" }
|
filter-parser = { path = "../filter-parser" }
|
||||||
|
|
||||||
# documents words self-join
|
# documents words self-join
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
|
||||||
csv = "1.3.1"
|
csv = "1.4.0"
|
||||||
candle-core = { version = "0.9.1" }
|
candle-core = { version = "0.9.1" }
|
||||||
candle-transformers = { version = "0.9.1" }
|
candle-transformers = { version = "0.9.1" }
|
||||||
candle-nn = { version = "0.9.1" }
|
candle-nn = { version = "0.9.1" }
|
||||||
@@ -81,9 +81,9 @@ hf-hub = { git = "https://github.com/dureuill/hf-hub.git", branch = "rust_tls",
|
|||||||
"online",
|
"online",
|
||||||
] }
|
] }
|
||||||
safetensors = "0.6.2"
|
safetensors = "0.6.2"
|
||||||
tiktoken-rs = "0.7.0"
|
tiktoken-rs = "0.9.1"
|
||||||
liquid = "0.26.11"
|
liquid = "0.26.11"
|
||||||
rhai = { version = "1.22.2", features = [
|
rhai = { version = "1.23.6", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"no_module",
|
"no_module",
|
||||||
"no_custom_syntax",
|
"no_custom_syntax",
|
||||||
@@ -91,18 +91,18 @@ rhai = { version = "1.22.2", features = [
|
|||||||
"sync",
|
"sync",
|
||||||
] }
|
] }
|
||||||
arroy = "0.6.4-nested-rtxns"
|
arroy = "0.6.4-nested-rtxns"
|
||||||
hannoy = { version = "0.0.9-nested-rtxns-2", features = ["arroy"] }
|
hannoy = { version = "0.1.0-nested-rtxns", features = ["arroy"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
ureq = { version = "2.12.1", features = ["json"] }
|
ureq = { version = "2.12.1", features = ["json"] }
|
||||||
url = "2.5.4"
|
url = "2.5.7"
|
||||||
hashbrown = "0.15.4"
|
hashbrown = "0.15.5"
|
||||||
bumpalo = "3.18.1"
|
bumpalo = "3.19.0"
|
||||||
bumparaw-collections = "0.1.4"
|
bumparaw-collections = "0.1.4"
|
||||||
steppe = { version = "0.4", default-features = false }
|
steppe = { version = "0.4", default-features = false }
|
||||||
thread_local = "1.1.9"
|
thread_local = "1.1.9"
|
||||||
rustc-hash = "2.1.1"
|
rustc-hash = "2.1.1"
|
||||||
enum-iterator = "2.1.0"
|
enum-iterator = "2.3.0"
|
||||||
bbqueue = { git = "https://github.com/meilisearch/bbqueue" }
|
bbqueue = { git = "https://github.com/meilisearch/bbqueue" }
|
||||||
flume = { version = "0.11.1", default-features = false }
|
flume = { version = "0.11.1", default-features = false }
|
||||||
utoipa = { version = "5.4.0", features = [
|
utoipa = { version = "5.4.0", features = [
|
||||||
@@ -112,21 +112,21 @@ utoipa = { version = "5.4.0", features = [
|
|||||||
"time",
|
"time",
|
||||||
"openapi_extensions",
|
"openapi_extensions",
|
||||||
] }
|
] }
|
||||||
lru = "0.14.0"
|
lru = "0.16.2"
|
||||||
twox-hash = { version = "2.1.1", default-features = false, features = [
|
twox-hash = { version = "2.1.2", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"xxhash3_64",
|
"xxhash3_64",
|
||||||
"xxhash64",
|
"xxhash64",
|
||||||
] }
|
] }
|
||||||
geo-types = "0.7.16"
|
geo-types = "0.7.17"
|
||||||
zerometry = "0.3.0"
|
zerometry = "0.3.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mimalloc = { version = "0.1.47", default-features = false }
|
mimalloc = { version = "0.1.48", default-features = false }
|
||||||
# fixed version due to format breakages in v1.40
|
# fixed version due to format breakages in v1.40
|
||||||
insta = "=1.39.0"
|
insta = "=1.39.0"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
md5 = "0.7.0"
|
md5 = "0.8.0"
|
||||||
meili-snap = { path = "../meili-snap" }
|
meili-snap = { path = "../meili-snap" }
|
||||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||||
|
|
||||||
@@ -141,6 +141,8 @@ lmdb-posix-sem = ["heed/posix-sem"]
|
|||||||
chinese = ["charabia/chinese"]
|
chinese = ["charabia/chinese"]
|
||||||
chinese-pinyin = ["chinese", "charabia/chinese-normalization-pinyin"]
|
chinese-pinyin = ["chinese", "charabia/chinese-normalization-pinyin"]
|
||||||
|
|
||||||
|
enterprise = []
|
||||||
|
|
||||||
# allow hebrew specialized tokenization
|
# allow hebrew specialized tokenization
|
||||||
hebrew = ["charabia/hebrew"]
|
hebrew = ["charabia/hebrew"]
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ impl Iterator for SortedDocumentsIterator<'_> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Otherwise don't directly iterate over children, skip them if we know we will go further
|
// Otherwise don't directly iterate over children, skip them if we know we will go further
|
||||||
let mut to_skip = n - 1;
|
let mut to_skip = n;
|
||||||
while to_skip > 0 {
|
while to_skip > 0 {
|
||||||
if let Err(e) = SortedDocumentsIterator::update_current(
|
if let Err(e) = SortedDocumentsIterator::update_current(
|
||||||
current_child,
|
current_child,
|
||||||
@@ -108,7 +108,7 @@ impl Iterator for SortedDocumentsIterator<'_> {
|
|||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// The current iterator is large enough, so we can forward the call to it.
|
// The current iterator is large enough, so we can forward the call to it.
|
||||||
return inner.nth(to_skip + 1);
|
return inner.nth(to_skip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,15 +240,25 @@ impl<'ctx> SortedDocumentsIteratorBuilder<'ctx> {
|
|||||||
) -> crate::Result<SortedDocumentsIterator<'ctx>> {
|
) -> crate::Result<SortedDocumentsIterator<'ctx>> {
|
||||||
let size = candidates.len() as usize;
|
let size = candidates.len() as usize;
|
||||||
|
|
||||||
|
// Get documents that have this facet field
|
||||||
|
let faceted_candidates = index.exists_faceted_documents_ids(rtxn, field_id)?;
|
||||||
|
// Documents that don't have this facet field should be returned at the end
|
||||||
|
let not_faceted_candidates = &candidates - &faceted_candidates;
|
||||||
|
// Only sort candidates that have the facet field
|
||||||
|
let faceted_candidates = candidates & faceted_candidates;
|
||||||
|
let mut not_faceted_candidates = Some(not_faceted_candidates);
|
||||||
|
|
||||||
// Perform the sort on the first field
|
// Perform the sort on the first field
|
||||||
let (number_iter, string_iter) = if ascending {
|
let (number_iter, string_iter) = if ascending {
|
||||||
let number_iter = ascending_facet_sort(rtxn, number_db, field_id, candidates.clone())?;
|
let number_iter =
|
||||||
let string_iter = ascending_facet_sort(rtxn, string_db, field_id, candidates)?;
|
ascending_facet_sort(rtxn, number_db, field_id, faceted_candidates.clone())?;
|
||||||
|
let string_iter = ascending_facet_sort(rtxn, string_db, field_id, faceted_candidates)?;
|
||||||
|
|
||||||
(itertools::Either::Left(number_iter), itertools::Either::Left(string_iter))
|
(itertools::Either::Left(number_iter), itertools::Either::Left(string_iter))
|
||||||
} else {
|
} else {
|
||||||
let number_iter = descending_facet_sort(rtxn, number_db, field_id, candidates.clone())?;
|
let number_iter =
|
||||||
let string_iter = descending_facet_sort(rtxn, string_db, field_id, candidates)?;
|
descending_facet_sort(rtxn, number_db, field_id, faceted_candidates.clone())?;
|
||||||
|
let string_iter = descending_facet_sort(rtxn, string_db, field_id, faceted_candidates)?;
|
||||||
|
|
||||||
(itertools::Either::Right(number_iter), itertools::Either::Right(string_iter))
|
(itertools::Either::Right(number_iter), itertools::Either::Right(string_iter))
|
||||||
};
|
};
|
||||||
@@ -256,7 +266,10 @@ impl<'ctx> SortedDocumentsIteratorBuilder<'ctx> {
|
|||||||
// Create builders for the next level of the tree
|
// Create builders for the next level of the tree
|
||||||
let number_iter = number_iter.map(|r| r.map(|(d, _)| d));
|
let number_iter = number_iter.map(|r| r.map(|(d, _)| d));
|
||||||
let string_iter = string_iter.map(|r| r.map(|(d, _)| d));
|
let string_iter = string_iter.map(|r| r.map(|(d, _)| d));
|
||||||
let next_children = number_iter.chain(string_iter).map(move |r| {
|
// Chain faceted documents with non-faceted documents at the end
|
||||||
|
let next_children = number_iter
|
||||||
|
.chain(string_iter)
|
||||||
|
.map(move |r| {
|
||||||
Ok(SortedDocumentsIteratorBuilder {
|
Ok(SortedDocumentsIteratorBuilder {
|
||||||
index,
|
index,
|
||||||
rtxn,
|
rtxn,
|
||||||
@@ -266,7 +279,24 @@ impl<'ctx> SortedDocumentsIteratorBuilder<'ctx> {
|
|||||||
candidates: r?,
|
candidates: r?,
|
||||||
geo_candidates,
|
geo_candidates,
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
.chain(std::iter::from_fn(move || {
|
||||||
|
// Once all faceted candidates have been processed, return the non-faceted ones
|
||||||
|
if let Some(not_faceted) = not_faceted_candidates.take() {
|
||||||
|
if !not_faceted.is_empty() {
|
||||||
|
return Some(Ok(SortedDocumentsIteratorBuilder {
|
||||||
|
index,
|
||||||
|
rtxn,
|
||||||
|
number_db,
|
||||||
|
string_db,
|
||||||
|
fields: next_fields,
|
||||||
|
candidates: not_faceted,
|
||||||
|
geo_candidates,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}));
|
||||||
|
|
||||||
Ok(SortedDocumentsIterator::Branch {
|
Ok(SortedDocumentsIterator::Branch {
|
||||||
current_child: None,
|
current_child: None,
|
||||||
@@ -398,10 +428,14 @@ pub fn recursive_sort<'ctx>(
|
|||||||
};
|
};
|
||||||
if let Some((field, ascending)) = field {
|
if let Some((field, ascending)) = field {
|
||||||
if is_faceted(&field, &sortable_fields) {
|
if is_faceted(&field, &sortable_fields) {
|
||||||
|
// The field may be in sortable_fields but not in fields_ids_map if no document
|
||||||
|
// has ever contained this field. In that case, we just skip this sort criterion
|
||||||
|
// since there are no values to sort by. Documents will be returned in their
|
||||||
|
// default order for this field.
|
||||||
if let Some(field_id) = fields_ids_map.id(&field) {
|
if let Some(field_id) = fields_ids_map.id(&field) {
|
||||||
fields.push(AscDescId::Facet { field_id, ascending });
|
fields.push(AscDescId::Facet { field_id, ascending });
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
return Err(UserError::InvalidDocumentSortableAttribute {
|
return Err(UserError::InvalidDocumentSortableAttribute {
|
||||||
field: field.to_string(),
|
field: field.to_string(),
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ use crate::{
|
|||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
/// The weight as defined in the FieldidsWeightsMap of the searchable attribute if it is searchable.
|
/// The weight as defined in the FieldidsWeightsMap of the searchable attribute if it is searchable.
|
||||||
pub searchable: Option<Weight>,
|
pub searchable: Option<Weight>,
|
||||||
|
/// The field is part of the exact attributes.
|
||||||
|
pub exact: bool,
|
||||||
/// The field is part of the sortable attributes.
|
/// The field is part of the sortable attributes.
|
||||||
pub sortable: bool,
|
pub sortable: bool,
|
||||||
/// The field is defined as the distinct attribute.
|
/// The field is defined as the distinct attribute.
|
||||||
@@ -209,6 +211,7 @@ impl Metadata {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MetadataBuilder {
|
pub struct MetadataBuilder {
|
||||||
searchable_attributes: Option<Vec<String>>,
|
searchable_attributes: Option<Vec<String>>,
|
||||||
|
exact_searchable_attributes: Vec<String>,
|
||||||
filterable_attributes: Vec<FilterableAttributesRule>,
|
filterable_attributes: Vec<FilterableAttributesRule>,
|
||||||
sortable_attributes: HashSet<String>,
|
sortable_attributes: HashSet<String>,
|
||||||
localized_attributes: Option<Vec<LocalizedAttributesRule>>,
|
localized_attributes: Option<Vec<LocalizedAttributesRule>>,
|
||||||
@@ -220,15 +223,18 @@ impl MetadataBuilder {
|
|||||||
pub fn from_index(index: &Index, rtxn: &RoTxn) -> Result<Self> {
|
pub fn from_index(index: &Index, rtxn: &RoTxn) -> Result<Self> {
|
||||||
let searchable_attributes = index
|
let searchable_attributes = index
|
||||||
.user_defined_searchable_fields(rtxn)?
|
.user_defined_searchable_fields(rtxn)?
|
||||||
.map(|fields| fields.into_iter().map(|s| s.to_string()).collect());
|
.map(|fields| fields.into_iter().map(String::from).collect());
|
||||||
|
let exact_searchable_attributes =
|
||||||
|
index.exact_attributes(rtxn)?.into_iter().map(String::from).collect();
|
||||||
let filterable_attributes = index.filterable_attributes_rules(rtxn)?;
|
let filterable_attributes = index.filterable_attributes_rules(rtxn)?;
|
||||||
let sortable_attributes = index.sortable_fields(rtxn)?;
|
let sortable_attributes = index.sortable_fields(rtxn)?;
|
||||||
let localized_attributes = index.localized_attributes_rules(rtxn)?;
|
let localized_attributes = index.localized_attributes_rules(rtxn)?;
|
||||||
let distinct_attribute = index.distinct_field(rtxn)?.map(|s| s.to_string());
|
let distinct_attribute = index.distinct_field(rtxn)?.map(String::from);
|
||||||
let asc_desc_attributes = index.asc_desc_fields(rtxn)?;
|
let asc_desc_attributes = index.asc_desc_fields(rtxn)?;
|
||||||
|
|
||||||
Ok(Self::new(
|
Ok(Self::new(
|
||||||
searchable_attributes,
|
searchable_attributes,
|
||||||
|
exact_searchable_attributes,
|
||||||
filterable_attributes,
|
filterable_attributes,
|
||||||
sortable_attributes,
|
sortable_attributes,
|
||||||
localized_attributes,
|
localized_attributes,
|
||||||
@@ -242,6 +248,7 @@ impl MetadataBuilder {
|
|||||||
/// This is used for testing, prefer using `MetadataBuilder::from_index` instead.
|
/// This is used for testing, prefer using `MetadataBuilder::from_index` instead.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
searchable_attributes: Option<Vec<String>>,
|
searchable_attributes: Option<Vec<String>>,
|
||||||
|
exact_searchable_attributes: Vec<String>,
|
||||||
filterable_attributes: Vec<FilterableAttributesRule>,
|
filterable_attributes: Vec<FilterableAttributesRule>,
|
||||||
sortable_attributes: HashSet<String>,
|
sortable_attributes: HashSet<String>,
|
||||||
localized_attributes: Option<Vec<LocalizedAttributesRule>>,
|
localized_attributes: Option<Vec<LocalizedAttributesRule>>,
|
||||||
@@ -256,6 +263,7 @@ impl MetadataBuilder {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
searchable_attributes,
|
searchable_attributes,
|
||||||
|
exact_searchable_attributes,
|
||||||
filterable_attributes,
|
filterable_attributes,
|
||||||
sortable_attributes,
|
sortable_attributes,
|
||||||
localized_attributes,
|
localized_attributes,
|
||||||
@@ -269,6 +277,7 @@ impl MetadataBuilder {
|
|||||||
// Vectors fields are not searchable, filterable, distinct or asc_desc
|
// Vectors fields are not searchable, filterable, distinct or asc_desc
|
||||||
return Metadata {
|
return Metadata {
|
||||||
searchable: None,
|
searchable: None,
|
||||||
|
exact: false,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
distinct: false,
|
distinct: false,
|
||||||
asc_desc: false,
|
asc_desc: false,
|
||||||
@@ -296,6 +305,7 @@ impl MetadataBuilder {
|
|||||||
// Geo fields are not searchable, distinct or asc_desc
|
// Geo fields are not searchable, distinct or asc_desc
|
||||||
return Metadata {
|
return Metadata {
|
||||||
searchable: None,
|
searchable: None,
|
||||||
|
exact: false,
|
||||||
sortable,
|
sortable,
|
||||||
distinct: false,
|
distinct: false,
|
||||||
asc_desc: false,
|
asc_desc: false,
|
||||||
@@ -309,6 +319,7 @@ impl MetadataBuilder {
|
|||||||
debug_assert!(!sortable, "geojson fields should not be sortable");
|
debug_assert!(!sortable, "geojson fields should not be sortable");
|
||||||
return Metadata {
|
return Metadata {
|
||||||
searchable: None,
|
searchable: None,
|
||||||
|
exact: false,
|
||||||
sortable,
|
sortable,
|
||||||
distinct: false,
|
distinct: false,
|
||||||
asc_desc: false,
|
asc_desc: false,
|
||||||
@@ -329,6 +340,8 @@ impl MetadataBuilder {
|
|||||||
None => Some(0),
|
None => Some(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let exact = self.exact_searchable_attributes.iter().any(|attr| is_faceted_by(field, attr));
|
||||||
|
|
||||||
let distinct =
|
let distinct =
|
||||||
self.distinct_attribute.as_ref().is_some_and(|distinct_field| field == distinct_field);
|
self.distinct_attribute.as_ref().is_some_and(|distinct_field| field == distinct_field);
|
||||||
let asc_desc = self.asc_desc_attributes.contains(field);
|
let asc_desc = self.asc_desc_attributes.contains(field);
|
||||||
@@ -343,6 +356,7 @@ impl MetadataBuilder {
|
|||||||
|
|
||||||
Metadata {
|
Metadata {
|
||||||
searchable,
|
searchable,
|
||||||
|
exact,
|
||||||
sortable,
|
sortable,
|
||||||
distinct,
|
distinct,
|
||||||
asc_desc,
|
asc_desc,
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ pub mod main_key {
|
|||||||
pub const SEARCH_CUTOFF: &str = "search_cutoff";
|
pub const SEARCH_CUTOFF: &str = "search_cutoff";
|
||||||
pub const LOCALIZED_ATTRIBUTES_RULES: &str = "localized_attributes_rules";
|
pub const LOCALIZED_ATTRIBUTES_RULES: &str = "localized_attributes_rules";
|
||||||
pub const FACET_SEARCH: &str = "facet_search";
|
pub const FACET_SEARCH: &str = "facet_search";
|
||||||
pub const EXECUTE_AFTER_UPDATE: &str = "execute-after-update";
|
|
||||||
pub const PREFIX_SEARCH: &str = "prefix_search";
|
pub const PREFIX_SEARCH: &str = "prefix_search";
|
||||||
pub const DOCUMENTS_STATS: &str = "documents_stats";
|
pub const DOCUMENTS_STATS: &str = "documents_stats";
|
||||||
pub const DISABLED_TYPOS_TERMS: &str = "disabled_typos_terms";
|
pub const DISABLED_TYPOS_TERMS: &str = "disabled_typos_terms";
|
||||||
@@ -282,6 +281,9 @@ impl Index {
|
|||||||
&mut wtxn,
|
&mut wtxn,
|
||||||
(constants::VERSION_MAJOR, constants::VERSION_MINOR, constants::VERSION_PATCH),
|
(constants::VERSION_MAJOR, constants::VERSION_MINOR, constants::VERSION_PATCH),
|
||||||
)?;
|
)?;
|
||||||
|
// The database before v1.29 defaulted to using arroy, so we
|
||||||
|
// need to set it explicitly because the new default is hannoy.
|
||||||
|
this.put_vector_store(&mut wtxn, VectorStoreBackend::Hannoy)?;
|
||||||
}
|
}
|
||||||
wtxn.commit()?;
|
wtxn.commit()?;
|
||||||
|
|
||||||
@@ -1766,22 +1768,6 @@ impl Index {
|
|||||||
self.main.remap_key_type::<Str>().delete(txn, main_key::CHAT)
|
self.main.remap_key_type::<Str>().delete(txn, main_key::CHAT)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_after_update<'t>(&self, txn: &'t RoTxn<'_>) -> heed::Result<Option<&'t str>> {
|
|
||||||
self.main.remap_types::<Str, Str>().get(txn, main_key::EXECUTE_AFTER_UPDATE)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn put_execute_after_update(
|
|
||||||
&self,
|
|
||||||
txn: &mut RwTxn<'_>,
|
|
||||||
val: &str,
|
|
||||||
) -> heed::Result<()> {
|
|
||||||
self.main.remap_types::<Str, Str>().put(txn, main_key::EXECUTE_AFTER_UPDATE, &val)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn delete_execute_after_update(&self, txn: &mut RwTxn<'_>) -> heed::Result<bool> {
|
|
||||||
self.main.remap_key_type::<Str>().delete(txn, main_key::EXECUTE_AFTER_UPDATE)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn localized_attributes_rules(
|
pub fn localized_attributes_rules(
|
||||||
&self,
|
&self,
|
||||||
rtxn: &RoTxn<'_>,
|
rtxn: &RoTxn<'_>,
|
||||||
|
|||||||
@@ -806,6 +806,10 @@ mod tests {
|
|||||||
use crate::vector::db::IndexEmbeddingConfig;
|
use crate::vector::db::IndexEmbeddingConfig;
|
||||||
use crate::{all_obkv_to_json, db_snap, Filter, FilterableAttributesRule, Search, UserError};
|
use crate::{all_obkv_to_json, db_snap, Filter, FilterableAttributesRule, Search, UserError};
|
||||||
|
|
||||||
|
fn no_cancel() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_document_replacement() {
|
fn simple_document_replacement() {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -1985,7 +1989,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2038,7 +2042,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2057,7 +2061,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2127,7 +2131,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2146,7 +2150,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
RuntimeEmbedders::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2317,7 +2321,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2333,7 +2337,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2381,7 +2385,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2397,7 +2401,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2436,7 +2440,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2452,7 +2456,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2490,7 +2494,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2506,7 +2510,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2546,7 +2550,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2562,7 +2566,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2607,7 +2611,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2623,7 +2627,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2661,7 +2665,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2677,7 +2681,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2715,7 +2719,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2731,7 +2735,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2927,7 +2931,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -2943,7 +2947,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -2988,7 +2992,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -3004,7 +3008,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
@@ -3046,7 +3050,7 @@ mod tests {
|
|||||||
&rtxn,
|
&rtxn,
|
||||||
None,
|
None,
|
||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -3062,7 +3066,7 @@ mod tests {
|
|||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&no_cancel,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -470,71 +470,6 @@ impl<'doc> Versions<'doc> {
|
|||||||
Ok(Some(Self::single(data)))
|
Ok(Some(Self::single(data)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiple_with_edits(
|
|
||||||
doc: Option<rhai::Map>,
|
|
||||||
mut versions: impl Iterator<Item = Result<RawMap<'doc, FxBuildHasher>>>,
|
|
||||||
engine: &rhai::Engine,
|
|
||||||
edit_function: &rhai::AST,
|
|
||||||
doc_alloc: &'doc bumpalo::Bump,
|
|
||||||
) -> Result<Option<Option<Self>>> {
|
|
||||||
let Some(data) = versions.next() else { return Ok(None) };
|
|
||||||
|
|
||||||
let mut doc = doc.unwrap_or_default();
|
|
||||||
let mut data = data?;
|
|
||||||
for version in versions {
|
|
||||||
let version = version?;
|
|
||||||
for (field, value) in version {
|
|
||||||
data.insert(field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut scope = rhai::Scope::new();
|
|
||||||
data.iter().for_each(|(k, v)| {
|
|
||||||
doc.insert(k.into(), serde_json::from_str(v.get()).unwrap());
|
|
||||||
});
|
|
||||||
scope.push("doc", doc.clone());
|
|
||||||
|
|
||||||
let _ = engine.eval_ast_with_scope::<rhai::Dynamic>(&mut scope, edit_function).unwrap();
|
|
||||||
data = RawMap::with_hasher_in(FxBuildHasher, doc_alloc);
|
|
||||||
match scope.get_value::<rhai::Map>("doc") {
|
|
||||||
Some(map) => {
|
|
||||||
for (key, value) in map {
|
|
||||||
let mut vec = bumpalo::collections::Vec::new_in(doc_alloc);
|
|
||||||
serde_json::to_writer(&mut vec, &value).unwrap();
|
|
||||||
let key = doc_alloc.alloc_str(key.as_str());
|
|
||||||
let raw_value = serde_json::from_slice(vec.into_bump_slice()).unwrap();
|
|
||||||
data.insert(key, raw_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// In case the deletes the document and it's not the last change
|
|
||||||
// we simply set the document to an empty one and await the next change.
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We must also run the code after the last change
|
|
||||||
let mut scope = rhai::Scope::new();
|
|
||||||
data.iter().for_each(|(k, v)| {
|
|
||||||
doc.insert(k.into(), serde_json::from_str(v.get()).unwrap());
|
|
||||||
});
|
|
||||||
scope.push("doc", doc);
|
|
||||||
|
|
||||||
let _ = engine.eval_ast_with_scope::<rhai::Dynamic>(&mut scope, edit_function).unwrap();
|
|
||||||
data = RawMap::with_hasher_in(FxBuildHasher, doc_alloc);
|
|
||||||
match scope.get_value::<rhai::Map>("doc") {
|
|
||||||
Some(map) => {
|
|
||||||
for (key, value) in map {
|
|
||||||
let mut vec = bumpalo::collections::Vec::new_in(doc_alloc);
|
|
||||||
serde_json::to_writer(&mut vec, &value).unwrap();
|
|
||||||
let key = doc_alloc.alloc_str(key.as_str());
|
|
||||||
let raw_value = serde_json::from_slice(vec.into_bump_slice()).unwrap();
|
|
||||||
data.insert(key, raw_value);
|
|
||||||
}
|
|
||||||
Ok(Some(Some(Self::single(data))))
|
|
||||||
}
|
|
||||||
None => Ok(Some(None)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn single(version: RawMap<'doc, FxBuildHasher>) -> Self {
|
pub fn single(version: RawMap<'doc, FxBuildHasher>) -> Self {
|
||||||
Self { data: version }
|
Self { data: version }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,26 @@ use bumpalo::Bump;
|
|||||||
|
|
||||||
use super::match_searchable_field;
|
use super::match_searchable_field;
|
||||||
use super::tokenize_document::{tokenizer_builder, DocumentTokenizer};
|
use super::tokenize_document::{tokenizer_builder, DocumentTokenizer};
|
||||||
|
use crate::fields_ids_map::metadata::Metadata;
|
||||||
use crate::update::new::document::DocumentContext;
|
use crate::update::new::document::DocumentContext;
|
||||||
use crate::update::new::extract::cache::BalancedCaches;
|
use crate::update::new::extract::cache::BalancedCaches;
|
||||||
use crate::update::new::extract::perm_json_p::contained_in;
|
use crate::update::new::extract::perm_json_p::contained_in;
|
||||||
|
use crate::update::new::extract::searchable::has_searchable_children;
|
||||||
use crate::update::new::indexer::document_changes::{
|
use crate::update::new::indexer::document_changes::{
|
||||||
extract, DocumentChanges, Extractor, IndexingContext,
|
extract, DocumentChanges, Extractor, IndexingContext,
|
||||||
};
|
};
|
||||||
|
use crate::update::new::indexer::settings_changes::{
|
||||||
|
settings_change_extract, DocumentsIndentifiers, SettingsChangeExtractor,
|
||||||
|
};
|
||||||
use crate::update::new::ref_cell_ext::RefCellExt as _;
|
use crate::update::new::ref_cell_ext::RefCellExt as _;
|
||||||
use crate::update::new::steps::IndexingStep;
|
use crate::update::new::steps::IndexingStep;
|
||||||
use crate::update::new::thread_local::{FullySend, MostlySend, ThreadLocal};
|
use crate::update::new::thread_local::{FullySend, MostlySend, ThreadLocal};
|
||||||
use crate::update::new::DocumentChange;
|
use crate::update::new::{DocumentChange, DocumentIdentifiers};
|
||||||
use crate::{bucketed_position, DocumentId, FieldId, Result, MAX_POSITION_PER_ATTRIBUTE};
|
use crate::update::settings::SettingsDelta;
|
||||||
|
use crate::{
|
||||||
|
bucketed_position, DocumentId, FieldId, PatternMatch, Result, UserError,
|
||||||
|
MAX_POSITION_PER_ATTRIBUTE,
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_COUNTED_WORDS: usize = 30;
|
const MAX_COUNTED_WORDS: usize = 30;
|
||||||
|
|
||||||
@@ -34,6 +43,15 @@ pub struct WordDocidsBalancedCaches<'extractor> {
|
|||||||
|
|
||||||
unsafe impl MostlySend for WordDocidsBalancedCaches<'_> {}
|
unsafe impl MostlySend for WordDocidsBalancedCaches<'_> {}
|
||||||
|
|
||||||
|
/// Whether to extract or skip fields during word extraction.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum FieldDbExtraction {
|
||||||
|
/// Extract the word and put it in to the fid-based databases.
|
||||||
|
Extract,
|
||||||
|
/// Do not store the word in the fid-based databases.
|
||||||
|
Skip,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
||||||
pub fn new_in(buckets: usize, max_memory: Option<usize>, alloc: &'extractor Bump) -> Self {
|
pub fn new_in(buckets: usize, max_memory: Option<usize>, alloc: &'extractor Bump) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -47,12 +65,14 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn insert_add_u32(
|
fn insert_add_u32(
|
||||||
&mut self,
|
&mut self,
|
||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
position: u16,
|
position: u16,
|
||||||
word: &str,
|
word: &str,
|
||||||
exact: bool,
|
exact: bool,
|
||||||
|
field_db_extraction: FieldDbExtraction,
|
||||||
docid: u32,
|
docid: u32,
|
||||||
bump: &Bump,
|
bump: &Bump,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -66,11 +86,13 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
|||||||
let buffer_size = word_bytes.len() + 1 + size_of::<FieldId>();
|
let buffer_size = word_bytes.len() + 1 + size_of::<FieldId>();
|
||||||
let mut buffer = BumpVec::with_capacity_in(buffer_size, bump);
|
let mut buffer = BumpVec::with_capacity_in(buffer_size, bump);
|
||||||
|
|
||||||
|
if field_db_extraction == FieldDbExtraction::Extract {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
buffer.extend_from_slice(word_bytes);
|
buffer.extend_from_slice(word_bytes);
|
||||||
buffer.push(0);
|
buffer.push(0);
|
||||||
buffer.extend_from_slice(&field_id.to_be_bytes());
|
buffer.extend_from_slice(&field_id.to_be_bytes());
|
||||||
self.word_fid_docids.insert_add_u32(&buffer, docid)?;
|
self.word_fid_docids.insert_add_u32(&buffer, docid)?;
|
||||||
|
}
|
||||||
|
|
||||||
let position = bucketed_position(position);
|
let position = bucketed_position(position);
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
@@ -83,21 +105,26 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
|||||||
self.flush_fid_word_count(&mut buffer)?;
|
self.flush_fid_word_count(&mut buffer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if field_db_extraction == FieldDbExtraction::Extract {
|
||||||
self.fid_word_count
|
self.fid_word_count
|
||||||
.entry(field_id)
|
.entry(field_id)
|
||||||
.and_modify(|(_current_count, new_count)| *new_count.get_or_insert(0) += 1)
|
.and_modify(|(_current_count, new_count)| *new_count.get_or_insert(0) += 1)
|
||||||
.or_insert((None, Some(1)));
|
.or_insert((None, Some(1)));
|
||||||
|
}
|
||||||
|
|
||||||
self.current_docid = Some(docid);
|
self.current_docid = Some(docid);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn insert_del_u32(
|
fn insert_del_u32(
|
||||||
&mut self,
|
&mut self,
|
||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
position: u16,
|
position: u16,
|
||||||
word: &str,
|
word: &str,
|
||||||
exact: bool,
|
exact: bool,
|
||||||
|
field_db_extraction: FieldDbExtraction,
|
||||||
docid: u32,
|
docid: u32,
|
||||||
bump: &Bump,
|
bump: &Bump,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -111,11 +138,13 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
|||||||
let buffer_size = word_bytes.len() + 1 + size_of::<FieldId>();
|
let buffer_size = word_bytes.len() + 1 + size_of::<FieldId>();
|
||||||
let mut buffer = BumpVec::with_capacity_in(buffer_size, bump);
|
let mut buffer = BumpVec::with_capacity_in(buffer_size, bump);
|
||||||
|
|
||||||
|
if field_db_extraction == FieldDbExtraction::Extract {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
buffer.extend_from_slice(word_bytes);
|
buffer.extend_from_slice(word_bytes);
|
||||||
buffer.push(0);
|
buffer.push(0);
|
||||||
buffer.extend_from_slice(&field_id.to_be_bytes());
|
buffer.extend_from_slice(&field_id.to_be_bytes());
|
||||||
self.word_fid_docids.insert_del_u32(&buffer, docid)?;
|
self.word_fid_docids.insert_del_u32(&buffer, docid)?;
|
||||||
|
}
|
||||||
|
|
||||||
let position = bucketed_position(position);
|
let position = bucketed_position(position);
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
@@ -128,10 +157,12 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
|
|||||||
self.flush_fid_word_count(&mut buffer)?;
|
self.flush_fid_word_count(&mut buffer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if field_db_extraction == FieldDbExtraction::Extract {
|
||||||
self.fid_word_count
|
self.fid_word_count
|
||||||
.entry(field_id)
|
.entry(field_id)
|
||||||
.and_modify(|(current_count, _new_count)| *current_count.get_or_insert(0) += 1)
|
.and_modify(|(current_count, _new_count)| *current_count.get_or_insert(0) += 1)
|
||||||
.or_insert((Some(1), None));
|
.or_insert((Some(1), None));
|
||||||
|
}
|
||||||
|
|
||||||
self.current_docid = Some(docid);
|
self.current_docid = Some(docid);
|
||||||
|
|
||||||
@@ -325,6 +356,24 @@ impl WordDocidsExtractors {
|
|||||||
exact_attributes.iter().any(|attr| contained_in(fname, attr))
|
exact_attributes.iter().any(|attr| contained_in(fname, attr))
|
||||||
|| disabled_typos_terms.is_exact(word)
|
|| disabled_typos_terms.is_exact(word)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut should_tokenize = |field_name: &str| {
|
||||||
|
let Some((field_id, meta)) = new_fields_ids_map.id_with_metadata_or_insert(field_name)
|
||||||
|
else {
|
||||||
|
return Err(UserError::AttributeLimitReached.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
let pattern_match = if meta.is_searchable() {
|
||||||
|
PatternMatch::Match
|
||||||
|
} else {
|
||||||
|
// TODO: should be a match on the field_name using `match_field_legacy` function,
|
||||||
|
// but for legacy reasons we iterate over all the fields to fill the field_id_map.
|
||||||
|
PatternMatch::Parent
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((field_id, pattern_match))
|
||||||
|
};
|
||||||
|
|
||||||
match document_change {
|
match document_change {
|
||||||
DocumentChange::Deletion(inner) => {
|
DocumentChange::Deletion(inner) => {
|
||||||
let mut token_fn = |fname: &str, fid, pos, word: &str| {
|
let mut token_fn = |fname: &str, fid, pos, word: &str| {
|
||||||
@@ -333,13 +382,14 @@ impl WordDocidsExtractors {
|
|||||||
pos,
|
pos,
|
||||||
word,
|
word,
|
||||||
is_exact(fname, word),
|
is_exact(fname, word),
|
||||||
|
FieldDbExtraction::Extract,
|
||||||
inner.docid(),
|
inner.docid(),
|
||||||
doc_alloc,
|
doc_alloc,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
document_tokenizer.tokenize_document(
|
document_tokenizer.tokenize_document(
|
||||||
inner.current(rtxn, index, context.db_fields_ids_map)?,
|
inner.current(rtxn, index, context.db_fields_ids_map)?,
|
||||||
new_fields_ids_map,
|
&mut should_tokenize,
|
||||||
&mut token_fn,
|
&mut token_fn,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -361,13 +411,14 @@ impl WordDocidsExtractors {
|
|||||||
pos,
|
pos,
|
||||||
word,
|
word,
|
||||||
is_exact(fname, word),
|
is_exact(fname, word),
|
||||||
|
FieldDbExtraction::Extract,
|
||||||
inner.docid(),
|
inner.docid(),
|
||||||
doc_alloc,
|
doc_alloc,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
document_tokenizer.tokenize_document(
|
document_tokenizer.tokenize_document(
|
||||||
inner.current(rtxn, index, context.db_fields_ids_map)?,
|
inner.current(rtxn, index, context.db_fields_ids_map)?,
|
||||||
new_fields_ids_map,
|
&mut should_tokenize,
|
||||||
&mut token_fn,
|
&mut token_fn,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -377,13 +428,14 @@ impl WordDocidsExtractors {
|
|||||||
pos,
|
pos,
|
||||||
word,
|
word,
|
||||||
is_exact(fname, word),
|
is_exact(fname, word),
|
||||||
|
FieldDbExtraction::Extract,
|
||||||
inner.docid(),
|
inner.docid(),
|
||||||
doc_alloc,
|
doc_alloc,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
document_tokenizer.tokenize_document(
|
document_tokenizer.tokenize_document(
|
||||||
inner.merged(rtxn, index, context.db_fields_ids_map)?,
|
inner.merged(rtxn, index, context.db_fields_ids_map)?,
|
||||||
new_fields_ids_map,
|
&mut should_tokenize,
|
||||||
&mut token_fn,
|
&mut token_fn,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -394,13 +446,14 @@ impl WordDocidsExtractors {
|
|||||||
pos,
|
pos,
|
||||||
word,
|
word,
|
||||||
is_exact(fname, word),
|
is_exact(fname, word),
|
||||||
|
FieldDbExtraction::Extract,
|
||||||
inner.docid(),
|
inner.docid(),
|
||||||
doc_alloc,
|
doc_alloc,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
document_tokenizer.tokenize_document(
|
document_tokenizer.tokenize_document(
|
||||||
inner.inserted(),
|
inner.inserted(),
|
||||||
new_fields_ids_map,
|
&mut should_tokenize,
|
||||||
&mut token_fn,
|
&mut token_fn,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -411,3 +464,292 @@ impl WordDocidsExtractors {
|
|||||||
cached_sorter.flush_fid_word_count(&mut buffer)
|
cached_sorter.flush_fid_word_count(&mut buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WordDocidsSettingsExtractorsData<'a, SD> {
|
||||||
|
tokenizer: DocumentTokenizer<'a>,
|
||||||
|
max_memory_by_thread: Option<usize>,
|
||||||
|
buckets: usize,
|
||||||
|
settings_delta: &'a SD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'extractor, SD: SettingsDelta + Sync> SettingsChangeExtractor<'extractor>
|
||||||
|
for WordDocidsSettingsExtractorsData<'_, SD>
|
||||||
|
{
|
||||||
|
type Data = RefCell<Option<WordDocidsBalancedCaches<'extractor>>>;
|
||||||
|
|
||||||
|
fn init_data<'doc>(&'doc self, extractor_alloc: &'extractor Bump) -> crate::Result<Self::Data> {
|
||||||
|
Ok(RefCell::new(Some(WordDocidsBalancedCaches::new_in(
|
||||||
|
self.buckets,
|
||||||
|
self.max_memory_by_thread,
|
||||||
|
extractor_alloc,
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process<'doc>(
|
||||||
|
&'doc self,
|
||||||
|
documents: impl Iterator<Item = crate::Result<DocumentIdentifiers<'doc>>>,
|
||||||
|
context: &'doc DocumentContext<Self::Data>,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
for document in documents {
|
||||||
|
let document = document?;
|
||||||
|
SettingsChangeWordDocidsExtractors::extract_document_from_settings_change(
|
||||||
|
document,
|
||||||
|
context,
|
||||||
|
&self.tokenizer,
|
||||||
|
self.settings_delta,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SettingsChangeWordDocidsExtractors;
|
||||||
|
|
||||||
|
impl SettingsChangeWordDocidsExtractors {
|
||||||
|
pub fn run_extraction<'fid, 'indexer, 'index, 'extractor, SD, MSP>(
|
||||||
|
settings_delta: &SD,
|
||||||
|
documents: &'indexer DocumentsIndentifiers<'indexer>,
|
||||||
|
indexing_context: IndexingContext<'fid, 'indexer, 'index, MSP>,
|
||||||
|
extractor_allocs: &'extractor mut ThreadLocal<FullySend<Bump>>,
|
||||||
|
step: IndexingStep,
|
||||||
|
) -> Result<WordDocidsCaches<'extractor>>
|
||||||
|
where
|
||||||
|
SD: SettingsDelta + Sync,
|
||||||
|
MSP: Fn() -> bool + Sync,
|
||||||
|
{
|
||||||
|
// Warning: this is duplicated code from extract_word_pair_proximity_docids.rs
|
||||||
|
// TODO we need to read the new AND old settings to support changing global parameters
|
||||||
|
let rtxn = indexing_context.index.read_txn()?;
|
||||||
|
let stop_words = indexing_context.index.stop_words(&rtxn)?;
|
||||||
|
let allowed_separators = indexing_context.index.allowed_separators(&rtxn)?;
|
||||||
|
let allowed_separators: Option<Vec<_>> =
|
||||||
|
allowed_separators.as_ref().map(|s| s.iter().map(String::as_str).collect());
|
||||||
|
let dictionary = indexing_context.index.dictionary(&rtxn)?;
|
||||||
|
let dictionary: Option<Vec<_>> =
|
||||||
|
dictionary.as_ref().map(|s| s.iter().map(String::as_str).collect());
|
||||||
|
let mut builder = tokenizer_builder(
|
||||||
|
stop_words.as_ref(),
|
||||||
|
allowed_separators.as_deref(),
|
||||||
|
dictionary.as_deref(),
|
||||||
|
);
|
||||||
|
let tokenizer = builder.build();
|
||||||
|
let localized_attributes_rules =
|
||||||
|
indexing_context.index.localized_attributes_rules(&rtxn)?.unwrap_or_default();
|
||||||
|
let document_tokenizer = DocumentTokenizer {
|
||||||
|
tokenizer: &tokenizer,
|
||||||
|
localized_attributes_rules: &localized_attributes_rules,
|
||||||
|
max_positions_per_attributes: MAX_POSITION_PER_ATTRIBUTE,
|
||||||
|
};
|
||||||
|
let extractor_data = WordDocidsSettingsExtractorsData {
|
||||||
|
tokenizer: document_tokenizer,
|
||||||
|
max_memory_by_thread: indexing_context.grenad_parameters.max_memory_by_thread(),
|
||||||
|
buckets: rayon::current_num_threads(),
|
||||||
|
settings_delta,
|
||||||
|
};
|
||||||
|
let datastore = ThreadLocal::new();
|
||||||
|
{
|
||||||
|
let span = tracing::debug_span!(target: "indexing::documents::extract", "vectors");
|
||||||
|
let _entered = span.enter();
|
||||||
|
|
||||||
|
settings_change_extract(
|
||||||
|
documents,
|
||||||
|
&extractor_data,
|
||||||
|
indexing_context,
|
||||||
|
extractor_allocs,
|
||||||
|
&datastore,
|
||||||
|
step,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut merger = WordDocidsCaches::new();
|
||||||
|
for cache in datastore.into_iter().flat_map(RefCell::into_inner) {
|
||||||
|
merger.push(cache)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(merger)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts document words from a settings change.
|
||||||
|
fn extract_document_from_settings_change<SD: SettingsDelta>(
|
||||||
|
document: DocumentIdentifiers<'_>,
|
||||||
|
context: &DocumentContext<RefCell<Option<WordDocidsBalancedCaches>>>,
|
||||||
|
document_tokenizer: &DocumentTokenizer,
|
||||||
|
settings_delta: &SD,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut cached_sorter_ref = context.data.borrow_mut_or_yield();
|
||||||
|
let cached_sorter = cached_sorter_ref.as_mut().unwrap();
|
||||||
|
let doc_alloc = &context.doc_alloc;
|
||||||
|
|
||||||
|
let new_fields_ids_map = settings_delta.new_fields_ids_map();
|
||||||
|
let old_fields_ids_map = context.index.fields_ids_map_with_metadata(&context.rtxn)?;
|
||||||
|
let old_searchable = settings_delta.old_searchable_attributes().as_ref();
|
||||||
|
let new_searchable = settings_delta.new_searchable_attributes().as_ref();
|
||||||
|
|
||||||
|
let current_document = document.current(
|
||||||
|
&context.rtxn,
|
||||||
|
context.index,
|
||||||
|
old_fields_ids_map.as_fields_ids_map(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum ActionToOperate {
|
||||||
|
ReindexAllFields,
|
||||||
|
// TODO improve by listing field prefixes
|
||||||
|
IndexAddedFields,
|
||||||
|
SkipDocument,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut action = ActionToOperate::SkipDocument;
|
||||||
|
// Here we do a preliminary check to determine the action to take.
|
||||||
|
// This check doesn't trigger the tokenizer as we never return
|
||||||
|
// PatternMatch::Match.
|
||||||
|
document_tokenizer.tokenize_document(
|
||||||
|
current_document,
|
||||||
|
&mut |field_name| {
|
||||||
|
let fid = new_fields_ids_map.id(field_name).expect("All fields IDs must exist");
|
||||||
|
|
||||||
|
// If the document must be reindexed, early return NoMatch to stop the scanning process.
|
||||||
|
if action == ActionToOperate::ReindexAllFields {
|
||||||
|
return Ok((fid, PatternMatch::NoMatch));
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_field_metadata = old_fields_ids_map.metadata(fid).unwrap();
|
||||||
|
let new_field_metadata = new_fields_ids_map.metadata(fid).unwrap();
|
||||||
|
|
||||||
|
action = match (old_field_metadata, new_field_metadata) {
|
||||||
|
// At least one field is added or removed from the exact fields => ReindexAllFields
|
||||||
|
(Metadata { exact: old_exact, .. }, Metadata { exact: new_exact, .. })
|
||||||
|
if old_exact != new_exact =>
|
||||||
|
{
|
||||||
|
ActionToOperate::ReindexAllFields
|
||||||
|
}
|
||||||
|
// At least one field is removed from the searchable fields => ReindexAllFields
|
||||||
|
(Metadata { searchable: Some(_), .. }, Metadata { searchable: None, .. }) => {
|
||||||
|
ActionToOperate::ReindexAllFields
|
||||||
|
}
|
||||||
|
// At least one field is added in the searchable fields => IndexAddedFields
|
||||||
|
(Metadata { searchable: None, .. }, Metadata { searchable: Some(_), .. }) => {
|
||||||
|
// We can safely overwrite the action, because we early return when action is ReindexAllFields.
|
||||||
|
ActionToOperate::IndexAddedFields
|
||||||
|
}
|
||||||
|
_ => action,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((fid, PatternMatch::Parent))
|
||||||
|
},
|
||||||
|
&mut |_, _, _, _| Ok(()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Early return when we don't need to index the document
|
||||||
|
if action == ActionToOperate::SkipDocument {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut should_tokenize = |field_name: &str| {
|
||||||
|
let field_id = new_fields_ids_map.id(field_name).expect("All fields IDs must exist");
|
||||||
|
let old_field_metadata = old_fields_ids_map.metadata(field_id).unwrap();
|
||||||
|
let new_field_metadata = new_fields_ids_map.metadata(field_id).unwrap();
|
||||||
|
|
||||||
|
let pattern_match = match action {
|
||||||
|
ActionToOperate::ReindexAllFields => {
|
||||||
|
if old_field_metadata.is_searchable() || new_field_metadata.is_searchable() {
|
||||||
|
PatternMatch::Match
|
||||||
|
// If any old or new field is searchable then we need to iterate over all fields
|
||||||
|
// else if any field matches we need to iterate over all fields
|
||||||
|
} else if has_searchable_children(
|
||||||
|
field_name,
|
||||||
|
old_searchable.zip(new_searchable).map(|(old, new)| old.iter().chain(new)),
|
||||||
|
) {
|
||||||
|
PatternMatch::Parent
|
||||||
|
} else {
|
||||||
|
PatternMatch::NoMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionToOperate::IndexAddedFields => {
|
||||||
|
// Was not searchable but now is
|
||||||
|
if !old_field_metadata.is_searchable() && new_field_metadata.is_searchable() {
|
||||||
|
PatternMatch::Match
|
||||||
|
// If the field is now a parent of a searchable field
|
||||||
|
} else if has_searchable_children(field_name, new_searchable) {
|
||||||
|
PatternMatch::Parent
|
||||||
|
} else {
|
||||||
|
PatternMatch::NoMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionToOperate::SkipDocument => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((field_id, pattern_match))
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_disabled_typos_terms = settings_delta.old_disabled_typos_terms();
|
||||||
|
let new_disabled_typos_terms = settings_delta.new_disabled_typos_terms();
|
||||||
|
let mut token_fn = |_field_name: &str, field_id, pos, word: &str| {
|
||||||
|
let old_field_metadata = old_fields_ids_map.metadata(field_id).unwrap();
|
||||||
|
let new_field_metadata = new_fields_ids_map.metadata(field_id).unwrap();
|
||||||
|
|
||||||
|
match (old_field_metadata, new_field_metadata) {
|
||||||
|
(
|
||||||
|
Metadata { searchable: Some(_), exact: old_exact, .. },
|
||||||
|
Metadata { searchable: None, .. },
|
||||||
|
) => cached_sorter.insert_del_u32(
|
||||||
|
field_id,
|
||||||
|
pos,
|
||||||
|
word,
|
||||||
|
old_exact || old_disabled_typos_terms.is_exact(word),
|
||||||
|
// We deleted the field globally
|
||||||
|
FieldDbExtraction::Skip,
|
||||||
|
document.docid(),
|
||||||
|
doc_alloc,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Metadata { searchable: None, .. },
|
||||||
|
Metadata { searchable: Some(_), exact: new_exact, .. },
|
||||||
|
) => cached_sorter.insert_add_u32(
|
||||||
|
field_id,
|
||||||
|
pos,
|
||||||
|
word,
|
||||||
|
new_exact || new_disabled_typos_terms.is_exact(word),
|
||||||
|
FieldDbExtraction::Extract,
|
||||||
|
document.docid(),
|
||||||
|
doc_alloc,
|
||||||
|
),
|
||||||
|
(Metadata { searchable: None, .. }, Metadata { searchable: None, .. }) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
(Metadata { exact: old_exact, .. }, Metadata { exact: new_exact, .. }) => {
|
||||||
|
cached_sorter.insert_del_u32(
|
||||||
|
field_id,
|
||||||
|
pos,
|
||||||
|
word,
|
||||||
|
old_exact || old_disabled_typos_terms.is_exact(word),
|
||||||
|
// The field has already been extracted
|
||||||
|
FieldDbExtraction::Skip,
|
||||||
|
document.docid(),
|
||||||
|
doc_alloc,
|
||||||
|
)?;
|
||||||
|
cached_sorter.insert_add_u32(
|
||||||
|
field_id,
|
||||||
|
pos,
|
||||||
|
word,
|
||||||
|
new_exact || new_disabled_typos_terms.is_exact(word),
|
||||||
|
// The field has already been extracted
|
||||||
|
FieldDbExtraction::Skip,
|
||||||
|
document.docid(),
|
||||||
|
doc_alloc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO we must tokenize twice when we change global parameters like stop words,
|
||||||
|
// the language settings, dictionary, separators, non-separators...
|
||||||
|
document_tokenizer.tokenize_document(
|
||||||
|
current_document,
|
||||||
|
&mut should_tokenize,
|
||||||
|
&mut token_fn,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,17 +6,24 @@ use bumpalo::Bump;
|
|||||||
|
|
||||||
use super::match_searchable_field;
|
use super::match_searchable_field;
|
||||||
use super::tokenize_document::{tokenizer_builder, DocumentTokenizer};
|
use super::tokenize_document::{tokenizer_builder, DocumentTokenizer};
|
||||||
|
use crate::fields_ids_map::metadata::Metadata;
|
||||||
|
use crate::proximity::ProximityPrecision::*;
|
||||||
use crate::proximity::{index_proximity, MAX_DISTANCE};
|
use crate::proximity::{index_proximity, MAX_DISTANCE};
|
||||||
use crate::update::new::document::{Document, DocumentContext};
|
use crate::update::new::document::{Document, DocumentContext};
|
||||||
use crate::update::new::extract::cache::BalancedCaches;
|
use crate::update::new::extract::cache::BalancedCaches;
|
||||||
use crate::update::new::indexer::document_changes::{
|
use crate::update::new::indexer::document_changes::{
|
||||||
extract, DocumentChanges, Extractor, IndexingContext,
|
extract, DocumentChanges, Extractor, IndexingContext,
|
||||||
};
|
};
|
||||||
|
use crate::update::new::indexer::settings_change_extract;
|
||||||
|
use crate::update::new::indexer::settings_changes::{
|
||||||
|
DocumentsIndentifiers, SettingsChangeExtractor,
|
||||||
|
};
|
||||||
use crate::update::new::ref_cell_ext::RefCellExt as _;
|
use crate::update::new::ref_cell_ext::RefCellExt as _;
|
||||||
use crate::update::new::steps::IndexingStep;
|
use crate::update::new::steps::IndexingStep;
|
||||||
use crate::update::new::thread_local::{FullySend, ThreadLocal};
|
use crate::update::new::thread_local::{FullySend, ThreadLocal};
|
||||||
use crate::update::new::DocumentChange;
|
use crate::update::new::{DocumentChange, DocumentIdentifiers};
|
||||||
use crate::{FieldId, GlobalFieldsIdsMap, Result, MAX_POSITION_PER_ATTRIBUTE};
|
use crate::update::settings::SettingsDelta;
|
||||||
|
use crate::{FieldId, PatternMatch, Result, UserError, MAX_POSITION_PER_ATTRIBUTE};
|
||||||
|
|
||||||
pub struct WordPairProximityDocidsExtractorData<'a> {
|
pub struct WordPairProximityDocidsExtractorData<'a> {
|
||||||
tokenizer: DocumentTokenizer<'a>,
|
tokenizer: DocumentTokenizer<'a>,
|
||||||
@@ -116,7 +123,7 @@ impl WordPairProximityDocidsExtractor {
|
|||||||
// and to store the docids of the documents that have a number of words in a given field
|
// and to store the docids of the documents that have a number of words in a given field
|
||||||
// equal to or under than MAX_COUNTED_WORDS.
|
// equal to or under than MAX_COUNTED_WORDS.
|
||||||
fn extract_document_change(
|
fn extract_document_change(
|
||||||
context: &DocumentContext<RefCell<BalancedCaches>>,
|
context: &DocumentContext<RefCell<BalancedCaches<'_>>>,
|
||||||
document_tokenizer: &DocumentTokenizer,
|
document_tokenizer: &DocumentTokenizer,
|
||||||
searchable_attributes: Option<&[&str]>,
|
searchable_attributes: Option<&[&str]>,
|
||||||
document_change: DocumentChange,
|
document_change: DocumentChange,
|
||||||
@@ -147,8 +154,12 @@ impl WordPairProximityDocidsExtractor {
|
|||||||
process_document_tokens(
|
process_document_tokens(
|
||||||
document,
|
document,
|
||||||
document_tokenizer,
|
document_tokenizer,
|
||||||
new_fields_ids_map,
|
|
||||||
&mut word_positions,
|
&mut word_positions,
|
||||||
|
&mut |field_name| {
|
||||||
|
new_fields_ids_map
|
||||||
|
.id_with_metadata_or_insert(field_name)
|
||||||
|
.ok_or(UserError::AttributeLimitReached.into())
|
||||||
|
},
|
||||||
&mut |(w1, w2), prox| {
|
&mut |(w1, w2), prox| {
|
||||||
del_word_pair_proximity.push(((w1, w2), prox));
|
del_word_pair_proximity.push(((w1, w2), prox));
|
||||||
},
|
},
|
||||||
@@ -170,8 +181,12 @@ impl WordPairProximityDocidsExtractor {
|
|||||||
process_document_tokens(
|
process_document_tokens(
|
||||||
document,
|
document,
|
||||||
document_tokenizer,
|
document_tokenizer,
|
||||||
new_fields_ids_map,
|
|
||||||
&mut word_positions,
|
&mut word_positions,
|
||||||
|
&mut |field_name| {
|
||||||
|
new_fields_ids_map
|
||||||
|
.id_with_metadata_or_insert(field_name)
|
||||||
|
.ok_or(UserError::AttributeLimitReached.into())
|
||||||
|
},
|
||||||
&mut |(w1, w2), prox| {
|
&mut |(w1, w2), prox| {
|
||||||
del_word_pair_proximity.push(((w1, w2), prox));
|
del_word_pair_proximity.push(((w1, w2), prox));
|
||||||
},
|
},
|
||||||
@@ -180,8 +195,12 @@ impl WordPairProximityDocidsExtractor {
|
|||||||
process_document_tokens(
|
process_document_tokens(
|
||||||
document,
|
document,
|
||||||
document_tokenizer,
|
document_tokenizer,
|
||||||
new_fields_ids_map,
|
|
||||||
&mut word_positions,
|
&mut word_positions,
|
||||||
|
&mut |field_name| {
|
||||||
|
new_fields_ids_map
|
||||||
|
.id_with_metadata_or_insert(field_name)
|
||||||
|
.ok_or(UserError::AttributeLimitReached.into())
|
||||||
|
},
|
||||||
&mut |(w1, w2), prox| {
|
&mut |(w1, w2), prox| {
|
||||||
add_word_pair_proximity.push(((w1, w2), prox));
|
add_word_pair_proximity.push(((w1, w2), prox));
|
||||||
},
|
},
|
||||||
@@ -192,8 +211,12 @@ impl WordPairProximityDocidsExtractor {
|
|||||||
process_document_tokens(
|
process_document_tokens(
|
||||||
document,
|
document,
|
||||||
document_tokenizer,
|
document_tokenizer,
|
||||||
new_fields_ids_map,
|
|
||||||
&mut word_positions,
|
&mut word_positions,
|
||||||
|
&mut |field_name| {
|
||||||
|
new_fields_ids_map
|
||||||
|
.id_with_metadata_or_insert(field_name)
|
||||||
|
.ok_or(UserError::AttributeLimitReached.into())
|
||||||
|
},
|
||||||
&mut |(w1, w2), prox| {
|
&mut |(w1, w2), prox| {
|
||||||
add_word_pair_proximity.push(((w1, w2), prox));
|
add_word_pair_proximity.push(((w1, w2), prox));
|
||||||
},
|
},
|
||||||
@@ -257,8 +280,8 @@ fn drain_word_positions(
|
|||||||
fn process_document_tokens<'doc>(
|
fn process_document_tokens<'doc>(
|
||||||
document: impl Document<'doc>,
|
document: impl Document<'doc>,
|
||||||
document_tokenizer: &DocumentTokenizer,
|
document_tokenizer: &DocumentTokenizer,
|
||||||
fields_ids_map: &mut GlobalFieldsIdsMap,
|
|
||||||
word_positions: &mut VecDeque<(Rc<str>, u16)>,
|
word_positions: &mut VecDeque<(Rc<str>, u16)>,
|
||||||
|
field_id_and_metadata: &mut impl FnMut(&str) -> Result<(FieldId, Metadata)>,
|
||||||
word_pair_proximity: &mut impl FnMut((Rc<str>, Rc<str>), u8),
|
word_pair_proximity: &mut impl FnMut((Rc<str>, Rc<str>), u8),
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut field_id = None;
|
let mut field_id = None;
|
||||||
@@ -279,8 +302,248 @@ fn process_document_tokens<'doc>(
|
|||||||
word_positions.push_back((Rc::from(word), pos));
|
word_positions.push_back((Rc::from(word), pos));
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
document_tokenizer.tokenize_document(document, fields_ids_map, &mut token_fn)?;
|
|
||||||
|
let mut should_tokenize = |field_name: &str| {
|
||||||
|
let (field_id, meta) = field_id_and_metadata(field_name)?;
|
||||||
|
|
||||||
|
let pattern_match = if meta.is_searchable() {
|
||||||
|
PatternMatch::Match
|
||||||
|
} else {
|
||||||
|
// TODO: should be a match on the field_name using `match_field_legacy` function,
|
||||||
|
// but for legacy reasons we iterate over all the fields to fill the field_id_map.
|
||||||
|
PatternMatch::Parent
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((field_id, pattern_match))
|
||||||
|
};
|
||||||
|
|
||||||
|
document_tokenizer.tokenize_document(document, &mut should_tokenize, &mut token_fn)?;
|
||||||
|
|
||||||
drain_word_positions(word_positions, word_pair_proximity);
|
drain_word_positions(word_positions, word_pair_proximity);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WordPairProximityDocidsSettingsExtractorsData<'a, SD> {
|
||||||
|
tokenizer: DocumentTokenizer<'a>,
|
||||||
|
max_memory_by_thread: Option<usize>,
|
||||||
|
buckets: usize,
|
||||||
|
settings_delta: &'a SD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'extractor, SD: SettingsDelta + Sync> SettingsChangeExtractor<'extractor>
|
||||||
|
for WordPairProximityDocidsSettingsExtractorsData<'_, SD>
|
||||||
|
{
|
||||||
|
type Data = RefCell<BalancedCaches<'extractor>>;
|
||||||
|
|
||||||
|
fn init_data<'doc>(&'doc self, extractor_alloc: &'extractor Bump) -> crate::Result<Self::Data> {
|
||||||
|
Ok(RefCell::new(BalancedCaches::new_in(
|
||||||
|
self.buckets,
|
||||||
|
self.max_memory_by_thread,
|
||||||
|
extractor_alloc,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process<'doc>(
|
||||||
|
&'doc self,
|
||||||
|
documents: impl Iterator<Item = crate::Result<DocumentIdentifiers<'doc>>>,
|
||||||
|
context: &'doc DocumentContext<Self::Data>,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
for document in documents {
|
||||||
|
let document = document?;
|
||||||
|
SettingsChangeWordPairProximityDocidsExtractors::extract_document_from_settings_change(
|
||||||
|
document,
|
||||||
|
context,
|
||||||
|
&self.tokenizer,
|
||||||
|
self.settings_delta,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SettingsChangeWordPairProximityDocidsExtractors;
|
||||||
|
|
||||||
|
impl SettingsChangeWordPairProximityDocidsExtractors {
|
||||||
|
pub fn run_extraction<'fid, 'indexer, 'index, 'extractor, SD, MSP>(
|
||||||
|
settings_delta: &SD,
|
||||||
|
documents: &'indexer DocumentsIndentifiers<'indexer>,
|
||||||
|
indexing_context: IndexingContext<'fid, 'indexer, 'index, MSP>,
|
||||||
|
extractor_allocs: &'extractor mut ThreadLocal<FullySend<Bump>>,
|
||||||
|
step: IndexingStep,
|
||||||
|
) -> Result<Vec<BalancedCaches<'extractor>>>
|
||||||
|
where
|
||||||
|
SD: SettingsDelta + Sync,
|
||||||
|
MSP: Fn() -> bool + Sync,
|
||||||
|
{
|
||||||
|
// Warning: this is duplicated code from extract_word_docids.rs
|
||||||
|
let rtxn = indexing_context.index.read_txn()?;
|
||||||
|
let stop_words = indexing_context.index.stop_words(&rtxn)?;
|
||||||
|
let allowed_separators = indexing_context.index.allowed_separators(&rtxn)?;
|
||||||
|
let allowed_separators: Option<Vec<_>> =
|
||||||
|
allowed_separators.as_ref().map(|s| s.iter().map(String::as_str).collect());
|
||||||
|
let dictionary = indexing_context.index.dictionary(&rtxn)?;
|
||||||
|
let dictionary: Option<Vec<_>> =
|
||||||
|
dictionary.as_ref().map(|s| s.iter().map(String::as_str).collect());
|
||||||
|
let mut builder = tokenizer_builder(
|
||||||
|
stop_words.as_ref(),
|
||||||
|
allowed_separators.as_deref(),
|
||||||
|
dictionary.as_deref(),
|
||||||
|
);
|
||||||
|
let tokenizer = builder.build();
|
||||||
|
let localized_attributes_rules =
|
||||||
|
indexing_context.index.localized_attributes_rules(&rtxn)?.unwrap_or_default();
|
||||||
|
let document_tokenizer = DocumentTokenizer {
|
||||||
|
tokenizer: &tokenizer,
|
||||||
|
localized_attributes_rules: &localized_attributes_rules,
|
||||||
|
max_positions_per_attributes: MAX_POSITION_PER_ATTRIBUTE,
|
||||||
|
};
|
||||||
|
let extractor_data = WordPairProximityDocidsSettingsExtractorsData {
|
||||||
|
tokenizer: document_tokenizer,
|
||||||
|
max_memory_by_thread: indexing_context.grenad_parameters.max_memory_by_thread(),
|
||||||
|
buckets: rayon::current_num_threads(),
|
||||||
|
settings_delta,
|
||||||
|
};
|
||||||
|
let datastore = ThreadLocal::new();
|
||||||
|
{
|
||||||
|
let span = tracing::trace_span!(target: "indexing::documents::extract", "word_pair_proximity_docids_extraction");
|
||||||
|
let _entered = span.enter();
|
||||||
|
|
||||||
|
settings_change_extract(
|
||||||
|
documents,
|
||||||
|
&extractor_data,
|
||||||
|
indexing_context,
|
||||||
|
extractor_allocs,
|
||||||
|
&datastore,
|
||||||
|
step,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(datastore.into_iter().map(RefCell::into_inner).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts document words from a settings change.
|
||||||
|
fn extract_document_from_settings_change<SD: SettingsDelta>(
|
||||||
|
document: DocumentIdentifiers<'_>,
|
||||||
|
context: &DocumentContext<RefCell<BalancedCaches<'_>>>,
|
||||||
|
document_tokenizer: &DocumentTokenizer,
|
||||||
|
settings_delta: &SD,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut cached_sorter = context.data.borrow_mut_or_yield();
|
||||||
|
let doc_alloc = &context.doc_alloc;
|
||||||
|
|
||||||
|
let new_fields_ids_map = settings_delta.new_fields_ids_map();
|
||||||
|
let old_fields_ids_map = settings_delta.old_fields_ids_map();
|
||||||
|
let old_proximity_precision = *settings_delta.old_proximity_precision();
|
||||||
|
let new_proximity_precision = *settings_delta.new_proximity_precision();
|
||||||
|
|
||||||
|
let current_document = document.current(
|
||||||
|
&context.rtxn,
|
||||||
|
context.index,
|
||||||
|
old_fields_ids_map.as_fields_ids_map(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum ActionToOperate {
|
||||||
|
ReindexAllFields,
|
||||||
|
SkipDocument,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO prefix_fid delete_old_fid_based_databases
|
||||||
|
let mut action = match (old_proximity_precision, new_proximity_precision) {
|
||||||
|
(ByAttribute, ByWord) => ActionToOperate::ReindexAllFields,
|
||||||
|
(_, _) => ActionToOperate::SkipDocument,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Here we do a preliminary check to determine the action to take.
|
||||||
|
// This check doesn't trigger the tokenizer as we never return
|
||||||
|
// PatternMatch::Match.
|
||||||
|
if action != ActionToOperate::ReindexAllFields {
|
||||||
|
document_tokenizer.tokenize_document(
|
||||||
|
current_document,
|
||||||
|
&mut |field_name| {
|
||||||
|
let fid = new_fields_ids_map.id(field_name).expect("All fields IDs must exist");
|
||||||
|
|
||||||
|
// If the document must be reindexed, early return NoMatch to stop the scanning process.
|
||||||
|
if action == ActionToOperate::ReindexAllFields {
|
||||||
|
return Ok((fid, PatternMatch::NoMatch));
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_field_metadata = old_fields_ids_map.metadata(fid).unwrap();
|
||||||
|
let new_field_metadata = new_fields_ids_map.metadata(fid).unwrap();
|
||||||
|
|
||||||
|
action = match (old_field_metadata, new_field_metadata) {
|
||||||
|
// At least one field is removed or added from the searchable fields
|
||||||
|
(
|
||||||
|
Metadata { searchable: Some(_), .. },
|
||||||
|
Metadata { searchable: None, .. },
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
Metadata { searchable: None, .. },
|
||||||
|
Metadata { searchable: Some(_), .. },
|
||||||
|
) => ActionToOperate::ReindexAllFields,
|
||||||
|
_ => action,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((fid, PatternMatch::Parent))
|
||||||
|
},
|
||||||
|
&mut |_, _, _, _| Ok(()),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Early return when we don't need to index the document
|
||||||
|
if action == ActionToOperate::SkipDocument {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut del_word_pair_proximity = bumpalo::collections::Vec::new_in(doc_alloc);
|
||||||
|
let mut add_word_pair_proximity = bumpalo::collections::Vec::new_in(doc_alloc);
|
||||||
|
|
||||||
|
// is a vecdequeue, and will be smol, so can stay on the heap for now
|
||||||
|
let mut word_positions: VecDeque<(Rc<str>, u16)> =
|
||||||
|
VecDeque::with_capacity(MAX_DISTANCE as usize);
|
||||||
|
|
||||||
|
process_document_tokens(
|
||||||
|
current_document,
|
||||||
|
// TODO Tokenize must be based on old settings
|
||||||
|
document_tokenizer,
|
||||||
|
&mut word_positions,
|
||||||
|
&mut |field_name| {
|
||||||
|
Ok(old_fields_ids_map.id_with_metadata(field_name).expect("All fields must exist"))
|
||||||
|
},
|
||||||
|
&mut |(w1, w2), prox| {
|
||||||
|
del_word_pair_proximity.push(((w1, w2), prox));
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
process_document_tokens(
|
||||||
|
current_document,
|
||||||
|
// TODO Tokenize must be based on new settings
|
||||||
|
document_tokenizer,
|
||||||
|
&mut word_positions,
|
||||||
|
&mut |field_name| {
|
||||||
|
Ok(new_fields_ids_map.id_with_metadata(field_name).expect("All fields must exist"))
|
||||||
|
},
|
||||||
|
&mut |(w1, w2), prox| {
|
||||||
|
add_word_pair_proximity.push(((w1, w2), prox));
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut key_buffer = bumpalo::collections::Vec::new_in(doc_alloc);
|
||||||
|
|
||||||
|
del_word_pair_proximity.sort_unstable();
|
||||||
|
del_word_pair_proximity.dedup_by(|(k1, _), (k2, _)| k1 == k2);
|
||||||
|
for ((w1, w2), prox) in del_word_pair_proximity.iter() {
|
||||||
|
let key = build_key(*prox, w1, w2, &mut key_buffer);
|
||||||
|
cached_sorter.insert_del_u32(key, document.docid())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_word_pair_proximity.sort_unstable();
|
||||||
|
add_word_pair_proximity.dedup_by(|(k1, _), (k2, _)| k1 == k2);
|
||||||
|
for ((w1, w2), prox) in add_word_pair_proximity.iter() {
|
||||||
|
let key = build_key(*prox, w1, w2, &mut key_buffer);
|
||||||
|
cached_sorter.insert_add_u32(key, document.docid())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ mod extract_word_docids;
|
|||||||
mod extract_word_pair_proximity_docids;
|
mod extract_word_pair_proximity_docids;
|
||||||
mod tokenize_document;
|
mod tokenize_document;
|
||||||
|
|
||||||
pub use extract_word_docids::{WordDocidsCaches, WordDocidsExtractors};
|
pub use extract_word_docids::{
|
||||||
pub use extract_word_pair_proximity_docids::WordPairProximityDocidsExtractor;
|
SettingsChangeWordDocidsExtractors, WordDocidsCaches, WordDocidsExtractors,
|
||||||
|
};
|
||||||
|
pub use extract_word_pair_proximity_docids::{
|
||||||
|
SettingsChangeWordPairProximityDocidsExtractors, WordPairProximityDocidsExtractor,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::attribute_patterns::{match_field_legacy, PatternMatch};
|
use crate::attribute_patterns::{match_field_legacy, PatternMatch};
|
||||||
|
|
||||||
@@ -27,3 +31,17 @@ pub fn match_searchable_field(
|
|||||||
|
|
||||||
selection
|
selection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return `true` if the provided `field_name` is a parent of at least one of the fields contained in `searchable`,
|
||||||
|
/// or if `searchable` is `None`.
|
||||||
|
fn has_searchable_children<I, A>(field_name: &str, searchable: Option<I>) -> bool
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = A>,
|
||||||
|
A: AsRef<str>,
|
||||||
|
{
|
||||||
|
searchable.is_none_or(|fields| {
|
||||||
|
fields
|
||||||
|
.into_iter()
|
||||||
|
.any(|attr| match_field_legacy(attr.as_ref(), field_name) == PatternMatch::Parent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ use crate::update::new::document::Document;
|
|||||||
use crate::update::new::extract::perm_json_p::{
|
use crate::update::new::extract::perm_json_p::{
|
||||||
seek_leaf_values_in_array, seek_leaf_values_in_object, Depth,
|
seek_leaf_values_in_array, seek_leaf_values_in_object, Depth,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{FieldId, InternalError, LocalizedAttributesRule, Result, MAX_WORD_LENGTH};
|
||||||
FieldId, GlobalFieldsIdsMap, InternalError, LocalizedAttributesRule, Result, UserError,
|
|
||||||
MAX_WORD_LENGTH,
|
|
||||||
};
|
|
||||||
|
|
||||||
// todo: should be crate::proximity::MAX_DISTANCE but it has been forgotten
|
// todo: should be crate::proximity::MAX_DISTANCE but it has been forgotten
|
||||||
const MAX_DISTANCE: u32 = 8;
|
const MAX_DISTANCE: u32 = 8;
|
||||||
@@ -26,26 +23,25 @@ impl DocumentTokenizer<'_> {
|
|||||||
pub fn tokenize_document<'doc>(
|
pub fn tokenize_document<'doc>(
|
||||||
&self,
|
&self,
|
||||||
document: impl Document<'doc>,
|
document: impl Document<'doc>,
|
||||||
field_id_map: &mut GlobalFieldsIdsMap,
|
should_tokenize: &mut impl FnMut(&str) -> Result<(FieldId, PatternMatch)>,
|
||||||
token_fn: &mut impl FnMut(&str, FieldId, u16, &str) -> Result<()>,
|
token_fn: &mut impl FnMut(&str, FieldId, u16, &str) -> Result<()>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut field_position = HashMap::new();
|
let mut field_position = HashMap::new();
|
||||||
let mut tokenize_field = |field_name: &str, _depth, value: &Value| {
|
|
||||||
let Some((field_id, meta)) = field_id_map.id_with_metadata_or_insert(field_name) else {
|
|
||||||
return Err(UserError::AttributeLimitReached.into());
|
|
||||||
};
|
|
||||||
|
|
||||||
if meta.is_searchable() {
|
|
||||||
self.tokenize_field(field_id, field_name, value, token_fn, &mut field_position)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: should be a match on the field_name using `match_field_legacy` function,
|
|
||||||
// but for legacy reasons we iterate over all the fields to fill the field_id_map.
|
|
||||||
Ok(PatternMatch::Match)
|
|
||||||
};
|
|
||||||
|
|
||||||
for entry in document.iter_top_level_fields() {
|
for entry in document.iter_top_level_fields() {
|
||||||
let (field_name, value) = entry?;
|
let (field_name, value) = entry?;
|
||||||
|
|
||||||
|
if let (_, PatternMatch::NoMatch) = should_tokenize(field_name)? {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tokenize_field = |field_name: &str, _depth, value: &Value| {
|
||||||
|
let (fid, pattern_match) = should_tokenize(field_name)?;
|
||||||
|
if pattern_match == PatternMatch::Match {
|
||||||
|
self.tokenize_field(fid, field_name, value, token_fn, &mut field_position)?;
|
||||||
|
}
|
||||||
|
Ok(pattern_match)
|
||||||
|
};
|
||||||
|
|
||||||
// parse json.
|
// parse json.
|
||||||
match serde_json::to_value(value).map_err(InternalError::SerdeJson)? {
|
match serde_json::to_value(value).map_err(InternalError::SerdeJson)? {
|
||||||
Value::Object(object) => seek_leaf_values_in_object(
|
Value::Object(object) => seek_leaf_values_in_object(
|
||||||
@@ -192,7 +188,7 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::fields_ids_map::metadata::{FieldIdMapWithMetadata, MetadataBuilder};
|
use crate::fields_ids_map::metadata::{FieldIdMapWithMetadata, MetadataBuilder};
|
||||||
use crate::update::new::document::{DocumentFromVersions, Versions};
|
use crate::update::new::document::{DocumentFromVersions, Versions};
|
||||||
use crate::FieldsIdsMap;
|
use crate::{FieldsIdsMap, GlobalFieldsIdsMap, UserError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokenize_document() {
|
fn test_tokenize_document() {
|
||||||
@@ -231,6 +227,7 @@ mod test {
|
|||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
|
Default::default(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
@@ -251,15 +248,19 @@ mod test {
|
|||||||
let document = Versions::single(document);
|
let document = Versions::single(document);
|
||||||
let document = DocumentFromVersions::new(&document);
|
let document = DocumentFromVersions::new(&document);
|
||||||
|
|
||||||
|
let mut should_tokenize = |field_name: &str| {
|
||||||
|
let Some(field_id) = global_fields_ids_map.id_or_insert(field_name) else {
|
||||||
|
return Err(UserError::AttributeLimitReached.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((field_id, PatternMatch::Match))
|
||||||
|
};
|
||||||
|
|
||||||
document_tokenizer
|
document_tokenizer
|
||||||
.tokenize_document(
|
.tokenize_document(document, &mut should_tokenize, &mut |_fname, fid, pos, word| {
|
||||||
document,
|
|
||||||
&mut global_fields_ids_map,
|
|
||||||
&mut |_fname, fid, pos, word| {
|
|
||||||
words.insert([fid, pos], word.to_string());
|
words.insert([fid, pos], word.to_string());
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
snapshot!(format!("{:#?}", words), @r###"
|
snapshot!(format!("{:#?}", words), @r###"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use bumpalo::collections::Vec as BVec;
|
use bumpalo::collections::Vec as BVec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
@@ -27,7 +28,10 @@ use crate::vector::extractor::{
|
|||||||
use crate::vector::session::{EmbedSession, Input, Metadata, OnEmbed};
|
use crate::vector::session::{EmbedSession, Input, Metadata, OnEmbed};
|
||||||
use crate::vector::settings::ReindexAction;
|
use crate::vector::settings::ReindexAction;
|
||||||
use crate::vector::{Embedding, RuntimeEmbedder, RuntimeEmbedders, RuntimeFragment};
|
use crate::vector::{Embedding, RuntimeEmbedder, RuntimeEmbedders, RuntimeFragment};
|
||||||
use crate::{DocumentId, FieldDistribution, InternalError, Result, ThreadPoolNoAbort, UserError};
|
use crate::{
|
||||||
|
DocumentId, FieldDistribution, GlobalFieldsIdsMap, InternalError, Result, ThreadPoolNoAbort,
|
||||||
|
UserError,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct EmbeddingExtractor<'a, 'b> {
|
pub struct EmbeddingExtractor<'a, 'b> {
|
||||||
embedders: &'a RuntimeEmbedders,
|
embedders: &'a RuntimeEmbedders,
|
||||||
@@ -321,6 +325,15 @@ impl<'extractor, SD: SettingsDelta + Sync> SettingsChangeExtractor<'extractor>
|
|||||||
let old_embedders = self.settings_delta.old_embedders();
|
let old_embedders = self.settings_delta.old_embedders();
|
||||||
let unused_vectors_distribution = UnusedVectorsDistributionBump::new_in(&context.doc_alloc);
|
let unused_vectors_distribution = UnusedVectorsDistributionBump::new_in(&context.doc_alloc);
|
||||||
|
|
||||||
|
// We get a reference to the new and old fields ids maps but
|
||||||
|
// note that those are local versions where updates to them
|
||||||
|
// will not be reflected in the database. It's not an issue
|
||||||
|
// because new settings do not generate new fields.
|
||||||
|
let new_fields_ids_map = RwLock::new(self.settings_delta.new_fields_ids_map().clone());
|
||||||
|
let new_fields_ids_map = RefCell::new(GlobalFieldsIdsMap::new(&new_fields_ids_map));
|
||||||
|
let old_fields_ids_map = RwLock::new(self.settings_delta.old_fields_ids_map().clone());
|
||||||
|
let old_fields_ids_map = RefCell::new(GlobalFieldsIdsMap::new(&old_fields_ids_map));
|
||||||
|
|
||||||
let mut all_chunks = BVec::with_capacity_in(embedders.len(), &context.doc_alloc);
|
let mut all_chunks = BVec::with_capacity_in(embedders.len(), &context.doc_alloc);
|
||||||
let embedder_configs = context.index.embedding_configs();
|
let embedder_configs = context.index.embedding_configs();
|
||||||
for (embedder_name, action) in self.settings_delta.embedder_actions().iter() {
|
for (embedder_name, action) in self.settings_delta.embedder_actions().iter() {
|
||||||
@@ -396,6 +409,7 @@ impl<'extractor, SD: SettingsDelta + Sync> SettingsChangeExtractor<'extractor>
|
|||||||
if !must_regenerate {
|
if !must_regenerate {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to regenerate the prompts for the document
|
// we need to regenerate the prompts for the document
|
||||||
chunks.settings_change_autogenerated(
|
chunks.settings_change_autogenerated(
|
||||||
document.docid(),
|
document.docid(),
|
||||||
@@ -406,7 +420,8 @@ impl<'extractor, SD: SettingsDelta + Sync> SettingsChangeExtractor<'extractor>
|
|||||||
context.db_fields_ids_map,
|
context.db_fields_ids_map,
|
||||||
)?,
|
)?,
|
||||||
self.settings_delta,
|
self.settings_delta,
|
||||||
context.new_fields_ids_map,
|
&old_fields_ids_map,
|
||||||
|
&new_fields_ids_map,
|
||||||
&unused_vectors_distribution,
|
&unused_vectors_distribution,
|
||||||
old_is_user_provided,
|
old_is_user_provided,
|
||||||
fragments_changed,
|
fragments_changed,
|
||||||
@@ -442,7 +457,8 @@ impl<'extractor, SD: SettingsDelta + Sync> SettingsChangeExtractor<'extractor>
|
|||||||
context.db_fields_ids_map,
|
context.db_fields_ids_map,
|
||||||
)?,
|
)?,
|
||||||
self.settings_delta,
|
self.settings_delta,
|
||||||
context.new_fields_ids_map,
|
&old_fields_ids_map,
|
||||||
|
&new_fields_ids_map,
|
||||||
&unused_vectors_distribution,
|
&unused_vectors_distribution,
|
||||||
old_is_user_provided,
|
old_is_user_provided,
|
||||||
true,
|
true,
|
||||||
@@ -638,7 +654,8 @@ impl<'a, 'b, 'extractor> Chunks<'a, 'b, 'extractor> {
|
|||||||
external_docid: &'a str,
|
external_docid: &'a str,
|
||||||
document: D,
|
document: D,
|
||||||
settings_delta: &SD,
|
settings_delta: &SD,
|
||||||
fields_ids_map: &'a RefCell<crate::GlobalFieldsIdsMap>,
|
old_fields_ids_map: &'a RefCell<GlobalFieldsIdsMap<'a>>,
|
||||||
|
new_fields_ids_map: &'a RefCell<GlobalFieldsIdsMap<'a>>,
|
||||||
unused_vectors_distribution: &UnusedVectorsDistributionBump<'a>,
|
unused_vectors_distribution: &UnusedVectorsDistributionBump<'a>,
|
||||||
old_is_user_provided: bool,
|
old_is_user_provided: bool,
|
||||||
full_reindex: bool,
|
full_reindex: bool,
|
||||||
@@ -733,10 +750,17 @@ impl<'a, 'b, 'extractor> Chunks<'a, 'b, 'extractor> {
|
|||||||
old_embedder.as_ref().map(|old_embedder| &old_embedder.document_template)
|
old_embedder.as_ref().map(|old_embedder| &old_embedder.document_template)
|
||||||
};
|
};
|
||||||
|
|
||||||
let extractor =
|
let extractor = DocumentTemplateExtractor::new(
|
||||||
DocumentTemplateExtractor::new(document_template, doc_alloc, fields_ids_map);
|
document_template,
|
||||||
|
doc_alloc,
|
||||||
|
new_fields_ids_map,
|
||||||
|
);
|
||||||
let old_extractor = old_document_template.map(|old_document_template| {
|
let old_extractor = old_document_template.map(|old_document_template| {
|
||||||
DocumentTemplateExtractor::new(old_document_template, doc_alloc, fields_ids_map)
|
DocumentTemplateExtractor::new(
|
||||||
|
old_document_template,
|
||||||
|
doc_alloc,
|
||||||
|
old_fields_ids_map,
|
||||||
|
)
|
||||||
});
|
});
|
||||||
let metadata =
|
let metadata =
|
||||||
Metadata { docid, external_docid, extractor_id: extractor.extractor_id() };
|
Metadata { docid, external_docid, extractor_id: extractor.extractor_id() };
|
||||||
|
|||||||
9
crates/milli/src/update/new/indexer/community_edition.rs
Normal file
9
crates/milli/src/update/new/indexer/community_edition.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
pub mod sharding {
|
||||||
|
pub struct Shards;
|
||||||
|
|
||||||
|
impl Shards {
|
||||||
|
pub fn must_process(&self, _docid: &str) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,7 @@ use super::guess_primary_key::retrieve_or_guess_primary_key;
|
|||||||
use crate::documents::PrimaryKey;
|
use crate::documents::PrimaryKey;
|
||||||
use crate::progress::{AtomicPayloadStep, Progress};
|
use crate::progress::{AtomicPayloadStep, Progress};
|
||||||
use crate::update::new::document::{DocumentContext, Versions};
|
use crate::update::new::document::{DocumentContext, Versions};
|
||||||
use crate::update::new::indexer::enterprise_edition::sharding::Shards;
|
use crate::update::new::indexer::current_edition::sharding::Shards;
|
||||||
use crate::update::new::indexer::update_by_function::obkv_to_rhaimap;
|
|
||||||
use crate::update::new::steps::IndexingStep;
|
use crate::update::new::steps::IndexingStep;
|
||||||
use crate::update::new::thread_local::MostlySend;
|
use crate::update::new::thread_local::MostlySend;
|
||||||
use crate::update::new::{DocumentIdentifiers, Insertion, Update};
|
use crate::update::new::{DocumentIdentifiers, Insertion, Update};
|
||||||
@@ -163,16 +162,7 @@ impl<'pl> DocumentOperation<'pl> {
|
|||||||
.sort_unstable_by_key(|(_, po)| first_update_pointer(&po.operations).unwrap_or(0));
|
.sort_unstable_by_key(|(_, po)| first_update_pointer(&po.operations).unwrap_or(0));
|
||||||
|
|
||||||
let docids_version_offsets = docids_version_offsets.into_bump_slice();
|
let docids_version_offsets = docids_version_offsets.into_bump_slice();
|
||||||
let engine = rhai::Engine::new();
|
Ok((DocumentOperationChanges { docids_version_offsets }, operations_stats, primary_key))
|
||||||
// Make sure to correctly setup the engine and remove all settings
|
|
||||||
let ast = index.execute_after_update(rtxn)?.map(|f| engine.compile(f).unwrap());
|
|
||||||
let fidmap = index.fields_ids_map(rtxn)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
DocumentOperationChanges { docids_version_offsets, engine, ast, fidmap },
|
|
||||||
operations_stats,
|
|
||||||
primary_key,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,15 +435,7 @@ impl<'pl> DocumentChanges<'pl> for DocumentOperationChanges<'pl> {
|
|||||||
'pl: 'doc,
|
'pl: 'doc,
|
||||||
{
|
{
|
||||||
let (external_doc, payload_operations) = item;
|
let (external_doc, payload_operations) = item;
|
||||||
payload_operations.merge(
|
payload_operations.merge(external_doc, &context.doc_alloc)
|
||||||
&context.rtxn,
|
|
||||||
context.index,
|
|
||||||
&self.fidmap,
|
|
||||||
&self.engine,
|
|
||||||
self.ast.as_ref(),
|
|
||||||
external_doc,
|
|
||||||
&context.doc_alloc,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
@@ -462,9 +444,6 @@ impl<'pl> DocumentChanges<'pl> for DocumentOperationChanges<'pl> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct DocumentOperationChanges<'pl> {
|
pub struct DocumentOperationChanges<'pl> {
|
||||||
engine: rhai::Engine,
|
|
||||||
ast: Option<rhai::AST>,
|
|
||||||
fidmap: FieldsIdsMap,
|
|
||||||
docids_version_offsets: &'pl [(&'pl str, PayloadOperations<'pl>)],
|
docids_version_offsets: &'pl [(&'pl str, PayloadOperations<'pl>)],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,14 +506,10 @@ impl<'pl> PayloadOperations<'pl> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns only the most recent version of a document based on the updates from the payloads.
|
/// Returns only the most recent version of a document based on the updates from the payloads.
|
||||||
#[allow(clippy::too_many_arguments)]
|
///
|
||||||
|
/// This function is only meant to be used when doing a replacement and not an update.
|
||||||
fn merge<'doc>(
|
fn merge<'doc>(
|
||||||
&self,
|
&self,
|
||||||
rtxn: &heed::RoTxn,
|
|
||||||
index: &Index,
|
|
||||||
fidmap: &FieldsIdsMap,
|
|
||||||
engine: &rhai::Engine,
|
|
||||||
ast: Option<&rhai::AST>,
|
|
||||||
external_doc: &'doc str,
|
external_doc: &'doc str,
|
||||||
doc_alloc: &'doc Bump,
|
doc_alloc: &'doc Bump,
|
||||||
) -> Result<Option<DocumentChange<'doc>>>
|
) -> Result<Option<DocumentChange<'doc>>>
|
||||||
@@ -598,33 +573,9 @@ impl<'pl> PayloadOperations<'pl> {
|
|||||||
Ok(document)
|
Ok(document)
|
||||||
});
|
});
|
||||||
|
|
||||||
let versions = match ast {
|
let Some(versions) = Versions::multiple(versions)? else { return Ok(None) };
|
||||||
Some(ast) => {
|
|
||||||
let doc = index
|
|
||||||
.documents
|
|
||||||
.get(rtxn, &self.docid)?
|
|
||||||
.map(|obkv| obkv_to_rhaimap(obkv, fidmap))
|
|
||||||
.transpose()?;
|
|
||||||
match Versions::multiple_with_edits(doc, versions, engine, ast, doc_alloc)?
|
|
||||||
{
|
|
||||||
Some(Some(versions)) => Some(versions),
|
|
||||||
Some(None) if self.is_new => return Ok(None),
|
|
||||||
Some(None) => {
|
|
||||||
return Ok(Some(DocumentChange::Deletion(
|
|
||||||
DocumentIdentifiers::create(self.docid, external_doc),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Versions::multiple(versions)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(versions) = versions else {
|
if self.is_new {
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.is_new || ast.is_some() {
|
|
||||||
Ok(Some(DocumentChange::Insertion(Insertion::create(
|
Ok(Some(DocumentChange::Insertion(Insertion::create(
|
||||||
self.docid,
|
self.docid,
|
||||||
external_doc,
|
external_doc,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user