mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-11-23 05:06:11 +00:00
Compare commits
450 Commits
fix_get_ba
...
more-effic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cf14e765f | ||
|
|
4a082683df | ||
|
|
26be5e0733 | ||
|
|
bd5110a2fe | ||
|
|
fa8b9acdf6 | ||
|
|
2b74d1824b | ||
|
|
c77b00d3ac | ||
|
|
c77073efcc | ||
|
|
1537323eb9 | ||
|
|
a0a3b55700 | ||
|
|
214b51de87 | ||
|
|
95975944d7 | ||
|
|
9a9383643f | ||
|
|
cac355bfa7 | ||
|
|
9020a50df8 | ||
|
|
52843123d4 | ||
|
|
6298db5bea | ||
|
|
a003a0934a | ||
|
|
3a11e39c01 | ||
|
|
5f896b1050 | ||
|
|
d0c4e6da6b | ||
|
|
2da5584bb5 | ||
|
|
b7eb802ae6 | ||
|
|
2e32d0474c | ||
|
|
cb99ac6f7e | ||
|
|
be411435f5 | ||
|
|
29ef164530 | ||
|
|
739c52a3cd | ||
|
|
7a2af06b1e | ||
|
|
cb0c3a5aad | ||
|
|
8388698993 | ||
|
|
cbcf6c9ba3 | ||
|
|
bf742d81cf | ||
|
|
7458f0386c | ||
|
|
fc1df5793c | ||
|
|
3ded069042 | ||
|
|
261d2ceb06 | ||
|
|
1a17e2e572 | ||
|
|
5b8cd68abe | ||
|
|
5ce9acb0b9 | ||
|
|
953a82ca04 | ||
|
|
54341c2e80 | ||
|
|
96831ed9bb | ||
|
|
0459b1a242 | ||
|
|
8ecb726683 | ||
|
|
297e72e262 | ||
|
|
0ad2f57a92 | ||
|
|
71d53f413f | ||
|
|
054622bd16 | ||
|
|
e905a72d73 | ||
|
|
2e879c1df8 | ||
|
|
d040aff101 | ||
|
|
5e30731cad | ||
|
|
beeb31ce41 | ||
|
|
057143214d | ||
|
|
6a1d26a60c | ||
|
|
d78f4666a0 | ||
|
|
a439fa3e1a | ||
|
|
767259be7e | ||
|
|
e9f34fb4b1 | ||
|
|
d5c07ef7b3 | ||
|
|
5e218f3f4d | ||
|
|
bcab61ab1d | ||
|
|
263c5a348e | ||
|
|
be7d2fbe63 | ||
|
|
f7f9a131e4 | ||
|
|
5df5eb2db2 | ||
|
|
30eb0e5b5b | ||
|
|
5b860cb989 | ||
|
|
76d0623b11 | ||
|
|
db4eaf4d2d | ||
|
|
13f21206a6 | ||
|
|
14ee7aa84c | ||
|
|
8a35cd1743 | ||
|
|
8d33af1dff | ||
|
|
3c7ac093d3 | ||
|
|
d49d127863 | ||
|
|
b57dd5c58e | ||
|
|
90b428a8c3 | ||
|
|
096a28656e | ||
|
|
3dc87f5baa | ||
|
|
cc4bd54669 | ||
|
|
5383f41bba | ||
|
|
58eab9a018 | ||
|
|
9f36ffcbdb | ||
|
|
68c4717e21 | ||
|
|
5c488e20cc | ||
|
|
da650f834e | ||
|
|
e83534a430 | ||
|
|
98d4a2909e | ||
|
|
a514ce472a | ||
|
|
cc63802115 | ||
|
|
acec45ad7c | ||
|
|
08d6413365 | ||
|
|
70802eb7c7 | ||
|
|
6ac5b3b136 | ||
|
|
e1e76f39d0 | ||
|
|
2094ce8a9a | ||
|
|
8442db8101 | ||
|
|
79671c9faa | ||
|
|
a2f64f6552 | ||
|
|
fde2e0691c | ||
|
|
18a9af353c | ||
|
|
aae0dc715d | ||
|
|
d0b2c0a523 | ||
|
|
2e896f30a5 | ||
|
|
8f57b4fdf4 | ||
|
|
f014e78684 | ||
|
|
9008ecda3d | ||
|
|
d7bcfb2d19 | ||
|
|
fb66fec398 | ||
|
|
fa15be5bc4 | ||
|
|
aa460819a7 | ||
|
|
e241f91285 | ||
|
|
d66dc363ed | ||
|
|
5560452ef9 | ||
|
|
d9df7e00e1 | ||
|
|
b4fb2dabd4 | ||
|
|
5606679c53 | ||
|
|
a3103f347e | ||
|
|
25aac45fc7 | ||
|
|
dd76eaaaec | ||
|
|
98a785b0d7 | ||
|
|
19e6f675b3 | ||
|
|
323ecbb885 | ||
|
|
ffb60cb885 | ||
|
|
dcc3caef0d | ||
|
|
221e547e86 | ||
|
|
61d0615253 | ||
|
|
5727e00374 | ||
|
|
9b60843831 | ||
|
|
36962b943b | ||
|
|
32bcacefd5 | ||
|
|
4ed195426c | ||
|
|
ff38f29981 | ||
|
|
5899861ff0 | ||
|
|
94b260fd25 | ||
|
|
03ab6b39e7 | ||
|
|
ab2c83f868 | ||
|
|
1f9692cd04 | ||
|
|
bda2b41d11 | ||
|
|
6e6acfcf1b | ||
|
|
e0864f1b21 | ||
|
|
a38344acb3 | ||
|
|
4d616f8794 | ||
|
|
ff9c92c409 | ||
|
|
8380ddbdcd | ||
|
|
867138f166 | ||
|
|
567bd4538b | ||
|
|
84600a10d1 | ||
|
|
35bbe1c2a2 | ||
|
|
7d64e8dbd3 | ||
|
|
cae8c89467 | ||
|
|
7cb8732b45 | ||
|
|
fe5d50969a | ||
|
|
56c7c5d5f0 | ||
|
|
4cdfdddd6d | ||
|
|
2afa33011a | ||
|
|
61feca1f41 | ||
|
|
f893b5153e | ||
|
|
ca779c21f9 | ||
|
|
477077bdc2 | ||
|
|
b1f8aec348 | ||
|
|
ba7f091db3 | ||
|
|
8049df125b | ||
|
|
50d1bd01df | ||
|
|
a28d4f5d0c | ||
|
|
fc14f4bc66 | ||
|
|
5f8a82d6f5 | ||
|
|
fe04e51a49 | ||
|
|
01b27e40ad | ||
|
|
8076d98544 | ||
|
|
9e951baad5 | ||
|
|
52f2fc4c46 | ||
|
|
3957917e0b | ||
|
|
651c30899e | ||
|
|
2c7a7fe4e8 | ||
|
|
23f0c2c29b | ||
|
|
6641c3f59b | ||
|
|
07a72824b7 | ||
|
|
000eb55c4e | ||
|
|
b4bf7ce9b0 | ||
|
|
1aef0e4037 | ||
|
|
32d0e50a75 | ||
|
|
df5884b0c1 | ||
|
|
9e0eb5ebb0 | ||
|
|
3cf1352ae1 | ||
|
|
aba8a0e9e0 | ||
|
|
670aff5553 | ||
|
|
41dbdd2d18 | ||
|
|
bfefaf71c2 | ||
|
|
c782c09208 | ||
|
|
75943a5a9b | ||
|
|
04c38220ca | ||
|
|
5f93651cef | ||
|
|
510ca99996 | ||
|
|
8924d486db | ||
|
|
e0c3f3d560 | ||
|
|
0a21d9bfb3 | ||
|
|
1f8b01a598 | ||
|
|
e736a74729 | ||
|
|
e9d17136b2 | ||
|
|
a05e448cf8 | ||
|
|
cd796b0f4b | ||
|
|
6570da3bcb | ||
|
|
5b4c06c24c | ||
|
|
3a8051866a | ||
|
|
9150c8f052 | ||
|
|
c202f3dbe2 | ||
|
|
677d7293f5 | ||
|
|
bd31ea2174 | ||
|
|
83865d2ebd | ||
|
|
72ba353498 | ||
|
|
4ff2b3c2ee | ||
|
|
91c58cfa38 | ||
|
|
9e8367f1e6 | ||
|
|
0dd321afc7 | ||
|
|
0e3c5d91ab | ||
|
|
695c2c6b99 | ||
|
|
40dd25d6b2 | ||
|
|
8e5b1a3ec1 | ||
|
|
e627e182ce | ||
|
|
51b6293738 | ||
|
|
b17896d899 | ||
|
|
a01bc7b454 | ||
|
|
7accfea624 | ||
|
|
82dcaba6ca | ||
|
|
cb1d6613dd | ||
|
|
3b0cb5b487 | ||
|
|
bfdcd1cf33 | ||
|
|
1d13e804f7 | ||
|
|
c4e9f761e9 | ||
|
|
8a6e61c77f | ||
|
|
68bbf674c9 | ||
|
|
980921e078 | ||
|
|
1fcd5f091e | ||
|
|
6094bb299a | ||
|
|
bef8fc6cf1 | ||
|
|
e32677999f | ||
|
|
5185aa21b8 | ||
|
|
8a314ab81d | ||
|
|
4706a0eb49 | ||
|
|
d97af4d8e6 | ||
|
|
1f5d801271 | ||
|
|
7864530589 | ||
|
|
700757c01f | ||
|
|
01f8f30a7a | ||
|
|
0e4e9e866a | ||
|
|
1477b81d38 | ||
|
|
c9f478bc45 | ||
|
|
39366a67c4 | ||
|
|
e2138170ad | ||
|
|
03650e3217 | ||
|
|
10f49f0d75 | ||
|
|
ee03743355 | ||
|
|
10feeb88f2 | ||
|
|
a9ecbf0b64 | ||
|
|
8b260de5a0 | ||
|
|
33b1f54b41 | ||
|
|
db55638714 | ||
|
|
ad52c950ba | ||
|
|
3658f57f93 | ||
|
|
a77d5ea8c1 | ||
|
|
c9082130c8 | ||
|
|
df5bc3c9fd | ||
|
|
0f6a1dbce7 | ||
|
|
4ebedf4dc8 | ||
|
|
1075dd34bb | ||
|
|
7058959a46 | ||
|
|
9cbb2b066a | ||
|
|
5efd70c251 | ||
|
|
65470e26e0 | ||
|
|
bbb67ae0a8 | ||
|
|
af9f96e2af | ||
|
|
1960003805 | ||
|
|
2a91849660 | ||
|
|
663deac236 | ||
|
|
c8189e975c | ||
|
|
9e7c455a01 | ||
|
|
c22dc55694 | ||
|
|
50de3fba7b | ||
|
|
3d29226a7f | ||
|
|
89243f7df0 | ||
|
|
9fe5122176 | ||
|
|
aff8ca4397 | ||
|
|
1a3f4e719d | ||
|
|
c278024709 | ||
|
|
73e29ee155 | ||
|
|
124b5c3df8 | ||
|
|
60cc09abec | ||
|
|
cd378e5bd2 | ||
|
|
c1fcb2ebc6 | ||
|
|
0749633618 | ||
|
|
0647f75e6b | ||
|
|
86a0097311 | ||
|
|
c75de1f391 | ||
|
|
198238687f | ||
|
|
f9a6c624a7 | ||
|
|
017757004e | ||
|
|
152683083b | ||
|
|
c283c95f6a | ||
|
|
9a0e1dc375 | ||
|
|
1e81d72b5f | ||
|
|
52b95c4e59 | ||
|
|
7e1dc8439b | ||
|
|
96658ec775 | ||
|
|
c01ee7b732 | ||
|
|
6ad3f57bc1 | ||
|
|
28d92c521a | ||
|
|
7df20d8282 | ||
|
|
b4102741e6 | ||
|
|
a525598ad6 | ||
|
|
4e97e38177 | ||
|
|
d675e73af1 | ||
|
|
a2fbf2ea21 | ||
|
|
132916f62c | ||
|
|
8371819114 | ||
|
|
6028d6ba43 | ||
|
|
68a2502388 | ||
|
|
39b27e42be | ||
|
|
470c2272dd | ||
|
|
2230674c0a | ||
|
|
eb09dfed04 | ||
|
|
83c09d0db0 | ||
|
|
c11b7e5c0f | ||
|
|
dff2d54784 | ||
|
|
58d96fbea3 | ||
|
|
4665bfcb19 | ||
|
|
a7a01646cf | ||
|
|
0409a26cd8 | ||
|
|
35f78b5423 | ||
|
|
14261f8f04 | ||
|
|
774ed28539 | ||
|
|
d79f75f630 | ||
|
|
ccf01c2471 | ||
|
|
b7a5ba100e | ||
|
|
dead7a56a3 | ||
|
|
0a8cb471df | ||
|
|
00e045b249 | ||
|
|
d83c9a4074 | ||
|
|
f3356ddaa4 | ||
|
|
31de5c747e | ||
|
|
3843240940 | ||
|
|
8cb5e7437d | ||
|
|
5b776556fe | ||
|
|
bb7a503e5d | ||
|
|
64589278ac | ||
|
|
8df6daf308 | ||
|
|
5b552caf42 | ||
|
|
2b51a63418 | ||
|
|
3d8024fb2b | ||
|
|
4b0da0ff24 | ||
|
|
079f2b5de0 | ||
|
|
960060ebdf | ||
|
|
3d244451df | ||
|
|
5f53935c8a | ||
|
|
29a7623c3f | ||
|
|
e97041f7d0 | ||
|
|
52d7f3ed1c | ||
|
|
86d5e6d9ff | ||
|
|
759b9b1546 | ||
|
|
3f7a500f3b | ||
|
|
974272f2e9 | ||
|
|
7ad037841f | ||
|
|
e0c7067355 | ||
|
|
6e87332410 | ||
|
|
2d1caf27df | ||
|
|
92678383d6 | ||
|
|
7f148c127c | ||
|
|
4ce5d3d66d | ||
|
|
ff931edb55 | ||
|
|
42b093687d | ||
|
|
835c5f98f9 | ||
|
|
f00664247d | ||
|
|
3c63d4a1e5 | ||
|
|
4551abf6d4 | ||
|
|
193d7f5d34 | ||
|
|
013acb3d93 | ||
|
|
f4ab1f168e | ||
|
|
1a0e962299 | ||
|
|
f13e076b8a | ||
|
|
7ba49b849e | ||
|
|
f7652186e1 | ||
|
|
b2f4e67c9a | ||
|
|
ff5d3b59f5 | ||
|
|
aa69308e45 | ||
|
|
eb9a20ff0b | ||
|
|
0d868f36d7 | ||
|
|
e7d9db078f | ||
|
|
3e9198ebaa | ||
|
|
2a0ad0982f | ||
|
|
2b317c681b | ||
|
|
39b5990f64 | ||
|
|
3848adf5a2 | ||
|
|
b4de06259e | ||
|
|
8287c2644f | ||
|
|
c1c44a0b81 | ||
|
|
04596f3616 | ||
|
|
24cb5839ad | ||
|
|
8d97b7b28c | ||
|
|
f69688e8f7 | ||
|
|
8fd0afaaaa | ||
|
|
72c6a21a30 | ||
|
|
8412be4a7d | ||
|
|
10f09c531f | ||
|
|
8fd99b111b | ||
|
|
f6b3d1f9a5 | ||
|
|
73ce67862d | ||
|
|
0fc02f7351 | ||
|
|
34f11e3380 | ||
|
|
27308eaab1 | ||
|
|
b33ec9ba3f | ||
|
|
9c0a1cd9fd | ||
|
|
0b061f1e70 | ||
|
|
19d937ab21 | ||
|
|
1d59c19cd2 | ||
|
|
98e48371c3 | ||
|
|
6d74fb0229 | ||
|
|
1eb75a1040 | ||
|
|
3b82d8b5b9 | ||
|
|
781a186f75 | ||
|
|
6a399556b5 | ||
|
|
27b4cab857 | ||
|
|
52d32b4ee9 | ||
|
|
da61408e52 | ||
|
|
fe69385bd7 | ||
|
|
c1557734dc | ||
|
|
c50d3edc4a | ||
|
|
5369bf4a62 | ||
|
|
bcb1aa3d22 | ||
|
|
9b7858fb90 | ||
|
|
ab01679a8f | ||
|
|
521775f788 | ||
|
|
72e7b7846e | ||
|
|
6526ce1208 | ||
|
|
e639ec79d1 | ||
|
|
bb885a5810 | ||
|
|
b625d31c7d | ||
|
|
6487a67f2b | ||
|
|
271ce91b3b | ||
|
|
54f2eb4507 | ||
|
|
794ebcd582 | ||
|
|
b7c77c7a39 | ||
|
|
0c57cf7565 | ||
|
|
27df9e6c73 | ||
|
|
45c060831e | ||
|
|
874c1ac538 | ||
|
|
e6ffa4d454 | ||
|
|
637a9c8bdd | ||
|
|
c683fa98e6 |
290
Cargo.lock
generated
290
Cargo.lock
generated
@@ -80,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -216,7 +216,7 @@ dependencies = [
|
|||||||
"actix-router",
|
"actix-router",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -296,9 +296,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.16"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anes"
|
name = "anes"
|
||||||
@@ -441,7 +441,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -489,16 +489,23 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bbqueue"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "git+https://github.com/meilisearch/bbqueue#cbb87cc707b5af415ef203bdaf2443e06ba0d6d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "benchmarks"
|
name = "benchmarks"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"bumpalo",
|
||||||
"bytes",
|
"bytes",
|
||||||
"convert_case 0.6.0",
|
"convert_case 0.6.0",
|
||||||
"criterion",
|
"criterion",
|
||||||
"csv",
|
"csv",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
"memmap2",
|
||||||
"milli",
|
"milli",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -506,6 +513,7 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"roaring",
|
"roaring",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -549,7 +557,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash 1.1.0",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -593,6 +601,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitpacking"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c1d3e2bfd8d06048a179f7b17afc3188effa10385e7b00dc65af6aae732ea92"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitvec"
|
name = "bitvec"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -634,7 +651,7 @@ dependencies = [
|
|||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
"syn_derive",
|
"syn_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -672,7 +689,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "build-info"
|
name = "build-info"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"time",
|
"time",
|
||||||
@@ -684,6 +701,10 @@ name = "bumpalo"
|
|||||||
version = "3.16.0"
|
version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byte-unit"
|
name = "byte-unit"
|
||||||
@@ -741,7 +762,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -953,9 +974,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "charabia"
|
name = "charabia"
|
||||||
version = "0.9.1"
|
version = "0.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55ff52497324e7d168505a16949ae836c14595606fab94687238d2f6c8d4c798"
|
checksum = "cf8921fe4d53ab8f9e8f9b72ce6f91726cfc40fffab1243d27db406b5e2e9cc2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"csv",
|
"csv",
|
||||||
@@ -1052,7 +1073,7 @@ dependencies = [
|
|||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1230,19 +1251,6 @@ dependencies = [
|
|||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-channel",
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-queue",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.13"
|
version = "0.5.13"
|
||||||
@@ -1378,7 +1386,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim 0.11.1",
|
"strsim 0.11.1",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1400,7 +1408,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core 0.20.9",
|
"darling_core 0.20.9",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1454,7 +1462,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1496,7 +1504,7 @@ dependencies = [
|
|||||||
"darling 0.20.9",
|
"darling 0.20.9",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1516,7 +1524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
|
checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_builder_core 0.20.0",
|
"derive_builder_core 0.20.0",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1558,7 +1566,7 @@ dependencies = [
|
|||||||
"convert_case 0.6.0",
|
"convert_case 0.6.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1622,7 +1630,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1642,7 +1650,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dump"
|
name = "dump"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"big_s",
|
"big_s",
|
||||||
@@ -1780,7 +1788,7 @@ dependencies = [
|
|||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1800,7 +1808,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1848,13 +1856,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "file-store"
|
name = "file-store"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -1876,7 +1884,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filter-parser"
|
name = "filter-parser"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"nom",
|
"nom",
|
||||||
@@ -1896,18 +1904,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flatten-serde-json"
|
name = "flatten-serde-json"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flume"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@@ -1985,7 +2008,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2020,10 +2043,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuzzers"
|
name = "fuzzers"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
|
"bumpalo",
|
||||||
"clap",
|
"clap",
|
||||||
|
"either",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"milli",
|
"milli",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2240,12 +2265,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grenad"
|
name = "grenad"
|
||||||
version = "0.4.7"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "350d89047298d3b1b40050acd11ab76e487b854a104b760ebc5a7f375093de77"
|
checksum = "0e2ac9baf835ee2a7f0622a5617792ced6f65af25994078c343d429431ef2bbc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"either",
|
||||||
"rayon",
|
"rayon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
@@ -2336,6 +2362,18 @@ dependencies = [
|
|||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
|
"foldhash",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heapless"
|
name = "heapless"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -2572,13 +2610,14 @@ checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "index-scheduler"
|
name = "index-scheduler"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arroy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arroy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"big_s",
|
"big_s",
|
||||||
"bincode",
|
"bincode",
|
||||||
"crossbeam",
|
"bumpalo",
|
||||||
|
"crossbeam-channel",
|
||||||
"csv",
|
"csv",
|
||||||
"derive_builder 0.20.0",
|
"derive_builder 0.20.0",
|
||||||
"dump",
|
"dump",
|
||||||
@@ -2590,7 +2629,9 @@ dependencies = [
|
|||||||
"meili-snap",
|
"meili-snap",
|
||||||
"meilisearch-auth",
|
"meilisearch-auth",
|
||||||
"meilisearch-types",
|
"meilisearch-types",
|
||||||
|
"memmap2",
|
||||||
"page_size",
|
"page_size",
|
||||||
|
"raw-collections",
|
||||||
"rayon",
|
"rayon",
|
||||||
"roaring",
|
"roaring",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2766,7 +2807,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "json-depth-checker"
|
name = "json-depth-checker"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2835,9 +2876,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.155"
|
version = "0.2.164"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
@@ -3221,9 +3262,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.12"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "liquid"
|
name = "liquid"
|
||||||
@@ -3264,7 +3305,7 @@ checksum = "915f6d0a2963a27cd5205c1902f32ddfe3bc035816afd268cf88c0fc0f8d287e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3368,7 +3409,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3385,7 +3426,7 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meili-snap"
|
name = "meili-snap"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"insta",
|
"insta",
|
||||||
"md5",
|
"md5",
|
||||||
@@ -3394,7 +3435,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilisearch"
|
name = "meilisearch"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-cors",
|
"actix-cors",
|
||||||
"actix-http",
|
"actix-http",
|
||||||
@@ -3484,7 +3525,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilisearch-auth"
|
name = "meilisearch-auth"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
@@ -3503,10 +3544,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilisearch-types"
|
name = "meilisearch-types"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"bumpalo",
|
||||||
"convert_case 0.6.0",
|
"convert_case 0.6.0",
|
||||||
"csv",
|
"csv",
|
||||||
"deserr",
|
"deserr",
|
||||||
@@ -3519,6 +3561,7 @@ dependencies = [
|
|||||||
"meili-snap",
|
"meili-snap",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"milli",
|
"milli",
|
||||||
|
"raw-collections",
|
||||||
"roaring",
|
"roaring",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-cs",
|
"serde-cs",
|
||||||
@@ -3533,7 +3576,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilitool"
|
name = "meilitool"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arroy 0.5.0 (git+https://github.com/meilisearch/arroy/?tag=DO-NOT-DELETE-upgrade-v04-to-v05)",
|
"arroy 0.5.0 (git+https://github.com/meilisearch/arroy/?tag=DO-NOT-DELETE-upgrade-v04-to-v05)",
|
||||||
@@ -3555,9 +3598,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.9.4"
|
version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
|
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
@@ -3565,13 +3608,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "milli"
|
name = "milli"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
"arroy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arroy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bbqueue",
|
||||||
"big_s",
|
"big_s",
|
||||||
"bimap",
|
"bimap",
|
||||||
"bincode",
|
"bincode",
|
||||||
"bstr",
|
"bstr",
|
||||||
|
"bumpalo",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"candle-core",
|
"candle-core",
|
||||||
@@ -3583,12 +3629,15 @@ dependencies = [
|
|||||||
"csv",
|
"csv",
|
||||||
"deserr",
|
"deserr",
|
||||||
"either",
|
"either",
|
||||||
|
"enum-iterator",
|
||||||
"filter-parser",
|
"filter-parser",
|
||||||
"flatten-serde-json",
|
"flatten-serde-json",
|
||||||
|
"flume",
|
||||||
"fst",
|
"fst",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"geoutils",
|
"geoutils",
|
||||||
"grenad",
|
"grenad",
|
||||||
|
"hashbrown 0.15.1",
|
||||||
"heed",
|
"heed",
|
||||||
"hf-hub",
|
"hf-hub",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@@ -3607,11 +3656,13 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"rand",
|
"rand",
|
||||||
|
"raw-collections",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rayon-par-bridge",
|
"rayon-par-bridge",
|
||||||
"rhai",
|
"rhai",
|
||||||
"roaring",
|
"roaring",
|
||||||
"rstar",
|
"rstar",
|
||||||
|
"rustc-hash 2.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"slice-group-by",
|
"slice-group-by",
|
||||||
@@ -3620,10 +3671,12 @@ dependencies = [
|
|||||||
"smartstring",
|
"smartstring",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"thread_local",
|
||||||
"tiktoken-rs",
|
"tiktoken-rs",
|
||||||
"time",
|
"time",
|
||||||
"tokenizers",
|
"tokenizers",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uell",
|
||||||
"ureq",
|
"ureq",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -3699,7 +3752,7 @@ checksum = "371717c0a5543d6a800cac822eac735aa7d2d2fbb41002e9856a4089532dbdce"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3835,7 +3888,7 @@ dependencies = [
|
|||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3864,9 +3917,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obkv"
|
name = "obkv"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2e27bcfe835a379d32352112f6b8dbae2d99d16a5fff42abe6e5ba5386c1e5a"
|
checksum = "ae4512a8f418ac322335255a72361b9ac927e106f4d7fe6ab4d8ac59cb01f7a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
@@ -4011,7 +4064,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "permissive-json-pointer"
|
name = "permissive-json-pointer"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"big_s",
|
"big_s",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -4047,7 +4100,7 @@ dependencies = [
|
|||||||
"pest_meta",
|
"pest_meta",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4101,7 +4154,7 @@ dependencies = [
|
|||||||
"phf_shared",
|
"phf_shared",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4130,7 +4183,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4247,9 +4300,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.81"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -4434,6 +4487,19 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-collections"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/meilisearch/raw-collections.git#15e5d7bdebc0c149b2a28b2454f307c717d07f8a"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"bitpacking",
|
||||||
|
"bumpalo",
|
||||||
|
"hashbrown 0.15.1",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-cpuid"
|
name = "raw-cpuid"
|
||||||
version = "10.7.0"
|
version = "10.7.0"
|
||||||
@@ -4631,7 +4697,7 @@ source = "git+https://github.com/rhaiscript/rhai?rev=ef3df63121d27aacd838f366f2b
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4680,9 +4746,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roaring"
|
name = "roaring"
|
||||||
version = "0.10.6"
|
version = "0.10.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f4b84ba6e838ceb47b41de5194a60244fac43d9fe03b71dbe8c5a201081d6d1"
|
checksum = "f81dc953b2244ddd5e7860cb0bb2a790494b898ef321d4aff8e260efab60cc88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@@ -4746,9 +4812,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.31"
|
version = "0.38.41"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
@@ -4873,9 +4939,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.209"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
|
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@@ -4891,23 +4957,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.209"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.120"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -5123,6 +5190,9 @@ name = "spin"
|
|||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spm_precompiled"
|
name = "spm_precompiled"
|
||||||
@@ -5190,7 +5260,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5212,9 +5282,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.60"
|
version = "2.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -5230,7 +5300,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5256,7 +5326,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5316,12 +5386,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.10.1"
|
version = "3.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@@ -5361,14 +5432,14 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -5513,7 +5584,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5645,7 +5716,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5740,6 +5811,15 @@ version = "0.1.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uell"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40de5982e28612e20330e77d81f1559b74f66caf3c7fc10b19ada4843f4b4fd7"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unescaper"
|
name = "unescaper"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -5946,9 +6026,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wana_kana"
|
name = "wana_kana"
|
||||||
version = "3.0.0"
|
version = "4.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "477976a5c56fb7b014795df5a2ce08d2de8bcd4d5980844c5bd3978a7fd1c30b"
|
checksum = "a74666202acfcb4f9b995be2e3e9f7f530deb65e05a1407b8d0b30c9c451238a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
@@ -5991,7 +6071,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6025,7 +6105,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@@ -6400,7 +6480,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"build-info",
|
"build-info",
|
||||||
@@ -6458,7 +6538,7 @@ checksum = "9e6936f0cce458098a201c245a11bef556c6a0181129c7034d10d76d1ec3a2b8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6479,7 +6559,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6499,7 +6579,7 @@ checksum = "e6a647510471d372f2e6c2e6b7219e44d8c574d24fdc11c610a61455782f18c3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -6520,7 +6600,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
23
Cargo.toml
23
Cargo.toml
@@ -22,7 +22,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "1.11.0"
|
version = "1.12.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>",
|
||||||
@@ -43,24 +43,3 @@ 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.lindera-ipadic-builder]
|
|
||||||
opt-level = 3
|
|
||||||
[profile.dev.package.encoding]
|
|
||||||
opt-level = 3
|
|
||||||
[profile.dev.package.yada]
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
[profile.release.package.lindera-ipadic-builder]
|
|
||||||
opt-level = 3
|
|
||||||
[profile.release.package.encoding]
|
|
||||||
opt-level = 3
|
|
||||||
[profile.release.package.yada]
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
[profile.bench.package.lindera-ipadic-builder]
|
|
||||||
opt-level = 3
|
|
||||||
[profile.bench.package.encoding]
|
|
||||||
opt-level = 3
|
|
||||||
[profile.bench.package.yada]
|
|
||||||
opt-level = 3
|
|
||||||
|
|||||||
@@ -12,16 +12,19 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
|
bumpalo = "3.16.0"
|
||||||
csv = "1.3.0"
|
csv = "1.3.0"
|
||||||
|
memmap2 = "0.9.5"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
mimalloc = { version = "0.1.43", default-features = false }
|
mimalloc = { version = "0.1.43", default-features = false }
|
||||||
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
||||||
|
tempfile = "3.14.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
criterion = { version = "0.5.1", features = ["html_reports"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rand_chacha = "0.3.1"
|
rand_chacha = "0.3.1"
|
||||||
roaring = "0.10.6"
|
roaring = "0.10.7"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main};
|
|||||||
use milli::update::Settings;
|
use milli::update::Settings;
|
||||||
use utils::Conf;
|
use utils::Conf;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main};
|
|||||||
use milli::update::Settings;
|
use milli::update::Settings;
|
||||||
use utils::Conf;
|
use utils::Conf;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main};
|
|||||||
use milli::update::Settings;
|
use milli::update::Settings;
|
||||||
use utils::Conf;
|
use utils::Conf;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::fs::{create_dir_all, remove_dir_all, File};
|
use std::fs::{create_dir_all, remove_dir_all, File};
|
||||||
use std::io::{self, BufRead, BufReader, Cursor, Read, Seek};
|
use std::io::{self, BufReader, BufWriter, Read};
|
||||||
use std::num::ParseFloatError;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr as _;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use bumpalo::Bump;
|
||||||
use criterion::BenchmarkId;
|
use criterion::BenchmarkId;
|
||||||
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use memmap2::Mmap;
|
||||||
use milli::heed::EnvOpenOptions;
|
use milli::heed::EnvOpenOptions;
|
||||||
use milli::update::{
|
use milli::update::new::indexer;
|
||||||
IndexDocuments, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Settings,
|
use milli::update::{IndexDocumentsMethod, IndexerConfig, Settings};
|
||||||
};
|
use milli::vector::EmbeddingConfigs;
|
||||||
use milli::{Criterion, Filter, Index, Object, TermsMatchingStrategy};
|
use milli::{Criterion, Filter, Index, Object, TermsMatchingStrategy};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
|
|
||||||
let mut options = EnvOpenOptions::new();
|
let mut options = EnvOpenOptions::new();
|
||||||
options.map_size(100 * 1024 * 1024 * 1024); // 100 GB
|
options.map_size(100 * 1024 * 1024 * 1024); // 100 GB
|
||||||
options.max_readers(10);
|
options.max_readers(100);
|
||||||
let index = Index::new(options, conf.database_name).unwrap();
|
let index = Index::new(options, conf.database_name).unwrap();
|
||||||
|
|
||||||
let config = IndexerConfig::default();
|
let config = IndexerConfig::default();
|
||||||
@@ -92,18 +93,44 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
|
|
||||||
let config = IndexerConfig::default();
|
let config = IndexerConfig::default();
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
let indexing_config = IndexDocumentsConfig {
|
let rtxn = index.read_txn().unwrap();
|
||||||
autogenerate_docids: conf.primary_key.is_none(),
|
let db_fields_ids_map = index.fields_ids_map(&rtxn).unwrap();
|
||||||
update_method: IndexDocumentsMethod::ReplaceDocuments,
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let builder =
|
|
||||||
IndexDocuments::new(&mut wtxn, &index, &config, indexing_config, |_| (), || false).unwrap();
|
|
||||||
let documents = documents_from(conf.dataset, conf.dataset_format);
|
let documents = documents_from(conf.dataset, conf.dataset_format);
|
||||||
let (builder, user_error) = builder.add_documents(documents).unwrap();
|
let mut indexer = indexer::DocumentOperation::new(IndexDocumentsMethod::ReplaceDocuments);
|
||||||
user_error.unwrap();
|
indexer.add_documents(&documents).unwrap();
|
||||||
builder.execute().unwrap();
|
|
||||||
|
let indexer_alloc = Bump::new();
|
||||||
|
let (document_changes, _operation_stats, primary_key) = indexer
|
||||||
|
.into_changes(
|
||||||
|
&indexer_alloc,
|
||||||
|
&index,
|
||||||
|
&rtxn,
|
||||||
|
None,
|
||||||
|
&mut new_fields_ids_map,
|
||||||
|
&|| false,
|
||||||
|
&|_progress| (),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
indexer::index(
|
||||||
|
&mut wtxn,
|
||||||
|
&index,
|
||||||
|
&milli::ThreadPoolNoAbortBuilder::new().build().unwrap(),
|
||||||
|
config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
primary_key,
|
||||||
|
&document_changes,
|
||||||
|
EmbeddingConfigs::default(),
|
||||||
|
&|| false,
|
||||||
|
&|_| (),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
|
drop(rtxn);
|
||||||
|
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
@@ -140,49 +167,96 @@ pub fn run_benches(c: &mut criterion::Criterion, confs: &[Conf]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn documents_from(filename: &str, filetype: &str) -> DocumentsBatchReader<impl BufRead + Seek> {
|
pub fn documents_from(filename: &str, filetype: &str) -> Mmap {
|
||||||
let reader = File::open(filename)
|
let file = File::open(filename)
|
||||||
.unwrap_or_else(|_| panic!("could not find the dataset in: {}", filename));
|
.unwrap_or_else(|_| panic!("could not find the dataset in: {filename}"));
|
||||||
let reader = BufReader::new(reader);
|
match filetype {
|
||||||
let documents = match filetype {
|
"csv" => documents_from_csv(file).unwrap(),
|
||||||
"csv" => documents_from_csv(reader).unwrap(),
|
"json" => documents_from_json(file).unwrap(),
|
||||||
"json" => documents_from_json(reader).unwrap(),
|
"jsonl" => documents_from_jsonl(file).unwrap(),
|
||||||
"jsonl" => documents_from_jsonl(reader).unwrap(),
|
otherwise => panic!("invalid update format {otherwise:?}"),
|
||||||
otherwise => panic!("invalid update format {:?}", otherwise),
|
}
|
||||||
};
|
|
||||||
DocumentsBatchReader::from_reader(Cursor::new(documents)).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn documents_from_jsonl(reader: impl BufRead) -> anyhow::Result<Vec<u8>> {
|
fn documents_from_jsonl(file: File) -> anyhow::Result<Mmap> {
|
||||||
let mut documents = DocumentsBatchBuilder::new(Vec::new());
|
unsafe { Mmap::map(&file).map_err(Into::into) }
|
||||||
|
}
|
||||||
|
|
||||||
for result in serde_json::Deserializer::from_reader(reader).into_iter::<Object>() {
|
fn documents_from_json(file: File) -> anyhow::Result<Mmap> {
|
||||||
let object = result?;
|
let reader = BufReader::new(file);
|
||||||
documents.append_json_object(&object)?;
|
let documents: Vec<milli::Object> = serde_json::from_reader(reader)?;
|
||||||
|
let mut output = tempfile::tempfile().map(BufWriter::new)?;
|
||||||
|
|
||||||
|
for document in documents {
|
||||||
|
serde_json::to_writer(&mut output, &document)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
documents.into_inner().map_err(Into::into)
|
let file = output.into_inner()?;
|
||||||
|
unsafe { Mmap::map(&file).map_err(Into::into) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn documents_from_json(reader: impl BufRead) -> anyhow::Result<Vec<u8>> {
|
fn documents_from_csv(file: File) -> anyhow::Result<Mmap> {
|
||||||
let mut documents = DocumentsBatchBuilder::new(Vec::new());
|
let output = tempfile::tempfile()?;
|
||||||
|
let mut output = BufWriter::new(output);
|
||||||
|
let mut reader = csv::ReaderBuilder::new().from_reader(file);
|
||||||
|
|
||||||
documents.append_json_array(reader)?;
|
let headers = reader.headers().context("while retrieving headers")?.clone();
|
||||||
|
let typed_fields: Vec<_> = headers.iter().map(parse_csv_header).collect();
|
||||||
|
let mut object: serde_json::Map<_, _> =
|
||||||
|
typed_fields.iter().map(|(k, _)| (k.to_string(), Value::Null)).collect();
|
||||||
|
|
||||||
documents.into_inner().map_err(Into::into)
|
let mut line = 0;
|
||||||
}
|
let mut record = csv::StringRecord::new();
|
||||||
|
while reader.read_record(&mut record).context("while reading a record")? {
|
||||||
|
// We increment here and not at the end of the loop
|
||||||
|
// to take the header offset into account.
|
||||||
|
line += 1;
|
||||||
|
|
||||||
fn documents_from_csv(reader: impl BufRead) -> anyhow::Result<Vec<u8>> {
|
// Reset the document values
|
||||||
let csv = csv::Reader::from_reader(reader);
|
object.iter_mut().for_each(|(_, v)| *v = Value::Null);
|
||||||
|
|
||||||
let mut documents = DocumentsBatchBuilder::new(Vec::new());
|
for (i, (name, atype)) in typed_fields.iter().enumerate() {
|
||||||
documents.append_csv(csv)?;
|
let value = &record[i];
|
||||||
|
let trimmed_value = value.trim();
|
||||||
|
let value = match atype {
|
||||||
|
AllowedType::Number if trimmed_value.is_empty() => Value::Null,
|
||||||
|
AllowedType::Number => {
|
||||||
|
match trimmed_value.parse::<i64>() {
|
||||||
|
Ok(integer) => Value::from(integer),
|
||||||
|
Err(_) => match trimmed_value.parse::<f64>() {
|
||||||
|
Ok(float) => Value::from(float),
|
||||||
|
Err(error) => {
|
||||||
|
anyhow::bail!("document format error on line {line}: {error}. For value: {value}")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AllowedType::Boolean if trimmed_value.is_empty() => Value::Null,
|
||||||
|
AllowedType::Boolean => match trimmed_value.parse::<bool>() {
|
||||||
|
Ok(bool) => Value::from(bool),
|
||||||
|
Err(error) => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"document format error on line {line}: {error}. For value: {value}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AllowedType::String if value.is_empty() => Value::Null,
|
||||||
|
AllowedType::String => Value::from(value),
|
||||||
|
};
|
||||||
|
|
||||||
documents.into_inner().map_err(Into::into)
|
*object.get_mut(name).expect("encountered an unknown field") = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_json::to_writer(&mut output, &object).context("while writing to disk")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = output.into_inner()?;
|
||||||
|
unsafe { Mmap::map(&output).map_err(Into::into) }
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AllowedType {
|
enum AllowedType {
|
||||||
String,
|
String,
|
||||||
|
Boolean,
|
||||||
Number,
|
Number,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +265,9 @@ fn parse_csv_header(header: &str) -> (String, AllowedType) {
|
|||||||
match header.rsplit_once(':') {
|
match header.rsplit_once(':') {
|
||||||
Some((field_name, field_type)) => match field_type {
|
Some((field_name, field_type)) => match field_type {
|
||||||
"string" => (field_name.to_string(), AllowedType::String),
|
"string" => (field_name.to_string(), AllowedType::String),
|
||||||
|
"boolean" => (field_name.to_string(), AllowedType::Boolean),
|
||||||
"number" => (field_name.to_string(), AllowedType::Number),
|
"number" => (field_name.to_string(), AllowedType::Number),
|
||||||
// we may return an error in this case.
|
// if the pattern isn't recognized, we keep the whole field.
|
||||||
_otherwise => (header.to_string(), AllowedType::String),
|
_otherwise => (header.to_string(), AllowedType::String),
|
||||||
},
|
},
|
||||||
None => (header.to_string(), AllowedType::String),
|
None => (header.to_string(), AllowedType::String),
|
||||||
@@ -230,10 +305,13 @@ impl<R: Read> Iterator for CSVDocumentDeserializer<R> {
|
|||||||
for ((field_name, field_type), value) in
|
for ((field_name, field_type), value) in
|
||||||
self.headers.iter().zip(csv_document.into_iter())
|
self.headers.iter().zip(csv_document.into_iter())
|
||||||
{
|
{
|
||||||
let parsed_value: Result<Value, ParseFloatError> = match field_type {
|
let parsed_value: anyhow::Result<Value> = match field_type {
|
||||||
AllowedType::Number => {
|
AllowedType::Number => {
|
||||||
value.parse::<f64>().map(Value::from).map_err(Into::into)
|
value.parse::<f64>().map(Value::from).map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
AllowedType::Boolean => {
|
||||||
|
value.parse::<bool>().map(Value::from).map_err(Into::into)
|
||||||
|
}
|
||||||
AllowedType::String => Ok(Value::String(value.to_string())),
|
AllowedType::String => Ok(Value::String(value.to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ http = "1.1.0"
|
|||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
regex = "1.10.5"
|
regex = "1.10.5"
|
||||||
roaring = { version = "0.10.6", features = ["serde"] }
|
roaring = { version = "0.10.7", features = ["serde"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
||||||
tar = "0.4.41"
|
tar = "0.4.41"
|
||||||
|
|||||||
@@ -292,6 +292,8 @@ pub(crate) mod test {
|
|||||||
embedders: Setting::NotSet,
|
embedders: Setting::NotSet,
|
||||||
search_cutoff_ms: Setting::NotSet,
|
search_cutoff_ms: Setting::NotSet,
|
||||||
localized_attributes: Setting::NotSet,
|
localized_attributes: Setting::NotSet,
|
||||||
|
facet_search: Setting::NotSet,
|
||||||
|
prefix_search: Setting::NotSet,
|
||||||
_kind: std::marker::PhantomData,
|
_kind: std::marker::PhantomData,
|
||||||
};
|
};
|
||||||
settings.check()
|
settings.check()
|
||||||
|
|||||||
@@ -382,6 +382,8 @@ impl<T> From<v5::Settings<T>> for v6::Settings<v6::Unchecked> {
|
|||||||
embedders: v6::Setting::NotSet,
|
embedders: v6::Setting::NotSet,
|
||||||
localized_attributes: v6::Setting::NotSet,
|
localized_attributes: v6::Setting::NotSet,
|
||||||
search_cutoff_ms: v6::Setting::NotSet,
|
search_cutoff_ms: v6::Setting::NotSet,
|
||||||
|
facet_search: v6::Setting::NotSet,
|
||||||
|
prefix_search: v6::Setting::NotSet,
|
||||||
_kind: std::marker::PhantomData,
|
_kind: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||||
|
bumpalo = "3.16.0"
|
||||||
clap = { version = "4.5.9", features = ["derive"] }
|
clap = { version = "4.5.9", features = ["derive"] }
|
||||||
|
either = "1.13.0"
|
||||||
fastrand = "2.1.0"
|
fastrand = "2.1.0"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
|
|||||||
@@ -4,11 +4,17 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use arbitrary::{Arbitrary, Unstructured};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
use bumpalo::Bump;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use either::Either;
|
||||||
use fuzzers::Operation;
|
use fuzzers::Operation;
|
||||||
|
use milli::documents::mmap_from_objects;
|
||||||
use milli::heed::EnvOpenOptions;
|
use milli::heed::EnvOpenOptions;
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig};
|
use milli::update::new::indexer;
|
||||||
|
use milli::update::{IndexDocumentsMethod, IndexerConfig};
|
||||||
|
use milli::vector::EmbeddingConfigs;
|
||||||
use milli::Index;
|
use milli::Index;
|
||||||
|
use serde_json::Value;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
#[derive(Debug, Arbitrary)]
|
#[derive(Debug, Arbitrary)]
|
||||||
@@ -58,7 +64,6 @@ fn main() {
|
|||||||
};
|
};
|
||||||
let index = Index::new(options, tempdir.path()).unwrap();
|
let index = Index::new(options, tempdir.path()).unwrap();
|
||||||
let indexer_config = IndexerConfig::default();
|
let indexer_config = IndexerConfig::default();
|
||||||
let index_documents_config = IndexDocumentsConfig::default();
|
|
||||||
|
|
||||||
std::thread::scope(|s| {
|
std::thread::scope(|s| {
|
||||||
loop {
|
loop {
|
||||||
@@ -75,38 +80,72 @@ fn main() {
|
|||||||
|
|
||||||
let handle = s.spawn(|| {
|
let handle = s.spawn(|| {
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
|
let rtxn = index.read_txn().unwrap();
|
||||||
|
|
||||||
for batch in batches {
|
for batch in batches {
|
||||||
let mut builder = IndexDocuments::new(
|
let db_fields_ids_map = index.fields_ids_map(&rtxn).unwrap();
|
||||||
&mut wtxn,
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
&index,
|
|
||||||
&indexer_config,
|
|
||||||
index_documents_config.clone(),
|
|
||||||
|_| (),
|
|
||||||
|| false,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
|
let indexer_alloc = Bump::new();
|
||||||
|
let embedders = EmbeddingConfigs::default();
|
||||||
|
let mut indexer = indexer::DocumentOperation::new(
|
||||||
|
IndexDocumentsMethod::ReplaceDocuments,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut operations = Vec::new();
|
||||||
for op in batch.0 {
|
for op in batch.0 {
|
||||||
match op {
|
match op {
|
||||||
Operation::AddDoc(doc) => {
|
Operation::AddDoc(doc) => {
|
||||||
let documents =
|
let object = match doc.to_d() {
|
||||||
milli::documents::objects_from_json_value(doc.to_d());
|
Value::Object(object) => object,
|
||||||
let documents =
|
_ => unreachable!(),
|
||||||
milli::documents::documents_batch_reader_from_objects(
|
};
|
||||||
documents,
|
let documents = mmap_from_objects(vec![object]);
|
||||||
);
|
operations.push(Either::Left(documents));
|
||||||
let (b, _added) = builder.add_documents(documents).unwrap();
|
|
||||||
builder = b;
|
|
||||||
}
|
}
|
||||||
Operation::DeleteDoc(id) => {
|
Operation::DeleteDoc(id) => {
|
||||||
let (b, _removed) =
|
let id = indexer_alloc.alloc_str(&id.to_s());
|
||||||
builder.remove_documents(vec![id.to_s()]).unwrap();
|
let ids = indexer_alloc.alloc_slice_copy(&[&*id]);
|
||||||
builder = b;
|
operations.push(Either::Right(ids));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.execute().unwrap();
|
|
||||||
|
for op in &operations {
|
||||||
|
match op {
|
||||||
|
Either::Left(documents) => {
|
||||||
|
indexer.add_documents(documents).unwrap()
|
||||||
|
}
|
||||||
|
Either::Right(ids) => indexer.delete_documents(ids),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (document_changes, _operation_stats, primary_key) = indexer
|
||||||
|
.into_changes(
|
||||||
|
&indexer_alloc,
|
||||||
|
&index,
|
||||||
|
&rtxn,
|
||||||
|
None,
|
||||||
|
&mut new_fields_ids_map,
|
||||||
|
&|| false,
|
||||||
|
&|_progress| (),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
indexer::index(
|
||||||
|
&mut wtxn,
|
||||||
|
&index,
|
||||||
|
&milli::ThreadPoolNoAbortBuilder::new().build().unwrap(),
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
primary_key,
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| false,
|
||||||
|
&|_| (),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// after executing a batch we check if the database is corrupted
|
// after executing a batch we check if the database is corrupted
|
||||||
let res = index.search(&wtxn).execute().unwrap();
|
let res = index.search(&wtxn).execute().unwrap();
|
||||||
|
|||||||
@@ -22,13 +22,15 @@ flate2 = "1.0.30"
|
|||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
page_size = "0.6.0"
|
page_size = "0.6.0"
|
||||||
|
raw-collections = { git = "https://github.com/meilisearch/raw-collections.git", version = "0.1.0" }
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
roaring = { version = "0.10.6", features = ["serde"] }
|
roaring = { version = "0.10.7", features = ["serde"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
||||||
synchronoise = "1.0.1"
|
synchronoise = "1.0.1"
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
|
memmap2 = "0.9.4"
|
||||||
time = { version = "0.3.36", features = [
|
time = { version = "0.3.36", features = [
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
"formatting",
|
"formatting",
|
||||||
@@ -38,11 +40,12 @@ time = { version = "0.3.36", features = [
|
|||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
ureq = "2.10.0"
|
ureq = "2.10.0"
|
||||||
uuid = { version = "1.10.0", features = ["serde", "v4"] }
|
uuid = { version = "1.10.0", features = ["serde", "v4"] }
|
||||||
|
bumpalo = "3.16.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
arroy = "0.5.0"
|
arroy = "0.5.0"
|
||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
crossbeam = "0.8.4"
|
crossbeam-channel = "0.5.13"
|
||||||
insta = { version = "1.39.0", features = ["json", "redactions"] }
|
insta = { version = "1.39.0", features = ["json", "redactions"] }
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
meili-snap = { path = "../meili-snap" }
|
meili-snap = { path = "../meili-snap" }
|
||||||
|
|||||||
@@ -115,13 +115,6 @@ pub enum BatchKind {
|
|||||||
allow_index_creation: bool,
|
allow_index_creation: bool,
|
||||||
settings_ids: Vec<TaskId>,
|
settings_ids: Vec<TaskId>,
|
||||||
},
|
},
|
||||||
SettingsAndDocumentOperation {
|
|
||||||
settings_ids: Vec<TaskId>,
|
|
||||||
method: IndexDocumentsMethod,
|
|
||||||
allow_index_creation: bool,
|
|
||||||
primary_key: Option<String>,
|
|
||||||
operation_ids: Vec<TaskId>,
|
|
||||||
},
|
|
||||||
Settings {
|
Settings {
|
||||||
allow_index_creation: bool,
|
allow_index_creation: bool,
|
||||||
settings_ids: Vec<TaskId>,
|
settings_ids: Vec<TaskId>,
|
||||||
@@ -146,7 +139,6 @@ impl BatchKind {
|
|||||||
match self {
|
match self {
|
||||||
BatchKind::DocumentOperation { allow_index_creation, .. }
|
BatchKind::DocumentOperation { allow_index_creation, .. }
|
||||||
| BatchKind::ClearAndSettings { allow_index_creation, .. }
|
| BatchKind::ClearAndSettings { allow_index_creation, .. }
|
||||||
| BatchKind::SettingsAndDocumentOperation { allow_index_creation, .. }
|
|
||||||
| BatchKind::Settings { allow_index_creation, .. } => Some(*allow_index_creation),
|
| BatchKind::Settings { allow_index_creation, .. } => Some(*allow_index_creation),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -154,10 +146,7 @@ impl BatchKind {
|
|||||||
|
|
||||||
fn primary_key(&self) -> Option<Option<&str>> {
|
fn primary_key(&self) -> Option<Option<&str>> {
|
||||||
match self {
|
match self {
|
||||||
BatchKind::DocumentOperation { primary_key, .. }
|
BatchKind::DocumentOperation { primary_key, .. } => Some(primary_key.as_deref()),
|
||||||
| BatchKind::SettingsAndDocumentOperation { primary_key, .. } => {
|
|
||||||
Some(primary_key.as_deref())
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,8 +264,7 @@ impl BatchKind {
|
|||||||
Break(BatchKind::IndexDeletion { ids })
|
Break(BatchKind::IndexDeletion { ids })
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
BatchKind::ClearAndSettings { settings_ids: mut ids, allow_index_creation: _, mut other }
|
BatchKind::ClearAndSettings { settings_ids: mut ids, allow_index_creation: _, mut other },
|
||||||
| BatchKind::SettingsAndDocumentOperation { operation_ids: mut ids, method: _, allow_index_creation: _, primary_key: _, settings_ids: mut other },
|
|
||||||
K::IndexDeletion,
|
K::IndexDeletion,
|
||||||
) => {
|
) => {
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
@@ -356,15 +344,9 @@ impl BatchKind {
|
|||||||
) => Break(this),
|
) => Break(this),
|
||||||
|
|
||||||
(
|
(
|
||||||
BatchKind::DocumentOperation { method, allow_index_creation, primary_key, operation_ids },
|
this @ BatchKind::DocumentOperation { .. },
|
||||||
K::Settings { .. },
|
K::Settings { .. },
|
||||||
) => Continue(BatchKind::SettingsAndDocumentOperation {
|
) => Break(this),
|
||||||
settings_ids: vec![id],
|
|
||||||
method,
|
|
||||||
allow_index_creation,
|
|
||||||
primary_key,
|
|
||||||
operation_ids,
|
|
||||||
}),
|
|
||||||
|
|
||||||
(BatchKind::DocumentDeletion { mut deletion_ids, includes_by_filter: _ }, K::DocumentClear) => {
|
(BatchKind::DocumentDeletion { mut deletion_ids, includes_by_filter: _ }, K::DocumentClear) => {
|
||||||
deletion_ids.push(id);
|
deletion_ids.push(id);
|
||||||
@@ -477,63 +459,7 @@ impl BatchKind {
|
|||||||
allow_index_creation,
|
allow_index_creation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
(
|
|
||||||
BatchKind::SettingsAndDocumentOperation { settings_ids, method: _, mut operation_ids, allow_index_creation, primary_key: _ },
|
|
||||||
K::DocumentClear,
|
|
||||||
) => {
|
|
||||||
operation_ids.push(id);
|
|
||||||
Continue(BatchKind::ClearAndSettings {
|
|
||||||
settings_ids,
|
|
||||||
other: operation_ids,
|
|
||||||
allow_index_creation,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
BatchKind::SettingsAndDocumentOperation { settings_ids, method: ReplaceDocuments, mut operation_ids, allow_index_creation, primary_key: _},
|
|
||||||
K::DocumentImport { method: ReplaceDocuments, primary_key: pk2, .. },
|
|
||||||
) => {
|
|
||||||
operation_ids.push(id);
|
|
||||||
Continue(BatchKind::SettingsAndDocumentOperation {
|
|
||||||
settings_ids,
|
|
||||||
method: ReplaceDocuments,
|
|
||||||
allow_index_creation,
|
|
||||||
primary_key: pk2,
|
|
||||||
operation_ids,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(
|
|
||||||
BatchKind::SettingsAndDocumentOperation { settings_ids, method: UpdateDocuments, allow_index_creation, primary_key: _, mut operation_ids },
|
|
||||||
K::DocumentImport { method: UpdateDocuments, primary_key: pk2, .. },
|
|
||||||
) => {
|
|
||||||
operation_ids.push(id);
|
|
||||||
Continue(BatchKind::SettingsAndDocumentOperation {
|
|
||||||
settings_ids,
|
|
||||||
method: UpdateDocuments,
|
|
||||||
allow_index_creation,
|
|
||||||
primary_key: pk2,
|
|
||||||
operation_ids,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// But we can't batch a settings and a doc op with another doc op
|
|
||||||
// this MUST be AFTER the two previous branch
|
|
||||||
(
|
|
||||||
this @ BatchKind::SettingsAndDocumentOperation { .. },
|
|
||||||
K::DocumentDeletion { .. } | K::DocumentImport { .. },
|
|
||||||
) => Break(this),
|
|
||||||
(
|
|
||||||
BatchKind::SettingsAndDocumentOperation { mut settings_ids, method, allow_index_creation,primary_key, operation_ids },
|
|
||||||
K::Settings { .. },
|
|
||||||
) => {
|
|
||||||
settings_ids.push(id);
|
|
||||||
Continue(BatchKind::SettingsAndDocumentOperation {
|
|
||||||
settings_ids,
|
|
||||||
method,
|
|
||||||
allow_index_creation,
|
|
||||||
primary_key,
|
|
||||||
operation_ids,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(
|
(
|
||||||
BatchKind::IndexCreation { .. }
|
BatchKind::IndexCreation { .. }
|
||||||
| BatchKind::IndexDeletion { .. }
|
| BatchKind::IndexDeletion { .. }
|
||||||
@@ -808,30 +734,30 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn document_addition_batch_with_settings() {
|
fn document_addition_doesnt_batch_with_settings() {
|
||||||
// simple case
|
// simple case
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
|
|
||||||
// multiple settings and doc addition
|
// multiple settings and doc addition
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [2, 3], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [2, 3], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
||||||
|
|
||||||
// addition and setting unordered
|
// addition and setting unordered
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1, 3], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 2] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1, 3], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 2] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
|
|
||||||
// We ensure this kind of batch doesn't batch with forbidden operations
|
// Doesn't batch with other forbidden operations
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_del()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_del()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_del()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_del()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_create()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_create()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_create()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_create()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_update()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_update()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_update()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_update()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_swap()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_swap()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_swap()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_swap()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -859,8 +785,8 @@ mod tests {
|
|||||||
debug_snapshot!(autobatch_from(true, None, [doc_clr(), settings(true)]), @"Some((DocumentClear { ids: [0] }, false))");
|
debug_snapshot!(autobatch_from(true, None, [doc_clr(), settings(true)]), @"Some((DocumentClear { ids: [0] }, false))");
|
||||||
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [settings(true), doc_clr(), settings(true)]), @"Some((ClearAndSettings { other: [1], allow_index_creation: true, settings_ids: [0, 2] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [settings(true), doc_clr(), settings(true)]), @"Some((ClearAndSettings { other: [1], allow_index_creation: true, settings_ids: [0, 2] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr()]), @"Some((ClearAndSettings { other: [0, 2], allow_index_creation: true, settings_ids: [1] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr()]), @"Some((ClearAndSettings { other: [0, 2], allow_index_creation: true, settings_ids: [1] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -907,50 +833,6 @@ mod tests {
|
|||||||
debug_snapshot!(autobatch_from(false,None, [doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, false))");
|
debug_snapshot!(autobatch_from(false,None, [doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, false))");
|
||||||
debug_snapshot!(autobatch_from(false,None, [settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, true))");
|
debug_snapshot!(autobatch_from(false,None, [settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, true))");
|
||||||
debug_snapshot!(autobatch_from(false,None, [settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, false))");
|
debug_snapshot!(autobatch_from(false,None, [settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, false))");
|
||||||
|
|
||||||
// Then the mixed cases.
|
|
||||||
// The index already exists, whatever is the right of the tasks it shouldn't change the result.
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
|
|
||||||
// When the index doesn't exists yet it's more complicated.
|
|
||||||
// Either the first task we encounter create it, in which case we can create a big batch with everything.
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
// The right of the tasks following isn't really important.
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments,true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments,true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))");
|
|
||||||
// Or, the second case; the first task doesn't create the index and thus we wants to batch it with only tasks that can't create an index.
|
|
||||||
// that can be a second task that don't have the right to create an index. Or anything that can't create an index like an index deletion, document deletion, document clear, etc.
|
|
||||||
// All theses tasks are going to throw an error `Index doesn't exist` once the batch is processed.
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments,false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments,false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))");
|
|
||||||
// The third and final case is when the first task doesn't create an index but is directly followed by a task creating an index. In this case we can't batch whit what
|
|
||||||
// follows because we first need to process the erronous batch.
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments,false, None), settings(true), idx_del()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, false, None), settings(true), idx_del()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments,false, None), settings(true), doc_clr(), idx_del()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(UpdateDocuments, false, None), settings(true), doc_clr(), idx_del()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -959,13 +841,13 @@ mod tests {
|
|||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, false, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, false, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
||||||
|
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))");
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, false, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))");
|
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, false, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))");
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))");
|
||||||
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
debug_snapshot!(autobatch_from(false,None, [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))");
|
||||||
|
|
||||||
// batch deletion and addition
|
// batch deletion and addition
|
||||||
|
|||||||
@@ -22,22 +22,27 @@ use std::ffi::OsStr;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
|
use std::sync::atomic::{self, AtomicU64};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bumpalo::collections::CollectIn;
|
||||||
|
use bumpalo::Bump;
|
||||||
use dump::IndexMetadata;
|
use dump::IndexMetadata;
|
||||||
use meilisearch_types::batches::BatchId;
|
use meilisearch_types::batches::BatchId;
|
||||||
use meilisearch_types::error::Code;
|
use meilisearch_types::error::Code;
|
||||||
use meilisearch_types::heed::{RoTxn, RwTxn};
|
use meilisearch_types::heed::{RoTxn, RwTxn};
|
||||||
use meilisearch_types::milli::documents::{obkv_to_object, DocumentsBatchReader};
|
use meilisearch_types::milli::documents::{obkv_to_object, DocumentsBatchReader, PrimaryKey};
|
||||||
use meilisearch_types::milli::heed::CompactionOption;
|
use meilisearch_types::milli::heed::CompactionOption;
|
||||||
use meilisearch_types::milli::update::{
|
use meilisearch_types::milli::update::new::indexer::{self, UpdateByFunction};
|
||||||
IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Settings as MilliSettings,
|
use meilisearch_types::milli::update::{IndexDocumentsMethod, Settings as MilliSettings};
|
||||||
};
|
|
||||||
use meilisearch_types::milli::vector::parsed_vectors::{
|
use meilisearch_types::milli::vector::parsed_vectors::{
|
||||||
ExplicitVectors, VectorOrArrayOfVectors, RESERVED_VECTORS_FIELD_NAME,
|
ExplicitVectors, VectorOrArrayOfVectors, RESERVED_VECTORS_FIELD_NAME,
|
||||||
};
|
};
|
||||||
use meilisearch_types::milli::{self, Filter, Object};
|
use meilisearch_types::milli::{self, Filter, ThreadPoolNoAbortBuilder};
|
||||||
use meilisearch_types::settings::{apply_settings_to_builder, Settings, Unchecked};
|
use meilisearch_types::settings::{apply_settings_to_builder, Settings, Unchecked};
|
||||||
use meilisearch_types::tasks::{Details, IndexSwap, Kind, KindWithContent, Status, Task};
|
use meilisearch_types::tasks::{
|
||||||
|
Details, IndexSwap, Kind, KindWithContent, Status, Task, TaskProgress,
|
||||||
|
};
|
||||||
use meilisearch_types::{compression, Index, VERSION_FILE_NAME};
|
use meilisearch_types::{compression, Index, VERSION_FILE_NAME};
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
use time::macros::format_description;
|
use time::macros::format_description;
|
||||||
@@ -46,7 +51,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::autobatcher::{self, BatchKind};
|
use crate::autobatcher::{self, BatchKind};
|
||||||
use crate::utils::{self, swap_index_uid_in_task, ProcessingBatch};
|
use crate::utils::{self, swap_index_uid_in_task, ProcessingBatch};
|
||||||
use crate::{Error, IndexScheduler, MustStopProcessing, Result, TaskId};
|
use crate::{Error, IndexScheduler, Result, TaskId};
|
||||||
|
|
||||||
/// Represents a combination of tasks that can all be processed at the same time.
|
/// Represents a combination of tasks that can all be processed at the same time.
|
||||||
///
|
///
|
||||||
@@ -99,7 +104,6 @@ pub(crate) enum IndexOperation {
|
|||||||
index_uid: String,
|
index_uid: String,
|
||||||
primary_key: Option<String>,
|
primary_key: Option<String>,
|
||||||
method: IndexDocumentsMethod,
|
method: IndexDocumentsMethod,
|
||||||
documents_counts: Vec<u64>,
|
|
||||||
operations: Vec<DocumentOperation>,
|
operations: Vec<DocumentOperation>,
|
||||||
tasks: Vec<Task>,
|
tasks: Vec<Task>,
|
||||||
},
|
},
|
||||||
@@ -125,19 +129,6 @@ pub(crate) enum IndexOperation {
|
|||||||
index_uid: String,
|
index_uid: String,
|
||||||
cleared_tasks: Vec<Task>,
|
cleared_tasks: Vec<Task>,
|
||||||
|
|
||||||
// The boolean indicates if it's a settings deletion or creation.
|
|
||||||
settings: Vec<(bool, Settings<Unchecked>)>,
|
|
||||||
settings_tasks: Vec<Task>,
|
|
||||||
},
|
|
||||||
SettingsAndDocumentOperation {
|
|
||||||
index_uid: String,
|
|
||||||
|
|
||||||
primary_key: Option<String>,
|
|
||||||
method: IndexDocumentsMethod,
|
|
||||||
documents_counts: Vec<u64>,
|
|
||||||
operations: Vec<DocumentOperation>,
|
|
||||||
document_import_tasks: Vec<Task>,
|
|
||||||
|
|
||||||
// The boolean indicates if it's a settings deletion or creation.
|
// The boolean indicates if it's a settings deletion or creation.
|
||||||
settings: Vec<(bool, Settings<Unchecked>)>,
|
settings: Vec<(bool, Settings<Unchecked>)>,
|
||||||
settings_tasks: Vec<Task>,
|
settings_tasks: Vec<Task>,
|
||||||
@@ -169,12 +160,7 @@ impl Batch {
|
|||||||
IndexOperation::DocumentEdition { task, .. } => {
|
IndexOperation::DocumentEdition { task, .. } => {
|
||||||
RoaringBitmap::from_sorted_iter(std::iter::once(task.uid)).unwrap()
|
RoaringBitmap::from_sorted_iter(std::iter::once(task.uid)).unwrap()
|
||||||
}
|
}
|
||||||
IndexOperation::SettingsAndDocumentOperation {
|
IndexOperation::DocumentClearAndSetting {
|
||||||
document_import_tasks: tasks,
|
|
||||||
settings_tasks: other,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
| IndexOperation::DocumentClearAndSetting {
|
|
||||||
cleared_tasks: tasks,
|
cleared_tasks: tasks,
|
||||||
settings_tasks: other,
|
settings_tasks: other,
|
||||||
..
|
..
|
||||||
@@ -234,8 +220,7 @@ impl IndexOperation {
|
|||||||
| IndexOperation::DocumentDeletion { index_uid, .. }
|
| IndexOperation::DocumentDeletion { index_uid, .. }
|
||||||
| IndexOperation::DocumentClear { index_uid, .. }
|
| IndexOperation::DocumentClear { index_uid, .. }
|
||||||
| IndexOperation::Settings { index_uid, .. }
|
| IndexOperation::Settings { index_uid, .. }
|
||||||
| IndexOperation::DocumentClearAndSetting { index_uid, .. }
|
| IndexOperation::DocumentClearAndSetting { index_uid, .. } => index_uid,
|
||||||
| IndexOperation::SettingsAndDocumentOperation { index_uid, .. } => index_uid,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,9 +242,6 @@ impl fmt::Display for IndexOperation {
|
|||||||
IndexOperation::DocumentClearAndSetting { .. } => {
|
IndexOperation::DocumentClearAndSetting { .. } => {
|
||||||
f.write_str("IndexOperation::DocumentClearAndSetting")
|
f.write_str("IndexOperation::DocumentClearAndSetting")
|
||||||
}
|
}
|
||||||
IndexOperation::SettingsAndDocumentOperation { .. } => {
|
|
||||||
f.write_str("IndexOperation::SettingsAndDocumentOperation")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,21 +307,14 @@ impl IndexScheduler {
|
|||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let mut documents_counts = Vec::new();
|
|
||||||
let mut operations = Vec::new();
|
let mut operations = Vec::new();
|
||||||
|
|
||||||
for task in tasks.iter() {
|
for task in tasks.iter() {
|
||||||
match task.kind {
|
match task.kind {
|
||||||
KindWithContent::DocumentAdditionOrUpdate {
|
KindWithContent::DocumentAdditionOrUpdate { content_file, .. } => {
|
||||||
content_file,
|
|
||||||
documents_count,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
documents_counts.push(documents_count);
|
|
||||||
operations.push(DocumentOperation::Add(content_file));
|
operations.push(DocumentOperation::Add(content_file));
|
||||||
}
|
}
|
||||||
KindWithContent::DocumentDeletion { ref documents_ids, .. } => {
|
KindWithContent::DocumentDeletion { ref documents_ids, .. } => {
|
||||||
documents_counts.push(documents_ids.len() as u64);
|
|
||||||
operations.push(DocumentOperation::Delete(documents_ids.clone()));
|
operations.push(DocumentOperation::Delete(documents_ids.clone()));
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -351,7 +326,6 @@ impl IndexScheduler {
|
|||||||
index_uid,
|
index_uid,
|
||||||
primary_key,
|
primary_key,
|
||||||
method,
|
method,
|
||||||
documents_counts,
|
|
||||||
operations,
|
operations,
|
||||||
tasks,
|
tasks,
|
||||||
},
|
},
|
||||||
@@ -436,67 +410,6 @@ impl IndexScheduler {
|
|||||||
must_create_index,
|
must_create_index,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
BatchKind::SettingsAndDocumentOperation {
|
|
||||||
settings_ids,
|
|
||||||
method,
|
|
||||||
allow_index_creation,
|
|
||||||
primary_key,
|
|
||||||
operation_ids,
|
|
||||||
} => {
|
|
||||||
let settings = self.create_next_batch_index(
|
|
||||||
rtxn,
|
|
||||||
index_uid.clone(),
|
|
||||||
BatchKind::Settings { settings_ids, allow_index_creation },
|
|
||||||
current_batch,
|
|
||||||
must_create_index,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let document_import = self.create_next_batch_index(
|
|
||||||
rtxn,
|
|
||||||
index_uid.clone(),
|
|
||||||
BatchKind::DocumentOperation {
|
|
||||||
method,
|
|
||||||
allow_index_creation,
|
|
||||||
primary_key,
|
|
||||||
operation_ids,
|
|
||||||
},
|
|
||||||
current_batch,
|
|
||||||
must_create_index,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match (document_import, settings) {
|
|
||||||
(
|
|
||||||
Some(Batch::IndexOperation {
|
|
||||||
op:
|
|
||||||
IndexOperation::DocumentOperation {
|
|
||||||
primary_key,
|
|
||||||
documents_counts,
|
|
||||||
operations,
|
|
||||||
tasks: document_import_tasks,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Some(Batch::IndexOperation {
|
|
||||||
op: IndexOperation::Settings { settings, tasks: settings_tasks, .. },
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) => Ok(Some(Batch::IndexOperation {
|
|
||||||
op: IndexOperation::SettingsAndDocumentOperation {
|
|
||||||
index_uid,
|
|
||||||
primary_key,
|
|
||||||
method,
|
|
||||||
documents_counts,
|
|
||||||
operations,
|
|
||||||
document_import_tasks,
|
|
||||||
settings,
|
|
||||||
settings_tasks,
|
|
||||||
},
|
|
||||||
must_create_index,
|
|
||||||
})),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BatchKind::IndexCreation { id } => {
|
BatchKind::IndexCreation { id } => {
|
||||||
let mut task = self.get_task(rtxn, id)?.ok_or(Error::CorruptedTaskQueue)?;
|
let mut task = self.get_task(rtxn, id)?.ok_or(Error::CorruptedTaskQueue)?;
|
||||||
current_batch.processing(Some(&mut task));
|
current_batch.processing(Some(&mut task));
|
||||||
@@ -584,7 +497,6 @@ impl IndexScheduler {
|
|||||||
// 5. We make a batch from the unprioritised tasks. Start by taking the next enqueued task.
|
// 5. We make a batch from the unprioritised tasks. Start by taking the next enqueued task.
|
||||||
let task_id = if let Some(task_id) = enqueued.min() { task_id } else { return Ok(None) };
|
let task_id = if let Some(task_id) = enqueued.min() { task_id } else { return Ok(None) };
|
||||||
let mut task = self.get_task(rtxn, task_id)?.ok_or(Error::CorruptedTaskQueue)?;
|
let mut task = self.get_task(rtxn, task_id)?.ok_or(Error::CorruptedTaskQueue)?;
|
||||||
current_batch.processing(Some(&mut task));
|
|
||||||
|
|
||||||
// If the task is not associated with any index, verify that it is an index swap and
|
// If the task is not associated with any index, verify that it is an index swap and
|
||||||
// create the batch directly. Otherwise, get the index name associated with the task
|
// create the batch directly. Otherwise, get the index name associated with the task
|
||||||
@@ -594,6 +506,7 @@ impl IndexScheduler {
|
|||||||
index_name
|
index_name
|
||||||
} else {
|
} else {
|
||||||
assert!(matches!(&task.kind, KindWithContent::IndexSwap { swaps } if swaps.is_empty()));
|
assert!(matches!(&task.kind, KindWithContent::IndexSwap { swaps } if swaps.is_empty()));
|
||||||
|
current_batch.processing(Some(&mut task));
|
||||||
return Ok(Some((Batch::IndexSwap { task }, current_batch)));
|
return Ok(Some((Batch::IndexSwap { task }, current_batch)));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -886,10 +799,8 @@ impl IndexScheduler {
|
|||||||
while let Some(doc) =
|
while let Some(doc) =
|
||||||
cursor.next_document().map_err(milli::Error::from)?
|
cursor.next_document().map_err(milli::Error::from)?
|
||||||
{
|
{
|
||||||
dump_content_file.push_document(&obkv_to_object(
|
dump_content_file
|
||||||
&doc,
|
.push_document(&obkv_to_object(doc, &documents_batch_index)?)?;
|
||||||
&documents_batch_index,
|
|
||||||
)?)?;
|
|
||||||
}
|
}
|
||||||
dump_content_file.flush()?;
|
dump_content_file.flush()?;
|
||||||
}
|
}
|
||||||
@@ -1021,7 +932,13 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
let mut index_wtxn = index.write_txn()?;
|
let mut index_wtxn = index.write_txn()?;
|
||||||
let tasks = self.apply_index_operation(&mut index_wtxn, &index, op)?;
|
let tasks = self.apply_index_operation(&mut index_wtxn, &index, op)?;
|
||||||
index_wtxn.commit()?;
|
|
||||||
|
{
|
||||||
|
let span = tracing::trace_span!(target: "indexing::scheduler", "commit");
|
||||||
|
let _entered = span.enter();
|
||||||
|
|
||||||
|
index_wtxn.commit()?;
|
||||||
|
}
|
||||||
|
|
||||||
// if the update processed successfully, we're going to store the new
|
// if the update processed successfully, we're going to store the new
|
||||||
// stats of the index. Since the tasks have already been processed and
|
// stats of the index. Since the tasks have already been processed and
|
||||||
@@ -1232,6 +1149,44 @@ impl IndexScheduler {
|
|||||||
index: &'i Index,
|
index: &'i Index,
|
||||||
operation: IndexOperation,
|
operation: IndexOperation,
|
||||||
) -> Result<Vec<Task>> {
|
) -> Result<Vec<Task>> {
|
||||||
|
let indexer_alloc = Bump::new();
|
||||||
|
|
||||||
|
let started_processing_at = std::time::Instant::now();
|
||||||
|
let secs_since_started_processing_at = AtomicU64::new(0);
|
||||||
|
const PRINT_SECS_DELTA: u64 = 5;
|
||||||
|
|
||||||
|
let processing_tasks = self.processing_tasks.clone();
|
||||||
|
let must_stop_processing = self.must_stop_processing.clone();
|
||||||
|
let send_progress = |progress| {
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
let elapsed = secs_since_started_processing_at.load(atomic::Ordering::Relaxed);
|
||||||
|
let previous = started_processing_at + Duration::from_secs(elapsed);
|
||||||
|
let elapsed = now - previous;
|
||||||
|
|
||||||
|
if elapsed.as_secs() < PRINT_SECS_DELTA {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
secs_since_started_processing_at
|
||||||
|
.store((now - started_processing_at).as_secs(), atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
let TaskProgress {
|
||||||
|
current_step,
|
||||||
|
finished_steps,
|
||||||
|
total_steps,
|
||||||
|
finished_substeps,
|
||||||
|
total_substeps,
|
||||||
|
} = processing_tasks.write().unwrap().update_progress(progress);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
current_step,
|
||||||
|
finished_steps,
|
||||||
|
total_steps,
|
||||||
|
finished_substeps,
|
||||||
|
total_substeps
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
match operation {
|
match operation {
|
||||||
IndexOperation::DocumentClear { mut tasks, .. } => {
|
IndexOperation::DocumentClear { mut tasks, .. } => {
|
||||||
let count = milli::update::ClearDocuments::new(index_wtxn, index).execute()?;
|
let count = milli::update::ClearDocuments::new(index_wtxn, index).execute()?;
|
||||||
@@ -1257,159 +1212,148 @@ impl IndexScheduler {
|
|||||||
index_uid: _,
|
index_uid: _,
|
||||||
primary_key,
|
primary_key,
|
||||||
method,
|
method,
|
||||||
documents_counts: _,
|
|
||||||
operations,
|
operations,
|
||||||
mut tasks,
|
mut tasks,
|
||||||
} => {
|
} => {
|
||||||
let started_processing_at = std::time::Instant::now();
|
// TODO: at some point, for better efficiency we might want to reuse the bumpalo for successive batches.
|
||||||
let mut primary_key_has_been_set = false;
|
// this is made difficult by the fact we're doing private clones of the index scheduler and sending it
|
||||||
let must_stop_processing = self.must_stop_processing.clone();
|
// to a fresh thread.
|
||||||
let indexer_config = self.index_mapper.indexer_config();
|
let mut content_files = Vec::new();
|
||||||
|
for operation in &operations {
|
||||||
if let Some(primary_key) = primary_key {
|
if let DocumentOperation::Add(content_uuid) = operation {
|
||||||
match index.primary_key(index_wtxn)? {
|
let content_file = self.file_store.get_update(*content_uuid)?;
|
||||||
// if a primary key was set AND had already been defined in the index
|
let mmap = unsafe { memmap2::Mmap::map(&content_file)? };
|
||||||
// but to a different value, we can make the whole batch fail.
|
if !mmap.is_empty() {
|
||||||
Some(pk) => {
|
content_files.push(mmap);
|
||||||
if primary_key != pk {
|
|
||||||
return Err(milli::Error::from(
|
|
||||||
milli::UserError::PrimaryKeyCannotBeChanged(pk.to_string()),
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if the primary key was set and there was no primary key set for this index
|
|
||||||
// we set it to the received value before starting the indexing process.
|
|
||||||
None => {
|
|
||||||
let mut builder =
|
|
||||||
milli::update::Settings::new(index_wtxn, index, indexer_config);
|
|
||||||
builder.set_primary_key(primary_key);
|
|
||||||
builder.execute(
|
|
||||||
|indexing_step| tracing::debug!(update = ?indexing_step),
|
|
||||||
|| must_stop_processing.clone().get(),
|
|
||||||
)?;
|
|
||||||
primary_key_has_been_set = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = IndexDocumentsConfig { update_method: method, ..Default::default() };
|
let rtxn = index.read_txn()?;
|
||||||
|
let db_fields_ids_map = index.fields_ids_map(&rtxn)?;
|
||||||
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
|
|
||||||
let embedder_configs = index.embedding_configs(index_wtxn)?;
|
let mut content_files_iter = content_files.iter();
|
||||||
// TODO: consider Arc'ing the map too (we only need read access + we'll be cloning it multiple times, so really makes sense)
|
let mut indexer = indexer::DocumentOperation::new(method);
|
||||||
let embedders = self.embedders(embedder_configs)?;
|
let embedders = index.embedding_configs(index_wtxn)?;
|
||||||
|
let embedders = self.embedders(embedders)?;
|
||||||
let mut builder = milli::update::IndexDocuments::new(
|
for operation in operations {
|
||||||
index_wtxn,
|
|
||||||
index,
|
|
||||||
indexer_config,
|
|
||||||
config,
|
|
||||||
|indexing_step| tracing::trace!(?indexing_step, "Update"),
|
|
||||||
|| must_stop_processing.get(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for (operation, task) in operations.into_iter().zip(tasks.iter_mut()) {
|
|
||||||
match operation {
|
match operation {
|
||||||
DocumentOperation::Add(content_uuid) => {
|
DocumentOperation::Add(_content_uuid) => {
|
||||||
let content_file = self.file_store.get_update(content_uuid)?;
|
let mmap = content_files_iter.next().unwrap();
|
||||||
let reader = DocumentsBatchReader::from_reader(content_file)
|
indexer.add_documents(mmap)?;
|
||||||
.map_err(milli::Error::from)?;
|
|
||||||
let (new_builder, user_result) = builder.add_documents(reader)?;
|
|
||||||
builder = new_builder;
|
|
||||||
|
|
||||||
builder = builder.with_embedders(embedders.clone());
|
|
||||||
|
|
||||||
let received_documents =
|
|
||||||
if let Some(Details::DocumentAdditionOrUpdate {
|
|
||||||
received_documents,
|
|
||||||
..
|
|
||||||
}) = task.details
|
|
||||||
{
|
|
||||||
received_documents
|
|
||||||
} else {
|
|
||||||
// In the case of a `documentAdditionOrUpdate` the details MUST be set
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
|
|
||||||
match user_result {
|
|
||||||
Ok(count) => {
|
|
||||||
task.status = Status::Succeeded;
|
|
||||||
task.details = Some(Details::DocumentAdditionOrUpdate {
|
|
||||||
received_documents,
|
|
||||||
indexed_documents: Some(count),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
task.status = Status::Failed;
|
|
||||||
task.details = Some(Details::DocumentAdditionOrUpdate {
|
|
||||||
received_documents,
|
|
||||||
indexed_documents: Some(0),
|
|
||||||
});
|
|
||||||
task.error = Some(milli::Error::from(e).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DocumentOperation::Delete(document_ids) => {
|
DocumentOperation::Delete(document_ids) => {
|
||||||
let (new_builder, user_result) =
|
let document_ids: bumpalo::collections::vec::Vec<_> = document_ids
|
||||||
builder.remove_documents(document_ids)?;
|
.iter()
|
||||||
builder = new_builder;
|
.map(|s| &*indexer_alloc.alloc_str(s))
|
||||||
// Uses Invariant: remove documents actually always returns Ok for the inner result
|
.collect_in(&indexer_alloc);
|
||||||
let count = user_result.unwrap();
|
indexer.delete_documents(document_ids.into_bump_slice());
|
||||||
let provided_ids =
|
|
||||||
if let Some(Details::DocumentDeletion { provided_ids, .. }) =
|
|
||||||
task.details
|
|
||||||
{
|
|
||||||
provided_ids
|
|
||||||
} else {
|
|
||||||
// In the case of a `documentAdditionOrUpdate` the details MUST be set
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
|
|
||||||
task.status = Status::Succeeded;
|
|
||||||
task.details = Some(Details::DocumentDeletion {
|
|
||||||
provided_ids,
|
|
||||||
deleted_documents: Some(count),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tasks.iter().all(|res| res.error.is_some()) {
|
let local_pool;
|
||||||
let addition = builder.execute()?;
|
let indexer_config = self.index_mapper.indexer_config();
|
||||||
tracing::info!(indexing_result = ?addition, processed_in = ?started_processing_at.elapsed(), "document indexing done");
|
let pool = match &indexer_config.thread_pool {
|
||||||
} else if primary_key_has_been_set {
|
Some(pool) => pool,
|
||||||
// Everything failed but we've set a primary key.
|
None => {
|
||||||
// We need to remove it.
|
local_pool = ThreadPoolNoAbortBuilder::new()
|
||||||
let mut builder =
|
.thread_name(|i| format!("indexing-thread-{i}"))
|
||||||
milli::update::Settings::new(index_wtxn, index, indexer_config);
|
.build()
|
||||||
builder.reset_primary_key();
|
.unwrap();
|
||||||
builder.execute(
|
&local_pool
|
||||||
|indexing_step| tracing::trace!(update = ?indexing_step),
|
}
|
||||||
|| must_stop_processing.clone().get(),
|
};
|
||||||
|
|
||||||
|
let (document_changes, operation_stats, primary_key) = indexer.into_changes(
|
||||||
|
&indexer_alloc,
|
||||||
|
index,
|
||||||
|
&rtxn,
|
||||||
|
primary_key.as_deref(),
|
||||||
|
&mut new_fields_ids_map,
|
||||||
|
&|| must_stop_processing.get(),
|
||||||
|
&send_progress,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut addition = 0;
|
||||||
|
for (stats, task) in operation_stats.into_iter().zip(&mut tasks) {
|
||||||
|
addition += stats.document_count;
|
||||||
|
match stats.error {
|
||||||
|
Some(error) => {
|
||||||
|
task.status = Status::Failed;
|
||||||
|
task.error = Some(milli::Error::UserError(error).into());
|
||||||
|
}
|
||||||
|
None => task.status = Status::Succeeded,
|
||||||
|
}
|
||||||
|
|
||||||
|
task.details = match task.details {
|
||||||
|
Some(Details::DocumentAdditionOrUpdate { received_documents, .. }) => {
|
||||||
|
Some(Details::DocumentAdditionOrUpdate {
|
||||||
|
received_documents,
|
||||||
|
indexed_documents: Some(stats.document_count),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(Details::DocumentDeletion { provided_ids, .. }) => {
|
||||||
|
Some(Details::DocumentDeletion {
|
||||||
|
provided_ids,
|
||||||
|
deleted_documents: Some(stats.document_count),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// In the case of a `documentAdditionOrUpdate` or `DocumentDeletion`
|
||||||
|
// the details MUST be set to either addition or deletion
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tasks.iter().any(|res| res.error.is_none()) {
|
||||||
|
indexer::index(
|
||||||
|
index_wtxn,
|
||||||
|
index,
|
||||||
|
pool,
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
primary_key,
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| must_stop_processing.get(),
|
||||||
|
&send_progress,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
tracing::info!(indexing_result = ?addition, processed_in = ?started_processing_at.elapsed(), "document indexing done");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(tasks)
|
Ok(tasks)
|
||||||
}
|
}
|
||||||
IndexOperation::DocumentEdition { mut task, .. } => {
|
IndexOperation::DocumentEdition { mut task, .. } => {
|
||||||
let (filter, context, function) =
|
let (filter, code) = if let KindWithContent::DocumentEdition {
|
||||||
if let KindWithContent::DocumentEdition {
|
filter_expr,
|
||||||
filter_expr, context, function, ..
|
context: _,
|
||||||
} = &task.kind
|
|
||||||
{
|
|
||||||
(filter_expr, context, function)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
let result_count = edit_documents_by_function(
|
|
||||||
index_wtxn,
|
|
||||||
filter,
|
|
||||||
context.clone(),
|
|
||||||
function,
|
function,
|
||||||
self.index_mapper.indexer_config(),
|
..
|
||||||
self.must_stop_processing.clone(),
|
} = &task.kind
|
||||||
index,
|
{
|
||||||
);
|
(filter_expr, function)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let candidates = match filter.as_ref().map(Filter::from_json) {
|
||||||
|
Some(Ok(Some(filter))) => {
|
||||||
|
filter.evaluate(index_wtxn, index).map_err(|err| match err {
|
||||||
|
milli::Error::UserError(milli::UserError::InvalidFilter(_)) => {
|
||||||
|
Error::from(err).with_custom_error_code(Code::InvalidDocumentFilter)
|
||||||
|
}
|
||||||
|
e => e.into(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
None | Some(Ok(None)) => index.documents_ids(index_wtxn)?,
|
||||||
|
Some(Err(e)) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
let (original_filter, context, function) = if let Some(Details::DocumentEdition {
|
let (original_filter, context, function) = if let Some(Details::DocumentEdition {
|
||||||
original_filter,
|
original_filter,
|
||||||
context,
|
context,
|
||||||
@@ -1423,6 +1367,68 @@ impl IndexScheduler {
|
|||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if candidates.is_empty() {
|
||||||
|
task.status = Status::Succeeded;
|
||||||
|
task.details = Some(Details::DocumentEdition {
|
||||||
|
original_filter,
|
||||||
|
context,
|
||||||
|
function,
|
||||||
|
deleted_documents: Some(0),
|
||||||
|
edited_documents: Some(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(vec![task]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rtxn = index.read_txn()?;
|
||||||
|
let db_fields_ids_map = index.fields_ids_map(&rtxn)?;
|
||||||
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
|
// candidates not empty => index not empty => a primary key is set
|
||||||
|
let primary_key = index.primary_key(&rtxn)?.unwrap();
|
||||||
|
|
||||||
|
let primary_key = PrimaryKey::new_or_insert(primary_key, &mut new_fields_ids_map)
|
||||||
|
.map_err(milli::Error::from)?;
|
||||||
|
|
||||||
|
let result_count = Ok((candidates.len(), candidates.len())) as Result<_>;
|
||||||
|
|
||||||
|
if task.error.is_none() {
|
||||||
|
let local_pool;
|
||||||
|
let indexer_config = self.index_mapper.indexer_config();
|
||||||
|
let pool = match &indexer_config.thread_pool {
|
||||||
|
Some(pool) => pool,
|
||||||
|
None => {
|
||||||
|
local_pool = ThreadPoolNoAbortBuilder::new()
|
||||||
|
.thread_name(|i| format!("indexing-thread-{i}"))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
&local_pool
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let indexer = UpdateByFunction::new(candidates, context.clone(), code.clone());
|
||||||
|
let document_changes =
|
||||||
|
pool.install(|| indexer.into_changes(&primary_key)).unwrap()?;
|
||||||
|
|
||||||
|
let embedders = index.embedding_configs(index_wtxn)?;
|
||||||
|
let embedders = self.embedders(embedders)?;
|
||||||
|
|
||||||
|
indexer::index(
|
||||||
|
index_wtxn,
|
||||||
|
index,
|
||||||
|
pool,
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
None, // cannot change primary key in DocumentEdition
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| must_stop_processing.get(),
|
||||||
|
&send_progress,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// tracing::info!(indexing_result = ?addition, processed_in = ?started_processing_at.elapsed(), "document indexing done");
|
||||||
|
}
|
||||||
|
|
||||||
match result_count {
|
match result_count {
|
||||||
Ok((deleted_documents, edited_documents)) => {
|
Ok((deleted_documents, edited_documents)) => {
|
||||||
task.status = Status::Succeeded;
|
task.status = Status::Succeeded;
|
||||||
@@ -1523,26 +1529,56 @@ impl IndexScheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = IndexDocumentsConfig {
|
if to_delete.is_empty() {
|
||||||
update_method: IndexDocumentsMethod::ReplaceDocuments,
|
return Ok(tasks);
|
||||||
..Default::default()
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let must_stop_processing = self.must_stop_processing.clone();
|
let rtxn = index.read_txn()?;
|
||||||
let mut builder = milli::update::IndexDocuments::new(
|
let db_fields_ids_map = index.fields_ids_map(&rtxn)?;
|
||||||
index_wtxn,
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
index,
|
|
||||||
self.index_mapper.indexer_config(),
|
|
||||||
config,
|
|
||||||
|indexing_step| tracing::debug!(update = ?indexing_step),
|
|
||||||
|| must_stop_processing.get(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (new_builder, _count) =
|
// to_delete not empty => index not empty => primary key set
|
||||||
builder.remove_documents_from_db_no_batch(&to_delete)?;
|
let primary_key = index.primary_key(&rtxn)?.unwrap();
|
||||||
builder = new_builder;
|
|
||||||
|
|
||||||
let _ = builder.execute()?;
|
let primary_key = PrimaryKey::new_or_insert(primary_key, &mut new_fields_ids_map)
|
||||||
|
.map_err(milli::Error::from)?;
|
||||||
|
|
||||||
|
if !tasks.iter().all(|res| res.error.is_some()) {
|
||||||
|
let local_pool;
|
||||||
|
let indexer_config = self.index_mapper.indexer_config();
|
||||||
|
let pool = match &indexer_config.thread_pool {
|
||||||
|
Some(pool) => pool,
|
||||||
|
None => {
|
||||||
|
local_pool = ThreadPoolNoAbortBuilder::new()
|
||||||
|
.thread_name(|i| format!("indexing-thread-{i}"))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
&local_pool
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut indexer = indexer::DocumentDeletion::new();
|
||||||
|
indexer.delete_documents_by_docids(to_delete);
|
||||||
|
let document_changes = indexer.into_changes(&indexer_alloc, primary_key);
|
||||||
|
let embedders = index.embedding_configs(index_wtxn)?;
|
||||||
|
let embedders = self.embedders(embedders)?;
|
||||||
|
|
||||||
|
indexer::index(
|
||||||
|
index_wtxn,
|
||||||
|
index,
|
||||||
|
pool,
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
None, // document deletion never changes primary key
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| must_stop_processing.get(),
|
||||||
|
&send_progress,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// tracing::info!(indexing_result = ?addition, processed_in = ?started_processing_at.elapsed(), "document indexing done");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(tasks)
|
Ok(tasks)
|
||||||
}
|
}
|
||||||
@@ -1560,7 +1596,6 @@ impl IndexScheduler {
|
|||||||
task.status = Status::Succeeded;
|
task.status = Status::Succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
let must_stop_processing = self.must_stop_processing.clone();
|
|
||||||
builder.execute(
|
builder.execute(
|
||||||
|indexing_step| tracing::debug!(update = ?indexing_step),
|
|indexing_step| tracing::debug!(update = ?indexing_step),
|
||||||
|| must_stop_processing.get(),
|
|| must_stop_processing.get(),
|
||||||
@@ -1568,43 +1603,6 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
Ok(tasks)
|
Ok(tasks)
|
||||||
}
|
}
|
||||||
IndexOperation::SettingsAndDocumentOperation {
|
|
||||||
index_uid,
|
|
||||||
primary_key,
|
|
||||||
method,
|
|
||||||
documents_counts,
|
|
||||||
operations,
|
|
||||||
document_import_tasks,
|
|
||||||
settings,
|
|
||||||
settings_tasks,
|
|
||||||
} => {
|
|
||||||
let settings_tasks = self.apply_index_operation(
|
|
||||||
index_wtxn,
|
|
||||||
index,
|
|
||||||
IndexOperation::Settings {
|
|
||||||
index_uid: index_uid.clone(),
|
|
||||||
settings,
|
|
||||||
tasks: settings_tasks,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut import_tasks = self.apply_index_operation(
|
|
||||||
index_wtxn,
|
|
||||||
index,
|
|
||||||
IndexOperation::DocumentOperation {
|
|
||||||
index_uid,
|
|
||||||
primary_key,
|
|
||||||
method,
|
|
||||||
documents_counts,
|
|
||||||
operations,
|
|
||||||
tasks: document_import_tasks,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut tasks = settings_tasks;
|
|
||||||
tasks.append(&mut import_tasks);
|
|
||||||
Ok(tasks)
|
|
||||||
}
|
|
||||||
IndexOperation::DocumentClearAndSetting {
|
IndexOperation::DocumentClearAndSetting {
|
||||||
index_uid,
|
index_uid,
|
||||||
cleared_tasks,
|
cleared_tasks,
|
||||||
@@ -1784,44 +1782,3 @@ impl IndexScheduler {
|
|||||||
Ok(tasks)
|
Ok(tasks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edit_documents_by_function<'a>(
|
|
||||||
wtxn: &mut RwTxn<'a>,
|
|
||||||
filter: &Option<serde_json::Value>,
|
|
||||||
context: Option<Object>,
|
|
||||||
code: &str,
|
|
||||||
indexer_config: &IndexerConfig,
|
|
||||||
must_stop_processing: MustStopProcessing,
|
|
||||||
index: &'a Index,
|
|
||||||
) -> Result<(u64, u64)> {
|
|
||||||
let candidates = match filter.as_ref().map(Filter::from_json) {
|
|
||||||
Some(Ok(Some(filter))) => filter.evaluate(wtxn, index).map_err(|err| match err {
|
|
||||||
milli::Error::UserError(milli::UserError::InvalidFilter(_)) => {
|
|
||||||
Error::from(err).with_custom_error_code(Code::InvalidDocumentFilter)
|
|
||||||
}
|
|
||||||
e => e.into(),
|
|
||||||
})?,
|
|
||||||
None | Some(Ok(None)) => index.documents_ids(wtxn)?,
|
|
||||||
Some(Err(e)) => return Err(e.into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let config = IndexDocumentsConfig {
|
|
||||||
update_method: IndexDocumentsMethod::ReplaceDocuments,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut builder = milli::update::IndexDocuments::new(
|
|
||||||
wtxn,
|
|
||||||
index,
|
|
||||||
indexer_config,
|
|
||||||
config,
|
|
||||||
|indexing_step| tracing::debug!(update = ?indexing_step),
|
|
||||||
|| must_stop_processing.get(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (new_builder, count) = builder.edit_documents(&candidates, context, code)?;
|
|
||||||
builder = new_builder;
|
|
||||||
|
|
||||||
let _ = builder.execute()?;
|
|
||||||
Ok(count.unwrap())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -56,11 +56,12 @@ use meilisearch_types::heed::types::{SerdeBincode, SerdeJson, Str, I128};
|
|||||||
use meilisearch_types::heed::{self, Database, Env, PutFlags, RoTxn, RwTxn};
|
use meilisearch_types::heed::{self, Database, Env, PutFlags, RoTxn, RwTxn};
|
||||||
use meilisearch_types::milli::documents::DocumentsBatchBuilder;
|
use meilisearch_types::milli::documents::DocumentsBatchBuilder;
|
||||||
use meilisearch_types::milli::index::IndexEmbeddingConfig;
|
use meilisearch_types::milli::index::IndexEmbeddingConfig;
|
||||||
|
use meilisearch_types::milli::update::new::indexer::document_changes::Progress;
|
||||||
use meilisearch_types::milli::update::IndexerConfig;
|
use meilisearch_types::milli::update::IndexerConfig;
|
||||||
use meilisearch_types::milli::vector::{Embedder, EmbedderOptions, EmbeddingConfigs};
|
use meilisearch_types::milli::vector::{Embedder, EmbedderOptions, EmbeddingConfigs};
|
||||||
use meilisearch_types::milli::{self, CboRoaringBitmapCodec, Index, RoaringBitmapCodec, BEU32};
|
use meilisearch_types::milli::{self, CboRoaringBitmapCodec, Index, RoaringBitmapCodec, BEU32};
|
||||||
use meilisearch_types::task_view::TaskView;
|
use meilisearch_types::task_view::TaskView;
|
||||||
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task};
|
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task, TaskProgress};
|
||||||
use rayon::current_num_threads;
|
use rayon::current_num_threads;
|
||||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
@@ -167,12 +168,14 @@ pub struct ProcessingTasks {
|
|||||||
batch: Option<ProcessingBatch>,
|
batch: Option<ProcessingBatch>,
|
||||||
/// The list of tasks ids that are currently running.
|
/// The list of tasks ids that are currently running.
|
||||||
processing: RoaringBitmap,
|
processing: RoaringBitmap,
|
||||||
|
/// The progress on processing tasks
|
||||||
|
progress: Option<TaskProgress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessingTasks {
|
impl ProcessingTasks {
|
||||||
/// Creates an empty `ProcessingAt` struct.
|
/// Creates an empty `ProcessingAt` struct.
|
||||||
fn new() -> ProcessingTasks {
|
fn new() -> ProcessingTasks {
|
||||||
ProcessingTasks { batch: None, processing: RoaringBitmap::new() }
|
ProcessingTasks { batch: None, processing: RoaringBitmap::new(), progress: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores the currently processing tasks, and the date time at which it started.
|
/// Stores the currently processing tasks, and the date time at which it started.
|
||||||
@@ -181,11 +184,18 @@ impl ProcessingTasks {
|
|||||||
self.processing = processing;
|
self.processing = processing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_progress(&mut self, progress: Progress) -> TaskProgress {
|
||||||
|
self.progress.get_or_insert_with(TaskProgress::default).update(progress)
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the processing tasks to an empty list
|
/// Set the processing tasks to an empty list
|
||||||
fn stop_processing(&mut self) -> Self {
|
fn stop_processing(&mut self) -> Self {
|
||||||
|
self.progress = None;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
batch: std::mem::take(&mut self.batch),
|
batch: std::mem::take(&mut self.batch),
|
||||||
processing: std::mem::take(&mut self.processing),
|
processing: std::mem::take(&mut self.processing),
|
||||||
|
progress: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +407,7 @@ pub struct IndexScheduler {
|
|||||||
///
|
///
|
||||||
/// See [self.breakpoint()](`IndexScheduler::breakpoint`) for an explanation.
|
/// See [self.breakpoint()](`IndexScheduler::breakpoint`) for an explanation.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
test_breakpoint_sdr: crossbeam::channel::Sender<(Breakpoint, bool)>,
|
test_breakpoint_sdr: crossbeam_channel::Sender<(Breakpoint, bool)>,
|
||||||
|
|
||||||
/// A list of planned failures within the [`tick`](IndexScheduler::tick) method of the index scheduler.
|
/// A list of planned failures within the [`tick`](IndexScheduler::tick) method of the index scheduler.
|
||||||
///
|
///
|
||||||
@@ -466,7 +476,7 @@ impl IndexScheduler {
|
|||||||
/// Create an index scheduler and start its run loop.
|
/// Create an index scheduler and start its run loop.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
options: IndexSchedulerOptions,
|
options: IndexSchedulerOptions,
|
||||||
#[cfg(test)] test_breakpoint_sdr: crossbeam::channel::Sender<(Breakpoint, bool)>,
|
#[cfg(test)] test_breakpoint_sdr: crossbeam_channel::Sender<(Breakpoint, bool)>,
|
||||||
#[cfg(test)] planned_failures: Vec<(usize, tests::FailureLocation)>,
|
#[cfg(test)] planned_failures: Vec<(usize, tests::FailureLocation)>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
std::fs::create_dir_all(&options.tasks_path)?;
|
std::fs::create_dir_all(&options.tasks_path)?;
|
||||||
@@ -768,7 +778,7 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
/// Return the task ids matched by the given query from the index scheduler's point of view.
|
/// Return the task ids matched by the given query from the index scheduler's point of view.
|
||||||
pub(crate) fn get_task_ids(&self, rtxn: &RoTxn, query: &Query) -> Result<RoaringBitmap> {
|
pub(crate) fn get_task_ids(&self, rtxn: &RoTxn, query: &Query) -> Result<RoaringBitmap> {
|
||||||
let ProcessingTasks { batch: processing_batch, processing: processing_tasks } =
|
let ProcessingTasks { batch: processing_batch, processing: processing_tasks, progress: _ } =
|
||||||
self.processing_tasks.read().unwrap().clone();
|
self.processing_tasks.read().unwrap().clone();
|
||||||
let Query {
|
let Query {
|
||||||
limit,
|
limit,
|
||||||
@@ -1352,9 +1362,12 @@ impl IndexScheduler {
|
|||||||
let tasks =
|
let tasks =
|
||||||
self.get_existing_tasks(&rtxn, tasks.take(query.limit.unwrap_or(u32::MAX) as usize))?;
|
self.get_existing_tasks(&rtxn, tasks.take(query.limit.unwrap_or(u32::MAX) as usize))?;
|
||||||
|
|
||||||
let ProcessingTasks { batch, processing } =
|
let ProcessingTasks { batch, processing, progress } =
|
||||||
self.processing_tasks.read().map_err(|_| Error::CorruptedTaskQueue)?.clone();
|
self.processing_tasks.read().map_err(|_| Error::CorruptedTaskQueue)?.clone();
|
||||||
|
|
||||||
|
// ignored for now, might be added to batch details later
|
||||||
|
let _ = progress;
|
||||||
|
|
||||||
let ret = tasks.into_iter();
|
let ret = tasks.into_iter();
|
||||||
if processing.is_empty() || batch.is_none() {
|
if processing.is_empty() || batch.is_none() {
|
||||||
Ok((ret.collect(), total))
|
Ok((ret.collect(), total))
|
||||||
@@ -1427,7 +1440,7 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
// if the task doesn't delete anything and 50% of the task queue is full, we must refuse to enqueue the incomming task
|
// if the task doesn't delete anything and 50% of the task queue is full, we must refuse to enqueue the incomming task
|
||||||
if !matches!(&kind, KindWithContent::TaskDeletion { tasks, .. } if !tasks.is_empty())
|
if !matches!(&kind, KindWithContent::TaskDeletion { tasks, .. } if !tasks.is_empty())
|
||||||
&& (self.env.non_free_pages_size()? * 100) / self.env.info().map_size as u64 > 50
|
&& (self.env.non_free_pages_size()? * 100) / self.env.info().map_size as u64 > 40
|
||||||
{
|
{
|
||||||
return Err(Error::NoSpaceLeftInTaskQueue);
|
return Err(Error::NoSpaceLeftInTaskQueue);
|
||||||
}
|
}
|
||||||
@@ -1725,11 +1738,8 @@ impl IndexScheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.processing_tasks.write().unwrap().stop_processing();
|
|
||||||
// We must re-add the canceled task so they're part of the same batch.
|
// We must re-add the canceled task so they're part of the same batch.
|
||||||
// processed.processing |= canceled;
|
|
||||||
ids |= canceled;
|
ids |= canceled;
|
||||||
|
|
||||||
self.write_batch(&mut wtxn, processing_batch, &ids)?;
|
self.write_batch(&mut wtxn, processing_batch, &ids)?;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -1737,6 +1747,10 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
wtxn.commit().map_err(Error::HeedTransaction)?;
|
wtxn.commit().map_err(Error::HeedTransaction)?;
|
||||||
|
|
||||||
|
// We should stop processing AFTER everything is processed and written to disk otherwise, a batch (which only lives in RAM) may appear in the processing task
|
||||||
|
// and then become « not found » for some time until the commit everything is written and the final commit is made.
|
||||||
|
self.processing_tasks.write().unwrap().stop_processing();
|
||||||
|
|
||||||
// Once the tasks are committed, we should delete all the update files associated ASAP to avoid leaking files in case of a restart
|
// Once the tasks are committed, we should delete all the update files associated ASAP to avoid leaking files in case of a restart
|
||||||
tracing::debug!("Deleting the update files");
|
tracing::debug!("Deleting the update files");
|
||||||
|
|
||||||
@@ -2224,7 +2238,7 @@ mod tests {
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use big_s::S;
|
use big_s::S;
|
||||||
use crossbeam::channel::RecvTimeoutError;
|
use crossbeam_channel::RecvTimeoutError;
|
||||||
use file_store::File;
|
use file_store::File;
|
||||||
use insta::assert_json_snapshot;
|
use insta::assert_json_snapshot;
|
||||||
use maplit::btreeset;
|
use maplit::btreeset;
|
||||||
@@ -2276,7 +2290,7 @@ mod tests {
|
|||||||
configuration: impl Fn(&mut IndexSchedulerOptions),
|
configuration: impl Fn(&mut IndexSchedulerOptions),
|
||||||
) -> (Self, IndexSchedulerHandle) {
|
) -> (Self, IndexSchedulerHandle) {
|
||||||
let tempdir = TempDir::new().unwrap();
|
let tempdir = TempDir::new().unwrap();
|
||||||
let (sender, receiver) = crossbeam::channel::bounded(0);
|
let (sender, receiver) = crossbeam_channel::bounded(0);
|
||||||
|
|
||||||
let indexer_config = IndexerConfig { skip_index_budget: true, ..Default::default() };
|
let indexer_config = IndexerConfig { skip_index_budget: true, ..Default::default() };
|
||||||
|
|
||||||
@@ -2290,7 +2304,7 @@ mod tests {
|
|||||||
dumps_path: tempdir.path().join("dumps"),
|
dumps_path: tempdir.path().join("dumps"),
|
||||||
webhook_url: None,
|
webhook_url: None,
|
||||||
webhook_authorization_header: None,
|
webhook_authorization_header: None,
|
||||||
task_db_size: 1000 * 1000, // 1 MB, we don't use MiB on purpose.
|
task_db_size: 1000 * 1000 * 10, // 10 MB, we don't use MiB on purpose.
|
||||||
index_base_map_size: 1000 * 1000, // 1 MB, we don't use MiB on purpose.
|
index_base_map_size: 1000 * 1000, // 1 MB, we don't use MiB on purpose.
|
||||||
enable_mdb_writemap: false,
|
enable_mdb_writemap: false,
|
||||||
index_growth_amount: 1000 * 1000 * 1000 * 1000, // 1 TB
|
index_growth_amount: 1000 * 1000 * 1000 * 1000, // 1 TB
|
||||||
@@ -2408,7 +2422,7 @@ mod tests {
|
|||||||
pub struct IndexSchedulerHandle {
|
pub struct IndexSchedulerHandle {
|
||||||
_tempdir: TempDir,
|
_tempdir: TempDir,
|
||||||
index_scheduler: IndexScheduler,
|
index_scheduler: IndexScheduler,
|
||||||
test_breakpoint_rcv: crossbeam::channel::Receiver<(Breakpoint, bool)>,
|
test_breakpoint_rcv: crossbeam_channel::Receiver<(Breakpoint, bool)>,
|
||||||
last_breakpoint: Breakpoint,
|
last_breakpoint: Breakpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4305,10 +4319,35 @@ mod tests {
|
|||||||
let proc = index_scheduler.processing_tasks.read().unwrap().clone();
|
let proc = index_scheduler.processing_tasks.read().unwrap().clone();
|
||||||
|
|
||||||
let query = Query { statuses: Some(vec![Status::Processing]), ..Default::default() };
|
let query = Query { statuses: Some(vec![Status::Processing]), ..Default::default() };
|
||||||
let (batches, _) = index_scheduler
|
let (mut batches, _) = index_scheduler
|
||||||
.get_batch_ids_from_authorized_indexes(&rtxn, &proc, &query, &AuthFilter::default())
|
.get_batches_from_authorized_indexes(query.clone(), &AuthFilter::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
snapshot!(snapshot_bitmap(&batches), @"[0,]"); // only the processing batch in the first tick
|
assert_eq!(batches.len(), 1);
|
||||||
|
batches[0].started_at = OffsetDateTime::UNIX_EPOCH;
|
||||||
|
// Insta cannot snapshot our batches because the batch stats contains an enum as key: https://github.com/mitsuhiko/insta/issues/689
|
||||||
|
let batch = serde_json::to_string_pretty(&batches[0]).unwrap();
|
||||||
|
snapshot!(batch, @r#"
|
||||||
|
{
|
||||||
|
"uid": 0,
|
||||||
|
"details": {
|
||||||
|
"primaryKey": "mouse"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"totalNbTasks": 1,
|
||||||
|
"status": {
|
||||||
|
"processing": 1
|
||||||
|
},
|
||||||
|
"types": {
|
||||||
|
"indexCreation": 1
|
||||||
|
},
|
||||||
|
"indexUids": {
|
||||||
|
"catto": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"startedAt": "1970-01-01T00:00:00Z",
|
||||||
|
"finishedAt": null
|
||||||
|
}
|
||||||
|
"#);
|
||||||
|
|
||||||
let query = Query { statuses: Some(vec![Status::Enqueued]), ..Default::default() };
|
let query = Query { statuses: Some(vec![Status::Enqueued]), ..Default::default() };
|
||||||
let (batches, _) = index_scheduler
|
let (batches, _) = index_scheduler
|
||||||
@@ -5186,12 +5225,10 @@ mod tests {
|
|||||||
handle.advance_one_successful_batch();
|
handle.advance_one_successful_batch();
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "only_first_task_succeed");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "only_first_task_succeed");
|
||||||
|
|
||||||
// The second batch should fail.
|
handle.advance_one_successful_batch();
|
||||||
handle.advance_one_failed_batch();
|
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "second_task_fails");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "second_task_fails");
|
||||||
|
|
||||||
// The second batch should fail.
|
handle.advance_one_successful_batch();
|
||||||
handle.advance_one_failed_batch();
|
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "third_task_fails");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "third_task_fails");
|
||||||
|
|
||||||
// Is the primary key still what we expect?
|
// Is the primary key still what we expect?
|
||||||
@@ -5251,8 +5288,7 @@ mod tests {
|
|||||||
handle.advance_one_successful_batch();
|
handle.advance_one_successful_batch();
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "only_first_task_succeed");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "only_first_task_succeed");
|
||||||
|
|
||||||
// The second batch should fail and contains two tasks.
|
handle.advance_one_successful_batch();
|
||||||
handle.advance_one_failed_batch();
|
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "second_and_third_tasks_fails");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "second_and_third_tasks_fails");
|
||||||
|
|
||||||
// Is the primary key still what we expect?
|
// Is the primary key still what we expect?
|
||||||
@@ -5331,7 +5367,8 @@ mod tests {
|
|||||||
snapshot!(primary_key, @"id");
|
snapshot!(primary_key, @"id");
|
||||||
|
|
||||||
// We're trying to `bork` again, but now there is already a primary key set for this index.
|
// We're trying to `bork` again, but now there is already a primary key set for this index.
|
||||||
handle.advance_one_failed_batch();
|
// NOTE: it's marked as successful because the batch didn't fails, it's the individual tasks that failed.
|
||||||
|
handle.advance_one_successful_batch();
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "fourth_task_fails");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "fourth_task_fails");
|
||||||
|
|
||||||
// Finally the last task should succeed since its primary key is the same as the valid one.
|
// Finally the last task should succeed since its primary key is the same as the valid one.
|
||||||
@@ -5491,7 +5528,7 @@ mod tests {
|
|||||||
snapshot!(primary_key.is_none(), @"false");
|
snapshot!(primary_key.is_none(), @"false");
|
||||||
|
|
||||||
// The second batch should contains only one task that fails because it tries to update the primary key to `bork`.
|
// The second batch should contains only one task that fails because it tries to update the primary key to `bork`.
|
||||||
handle.advance_one_failed_batch();
|
handle.advance_one_successful_batch();
|
||||||
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "second_task_fails");
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "second_task_fails");
|
||||||
|
|
||||||
// The third batch should succeed and only contains one task.
|
// The third batch should succeed and only contains one task.
|
||||||
@@ -6136,9 +6173,10 @@ mod tests {
|
|||||||
|
|
||||||
let configs = index_scheduler.embedders(configs).unwrap();
|
let configs = index_scheduler.embedders(configs).unwrap();
|
||||||
let (hf_embedder, _, _) = configs.get(&simple_hf_name).unwrap();
|
let (hf_embedder, _, _) = configs.get(&simple_hf_name).unwrap();
|
||||||
let beagle_embed = hf_embedder.embed_one(S("Intel the beagle best doggo")).unwrap();
|
let beagle_embed =
|
||||||
let lab_embed = hf_embedder.embed_one(S("Max the lab best doggo")).unwrap();
|
hf_embedder.embed_one(S("Intel the beagle best doggo"), None).unwrap();
|
||||||
let patou_embed = hf_embedder.embed_one(S("kefir the patou best doggo")).unwrap();
|
let lab_embed = hf_embedder.embed_one(S("Max the lab best doggo"), None).unwrap();
|
||||||
|
let patou_embed = hf_embedder.embed_one(S("kefir the patou best doggo"), None).unwrap();
|
||||||
(fakerest_name, simple_hf_name, beagle_embed, lab_embed, patou_embed)
|
(fakerest_name, simple_hf_name, beagle_embed, lab_embed, patou_embed)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(1):
|
### Processing batch Some(1):
|
||||||
[1,]
|
[1,]
|
||||||
{uid: 1, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"beavero":2}}, }
|
{uid: 1, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"beavero":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(1):
|
### Processing batch Some(1):
|
||||||
[1,]
|
[1,]
|
||||||
{uid: 1, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"beavero":2}}, }
|
{uid: 1, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"beavero":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"dumpUid":null}, stats: {"totalNbTasks":1,"status":{"enqueued":1},"types":{"dumpCreation":1},"indexUids":{}}, }
|
{uid: 0, details: {"dumpUid":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"dumpCreation":1},"indexUids":{}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { dump_uid: None }, kind: DumpCreation { keys: [], instance_uid: None }}
|
0 {uid: 0, status: enqueued, details: { dump_uid: None }, kind: DumpCreation { keys: [], instance_uid: None }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"catto":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"catto":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"catto":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"catto":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"catto":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"catto":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, status: enqueued, details: { received_documents: 3, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
1 {uid: 1, status: enqueued, details: { received_documents: 3, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
||||||
2 {uid: 2, batch_uid: 2, status: succeeded, details: { received_document_ids: 1, deleted_documents: Some(1) }, kind: DocumentDeletion { index_uid: "doggos", documents_ids: ["1"] }}
|
2 {uid: 2, batch_uid: 2, status: succeeded, details: { received_document_ids: 1, deleted_documents: Some(1) }, kind: DocumentDeletion { index_uid: "doggos", documents_ids: ["1"] }}
|
||||||
3 {uid: 3, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Invalid type for filter subexpression: expected: String, Array, found: true.", error_code: "invalid_document_filter", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid_document_filter" }, details: { original_filter: true, deleted_documents: Some(0) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }}
|
3 {uid: 3, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Invalid type for filter subexpression: expected: String, Array, found: true.", error_code: "invalid_document_filter", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid_document_filter" }, details: { original_filter: true, deleted_documents: Some(0) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
||||||
2 {uid: 2, status: enqueued, details: { received_document_ids: 1, deleted_documents: None }, kind: DocumentDeletion { index_uid: "doggos", documents_ids: ["1"] }}
|
2 {uid: 2, status: enqueued, details: { received_document_ids: 1, deleted_documents: None }, kind: DocumentDeletion { index_uid: "doggos", documents_ids: ["1"] }}
|
||||||
3 {uid: 3, status: enqueued, details: { original_filter: true, deleted_documents: None }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }}
|
3 {uid: 3, status: enqueued, details: { original_filter: true, deleted_documents: None }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, status: enqueued, details: { received_documents: 3, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
1 {uid: 1, status: enqueued, details: { received_documents: 3, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, }
|
{uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
2 {uid: 2, batch_uid: 2, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: None, method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000001, documents_count: 1, allow_index_creation: true }}
|
2 {uid: 2, batch_uid: 2, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: None, method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000001, documents_count: 1, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
@@ -23,7 +22,7 @@ succeeded [0,1,2,]
|
|||||||
doggos [0,1,2,]
|
doggos [0,1,2,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Index Mapper:
|
### Index Mapper:
|
||||||
doggos: { number_of_documents: 1, field_distribution: {"_vectors": 1, "breed": 1, "doggo": 1, "id": 1} }
|
doggos: { number_of_documents: 1, field_distribution: {"breed": 1, "doggo": 1, "id": 1} }
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Canceled By:
|
### Canceled By:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
2 {uid: 2, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: None, method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000001, documents_count: 1, allow_index_creation: true }}
|
2 {uid: 2, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: None, method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000001, documents_count: 1, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
@@ -23,7 +22,7 @@ succeeded [0,1,]
|
|||||||
doggos [0,1,2,]
|
doggos [0,1,2,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Index Mapper:
|
### Index Mapper:
|
||||||
doggos: { number_of_documents: 1, field_distribution: {"_vectors": 1, "breed": 1, "doggo": 1, "id": 1} }
|
doggos: { number_of_documents: 1, field_distribution: {"breed": 1, "doggo": 1, "id": 1} }
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Canceled By:
|
### Canceled By:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
@@ -22,7 +21,7 @@ succeeded [0,1,]
|
|||||||
doggos [0,1,]
|
doggos [0,1,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Index Mapper:
|
### Index Mapper:
|
||||||
doggos: { number_of_documents: 1, field_distribution: {"_vectors": 1, "breed": 1, "doggo": 1, "id": 1} }
|
doggos: { number_of_documents: 1, field_distribution: {"breed": 1, "doggo": 1, "id": 1} }
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Canceled By:
|
### Canceled By:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
1 {uid: 1, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
1 {uid: 1, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,]
|
enqueued [0,]
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"index_a":2}}, }
|
{uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"index_a":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"index_a":2}}, }
|
{uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"index_a":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[0,]
|
[0,]
|
||||||
{uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"index_a":2}}, }
|
{uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"index_a":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(1):
|
### Processing batch Some(1):
|
||||||
[1,]
|
[1,]
|
||||||
{uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"doggo":2}}, }
|
{uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ snapshot_kind: text
|
|||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch Some(0):
|
### Processing batch Some(0):
|
||||||
[3,]
|
[3,]
|
||||||
{uid: 0, details: {"matchedTasks":2,"deletedTasks":null,"originalFilter":"test_query"}, stats: {"totalNbTasks":1,"status":{"enqueued":1},"types":{"taskDeletion":1},"indexUids":{}}, }
|
{uid: 0, details: {"matchedTasks":2,"deletedTasks":null,"originalFilter":"test_query"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"taskDeletion":1},"indexUids":{}}, }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,]
|
enqueued [0,]
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/lib.rs
|
source: crates/index-scheduler/src/lib.rs
|
||||||
snapshot_kind: text
|
|
||||||
---
|
---
|
||||||
### Autobatching Enabled = true
|
### Autobatching Enabled = true
|
||||||
### Processing batch None:
|
### Processing batch None:
|
||||||
[]
|
[]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Tasks:
|
### All Tasks:
|
||||||
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ impl ProcessingBatch {
|
|||||||
task.batch_uid = Some(self.uid);
|
task.batch_uid = Some(self.uid);
|
||||||
// We don't store the statuses in the map since they're all enqueued but we must
|
// We don't store the statuses in the map since they're all enqueued but we must
|
||||||
// still store them in the stats since that can be displayed.
|
// still store them in the stats since that can be displayed.
|
||||||
*self.stats.status.entry(task.status).or_default() += 1;
|
*self.stats.status.entry(Status::Processing).or_default() += 1;
|
||||||
|
|
||||||
self.kinds.insert(task.kind.as_kind());
|
self.kinds.insert(task.kind.as_kind());
|
||||||
*self.stats.types.entry(task.kind.as_kind()).or_default() += 1;
|
*self.stats.types.entry(task.kind.as_kind()).or_default() += 1;
|
||||||
@@ -106,7 +106,7 @@ impl ProcessingBatch {
|
|||||||
self.stats.total_nb_tasks = 0;
|
self.stats.total_nb_tasks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the timestamp of the tasks and the inner structure of this sturcture.
|
/// Update the timestamp of the tasks and the inner structure of this structure.
|
||||||
pub fn update(&mut self, task: &mut Task) {
|
pub fn update(&mut self, task: &mut Task) {
|
||||||
// We must re-set this value in case we're dealing with a task that has been added between
|
// We must re-set this value in case we're dealing with a task that has been added between
|
||||||
// the `processing` and `finished` state
|
// the `processing` and `finished` state
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
insta = { version = "^1.39.0", features = ["json", "redactions"] }
|
# fixed version due to format breakages in v1.40
|
||||||
|
insta = { version = "=1.39.0", features = ["json", "redactions"] }
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
once_cell = "1.19"
|
once_cell = "1.19"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ 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.6", features = ["serde"] }
|
roaring = { version = "0.10.7", features = ["serde"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ license.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.8.0", default-features = false }
|
actix-web = { version = "4.8.0", default-features = false }
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
|
bumpalo = "3.16.0"
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
csv = "1.3.0"
|
csv = "1.3.0"
|
||||||
deserr = { version = "0.6.2", features = ["actix-web"] }
|
deserr = { version = "0.6.2", features = ["actix-web"] }
|
||||||
@@ -23,7 +24,8 @@ flate2 = "1.0.30"
|
|||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
memmap2 = "0.9.4"
|
memmap2 = "0.9.4"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
roaring = { version = "0.10.6", features = ["serde"] }
|
raw-collections = { git = "https://github.com/meilisearch/raw-collections.git", version = "0.1.0" }
|
||||||
|
roaring = { version = "0.10.7", features = ["serde"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde-cs = "0.2.4"
|
serde-cs = "0.2.4"
|
||||||
serde_json = "1.0.120"
|
serde_json = "1.0.120"
|
||||||
@@ -70,4 +72,3 @@ swedish-recomposition = ["milli/swedish-recomposition"]
|
|||||||
german = ["milli/german"]
|
german = ["milli/german"]
|
||||||
# allow turkish normalization
|
# allow turkish normalization
|
||||||
turkish = ["milli/turkish"]
|
turkish = ["milli/turkish"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
use std::fmt::{self, Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, BufWriter, Write};
|
use std::io::{self, BufWriter};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use memmap2::MmapOptions;
|
use bumpalo::Bump;
|
||||||
use milli::documents::{DocumentsBatchBuilder, Error};
|
use memmap2::Mmap;
|
||||||
|
use milli::documents::Error;
|
||||||
use milli::Object;
|
use milli::Object;
|
||||||
|
use raw_collections::RawMap;
|
||||||
use serde::de::{SeqAccess, Visitor};
|
use serde::de::{SeqAccess, Visitor};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use serde_json::error::Category;
|
use serde_json::error::Category;
|
||||||
|
use serde_json::value::RawValue;
|
||||||
|
use serde_json::{to_writer, Map, Value};
|
||||||
|
|
||||||
use crate::error::{Code, ErrorCode};
|
use crate::error::{Code, ErrorCode};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, DocumentFormatError>;
|
type Result<T> = std::result::Result<T, DocumentFormatError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum PayloadType {
|
pub enum PayloadType {
|
||||||
Ndjson,
|
Ndjson,
|
||||||
Json,
|
Json,
|
||||||
@@ -88,6 +92,26 @@ impl From<(PayloadType, Error)> for DocumentFormatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(PayloadType, serde_json::Error)> for DocumentFormatError {
|
||||||
|
fn from((ty, error): (PayloadType, serde_json::Error)) -> Self {
|
||||||
|
if error.classify() == Category::Data {
|
||||||
|
Self::Io(error.into())
|
||||||
|
} else {
|
||||||
|
Self::MalformedPayload(Error::Json(error), ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(PayloadType, csv::Error)> for DocumentFormatError {
|
||||||
|
fn from((ty, error): (PayloadType, csv::Error)) -> Self {
|
||||||
|
if error.is_io_error() {
|
||||||
|
Self::Io(error.into())
|
||||||
|
} else {
|
||||||
|
Self::MalformedPayload(Error::Csv(error), ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<io::Error> for DocumentFormatError {
|
impl From<io::Error> for DocumentFormatError {
|
||||||
fn from(error: io::Error) -> Self {
|
fn from(error: io::Error) -> Self {
|
||||||
Self::Io(error)
|
Self::Io(error)
|
||||||
@@ -103,67 +127,152 @@ impl ErrorCode for DocumentFormatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads CSV from input and write an obkv batch to writer.
|
#[derive(Debug)]
|
||||||
pub fn read_csv(file: &File, writer: impl Write, delimiter: u8) -> Result<u64> {
|
enum AllowedType {
|
||||||
let mut builder = DocumentsBatchBuilder::new(BufWriter::new(writer));
|
String,
|
||||||
let mmap = unsafe { MmapOptions::new().map(file)? };
|
Boolean,
|
||||||
let csv = csv::ReaderBuilder::new().delimiter(delimiter).from_reader(mmap.as_ref());
|
Number,
|
||||||
builder.append_csv(csv).map_err(|e| (PayloadType::Csv { delimiter }, e))?;
|
|
||||||
|
|
||||||
let count = builder.documents_count();
|
|
||||||
let _ = builder.into_inner().map_err(DocumentFormatError::Io)?;
|
|
||||||
|
|
||||||
Ok(count as u64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads JSON from temporary file and write an obkv batch to writer.
|
fn parse_csv_header(header: &str) -> (&str, AllowedType) {
|
||||||
pub fn read_json(file: &File, writer: impl Write) -> Result<u64> {
|
// if there are several separators we only split on the last one.
|
||||||
let mut builder = DocumentsBatchBuilder::new(BufWriter::new(writer));
|
match header.rsplit_once(':') {
|
||||||
let mmap = unsafe { MmapOptions::new().map(file)? };
|
Some((field_name, field_type)) => match field_type {
|
||||||
let mut deserializer = serde_json::Deserializer::from_slice(&mmap);
|
"string" => (field_name, AllowedType::String),
|
||||||
|
"boolean" => (field_name, AllowedType::Boolean),
|
||||||
|
"number" => (field_name, AllowedType::Number),
|
||||||
|
// if the pattern isn't recognized, we keep the whole field.
|
||||||
|
_otherwise => (header, AllowedType::String),
|
||||||
|
},
|
||||||
|
None => (header, AllowedType::String),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match array_each(&mut deserializer, |obj| builder.append_json_object(&obj)) {
|
/// Reads CSV from file and write it in NDJSON in a file checking it along the way.
|
||||||
|
pub fn read_csv(input: &File, output: impl io::Write, delimiter: u8) -> Result<u64> {
|
||||||
|
let ptype = PayloadType::Csv { delimiter };
|
||||||
|
let mut output = BufWriter::new(output);
|
||||||
|
let mut reader = csv::ReaderBuilder::new().delimiter(delimiter).from_reader(input);
|
||||||
|
|
||||||
|
let headers = reader.headers().map_err(|e| DocumentFormatError::from((ptype, e)))?.clone();
|
||||||
|
let typed_fields: Vec<_> = headers.iter().map(parse_csv_header).collect();
|
||||||
|
let mut object: Map<_, _> =
|
||||||
|
typed_fields.iter().map(|(k, _)| (k.to_string(), Value::Null)).collect();
|
||||||
|
|
||||||
|
let mut line = 0;
|
||||||
|
let mut record = csv::StringRecord::new();
|
||||||
|
while reader.read_record(&mut record).map_err(|e| DocumentFormatError::from((ptype, e)))? {
|
||||||
|
// We increment here and not at the end of the loop
|
||||||
|
// to take the header offset into account.
|
||||||
|
line += 1;
|
||||||
|
|
||||||
|
// Reset the document values
|
||||||
|
object.iter_mut().for_each(|(_, v)| *v = Value::Null);
|
||||||
|
|
||||||
|
for (i, (name, atype)) in typed_fields.iter().enumerate() {
|
||||||
|
let value = &record[i];
|
||||||
|
let trimmed_value = value.trim();
|
||||||
|
let value = match atype {
|
||||||
|
AllowedType::Number if trimmed_value.is_empty() => Value::Null,
|
||||||
|
AllowedType::Number => match trimmed_value.parse::<i64>() {
|
||||||
|
Ok(integer) => Value::from(integer),
|
||||||
|
Err(_) => match trimmed_value.parse::<f64>() {
|
||||||
|
Ok(float) => Value::from(float),
|
||||||
|
Err(error) => {
|
||||||
|
return Err(DocumentFormatError::MalformedPayload(
|
||||||
|
Error::ParseFloat { error, line, value: value.to_string() },
|
||||||
|
ptype,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AllowedType::Boolean if trimmed_value.is_empty() => Value::Null,
|
||||||
|
AllowedType::Boolean => match trimmed_value.parse::<bool>() {
|
||||||
|
Ok(bool) => Value::from(bool),
|
||||||
|
Err(error) => {
|
||||||
|
return Err(DocumentFormatError::MalformedPayload(
|
||||||
|
Error::ParseBool { error, line, value: value.to_string() },
|
||||||
|
ptype,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AllowedType::String if value.is_empty() => Value::Null,
|
||||||
|
AllowedType::String => Value::from(value),
|
||||||
|
};
|
||||||
|
|
||||||
|
*object.get_mut(*name).expect("encountered an unknown field") = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_writer(&mut output, &object).map_err(|e| DocumentFormatError::from((ptype, e)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(line as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads JSON from file and write it in NDJSON in a file checking it along the way.
|
||||||
|
pub fn read_json(input: &File, output: impl io::Write) -> Result<u64> {
|
||||||
|
// We memory map to be able to deserialize into a RawMap that
|
||||||
|
// does not allocate when possible and only materialize the first/top level.
|
||||||
|
let input = unsafe { Mmap::map(input).map_err(DocumentFormatError::Io)? };
|
||||||
|
let mut doc_alloc = Bump::with_capacity(1024 * 1024); // 1MiB
|
||||||
|
|
||||||
|
let mut out = BufWriter::new(output);
|
||||||
|
let mut deserializer = serde_json::Deserializer::from_slice(&input);
|
||||||
|
let res = array_each(&mut deserializer, |obj: &RawValue| {
|
||||||
|
doc_alloc.reset();
|
||||||
|
let map = RawMap::from_raw_value(obj, &doc_alloc)?;
|
||||||
|
to_writer(&mut out, &map)
|
||||||
|
});
|
||||||
|
let count = match res {
|
||||||
// The json data has been deserialized and does not need to be processed again.
|
// The json data has been deserialized and does not need to be processed again.
|
||||||
// The data has been transferred to the writer during the deserialization process.
|
// The data has been transferred to the writer during the deserialization process.
|
||||||
Ok(Ok(_)) => (),
|
Ok(Ok(count)) => count,
|
||||||
Ok(Err(e)) => return Err(DocumentFormatError::Io(e)),
|
Ok(Err(e)) => return Err(DocumentFormatError::from((PayloadType::Json, e))),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Attempt to deserialize a single json string when the cause of the exception is not Category.data
|
// Attempt to deserialize a single json string when the cause of the exception is not Category.data
|
||||||
// Other types of deserialisation exceptions are returned directly to the front-end
|
// Other types of deserialisation exceptions are returned directly to the front-end
|
||||||
if e.classify() != serde_json::error::Category::Data {
|
if e.classify() != Category::Data {
|
||||||
return Err(DocumentFormatError::MalformedPayload(
|
return Err(DocumentFormatError::from((PayloadType::Json, e)));
|
||||||
Error::Json(e),
|
|
||||||
PayloadType::Json,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let content: Object = serde_json::from_slice(&mmap)
|
let content: Object = serde_json::from_slice(&input)
|
||||||
.map_err(Error::Json)
|
.map_err(Error::Json)
|
||||||
.map_err(|e| (PayloadType::Json, e))?;
|
.map_err(|e| (PayloadType::Json, e))?;
|
||||||
builder.append_json_object(&content).map_err(DocumentFormatError::Io)?;
|
to_writer(&mut out, &content)
|
||||||
|
.map(|_| 1)
|
||||||
|
.map_err(|e| DocumentFormatError::from((PayloadType::Json, e)))?
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match out.into_inner() {
|
||||||
|
Ok(_) => Ok(count),
|
||||||
|
Err(ie) => Err(DocumentFormatError::Io(ie.into_error())),
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = builder.documents_count();
|
|
||||||
let _ = builder.into_inner().map_err(DocumentFormatError::Io)?;
|
|
||||||
|
|
||||||
Ok(count as u64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads JSON from temporary file and write an obkv batch to writer.
|
/// Reads NDJSON from file and write it in NDJSON in a file checking it along the way.
|
||||||
pub fn read_ndjson(file: &File, writer: impl Write) -> Result<u64> {
|
pub fn read_ndjson(input: &File, output: impl io::Write) -> Result<u64> {
|
||||||
let mut builder = DocumentsBatchBuilder::new(BufWriter::new(writer));
|
// We memory map to be able to deserialize into a RawMap that
|
||||||
let mmap = unsafe { MmapOptions::new().map(file)? };
|
// does not allocate when possible and only materialize the first/top level.
|
||||||
|
let input = unsafe { Mmap::map(input).map_err(DocumentFormatError::Io)? };
|
||||||
|
let mut output = BufWriter::new(output);
|
||||||
|
|
||||||
for result in serde_json::Deserializer::from_slice(&mmap).into_iter() {
|
let mut bump = Bump::with_capacity(1024 * 1024);
|
||||||
let object = result.map_err(Error::Json).map_err(|e| (PayloadType::Ndjson, e))?;
|
|
||||||
builder.append_json_object(&object).map_err(Into::into).map_err(DocumentFormatError::Io)?;
|
let mut count = 0;
|
||||||
|
for result in serde_json::Deserializer::from_slice(&input).into_iter() {
|
||||||
|
bump.reset();
|
||||||
|
count += 1;
|
||||||
|
result
|
||||||
|
.and_then(|raw: &RawValue| {
|
||||||
|
// try to deserialize as a map
|
||||||
|
let map = RawMap::from_raw_value(raw, &bump)?;
|
||||||
|
to_writer(&mut output, &map)
|
||||||
|
})
|
||||||
|
.map_err(|e| DocumentFormatError::from((PayloadType::Ndjson, e)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = builder.documents_count();
|
Ok(count)
|
||||||
let _ = builder.into_inner().map_err(Into::into).map_err(DocumentFormatError::Io)?;
|
|
||||||
|
|
||||||
Ok(count as u64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The actual handling of the deserialization process in serde
|
/// The actual handling of the deserialization process in serde
|
||||||
@@ -172,20 +281,23 @@ pub fn read_ndjson(file: &File, writer: impl Write) -> Result<u64> {
|
|||||||
/// ## References
|
/// ## References
|
||||||
/// <https://serde.rs/stream-array.html>
|
/// <https://serde.rs/stream-array.html>
|
||||||
/// <https://github.com/serde-rs/json/issues/160>
|
/// <https://github.com/serde-rs/json/issues/160>
|
||||||
fn array_each<'de, D, T, F>(deserializer: D, f: F) -> std::result::Result<io::Result<u64>, D::Error>
|
fn array_each<'de, D, T, F>(
|
||||||
|
deserializer: D,
|
||||||
|
f: F,
|
||||||
|
) -> std::result::Result<serde_json::Result<u64>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
T: Deserialize<'de>,
|
T: Deserialize<'de>,
|
||||||
F: FnMut(T) -> io::Result<()>,
|
F: FnMut(T) -> serde_json::Result<()>,
|
||||||
{
|
{
|
||||||
struct SeqVisitor<T, F>(F, PhantomData<T>);
|
struct SeqVisitor<T, F>(F, PhantomData<T>);
|
||||||
|
|
||||||
impl<'de, T, F> Visitor<'de> for SeqVisitor<T, F>
|
impl<'de, T, F> Visitor<'de> for SeqVisitor<T, F>
|
||||||
where
|
where
|
||||||
T: Deserialize<'de>,
|
T: Deserialize<'de>,
|
||||||
F: FnMut(T) -> io::Result<()>,
|
F: FnMut(T) -> serde_json::Result<()>,
|
||||||
{
|
{
|
||||||
type Value = io::Result<u64>;
|
type Value = serde_json::Result<u64>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("a nonempty sequence")
|
formatter.write_str("a nonempty sequence")
|
||||||
@@ -194,7 +306,7 @@ where
|
|||||||
fn visit_seq<A>(
|
fn visit_seq<A>(
|
||||||
mut self,
|
mut self,
|
||||||
mut seq: A,
|
mut seq: A,
|
||||||
) -> std::result::Result<io::Result<u64>, <A as SeqAccess<'de>>::Error>
|
) -> std::result::Result<serde_json::Result<u64>, <A as SeqAccess<'de>>::Error>
|
||||||
where
|
where
|
||||||
A: SeqAccess<'de>,
|
A: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
@@ -203,7 +315,7 @@ where
|
|||||||
match self.0(value) {
|
match self.0(value) {
|
||||||
Ok(()) => max += 1,
|
Ok(()) => max += 1,
|
||||||
Err(e) => return Ok(Err(e)),
|
Err(e) => return Ok(Err(e)),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
Ok(Ok(max))
|
Ok(Ok(max))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ InvalidSearchPage , InvalidRequest , BAD_REQUEST ;
|
|||||||
InvalidSearchQ , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchQ , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidFacetSearchQuery , InvalidRequest , BAD_REQUEST ;
|
InvalidFacetSearchQuery , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidFacetSearchName , InvalidRequest , BAD_REQUEST ;
|
InvalidFacetSearchName , InvalidRequest , BAD_REQUEST ;
|
||||||
|
FacetSearchDisabled , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchVector , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchVector , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchShowMatchesPosition , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchShowMatchesPosition , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchShowRankingScore , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchShowRankingScore , InvalidRequest , BAD_REQUEST ;
|
||||||
@@ -290,6 +291,8 @@ InvalidSearchDistinct , InvalidRequest , BAD_REQUEST ;
|
|||||||
InvalidSettingsDisplayedAttributes , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsDisplayedAttributes , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsDistinctAttribute , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsDistinctAttribute , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsProximityPrecision , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsProximityPrecision , InvalidRequest , BAD_REQUEST ;
|
||||||
|
InvalidSettingsFacetSearch , InvalidRequest , BAD_REQUEST ;
|
||||||
|
InvalidSettingsPrefixSearch , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsFaceting , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsFaceting , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsFilterableAttributes , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsFilterableAttributes , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSettingsPagination , InvalidRequest , BAD_REQUEST ;
|
InvalidSettingsPagination , InvalidRequest , BAD_REQUEST ;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef};
|
use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef};
|
||||||
use fst::IntoStreamer;
|
use fst::IntoStreamer;
|
||||||
use milli::index::IndexEmbeddingConfig;
|
use milli::index::{IndexEmbeddingConfig, PrefixSearch};
|
||||||
use milli::proximity::ProximityPrecision;
|
use milli::proximity::ProximityPrecision;
|
||||||
use milli::update::Setting;
|
use milli::update::Setting;
|
||||||
use milli::{Criterion, CriterionError, Index, DEFAULT_VALUES_PER_FACET};
|
use milli::{Criterion, CriterionError, Index, DEFAULT_VALUES_PER_FACET};
|
||||||
@@ -202,6 +202,12 @@ pub struct Settings<T> {
|
|||||||
#[serde(default, skip_serializing_if = "Setting::is_not_set")]
|
#[serde(default, skip_serializing_if = "Setting::is_not_set")]
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSettingsLocalizedAttributes>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSettingsLocalizedAttributes>)]
|
||||||
pub localized_attributes: Setting<Vec<LocalizedAttributesRuleView>>,
|
pub localized_attributes: Setting<Vec<LocalizedAttributesRuleView>>,
|
||||||
|
#[serde(default, skip_serializing_if = "Setting::is_not_set")]
|
||||||
|
#[deserr(default, error = DeserrJsonError<InvalidSettingsFacetSearch>)]
|
||||||
|
pub facet_search: Setting<bool>,
|
||||||
|
#[serde(default, skip_serializing_if = "Setting::is_not_set")]
|
||||||
|
#[deserr(default, error = DeserrJsonError<InvalidSettingsPrefixSearch>)]
|
||||||
|
pub prefix_search: Setting<PrefixSearchSettings>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[deserr(skip)]
|
#[deserr(skip)]
|
||||||
@@ -266,6 +272,8 @@ impl Settings<Checked> {
|
|||||||
embedders: Setting::Reset,
|
embedders: Setting::Reset,
|
||||||
search_cutoff_ms: Setting::Reset,
|
search_cutoff_ms: Setting::Reset,
|
||||||
localized_attributes: Setting::Reset,
|
localized_attributes: Setting::Reset,
|
||||||
|
facet_search: Setting::Reset,
|
||||||
|
prefix_search: Setting::Reset,
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,6 +298,8 @@ impl Settings<Checked> {
|
|||||||
embedders,
|
embedders,
|
||||||
search_cutoff_ms,
|
search_cutoff_ms,
|
||||||
localized_attributes: localized_attributes_rules,
|
localized_attributes: localized_attributes_rules,
|
||||||
|
facet_search,
|
||||||
|
prefix_search,
|
||||||
_kind,
|
_kind,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
@@ -312,6 +322,8 @@ impl Settings<Checked> {
|
|||||||
embedders,
|
embedders,
|
||||||
search_cutoff_ms,
|
search_cutoff_ms,
|
||||||
localized_attributes: localized_attributes_rules,
|
localized_attributes: localized_attributes_rules,
|
||||||
|
facet_search,
|
||||||
|
prefix_search,
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,6 +372,8 @@ impl Settings<Unchecked> {
|
|||||||
embedders: self.embedders,
|
embedders: self.embedders,
|
||||||
search_cutoff_ms: self.search_cutoff_ms,
|
search_cutoff_ms: self.search_cutoff_ms,
|
||||||
localized_attributes: self.localized_attributes,
|
localized_attributes: self.localized_attributes,
|
||||||
|
facet_search: self.facet_search,
|
||||||
|
prefix_search: self.prefix_search,
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,6 +447,8 @@ impl Settings<Unchecked> {
|
|||||||
Setting::Set(this)
|
Setting::Set(this)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
prefix_search: other.prefix_search.or(self.prefix_search),
|
||||||
|
facet_search: other.facet_search.or(self.facet_search),
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,6 +485,8 @@ pub fn apply_settings_to_builder(
|
|||||||
embedders,
|
embedders,
|
||||||
search_cutoff_ms,
|
search_cutoff_ms,
|
||||||
localized_attributes: localized_attributes_rules,
|
localized_attributes: localized_attributes_rules,
|
||||||
|
facet_search,
|
||||||
|
prefix_search,
|
||||||
_kind,
|
_kind,
|
||||||
} = settings;
|
} = settings;
|
||||||
|
|
||||||
@@ -657,6 +675,20 @@ pub fn apply_settings_to_builder(
|
|||||||
Setting::Reset => builder.reset_search_cutoff(),
|
Setting::Reset => builder.reset_search_cutoff(),
|
||||||
Setting::NotSet => (),
|
Setting::NotSet => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match prefix_search {
|
||||||
|
Setting::Set(prefix_search) => {
|
||||||
|
builder.set_prefix_search(PrefixSearch::from(*prefix_search))
|
||||||
|
}
|
||||||
|
Setting::Reset => builder.reset_prefix_search(),
|
||||||
|
Setting::NotSet => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
match facet_search {
|
||||||
|
Setting::Set(facet_search) => builder.set_facet_search(*facet_search),
|
||||||
|
Setting::Reset => builder.reset_facet_search(),
|
||||||
|
Setting::NotSet => (),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SecretPolicy {
|
pub enum SecretPolicy {
|
||||||
@@ -755,6 +787,10 @@ pub fn settings(
|
|||||||
|
|
||||||
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 facet_search = index.facet_search(rtxn)?;
|
||||||
|
|
||||||
let mut settings = Settings {
|
let mut settings = Settings {
|
||||||
displayed_attributes: match displayed_attributes {
|
displayed_attributes: match displayed_attributes {
|
||||||
Some(attrs) => Setting::Set(attrs),
|
Some(attrs) => Setting::Set(attrs),
|
||||||
@@ -791,13 +827,14 @@ pub fn settings(
|
|||||||
Some(rules) => Setting::Set(rules.into_iter().map(|r| r.into()).collect()),
|
Some(rules) => Setting::Set(rules.into_iter().map(|r| r.into()).collect()),
|
||||||
None => Setting::Reset,
|
None => Setting::Reset,
|
||||||
},
|
},
|
||||||
|
prefix_search: Setting::Set(prefix_search.unwrap_or_default()),
|
||||||
|
facet_search: Setting::Set(facet_search),
|
||||||
_kind: PhantomData,
|
_kind: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let SecretPolicy::HideSecrets = secret_policy {
|
if let SecretPolicy::HideSecrets = secret_policy {
|
||||||
settings.hide_secrets()
|
settings.hide_secrets()
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -964,6 +1001,32 @@ impl std::ops::Deref for WildcardSetting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Deserr, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||||
|
#[deserr(error = DeserrJsonError<InvalidSettingsPrefixSearch>, rename_all = camelCase, deny_unknown_fields)]
|
||||||
|
pub enum PrefixSearchSettings {
|
||||||
|
#[default]
|
||||||
|
IndexingTime,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PrefixSearch> for PrefixSearchSettings {
|
||||||
|
fn from(value: PrefixSearch) -> Self {
|
||||||
|
match value {
|
||||||
|
PrefixSearch::IndexingTime => PrefixSearchSettings::IndexingTime,
|
||||||
|
PrefixSearch::Disabled => PrefixSearchSettings::Disabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<PrefixSearchSettings> for PrefixSearch {
|
||||||
|
fn from(value: PrefixSearchSettings) -> Self {
|
||||||
|
match value {
|
||||||
|
PrefixSearchSettings::IndexingTime => PrefixSearch::IndexingTime,
|
||||||
|
PrefixSearchSettings::Disabled => PrefixSearch::Disabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test {
|
pub(crate) mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -990,6 +1053,8 @@ pub(crate) mod test {
|
|||||||
embedders: Setting::NotSet,
|
embedders: Setting::NotSet,
|
||||||
localized_attributes: Setting::NotSet,
|
localized_attributes: Setting::NotSet,
|
||||||
search_cutoff_ms: Setting::NotSet,
|
search_cutoff_ms: Setting::NotSet,
|
||||||
|
facet_search: Setting::NotSet,
|
||||||
|
prefix_search: Setting::NotSet,
|
||||||
_kind: PhantomData::<Unchecked>,
|
_kind: PhantomData::<Unchecked>,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1019,6 +1084,8 @@ pub(crate) mod test {
|
|||||||
embedders: Setting::NotSet,
|
embedders: Setting::NotSet,
|
||||||
localized_attributes: Setting::NotSet,
|
localized_attributes: Setting::NotSet,
|
||||||
search_cutoff_ms: Setting::NotSet,
|
search_cutoff_ms: Setting::NotSet,
|
||||||
|
facet_search: Setting::NotSet,
|
||||||
|
prefix_search: Setting::NotSet,
|
||||||
_kind: PhantomData::<Unchecked>,
|
_kind: PhantomData::<Unchecked>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::fmt::{Display, Write};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use enum_iterator::Sequence;
|
use enum_iterator::Sequence;
|
||||||
|
use milli::update::new::indexer::document_changes::Progress;
|
||||||
use milli::update::IndexDocumentsMethod;
|
use milli::update::IndexDocumentsMethod;
|
||||||
use milli::Object;
|
use milli::Object;
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
@@ -40,6 +41,62 @@ pub struct Task {
|
|||||||
pub kind: KindWithContent,
|
pub kind: KindWithContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TaskProgress {
|
||||||
|
pub current_step: &'static str,
|
||||||
|
pub finished_steps: u16,
|
||||||
|
pub total_steps: u16,
|
||||||
|
pub finished_substeps: Option<u32>,
|
||||||
|
pub total_substeps: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TaskProgress {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaskProgress {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
current_step: "start",
|
||||||
|
finished_steps: 0,
|
||||||
|
total_steps: 1,
|
||||||
|
finished_substeps: None,
|
||||||
|
total_substeps: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, progress: Progress) -> TaskProgress {
|
||||||
|
if self.finished_steps > progress.finished_steps {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.current_step != progress.step_name {
|
||||||
|
self.current_step = progress.step_name
|
||||||
|
}
|
||||||
|
|
||||||
|
self.total_steps = progress.total_steps;
|
||||||
|
|
||||||
|
if self.finished_steps < progress.finished_steps {
|
||||||
|
self.finished_substeps = None;
|
||||||
|
self.total_substeps = None;
|
||||||
|
}
|
||||||
|
self.finished_steps = progress.finished_steps;
|
||||||
|
if let Some((finished_substeps, total_substeps)) = progress.finished_total_substep {
|
||||||
|
if let Some(task_finished_substeps) = self.finished_substeps {
|
||||||
|
if task_finished_substeps > finished_substeps {
|
||||||
|
return *self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.finished_substeps = Some(finished_substeps);
|
||||||
|
self.total_substeps = Some(total_substeps);
|
||||||
|
}
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
pub fn index_uid(&self) -> Option<&str> {
|
pub fn index_uid(&self) -> Option<&str> {
|
||||||
use KindWithContent::*;
|
use KindWithContent::*;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ meilisearch-types = { path = "../meilisearch-types" }
|
|||||||
mimalloc = { version = "0.1.43", default-features = false }
|
mimalloc = { version = "0.1.43", default-features = false }
|
||||||
mime = "0.3.17"
|
mime = "0.3.17"
|
||||||
num_cpus = "1.16.0"
|
num_cpus = "1.16.0"
|
||||||
obkv = "0.2.2"
|
obkv = "0.3.0"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
ordered-float = "4.2.1"
|
ordered-float = "4.2.1"
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
@@ -103,7 +103,7 @@ tracing-subscriber = { version = "0.3.18", 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.11"
|
tracing-actix-web = "0.7.11"
|
||||||
build-info = { version = "1.7.0", path = "../build-info" }
|
build-info = { version = "1.7.0", path = "../build-info" }
|
||||||
roaring = "0.10.2"
|
roaring = "0.10.7"
|
||||||
mopa-maintained = "0.2.3"
|
mopa-maintained = "0.2.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@@ -157,5 +157,5 @@ german = ["meilisearch-types/german"]
|
|||||||
turkish = ["meilisearch-types/turkish"]
|
turkish = ["meilisearch-types/turkish"]
|
||||||
|
|
||||||
[package.metadata.mini-dashboard]
|
[package.metadata.mini-dashboard]
|
||||||
assets-url = "https://github.com/meilisearch/mini-dashboard/releases/download/v0.2.15/build.zip"
|
assets-url = "https://github.com/meilisearch/mini-dashboard/releases/download/v0.2.16/build.zip"
|
||||||
sha1 = "d057600b4a839a2e0c0be7a372cd1b2683f3ca7e"
|
sha1 = "68f83438a114aabbe76bc9fe480071e741996662"
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ use meilisearch::{
|
|||||||
LogStderrType, Opt, SubscriberForSecondLayer,
|
LogStderrType, Opt, SubscriberForSecondLayer,
|
||||||
};
|
};
|
||||||
use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE};
|
use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE};
|
||||||
use mimalloc::MiMalloc;
|
|
||||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::level_filters::LevelFilter;
|
||||||
use tracing_subscriber::layer::SubscriberExt as _;
|
use tracing_subscriber::layer::SubscriberExt as _;
|
||||||
use tracing_subscriber::Layer;
|
use tracing_subscriber::Layer;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: MiMalloc = MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
fn default_log_route_layer() -> LogRouteType {
|
fn default_log_route_layer() -> LogRouteType {
|
||||||
None.with_filter(tracing_subscriber::filter::Targets::new().with_target("", LevelFilter::OFF))
|
None.with_filter(tracing_subscriber::filter::Targets::new().with_target("", LevelFilter::OFF))
|
||||||
|
|||||||
@@ -654,8 +654,9 @@ impl Opt {
|
|||||||
|
|
||||||
#[derive(Debug, Default, Clone, Parser, Deserialize)]
|
#[derive(Debug, Default, Clone, Parser, Deserialize)]
|
||||||
pub struct IndexerOpts {
|
pub struct IndexerOpts {
|
||||||
/// Sets the maximum amount of RAM Meilisearch can use when indexing. By default, Meilisearch
|
/// Specifies the maximum resident memory that Meilisearch can use for indexing.
|
||||||
/// uses no more than two thirds of available memory.
|
/// By default, Meilisearch limits the RAM usage to 5% of the total available memory.
|
||||||
|
/// Note that the underlying store utilizes memory-mapping and makes use of the rest.
|
||||||
#[clap(long, env = MEILI_MAX_INDEXING_MEMORY, default_value_t)]
|
#[clap(long, env = MEILI_MAX_INDEXING_MEMORY, default_value_t)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub max_indexing_memory: MaxMemory,
|
pub max_indexing_memory: MaxMemory,
|
||||||
@@ -714,7 +715,7 @@ impl TryFrom<&IndexerOpts> for IndexerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type used to detect the max memory available and use 2/3 of it.
|
/// A type used to detect the max resident memory available and use 5% of it.
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
||||||
pub struct MaxMemory(Option<Byte>);
|
pub struct MaxMemory(Option<Byte>);
|
||||||
|
|
||||||
@@ -728,7 +729,7 @@ impl FromStr for MaxMemory {
|
|||||||
|
|
||||||
impl Default for MaxMemory {
|
impl Default for MaxMemory {
|
||||||
fn default() -> MaxMemory {
|
fn default() -> MaxMemory {
|
||||||
MaxMemory(total_memory_bytes().map(|bytes| bytes * 2 / 3).map(Byte::from_u64))
|
MaxMemory(total_memory_bytes().map(|bytes| bytes * 5 / 100).map(Byte::from_u64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,32 @@ use crate::extractors::authentication::GuardedData;
|
|||||||
use crate::routes::{get_task_id, is_dry_run, SummarizedTaskView};
|
use crate::routes::{get_task_id, is_dry_run, SummarizedTaskView};
|
||||||
use crate::Opt;
|
use crate::Opt;
|
||||||
|
|
||||||
|
/// This macro generates the routes for the settings.
|
||||||
|
///
|
||||||
|
/// It takes a list of settings and generates a module for each setting.
|
||||||
|
/// Each module contains the `get`, `update` and `delete` routes for the setting.
|
||||||
|
///
|
||||||
|
/// It also generates a `configure` function that configures the routes for the settings.
|
||||||
|
macro_rules! make_setting_routes {
|
||||||
|
($({route: $route:literal, update_verb: $update_verb:ident, value_type: $type:ty, err_type: $err_ty:ty, attr: $attr:ident, camelcase_attr: $camelcase_attr:literal, analytics: $analytics:ident},)*) => {
|
||||||
|
$(
|
||||||
|
make_setting_route!($route, $update_verb, $type, $err_ty, $attr, $camelcase_attr, $analytics);
|
||||||
|
)*
|
||||||
|
|
||||||
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||||
|
use crate::extractors::sequential_extractor::SeqHandler;
|
||||||
|
cfg.service(
|
||||||
|
web::resource("")
|
||||||
|
.route(web::patch().to(SeqHandler(update_all)))
|
||||||
|
.route(web::get().to(SeqHandler(get_all)))
|
||||||
|
.route(web::delete().to(SeqHandler(delete_all))))
|
||||||
|
$(.service($attr::resources()))*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ALL_SETTINGS_NAMES: &[&str] = &[$(stringify!($attr)),*];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! make_setting_route {
|
macro_rules! make_setting_route {
|
||||||
($route:literal, $update_verb:ident, $type:ty, $err_ty:ty, $attr:ident, $camelcase_attr:literal, $analytics:ident) => {
|
($route:literal, $update_verb:ident, $type:ty, $err_ty:ty, $attr:ident, $camelcase_attr:literal, $analytics:ident) => {
|
||||||
@@ -153,255 +179,227 @@ macro_rules! make_setting_route {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
make_setting_route!(
|
make_setting_routes!(
|
||||||
"/filterable-attributes",
|
{
|
||||||
put,
|
route: "/filterable-attributes",
|
||||||
std::collections::BTreeSet<String>,
|
update_verb: put,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
value_type: std::collections::BTreeSet<String>,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
>,
|
meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes,
|
||||||
filterable_attributes,
|
>,
|
||||||
"filterableAttributes",
|
attr: filterable_attributes,
|
||||||
FilterableAttributesAnalytics
|
camelcase_attr: "filterableAttributes",
|
||||||
);
|
analytics: FilterableAttributesAnalytics
|
||||||
|
},
|
||||||
make_setting_route!(
|
{
|
||||||
"/sortable-attributes",
|
route: "/sortable-attributes",
|
||||||
put,
|
update_verb: put,
|
||||||
std::collections::BTreeSet<String>,
|
value_type: std::collections::BTreeSet<String>,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes,
|
meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes,
|
||||||
>,
|
>,
|
||||||
sortable_attributes,
|
attr: sortable_attributes,
|
||||||
"sortableAttributes",
|
camelcase_attr: "sortableAttributes",
|
||||||
SortableAttributesAnalytics
|
analytics: SortableAttributesAnalytics
|
||||||
);
|
},
|
||||||
|
{
|
||||||
make_setting_route!(
|
route: "/displayed-attributes",
|
||||||
"/displayed-attributes",
|
update_verb: put,
|
||||||
put,
|
value_type: Vec<String>,
|
||||||
Vec<String>,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes,
|
>,
|
||||||
>,
|
attr: displayed_attributes,
|
||||||
displayed_attributes,
|
camelcase_attr: "displayedAttributes",
|
||||||
"displayedAttributes",
|
analytics: DisplayedAttributesAnalytics
|
||||||
DisplayedAttributesAnalytics
|
},
|
||||||
);
|
{
|
||||||
|
route: "/typo-tolerance",
|
||||||
make_setting_route!(
|
update_verb: patch,
|
||||||
"/typo-tolerance",
|
value_type: meilisearch_types::settings::TypoSettings,
|
||||||
patch,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
meilisearch_types::settings::TypoSettings,
|
meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
>,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance,
|
attr: typo_tolerance,
|
||||||
>,
|
camelcase_attr: "typoTolerance",
|
||||||
typo_tolerance,
|
analytics: TypoToleranceAnalytics
|
||||||
"typoTolerance",
|
},
|
||||||
TypoToleranceAnalytics
|
{
|
||||||
);
|
route: "/searchable-attributes",
|
||||||
|
update_verb: put,
|
||||||
make_setting_route!(
|
value_type: Vec<String>,
|
||||||
"/searchable-attributes",
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
put,
|
meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes,
|
||||||
Vec<String>,
|
>,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
attr: searchable_attributes,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes,
|
camelcase_attr: "searchableAttributes",
|
||||||
>,
|
analytics: SearchableAttributesAnalytics
|
||||||
searchable_attributes,
|
},
|
||||||
"searchableAttributes",
|
{
|
||||||
SearchableAttributesAnalytics
|
route: "/stop-words",
|
||||||
);
|
update_verb: put,
|
||||||
|
value_type: std::collections::BTreeSet<String>,
|
||||||
make_setting_route!(
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
"/stop-words",
|
meilisearch_types::error::deserr_codes::InvalidSettingsStopWords,
|
||||||
put,
|
>,
|
||||||
std::collections::BTreeSet<String>,
|
attr: stop_words,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
camelcase_attr: "stopWords",
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsStopWords,
|
analytics: StopWordsAnalytics
|
||||||
>,
|
},
|
||||||
stop_words,
|
{
|
||||||
"stopWords",
|
route: "/non-separator-tokens",
|
||||||
StopWordsAnalytics
|
update_verb: put,
|
||||||
);
|
value_type: std::collections::BTreeSet<String>,
|
||||||
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
make_setting_route!(
|
meilisearch_types::error::deserr_codes::InvalidSettingsNonSeparatorTokens,
|
||||||
"/non-separator-tokens",
|
>,
|
||||||
put,
|
attr: non_separator_tokens,
|
||||||
std::collections::BTreeSet<String>,
|
camelcase_attr: "nonSeparatorTokens",
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
analytics: NonSeparatorTokensAnalytics
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsNonSeparatorTokens,
|
},
|
||||||
>,
|
{
|
||||||
non_separator_tokens,
|
route: "/separator-tokens",
|
||||||
"nonSeparatorTokens",
|
update_verb: put,
|
||||||
NonSeparatorTokensAnalytics
|
value_type: std::collections::BTreeSet<String>,
|
||||||
);
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
|
meilisearch_types::error::deserr_codes::InvalidSettingsSeparatorTokens,
|
||||||
make_setting_route!(
|
>,
|
||||||
"/separator-tokens",
|
attr: separator_tokens,
|
||||||
put,
|
camelcase_attr: "separatorTokens",
|
||||||
std::collections::BTreeSet<String>,
|
analytics: SeparatorTokensAnalytics
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
},
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsSeparatorTokens,
|
{
|
||||||
>,
|
route: "/dictionary",
|
||||||
separator_tokens,
|
update_verb: put,
|
||||||
"separatorTokens",
|
value_type: std::collections::BTreeSet<String>,
|
||||||
SeparatorTokensAnalytics
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
);
|
meilisearch_types::error::deserr_codes::InvalidSettingsDictionary,
|
||||||
|
>,
|
||||||
make_setting_route!(
|
attr: dictionary,
|
||||||
"/dictionary",
|
camelcase_attr: "dictionary",
|
||||||
put,
|
analytics: DictionaryAnalytics
|
||||||
std::collections::BTreeSet<String>,
|
},
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
{
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsDictionary,
|
route: "/synonyms",
|
||||||
>,
|
update_verb: put,
|
||||||
dictionary,
|
value_type: std::collections::BTreeMap<String, Vec<String>>,
|
||||||
"dictionary",
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
DictionaryAnalytics
|
meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms,
|
||||||
);
|
>,
|
||||||
|
attr: synonyms,
|
||||||
make_setting_route!(
|
camelcase_attr: "synonyms",
|
||||||
"/synonyms",
|
analytics: SynonymsAnalytics
|
||||||
put,
|
},
|
||||||
std::collections::BTreeMap<String, Vec<String>>,
|
{
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
route: "/distinct-attribute",
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms,
|
update_verb: put,
|
||||||
>,
|
value_type: String,
|
||||||
synonyms,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
"synonyms",
|
meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute,
|
||||||
SynonymsAnalytics
|
>,
|
||||||
);
|
attr: distinct_attribute,
|
||||||
|
camelcase_attr: "distinctAttribute",
|
||||||
make_setting_route!(
|
analytics: DistinctAttributeAnalytics
|
||||||
"/distinct-attribute",
|
},
|
||||||
put,
|
{
|
||||||
String,
|
route: "/proximity-precision",
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
update_verb: put,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute,
|
value_type: meilisearch_types::settings::ProximityPrecisionView,
|
||||||
>,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
distinct_attribute,
|
meilisearch_types::error::deserr_codes::InvalidSettingsProximityPrecision,
|
||||||
"distinctAttribute",
|
>,
|
||||||
DistinctAttributeAnalytics
|
attr: proximity_precision,
|
||||||
);
|
camelcase_attr: "proximityPrecision",
|
||||||
|
analytics: ProximityPrecisionAnalytics
|
||||||
make_setting_route!(
|
},
|
||||||
"/proximity-precision",
|
{
|
||||||
put,
|
route: "/localized-attributes",
|
||||||
meilisearch_types::settings::ProximityPrecisionView,
|
update_verb: put,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
value_type: Vec<meilisearch_types::locales::LocalizedAttributesRuleView>,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsProximityPrecision,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
>,
|
meilisearch_types::error::deserr_codes::InvalidSettingsLocalizedAttributes,
|
||||||
proximity_precision,
|
>,
|
||||||
"proximityPrecision",
|
attr: localized_attributes,
|
||||||
ProximityPrecisionAnalytics
|
camelcase_attr: "localizedAttributes",
|
||||||
);
|
analytics: LocalesAnalytics
|
||||||
|
},
|
||||||
make_setting_route!(
|
{
|
||||||
"/localized-attributes",
|
route: "/ranking-rules",
|
||||||
put,
|
update_verb: put,
|
||||||
Vec<meilisearch_types::locales::LocalizedAttributesRuleView>,
|
value_type: Vec<meilisearch_types::settings::RankingRuleView>,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsLocalizedAttributes,
|
meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules,
|
||||||
>,
|
>,
|
||||||
localized_attributes,
|
attr: ranking_rules,
|
||||||
"localizedAttributes",
|
camelcase_attr: "rankingRules",
|
||||||
LocalesAnalytics
|
analytics: RankingRulesAnalytics
|
||||||
);
|
},
|
||||||
|
{
|
||||||
make_setting_route!(
|
route: "/faceting",
|
||||||
"/ranking-rules",
|
update_verb: patch,
|
||||||
put,
|
value_type: meilisearch_types::settings::FacetingSettings,
|
||||||
Vec<meilisearch_types::settings::RankingRuleView>,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
meilisearch_types::error::deserr_codes::InvalidSettingsFaceting,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules,
|
>,
|
||||||
>,
|
attr: faceting,
|
||||||
ranking_rules,
|
camelcase_attr: "faceting",
|
||||||
"rankingRules",
|
analytics: FacetingAnalytics
|
||||||
RankingRulesAnalytics
|
},
|
||||||
);
|
{
|
||||||
|
route: "/pagination",
|
||||||
make_setting_route!(
|
update_verb: patch,
|
||||||
"/faceting",
|
value_type: meilisearch_types::settings::PaginationSettings,
|
||||||
patch,
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
meilisearch_types::settings::FacetingSettings,
|
meilisearch_types::error::deserr_codes::InvalidSettingsPagination,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
>,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsFaceting,
|
attr: pagination,
|
||||||
>,
|
camelcase_attr: "pagination",
|
||||||
faceting,
|
analytics: PaginationAnalytics
|
||||||
"faceting",
|
},
|
||||||
FacetingAnalytics
|
{
|
||||||
);
|
route: "/embedders",
|
||||||
|
update_verb: patch,
|
||||||
make_setting_route!(
|
value_type: std::collections::BTreeMap<String, Setting<meilisearch_types::milli::vector::settings::EmbeddingSettings>>,
|
||||||
"/pagination",
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
patch,
|
meilisearch_types::error::deserr_codes::InvalidSettingsEmbedders,
|
||||||
meilisearch_types::settings::PaginationSettings,
|
>,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
attr: embedders,
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsPagination,
|
camelcase_attr: "embedders",
|
||||||
>,
|
analytics: EmbeddersAnalytics
|
||||||
pagination,
|
},
|
||||||
"pagination",
|
{
|
||||||
PaginationAnalytics
|
route: "/search-cutoff-ms",
|
||||||
);
|
update_verb: put,
|
||||||
|
value_type: u64,
|
||||||
make_setting_route!(
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
"/embedders",
|
meilisearch_types::error::deserr_codes::InvalidSettingsSearchCutoffMs,
|
||||||
patch,
|
>,
|
||||||
std::collections::BTreeMap<String, Setting<meilisearch_types::milli::vector::settings::EmbeddingSettings>>,
|
attr: search_cutoff_ms,
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
camelcase_attr: "searchCutoffMs",
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsEmbedders,
|
analytics: SearchCutoffMsAnalytics
|
||||||
>,
|
},
|
||||||
embedders,
|
{
|
||||||
"embedders",
|
route: "/facet-search",
|
||||||
EmbeddersAnalytics
|
update_verb: put,
|
||||||
);
|
value_type: bool,
|
||||||
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
make_setting_route!(
|
meilisearch_types::error::deserr_codes::InvalidSettingsFacetSearch,
|
||||||
"/search-cutoff-ms",
|
>,
|
||||||
put,
|
attr: facet_search,
|
||||||
u64,
|
camelcase_attr: "facetSearch",
|
||||||
meilisearch_types::deserr::DeserrJsonError<
|
analytics: FacetSearchAnalytics
|
||||||
meilisearch_types::error::deserr_codes::InvalidSettingsSearchCutoffMs,
|
},
|
||||||
>,
|
{
|
||||||
search_cutoff_ms,
|
route: "/prefix-search",
|
||||||
"searchCutoffMs",
|
update_verb: put,
|
||||||
SearchCutoffMsAnalytics
|
value_type: meilisearch_types::settings::PrefixSearchSettings,
|
||||||
);
|
err_type: meilisearch_types::deserr::DeserrJsonError<
|
||||||
|
meilisearch_types::error::deserr_codes::InvalidSettingsPrefixSearch,
|
||||||
macro_rules! generate_configure {
|
>,
|
||||||
($($mod:ident),*) => {
|
attr: prefix_search,
|
||||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
camelcase_attr: "prefixSearch",
|
||||||
use crate::extractors::sequential_extractor::SeqHandler;
|
analytics: PrefixSearchAnalytics
|
||||||
cfg.service(
|
},
|
||||||
web::resource("")
|
|
||||||
.route(web::patch().to(SeqHandler(update_all)))
|
|
||||||
.route(web::get().to(SeqHandler(get_all)))
|
|
||||||
.route(web::delete().to(SeqHandler(delete_all))))
|
|
||||||
$(.service($mod::resources()))*;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_configure!(
|
|
||||||
filterable_attributes,
|
|
||||||
sortable_attributes,
|
|
||||||
displayed_attributes,
|
|
||||||
localized_attributes,
|
|
||||||
searchable_attributes,
|
|
||||||
distinct_attribute,
|
|
||||||
proximity_precision,
|
|
||||||
stop_words,
|
|
||||||
separator_tokens,
|
|
||||||
non_separator_tokens,
|
|
||||||
dictionary,
|
|
||||||
synonyms,
|
|
||||||
ranking_rules,
|
|
||||||
typo_tolerance,
|
|
||||||
pagination,
|
|
||||||
faceting,
|
|
||||||
embedders,
|
|
||||||
search_cutoff_ms
|
|
||||||
);
|
);
|
||||||
|
|
||||||
pub async fn update_all(
|
pub async fn update_all(
|
||||||
@@ -456,6 +454,8 @@ pub async fn update_all(
|
|||||||
non_separator_tokens: NonSeparatorTokensAnalytics::new(
|
non_separator_tokens: NonSeparatorTokensAnalytics::new(
|
||||||
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()),
|
||||||
|
prefix_search: PrefixSearchAnalytics::new(new_settings.prefix_search.as_ref().set()),
|
||||||
},
|
},
|
||||||
&req,
|
&req,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use meilisearch_types::locales::{Locale, LocalizedAttributesRuleView};
|
|||||||
use meilisearch_types::milli::update::Setting;
|
use meilisearch_types::milli::update::Setting;
|
||||||
use meilisearch_types::milli::vector::settings::EmbeddingSettings;
|
use meilisearch_types::milli::vector::settings::EmbeddingSettings;
|
||||||
use meilisearch_types::settings::{
|
use meilisearch_types::settings::{
|
||||||
FacetingSettings, PaginationSettings, ProximityPrecisionView, RankingRuleView, TypoSettings,
|
FacetingSettings, PaginationSettings, PrefixSearchSettings, ProximityPrecisionView,
|
||||||
|
RankingRuleView, TypoSettings,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -36,6 +37,8 @@ pub struct SettingsAnalytics {
|
|||||||
pub dictionary: DictionaryAnalytics,
|
pub dictionary: DictionaryAnalytics,
|
||||||
pub separator_tokens: SeparatorTokensAnalytics,
|
pub separator_tokens: SeparatorTokensAnalytics,
|
||||||
pub non_separator_tokens: NonSeparatorTokensAnalytics,
|
pub non_separator_tokens: NonSeparatorTokensAnalytics,
|
||||||
|
pub facet_search: FacetSearchAnalytics,
|
||||||
|
pub prefix_search: PrefixSearchAnalytics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aggregate for SettingsAnalytics {
|
impl Aggregate for SettingsAnalytics {
|
||||||
@@ -183,6 +186,14 @@ impl Aggregate for SettingsAnalytics {
|
|||||||
non_separator_tokens: NonSeparatorTokensAnalytics {
|
non_separator_tokens: NonSeparatorTokensAnalytics {
|
||||||
total: new.non_separator_tokens.total.or(self.non_separator_tokens.total),
|
total: new.non_separator_tokens.total.or(self.non_separator_tokens.total),
|
||||||
},
|
},
|
||||||
|
facet_search: FacetSearchAnalytics {
|
||||||
|
set: new.facet_search.set | self.facet_search.set,
|
||||||
|
value: new.facet_search.value.or(self.facet_search.value),
|
||||||
|
},
|
||||||
|
prefix_search: PrefixSearchAnalytics {
|
||||||
|
set: new.prefix_search.set | self.prefix_search.set,
|
||||||
|
value: new.prefix_search.value.or(self.prefix_search.value),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,3 +631,35 @@ impl NonSeparatorTokensAnalytics {
|
|||||||
SettingsAnalytics { non_separator_tokens: self, ..Default::default() }
|
SettingsAnalytics { non_separator_tokens: self, ..Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Default)]
|
||||||
|
pub struct FacetSearchAnalytics {
|
||||||
|
pub set: bool,
|
||||||
|
pub value: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FacetSearchAnalytics {
|
||||||
|
pub fn new(settings: Option<&bool>) -> Self {
|
||||||
|
Self { set: settings.is_some(), value: settings.copied() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_settings(self) -> SettingsAnalytics {
|
||||||
|
SettingsAnalytics { facet_search: self, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Default)]
|
||||||
|
pub struct PrefixSearchAnalytics {
|
||||||
|
pub set: bool,
|
||||||
|
pub value: Option<PrefixSearchSettings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrefixSearchAnalytics {
|
||||||
|
pub fn new(settings: Option<&PrefixSearchSettings>) -> Self {
|
||||||
|
Self { set: settings.is_some(), value: settings.cloned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_settings(self) -> SettingsAnalytics {
|
||||||
|
SettingsAnalytics { prefix_search: self, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -796,8 +796,10 @@ fn prepare_search<'t>(
|
|||||||
let span = tracing::trace_span!(target: "search::vector", "embed_one");
|
let span = tracing::trace_span!(target: "search::vector", "embed_one");
|
||||||
let _entered = span.enter();
|
let _entered = span.enter();
|
||||||
|
|
||||||
|
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
|
||||||
|
|
||||||
embedder
|
embedder
|
||||||
.embed_one(query.q.clone().unwrap())
|
.embed_one(query.q.clone().unwrap(), Some(deadline))
|
||||||
.map_err(milli::vector::Error::from)
|
.map_err(milli::vector::Error::from)
|
||||||
.map_err(milli::Error::from)?
|
.map_err(milli::Error::from)?
|
||||||
}
|
}
|
||||||
@@ -1405,6 +1407,13 @@ pub fn perform_facet_search(
|
|||||||
None => TimeBudget::default(),
|
None => TimeBudget::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !index.facet_search(&rtxn)? {
|
||||||
|
return Err(ResponseError::from_msg(
|
||||||
|
"The facet search is disabled for this index".to_string(),
|
||||||
|
Code::FacetSearchDisabled,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// In the faceted search context, we want to use the intersection between the locales provided by the user
|
// In the faceted search context, we want to use the intersection between the locales provided by the user
|
||||||
// and the locales of the facet string.
|
// and the locales of the facet string.
|
||||||
// If the facet string is not localized, we **ignore** the locales provided by the user because the facet data has no locale.
|
// If the facet string is not localized, we **ignore** the locales provided by the user because the facet data has no locale.
|
||||||
@@ -1687,7 +1696,7 @@ fn add_non_formatted_ids_to_formatted_options(
|
|||||||
fn make_document(
|
fn make_document(
|
||||||
displayed_attributes: &BTreeSet<FieldId>,
|
displayed_attributes: &BTreeSet<FieldId>,
|
||||||
field_ids_map: &FieldsIdsMap,
|
field_ids_map: &FieldsIdsMap,
|
||||||
obkv: obkv::KvReaderU16,
|
obkv: &obkv::KvReaderU16,
|
||||||
) -> Result<Document, MeilisearchHttpError> {
|
) -> Result<Document, MeilisearchHttpError> {
|
||||||
let mut document = serde_json::Map::new();
|
let mut document = serde_json::Map::new();
|
||||||
|
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ async fn list_batches_status_and_type_filtered() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn get_batch_filter_error() {
|
async fn list_batch_filter_error() {
|
||||||
let server = Server::new().await;
|
let server = Server::new().await;
|
||||||
|
|
||||||
let (response, code) = server.batches_filter("lol=pied").await;
|
let (response, code) = server.batches_filter("lol=pied").await;
|
||||||
|
|||||||
@@ -52,6 +52,25 @@ impl Value {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the `status` field is set to `failed`.
|
||||||
|
/// Panic if the `status` field doesn't exists.
|
||||||
|
#[track_caller]
|
||||||
|
pub fn is_fail(&self) -> bool {
|
||||||
|
if !self["status"].is_string() {
|
||||||
|
panic!("Called `is_fail` on {}", serde_json::to_string_pretty(&self.0).unwrap());
|
||||||
|
}
|
||||||
|
self["status"] == serde_json::Value::String(String::from("failed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic if the json doesn't contain the `status` field set to "succeeded"
|
||||||
|
#[track_caller]
|
||||||
|
pub fn failed(&self) -> &Self {
|
||||||
|
if !self.is_fail() {
|
||||||
|
panic!("Called failed on {}", serde_json::to_string_pretty(&self.0).unwrap());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serde_json::Value> for Value {
|
impl From<serde_json::Value> for Value {
|
||||||
|
|||||||
@@ -1344,7 +1344,6 @@ async fn error_add_documents_missing_document_id() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
#[should_panic]
|
|
||||||
async fn error_document_field_limit_reached_in_one_document() {
|
async fn error_document_field_limit_reached_in_one_document() {
|
||||||
let server = Server::new().await;
|
let server = Server::new().await;
|
||||||
let index = server.index("test");
|
let index = server.index("test");
|
||||||
@@ -1361,7 +1360,7 @@ async fn error_document_field_limit_reached_in_one_document() {
|
|||||||
let documents = json!([big_object]);
|
let documents = json!([big_object]);
|
||||||
|
|
||||||
let (response, code) = index.update_documents(documents, Some("id")).await;
|
let (response, code) = index.update_documents(documents, Some("id")).await;
|
||||||
snapshot!(code, @"500 Internal Server Error");
|
snapshot!(code, @"202 Accepted");
|
||||||
|
|
||||||
let response = index.wait_task(response.uid()).await;
|
let response = index.wait_task(response.uid()).await;
|
||||||
snapshot!(code, @"202 Accepted");
|
snapshot!(code, @"202 Accepted");
|
||||||
@@ -1369,16 +1368,22 @@ async fn error_document_field_limit_reached_in_one_document() {
|
|||||||
snapshot!(response,
|
snapshot!(response,
|
||||||
@r###"
|
@r###"
|
||||||
{
|
{
|
||||||
"uid": 1,
|
"uid": "[uid]",
|
||||||
|
"batchUid": "[batch_uid]",
|
||||||
"indexUid": "test",
|
"indexUid": "test",
|
||||||
"status": "succeeded",
|
"status": "failed",
|
||||||
"type": "documentAdditionOrUpdate",
|
"type": "documentAdditionOrUpdate",
|
||||||
"canceledBy": null,
|
"canceledBy": null,
|
||||||
"details": {
|
"details": {
|
||||||
"receivedDocuments": 1,
|
"receivedDocuments": 1,
|
||||||
"indexedDocuments": 1
|
"indexedDocuments": 0
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"message": "A document cannot contain more than 65,535 fields.",
|
||||||
|
"code": "max_fields_limit_exceeded",
|
||||||
|
"type": "invalid_request",
|
||||||
|
"link": "https://docs.meilisearch.com/errors#max_fields_limit_exceeded"
|
||||||
},
|
},
|
||||||
"error": null,
|
|
||||||
"duration": "[duration]",
|
"duration": "[duration]",
|
||||||
"enqueuedAt": "[date]",
|
"enqueuedAt": "[date]",
|
||||||
"startedAt": "[date]",
|
"startedAt": "[date]",
|
||||||
@@ -1676,7 +1681,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "The `_geo` field in the document with the id: `11` is not an object. Was expecting an object with the `_geo.lat` and `_geo.lng` fields but instead got `\"foobar\"`.",
|
"message": "The `_geo` field in the document with the id: `\"11\"` is not an object. Was expecting an object with the `_geo.lat` and `_geo.lng` fields but instead got `\"foobar\"`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1714,7 +1719,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find latitude nor longitude in the document with the id: `11`. Was expecting `_geo.lat` and `_geo.lng` fields.",
|
"message": "Could not find latitude nor longitude in the document with the id: `\"11\"`. Was expecting `_geo.lat` and `_geo.lng` fields.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1752,7 +1757,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find latitude nor longitude in the document with the id: `11`. Was expecting `_geo.lat` and `_geo.lng` fields.",
|
"message": "Could not find latitude nor longitude in the document with the id: `\"11\"`. Was expecting `_geo.lat` and `_geo.lng` fields.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1790,7 +1795,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find longitude in the document with the id: `11`. Was expecting a `_geo.lng` field.",
|
"message": "Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1828,7 +1833,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find latitude in the document with the id: `11`. Was expecting a `_geo.lat` field.",
|
"message": "Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1866,7 +1871,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find longitude in the document with the id: `11`. Was expecting a `_geo.lng` field.",
|
"message": "Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1904,7 +1909,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find latitude in the document with the id: `11`. Was expecting a `_geo.lat` field.",
|
"message": "Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1942,7 +1947,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not parse latitude nor longitude in the document with the id: `11`. Was expecting finite numbers but instead got `false` and `true`.",
|
"message": "Could not parse latitude nor longitude in the document with the id: `\"11\"`. Was expecting finite numbers but instead got `false` and `true`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -1980,7 +1985,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find longitude in the document with the id: `11`. Was expecting a `_geo.lng` field.",
|
"message": "Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2018,7 +2023,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not find latitude in the document with the id: `11`. Was expecting a `_geo.lat` field.",
|
"message": "Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2056,7 +2061,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not parse latitude nor longitude in the document with the id: `11`. Was expecting finite numbers but instead got `\"doggo\"` and `\"doggo\"`.",
|
"message": "Could not parse latitude nor longitude in the document with the id: `\"11\"`. Was expecting finite numbers but instead got `\"doggo\"` and `\"doggo\"`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2094,7 +2099,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "The `_geo` field in the document with the id: `11` contains the following unexpected fields: `{\"doggo\":\"are the best\"}`.",
|
"message": "The `_geo` field in the document with the id: `\"11\"` contains the following unexpected fields: `{\"doggo\":\"are the best\"}`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2133,7 +2138,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not parse longitude in the document with the id: `12`. Was expecting a finite number but instead got `null`.",
|
"message": "Could not parse longitude in the document with the id: `\"12\"`. Was expecting a finite number but instead got `null`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2170,7 +2175,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not parse latitude in the document with the id: `12`. Was expecting a finite number but instead got `null`.",
|
"message": "Could not parse latitude in the document with the id: `\"12\"`. Was expecting a finite number but instead got `null`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2207,7 +2212,7 @@ async fn add_documents_invalid_geo_field() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Could not parse latitude nor longitude in the document with the id: `13`. Was expecting finite numbers but instead got `null` and `null`.",
|
"message": "Could not parse latitude nor longitude in the document with the id: `\"13\"`. Was expecting finite numbers but instead got `null` and `null`.",
|
||||||
"code": "invalid_document_geo_field",
|
"code": "invalid_document_geo_field",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
"link": "https://docs.meilisearch.com/errors#invalid_document_geo_field"
|
||||||
@@ -2227,7 +2232,7 @@ async fn add_invalid_geo_and_then_settings() {
|
|||||||
let index = server.index("test");
|
let index = server.index("test");
|
||||||
index.create(Some("id")).await;
|
index.create(Some("id")).await;
|
||||||
|
|
||||||
// _geo is not an object
|
// _geo is not a correct object
|
||||||
let documents = json!([
|
let documents = json!([
|
||||||
{
|
{
|
||||||
"id": "11",
|
"id": "11",
|
||||||
@@ -2257,7 +2262,7 @@ async fn add_invalid_geo_and_then_settings() {
|
|||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
let (ret, code) = index.update_settings(json!({"sortableAttributes": ["_geo"]})).await;
|
let (ret, code) = index.update_settings(json!({ "sortableAttributes": ["_geo"] })).await;
|
||||||
snapshot!(code, @"202 Accepted");
|
snapshot!(code, @"202 Accepted");
|
||||||
let ret = index.wait_task(ret.uid()).await;
|
let ret = index.wait_task(ret.uid()).await;
|
||||||
snapshot!(ret, @r###"
|
snapshot!(ret, @r###"
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ async fn import_dump_v1_movie_raw() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -242,7 +244,9 @@ async fn import_dump_v1_movie_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -391,7 +395,9 @@ async fn import_dump_v1_rubygems_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -526,7 +532,9 @@ async fn import_dump_v2_movie_raw() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -673,7 +681,9 @@ async fn import_dump_v2_movie_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -819,7 +829,9 @@ async fn import_dump_v2_rubygems_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -954,7 +966,9 @@ async fn import_dump_v3_movie_raw() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -1101,7 +1115,9 @@ async fn import_dump_v3_movie_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -1247,7 +1263,9 @@ async fn import_dump_v3_rubygems_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -1382,7 +1400,9 @@ async fn import_dump_v4_movie_raw() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -1529,7 +1549,9 @@ async fn import_dump_v4_movie_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -1675,7 +1697,9 @@ async fn import_dump_v4_rubygems_with_settings() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
@@ -1922,7 +1946,9 @@ async fn import_dump_v6_containing_experimental_features() {
|
|||||||
"maxTotalHits": 1000
|
"maxTotalHits": 1000
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
@@ -2102,7 +2128,9 @@ async fn generate_and_import_dump_containing_vectors() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ async fn simple_facet_search() {
|
|||||||
|
|
||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(1).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
@@ -65,8 +65,8 @@ async fn advanced_facet_search() {
|
|||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.update_settings_typo_tolerance(json!({ "enabled": false })).await;
|
index.update_settings_typo_tolerance(json!({ "enabled": false })).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "adventre"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "adventre"})).await;
|
||||||
@@ -89,8 +89,8 @@ async fn more_advanced_facet_search() {
|
|||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.update_settings_typo_tolerance(json!({ "disableOnWords": ["adventre"] })).await;
|
index.update_settings_typo_tolerance(json!({ "disableOnWords": ["adventre"] })).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "adventre"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "adventre"})).await;
|
||||||
@@ -113,8 +113,8 @@ async fn simple_facet_search_with_max_values() {
|
|||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_faceting(json!({ "maxValuesPerFacet": 1 })).await;
|
index.update_settings_faceting(json!({ "maxValuesPerFacet": 1 })).await;
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
@@ -135,8 +135,8 @@ async fn simple_facet_search_by_count_with_max_values() {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
@@ -151,8 +151,8 @@ async fn non_filterable_facet_search_error() {
|
|||||||
let index = server.index("test");
|
let index = server.index("test");
|
||||||
|
|
||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(0).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
@@ -170,8 +170,8 @@ async fn facet_search_dont_support_words() {
|
|||||||
|
|
||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(1).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "words"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "words"})).await;
|
||||||
@@ -188,8 +188,8 @@ async fn simple_facet_search_with_sort_by_count() {
|
|||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_faceting(json!({ "sortFacetValuesBy": { "*": "count" } })).await;
|
index.update_settings_faceting(json!({ "sortFacetValuesBy": { "*": "count" } })).await;
|
||||||
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
index.update_settings_filterable_attributes(json!(["genres"])).await;
|
||||||
index.add_documents(documents, None).await;
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
@@ -200,3 +200,129 @@ async fn simple_facet_search_with_sort_by_count() {
|
|||||||
assert_eq!(hits[0], json!({ "value": "Action", "count": 3 }));
|
assert_eq!(hits[0], json!({ "value": "Action", "count": 3 }));
|
||||||
assert_eq!(hits[1], json!({ "value": "Adventure", "count": 2 }));
|
assert_eq!(hits[1], json!({ "value": "Adventure", "count": 2 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn add_documents_and_deactivate_facet_search() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"facetSearch": false,
|
||||||
|
"filterableAttributes": ["genres"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) =
|
||||||
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
|
|
||||||
|
assert_eq!(code, 400, "{}", response);
|
||||||
|
snapshot!(response, @r###"
|
||||||
|
{
|
||||||
|
"message": "The facet search is disabled for this index",
|
||||||
|
"code": "facet_search_disabled",
|
||||||
|
"type": "invalid_request",
|
||||||
|
"link": "https://docs.meilisearch.com/errors#facet_search_disabled"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn deactivate_facet_search_and_add_documents() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"facetSearch": false,
|
||||||
|
"filterableAttributes": ["genres"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) =
|
||||||
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
|
|
||||||
|
assert_eq!(code, 400, "{}", response);
|
||||||
|
snapshot!(response, @r###"
|
||||||
|
{
|
||||||
|
"message": "The facet search is disabled for this index",
|
||||||
|
"code": "facet_search_disabled",
|
||||||
|
"type": "invalid_request",
|
||||||
|
"link": "https://docs.meilisearch.com/errors#facet_search_disabled"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn deactivate_facet_search_add_documents_and_activate_facet_search() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"facetSearch": false,
|
||||||
|
"filterableAttributes": ["genres"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"facetSearch": true,
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) =
|
||||||
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
|
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
assert_eq!(dbg!(response)["facetHits"].as_array().unwrap().len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn deactivate_facet_search_add_documents_and_reset_facet_search() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"facetSearch": false,
|
||||||
|
"filterableAttributes": ["genres"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
let (response, _code) = index.add_documents(documents, None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"facetSearch": serde_json::Value::Null,
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) =
|
||||||
|
index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await;
|
||||||
|
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
assert_eq!(dbg!(response)["facetHits"].as_array().unwrap().len(), 2);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,58 @@ use super::*;
|
|||||||
use crate::common::Server;
|
use crate::common::Server;
|
||||||
use crate::json;
|
use crate::json;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_formatted_from_sdk() {
|
||||||
|
let server = Server::new_shared();
|
||||||
|
let index = server.unique_index();
|
||||||
|
|
||||||
|
index
|
||||||
|
.update_settings(
|
||||||
|
json!({ "filterableAttributes": ["genre"], "searchableAttributes": ["title"] }),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let documents = json!([
|
||||||
|
{ "id": 123, "title": "Pride and Prejudice", "genre": "romance" },
|
||||||
|
{ "id": 456, "title": "Le Petit Prince", "genre": "adventure" },
|
||||||
|
{ "id": 1, "title": "Alice In Wonderland", "genre": "adventure" },
|
||||||
|
{ "id": 2, "title": "Le Rouge et le Noir", "genre": "romance" },
|
||||||
|
{ "id": 1344, "title": "The Hobbit", "genre": "adventure" },
|
||||||
|
{ "id": 4, "title": "Harry Potter and the Half-Blood Prince", "genre": "fantasy" },
|
||||||
|
{ "id": 7, "title": "Harry Potter and the Chamber of Secrets", "genre": "fantasy" },
|
||||||
|
{ "id": 42, "title": "The Hitchhiker's Guide to the Galaxy" }
|
||||||
|
]);
|
||||||
|
let (response, _) = index.add_documents(documents, None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
index
|
||||||
|
.search(
|
||||||
|
json!({ "q":"prince",
|
||||||
|
"attributesToCrop": ["title"],
|
||||||
|
"cropLength": 2,
|
||||||
|
"filter": "genre = adventure",
|
||||||
|
"attributesToHighlight": ["title"],
|
||||||
|
"attributesToRetrieve": ["title"]
|
||||||
|
}),
|
||||||
|
|response, code| {
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
allow_duplicates! {
|
||||||
|
assert_json_snapshot!(response["hits"][0],
|
||||||
|
{ "._rankingScore" => "[score]" },
|
||||||
|
@r###"
|
||||||
|
{
|
||||||
|
"title": "Le Petit Prince",
|
||||||
|
"_formatted": {
|
||||||
|
"title": "…Petit <em>Prince</em>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn formatted_contain_wildcard() {
|
async fn formatted_contain_wildcard() {
|
||||||
let server = Server::new_shared();
|
let server = Server::new_shared();
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ async fn geo_bounding_box_with_string_and_number() {
|
|||||||
let documents = DOCUMENTS.clone();
|
let documents = DOCUMENTS.clone();
|
||||||
index.update_settings_filterable_attributes(json!(["_geo"])).await;
|
index.update_settings_filterable_attributes(json!(["_geo"])).await;
|
||||||
index.update_settings_sortable_attributes(json!(["_geo"])).await;
|
index.update_settings_sortable_attributes(json!(["_geo"])).await;
|
||||||
index.add_documents(documents, None).await;
|
let (ret, _code) = index.add_documents(documents, None).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(ret.uid()).await.succeeded();
|
||||||
|
|
||||||
index
|
index
|
||||||
.search(
|
.search(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ mod pagination;
|
|||||||
mod restrict_searchable;
|
mod restrict_searchable;
|
||||||
mod search_queue;
|
mod search_queue;
|
||||||
|
|
||||||
|
use meili_snap::{json_string, snapshot};
|
||||||
use meilisearch::Opt;
|
use meilisearch::Opt;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
@@ -62,6 +63,71 @@ async fn simple_search() {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn search_with_stop_word() {
|
||||||
|
// related to https://github.com/meilisearch/meilisearch/issues/4984
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (_, code) = index
|
||||||
|
.update_settings(json!({"stopWords": ["the", "The", "a", "an", "to", "in", "of"]}))
|
||||||
|
.await;
|
||||||
|
meili_snap::snapshot!(code, @"202 Accepted");
|
||||||
|
|
||||||
|
let documents = DOCUMENTS.clone();
|
||||||
|
index.add_documents(documents, None).await;
|
||||||
|
index.wait_task(1).await;
|
||||||
|
|
||||||
|
// prefix search
|
||||||
|
index
|
||||||
|
.search(json!({"q": "to the", "attributesToHighlight": ["title"], "attributesToRetrieve": ["title"] }), |response, code| {
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
snapshot!(json_string!(response["hits"]), @"[]");
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// non-prefix search
|
||||||
|
index
|
||||||
|
.search(json!({"q": "to the ", "attributesToHighlight": ["title"], "attributesToRetrieve": ["title"] }), |response, code| {
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Shazam!",
|
||||||
|
"_formatted": {
|
||||||
|
"title": "Shazam!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Captain Marvel",
|
||||||
|
"_formatted": {
|
||||||
|
"title": "Captain Marvel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Escape Room",
|
||||||
|
"_formatted": {
|
||||||
|
"title": "Escape Room"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "How to Train Your Dragon: The Hidden World",
|
||||||
|
"_formatted": {
|
||||||
|
"title": "How to Train Your Dragon: The Hidden World"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Gläss",
|
||||||
|
"_formatted": {
|
||||||
|
"title": "Gläss"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn phrase_search_with_stop_word() {
|
async fn phrase_search_with_stop_word() {
|
||||||
// related to https://github.com/meilisearch/meilisearch/issues/3521
|
// related to https://github.com/meilisearch/meilisearch/issues/3521
|
||||||
@@ -750,9 +816,9 @@ async fn test_score_details() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_rankingScoreDetails": {
|
"_rankingScoreDetails": {
|
||||||
@@ -1543,9 +1609,9 @@ async fn simple_search_with_strange_synonyms() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1568,9 +1634,9 @@ async fn simple_search_with_strange_synonyms() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1593,9 +1659,9 @@ async fn simple_search_with_strange_synonyms() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1604,3 +1670,79 @@ async fn simple_search_with_strange_synonyms() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn change_attributes_settings() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
index.update_settings(json!({ "searchableAttributes": ["father", "mother"] })).await;
|
||||||
|
|
||||||
|
let documents = NESTED_DOCUMENTS.clone();
|
||||||
|
index.add_documents(json!(documents), None).await;
|
||||||
|
index.wait_task(1).await;
|
||||||
|
|
||||||
|
index.update_settings(json!({ "searchableAttributes": ["father", "mother", "doggos"], "filterableAttributes": ["doggos"] })).await;
|
||||||
|
index.wait_task(2).await;
|
||||||
|
|
||||||
|
// search
|
||||||
|
index
|
||||||
|
.search(
|
||||||
|
json!({
|
||||||
|
"q": "bobby",
|
||||||
|
"attributesToRetrieve": ["id", "doggos"]
|
||||||
|
}),
|
||||||
|
|response, code| {
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 852,
|
||||||
|
"doggos": [
|
||||||
|
{
|
||||||
|
"name": "bobby",
|
||||||
|
"age": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buddy",
|
||||||
|
"age": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// filter
|
||||||
|
index
|
||||||
|
.search(
|
||||||
|
json!({
|
||||||
|
"q": "",
|
||||||
|
"filter": "doggos.age < 5",
|
||||||
|
"attributesToRetrieve": ["id", "doggos"]
|
||||||
|
}),
|
||||||
|
|response, code| {
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 852,
|
||||||
|
"doggos": [
|
||||||
|
{
|
||||||
|
"name": "bobby",
|
||||||
|
"age": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "buddy",
|
||||||
|
"age": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ async fn simple_search_single_index() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,9 +138,9 @@ async fn simple_search_single_index() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,9 +182,9 @@ async fn federation_single_search_single_index() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -305,9 +305,9 @@ async fn federation_two_search_single_index() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -325,9 +325,9 @@ async fn federation_two_search_single_index() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -480,9 +480,9 @@ async fn simple_search_two_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,9 +513,9 @@ async fn simple_search_two_indexes() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -535,9 +535,9 @@ async fn simple_search_two_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -585,9 +585,9 @@ async fn federation_two_search_two_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -613,9 +613,9 @@ async fn federation_two_search_two_indexes() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -640,9 +640,9 @@ async fn federation_two_search_two_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -707,9 +707,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -735,9 +735,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -773,9 +773,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -793,9 +793,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
-23.0,
|
-23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -824,9 +824,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -869,9 +869,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -898,9 +898,9 @@ async fn federation_multiple_search_multiple_indexes() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1393,9 +1393,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1414,9 +1414,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1442,9 +1442,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1474,9 +1474,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1522,9 +1522,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1550,9 +1550,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1582,9 +1582,9 @@ async fn federation_sort_same_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1716,9 +1716,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1748,9 +1748,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1769,9 +1769,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1797,9 +1797,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1845,9 +1845,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1874,9 +1874,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
"cattos": "pésti",
|
"cattos": "pésti",
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -1906,9 +1906,9 @@ async fn federation_sort_same_indexes_different_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
23.0,
|
23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2103,9 +2103,9 @@ async fn federation_sort_different_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2124,9 +2124,9 @@ async fn federation_sort_different_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
-23.0,
|
-23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2145,9 +2145,9 @@ async fn federation_sort_different_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2166,9 +2166,9 @@ async fn federation_sort_different_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2187,9 +2187,9 @@ async fn federation_sort_different_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2228,9 +2228,9 @@ async fn federation_sort_different_indexes_same_criterion_same_direction() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2415,9 +2415,9 @@ async fn federation_sort_different_ranking_rules() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2436,9 +2436,9 @@ async fn federation_sort_different_ranking_rules() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
-23.0,
|
-23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2457,9 +2457,9 @@ async fn federation_sort_different_ranking_rules() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2478,9 +2478,9 @@ async fn federation_sort_different_ranking_rules() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2499,9 +2499,9 @@ async fn federation_sort_different_ranking_rules() {
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2716,9 +2716,9 @@ async fn federation_sort_different_indexes_different_criterion_same_direction()
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2757,9 +2757,9 @@ async fn federation_sort_different_indexes_different_criterion_same_direction()
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
10.0,
|
10,
|
||||||
-23.0,
|
-23,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2778,9 +2778,9 @@ async fn federation_sort_different_indexes_different_criterion_same_direction()
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
340.0,
|
340,
|
||||||
90.0
|
90
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2799,9 +2799,9 @@ async fn federation_sort_different_indexes_different_criterion_same_direction()
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
-100.0,
|
-100,
|
||||||
231.0,
|
231,
|
||||||
32.0
|
32
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2820,9 +2820,9 @@ async fn federation_sort_different_indexes_different_criterion_same_direction()
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
3.0
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -2881,9 +2881,9 @@ async fn federation_sort_different_indexes_different_criterion_same_direction()
|
|||||||
],
|
],
|
||||||
"_vectors": {
|
"_vectors": {
|
||||||
"manual": [
|
"manual": [
|
||||||
1.0,
|
1,
|
||||||
2.0,
|
2,
|
||||||
54.0
|
54
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
@@ -4346,10 +4346,10 @@ async fn federation_vector_two_indexes() {
|
|||||||
|
|
||||||
let (response, code) = server
|
let (response, code) = server
|
||||||
.multi_search(json!({"federation": {}, "queries": [
|
.multi_search(json!({"federation": {}, "queries": [
|
||||||
{"indexUid" : "vectors-animal", "vector": [1.0, 0.0, 0.5], "hybrid": {"semanticRatio": 1.0, "embedder": "animal"}},
|
{"indexUid" : "vectors-animal", "vector": [1.0, 0.0, 0.5], "hybrid": {"semanticRatio": 1.0, "embedder": "animal"}, "retrieveVectors": true},
|
||||||
// joyful and energetic first
|
// joyful and energetic first
|
||||||
{"indexUid": "vectors-sentiment", "vector": [0.8, 0.6], "hybrid": {"semanticRatio": 1.0, "embedder": "sentiment"}},
|
{"indexUid": "vectors-sentiment", "vector": [0.8, 0.6], "hybrid": {"semanticRatio": 1.0, "embedder": "sentiment"}, "retrieveVectors": true},
|
||||||
{"indexUid": "vectors-sentiment", "q": "dog"},
|
{"indexUid": "vectors-sentiment", "q": "dog", "retrieveVectors": true},
|
||||||
]}))
|
]}))
|
||||||
.await;
|
.await;
|
||||||
snapshot!(code, @"200 OK");
|
snapshot!(code, @"200 OK");
|
||||||
@@ -4364,7 +4364,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.8,
|
0.8,
|
||||||
0.09,
|
0.09,
|
||||||
0.8
|
0.8
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.800000011920929,
|
||||||
|
0.30000001192092896
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4379,7 +4388,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
0.8,
|
0.8,
|
||||||
0.3
|
0.3
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.800000011920929,
|
||||||
|
0.09000000357627869,
|
||||||
|
0.800000011920929
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4394,7 +4413,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
-1.0,
|
-1.0,
|
||||||
0.1
|
0.1
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.8500000238418579,
|
||||||
|
0.019999999552965164,
|
||||||
|
0.10000000149011612
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4410,7 +4439,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.9,
|
0.9,
|
||||||
0.8,
|
0.8,
|
||||||
0.05
|
0.05
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
-0.10000000149011612,
|
||||||
|
0.550000011920929
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4426,7 +4464,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.85,
|
0.85,
|
||||||
0.02,
|
0.02,
|
||||||
0.1
|
0.1
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
-1.0,
|
||||||
|
0.10000000149011612
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4441,7 +4488,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
-0.2,
|
-0.2,
|
||||||
0.65
|
0.65
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.800000011920929,
|
||||||
|
0.8999999761581421,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4456,7 +4513,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
-0.1,
|
-0.1,
|
||||||
0.55
|
0.55
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.8999999761581421,
|
||||||
|
0.800000011920929,
|
||||||
|
0.05000000074505806
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4472,7 +4539,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.8,
|
0.8,
|
||||||
0.9,
|
0.9,
|
||||||
0.5
|
0.5
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
-0.20000000298023224,
|
||||||
|
0.6499999761581421
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4492,8 +4568,8 @@ async fn federation_vector_two_indexes() {
|
|||||||
// hybrid search, distinct embedder
|
// hybrid search, distinct embedder
|
||||||
let (response, code) = server
|
let (response, code) = server
|
||||||
.multi_search(json!({"federation": {}, "queries": [
|
.multi_search(json!({"federation": {}, "queries": [
|
||||||
{"indexUid" : "vectors-animal", "vector": [1.0, 0.0, 0.5], "hybrid": {"semanticRatio": 1.0, "embedder": "animal"}, "showRankingScore": true},
|
{"indexUid" : "vectors-animal", "vector": [1.0, 0.0, 0.5], "hybrid": {"semanticRatio": 1.0, "embedder": "animal"}, "showRankingScore": true, "retrieveVectors": true},
|
||||||
{"indexUid": "vectors-sentiment", "vector": [-1, 0.6], "q": "beagle", "hybrid": {"semanticRatio": 1.0, "embedder": "sentiment"}, "showRankingScore": true},
|
{"indexUid": "vectors-sentiment", "vector": [-1, 0.6], "q": "beagle", "hybrid": {"semanticRatio": 1.0, "embedder": "sentiment"}, "showRankingScore": true, "retrieveVectors": true,},
|
||||||
]}))
|
]}))
|
||||||
.await;
|
.await;
|
||||||
snapshot!(code, @"200 OK");
|
snapshot!(code, @"200 OK");
|
||||||
@@ -4507,7 +4583,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
0.8,
|
0.8,
|
||||||
0.3
|
0.3
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.800000011920929,
|
||||||
|
0.09000000357627869,
|
||||||
|
0.800000011920929
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4523,7 +4609,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
-1.0,
|
-1.0,
|
||||||
0.1
|
0.1
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.8500000238418579,
|
||||||
|
0.019999999552965164,
|
||||||
|
0.10000000149011612
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4540,7 +4636,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.85,
|
0.85,
|
||||||
0.02,
|
0.02,
|
||||||
0.1
|
0.1
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
-1.0,
|
||||||
|
0.10000000149011612
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4557,7 +4662,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.8,
|
0.8,
|
||||||
0.9,
|
0.9,
|
||||||
0.5
|
0.5
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
-0.20000000298023224,
|
||||||
|
0.6499999761581421
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4573,7 +4687,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
-0.2,
|
-0.2,
|
||||||
0.65
|
0.65
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.800000011920929,
|
||||||
|
0.8999999761581421,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4589,7 +4713,17 @@ async fn federation_vector_two_indexes() {
|
|||||||
"sentiment": [
|
"sentiment": [
|
||||||
-0.1,
|
-0.1,
|
||||||
0.55
|
0.55
|
||||||
]
|
],
|
||||||
|
"animal": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.8999999761581421,
|
||||||
|
0.800000011920929,
|
||||||
|
0.05000000074505806
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-animal",
|
"indexUid": "vectors-animal",
|
||||||
@@ -4606,7 +4740,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.9,
|
0.9,
|
||||||
0.8,
|
0.8,
|
||||||
0.05
|
0.05
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
-0.10000000149011612,
|
||||||
|
0.550000011920929
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
@@ -4623,7 +4766,16 @@ async fn federation_vector_two_indexes() {
|
|||||||
0.8,
|
0.8,
|
||||||
0.09,
|
0.09,
|
||||||
0.8
|
0.8
|
||||||
]
|
],
|
||||||
|
"sentiment": {
|
||||||
|
"embeddings": [
|
||||||
|
[
|
||||||
|
0.800000011920929,
|
||||||
|
0.30000001192092896
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"regenerate": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"_federation": {
|
"_federation": {
|
||||||
"indexUid": "vectors-sentiment",
|
"indexUid": "vectors-sentiment",
|
||||||
|
|||||||
@@ -367,3 +367,50 @@ async fn search_on_exact_field() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn phrase_search_on_title() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let documents = json!([
|
||||||
|
{ "id": 8, "desc": "Document Review", "title": "Document Review Specialist II" },
|
||||||
|
{ "id": 5, "desc": "Document Review", "title": "Document Review Attorney" },
|
||||||
|
{ "id": 4, "desc": "Document Review", "title": "Document Review Manager - Cyber Incident Response (Remote)" },
|
||||||
|
{ "id": 3, "desc": "Document Review", "title": "Document Review Paralegal" },
|
||||||
|
{ "id": 2, "desc": "Document Review", "title": "Document Controller (Saudi National)" },
|
||||||
|
{ "id": 1, "desc": "Document Review", "title": "Document Reviewer" },
|
||||||
|
{ "id": 7, "desc": "Document Review", "title": "Document Review Specialist II" },
|
||||||
|
{ "id": 6, "desc": "Document Review", "title": "Document Review (Entry Level)" }
|
||||||
|
]);
|
||||||
|
let index = index_with_documents(&server, &documents).await;
|
||||||
|
|
||||||
|
index
|
||||||
|
.search(
|
||||||
|
json!({"q": "\"Document Review\"", "attributesToSearchOn": ["title"], "attributesToRetrieve": ["title"]}),
|
||||||
|
|response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Document Review Specialist II"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Document Review Attorney"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Document Review Manager - Cyber Incident Response (Remote)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Document Review Paralegal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Document Review Specialist II"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Document Review (Entry Level)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,44 +1,185 @@
|
|||||||
use std::collections::HashMap;
|
use crate::common::Server;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
use crate::common::{Server, Value};
|
|
||||||
use crate::json;
|
use crate::json;
|
||||||
|
|
||||||
static DEFAULT_SETTINGS_VALUES: Lazy<HashMap<&'static str, Value>> = Lazy::new(|| {
|
macro_rules! test_setting_routes {
|
||||||
let mut map = HashMap::new();
|
($({setting: $setting:ident, update_verb: $update_verb:ident, default_value: $default_value:tt},) *) => {
|
||||||
map.insert("displayed_attributes", json!(["*"]));
|
$(
|
||||||
map.insert("searchable_attributes", json!(["*"]));
|
mod $setting {
|
||||||
map.insert("localized_attributes", json!(null));
|
use crate::common::Server;
|
||||||
map.insert("filterable_attributes", json!([]));
|
|
||||||
map.insert("distinct_attribute", json!(null));
|
#[actix_rt::test]
|
||||||
map.insert(
|
async fn get_unexisting_index() {
|
||||||
"ranking_rules",
|
let server = Server::new().await;
|
||||||
json!(["words", "typo", "proximity", "attribute", "sort", "exactness"]),
|
let url = format!("/indexes/test/settings/{}",
|
||||||
);
|
stringify!($setting)
|
||||||
map.insert("stop_words", json!([]));
|
.chars()
|
||||||
map.insert("non_separator_tokens", json!([]));
|
.map(|c| if c == '_' { '-' } else { c })
|
||||||
map.insert("separator_tokens", json!([]));
|
.collect::<String>());
|
||||||
map.insert("dictionary", json!([]));
|
let (_response, code) = server.service.get(url).await;
|
||||||
map.insert("synonyms", json!({}));
|
assert_eq!(code, 404);
|
||||||
map.insert(
|
}
|
||||||
"faceting",
|
|
||||||
json!({
|
#[actix_rt::test]
|
||||||
"maxValuesPerFacet": json!(100),
|
async fn update_unexisting_index() {
|
||||||
"sortFacetValuesBy": {
|
let server = Server::new().await;
|
||||||
"*": "alpha"
|
let url = format!("/indexes/test/settings/{}",
|
||||||
|
stringify!($setting)
|
||||||
|
.chars()
|
||||||
|
.map(|c| if c == '_' { '-' } else { c })
|
||||||
|
.collect::<String>());
|
||||||
|
let (response, code) = server.service.$update_verb(url, serde_json::Value::Null.into()).await;
|
||||||
|
assert_eq!(code, 202, "{}", response);
|
||||||
|
server.index("").wait_task(0).await;
|
||||||
|
let (response, code) = server.index("test").get().await;
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn delete_unexisting_index() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let url = format!("/indexes/test/settings/{}",
|
||||||
|
stringify!($setting)
|
||||||
|
.chars()
|
||||||
|
.map(|c| if c == '_' { '-' } else { c })
|
||||||
|
.collect::<String>());
|
||||||
|
let (_, code) = server.service.delete(url).await;
|
||||||
|
assert_eq!(code, 202);
|
||||||
|
let response = server.index("").wait_task(0).await;
|
||||||
|
assert_eq!(response["status"], "failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn get_default() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
let (response, code) = index.create(None).await;
|
||||||
|
assert_eq!(code, 202, "{}", response);
|
||||||
|
index.wait_task(0).await;
|
||||||
|
let url = format!("/indexes/test/settings/{}",
|
||||||
|
stringify!($setting)
|
||||||
|
.chars()
|
||||||
|
.map(|c| if c == '_' { '-' } else { c })
|
||||||
|
.collect::<String>());
|
||||||
|
let (response, code) = server.service.get(url).await;
|
||||||
|
assert_eq!(code, 200, "{}", response);
|
||||||
|
let expected = crate::json!($default_value);
|
||||||
|
assert_eq!(expected, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
)*
|
||||||
);
|
|
||||||
map.insert(
|
#[actix_rt::test]
|
||||||
"pagination",
|
async fn all_setting_tested() {
|
||||||
json!({
|
let expected = std::collections::BTreeSet::from_iter(meilisearch::routes::indexes::settings::ALL_SETTINGS_NAMES.iter());
|
||||||
"maxTotalHits": json!(1000),
|
let tested = std::collections::BTreeSet::from_iter([$(stringify!($setting)),*].iter());
|
||||||
}),
|
let diff: Vec<_> = expected.difference(&tested).collect();
|
||||||
);
|
assert!(diff.is_empty(), "Not all settings were tested, please add the following settings to the `test_setting_routes!` macro: {:?}", diff);
|
||||||
map.insert("search_cutoff_ms", json!(null));
|
}
|
||||||
map
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
test_setting_routes!(
|
||||||
|
{
|
||||||
|
setting: filterable_attributes,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: displayed_attributes,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: ["*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: localized_attributes,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: searchable_attributes,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: ["*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: distinct_attribute,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: stop_words,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: separator_tokens,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: non_separator_tokens,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: dictionary,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: ranking_rules,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: ["words", "typo", "proximity", "attribute", "sort", "exactness"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: synonyms,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: pagination,
|
||||||
|
update_verb: patch,
|
||||||
|
default_value: {"maxTotalHits": 1000}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: faceting,
|
||||||
|
update_verb: patch,
|
||||||
|
default_value: {"maxValuesPerFacet": 100, "sortFacetValuesBy": {"*": "alpha"}}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: search_cutoff_ms,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: embedders,
|
||||||
|
update_verb: patch,
|
||||||
|
default_value: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: facet_search,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: prefix_search,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: "indexingTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: proximity_precision,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: "byWord"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: sortable_attributes,
|
||||||
|
update_verb: put,
|
||||||
|
default_value: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
setting: typo_tolerance,
|
||||||
|
update_verb: patch,
|
||||||
|
default_value: {"enabled": true, "minWordSizeForTypos": {"oneTypo": 5, "twoTypos": 9}, "disableOnWords": [], "disableOnAttributes": []}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn get_settings_unexisting_index() {
|
async fn get_settings_unexisting_index() {
|
||||||
@@ -56,7 +197,7 @@ async fn get_settings() {
|
|||||||
let (response, code) = index.settings().await;
|
let (response, code) = index.settings().await;
|
||||||
assert_eq!(code, 200);
|
assert_eq!(code, 200);
|
||||||
let settings = response.as_object().unwrap();
|
let settings = response.as_object().unwrap();
|
||||||
assert_eq!(settings.keys().len(), 17);
|
assert_eq!(settings.keys().len(), 19);
|
||||||
assert_eq!(settings["displayedAttributes"], json!(["*"]));
|
assert_eq!(settings["displayedAttributes"], json!(["*"]));
|
||||||
assert_eq!(settings["searchableAttributes"], json!(["*"]));
|
assert_eq!(settings["searchableAttributes"], json!(["*"]));
|
||||||
assert_eq!(settings["filterableAttributes"], json!([]));
|
assert_eq!(settings["filterableAttributes"], json!([]));
|
||||||
@@ -87,6 +228,8 @@ async fn get_settings() {
|
|||||||
);
|
);
|
||||||
assert_eq!(settings["proximityPrecision"], json!("byWord"));
|
assert_eq!(settings["proximityPrecision"], json!("byWord"));
|
||||||
assert_eq!(settings["searchCutoffMs"], json!(null));
|
assert_eq!(settings["searchCutoffMs"], json!(null));
|
||||||
|
assert_eq!(settings["prefixSearch"], json!("indexingTime"));
|
||||||
|
assert_eq!(settings["facetSearch"], json!(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
@@ -199,7 +342,9 @@ async fn secrets_are_hidden_in_settings() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"searchCutoffMs": null,
|
"searchCutoffMs": null,
|
||||||
"localizedAttributes": null
|
"localizedAttributes": null,
|
||||||
|
"facetSearch": true,
|
||||||
|
"prefixSearch": "indexingTime"
|
||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
@@ -338,93 +483,6 @@ async fn error_update_setting_unexisting_index_invalid_uid() {
|
|||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test_setting_routes {
|
|
||||||
($($setting:ident $write_method:ident), *) => {
|
|
||||||
$(
|
|
||||||
mod $setting {
|
|
||||||
use crate::common::Server;
|
|
||||||
use super::DEFAULT_SETTINGS_VALUES;
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn get_unexisting_index() {
|
|
||||||
let server = Server::new().await;
|
|
||||||
let url = format!("/indexes/test/settings/{}",
|
|
||||||
stringify!($setting)
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c == '_' { '-' } else { c })
|
|
||||||
.collect::<String>());
|
|
||||||
let (_response, code) = server.service.get(url).await;
|
|
||||||
assert_eq!(code, 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn update_unexisting_index() {
|
|
||||||
let server = Server::new().await;
|
|
||||||
let url = format!("/indexes/test/settings/{}",
|
|
||||||
stringify!($setting)
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c == '_' { '-' } else { c })
|
|
||||||
.collect::<String>());
|
|
||||||
let (response, code) = server.service.$write_method(url, serde_json::Value::Null.into()).await;
|
|
||||||
assert_eq!(code, 202, "{}", response);
|
|
||||||
server.index("").wait_task(0).await;
|
|
||||||
let (response, code) = server.index("test").get().await;
|
|
||||||
assert_eq!(code, 200, "{}", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn delete_unexisting_index() {
|
|
||||||
let server = Server::new().await;
|
|
||||||
let url = format!("/indexes/test/settings/{}",
|
|
||||||
stringify!($setting)
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c == '_' { '-' } else { c })
|
|
||||||
.collect::<String>());
|
|
||||||
let (_, code) = server.service.delete(url).await;
|
|
||||||
assert_eq!(code, 202);
|
|
||||||
let response = server.index("").wait_task(0).await;
|
|
||||||
assert_eq!(response["status"], "failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn get_default() {
|
|
||||||
let server = Server::new().await;
|
|
||||||
let index = server.index("test");
|
|
||||||
let (response, code) = index.create(None).await;
|
|
||||||
assert_eq!(code, 202, "{}", response);
|
|
||||||
index.wait_task(0).await;
|
|
||||||
let url = format!("/indexes/test/settings/{}",
|
|
||||||
stringify!($setting)
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c == '_' { '-' } else { c })
|
|
||||||
.collect::<String>());
|
|
||||||
let (response, code) = server.service.get(url).await;
|
|
||||||
assert_eq!(code, 200, "{}", response);
|
|
||||||
let expected = DEFAULT_SETTINGS_VALUES.get(stringify!($setting)).unwrap();
|
|
||||||
assert_eq!(expected, &response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_setting_routes!(
|
|
||||||
filterable_attributes put,
|
|
||||||
displayed_attributes put,
|
|
||||||
localized_attributes put,
|
|
||||||
searchable_attributes put,
|
|
||||||
distinct_attribute put,
|
|
||||||
stop_words put,
|
|
||||||
separator_tokens put,
|
|
||||||
non_separator_tokens put,
|
|
||||||
dictionary put,
|
|
||||||
ranking_rules put,
|
|
||||||
synonyms put,
|
|
||||||
pagination patch,
|
|
||||||
faceting patch,
|
|
||||||
search_cutoff_ms put
|
|
||||||
);
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn error_set_invalid_ranking_rules() {
|
async fn error_set_invalid_ranking_rules() {
|
||||||
let server = Server::new().await;
|
let server = Server::new().await;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod distinct;
|
mod distinct;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod get_settings;
|
mod get_settings;
|
||||||
|
mod prefix_search_settings;
|
||||||
mod proximity_settings;
|
mod proximity_settings;
|
||||||
mod tokenizer_customization;
|
mod tokenizer_customization;
|
||||||
|
|||||||
458
crates/meilisearch/tests/settings/prefix_search_settings.rs
Normal file
458
crates/meilisearch/tests/settings/prefix_search_settings.rs
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
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,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn add_docs_and_disable() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"prefixSearch": "disabled",
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
// only 1 document should match
|
||||||
|
index
|
||||||
|
.search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day <em>so</em>",
|
||||||
|
"b": "manythe manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// only 1 document should match
|
||||||
|
index
|
||||||
|
.search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "<em>manythe</em> manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn disable_and_add_docs() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"prefixSearch": "disabled",
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
// only 1 document should match
|
||||||
|
index
|
||||||
|
.search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day <em>so</em>",
|
||||||
|
"b": "manythe manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
index
|
||||||
|
.search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "<em>manythe</em> manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn disable_add_docs_and_enable() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"prefixSearch": "disabled",
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"prefixSearch": "indexingTime",
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(2).await;
|
||||||
|
|
||||||
|
// all documents should match
|
||||||
|
index
|
||||||
|
.search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "1",
|
||||||
|
"a": "<em>So</em>up of the day",
|
||||||
|
"b": "manythefishou"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "<em>So</em>up of day <em>so</em>",
|
||||||
|
"b": "manythe manythelazyfish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "3",
|
||||||
|
"a": "the <em>So</em>up of day",
|
||||||
|
"b": "manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
index
|
||||||
|
.search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "1",
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "<em>manythe</em>fishou"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "<em>manythe</em> <em>manythe</em>lazyfish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "3",
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "<em>manythe</em>lazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn disable_add_docs_and_reset() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"prefixSearch": "disabled",
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"prefixSearch": serde_json::Value::Null,
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(2).await;
|
||||||
|
|
||||||
|
// all documents should match
|
||||||
|
index
|
||||||
|
.search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "1",
|
||||||
|
"a": "<em>So</em>up of the day",
|
||||||
|
"b": "manythefishou"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "<em>So</em>up of day <em>so</em>",
|
||||||
|
"b": "manythe manythelazyfish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "3",
|
||||||
|
"a": "the <em>So</em>up of day",
|
||||||
|
"b": "manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
index
|
||||||
|
.search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "1",
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "<em>manythe</em>fishou"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "<em>manythe</em> <em>manythe</em>lazyfish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "3",
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "<em>manythe</em>lazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn default_behavior() {
|
||||||
|
let server = Server::new().await;
|
||||||
|
let index = server.index("test");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"rankingRules": ["words", "typo", "proximity"],
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
assert_eq!("202", code.as_str(), "{:?}", response);
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await;
|
||||||
|
index.wait_task(response.uid()).await;
|
||||||
|
|
||||||
|
// all documents should match
|
||||||
|
index
|
||||||
|
.search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "1",
|
||||||
|
"a": "<em>So</em>up of the day",
|
||||||
|
"b": "manythefishou"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "<em>So</em>up of day <em>so</em>",
|
||||||
|
"b": "manythe manythelazyfish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "3",
|
||||||
|
"a": "the <em>So</em>up of day",
|
||||||
|
"b": "manythelazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
index
|
||||||
|
.search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| {
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "manythefishou",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "1",
|
||||||
|
"a": "Soup of the day",
|
||||||
|
"b": "<em>manythe</em>fishou"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "manythe manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "2",
|
||||||
|
"a": "Soup of day so",
|
||||||
|
"b": "<em>manythe</em> <em>manythe</em>lazyfish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "manythelazyfish",
|
||||||
|
"_formatted": {
|
||||||
|
"id": "3",
|
||||||
|
"a": "the Soup of day",
|
||||||
|
"b": "<em>manythe</em>lazyfish"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
@@ -129,11 +129,11 @@ async fn perform_on_demand_snapshot() {
|
|||||||
|
|
||||||
index.load_test_set().await;
|
index.load_test_set().await;
|
||||||
|
|
||||||
server.index("doggo").create(Some("bone")).await;
|
let (task, _) = server.index("doggo").create(Some("bone")).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(task.uid()).await.succeeded();
|
||||||
|
|
||||||
server.index("doggo").create(Some("bone")).await;
|
let (task, _) = server.index("doggo").create(Some("bone")).await;
|
||||||
index.wait_task(2).await;
|
index.wait_task(task.uid()).await.failed();
|
||||||
|
|
||||||
let (task, code) = server.create_snapshot().await;
|
let (task, code) = server.create_snapshot().await;
|
||||||
snapshot!(code, @"202 Accepted");
|
snapshot!(code, @"202 Accepted");
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Missing field `regenerate` inside `.manual`",
|
"message": "Bad embedder configuration in the document with id: `0`. Missing field `._vectors.manual.regenerate`\n - note: `._vectors.manual` must be an array of floats, an array of arrays of floats, or an object with field `regenerate`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -280,7 +280,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Missing field `regenerate` inside `.manual`",
|
"message": "Bad embedder configuration in the document with id: `0`. Missing field `._vectors.manual.regenerate`\n - note: `._vectors.manual` must be an array of floats, an array of arrays of floats, or an object with field `regenerate`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -311,7 +311,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.regenerate`: expected a boolean, but found a string: `\"yes please\"`",
|
"message": "Bad embedder configuration in the document with id: `0`. Could not parse `._vectors.manual.regenerate`: invalid type: string \"yes please\", expected a boolean at line 1 column 26",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -323,8 +323,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
let documents =
|
let documents = json!({"id": 0, "name": "kefir", "_vectors": { "manual": { "embeddings": true, "regenerate": true }}});
|
||||||
json!({"id": 0, "name": "kefir", "_vectors": { "manual": { "embeddings": true }}});
|
|
||||||
let (value, code) = index.add_documents(documents, None).await;
|
let (value, code) = index.add_documents(documents, None).await;
|
||||||
snapshot!(code, @"202 Accepted");
|
snapshot!(code, @"202 Accepted");
|
||||||
let task = index.wait_task(value.uid()).await;
|
let task = index.wait_task(value.uid()).await;
|
||||||
@@ -341,7 +340,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.embeddings`: expected null or an array, but found a boolean: `true`",
|
"message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings`: expected null or an array, but found a boolean: `true`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -353,8 +352,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
let documents =
|
let documents = json!({"id": 0, "name": "kefir", "_vectors": { "manual": { "embeddings": [true], "regenerate": true }}});
|
||||||
json!({"id": 0, "name": "kefir", "_vectors": { "manual": { "embeddings": [true] }}});
|
|
||||||
let (value, code) = index.add_documents(documents, None).await;
|
let (value, code) = index.add_documents(documents, None).await;
|
||||||
snapshot!(code, @"202 Accepted");
|
snapshot!(code, @"202 Accepted");
|
||||||
let task = index.wait_task(value.uid()).await;
|
let task = index.wait_task(value.uid()).await;
|
||||||
@@ -371,7 +369,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.embeddings[0]`: expected a number or an array, but found a boolean: `true`",
|
"message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0]`: expected a number or an array, but found a boolean: `true`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -383,8 +381,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
let documents =
|
let documents = json!({"id": 0, "name": "kefir", "_vectors": { "manual": { "embeddings": [[true]], "regenerate": false }}});
|
||||||
json!({"id": 0, "name": "kefir", "_vectors": { "manual": { "embeddings": [[true]] }}});
|
|
||||||
let (value, code) = index.add_documents(documents, None).await;
|
let (value, code) = index.add_documents(documents, None).await;
|
||||||
snapshot!(code, @"202 Accepted");
|
snapshot!(code, @"202 Accepted");
|
||||||
let task = index.wait_task(value.uid()).await;
|
let task = index.wait_task(value.uid()).await;
|
||||||
@@ -401,7 +398,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.embeddings[0][0]`: expected a number, but found a boolean: `true`",
|
"message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0][0]`: expected a number, but found a boolean: `true`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -443,7 +440,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.embeddings[1]`: expected a number, but found an array: `[0.2,0.3]`",
|
"message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[1]`: expected a number, but found an array: `[0.2,0.3]`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -472,7 +469,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.embeddings[1]`: expected an array, but found a number: `0.3`",
|
"message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[1]`: expected an array, but found a number: `0.3`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -501,7 +498,7 @@ async fn user_provided_embeddings_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Bad embedder configuration in the document with id: `\"0\"`. Invalid value type at `.manual.embeddings[0][1]`: expected a number, but found a boolean: `true`",
|
"message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0][1]`: expected a number, but found a boolean: `true`",
|
||||||
"code": "invalid_vectors_type",
|
"code": "invalid_vectors_type",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
"link": "https://docs.meilisearch.com/errors#invalid_vectors_type"
|
||||||
@@ -542,7 +539,7 @@ async fn user_provided_vectors_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "While embedding documents for embedder `manual`: no vectors provided for document \"40\" and at least 4 other document(s)\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: opt-out for a document with `_vectors.manual: null`",
|
"message": "While embedding documents for embedder `manual`: no vectors provided for document `40` and at least 4 other document(s)\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: opt-out for a document with `_vectors.manual: null`",
|
||||||
"code": "vector_embedding_error",
|
"code": "vector_embedding_error",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#vector_embedding_error"
|
"link": "https://docs.meilisearch.com/errors#vector_embedding_error"
|
||||||
@@ -572,7 +569,7 @@ async fn user_provided_vectors_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "While embedding documents for embedder `manual`: no vectors provided for document \"42\"\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vector` by `_vectors` in 1 document(s).",
|
"message": "While embedding documents for embedder `manual`: no vectors provided for document `42`\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vector` by `_vectors` in 1 document(s).",
|
||||||
"code": "vector_embedding_error",
|
"code": "vector_embedding_error",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#vector_embedding_error"
|
"link": "https://docs.meilisearch.com/errors#vector_embedding_error"
|
||||||
@@ -602,7 +599,7 @@ async fn user_provided_vectors_error() {
|
|||||||
"indexedDocuments": 0
|
"indexedDocuments": 0
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "While embedding documents for embedder `manual`: no vectors provided for document \"42\"\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vectors.manaul` by `_vectors.manual` in 1 document(s).",
|
"message": "While embedding documents for embedder `manual`: no vectors provided for document `42`\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vectors.manaul` by `_vectors.manual` in 1 document(s).",
|
||||||
"code": "vector_embedding_error",
|
"code": "vector_embedding_error",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#vector_embedding_error"
|
"link": "https://docs.meilisearch.com/errors#vector_embedding_error"
|
||||||
|
|||||||
@@ -137,13 +137,14 @@ fn long_text() -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn create_mock_tokenized() -> (MockServer, Value) {
|
async fn create_mock_tokenized() -> (MockServer, Value) {
|
||||||
create_mock_with_template("{{doc.text}}", ModelDimensions::Large, false).await
|
create_mock_with_template("{{doc.text}}", ModelDimensions::Large, false, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_mock_with_template(
|
async fn create_mock_with_template(
|
||||||
document_template: &str,
|
document_template: &str,
|
||||||
model_dimensions: ModelDimensions,
|
model_dimensions: ModelDimensions,
|
||||||
fallible: bool,
|
fallible: bool,
|
||||||
|
slow: bool,
|
||||||
) -> (MockServer, Value) {
|
) -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
const API_KEY: &str = "my-api-key";
|
const API_KEY: &str = "my-api-key";
|
||||||
@@ -154,7 +155,11 @@ async fn create_mock_with_template(
|
|||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/"))
|
.and(path("/"))
|
||||||
.respond_with(move |req: &Request| {
|
.respond_with(move |req: &Request| {
|
||||||
// 0. maybe return 500
|
// 0. wait for a long time
|
||||||
|
if slow {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
// 1. maybe return 500
|
||||||
if fallible {
|
if fallible {
|
||||||
let attempt = attempt.fetch_add(1, Ordering::Relaxed);
|
let attempt = attempt.fetch_add(1, Ordering::Relaxed);
|
||||||
let failed = matches!(attempt % 4, 0 | 1 | 3);
|
let failed = matches!(attempt % 4, 0 | 1 | 3);
|
||||||
@@ -167,7 +172,7 @@ async fn create_mock_with_template(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 1. check API key
|
// 2. check API key
|
||||||
match req.headers.get("Authorization") {
|
match req.headers.get("Authorization") {
|
||||||
Some(api_key) if api_key == API_KEY_BEARER => {
|
Some(api_key) if api_key == API_KEY_BEARER => {
|
||||||
{}
|
{}
|
||||||
@@ -202,7 +207,7 @@ async fn create_mock_with_template(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2. parse text inputs
|
// 3. parse text inputs
|
||||||
let query: serde_json::Value = match req.body_json() {
|
let query: serde_json::Value = match req.body_json() {
|
||||||
Ok(query) => query,
|
Ok(query) => query,
|
||||||
Err(_error) => return ResponseTemplate::new(400).set_body_json(
|
Err(_error) => return ResponseTemplate::new(400).set_body_json(
|
||||||
@@ -223,7 +228,7 @@ async fn create_mock_with_template(
|
|||||||
panic!("Expected {model_dimensions:?}, got {query_model_dimensions:?}")
|
panic!("Expected {model_dimensions:?}, got {query_model_dimensions:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. for each text, find embedding in responses
|
// 4. for each text, find embedding in responses
|
||||||
let serde_json::Value::Array(inputs) = &query["input"] else {
|
let serde_json::Value::Array(inputs) = &query["input"] else {
|
||||||
panic!("Unexpected `input` value")
|
panic!("Unexpected `input` value")
|
||||||
};
|
};
|
||||||
@@ -283,7 +288,7 @@ async fn create_mock_with_template(
|
|||||||
"embedding": embedding,
|
"embedding": embedding,
|
||||||
})).collect();
|
})).collect();
|
||||||
|
|
||||||
// 4. produce output from embeddings
|
// 5. produce output from embeddings
|
||||||
ResponseTemplate::new(200).set_body_json(json!({
|
ResponseTemplate::new(200).set_body_json(json!({
|
||||||
"object": "list",
|
"object": "list",
|
||||||
"data": data,
|
"data": data,
|
||||||
@@ -317,23 +322,27 @@ const DOGGO_TEMPLATE: &str = r#"{%- if doc.gender == "F" -%}Une chienne nommée
|
|||||||
{%- endif %}, de race {{doc.breed}}."#;
|
{%- endif %}, de race {{doc.breed}}."#;
|
||||||
|
|
||||||
async fn create_mock() -> (MockServer, Value) {
|
async fn create_mock() -> (MockServer, Value) {
|
||||||
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large, false).await
|
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large, false, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_mock_dimensions() -> (MockServer, Value) {
|
async fn create_mock_dimensions() -> (MockServer, Value) {
|
||||||
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large512, false).await
|
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large512, false, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_mock_small_embedding_model() -> (MockServer, Value) {
|
async fn create_mock_small_embedding_model() -> (MockServer, Value) {
|
||||||
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Small, false).await
|
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Small, false, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_mock_legacy_embedding_model() -> (MockServer, Value) {
|
async fn create_mock_legacy_embedding_model() -> (MockServer, Value) {
|
||||||
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Ada, false).await
|
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Ada, false, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_fallible_mock() -> (MockServer, Value) {
|
async fn create_fallible_mock() -> (MockServer, Value) {
|
||||||
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large, true).await
|
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large, true, false).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_slow_mock() -> (MockServer, Value) {
|
||||||
|
create_mock_with_template(DOGGO_TEMPLATE, ModelDimensions::Large, true, true).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// basic test "it works"
|
// basic test "it works"
|
||||||
@@ -1884,4 +1893,115 @@ async fn it_still_works() {
|
|||||||
]
|
]
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test with a server that responds 500 on 3 out of 4 calls
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn timeout() {
|
||||||
|
let (_mock, setting) = create_slow_mock().await;
|
||||||
|
let server = get_server_vector().await;
|
||||||
|
let index = server.index("doggo");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.update_settings(json!({
|
||||||
|
"embedders": {
|
||||||
|
"default": setting,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
snapshot!(code, @"202 Accepted");
|
||||||
|
let task = server.wait_task(response.uid()).await;
|
||||||
|
snapshot!(task["status"], @r###""succeeded""###);
|
||||||
|
let documents = json!([
|
||||||
|
{"id": 0, "name": "kefir", "gender": "M", "birthyear": 2023, "breed": "Patou"},
|
||||||
|
]);
|
||||||
|
let (value, code) = index.add_documents(documents, None).await;
|
||||||
|
snapshot!(code, @"202 Accepted");
|
||||||
|
let task = index.wait_task(value.uid()).await;
|
||||||
|
snapshot!(task, @r###"
|
||||||
|
{
|
||||||
|
"uid": "[uid]",
|
||||||
|
"batchUid": "[batch_uid]",
|
||||||
|
"indexUid": "doggo",
|
||||||
|
"status": "succeeded",
|
||||||
|
"type": "documentAdditionOrUpdate",
|
||||||
|
"canceledBy": null,
|
||||||
|
"details": {
|
||||||
|
"receivedDocuments": 1,
|
||||||
|
"indexedDocuments": 1
|
||||||
|
},
|
||||||
|
"error": null,
|
||||||
|
"duration": "[duration]",
|
||||||
|
"enqueuedAt": "[date]",
|
||||||
|
"startedAt": "[date]",
|
||||||
|
"finishedAt": "[date]"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let (documents, _code) = index
|
||||||
|
.get_all_documents(GetAllDocumentsOptions { retrieve_vectors: true, ..Default::default() })
|
||||||
|
.await;
|
||||||
|
snapshot!(json_string!(documents, {".results.*._vectors.default.embeddings" => "[vector]"}), @r###"
|
||||||
|
{
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou",
|
||||||
|
"_vectors": {
|
||||||
|
"default": {
|
||||||
|
"embeddings": "[vector]",
|
||||||
|
"regenerate": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"offset": 0,
|
||||||
|
"limit": 20,
|
||||||
|
"total": 1
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.search_post(json!({
|
||||||
|
"q": "grand chien de berger des montagnes",
|
||||||
|
"hybrid": {"semanticRatio": 0.99, "embedder": "default"}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["semanticHitCount"]), @"0");
|
||||||
|
snapshot!(json_string!(response["hits"]), @"[]");
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.search_post(json!({
|
||||||
|
"q": "grand chien de berger des montagnes",
|
||||||
|
"hybrid": {"semanticRatio": 0.99, "embedder": "default"}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["semanticHitCount"]), @"1");
|
||||||
|
snapshot!(json_string!(response["hits"]), @r###"
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "kefir",
|
||||||
|
"gender": "M",
|
||||||
|
"birthyear": 2023,
|
||||||
|
"breed": "Patou"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let (response, code) = index
|
||||||
|
.search_post(json!({
|
||||||
|
"q": "grand chien de berger des montagnes",
|
||||||
|
"hybrid": {"semanticRatio": 0.99, "embedder": "default"}
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
snapshot!(code, @"200 OK");
|
||||||
|
snapshot!(json_string!(response["semanticHitCount"]), @"0");
|
||||||
|
snapshot!(json_string!(response["hits"]), @"[]");
|
||||||
|
}
|
||||||
|
|
||||||
// test with a server that wrongly responds 400
|
// test with a server that wrongly responds 400
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use meili_snap::{json_string, snapshot};
|
use meili_snap::{json_string, snapshot};
|
||||||
use reqwest::IntoUrl;
|
use reqwest::IntoUrl;
|
||||||
@@ -13,13 +12,22 @@ use crate::vector::{get_server_vector, GetAllDocumentsOptions};
|
|||||||
async fn create_mock() -> (MockServer, Value) {
|
async fn create_mock() -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
let counter = AtomicUsize::new(0);
|
let text_to_embedding: BTreeMap<_, _> = vec![
|
||||||
|
// text -> embedding
|
||||||
|
("kefir", [0.0, 0.0, 0.0]),
|
||||||
|
("intel", [1.0, 1.0, 1.0]),
|
||||||
|
]
|
||||||
|
// turn into btree
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/"))
|
.and(path("/"))
|
||||||
.respond_with(move |_req: &Request| {
|
.respond_with(move |req: &Request| {
|
||||||
let counter = counter.fetch_add(1, Ordering::Relaxed);
|
let text: String = req.body_json().unwrap();
|
||||||
ResponseTemplate::new(200).set_body_json(json!({ "data": vec![counter; 3] }))
|
ResponseTemplate::new(200).set_body_json(
|
||||||
|
json!({ "data": text_to_embedding.get(text.as_str()).unwrap_or(&[99., 99., 99.]) }),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.mount(&mock_server)
|
.mount(&mock_server)
|
||||||
.await;
|
.await;
|
||||||
@@ -32,13 +40,14 @@ async fn create_mock() -> (MockServer, Value) {
|
|||||||
"request": "{{text}}",
|
"request": "{{text}}",
|
||||||
"response": {
|
"response": {
|
||||||
"data": "{{embedding}}"
|
"data": "{{embedding}}"
|
||||||
}
|
},
|
||||||
|
"documentTemplate": "{{doc.name}}",
|
||||||
});
|
});
|
||||||
|
|
||||||
(mock_server, embedder_settings)
|
(mock_server, embedder_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_mock_map() -> (MockServer, Value) {
|
async fn create_mock_default_template() -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
let text_to_embedding: BTreeMap<_, _> = vec![
|
let text_to_embedding: BTreeMap<_, _> = vec![
|
||||||
@@ -97,7 +106,14 @@ struct SingleResponse {
|
|||||||
async fn create_mock_multiple() -> (MockServer, Value) {
|
async fn create_mock_multiple() -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
let counter = AtomicUsize::new(0);
|
let text_to_embedding: BTreeMap<_, _> = vec![
|
||||||
|
// text -> embedding
|
||||||
|
("kefir", [0.0, 0.0, 0.0]),
|
||||||
|
("intel", [1.0, 1.0, 1.0]),
|
||||||
|
]
|
||||||
|
// turn into btree
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/"))
|
.and(path("/"))
|
||||||
@@ -115,8 +131,11 @@ async fn create_mock_multiple() -> (MockServer, Value) {
|
|||||||
.input
|
.input
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|text| SingleResponse {
|
.map(|text| SingleResponse {
|
||||||
|
embedding: text_to_embedding
|
||||||
|
.get(text.as_str())
|
||||||
|
.unwrap_or(&[99., 99., 99.])
|
||||||
|
.to_vec(),
|
||||||
text,
|
text,
|
||||||
embedding: vec![counter.fetch_add(1, Ordering::Relaxed) as f32; 3],
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -142,7 +161,8 @@ async fn create_mock_multiple() -> (MockServer, Value) {
|
|||||||
},
|
},
|
||||||
"{{..}}"
|
"{{..}}"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"documentTemplate": "{{doc.name}}"
|
||||||
});
|
});
|
||||||
|
|
||||||
(mock_server, embedder_settings)
|
(mock_server, embedder_settings)
|
||||||
@@ -156,7 +176,14 @@ struct SingleRequest {
|
|||||||
async fn create_mock_single_response_in_array() -> (MockServer, Value) {
|
async fn create_mock_single_response_in_array() -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
let counter = AtomicUsize::new(0);
|
let text_to_embedding: BTreeMap<_, _> = vec![
|
||||||
|
// text -> embedding
|
||||||
|
("kefir", [0.0, 0.0, 0.0]),
|
||||||
|
("intel", [1.0, 1.0, 1.0]),
|
||||||
|
]
|
||||||
|
// turn into btree
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/"))
|
.and(path("/"))
|
||||||
@@ -171,8 +198,11 @@ async fn create_mock_single_response_in_array() -> (MockServer, Value) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let output = vec![SingleResponse {
|
let output = vec![SingleResponse {
|
||||||
|
embedding: text_to_embedding
|
||||||
|
.get(req.input.as_str())
|
||||||
|
.unwrap_or(&[99., 99., 99.])
|
||||||
|
.to_vec(),
|
||||||
text: req.input,
|
text: req.input,
|
||||||
embedding: vec![counter.fetch_add(1, Ordering::Relaxed) as f32; 3],
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let response = MultipleResponse { output };
|
let response = MultipleResponse { output };
|
||||||
@@ -196,7 +226,8 @@ async fn create_mock_single_response_in_array() -> (MockServer, Value) {
|
|||||||
"embedding": "{{embedding}}"
|
"embedding": "{{embedding}}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"documentTemplate": "{{doc.name}}"
|
||||||
});
|
});
|
||||||
|
|
||||||
(mock_server, embedder_settings)
|
(mock_server, embedder_settings)
|
||||||
@@ -205,7 +236,14 @@ async fn create_mock_single_response_in_array() -> (MockServer, Value) {
|
|||||||
async fn create_mock_raw_with_custom_header() -> (MockServer, Value) {
|
async fn create_mock_raw_with_custom_header() -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
let counter = AtomicUsize::new(0);
|
let text_to_embedding: BTreeMap<_, _> = vec![
|
||||||
|
// text -> embedding
|
||||||
|
("kefir", [0.0, 0.0, 0.0]),
|
||||||
|
("intel", [1.0, 1.0, 1.0]),
|
||||||
|
]
|
||||||
|
// turn into btree
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/"))
|
.and(path("/"))
|
||||||
@@ -223,7 +261,7 @@ async fn create_mock_raw_with_custom_header() -> (MockServer, Value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _req: String = match req.body_json() {
|
let req: String = match req.body_json() {
|
||||||
Ok(req) => req,
|
Ok(req) => req,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return ResponseTemplate::new(400).set_body_json(json!({
|
return ResponseTemplate::new(400).set_body_json(json!({
|
||||||
@@ -232,7 +270,7 @@ async fn create_mock_raw_with_custom_header() -> (MockServer, Value) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = vec![counter.fetch_add(1, Ordering::Relaxed) as f32; 3];
|
let output = text_to_embedding.get(req.as_str()).unwrap_or(&[99., 99., 99.]).to_vec();
|
||||||
|
|
||||||
ResponseTemplate::new(200).set_body_json(output)
|
ResponseTemplate::new(200).set_body_json(output)
|
||||||
})
|
})
|
||||||
@@ -245,7 +283,8 @@ async fn create_mock_raw_with_custom_header() -> (MockServer, Value) {
|
|||||||
"url": url,
|
"url": url,
|
||||||
"request": "{{text}}",
|
"request": "{{text}}",
|
||||||
"response": "{{embedding}}",
|
"response": "{{embedding}}",
|
||||||
"headers": {"my-nonstandard-auth": "bearer of the ring"}
|
"headers": {"my-nonstandard-auth": "bearer of the ring"},
|
||||||
|
"documentTemplate": "{{doc.name}}"
|
||||||
});
|
});
|
||||||
|
|
||||||
(mock_server, embedder_settings)
|
(mock_server, embedder_settings)
|
||||||
@@ -254,12 +293,19 @@ async fn create_mock_raw_with_custom_header() -> (MockServer, Value) {
|
|||||||
async fn create_mock_raw() -> (MockServer, Value) {
|
async fn create_mock_raw() -> (MockServer, Value) {
|
||||||
let mock_server = MockServer::start().await;
|
let mock_server = MockServer::start().await;
|
||||||
|
|
||||||
let counter = AtomicUsize::new(0);
|
let text_to_embedding: BTreeMap<_, _> = vec![
|
||||||
|
// text -> embedding
|
||||||
|
("kefir", [0.0, 0.0, 0.0]),
|
||||||
|
("intel", [1.0, 1.0, 1.0]),
|
||||||
|
]
|
||||||
|
// turn into btree
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
Mock::given(method("POST"))
|
Mock::given(method("POST"))
|
||||||
.and(path("/"))
|
.and(path("/"))
|
||||||
.respond_with(move |req: &Request| {
|
.respond_with(move |req: &Request| {
|
||||||
let _req: String = match req.body_json() {
|
let req: String = match req.body_json() {
|
||||||
Ok(req) => req,
|
Ok(req) => req,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return ResponseTemplate::new(400).set_body_json(json!({
|
return ResponseTemplate::new(400).set_body_json(json!({
|
||||||
@@ -268,7 +314,7 @@ async fn create_mock_raw() -> (MockServer, Value) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = vec![counter.fetch_add(1, Ordering::Relaxed) as f32; 3];
|
let output = text_to_embedding.get(req.as_str()).unwrap_or(&[99., 99., 99.]).to_vec();
|
||||||
|
|
||||||
ResponseTemplate::new(200).set_body_json(output)
|
ResponseTemplate::new(200).set_body_json(output)
|
||||||
})
|
})
|
||||||
@@ -281,29 +327,30 @@ async fn create_mock_raw() -> (MockServer, Value) {
|
|||||||
"url": url,
|
"url": url,
|
||||||
"dimensions": 3,
|
"dimensions": 3,
|
||||||
"request": "{{text}}",
|
"request": "{{text}}",
|
||||||
"response": "{{embedding}}"
|
"response": "{{embedding}}",
|
||||||
|
"documentTemplate": "{{doc.name}}"
|
||||||
});
|
});
|
||||||
|
|
||||||
(mock_server, embedder_settings)
|
(mock_server, embedder_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post<T: IntoUrl>(url: T) -> reqwest::Result<reqwest::Response> {
|
pub async fn post<T: IntoUrl>(url: T, text: &str) -> reqwest::Result<reqwest::Response> {
|
||||||
reqwest::Client::builder().build()?.post(url).send().await
|
reqwest::Client::builder().build()?.post(url).json(&json!(text)).send().await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn dummy_testing_the_mock() {
|
async fn dummy_testing_the_mock() {
|
||||||
let (mock, _setting) = create_mock().await;
|
let (mock, _setting) = create_mock().await;
|
||||||
let body = post(&mock.uri()).await.unwrap().text().await.unwrap();
|
let body = post(&mock.uri(), "kefir").await.unwrap().text().await.unwrap();
|
||||||
snapshot!(body, @r###"{"data":[0,0,0]}"###);
|
snapshot!(body, @r###"{"data":[0.0,0.0,0.0]}"###);
|
||||||
let body = post(&mock.uri()).await.unwrap().text().await.unwrap();
|
let body = post(&mock.uri(), "intel").await.unwrap().text().await.unwrap();
|
||||||
snapshot!(body, @r###"{"data":[1,1,1]}"###);
|
snapshot!(body, @r###"{"data":[1.0,1.0,1.0]}"###);
|
||||||
let body = post(&mock.uri()).await.unwrap().text().await.unwrap();
|
let body = post(&mock.uri(), "kefir").await.unwrap().text().await.unwrap();
|
||||||
snapshot!(body, @r###"{"data":[2,2,2]}"###);
|
snapshot!(body, @r###"{"data":[0.0,0.0,0.0]}"###);
|
||||||
let body = post(&mock.uri()).await.unwrap().text().await.unwrap();
|
let body = post(&mock.uri(), "kefir").await.unwrap().text().await.unwrap();
|
||||||
snapshot!(body, @r###"{"data":[3,3,3]}"###);
|
snapshot!(body, @r###"{"data":[0.0,0.0,0.0]}"###);
|
||||||
let body = post(&mock.uri()).await.unwrap().text().await.unwrap();
|
let body = post(&mock.uri(), "intel").await.unwrap().text().await.unwrap();
|
||||||
snapshot!(body, @r###"{"data":[4,4,4]}"###);
|
snapshot!(body, @r###"{"data":[1.0,1.0,1.0]}"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
@@ -954,7 +1001,7 @@ async fn bad_settings() {
|
|||||||
let (response, code) = index
|
let (response, code) = index
|
||||||
.update_settings(json!({
|
.update_settings(json!({
|
||||||
"embedders": {
|
"embedders": {
|
||||||
"rest": json!({ "source": "rest", "url": mock.uri(), "request": "{{text}}", "response": { "data": "{{embedding}}" }, "dimensions": 2 }),
|
"rest": json!({ "source": "rest", "url": mock.uri(), "request": "{{text}}", "response": { "data": "{{embedding}}" }, "dimensions": 2, "documentTemplate": "{{doc.name}}" }),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
@@ -1937,6 +1984,7 @@ async fn server_custom_header() {
|
|||||||
"embedders": {
|
"embedders": {
|
||||||
"rest": {
|
"rest": {
|
||||||
"source": "rest",
|
"source": "rest",
|
||||||
|
"documentTemplate": "{{doc.name}}",
|
||||||
"url": "[url]",
|
"url": "[url]",
|
||||||
"request": "{{text}}",
|
"request": "{{text}}",
|
||||||
"response": "{{embedding}}",
|
"response": "{{embedding}}",
|
||||||
@@ -1957,7 +2005,7 @@ async fn server_custom_header() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn searchable_reindex() {
|
async fn searchable_reindex() {
|
||||||
let (_mock, setting) = create_mock_map().await;
|
let (_mock, setting) = create_mock_default_template().await;
|
||||||
let server = get_server_vector().await;
|
let server = get_server_vector().await;
|
||||||
let index = server.index("doggo");
|
let index = server.index("doggo");
|
||||||
|
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ fn export_a_dump(
|
|||||||
format!("While iterating on content file {:?}", content_file_uuid)
|
format!("While iterating on content file {:?}", content_file_uuid)
|
||||||
})? {
|
})? {
|
||||||
dump_content_file
|
dump_content_file
|
||||||
.push_document(&obkv_to_object(&doc, &documents_batch_index)?)?;
|
.push_document(&obkv_to_object(doc, &documents_batch_index)?)?;
|
||||||
}
|
}
|
||||||
dump_content_file.flush()?;
|
dump_content_file.flush()?;
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ readme.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
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.9.1"
|
bstr = "1.9.1"
|
||||||
bytemuck = { version = "1.18.0", features = ["extern_crate_alloc"] }
|
bytemuck = { version = "1.18.0", features = ["extern_crate_alloc"] }
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
charabia = { version = "0.9.1", default-features = false }
|
charabia = { version = "0.9.2", default-features = false }
|
||||||
concat-arrays = "0.1.2"
|
concat-arrays = "0.1.2"
|
||||||
crossbeam-channel = "0.5.13"
|
crossbeam-channel = "0.5.13"
|
||||||
deserr = "0.6.2"
|
deserr = "0.6.2"
|
||||||
@@ -26,10 +27,7 @@ flatten-serde-json = { path = "../flatten-serde-json" }
|
|||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
geoutils = "0.5.1"
|
geoutils = "0.5.1"
|
||||||
grenad = { version = "0.4.7", default-features = false, features = [
|
grenad = { version = "0.5.0", default-features = false, features = ["rayon", "tempfile"] }
|
||||||
"rayon",
|
|
||||||
"tempfile",
|
|
||||||
] }
|
|
||||||
heed = { version = "0.20.3", default-features = false, features = [
|
heed = { version = "0.20.3", default-features = false, features = [
|
||||||
"serde-json",
|
"serde-json",
|
||||||
"serde-bincode",
|
"serde-bincode",
|
||||||
@@ -40,14 +38,14 @@ 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.5.0"
|
memchr = "2.5.0"
|
||||||
memmap2 = "0.9.4"
|
memmap2 = "0.9.4"
|
||||||
obkv = "0.2.2"
|
obkv = "0.3.0"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
ordered-float = "4.2.1"
|
ordered-float = "4.2.1"
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
roaring = { version = "0.10.6", features = ["serde"] }
|
roaring = { version = "0.10.7", features = ["serde"] }
|
||||||
rstar = { version = "0.12.0", features = ["serde"] }
|
rstar = { version = "0.12.0", features = ["serde"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.120", features = ["preserve_order"] }
|
serde_json = { version = "1.0.120", 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.0", features = ["serde"] }
|
||||||
smallvec = "1.13.2"
|
smallvec = "1.13.2"
|
||||||
@@ -79,17 +77,32 @@ hf-hub = { git = "https://github.com/dureuill/hf-hub.git", branch = "rust_tls",
|
|||||||
] }
|
] }
|
||||||
tiktoken-rs = "0.5.9"
|
tiktoken-rs = "0.5.9"
|
||||||
liquid = "0.26.6"
|
liquid = "0.26.6"
|
||||||
rhai = { git = "https://github.com/rhaiscript/rhai", rev = "ef3df63121d27aacd838f366f2b83fd65f20a1e4", features = ["serde", "no_module", "no_custom_syntax", "no_time", "sync"] }
|
rhai = { git = "https://github.com/rhaiscript/rhai", rev = "ef3df63121d27aacd838f366f2b83fd65f20a1e4", features = [
|
||||||
|
"serde",
|
||||||
|
"no_module",
|
||||||
|
"no_custom_syntax",
|
||||||
|
"no_time",
|
||||||
|
"sync",
|
||||||
|
] }
|
||||||
arroy = "0.5.0"
|
arroy = "0.5.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
ureq = { version = "2.10.0", features = ["json"] }
|
ureq = { version = "2.10.0", features = ["json"] }
|
||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
rayon-par-bridge = "0.1.0"
|
rayon-par-bridge = "0.1.0"
|
||||||
|
hashbrown = "0.15.0"
|
||||||
|
raw-collections = { git = "https://github.com/meilisearch/raw-collections.git", version = "0.1.0" }
|
||||||
|
bumpalo = "3.16.0"
|
||||||
|
thread_local = "1.1.8"
|
||||||
|
allocator-api2 = "0.2.18"
|
||||||
|
rustc-hash = "2.0.0"
|
||||||
|
uell = "0.1.0"
|
||||||
|
enum-iterator = "2.1.0"
|
||||||
|
bbqueue = { git = "https://github.com/meilisearch/bbqueue" }
|
||||||
|
flume = { version = "0.11.1", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mimalloc = { version = "0.1.43", default-features = false }
|
mimalloc = { version = "0.1.43", default-features = false }
|
||||||
big_s = "1.0.2"
|
|
||||||
insta = "1.39.0"
|
insta = "1.39.0"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Cursor, Seek};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use heed::EnvOpenOptions;
|
|
||||||
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
|
||||||
use milli::{Index, Object};
|
|
||||||
|
|
||||||
fn usage(error: &str, program_name: &str) -> String {
|
|
||||||
format!(
|
|
||||||
"{}. Usage: {} <PATH-TO-INDEX> <PATH-TO-DATASET> [searchable_fields] [filterable_fields]",
|
|
||||||
error, program_name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut args = std::env::args();
|
|
||||||
let program_name = args.next().expect("No program name");
|
|
||||||
let index_path =
|
|
||||||
args.next().unwrap_or_else(|| panic!("{}", usage("Missing path to index.", &program_name)));
|
|
||||||
let dataset_path = args
|
|
||||||
.next()
|
|
||||||
.unwrap_or_else(|| panic!("{}", usage("Missing path to source dataset.", &program_name)));
|
|
||||||
// let primary_key = args.next().unwrap_or_else(|| "id".into());
|
|
||||||
// "title overview"
|
|
||||||
let searchable_fields: Vec<String> = args
|
|
||||||
.next()
|
|
||||||
.map(|arg| arg.split_whitespace().map(ToString::to_string).collect())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
println!("{searchable_fields:?}");
|
|
||||||
// "release_date genres"
|
|
||||||
let filterable_fields: Vec<String> = args
|
|
||||||
.next()
|
|
||||||
.map(|arg| arg.split_whitespace().map(ToString::to_string).collect())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let mut options = EnvOpenOptions::new();
|
|
||||||
options.map_size(100 * 1024 * 1024 * 1024); // 100 GB
|
|
||||||
|
|
||||||
std::fs::create_dir_all(&index_path).unwrap();
|
|
||||||
let index = Index::new(options, index_path).unwrap();
|
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
|
||||||
|
|
||||||
let config = IndexerConfig::default();
|
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
|
||||||
// builder.set_primary_key(primary_key);
|
|
||||||
let searchable_fields = searchable_fields.iter().map(|s| s.to_string()).collect();
|
|
||||||
builder.set_searchable_fields(searchable_fields);
|
|
||||||
let filterable_fields = filterable_fields.iter().map(|s| s.to_string()).collect();
|
|
||||||
builder.set_filterable_fields(filterable_fields);
|
|
||||||
|
|
||||||
builder.execute(|_| (), || false).unwrap();
|
|
||||||
|
|
||||||
let config = IndexerConfig::default();
|
|
||||||
let indexing_config = IndexDocumentsConfig::default();
|
|
||||||
|
|
||||||
let builder =
|
|
||||||
IndexDocuments::new(&mut wtxn, &index, &config, indexing_config, |_| (), || false).unwrap();
|
|
||||||
|
|
||||||
let documents = documents_from(
|
|
||||||
&dataset_path,
|
|
||||||
Path::new(&dataset_path).extension().unwrap_or_default().to_str().unwrap_or_default(),
|
|
||||||
);
|
|
||||||
let (builder, user_error) = builder.add_documents(documents).unwrap();
|
|
||||||
user_error.unwrap();
|
|
||||||
builder.execute().unwrap();
|
|
||||||
wtxn.commit().unwrap();
|
|
||||||
|
|
||||||
index.prepare_for_closing().wait();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn documents_from(filename: &str, filetype: &str) -> DocumentsBatchReader<impl BufRead + Seek> {
|
|
||||||
let reader = File::open(filename)
|
|
||||||
.unwrap_or_else(|_| panic!("could not find the dataset in: {}", filename));
|
|
||||||
let reader = BufReader::new(reader);
|
|
||||||
let documents = match filetype {
|
|
||||||
"csv" => documents_from_csv(reader).unwrap(),
|
|
||||||
"json" => documents_from_json(reader).unwrap(),
|
|
||||||
"jsonl" => documents_from_jsonl(reader).unwrap(),
|
|
||||||
otherwise => panic!("invalid update format {:?}", otherwise),
|
|
||||||
};
|
|
||||||
DocumentsBatchReader::from_reader(Cursor::new(documents)).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn documents_from_jsonl(reader: impl BufRead) -> milli::Result<Vec<u8>> {
|
|
||||||
let mut documents = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
|
|
||||||
for result in serde_json::Deserializer::from_reader(reader).into_iter::<Object>() {
|
|
||||||
let object = result.unwrap();
|
|
||||||
documents.append_json_object(&object)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
documents.into_inner().map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn documents_from_json(reader: impl BufRead) -> milli::Result<Vec<u8>> {
|
|
||||||
let mut documents = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
|
|
||||||
documents.append_json_array(reader)?;
|
|
||||||
|
|
||||||
documents.into_inner().map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn documents_from_csv(reader: impl BufRead) -> milli::Result<Vec<u8>> {
|
|
||||||
let csv = csv::Reader::from_reader(reader);
|
|
||||||
|
|
||||||
let mut documents = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
documents.append_csv(csv)?;
|
|
||||||
|
|
||||||
documents.into_inner().map_err(Into::into)
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::io::stdin;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use heed::EnvOpenOptions;
|
|
||||||
use milli::{
|
|
||||||
execute_search, filtered_universe, DefaultSearchLogger, GeoSortStrategy, Index, SearchContext,
|
|
||||||
SearchLogger, TermsMatchingStrategy, TimeBudget,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut args = std::env::args();
|
|
||||||
let program_name = args.next().expect("No program name");
|
|
||||||
let dataset = args.next().unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"Missing path to index. Usage: {} <PATH-TO-INDEX> [<logger-dir>] [print-documents]",
|
|
||||||
program_name
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let detailed_logger_dir = args.next();
|
|
||||||
let print_documents: bool =
|
|
||||||
if let Some(arg) = args.next() { arg == "print-documents" } else { false };
|
|
||||||
|
|
||||||
let mut options = EnvOpenOptions::new();
|
|
||||||
options.map_size(100 * 1024 * 1024 * 1024); // 100 GB
|
|
||||||
|
|
||||||
let index = Index::new(options, dataset)?;
|
|
||||||
let txn = index.read_txn()?;
|
|
||||||
let mut query = String::new();
|
|
||||||
while stdin().read_line(&mut query)? > 0 {
|
|
||||||
for _ in 0..2 {
|
|
||||||
let mut default_logger = DefaultSearchLogger;
|
|
||||||
// FIXME: consider resetting the state of the logger between search executions as otherwise panics are possible.
|
|
||||||
// Workaround'd here by recreating the logger on each iteration of the loop
|
|
||||||
let mut detailed_logger = detailed_logger_dir
|
|
||||||
.as_ref()
|
|
||||||
.map(|logger_dir| (milli::VisualSearchLogger::default(), logger_dir));
|
|
||||||
let logger: &mut dyn SearchLogger<_> =
|
|
||||||
if let Some((detailed_logger, _)) = detailed_logger.as_mut() {
|
|
||||||
detailed_logger
|
|
||||||
} else {
|
|
||||||
&mut default_logger
|
|
||||||
};
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
|
|
||||||
let mut ctx = SearchContext::new(&index, &txn)?;
|
|
||||||
let universe = filtered_universe(ctx.index, ctx.txn, &None)?;
|
|
||||||
|
|
||||||
let docs = execute_search(
|
|
||||||
&mut ctx,
|
|
||||||
(!query.trim().is_empty()).then(|| query.trim()),
|
|
||||||
TermsMatchingStrategy::Last,
|
|
||||||
milli::score_details::ScoringStrategy::Skip,
|
|
||||||
false,
|
|
||||||
universe,
|
|
||||||
&None,
|
|
||||||
&None,
|
|
||||||
GeoSortStrategy::default(),
|
|
||||||
0,
|
|
||||||
20,
|
|
||||||
None,
|
|
||||||
&mut DefaultSearchLogger,
|
|
||||||
logger,
|
|
||||||
TimeBudget::max(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
if let Some((logger, dir)) = detailed_logger {
|
|
||||||
logger.finish(&mut ctx, Path::new(dir))?;
|
|
||||||
}
|
|
||||||
let elapsed = start.elapsed();
|
|
||||||
println!("new: {}us, docids: {:?}", elapsed.as_micros(), docs.documents_ids);
|
|
||||||
if print_documents {
|
|
||||||
let documents = index
|
|
||||||
.documents(&txn, docs.documents_ids.iter().copied())
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|(id, obkv)| {
|
|
||||||
let mut object = serde_json::Map::default();
|
|
||||||
for (fid, fid_name) in index.fields_ids_map(&txn).unwrap().iter() {
|
|
||||||
let value = obkv.get(fid).unwrap();
|
|
||||||
let value: serde_json::Value = serde_json::from_slice(value).unwrap();
|
|
||||||
object.insert(fid_name.to_owned(), value);
|
|
||||||
}
|
|
||||||
(id, serde_json::to_string_pretty(&object).unwrap())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for (id, document) in documents {
|
|
||||||
println!("{id}:");
|
|
||||||
println!("{document}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let documents = index
|
|
||||||
.documents(&txn, docs.documents_ids.iter().copied())
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|(id, obkv)| {
|
|
||||||
let mut object = serde_json::Map::default();
|
|
||||||
for (fid, fid_name) in index.fields_ids_map(&txn).unwrap().iter() {
|
|
||||||
let value = obkv.get(fid).unwrap();
|
|
||||||
let value: serde_json::Value = serde_json::from_slice(value).unwrap();
|
|
||||||
object.insert(fid_name.to_owned(), value);
|
|
||||||
}
|
|
||||||
(id, serde_json::to_string_pretty(&object).unwrap())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
println!("{}us: {:?}", elapsed.as_micros(), docs.documents_ids);
|
|
||||||
for (id, document) in documents {
|
|
||||||
println!("{id}:");
|
|
||||||
println!("{document}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
query.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// use big_s::S;
|
|
||||||
use heed::EnvOpenOptions;
|
|
||||||
// use maplit::hashset;
|
|
||||||
use milli::{
|
|
||||||
update::{IndexerConfig, Settings},
|
|
||||||
Criterion, Index,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut options = EnvOpenOptions::new();
|
|
||||||
options.map_size(100 * 1024 * 1024 * 1024); // 100 GB
|
|
||||||
|
|
||||||
let index = Index::new(options, "data_movies.ms").unwrap();
|
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
|
||||||
|
|
||||||
let config = IndexerConfig::default();
|
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
|
||||||
|
|
||||||
// builder.set_min_word_len_one_typo(5);
|
|
||||||
// builder.set_min_word_len_two_typos(7);
|
|
||||||
// builder.set_sortable_fields(hashset! { S("release_date") });
|
|
||||||
builder.set_criteria(vec![
|
|
||||||
Criterion::Words,
|
|
||||||
Criterion::Typo,
|
|
||||||
Criterion::Proximity,
|
|
||||||
Criterion::Attribute,
|
|
||||||
Criterion::Sort,
|
|
||||||
Criterion::Exactness,
|
|
||||||
]);
|
|
||||||
|
|
||||||
builder.execute(|_| (), || false).unwrap();
|
|
||||||
wtxn.commit().unwrap();
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ use std::io::{self, Write};
|
|||||||
|
|
||||||
use grenad::{CompressionType, WriterBuilder};
|
use grenad::{CompressionType, WriterBuilder};
|
||||||
use serde::de::Deserializer;
|
use serde::de::Deserializer;
|
||||||
use serde_json::{to_writer, Value};
|
use serde_json::to_writer;
|
||||||
|
|
||||||
use super::{DocumentsBatchIndex, Error, DOCUMENTS_BATCH_INDEX_KEY};
|
use super::{DocumentsBatchIndex, Error, DOCUMENTS_BATCH_INDEX_KEY};
|
||||||
use crate::documents::serde_impl::DocumentVisitor;
|
use crate::documents::serde_impl::DocumentVisitor;
|
||||||
@@ -87,95 +87,6 @@ impl<W: Write> DocumentsBatchBuilder<W> {
|
|||||||
de.deserialize_any(&mut visitor)?
|
de.deserialize_any(&mut visitor)?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a new CSV file into the batch and updates the `DocumentsBatchIndex` accordingly.
|
|
||||||
pub fn append_csv<R: io::Read>(&mut self, mut reader: csv::Reader<R>) -> Result<(), Error> {
|
|
||||||
// Make sure that we insert the fields ids in order as the obkv writer has this requirement.
|
|
||||||
let mut typed_fields_ids: Vec<_> = reader
|
|
||||||
.headers()?
|
|
||||||
.into_iter()
|
|
||||||
.map(parse_csv_header)
|
|
||||||
.map(|(k, t)| (self.fields_index.insert(k), t))
|
|
||||||
.enumerate()
|
|
||||||
.collect();
|
|
||||||
// Make sure that we insert the fields ids in order as the obkv writer has this requirement.
|
|
||||||
typed_fields_ids.sort_unstable_by_key(|(_, (fid, _))| *fid);
|
|
||||||
|
|
||||||
let mut record = csv::StringRecord::new();
|
|
||||||
let mut line = 0;
|
|
||||||
while reader.read_record(&mut record)? {
|
|
||||||
// We increment here and not at the end of the while loop to take
|
|
||||||
// the header offset into account.
|
|
||||||
line += 1;
|
|
||||||
|
|
||||||
self.obkv_buffer.clear();
|
|
||||||
let mut writer = obkv::KvWriter::new(&mut self.obkv_buffer);
|
|
||||||
|
|
||||||
for (i, (field_id, type_)) in typed_fields_ids.iter() {
|
|
||||||
self.value_buffer.clear();
|
|
||||||
|
|
||||||
let value = &record[*i];
|
|
||||||
let trimmed_value = value.trim();
|
|
||||||
match type_ {
|
|
||||||
AllowedType::Number => {
|
|
||||||
if trimmed_value.is_empty() {
|
|
||||||
to_writer(&mut self.value_buffer, &Value::Null)?;
|
|
||||||
} else if let Ok(integer) = trimmed_value.parse::<i64>() {
|
|
||||||
to_writer(&mut self.value_buffer, &integer)?;
|
|
||||||
} else {
|
|
||||||
match trimmed_value.parse::<f64>() {
|
|
||||||
Ok(float) => {
|
|
||||||
to_writer(&mut self.value_buffer, &float)?;
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
return Err(Error::ParseFloat {
|
|
||||||
error,
|
|
||||||
line,
|
|
||||||
value: value.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AllowedType::Boolean => {
|
|
||||||
if trimmed_value.is_empty() {
|
|
||||||
to_writer(&mut self.value_buffer, &Value::Null)?;
|
|
||||||
} else {
|
|
||||||
match trimmed_value.parse::<bool>() {
|
|
||||||
Ok(bool) => {
|
|
||||||
to_writer(&mut self.value_buffer, &bool)?;
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
return Err(Error::ParseBool {
|
|
||||||
error,
|
|
||||||
line,
|
|
||||||
value: value.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AllowedType::String => {
|
|
||||||
if value.is_empty() {
|
|
||||||
to_writer(&mut self.value_buffer, &Value::Null)?;
|
|
||||||
} else {
|
|
||||||
to_writer(&mut self.value_buffer, value)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We insert into the obkv writer the value buffer that has been filled just above.
|
|
||||||
writer.insert(*field_id, &self.value_buffer)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let internal_id = self.documents_count.to_be_bytes();
|
|
||||||
let document_bytes = writer.into_inner()?;
|
|
||||||
self.writer.insert(internal_id, &document_bytes)?;
|
|
||||||
self.documents_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flushes the content on disk and stores the final version of the `DocumentsBatchIndex`.
|
/// Flushes the content on disk and stores the final version of the `DocumentsBatchIndex`.
|
||||||
pub fn into_inner(mut self) -> io::Result<W> {
|
pub fn into_inner(mut self) -> io::Result<W> {
|
||||||
let DocumentsBatchBuilder { mut writer, fields_index, .. } = self;
|
let DocumentsBatchBuilder { mut writer, fields_index, .. } = self;
|
||||||
@@ -189,35 +100,12 @@ impl<W: Write> DocumentsBatchBuilder<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum AllowedType {
|
|
||||||
String,
|
|
||||||
Boolean,
|
|
||||||
Number,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_csv_header(header: &str) -> (&str, AllowedType) {
|
|
||||||
// if there are several separators we only split on the last one.
|
|
||||||
match header.rsplit_once(':') {
|
|
||||||
Some((field_name, field_type)) => match field_type {
|
|
||||||
"string" => (field_name, AllowedType::String),
|
|
||||||
"boolean" => (field_name, AllowedType::Boolean),
|
|
||||||
"number" => (field_name, AllowedType::Number),
|
|
||||||
// if the pattern isn't recognized, we keep the whole field.
|
|
||||||
_otherwise => (header, AllowedType::String),
|
|
||||||
},
|
|
||||||
None => (header, AllowedType::String),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::documents::{obkv_to_object, DocumentsBatchReader};
|
use crate::documents::DocumentsBatchReader;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_single_documents_json() {
|
fn add_single_documents_json() {
|
||||||
@@ -253,348 +141,4 @@ mod test {
|
|||||||
|
|
||||||
assert!(cursor.next_document().unwrap().is_none());
|
assert!(cursor.next_document().unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_documents_csv() {
|
|
||||||
let csv_content = "id:number,field:string\n1,hello!\n2,blabla";
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
assert_eq!(builder.documents_count(), 2);
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
assert_eq!(index.len(), 2);
|
|
||||||
|
|
||||||
let document = cursor.next_document().unwrap().unwrap();
|
|
||||||
assert_eq!(document.iter().count(), 2);
|
|
||||||
|
|
||||||
let document = cursor.next_document().unwrap().unwrap();
|
|
||||||
assert_eq!(document.iter().count(), 2);
|
|
||||||
|
|
||||||
assert!(cursor.next_document().unwrap().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_csv_document() {
|
|
||||||
let csv_content = r#"city,country,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city": "Boston",
|
|
||||||
"country": "United States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(cursor.next_document().unwrap().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn coma_in_field() {
|
|
||||||
let csv_content = r#"city,country,pop
|
|
||||||
"Boston","United, States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city": "Boston",
|
|
||||||
"country": "United, States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn quote_in_field() {
|
|
||||||
let csv_content = r#"city,country,pop
|
|
||||||
"Boston","United"" States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city": "Boston",
|
|
||||||
"country": "United\" States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn integer_in_field() {
|
|
||||||
let csv_content = r#"city,country,pop:number
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city": "Boston",
|
|
||||||
"country": "United States",
|
|
||||||
"pop": 4628910,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn integer_as_id() {
|
|
||||||
let csv_content = r#""id:number","title:string","comment:string"
|
|
||||||
"1239","Pride and Prejudice","A great book""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"id": 1239,
|
|
||||||
"title": "Pride and Prejudice",
|
|
||||||
"comment": "A great book",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn float_in_field() {
|
|
||||||
let csv_content = r#"city,country,pop:number
|
|
||||||
"Boston","United States","4628910.01""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city": "Boston",
|
|
||||||
"country": "United States",
|
|
||||||
"pop": 4628910.01,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn several_colon_in_header() {
|
|
||||||
let csv_content = r#"city:love:string,country:state,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city:love": "Boston",
|
|
||||||
"country:state": "United States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ending_by_colon_in_header() {
|
|
||||||
let csv_content = r#"city:,country,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city:": "Boston",
|
|
||||||
"country": "United States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn starting_by_colon_in_header() {
|
|
||||||
let csv_content = r#":city,country,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
":city": "Boston",
|
|
||||||
"country": "United States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ignore]
|
|
||||||
#[test]
|
|
||||||
fn starting_by_colon_in_header2() {
|
|
||||||
let csv_content = r#":string,country,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, _) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
assert!(cursor.next_document().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn double_colon_in_header() {
|
|
||||||
let csv_content = r#"city::string,country,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
let (mut cursor, index) = DocumentsBatchReader::from_reader(Cursor::new(vector))
|
|
||||||
.unwrap()
|
|
||||||
.into_cursor_and_fields_index();
|
|
||||||
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let val = obkv_to_object(&doc, &index).map(Value::from).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
val,
|
|
||||||
json!({
|
|
||||||
"city:": "Boston",
|
|
||||||
"country": "United States",
|
|
||||||
"pop": "4628910",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bad_type_in_header() {
|
|
||||||
let csv_content = r#"city,country:number,pop
|
|
||||||
"Boston","United States","4628910""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
assert!(builder.append_csv(csv).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bad_column_count1() {
|
|
||||||
let csv_content = r#"city,country,pop
|
|
||||||
"Boston","United States","4628910", "too much
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content"#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
assert!(builder.append_csv(csv).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bad_column_count2() {
|
|
||||||
let csv_content = r#"city,country,pop
|
|
||||||
"Boston","United States""#;
|
|
||||||
let csv = csv::Reader::from_reader(Cursor::new(csv_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
assert!(builder.append_csv(csv).is_err());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ impl<R: io::Read + io::Seek> EnrichedDocumentsBatchReader<R> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EnrichedDocument<'a> {
|
pub struct EnrichedDocument<'a> {
|
||||||
pub document: KvReader<'a, FieldId>,
|
pub document: &'a KvReader<FieldId>,
|
||||||
pub document_id: DocumentId,
|
pub document_id: DocumentId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ pub use builder::DocumentsBatchBuilder;
|
|||||||
pub use enriched::{EnrichedDocument, EnrichedDocumentsBatchCursor, EnrichedDocumentsBatchReader};
|
pub use enriched::{EnrichedDocument, EnrichedDocumentsBatchCursor, EnrichedDocumentsBatchReader};
|
||||||
use obkv::KvReader;
|
use obkv::KvReader;
|
||||||
pub use primary_key::{
|
pub use primary_key::{
|
||||||
validate_document_id_value, DocumentIdExtractionError, FieldIdMapper, PrimaryKey,
|
validate_document_id_str, validate_document_id_value, DocumentIdExtractionError, FieldIdMapper,
|
||||||
DEFAULT_PRIMARY_KEY,
|
PrimaryKey, DEFAULT_PRIMARY_KEY,
|
||||||
};
|
};
|
||||||
pub use reader::{DocumentsBatchCursor, DocumentsBatchCursorError, DocumentsBatchReader};
|
pub use reader::{DocumentsBatchCursor, DocumentsBatchCursorError, DocumentsBatchReader};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -27,7 +27,7 @@ use crate::{FieldId, Object, Result};
|
|||||||
const DOCUMENTS_BATCH_INDEX_KEY: [u8; 8] = u64::MAX.to_be_bytes();
|
const DOCUMENTS_BATCH_INDEX_KEY: [u8; 8] = u64::MAX.to_be_bytes();
|
||||||
|
|
||||||
/// Helper function to convert an obkv reader into a JSON object.
|
/// Helper function to convert an obkv reader into a JSON object.
|
||||||
pub fn obkv_to_object(obkv: &KvReader<'_, FieldId>, index: &DocumentsBatchIndex) -> Result<Object> {
|
pub fn obkv_to_object(obkv: &KvReader<FieldId>, index: &DocumentsBatchIndex) -> Result<Object> {
|
||||||
obkv.iter()
|
obkv.iter()
|
||||||
.map(|(field_id, value)| {
|
.map(|(field_id, value)| {
|
||||||
let field_name = index
|
let field_name = index
|
||||||
@@ -76,7 +76,7 @@ impl DocumentsBatchIndex {
|
|||||||
self.0.get_by_right(name).cloned()
|
self.0.get_by_right(name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recreate_json(&self, document: &obkv::KvReaderU16<'_>) -> Result<Object> {
|
pub fn recreate_json(&self, document: &obkv::KvReaderU16) -> Result<Object> {
|
||||||
let mut map = Object::new();
|
let mut map = Object::new();
|
||||||
|
|
||||||
for (k, v) in document.iter() {
|
for (k, v) in document.iter() {
|
||||||
@@ -96,6 +96,10 @@ impl FieldIdMapper for DocumentsBatchIndex {
|
|||||||
fn id(&self, name: &str) -> Option<FieldId> {
|
fn id(&self, name: &str) -> Option<FieldId> {
|
||||||
self.id(name)
|
self.id(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn name(&self, id: FieldId) -> Option<&str> {
|
||||||
|
self.name(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
@@ -146,11 +150,33 @@ pub fn objects_from_json_value(json: serde_json::Value) -> Vec<crate::Object> {
|
|||||||
macro_rules! documents {
|
macro_rules! documents {
|
||||||
($data:tt) => {{
|
($data:tt) => {{
|
||||||
let documents = serde_json::json!($data);
|
let documents = serde_json::json!($data);
|
||||||
let documents = $crate::documents::objects_from_json_value(documents);
|
let mut file = tempfile::tempfile().unwrap();
|
||||||
$crate::documents::documents_batch_reader_from_objects(documents)
|
|
||||||
|
match documents {
|
||||||
|
serde_json::Value::Array(vec) => {
|
||||||
|
for document in vec {
|
||||||
|
serde_json::to_writer(&mut file, &document).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serde_json::Value::Object(document) => {
|
||||||
|
serde_json::to_writer(&mut file, &document).unwrap();
|
||||||
|
}
|
||||||
|
_ => unimplemented!("The `documents!` macro only support Objects and Array"),
|
||||||
|
}
|
||||||
|
file.sync_all().unwrap();
|
||||||
|
unsafe { memmap2::Mmap::map(&file).unwrap() }
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mmap_from_objects(objects: impl IntoIterator<Item = Object>) -> memmap2::Mmap {
|
||||||
|
let mut writer = tempfile::tempfile().map(std::io::BufWriter::new).unwrap();
|
||||||
|
for object in objects {
|
||||||
|
serde_json::to_writer(&mut writer, &object).unwrap();
|
||||||
|
}
|
||||||
|
let file = writer.into_inner().unwrap();
|
||||||
|
unsafe { memmap2::Mmap::map(&file).unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn documents_batch_reader_from_objects(
|
pub fn documents_batch_reader_from_objects(
|
||||||
objects: impl IntoIterator<Item = Object>,
|
objects: impl IntoIterator<Item = Object>,
|
||||||
) -> DocumentsBatchReader<std::io::Cursor<Vec<u8>>> {
|
) -> DocumentsBatchReader<std::io::Cursor<Vec<u8>>> {
|
||||||
@@ -166,7 +192,7 @@ pub fn documents_batch_reader_from_objects(
|
|||||||
mod test {
|
mod test {
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
use serde_json::{json, Value};
|
use serde_json::json;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -220,20 +246,6 @@ mod test {
|
|||||||
assert!(documents.next_document().unwrap().is_none());
|
assert!(documents.next_document().unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_nested() {
|
|
||||||
let docs_reader = documents!([{
|
|
||||||
"hello": {
|
|
||||||
"toto": ["hello"]
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
let (mut cursor, _) = docs_reader.into_cursor_and_fields_index();
|
|
||||||
let doc = cursor.next_document().unwrap().unwrap();
|
|
||||||
let nested: Value = serde_json::from_slice(doc.get(0).unwrap()).unwrap();
|
|
||||||
assert_eq!(nested, json!({ "toto": ["hello"] }));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn out_of_order_json_fields() {
|
fn out_of_order_json_fields() {
|
||||||
let _documents = documents!([
|
let _documents = documents!([
|
||||||
@@ -241,33 +253,4 @@ mod test {
|
|||||||
{"id": 2,"a": 0,"b": 0},
|
{"id": 2,"a": 0,"b": 0},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn csv_types_dont_panic() {
|
|
||||||
let csv1_content =
|
|
||||||
"id:number,b:boolean,c,d:number\n1,,,\n2,true,doggo,2\n3,false,the best doggo,-2\n4,,\"Hello, World!\",2.5";
|
|
||||||
let csv1 = csv::Reader::from_reader(Cursor::new(csv1_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv1).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
DocumentsBatchReader::from_reader(Cursor::new(vector)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn out_of_order_csv_fields() {
|
|
||||||
let csv1_content = "id:number,b\n1,0";
|
|
||||||
let csv1 = csv::Reader::from_reader(Cursor::new(csv1_content));
|
|
||||||
|
|
||||||
let csv2_content = "id:number,a,b\n2,0,0";
|
|
||||||
let csv2 = csv::Reader::from_reader(Cursor::new(csv2_content));
|
|
||||||
|
|
||||||
let mut builder = DocumentsBatchBuilder::new(Vec::new());
|
|
||||||
builder.append_csv(csv1).unwrap();
|
|
||||||
builder.append_csv(csv2).unwrap();
|
|
||||||
let vector = builder.into_inner().unwrap();
|
|
||||||
|
|
||||||
DocumentsBatchReader::from_reader(Cursor::new(vector)).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::ops::ControlFlow;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use serde_json::value::RawValue;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::fields_ids_map::MutFieldIdMapper;
|
||||||
|
use crate::update::new::indexer::de::{match_component, DeOrBumpStr};
|
||||||
|
use crate::update::new::KvReaderFieldId;
|
||||||
use crate::{FieldId, InternalError, Object, Result, UserError};
|
use crate::{FieldId, InternalError, Object, Result, UserError};
|
||||||
|
|
||||||
/// The symbol used to define levels in a nested primary key.
|
/// The symbol used to define levels in a nested primary key.
|
||||||
@@ -17,6 +23,21 @@ pub trait FieldIdMapper {
|
|||||||
///
|
///
|
||||||
/// `None` if the field with this name was not found.
|
/// `None` if the field with this name was not found.
|
||||||
fn id(&self, name: &str) -> Option<FieldId>;
|
fn id(&self, name: &str) -> Option<FieldId>;
|
||||||
|
|
||||||
|
fn name(&self, id: FieldId) -> Option<&str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FieldIdMapper for &T
|
||||||
|
where
|
||||||
|
T: FieldIdMapper,
|
||||||
|
{
|
||||||
|
fn id(&self, name: &str) -> Option<FieldId> {
|
||||||
|
T::id(self, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self, id: FieldId) -> Option<&str> {
|
||||||
|
T::name(self, id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that represent the type of primary key that has been set
|
/// A type that represent the type of primary key that has been set
|
||||||
@@ -43,7 +64,19 @@ impl<'a> PrimaryKey<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &str {
|
pub fn new_or_insert(
|
||||||
|
path: &'a str,
|
||||||
|
fields: &mut impl MutFieldIdMapper,
|
||||||
|
) -> StdResult<Self, UserError> {
|
||||||
|
Ok(if path.contains(PRIMARY_KEY_SPLIT_SYMBOL) {
|
||||||
|
Self::Nested { name: path }
|
||||||
|
} else {
|
||||||
|
let field_id = fields.insert(path).ok_or(UserError::AttributeLimitReached)?;
|
||||||
|
Self::Flat { name: path, field_id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &'a str {
|
||||||
match self {
|
match self {
|
||||||
PrimaryKey::Flat { name, .. } => name,
|
PrimaryKey::Flat { name, .. } => name,
|
||||||
PrimaryKey::Nested { name } => name,
|
PrimaryKey::Nested { name } => name,
|
||||||
@@ -52,7 +85,7 @@ impl<'a> PrimaryKey<'a> {
|
|||||||
|
|
||||||
pub fn document_id(
|
pub fn document_id(
|
||||||
&self,
|
&self,
|
||||||
document: &obkv::KvReader<'_, FieldId>,
|
document: &obkv::KvReader<FieldId>,
|
||||||
fields: &impl FieldIdMapper,
|
fields: &impl FieldIdMapper,
|
||||||
) -> Result<StdResult<String, DocumentIdExtractionError>> {
|
) -> Result<StdResult<String, DocumentIdExtractionError>> {
|
||||||
match self {
|
match self {
|
||||||
@@ -100,9 +133,105 @@ impl<'a> PrimaryKey<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_docid_from_db<'pl, 'bump: 'pl, Mapper: FieldIdMapper>(
|
||||||
|
&self,
|
||||||
|
document: &'pl KvReaderFieldId,
|
||||||
|
db_fields_ids_map: &Mapper,
|
||||||
|
indexer: &'bump Bump,
|
||||||
|
) -> Result<DeOrBumpStr<'pl, 'bump>> {
|
||||||
|
use serde::Deserializer as _;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PrimaryKey::Flat { name: _, field_id } => {
|
||||||
|
let Some(document_id) = document.get(*field_id) else {
|
||||||
|
return Err(InternalError::DocumentsError(
|
||||||
|
crate::documents::Error::InvalidDocumentFormat,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
let document_id: &RawValue =
|
||||||
|
serde_json::from_slice(document_id).map_err(InternalError::SerdeJson)?;
|
||||||
|
|
||||||
|
let document_id = document_id
|
||||||
|
.deserialize_any(crate::update::new::indexer::de::DocumentIdVisitor(indexer))
|
||||||
|
.map_err(InternalError::SerdeJson)?;
|
||||||
|
|
||||||
|
let external_document_id = match document_id {
|
||||||
|
Ok(document_id) => Ok(document_id),
|
||||||
|
Err(_) => Err(InternalError::DocumentsError(
|
||||||
|
crate::documents::Error::InvalidDocumentFormat,
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(external_document_id)
|
||||||
|
}
|
||||||
|
nested @ PrimaryKey::Nested { name: _ } => {
|
||||||
|
let mut docid = None;
|
||||||
|
for (first_level, right) in nested.possible_level_names() {
|
||||||
|
let Some(fid) = db_fields_ids_map.id(first_level) else { continue };
|
||||||
|
|
||||||
|
let Some(value) = document.get(fid) else { continue };
|
||||||
|
let value: &RawValue =
|
||||||
|
serde_json::from_slice(value).map_err(InternalError::SerdeJson)?;
|
||||||
|
match match_component(first_level, right, value, indexer, &mut docid) {
|
||||||
|
ControlFlow::Continue(()) => continue,
|
||||||
|
ControlFlow::Break(Ok(_)) => {
|
||||||
|
return Err(InternalError::DocumentsError(
|
||||||
|
crate::documents::Error::InvalidDocumentFormat,
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
ControlFlow::Break(Err(err)) => {
|
||||||
|
return Err(InternalError::SerdeJson(err).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(docid.ok_or(InternalError::DocumentsError(
|
||||||
|
crate::documents::Error::InvalidDocumentFormat,
|
||||||
|
))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_fields_and_docid<'pl, 'bump: 'pl, Mapper: MutFieldIdMapper>(
|
||||||
|
&self,
|
||||||
|
document: &'pl RawValue,
|
||||||
|
new_fields_ids_map: &mut Mapper,
|
||||||
|
indexer: &'bump Bump,
|
||||||
|
) -> Result<DeOrBumpStr<'pl, 'bump>> {
|
||||||
|
use serde::Deserializer as _;
|
||||||
|
let res = document
|
||||||
|
.deserialize_map(crate::update::new::indexer::de::FieldAndDocidExtractor::new(
|
||||||
|
new_fields_ids_map,
|
||||||
|
self,
|
||||||
|
indexer,
|
||||||
|
))
|
||||||
|
.map_err(UserError::SerdeJson)??;
|
||||||
|
|
||||||
|
let external_document_id = match res {
|
||||||
|
Ok(document_id) => Ok(document_id),
|
||||||
|
Err(DocumentIdExtractionError::InvalidDocumentId(e)) => Err(e),
|
||||||
|
Err(DocumentIdExtractionError::MissingDocumentId) => {
|
||||||
|
Err(UserError::MissingDocumentId {
|
||||||
|
primary_key: self.name().to_string(),
|
||||||
|
document: serde_json::from_str(document.get()).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(DocumentIdExtractionError::TooManyDocumentIds(_)) => {
|
||||||
|
Err(UserError::TooManyDocumentIds {
|
||||||
|
primary_key: self.name().to_string(),
|
||||||
|
document: serde_json::from_str(document.get()).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(external_document_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns an `Iterator` that gives all the possible fields names the primary key
|
/// Returns an `Iterator` that gives all the possible fields names the primary key
|
||||||
/// can have depending of the first level name and depth of the objects.
|
/// can have depending of the first level name and depth of the objects.
|
||||||
pub fn possible_level_names(&self) -> impl Iterator<Item = (&str, &str)> + '_ {
|
pub fn possible_level_names(&self) -> impl Iterator<Item = (&'a str, &'a str)> + '_ {
|
||||||
let name = self.name();
|
let name = self.name();
|
||||||
name.match_indices(PRIMARY_KEY_SPLIT_SYMBOL)
|
name.match_indices(PRIMARY_KEY_SPLIT_SYMBOL)
|
||||||
.map(move |(i, _)| (&name[..i], &name[i + PRIMARY_KEY_SPLIT_SYMBOL.len_utf8()..]))
|
.map(move |(i, _)| (&name[..i], &name[i + PRIMARY_KEY_SPLIT_SYMBOL.len_utf8()..]))
|
||||||
@@ -149,7 +278,7 @@ fn starts_with(selector: &str, key: &str) -> bool {
|
|||||||
|
|
||||||
// FIXME: move to a DocumentId struct
|
// FIXME: move to a DocumentId struct
|
||||||
|
|
||||||
fn validate_document_id(document_id: &str) -> Option<&str> {
|
pub fn validate_document_id_str(document_id: &str) -> Option<&str> {
|
||||||
if document_id.is_empty()
|
if document_id.is_empty()
|
||||||
|| document_id.len() > 512
|
|| document_id.len() > 512
|
||||||
|| !document_id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
|
|| !document_id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
|
||||||
@@ -162,7 +291,7 @@ fn validate_document_id(document_id: &str) -> Option<&str> {
|
|||||||
|
|
||||||
pub fn validate_document_id_value(document_id: Value) -> StdResult<String, UserError> {
|
pub fn validate_document_id_value(document_id: Value) -> StdResult<String, UserError> {
|
||||||
match document_id {
|
match document_id {
|
||||||
Value::String(string) => match validate_document_id(&string) {
|
Value::String(string) => match validate_document_id_str(&string) {
|
||||||
Some(s) if s.len() == string.len() => Ok(string),
|
Some(s) if s.len() == string.len() => Ok(string),
|
||||||
Some(s) => Ok(s.to_string()),
|
Some(s) => Ok(s.to_string()),
|
||||||
None => Err(UserError::InvalidDocumentId { document_id: Value::String(string) }),
|
None => Err(UserError::InvalidDocumentId { document_id: Value::String(string) }),
|
||||||
|
|||||||
@@ -72,15 +72,24 @@ impl<R> DocumentsBatchCursor<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read + io::Seek> DocumentsBatchCursor<R> {
|
impl<R: io::Read + io::Seek> DocumentsBatchCursor<R> {
|
||||||
|
/// Returns a single document from the database.
|
||||||
|
pub fn get(
|
||||||
|
&mut self,
|
||||||
|
offset: u32,
|
||||||
|
) -> Result<Option<&KvReader<FieldId>>, DocumentsBatchCursorError> {
|
||||||
|
match self.cursor.move_on_key_equal_to(offset.to_be_bytes())? {
|
||||||
|
Some((key, value)) if key != DOCUMENTS_BATCH_INDEX_KEY => Ok(Some(value.into())),
|
||||||
|
_otherwise => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the next document, starting from the first one. Subsequent calls to
|
/// Returns the next document, starting from the first one. Subsequent calls to
|
||||||
/// `next_document` advance the document reader until all the documents have been read.
|
/// `next_document` advance the document reader until all the documents have been read.
|
||||||
pub fn next_document(
|
pub fn next_document(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<Option<KvReader<'_, FieldId>>, DocumentsBatchCursorError> {
|
) -> Result<Option<&KvReader<FieldId>>, DocumentsBatchCursorError> {
|
||||||
match self.cursor.move_on_next()? {
|
match self.cursor.move_on_next()? {
|
||||||
Some((key, value)) if key != DOCUMENTS_BATCH_INDEX_KEY => {
|
Some((key, value)) if key != DOCUMENTS_BATCH_INDEX_KEY => Ok(Some(value.into())),
|
||||||
Ok(Some(KvReader::new(value)))
|
|
||||||
}
|
|
||||||
_otherwise => Ok(None),
|
_otherwise => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::convert::Infallible;
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::{io, str};
|
use std::{io, str};
|
||||||
|
|
||||||
|
use bstr::BString;
|
||||||
use heed::{Error as HeedError, MdbError};
|
use heed::{Error as HeedError, MdbError};
|
||||||
use rayon::ThreadPoolBuildError;
|
use rayon::ThreadPoolBuildError;
|
||||||
use rhai::EvalAltResult;
|
use rhai::EvalAltResult;
|
||||||
@@ -31,23 +32,23 @@ pub enum Error {
|
|||||||
pub enum InternalError {
|
pub enum InternalError {
|
||||||
#[error("{}", HeedError::DatabaseClosing)]
|
#[error("{}", HeedError::DatabaseClosing)]
|
||||||
DatabaseClosing,
|
DatabaseClosing,
|
||||||
#[error("Missing {} in the {db_name} database.", key.unwrap_or("key"))]
|
#[error("missing {} in the {db_name} database", key.unwrap_or("key"))]
|
||||||
DatabaseMissingEntry { db_name: &'static str, key: Option<&'static str> },
|
DatabaseMissingEntry { db_name: &'static str, key: Option<&'static str> },
|
||||||
#[error("Missing {key} in the fieldids weights mapping.")]
|
#[error("missing {key} in the fieldids weights mapping")]
|
||||||
FieldidsWeightsMapMissingEntry { key: FieldId },
|
FieldidsWeightsMapMissingEntry { key: FieldId },
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FieldIdMapMissingEntry(#[from] FieldIdMapMissingEntry),
|
FieldIdMapMissingEntry(#[from] FieldIdMapMissingEntry),
|
||||||
#[error("Missing {key} in the field id mapping.")]
|
#[error("missing {key} in the field id mapping")]
|
||||||
FieldIdMappingMissingEntry { key: FieldId },
|
FieldIdMappingMissingEntry { key: FieldId },
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Fst(#[from] fst::Error),
|
Fst(#[from] fst::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
DocumentsError(#[from] documents::Error),
|
DocumentsError(#[from] documents::Error),
|
||||||
#[error("Invalid compression type have been specified to grenad")]
|
#[error("invalid compression type have been specified to grenad")]
|
||||||
GrenadInvalidCompressionType,
|
GrenadInvalidCompressionType,
|
||||||
#[error("Invalid grenad file with an invalid version format")]
|
#[error("invalid grenad file with an invalid version format")]
|
||||||
GrenadInvalidFormatVersion,
|
GrenadInvalidFormatVersion,
|
||||||
#[error("Invalid merge while processing {process}")]
|
#[error("invalid merge while processing {process}")]
|
||||||
IndexingMergingKeys { process: &'static str },
|
IndexingMergingKeys { process: &'static str },
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
RayonThreadPool(#[from] ThreadPoolBuildError),
|
RayonThreadPool(#[from] ThreadPoolBuildError),
|
||||||
@@ -56,9 +57,15 @@ pub enum InternalError {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SerdeJson(#[from] serde_json::Error),
|
SerdeJson(#[from] serde_json::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
BincodeError(#[from] bincode::Error),
|
||||||
|
#[error(transparent)]
|
||||||
Serialization(#[from] SerializationError),
|
Serialization(#[from] SerializationError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Store(#[from] MdbError),
|
Store(#[from] MdbError),
|
||||||
|
#[error("Cannot delete {key:?} from database {database_name}: {error}")]
|
||||||
|
StoreDeletion { database_name: &'static str, key: BString, error: heed::Error },
|
||||||
|
#[error("Cannot insert {key:?} and value with length {value_length} into database {database_name}: {error}")]
|
||||||
|
StorePut { database_name: &'static str, key: BString, value_length: usize, error: heed::Error },
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Utf8(#[from] str::Utf8Error),
|
Utf8(#[from] str::Utf8Error),
|
||||||
#[error("An indexation process was explicitly aborted")]
|
#[error("An indexation process was explicitly aborted")]
|
||||||
@@ -122,7 +129,7 @@ and can not be more than 512 bytes.", .document_id.to_string()
|
|||||||
#[error("The `_vectors` field in the document with id: `{document_id}` is not an object. Was expecting an object with a key for each embedder with manually provided vectors, but instead got `{value}`")]
|
#[error("The `_vectors` field in the document with id: `{document_id}` is not an object. Was expecting an object with a key for each embedder with manually provided vectors, but instead got `{value}`")]
|
||||||
InvalidVectorsMapType { document_id: String, value: Value },
|
InvalidVectorsMapType { document_id: String, value: Value },
|
||||||
#[error("Bad embedder configuration in the document with id: `{document_id}`. {error}")]
|
#[error("Bad embedder configuration in the document with id: `{document_id}`. {error}")]
|
||||||
InvalidVectorsEmbedderConf { document_id: String, error: deserr::errors::JsonError },
|
InvalidVectorsEmbedderConf { document_id: String, error: String },
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidFilter(String),
|
InvalidFilter(String),
|
||||||
#[error("Invalid type for filter subexpression: expected: {}, found: {1}.", .0.join(", "))]
|
#[error("Invalid type for filter subexpression: expected: {}, found: {1}.", .0.join(", "))]
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::FieldId;
|
use crate::FieldId;
|
||||||
|
|
||||||
|
mod global;
|
||||||
|
pub mod metadata;
|
||||||
|
pub use global::GlobalFieldsIdsMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FieldsIdsMap {
|
pub struct FieldsIdsMap {
|
||||||
names_ids: BTreeMap<String, FieldId>,
|
names_ids: BTreeMap<String, FieldId>,
|
||||||
@@ -95,6 +99,20 @@ impl crate::documents::FieldIdMapper for FieldsIdsMap {
|
|||||||
fn id(&self, name: &str) -> Option<FieldId> {
|
fn id(&self, name: &str) -> Option<FieldId> {
|
||||||
self.id(name)
|
self.id(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn name(&self, id: FieldId) -> Option<&str> {
|
||||||
|
self.name(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MutFieldIdMapper {
|
||||||
|
fn insert(&mut self, name: &str) -> Option<FieldId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MutFieldIdMapper for FieldsIdsMap {
|
||||||
|
fn insert(&mut self, name: &str) -> Option<FieldId> {
|
||||||
|
self.insert(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
114
crates/milli/src/fields_ids_map/global.rs
Normal file
114
crates/milli/src/fields_ids_map/global.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use super::metadata::{FieldIdMapWithMetadata, Metadata};
|
||||||
|
use super::MutFieldIdMapper;
|
||||||
|
use crate::FieldId;
|
||||||
|
|
||||||
|
/// A fields ids map that can be globally updated to add fields
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GlobalFieldsIdsMap<'indexing> {
|
||||||
|
global: &'indexing RwLock<FieldIdMapWithMetadata>,
|
||||||
|
local: LocalFieldsIdsMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct LocalFieldsIdsMap {
|
||||||
|
names_ids: BTreeMap<String, FieldId>,
|
||||||
|
ids_names: BTreeMap<FieldId, String>,
|
||||||
|
metadata: BTreeMap<FieldId, Metadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalFieldsIdsMap {
|
||||||
|
fn new(global: &RwLock<FieldIdMapWithMetadata>) -> Self {
|
||||||
|
let global = global.read().unwrap();
|
||||||
|
Self {
|
||||||
|
names_ids: global.as_fields_ids_map().names_ids.clone(),
|
||||||
|
ids_names: global.as_fields_ids_map().ids_names.clone(),
|
||||||
|
metadata: global.iter_id_metadata().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, name: &str, field_id: FieldId, metadata: Metadata) {
|
||||||
|
self.names_ids.insert(name.to_owned(), field_id);
|
||||||
|
self.ids_names.insert(field_id, name.to_owned());
|
||||||
|
self.metadata.insert(field_id, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self, id: FieldId) -> Option<&str> {
|
||||||
|
self.ids_names.get(&id).map(String::as_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self, name: &str) -> Option<FieldId> {
|
||||||
|
self.names_ids.get(name).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_with_metadata(&self, name: &str) -> Option<(FieldId, Metadata)> {
|
||||||
|
let id = self.id(name)?;
|
||||||
|
Some((id, self.metadata(id).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&self, id: FieldId) -> Option<Metadata> {
|
||||||
|
self.metadata.get(&id).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'indexing> GlobalFieldsIdsMap<'indexing> {
|
||||||
|
pub fn new(global: &'indexing RwLock<FieldIdMapWithMetadata>) -> Self {
|
||||||
|
Self { local: LocalFieldsIdsMap::new(global), global }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the field id related to a field name, it will create a new field id if the
|
||||||
|
/// name is not already known. Returns `None` if the maximum field id as been reached.
|
||||||
|
pub fn id_or_insert(&mut self, name: &str) -> Option<FieldId> {
|
||||||
|
self.id_with_metadata_or_insert(name).map(|(fid, _meta)| fid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id_with_metadata_or_insert(&mut self, name: &str) -> Option<(FieldId, Metadata)> {
|
||||||
|
if let Some(entry) = self.local.id_with_metadata(name) {
|
||||||
|
return Some(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// optimistically lookup the global map
|
||||||
|
let global = self.global.read().unwrap();
|
||||||
|
|
||||||
|
if let Some((field_id, metadata)) = global.id_with_metadata(name) {
|
||||||
|
self.local.insert(name, field_id, metadata);
|
||||||
|
return Some((field_id, metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut global = self.global.write().unwrap();
|
||||||
|
|
||||||
|
if let Some((field_id, metadata)) = global.id_with_metadata(name) {
|
||||||
|
self.local.insert(name, field_id, metadata);
|
||||||
|
return Some((field_id, metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
let field_id = global.insert(name)?;
|
||||||
|
let metadata = global.metadata(field_id).unwrap();
|
||||||
|
self.local.insert(name, field_id, metadata);
|
||||||
|
Some((field_id, metadata))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the name of a field based on its id.
|
||||||
|
pub fn name(&mut self, id: FieldId) -> Option<&str> {
|
||||||
|
if self.local.name(id).is_none() {
|
||||||
|
let global = self.global.read().unwrap();
|
||||||
|
|
||||||
|
let (name, metadata) = global.name_with_metadata(id)?;
|
||||||
|
self.local.insert(name, id, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.local.name(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'indexing> MutFieldIdMapper for GlobalFieldsIdsMap<'indexing> {
|
||||||
|
fn insert(&mut self, name: &str) -> Option<FieldId> {
|
||||||
|
self.id_or_insert(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
185
crates/milli/src/fields_ids_map/metadata.rs
Normal file
185
crates/milli/src/fields_ids_map/metadata.rs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
|
use charabia::Language;
|
||||||
|
use heed::RoTxn;
|
||||||
|
|
||||||
|
use super::FieldsIdsMap;
|
||||||
|
use crate::{FieldId, Index, LocalizedAttributesRule, Result};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Metadata {
|
||||||
|
pub searchable: bool,
|
||||||
|
pub filterable: bool,
|
||||||
|
pub sortable: bool,
|
||||||
|
localized_attributes_rule_id: Option<NonZeroU16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FieldIdMapWithMetadata {
|
||||||
|
fields_ids_map: FieldsIdsMap,
|
||||||
|
builder: MetadataBuilder,
|
||||||
|
metadata: BTreeMap<FieldId, Metadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldIdMapWithMetadata {
|
||||||
|
pub fn new(existing_fields_ids_map: FieldsIdsMap, builder: MetadataBuilder) -> Self {
|
||||||
|
let metadata = existing_fields_ids_map
|
||||||
|
.iter()
|
||||||
|
.map(|(id, name)| (id, builder.metadata_for_field(name)))
|
||||||
|
.collect();
|
||||||
|
Self { fields_ids_map: existing_fields_ids_map, builder, metadata }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_fields_ids_map(&self) -> &FieldsIdsMap {
|
||||||
|
&self.fields_ids_map
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of fields ids in the map.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.fields_ids_map.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the map is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.fields_ids_map.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the field id related to a field name, it will create a new field id if the
|
||||||
|
/// name is not already known. Returns `None` if the maximum field id as been reached.
|
||||||
|
pub fn insert(&mut self, name: &str) -> Option<FieldId> {
|
||||||
|
let id = self.fields_ids_map.insert(name)?;
|
||||||
|
self.metadata.insert(id, self.builder.metadata_for_field(name));
|
||||||
|
Some(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the id of a field based on its name.
|
||||||
|
pub fn id(&self, name: &str) -> Option<FieldId> {
|
||||||
|
self.fields_ids_map.id(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id_with_metadata(&self, name: &str) -> Option<(FieldId, Metadata)> {
|
||||||
|
let id = self.fields_ids_map.id(name)?;
|
||||||
|
Some((id, self.metadata(id).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the name of a field based on its id.
|
||||||
|
pub fn name(&self, id: FieldId) -> Option<&str> {
|
||||||
|
self.fields_ids_map.name(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the name of a field based on its id.
|
||||||
|
pub fn name_with_metadata(&self, id: FieldId) -> Option<(&str, Metadata)> {
|
||||||
|
let name = self.fields_ids_map.name(id)?;
|
||||||
|
Some((name, self.metadata(id).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self, id: FieldId) -> Option<Metadata> {
|
||||||
|
self.metadata.get(&id).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the ids and names in the ids order.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (FieldId, &str, Metadata)> {
|
||||||
|
self.fields_ids_map.iter().map(|(id, name)| (id, name, self.metadata(id).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_id_metadata(&self) -> impl Iterator<Item = (FieldId, Metadata)> + '_ {
|
||||||
|
self.metadata.iter().map(|(k, v)| (*k, *v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_metadata(&self) -> impl Iterator<Item = Metadata> + '_ {
|
||||||
|
self.metadata.values().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata_builder(&self) -> &MetadataBuilder {
|
||||||
|
&self.builder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata {
|
||||||
|
pub fn locales<'rules>(
|
||||||
|
&self,
|
||||||
|
rules: &'rules [LocalizedAttributesRule],
|
||||||
|
) -> Option<&'rules [Language]> {
|
||||||
|
let localized_attributes_rule_id = self.localized_attributes_rule_id?.get();
|
||||||
|
// - 1: `localized_attributes_rule_id` is NonZero
|
||||||
|
let rule = rules.get((localized_attributes_rule_id - 1) as usize).unwrap();
|
||||||
|
Some(rule.locales())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MetadataBuilder {
|
||||||
|
searchable_attributes: Vec<String>,
|
||||||
|
filterable_attributes: HashSet<String>,
|
||||||
|
sortable_attributes: HashSet<String>,
|
||||||
|
localized_attributes: Option<Vec<LocalizedAttributesRule>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MetadataBuilder {
|
||||||
|
pub fn from_index(index: &Index, rtxn: &RoTxn) -> Result<Self> {
|
||||||
|
let searchable_attributes =
|
||||||
|
index.searchable_fields(rtxn)?.into_iter().map(|s| s.to_string()).collect();
|
||||||
|
let filterable_attributes = index.filterable_fields(rtxn)?;
|
||||||
|
let sortable_attributes = index.sortable_fields(rtxn)?;
|
||||||
|
let localized_attributes = index.localized_attributes_rules(rtxn)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
searchable_attributes,
|
||||||
|
filterable_attributes,
|
||||||
|
sortable_attributes,
|
||||||
|
localized_attributes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
searchable_attributes: Vec<String>,
|
||||||
|
filterable_attributes: HashSet<String>,
|
||||||
|
sortable_attributes: HashSet<String>,
|
||||||
|
localized_attributes: Option<Vec<LocalizedAttributesRule>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
searchable_attributes,
|
||||||
|
filterable_attributes,
|
||||||
|
sortable_attributes,
|
||||||
|
localized_attributes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata_for_field(&self, field: &str) -> Metadata {
|
||||||
|
let searchable = self
|
||||||
|
.searchable_attributes
|
||||||
|
.iter()
|
||||||
|
.any(|attribute| attribute == "*" || attribute == field);
|
||||||
|
|
||||||
|
let filterable = self.filterable_attributes.contains(field);
|
||||||
|
|
||||||
|
let sortable = self.sortable_attributes.contains(field);
|
||||||
|
|
||||||
|
let localized_attributes_rule_id = self
|
||||||
|
.localized_attributes
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.iter())
|
||||||
|
.position(|rule| rule.match_str(field))
|
||||||
|
// saturating_add(1): make `id` `NonZero`
|
||||||
|
.map(|id| NonZeroU16::new(id.saturating_add(1).try_into().unwrap()).unwrap());
|
||||||
|
|
||||||
|
Metadata { searchable, filterable, sortable, localized_attributes_rule_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn searchable_attributes(&self) -> &[String] {
|
||||||
|
self.searchable_attributes.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sortable_attributes(&self) -> &HashSet<String> {
|
||||||
|
&self.sortable_attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filterable_attributes(&self) -> &HashSet<String> {
|
||||||
|
&self.filterable_attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn localized_attributes_rules(&self) -> Option<&[LocalizedAttributesRule]> {
|
||||||
|
self.localized_attributes.as_deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,7 +97,7 @@ impl<'a> heed::BytesEncode<'a> for FacetGroupValueCodec {
|
|||||||
|
|
||||||
fn bytes_encode(value: &'a Self::EItem) -> Result<Cow<'a, [u8]>, BoxedError> {
|
fn bytes_encode(value: &'a Self::EItem) -> Result<Cow<'a, [u8]>, BoxedError> {
|
||||||
let mut v = vec![value.size];
|
let mut v = vec![value.size];
|
||||||
CboRoaringBitmapCodec::serialize_into(&value.bitmap, &mut v);
|
CboRoaringBitmapCodec::serialize_into_vec(&value.bitmap, &mut v);
|
||||||
Ok(Cow::Owned(v))
|
Ok(Cow::Owned(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,17 +27,35 @@ impl heed::BytesEncode<'_> for OrderedF64Codec {
|
|||||||
fn bytes_encode(f: &Self::EItem) -> Result<Cow<'_, [u8]>, BoxedError> {
|
fn bytes_encode(f: &Self::EItem) -> Result<Cow<'_, [u8]>, BoxedError> {
|
||||||
let mut buffer = [0u8; 16];
|
let mut buffer = [0u8; 16];
|
||||||
|
|
||||||
// write the globally ordered float
|
encode_f64_into_ordered_bytes(*f, &mut buffer)?;
|
||||||
let bytes = f64_into_bytes(*f).ok_or(InvalidGloballyOrderedFloatError { float: *f })?;
|
|
||||||
buffer[..8].copy_from_slice(&bytes[..]);
|
|
||||||
// Then the f64 value just to be able to read it back
|
|
||||||
let bytes = f.to_be_bytes();
|
|
||||||
buffer[8..16].copy_from_slice(&bytes[..]);
|
|
||||||
|
|
||||||
Ok(Cow::Owned(buffer.to_vec()))
|
Ok(Cow::Owned(buffer.to_vec()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OrderedF64Codec {
|
||||||
|
pub fn serialize_into(
|
||||||
|
f: f64,
|
||||||
|
buffer: &mut [u8; 16],
|
||||||
|
) -> Result<(), InvalidGloballyOrderedFloatError> {
|
||||||
|
encode_f64_into_ordered_bytes(f, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_f64_into_ordered_bytes(
|
||||||
|
f: f64,
|
||||||
|
buffer: &mut [u8; 16],
|
||||||
|
) -> Result<(), InvalidGloballyOrderedFloatError> {
|
||||||
|
// write the globally ordered float
|
||||||
|
let bytes = f64_into_bytes(f).ok_or(InvalidGloballyOrderedFloatError { float: f })?;
|
||||||
|
buffer[..8].copy_from_slice(&bytes[..]);
|
||||||
|
// Then the f64 value just to be able to read it back
|
||||||
|
let bytes = f.to_be_bytes();
|
||||||
|
buffer[8..16].copy_from_slice(&bytes[..]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("the float {float} cannot be converted to a globally ordered representation")]
|
#[error("the float {float} cannot be converted to a globally ordered representation")]
|
||||||
pub struct InvalidGloballyOrderedFloatError {
|
pub struct InvalidGloballyOrderedFloatError {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ use obkv::{KvReaderU16, KvWriterU16};
|
|||||||
pub struct ObkvCodec;
|
pub struct ObkvCodec;
|
||||||
|
|
||||||
impl<'a> heed::BytesDecode<'a> for ObkvCodec {
|
impl<'a> heed::BytesDecode<'a> for ObkvCodec {
|
||||||
type DItem = KvReaderU16<'a>;
|
type DItem = &'a KvReaderU16;
|
||||||
|
|
||||||
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, BoxedError> {
|
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, BoxedError> {
|
||||||
Ok(KvReaderU16::new(bytes))
|
Ok(KvReaderU16::from_slice(bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,18 +27,27 @@ impl CboRoaringBitmapCodec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_into(roaring: &RoaringBitmap, vec: &mut Vec<u8>) {
|
pub fn serialize_into_vec(roaring: &RoaringBitmap, vec: &mut Vec<u8>) {
|
||||||
|
Self::serialize_into_writer(roaring, vec).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_into_writer<W: io::Write>(
|
||||||
|
roaring: &RoaringBitmap,
|
||||||
|
mut writer: W,
|
||||||
|
) -> io::Result<()> {
|
||||||
if roaring.len() <= THRESHOLD as u64 {
|
if roaring.len() <= THRESHOLD as u64 {
|
||||||
// If the number of items (u32s) to encode is less than or equal to the threshold
|
// If the number of items (u32s) to encode is less than or equal to the threshold
|
||||||
// it means that it would weigh the same or less than the RoaringBitmap
|
// it means that it would weigh the same or less than the RoaringBitmap
|
||||||
// header, so we directly encode them using ByteOrder instead.
|
// header, so we directly encode them using ByteOrder instead.
|
||||||
for integer in roaring {
|
for integer in roaring {
|
||||||
vec.write_u32::<NativeEndian>(integer).unwrap();
|
writer.write_u32::<NativeEndian>(integer)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we use the classic RoaringBitmapCodec that writes a header.
|
// Otherwise, we use the classic RoaringBitmapCodec that writes a header.
|
||||||
roaring.serialize_into(vec).unwrap();
|
roaring.serialize_into(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize_from(mut bytes: &[u8]) -> io::Result<RoaringBitmap> {
|
pub fn deserialize_from(mut bytes: &[u8]) -> io::Result<RoaringBitmap> {
|
||||||
@@ -122,7 +131,7 @@ impl CboRoaringBitmapCodec {
|
|||||||
|
|
||||||
/// Merges a DelAdd delta into a CboRoaringBitmap.
|
/// Merges a DelAdd delta into a CboRoaringBitmap.
|
||||||
pub fn merge_deladd_into<'a>(
|
pub fn merge_deladd_into<'a>(
|
||||||
deladd: KvReaderDelAdd<'_>,
|
deladd: &KvReaderDelAdd,
|
||||||
previous: &[u8],
|
previous: &[u8],
|
||||||
buffer: &'a mut Vec<u8>,
|
buffer: &'a mut Vec<u8>,
|
||||||
) -> io::Result<Option<&'a [u8]>> {
|
) -> io::Result<Option<&'a [u8]>> {
|
||||||
@@ -143,7 +152,7 @@ impl CboRoaringBitmapCodec {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::serialize_into(&previous, buffer);
|
Self::serialize_into_vec(&previous, buffer);
|
||||||
Ok(Some(&buffer[..]))
|
Ok(Some(&buffer[..]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +178,7 @@ impl heed::BytesEncode<'_> for CboRoaringBitmapCodec {
|
|||||||
|
|
||||||
fn bytes_encode(item: &Self::EItem) -> Result<Cow<'_, [u8]>, BoxedError> {
|
fn bytes_encode(item: &Self::EItem) -> Result<Cow<'_, [u8]>, BoxedError> {
|
||||||
let mut vec = Vec::with_capacity(Self::serialized_size(item));
|
let mut vec = Vec::with_capacity(Self::serialized_size(item));
|
||||||
Self::serialize_into(item, &mut vec);
|
Self::serialize_into_vec(item, &mut vec);
|
||||||
Ok(Cow::Owned(vec))
|
Ok(Cow::Owned(vec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ pub mod main_key {
|
|||||||
pub const EMBEDDING_CONFIGS: &str = "embedding_configs";
|
pub const EMBEDDING_CONFIGS: &str = "embedding_configs";
|
||||||
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 PREFIX_SEARCH: &str = "prefix_search";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod db_name {
|
pub mod db_name {
|
||||||
@@ -1233,6 +1235,10 @@ impl Index {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn delete_words_prefixes_fst(&self, wtxn: &mut RwTxn<'_>) -> heed::Result<bool> {
|
||||||
|
self.main.remap_key_type::<Str>().delete(wtxn, main_key::WORDS_PREFIXES_FST_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the FST which is the words prefixes dictionary of the engine.
|
/// Returns the FST which is the words prefixes dictionary of the engine.
|
||||||
pub fn words_prefixes_fst<'t>(&self, rtxn: &'t RoTxn<'t>) -> Result<fst::Set<Cow<'t, [u8]>>> {
|
pub fn words_prefixes_fst<'t>(&self, rtxn: &'t RoTxn<'t>) -> Result<fst::Set<Cow<'t, [u8]>>> {
|
||||||
match self.main.remap_types::<Str, Bytes>().get(rtxn, main_key::WORDS_PREFIXES_FST_KEY)? {
|
match self.main.remap_types::<Str, Bytes>().get(rtxn, main_key::WORDS_PREFIXES_FST_KEY)? {
|
||||||
@@ -1251,12 +1257,20 @@ impl Index {
|
|||||||
|
|
||||||
/* documents */
|
/* documents */
|
||||||
|
|
||||||
|
/// Returns a document by using the document id.
|
||||||
|
pub fn document<'t>(&self, rtxn: &'t RoTxn, id: DocumentId) -> Result<&'t obkv::KvReaderU16> {
|
||||||
|
self.documents
|
||||||
|
.get(rtxn, &id)?
|
||||||
|
.ok_or(UserError::UnknownInternalDocumentId { document_id: id })
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the requested documents. The next item will be an error if a document is missing.
|
/// Returns an iterator over the requested documents. The next item will be an error if a document is missing.
|
||||||
pub fn iter_documents<'a, 't: 'a>(
|
pub fn iter_documents<'a, 't: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
rtxn: &'t RoTxn<'t>,
|
rtxn: &'t RoTxn<'t>,
|
||||||
ids: impl IntoIterator<Item = DocumentId> + 'a,
|
ids: impl IntoIterator<Item = DocumentId> + 'a,
|
||||||
) -> Result<impl Iterator<Item = Result<(DocumentId, obkv::KvReaderU16<'t>)>> + 'a> {
|
) -> Result<impl Iterator<Item = Result<(DocumentId, &'t obkv::KvReaderU16)>> + 'a> {
|
||||||
Ok(ids.into_iter().map(move |id| {
|
Ok(ids.into_iter().map(move |id| {
|
||||||
let kv = self
|
let kv = self
|
||||||
.documents
|
.documents
|
||||||
@@ -1271,7 +1285,7 @@ impl Index {
|
|||||||
&self,
|
&self,
|
||||||
rtxn: &'t RoTxn<'t>,
|
rtxn: &'t RoTxn<'t>,
|
||||||
ids: impl IntoIterator<Item = DocumentId>,
|
ids: impl IntoIterator<Item = DocumentId>,
|
||||||
) -> Result<Vec<(DocumentId, obkv::KvReaderU16<'t>)>> {
|
) -> Result<Vec<(DocumentId, &'t obkv::KvReaderU16)>> {
|
||||||
self.iter_documents(rtxn, ids)?.collect()
|
self.iter_documents(rtxn, ids)?.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1279,7 +1293,7 @@ impl Index {
|
|||||||
pub fn all_documents<'a, 't: 'a>(
|
pub fn all_documents<'a, 't: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
rtxn: &'t RoTxn<'t>,
|
rtxn: &'t RoTxn<'t>,
|
||||||
) -> Result<impl Iterator<Item = Result<(DocumentId, obkv::KvReaderU16<'t>)>> + 'a> {
|
) -> Result<impl Iterator<Item = Result<(DocumentId, &'t obkv::KvReaderU16)>> + 'a> {
|
||||||
self.iter_documents(rtxn, self.documents_ids(rtxn)?)
|
self.iter_documents(rtxn, self.documents_ids(rtxn)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1303,7 +1317,7 @@ impl Index {
|
|||||||
})?;
|
})?;
|
||||||
Ok(self.iter_documents(rtxn, ids)?.map(move |entry| -> Result<_> {
|
Ok(self.iter_documents(rtxn, ids)?.map(move |entry| -> Result<_> {
|
||||||
let (_docid, obkv) = entry?;
|
let (_docid, obkv) = entry?;
|
||||||
match primary_key.document_id(&obkv, &fields)? {
|
match primary_key.document_id(obkv, &fields)? {
|
||||||
Ok(document_id) => Ok(document_id),
|
Ok(document_id) => Ok(document_id),
|
||||||
Err(_) => Err(InternalError::DocumentsError(
|
Err(_) => Err(InternalError::DocumentsError(
|
||||||
crate::documents::Error::InvalidDocumentFormat,
|
crate::documents::Error::InvalidDocumentFormat,
|
||||||
@@ -1554,6 +1568,41 @@ impl Index {
|
|||||||
self.main.remap_key_type::<Str>().delete(txn, main_key::PROXIMITY_PRECISION)
|
self.main.remap_key_type::<Str>().delete(txn, main_key::PROXIMITY_PRECISION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prefix_search(&self, txn: &RoTxn<'_>) -> heed::Result<Option<PrefixSearch>> {
|
||||||
|
self.main.remap_types::<Str, SerdeBincode<PrefixSearch>>().get(txn, main_key::PREFIX_SEARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn put_prefix_search(
|
||||||
|
&self,
|
||||||
|
txn: &mut RwTxn<'_>,
|
||||||
|
val: PrefixSearch,
|
||||||
|
) -> heed::Result<()> {
|
||||||
|
self.main.remap_types::<Str, SerdeBincode<PrefixSearch>>().put(
|
||||||
|
txn,
|
||||||
|
main_key::PREFIX_SEARCH,
|
||||||
|
&val,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn delete_prefix_search(&self, txn: &mut RwTxn<'_>) -> heed::Result<bool> {
|
||||||
|
self.main.remap_key_type::<Str>().delete(txn, main_key::PREFIX_SEARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn facet_search(&self, txn: &RoTxn<'_>) -> heed::Result<bool> {
|
||||||
|
self.main
|
||||||
|
.remap_types::<Str, SerdeBincode<bool>>()
|
||||||
|
.get(txn, main_key::FACET_SEARCH)
|
||||||
|
.map(|v| v.unwrap_or(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn put_facet_search(&self, txn: &mut RwTxn<'_>, val: bool) -> heed::Result<()> {
|
||||||
|
self.main.remap_types::<Str, SerdeBincode<bool>>().put(txn, main_key::FACET_SEARCH, &val)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn delete_facet_search(&self, txn: &mut RwTxn<'_>) -> heed::Result<bool> {
|
||||||
|
self.main.remap_key_type::<Str>().delete(txn, main_key::FACET_SEARCH)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn localized_attributes_rules(
|
pub fn localized_attributes_rules(
|
||||||
&self,
|
&self,
|
||||||
rtxn: &RoTxn<'_>,
|
rtxn: &RoTxn<'_>,
|
||||||
@@ -1638,6 +1687,11 @@ impl Index {
|
|||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prefix_settings(&self, rtxn: &RoTxn<'_>) -> Result<PrefixSettings> {
|
||||||
|
let compute_prefixes = self.prefix_search(rtxn)?.unwrap_or_default();
|
||||||
|
Ok(PrefixSettings { compute_prefixes, max_prefix_length: 4, prefix_count_threshold: 100 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
@@ -1647,6 +1701,21 @@ pub struct IndexEmbeddingConfig {
|
|||||||
pub user_provided: RoaringBitmap,
|
pub user_provided: RoaringBitmap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct PrefixSettings {
|
||||||
|
pub prefix_count_threshold: usize,
|
||||||
|
pub max_prefix_length: usize,
|
||||||
|
pub compute_prefixes: PrefixSearch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum PrefixSearch {
|
||||||
|
#[default]
|
||||||
|
IndexingTime,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
struct OffsetDateTime(#[serde(with = "time::serde::rfc3339")] time::OffsetDateTime);
|
struct OffsetDateTime(#[serde(with = "time::serde::rfc3339")] time::OffsetDateTime);
|
||||||
@@ -1657,19 +1726,24 @@ pub(crate) mod tests {
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use big_s::S;
|
use big_s::S;
|
||||||
|
use bumpalo::Bump;
|
||||||
use heed::{EnvOpenOptions, RwTxn};
|
use heed::{EnvOpenOptions, RwTxn};
|
||||||
use maplit::{btreemap, hashset};
|
use maplit::{btreemap, hashset};
|
||||||
|
use memmap2::Mmap;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
use crate::documents::DocumentsBatchReader;
|
|
||||||
use crate::error::{Error, InternalError};
|
use crate::error::{Error, InternalError};
|
||||||
use crate::index::{DEFAULT_MIN_WORD_LEN_ONE_TYPO, DEFAULT_MIN_WORD_LEN_TWO_TYPOS};
|
use crate::index::{DEFAULT_MIN_WORD_LEN_ONE_TYPO, DEFAULT_MIN_WORD_LEN_TWO_TYPOS};
|
||||||
|
use crate::update::new::indexer;
|
||||||
|
use crate::update::settings::InnerIndexSettings;
|
||||||
use crate::update::{
|
use crate::update::{
|
||||||
self, IndexDocuments, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Setting,
|
self, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Setting, Settings,
|
||||||
Settings,
|
|
||||||
};
|
};
|
||||||
use crate::vector::settings::{EmbedderSource, EmbeddingSettings};
|
use crate::vector::settings::{EmbedderSource, EmbeddingSettings};
|
||||||
use crate::{db_snap, obkv_to_json, Filter, Index, Search, SearchResult};
|
use crate::vector::EmbeddingConfigs;
|
||||||
|
use crate::{
|
||||||
|
db_snap, obkv_to_json, Filter, Index, Search, SearchResult, ThreadPoolNoAbortBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct TempIndex {
|
pub(crate) struct TempIndex {
|
||||||
pub inner: Index,
|
pub inner: Index,
|
||||||
@@ -1702,35 +1776,68 @@ pub(crate) mod tests {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::new_with_map_size(4096 * 2000)
|
Self::new_with_map_size(4096 * 2000)
|
||||||
}
|
}
|
||||||
pub fn add_documents_using_wtxn<'t, R>(
|
|
||||||
|
pub fn add_documents_using_wtxn<'t>(
|
||||||
&'t self,
|
&'t self,
|
||||||
wtxn: &mut RwTxn<'t>,
|
wtxn: &mut RwTxn<'t>,
|
||||||
documents: DocumentsBatchReader<R>,
|
documents: Mmap,
|
||||||
) -> Result<(), crate::error::Error>
|
) -> Result<(), crate::error::Error> {
|
||||||
where
|
let local_pool;
|
||||||
R: std::io::Read + std::io::Seek,
|
let indexer_config = &self.indexer_config;
|
||||||
{
|
let pool = match &indexer_config.thread_pool {
|
||||||
let builder = IndexDocuments::new(
|
Some(pool) => pool,
|
||||||
wtxn,
|
None => {
|
||||||
self,
|
local_pool = ThreadPoolNoAbortBuilder::new().build().unwrap();
|
||||||
&self.indexer_config,
|
&local_pool
|
||||||
self.index_documents_config.clone(),
|
}
|
||||||
|_| (),
|
};
|
||||||
|| false,
|
|
||||||
)
|
let rtxn = self.inner.read_txn()?;
|
||||||
.unwrap();
|
let db_fields_ids_map = self.inner.fields_ids_map(&rtxn)?;
|
||||||
let (builder, user_error) = builder.add_documents(documents).unwrap();
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
user_error?;
|
|
||||||
builder.execute()?;
|
let embedders =
|
||||||
|
InnerIndexSettings::from_index(&self.inner, &rtxn, None)?.embedding_configs;
|
||||||
|
let mut indexer =
|
||||||
|
indexer::DocumentOperation::new(self.index_documents_config.update_method);
|
||||||
|
indexer.add_documents(&documents).unwrap();
|
||||||
|
|
||||||
|
let indexer_alloc = Bump::new();
|
||||||
|
let (document_changes, operation_stats, primary_key) = indexer.into_changes(
|
||||||
|
&indexer_alloc,
|
||||||
|
&self.inner,
|
||||||
|
&rtxn,
|
||||||
|
None,
|
||||||
|
&mut new_fields_ids_map,
|
||||||
|
&|| false,
|
||||||
|
&|_progress| (),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if let Some(error) = operation_stats.into_iter().find_map(|stat| stat.error) {
|
||||||
|
return Err(error.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.install(|| {
|
||||||
|
indexer::index(
|
||||||
|
wtxn,
|
||||||
|
&self.inner,
|
||||||
|
&crate::ThreadPoolNoAbortBuilder::new().build().unwrap(),
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
primary_key,
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| false,
|
||||||
|
&|_| (),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn add_documents<R>(
|
|
||||||
&self,
|
pub fn add_documents(&self, documents: Mmap) -> Result<(), crate::error::Error> {
|
||||||
documents: DocumentsBatchReader<R>,
|
|
||||||
) -> Result<(), crate::error::Error>
|
|
||||||
where
|
|
||||||
R: std::io::Read + std::io::Seek,
|
|
||||||
{
|
|
||||||
let mut wtxn = self.write_txn().unwrap();
|
let mut wtxn = self.write_txn().unwrap();
|
||||||
self.add_documents_using_wtxn(&mut wtxn, documents)?;
|
self.add_documents_using_wtxn(&mut wtxn, documents)?;
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
@@ -1746,6 +1853,7 @@ pub(crate) mod tests {
|
|||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_settings_using_wtxn<'t>(
|
pub fn update_settings_using_wtxn<'t>(
|
||||||
&'t self,
|
&'t self,
|
||||||
wtxn: &mut RwTxn<'t>,
|
wtxn: &mut RwTxn<'t>,
|
||||||
@@ -1761,25 +1869,69 @@ pub(crate) mod tests {
|
|||||||
&'t self,
|
&'t self,
|
||||||
wtxn: &mut RwTxn<'t>,
|
wtxn: &mut RwTxn<'t>,
|
||||||
external_document_ids: Vec<String>,
|
external_document_ids: Vec<String>,
|
||||||
) {
|
) -> Result<(), crate::error::Error> {
|
||||||
let builder = IndexDocuments::new(
|
let local_pool;
|
||||||
wtxn,
|
let indexer_config = &self.indexer_config;
|
||||||
self,
|
let pool = match &indexer_config.thread_pool {
|
||||||
&self.indexer_config,
|
Some(pool) => pool,
|
||||||
self.index_documents_config.clone(),
|
None => {
|
||||||
|_| (),
|
local_pool = ThreadPoolNoAbortBuilder::new().build().unwrap();
|
||||||
|| false,
|
&local_pool
|
||||||
)
|
}
|
||||||
.unwrap();
|
};
|
||||||
let (builder, user_error) = builder.remove_documents(external_document_ids).unwrap();
|
|
||||||
user_error.unwrap();
|
let rtxn = self.inner.read_txn()?;
|
||||||
builder.execute().unwrap();
|
let db_fields_ids_map = self.inner.fields_ids_map(&rtxn)?;
|
||||||
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
|
|
||||||
|
let embedders =
|
||||||
|
InnerIndexSettings::from_index(&self.inner, &rtxn, None)?.embedding_configs;
|
||||||
|
|
||||||
|
let mut indexer =
|
||||||
|
indexer::DocumentOperation::new(self.index_documents_config.update_method);
|
||||||
|
let external_document_ids: Vec<_> =
|
||||||
|
external_document_ids.iter().map(AsRef::as_ref).collect();
|
||||||
|
indexer.delete_documents(external_document_ids.as_slice());
|
||||||
|
|
||||||
|
let indexer_alloc = Bump::new();
|
||||||
|
let (document_changes, operation_stats, primary_key) = indexer.into_changes(
|
||||||
|
&indexer_alloc,
|
||||||
|
&self.inner,
|
||||||
|
&rtxn,
|
||||||
|
None,
|
||||||
|
&mut new_fields_ids_map,
|
||||||
|
&|| false,
|
||||||
|
&|_progress| (),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if let Some(error) = operation_stats.into_iter().find_map(|stat| stat.error) {
|
||||||
|
return Err(error.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.install(|| {
|
||||||
|
indexer::index(
|
||||||
|
wtxn,
|
||||||
|
&self.inner,
|
||||||
|
&crate::ThreadPoolNoAbortBuilder::new().build().unwrap(),
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
primary_key,
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| false,
|
||||||
|
&|_| (),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_documents(&self, external_document_ids: Vec<String>) {
|
pub fn delete_documents(&self, external_document_ids: Vec<String>) {
|
||||||
let mut wtxn = self.write_txn().unwrap();
|
let mut wtxn = self.write_txn().unwrap();
|
||||||
|
|
||||||
self.delete_documents_using_wtxn(&mut wtxn, external_document_ids);
|
self.delete_documents_using_wtxn(&mut wtxn, external_document_ids).unwrap();
|
||||||
|
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
}
|
}
|
||||||
@@ -1796,29 +1948,64 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
let mut wtxn = index.inner.write_txn().unwrap();
|
let mut wtxn = index.inner.write_txn().unwrap();
|
||||||
|
|
||||||
let should_abort = AtomicBool::new(false);
|
let should_abort = AtomicBool::new(false);
|
||||||
let builder = IndexDocuments::new(
|
|
||||||
&mut wtxn,
|
|
||||||
&index.inner,
|
|
||||||
&index.indexer_config,
|
|
||||||
index.index_documents_config.clone(),
|
|
||||||
|_| (),
|
|
||||||
|| should_abort.load(Relaxed),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let (builder, user_error) = builder
|
let local_pool;
|
||||||
.add_documents(documents!([
|
let indexer_config = &index.indexer_config;
|
||||||
{ "id": 1, "name": "kevin" },
|
let pool = match &indexer_config.thread_pool {
|
||||||
{ "id": 2, "name": "bob", "age": 20 },
|
Some(pool) => pool,
|
||||||
{ "id": 2, "name": "bob", "age": 20 },
|
None => {
|
||||||
]))
|
local_pool = ThreadPoolNoAbortBuilder::new().build().unwrap();
|
||||||
|
&local_pool
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let rtxn = index.inner.read_txn().unwrap();
|
||||||
|
let db_fields_ids_map = index.inner.fields_ids_map(&rtxn).unwrap();
|
||||||
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
|
|
||||||
|
let embedders = EmbeddingConfigs::default();
|
||||||
|
let mut indexer = indexer::DocumentOperation::new(IndexDocumentsMethod::ReplaceDocuments);
|
||||||
|
let payload = documents!([
|
||||||
|
{ "id": 1, "name": "kevin" },
|
||||||
|
{ "id": 2, "name": "bob", "age": 20 },
|
||||||
|
{ "id": 2, "name": "bob", "age": 20 },
|
||||||
|
]);
|
||||||
|
indexer.add_documents(&payload).unwrap();
|
||||||
|
|
||||||
|
let indexer_alloc = Bump::new();
|
||||||
|
let (document_changes, _operation_stats, primary_key) = indexer
|
||||||
|
.into_changes(
|
||||||
|
&indexer_alloc,
|
||||||
|
&index.inner,
|
||||||
|
&rtxn,
|
||||||
|
None,
|
||||||
|
&mut new_fields_ids_map,
|
||||||
|
&|| false,
|
||||||
|
&|_progress| (),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
user_error.unwrap();
|
|
||||||
|
|
||||||
should_abort.store(true, Relaxed);
|
should_abort.store(true, Relaxed);
|
||||||
let err = builder.execute().unwrap_err();
|
|
||||||
|
let err = pool
|
||||||
|
.install(|| {
|
||||||
|
indexer::index(
|
||||||
|
&mut wtxn,
|
||||||
|
&index.inner,
|
||||||
|
&crate::ThreadPoolNoAbortBuilder::new().build().unwrap(),
|
||||||
|
indexer_config.grenad_parameters(),
|
||||||
|
&db_fields_ids_map,
|
||||||
|
new_fields_ids_map,
|
||||||
|
primary_key,
|
||||||
|
&document_changes,
|
||||||
|
embedders,
|
||||||
|
&|| should_abort.load(Relaxed),
|
||||||
|
&|_| (),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
assert!(matches!(err, Error::InternalError(InternalError::AbortedIndexation)));
|
assert!(matches!(err, Error::InternalError(InternalError::AbortedIndexation)));
|
||||||
}
|
}
|
||||||
@@ -2314,7 +2501,16 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
// And adding lots of documents afterwards instead of just one.
|
// And adding lots of documents afterwards instead of just one.
|
||||||
// These extra subtests don't add much, but it's better than nothing.
|
// These extra subtests don't add much, but it's better than nothing.
|
||||||
index.add_documents(documents!([{ "primary_key": 38 }, { "primary_key": 39 }, { "primary_key": 41 }, { "primary_key": 40 }, { "primary_key": 41 }, { "primary_key": 42 }])).unwrap();
|
index
|
||||||
|
.add_documents(documents!([
|
||||||
|
{ "primary_key": 38 },
|
||||||
|
{ "primary_key": 39 },
|
||||||
|
{ "primary_key": 41 },
|
||||||
|
{ "primary_key": 40 },
|
||||||
|
{ "primary_key": 41 },
|
||||||
|
{ "primary_key": 42 },
|
||||||
|
]))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
db_snap!(index, documents_ids, @"[0, 1, 2, 3, 4, 5, ]");
|
db_snap!(index, documents_ids, @"[0, 1, 2, 3, 4, 5, ]");
|
||||||
db_snap!(index, external_documents_ids, 7, @r###"
|
db_snap!(index, external_documents_ids, 7, @r###"
|
||||||
@@ -2701,7 +2897,7 @@ pub(crate) mod tests {
|
|||||||
documents!({ "id" : "doggo", "_geo": { "lat": 1, "lng": 2, "doggo": "are the best" }}),
|
documents!({ "id" : "doggo", "_geo": { "lat": 1, "lng": 2, "doggo": "are the best" }}),
|
||||||
)
|
)
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
insta::assert_snapshot!(err, @r###"The `_geo` field in the document with the id: `"\"doggo\""` contains the following unexpected fields: `{"doggo":"are the best"}`."###);
|
insta::assert_snapshot!(err, @r###"The `_geo` field in the document with the id: `"doggo"` contains the following unexpected fields: `{"doggo":"are the best"}`."###);
|
||||||
|
|
||||||
db_snap!(index, geo_faceted_documents_ids); // ensure that no documents were inserted
|
db_snap!(index, geo_faceted_documents_ids); // ensure that no documents were inserted
|
||||||
|
|
||||||
@@ -2711,7 +2907,7 @@ pub(crate) mod tests {
|
|||||||
documents!({ "id" : "doggo", "_geo": { "lat": 1, "lng": 2, "doggo": "are the best", "and": { "all": ["cats", { "are": "beautiful" } ] } } }),
|
documents!({ "id" : "doggo", "_geo": { "lat": 1, "lng": 2, "doggo": "are the best", "and": { "all": ["cats", { "are": "beautiful" } ] } } }),
|
||||||
)
|
)
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
insta::assert_snapshot!(err, @r###"The `_geo` field in the document with the id: `"\"doggo\""` contains the following unexpected fields: `{"and":{"all":["cats",{"are":"beautiful"}]},"doggo":"are the best"}`."###);
|
insta::assert_snapshot!(err, @r###"The `_geo` field in the document with the id: `"doggo"` contains the following unexpected fields: `{"and":{"all":["cats",{"are":"beautiful"}]},"doggo":"are the best"}`."###);
|
||||||
|
|
||||||
db_snap!(index, geo_faceted_documents_ids); // ensure that no documents were inserted
|
db_snap!(index, geo_faceted_documents_ids); // ensure that no documents were inserted
|
||||||
}
|
}
|
||||||
@@ -2830,7 +3026,6 @@ pub(crate) mod tests {
|
|||||||
db_snap!(index, fields_ids_map, @r###"
|
db_snap!(index, fields_ids_map, @r###"
|
||||||
0 id |
|
0 id |
|
||||||
1 _vectors |
|
1 _vectors |
|
||||||
2 _vectors.doggo |
|
|
||||||
"###);
|
"###);
|
||||||
db_snap!(index, searchable_fields, @r###"["id"]"###);
|
db_snap!(index, searchable_fields, @r###"["id"]"###);
|
||||||
db_snap!(index, fieldids_weights_map, @r###"
|
db_snap!(index, fieldids_weights_map, @r###"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![cfg_attr(all(test, fuzzing), feature(no_coverage))]
|
#![cfg_attr(all(test, fuzzing), feature(no_coverage))]
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
pub static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
pub static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
@@ -55,7 +56,7 @@ pub use self::error::{
|
|||||||
};
|
};
|
||||||
pub use self::external_documents_ids::ExternalDocumentsIds;
|
pub use self::external_documents_ids::ExternalDocumentsIds;
|
||||||
pub use self::fieldids_weights_map::FieldidsWeightsMap;
|
pub use self::fieldids_weights_map::FieldidsWeightsMap;
|
||||||
pub use self::fields_ids_map::FieldsIdsMap;
|
pub use self::fields_ids_map::{FieldsIdsMap, GlobalFieldsIdsMap};
|
||||||
pub use self::heed_codec::{
|
pub use self::heed_codec::{
|
||||||
BEU16StrCodec, BEU32StrCodec, BoRoaringBitmapCodec, BoRoaringBitmapLenCodec,
|
BEU16StrCodec, BEU32StrCodec, BoRoaringBitmapCodec, BoRoaringBitmapLenCodec,
|
||||||
CboRoaringBitmapCodec, CboRoaringBitmapLenCodec, FieldIdWordCountCodec, ObkvCodec,
|
CboRoaringBitmapCodec, CboRoaringBitmapLenCodec, FieldIdWordCountCodec, ObkvCodec,
|
||||||
@@ -88,6 +89,7 @@ pub type Object = serde_json::Map<String, serde_json::Value>;
|
|||||||
pub type Position = u32;
|
pub type Position = u32;
|
||||||
pub type RelativePosition = u16;
|
pub type RelativePosition = u16;
|
||||||
pub type SmallString32 = smallstr::SmallString<[u8; 32]>;
|
pub type SmallString32 = smallstr::SmallString<[u8; 32]>;
|
||||||
|
pub type Prefix = smallstr::SmallString<[u8; 16]>;
|
||||||
pub type SmallVec16<T> = smallvec::SmallVec<[T; 16]>;
|
pub type SmallVec16<T> = smallvec::SmallVec<[T; 16]>;
|
||||||
pub type SmallVec32<T> = smallvec::SmallVec<[T; 32]>;
|
pub type SmallVec32<T> = smallvec::SmallVec<[T; 32]>;
|
||||||
pub type SmallVec8<T> = smallvec::SmallVec<[T; 8]>;
|
pub type SmallVec8<T> = smallvec::SmallVec<[T; 8]>;
|
||||||
@@ -214,7 +216,7 @@ pub fn bucketed_position(relative: u16) -> u16 {
|
|||||||
pub fn obkv_to_json(
|
pub fn obkv_to_json(
|
||||||
displayed_fields: &[FieldId],
|
displayed_fields: &[FieldId],
|
||||||
fields_ids_map: &FieldsIdsMap,
|
fields_ids_map: &FieldsIdsMap,
|
||||||
obkv: obkv::KvReaderU16<'_>,
|
obkv: &obkv::KvReaderU16,
|
||||||
) -> Result<Object> {
|
) -> Result<Object> {
|
||||||
displayed_fields
|
displayed_fields
|
||||||
.iter()
|
.iter()
|
||||||
@@ -232,10 +234,7 @@ pub fn obkv_to_json(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Transform every field of a raw obkv store into a JSON Object.
|
/// Transform every field of a raw obkv store into a JSON Object.
|
||||||
pub fn all_obkv_to_json(
|
pub fn all_obkv_to_json(obkv: &obkv::KvReaderU16, fields_ids_map: &FieldsIdsMap) -> Result<Object> {
|
||||||
obkv: obkv::KvReaderU16<'_>,
|
|
||||||
fields_ids_map: &FieldsIdsMap,
|
|
||||||
) -> Result<Object> {
|
|
||||||
let all_keys = obkv.iter().map(|(k, _v)| k).collect::<Vec<_>>();
|
let all_keys = obkv.iter().map(|(k, _v)| k).collect::<Vec<_>>();
|
||||||
obkv_to_json(all_keys.as_slice(), fields_ids_map, obkv)
|
obkv_to_json(all_keys.as_slice(), fields_ids_map, obkv)
|
||||||
}
|
}
|
||||||
@@ -434,7 +433,7 @@ mod tests {
|
|||||||
writer.insert(id1, b"1234").unwrap();
|
writer.insert(id1, b"1234").unwrap();
|
||||||
writer.insert(id2, b"4321").unwrap();
|
writer.insert(id2, b"4321").unwrap();
|
||||||
let contents = writer.into_inner().unwrap();
|
let contents = writer.into_inner().unwrap();
|
||||||
let obkv = obkv::KvReaderU16::new(&contents);
|
let obkv = obkv::KvReaderU16::from_slice(&contents);
|
||||||
|
|
||||||
let expected = json!({
|
let expected = json!({
|
||||||
"field1": 1234,
|
"field1": 1234,
|
||||||
|
|||||||
@@ -3,23 +3,19 @@ use liquid::model::{
|
|||||||
};
|
};
|
||||||
use liquid::{ObjectView, ValueView};
|
use liquid::{ObjectView, ValueView};
|
||||||
|
|
||||||
use super::document::Document;
|
|
||||||
use super::fields::Fields;
|
|
||||||
use super::FieldsIdsMapWithMetadata;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a, D: ObjectView, F: ArrayView> {
|
||||||
document: &'a Document<'a>,
|
document: &'a D,
|
||||||
fields: Fields<'a>,
|
fields: &'a F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a, D: ObjectView, F: ArrayView> Context<'a, D, F> {
|
||||||
pub fn new(document: &'a Document<'a>, field_id_map: &'a FieldsIdsMapWithMetadata<'a>) -> Self {
|
pub fn new(document: &'a D, fields: &'a F) -> Self {
|
||||||
Self { document, fields: Fields::new(document, field_id_map) }
|
Self { document, fields }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ObjectView for Context<'a> {
|
impl<'a, D: ObjectView, F: ArrayView> ObjectView for Context<'a, D, F> {
|
||||||
fn as_value(&self) -> &dyn ValueView {
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -56,7 +52,7 @@ impl<'a> ObjectView for Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ValueView for Context<'a> {
|
impl<'a, D: ObjectView, F: ArrayView> ValueView for Context<'a, D, F> {
|
||||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
use liquid::model::{
|
use liquid::model::{
|
||||||
DisplayCow, KString, KStringCow, ObjectRender, ObjectSource, State, Value as LiquidValue,
|
ArrayView, DisplayCow, KString, KStringCow, ObjectRender, ObjectSource, ScalarCow, State,
|
||||||
|
Value as LiquidValue,
|
||||||
};
|
};
|
||||||
use liquid::{ObjectView, ValueView};
|
use liquid::{ObjectView, ValueView};
|
||||||
|
use raw_collections::{RawMap, RawVec};
|
||||||
|
use serde_json::value::RawValue;
|
||||||
|
|
||||||
use crate::update::del_add::{DelAdd, KvReaderDelAdd};
|
use crate::update::del_add::{DelAdd, KvReaderDelAdd};
|
||||||
use crate::FieldsIdsMap;
|
use crate::FieldsIdsMap;
|
||||||
@@ -30,13 +35,13 @@ impl ParsedValue {
|
|||||||
|
|
||||||
impl<'a> Document<'a> {
|
impl<'a> Document<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
data: obkv::KvReaderU16<'a>,
|
data: &'a obkv::KvReaderU16,
|
||||||
side: DelAdd,
|
side: DelAdd,
|
||||||
inverted_field_map: &'a FieldsIdsMap,
|
inverted_field_map: &'a FieldsIdsMap,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut out_data = BTreeMap::new();
|
let mut out_data = BTreeMap::new();
|
||||||
for (fid, raw) in data {
|
for (fid, raw) in data {
|
||||||
let obkv = KvReaderDelAdd::new(raw);
|
let obkv = KvReaderDelAdd::from_slice(raw);
|
||||||
let Some(raw) = obkv.get(side) else {
|
let Some(raw) = obkv.get(side) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@@ -93,7 +98,7 @@ impl<'a> ObjectView for Document<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ValueView for Document<'a> {
|
impl<'a> ValueView for Document<'a> {
|
||||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
fn as_debug(&self) -> &dyn Debug {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,4 +133,515 @@ impl<'a> ValueView for Document<'a> {
|
|||||||
fn as_object(&self) -> Option<&dyn ObjectView> {
|
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_object(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation for any type that implements the Document trait
|
||||||
|
use crate::update::new::document::Document as DocumentTrait;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParseableDocument<'doc, D> {
|
||||||
|
document: D,
|
||||||
|
doc_alloc: &'doc Bump,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc, D> ParseableDocument<'doc, D> {
|
||||||
|
pub fn new(document: D, doc_alloc: &'doc Bump) -> Self {
|
||||||
|
Self { document, doc_alloc }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc, D: DocumentTrait<'doc> + Debug> ObjectView for ParseableDocument<'doc, D> {
|
||||||
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> i64 {
|
||||||
|
self.document.top_level_fields_count() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
|
||||||
|
Box::new(self.document.iter_top_level_fields().map(|res| {
|
||||||
|
let (field, _) = res.unwrap();
|
||||||
|
KStringCow::from_ref(field)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||||
|
Box::new(self.document.iter_top_level_fields().map(|res| {
|
||||||
|
let (_, value) = res.unwrap();
|
||||||
|
ParseableValue::new_bump(value, self.doc_alloc) as _
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
|
||||||
|
Box::new(self.document.iter_top_level_fields().map(|res| {
|
||||||
|
let (field, value) = res.unwrap();
|
||||||
|
(KStringCow::from_ref(field), ParseableValue::new_bump(value, self.doc_alloc) as _)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_key(&self, index: &str) -> bool {
|
||||||
|
self.document.top_level_field(index).unwrap().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
|
||||||
|
let s = self.document.top_level_field(index).unwrap()?;
|
||||||
|
Some(ParseableValue::new_bump(s, self.doc_alloc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc, D: DocumentTrait<'doc> + Debug> ValueView for ParseableDocument<'doc, D> {
|
||||||
|
fn as_debug(&self) -> &dyn fmt::Debug {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn render(&self) -> liquid::model::DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ObjectRender::new(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> liquid::model::DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ObjectSource::new(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
"object"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_state(&self, state: liquid::model::State) -> bool {
|
||||||
|
match state {
|
||||||
|
State::Truthy => true,
|
||||||
|
State::DefaultValue | State::Empty | State::Blank => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
|
||||||
|
let s = ObjectRender::new(self).to_string();
|
||||||
|
KStringCow::from_string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> LiquidValue {
|
||||||
|
LiquidValue::Object(
|
||||||
|
self.document
|
||||||
|
.iter_top_level_fields()
|
||||||
|
.map(|res| {
|
||||||
|
let (k, v) = res.unwrap();
|
||||||
|
(k.to_string().into(), ParseableValue::new(v, self.doc_alloc).to_value())
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_object(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseableValue<'doc> {
|
||||||
|
value: raw_collections::Value<'doc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ParseableValue<'doc> {
|
||||||
|
pub fn new(value: &'doc RawValue, doc_alloc: &'doc Bump) -> Self {
|
||||||
|
let value = raw_collections::Value::from_raw_value(value, doc_alloc).unwrap();
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_bump(value: &'doc RawValue, doc_alloc: &'doc Bump) -> &'doc Self {
|
||||||
|
doc_alloc.alloc(Self::new(value, doc_alloc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transparent newtype for implementing ValueView
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseableMap<'doc>(RawMap<'doc>);
|
||||||
|
|
||||||
|
// transparent newtype for implementing ValueView
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParseableArray<'doc>(RawVec<'doc>);
|
||||||
|
|
||||||
|
impl<'doc> ParseableMap<'doc> {
|
||||||
|
pub fn as_parseable<'a>(map: &'a RawMap<'doc>) -> &'a ParseableMap<'doc> {
|
||||||
|
// SAFETY: repr(transparent)
|
||||||
|
unsafe { &*(map as *const RawMap as *const Self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ParseableArray<'doc> {
|
||||||
|
pub fn as_parseable<'a>(array: &'a RawVec<'doc>) -> &'a ParseableArray<'doc> {
|
||||||
|
// SAFETY: repr(transparent)
|
||||||
|
unsafe { &*(array as *const RawVec as *const Self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ArrayView for ParseableArray<'doc> {
|
||||||
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> i64 {
|
||||||
|
self.0.len() as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||||
|
Box::new(self.0.iter().map(|v| ParseableValue::new_bump(v, self.0.bump()) as _))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_key(&self, index: i64) -> bool {
|
||||||
|
let index = convert_index(index, self.size());
|
||||||
|
index < self.size() && index >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, index: i64) -> Option<&dyn ValueView> {
|
||||||
|
let index = convert_index(index, self.size());
|
||||||
|
if index <= 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let v = self.0.get(index as usize)?;
|
||||||
|
Some(ParseableValue::new_bump(v, self.0.bump()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ValueView for ParseableArray<'doc> {
|
||||||
|
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ArrayRender { s: &self.0 }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ArraySource { s: &self.0 }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
"array"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_state(&self, state: State) -> bool {
|
||||||
|
match state {
|
||||||
|
State::Truthy => true,
|
||||||
|
State::DefaultValue | State::Empty | State::Blank => self.0.is_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_kstr(&self) -> KStringCow<'_> {
|
||||||
|
let s = ArrayRender { s: &self.0 }.to_string();
|
||||||
|
KStringCow::from_string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> LiquidValue {
|
||||||
|
LiquidValue::Array(self.values().map(|v| v.to_value()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_array(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_array(&self) -> Option<&dyn ArrayView> {
|
||||||
|
Some(self as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ObjectView for ParseableMap<'doc> {
|
||||||
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> i64 {
|
||||||
|
self.0.len() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
|
||||||
|
Box::new(self.0.keys().map(Into::into))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||||
|
Box::new(self.0.values().map(|value| {
|
||||||
|
let doc_alloc = self.0.bump();
|
||||||
|
ParseableValue::new_bump(value, doc_alloc) as _
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
|
||||||
|
Box::new(self.0.iter().map(|(k, v)| {
|
||||||
|
let doc_alloc = self.0.bump();
|
||||||
|
(k.into(), ParseableValue::new_bump(v, doc_alloc) as _)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_key(&self, index: &str) -> bool {
|
||||||
|
self.0.get(index).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
|
||||||
|
let v = self.0.get(index)?;
|
||||||
|
let doc_alloc = self.0.bump();
|
||||||
|
let value = ParseableValue::new(v, doc_alloc);
|
||||||
|
Some(doc_alloc.alloc(value) as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ValueView for ParseableMap<'doc> {
|
||||||
|
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> liquid::model::DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ObjectRender::new(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> liquid::model::DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ObjectSource::new(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
"object"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_state(&self, state: liquid::model::State) -> bool {
|
||||||
|
match state {
|
||||||
|
State::Truthy => true,
|
||||||
|
State::DefaultValue | State::Empty | State::Blank => self.0.is_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
|
||||||
|
let s = ObjectRender::new(self).to_string();
|
||||||
|
KStringCow::from_string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> LiquidValue {
|
||||||
|
LiquidValue::Object(
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(k.to_string().into(), ParseableValue::new(v, self.0.bump()).to_value())
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_object(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'doc> ValueView for ParseableValue<'doc> {
|
||||||
|
fn as_debug(&self) -> &dyn Debug {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> DisplayCow<'_> {
|
||||||
|
use raw_collections::value::Number;
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Null => LiquidValue::Nil.render(),
|
||||||
|
Value::Bool(v) => v.render(),
|
||||||
|
Value::Number(number) => match number {
|
||||||
|
Number::PosInt(x) => DisplayCow::Borrowed(x),
|
||||||
|
Number::NegInt(x) => x.render(),
|
||||||
|
Number::Finite(x) => x.render(),
|
||||||
|
},
|
||||||
|
Value::String(s) => s.render(),
|
||||||
|
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).render(),
|
||||||
|
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).render(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> DisplayCow<'_> {
|
||||||
|
use raw_collections::value::Number;
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Null => LiquidValue::Nil.source(),
|
||||||
|
Value::Bool(v) => ValueView::source(v),
|
||||||
|
Value::Number(number) => match number {
|
||||||
|
Number::PosInt(x) => DisplayCow::Borrowed(x),
|
||||||
|
Number::NegInt(x) => x.source(),
|
||||||
|
Number::Finite(x) => x.source(),
|
||||||
|
},
|
||||||
|
Value::String(s) => s.source(),
|
||||||
|
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).source(),
|
||||||
|
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).source(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
use raw_collections::value::Number;
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Null => LiquidValue::Nil.type_name(),
|
||||||
|
Value::Bool(v) => v.type_name(),
|
||||||
|
Value::Number(number) => match number {
|
||||||
|
Number::PosInt(_x) => "whole positive number",
|
||||||
|
Number::NegInt(x) => x.type_name(),
|
||||||
|
Number::Finite(x) => x.type_name(),
|
||||||
|
},
|
||||||
|
Value::String(s) => s.type_name(),
|
||||||
|
Value::Array(_raw_vec) => "array",
|
||||||
|
Value::Object(_raw_map) => "object",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_state(&self, state: State) -> bool {
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Null => ValueView::query_state(&LiquidValue::Nil, state),
|
||||||
|
Value::Bool(v) => ValueView::query_state(v, state),
|
||||||
|
Value::Number(_number) => match state {
|
||||||
|
State::Truthy => true,
|
||||||
|
State::DefaultValue => false,
|
||||||
|
State::Empty => false,
|
||||||
|
State::Blank => false,
|
||||||
|
},
|
||||||
|
Value::String(s) => ValueView::query_state(s, state),
|
||||||
|
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).query_state(state),
|
||||||
|
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).query_state(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_kstr(&self) -> KStringCow<'_> {
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Null => ValueView::to_kstr(&LiquidValue::Nil),
|
||||||
|
Value::Bool(v) => ValueView::to_kstr(v),
|
||||||
|
Value::Number(_number) => self.render().to_string().into(),
|
||||||
|
Value::String(s) => KStringCow::from_ref(s),
|
||||||
|
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).to_kstr(),
|
||||||
|
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).to_kstr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> LiquidValue {
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Null => LiquidValue::Nil,
|
||||||
|
Value::Bool(v) => LiquidValue::Scalar(liquid::model::ScalarCow::new(*v)),
|
||||||
|
Value::Number(number) => match number {
|
||||||
|
raw_collections::value::Number::PosInt(number) => {
|
||||||
|
let number: i64 = match (*number).try_into() {
|
||||||
|
Ok(number) => number,
|
||||||
|
Err(_) => {
|
||||||
|
return LiquidValue::Scalar(ScalarCow::new(self.render().to_string()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LiquidValue::Scalar(ScalarCow::new(number))
|
||||||
|
}
|
||||||
|
raw_collections::value::Number::NegInt(number) => {
|
||||||
|
LiquidValue::Scalar(ScalarCow::new(*number))
|
||||||
|
}
|
||||||
|
raw_collections::value::Number::Finite(number) => {
|
||||||
|
LiquidValue::Scalar(ScalarCow::new(*number))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Value::String(s) => LiquidValue::Scalar(liquid::model::ScalarCow::new(s.to_string())),
|
||||||
|
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).to_value(),
|
||||||
|
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).to_value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_scalar(&self) -> Option<liquid::model::ScalarCow<'_>> {
|
||||||
|
use raw_collections::value::Number;
|
||||||
|
use raw_collections::Value;
|
||||||
|
match &self.value {
|
||||||
|
Value::Bool(v) => Some(liquid::model::ScalarCow::new(*v)),
|
||||||
|
Value::Number(number) => match number {
|
||||||
|
Number::PosInt(number) => {
|
||||||
|
let number: i64 = match (*number).try_into() {
|
||||||
|
Ok(number) => number,
|
||||||
|
Err(_) => return Some(ScalarCow::new(self.render().to_string())),
|
||||||
|
};
|
||||||
|
Some(ScalarCow::new(number))
|
||||||
|
}
|
||||||
|
Number::NegInt(number) => Some(ScalarCow::new(*number)),
|
||||||
|
Number::Finite(number) => Some(ScalarCow::new(*number)),
|
||||||
|
},
|
||||||
|
Value::String(s) => Some(ScalarCow::new(*s)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_scalar(&self) -> bool {
|
||||||
|
use raw_collections::Value;
|
||||||
|
matches!(&self.value, Value::Bool(_) | Value::Number(_) | Value::String(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_array(&self) -> Option<&dyn liquid::model::ArrayView> {
|
||||||
|
if let raw_collections::Value::Array(array) = &self.value {
|
||||||
|
return Some(ParseableArray::as_parseable(array) as _);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_array(&self) -> bool {
|
||||||
|
matches!(&self.value, raw_collections::Value::Array(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||||
|
if let raw_collections::Value::Object(object) = &self.value {
|
||||||
|
return Some(ParseableMap::as_parseable(object) as _);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_object(&self) -> bool {
|
||||||
|
matches!(&self.value, raw_collections::Value::Object(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_nil(&self) -> bool {
|
||||||
|
matches!(&self.value, raw_collections::Value::Null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArraySource<'s, 'doc> {
|
||||||
|
s: &'s RawVec<'doc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s, 'doc> fmt::Display for ArraySource<'s, 'doc> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "[")?;
|
||||||
|
for item in self.s {
|
||||||
|
let v = ParseableValue::new(item, self.s.bump());
|
||||||
|
write!(f, "{}, ", v.render())?;
|
||||||
|
}
|
||||||
|
write!(f, "]")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArrayRender<'s, 'doc> {
|
||||||
|
s: &'s RawVec<'doc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s, 'doc> fmt::Display for ArrayRender<'s, 'doc> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for item in self.s {
|
||||||
|
let v = ParseableValue::new(item, self.s.bump());
|
||||||
|
|
||||||
|
write!(f, "{}", v.render())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_index(index: i64, max_size: i64) -> i64 {
|
||||||
|
if 0 <= index {
|
||||||
|
index
|
||||||
|
} else {
|
||||||
|
max_size + index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,23 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
use liquid::model::{
|
use liquid::model::{
|
||||||
ArrayView, DisplayCow, KStringCow, ObjectRender, ObjectSource, State, Value as LiquidValue,
|
ArrayView, DisplayCow, KStringCow, ObjectRender, ObjectSource, State, Value as LiquidValue,
|
||||||
};
|
};
|
||||||
use liquid::{ObjectView, ValueView};
|
use liquid::{ObjectView, ValueView};
|
||||||
|
|
||||||
use super::document::Document;
|
|
||||||
use super::{FieldMetadata, FieldsIdsMapWithMetadata};
|
use super::{FieldMetadata, FieldsIdsMapWithMetadata};
|
||||||
#[derive(Debug, Clone)]
|
use crate::GlobalFieldsIdsMap;
|
||||||
pub struct Fields<'a>(Vec<FieldValue<'a>>);
|
|
||||||
|
|
||||||
impl<'a> Fields<'a> {
|
|
||||||
pub fn new(document: &'a Document<'a>, field_id_map: &'a FieldsIdsMapWithMetadata<'a>) -> Self {
|
|
||||||
Self(
|
|
||||||
std::iter::repeat(document)
|
|
||||||
.zip(field_id_map.iter())
|
|
||||||
.map(|(document, (fid, name))| FieldValue {
|
|
||||||
document,
|
|
||||||
name,
|
|
||||||
metadata: field_id_map.metadata(fid).unwrap_or_default(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct FieldValue<'a> {
|
pub struct FieldValue<'a, D: ObjectView> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
document: &'a Document<'a>,
|
document: &'a D,
|
||||||
metadata: FieldMetadata,
|
metadata: FieldMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ValueView for FieldValue<'a> {
|
impl<'a, D: ObjectView> ValueView for FieldValue<'a, D> {
|
||||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -70,7 +57,7 @@ impl<'a> ValueView for FieldValue<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FieldValue<'a> {
|
impl<'a, D: ObjectView> FieldValue<'a, D> {
|
||||||
pub fn name(&self) -> &&'a str {
|
pub fn name(&self) -> &&'a str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
@@ -88,7 +75,7 @@ impl<'a> FieldValue<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ObjectView for FieldValue<'a> {
|
impl<'a, D: ObjectView> ObjectView for FieldValue<'a, D> {
|
||||||
fn as_value(&self) -> &dyn ValueView {
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -127,7 +114,42 @@ impl<'a> ObjectView for FieldValue<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArrayView for Fields<'a> {
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OwnedFields<'a, D: ObjectView>(Vec<FieldValue<'a, D>>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BorrowedFields<'a, 'map, D: ObjectView> {
|
||||||
|
document: &'a D,
|
||||||
|
field_id_map: &'a RefCell<GlobalFieldsIdsMap<'map>>,
|
||||||
|
doc_alloc: &'a Bump,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: ObjectView> OwnedFields<'a, D> {
|
||||||
|
pub fn new(document: &'a D, field_id_map: &'a FieldsIdsMapWithMetadata<'a>) -> Self {
|
||||||
|
Self(
|
||||||
|
std::iter::repeat(document)
|
||||||
|
.zip(field_id_map.iter())
|
||||||
|
.map(|(document, (fid, name))| FieldValue {
|
||||||
|
document,
|
||||||
|
name,
|
||||||
|
metadata: field_id_map.metadata(fid).unwrap_or_default(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'map, D: ObjectView> BorrowedFields<'a, 'map, D> {
|
||||||
|
pub fn new(
|
||||||
|
document: &'a D,
|
||||||
|
field_id_map: &'a RefCell<GlobalFieldsIdsMap<'map>>,
|
||||||
|
doc_alloc: &'a Bump,
|
||||||
|
) -> Self {
|
||||||
|
Self { document, field_id_map, doc_alloc }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: ObjectView> ArrayView for OwnedFields<'a, D> {
|
||||||
fn as_value(&self) -> &dyn ValueView {
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
self.0.as_value()
|
self.0.as_value()
|
||||||
}
|
}
|
||||||
@@ -149,7 +171,91 @@ impl<'a> ArrayView for Fields<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ValueView for Fields<'a> {
|
impl<'a, 'map, D: ObjectView> ArrayView for BorrowedFields<'a, 'map, D> {
|
||||||
|
fn as_value(&self) -> &dyn ValueView {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> i64 {
|
||||||
|
self.document.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||||
|
Box::new(self.document.keys().map(|k| {
|
||||||
|
let mut field_id_map = self.field_id_map.borrow_mut();
|
||||||
|
let (_, metadata) = field_id_map.id_with_metadata_or_insert(&k).unwrap();
|
||||||
|
let fv = self.doc_alloc.alloc(FieldValue {
|
||||||
|
name: self.doc_alloc.alloc_str(&k),
|
||||||
|
document: self.document,
|
||||||
|
metadata: FieldMetadata { searchable: metadata.searchable },
|
||||||
|
});
|
||||||
|
fv as _
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_key(&self, index: i64) -> bool {
|
||||||
|
let index = if index >= 0 { index } else { self.size() + index };
|
||||||
|
index >= 0 && index < self.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, index: i64) -> Option<&dyn ValueView> {
|
||||||
|
let index = if index >= 0 { index } else { self.size() + index };
|
||||||
|
let index: usize = index.try_into().ok()?;
|
||||||
|
let key = self.document.keys().nth(index)?;
|
||||||
|
let mut field_id_map = self.field_id_map.borrow_mut();
|
||||||
|
let (_, metadata) = field_id_map.id_with_metadata_or_insert(&key)?;
|
||||||
|
let fv = self.doc_alloc.alloc(FieldValue {
|
||||||
|
name: self.doc_alloc.alloc_str(&key),
|
||||||
|
document: self.document,
|
||||||
|
metadata: FieldMetadata { searchable: metadata.searchable },
|
||||||
|
});
|
||||||
|
Some(fv as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'map, D: ObjectView> ValueView for BorrowedFields<'a, 'map, D> {
|
||||||
|
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self) -> liquid::model::DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ArrayRender { s: self }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> liquid::model::DisplayCow<'_> {
|
||||||
|
DisplayCow::Owned(Box::new(ArraySource { s: self }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
"array"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_state(&self, state: liquid::model::State) -> bool {
|
||||||
|
match state {
|
||||||
|
State::Truthy => true,
|
||||||
|
State::DefaultValue | State::Empty | State::Blank => self.document.size() == 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
|
||||||
|
let s = ArrayRender { s: self }.to_string();
|
||||||
|
KStringCow::from_string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> LiquidValue {
|
||||||
|
LiquidValue::Array(self.values().map(|v| v.to_value()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_array(&self) -> Option<&dyn ArrayView> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_array(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: ObjectView> ValueView for OwnedFields<'a, D> {
|
||||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -182,3 +288,31 @@ impl<'a> ValueView for Fields<'a> {
|
|||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ArraySource<'a, 'map, D: ObjectView> {
|
||||||
|
s: &'a BorrowedFields<'a, 'map, D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'map, D: ObjectView> fmt::Display for ArraySource<'a, 'map, D> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "[")?;
|
||||||
|
for item in self.s.values() {
|
||||||
|
write!(f, "{}, ", item.render())?;
|
||||||
|
}
|
||||||
|
write!(f, "]")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArrayRender<'a, 'map, D: ObjectView> {
|
||||||
|
s: &'a BorrowedFields<'a, 'map, D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'map, D: ObjectView> fmt::Display for ArrayRender<'a, 'map, D> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for item in self.s.values() {
|
||||||
|
write!(f, "{}", item.render())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user