mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-11-22 12:46:53 +00:00
Compare commits
1145 Commits
lazy-word-
...
release-v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a94a13c9b0 | ||
|
|
9dcdde592c | ||
|
|
7de44ad2b7 | ||
|
|
820854ba5c | ||
|
|
496de5563a | ||
|
|
795045c03a | ||
|
|
b541b7bed3 | ||
|
|
6fb3cf95e4 | ||
|
|
cbd2bdf0fa | ||
|
|
601785692f | ||
|
|
65c212d1fd | ||
|
|
85feb3a26c | ||
|
|
d550b90c60 | ||
|
|
385acbbcd2 | ||
|
|
484dbf8c06 | ||
|
|
9c6c0af076 | ||
|
|
e33fbcf7b2 | ||
|
|
d352f33d16 | ||
|
|
3682b92ee8 | ||
|
|
ef10c1fb23 | ||
|
|
bd97a7cc19 | ||
|
|
56c7f54804 | ||
|
|
15d34c33e8 | ||
|
|
42ac869c5c | ||
|
|
6e0152921f | ||
|
|
069d25dce6 | ||
|
|
9929f798d3 | ||
|
|
80ff438402 | ||
|
|
e62a807b60 | ||
|
|
907055ed08 | ||
|
|
8b18adee95 | ||
|
|
53223ace47 | ||
|
|
a579ea2596 | ||
|
|
e13541818a | ||
|
|
c974f0ab0a | ||
|
|
36cac8acf7 | ||
|
|
5507a73b23 | ||
|
|
a608e57c3c | ||
|
|
398efa3c55 | ||
|
|
307ea38c2a | ||
|
|
cdeca59587 | ||
|
|
8529e2161a | ||
|
|
b80869f2be | ||
|
|
666ae1a3e7 | ||
|
|
f6559258ce | ||
|
|
b5ba0e42b3 | ||
|
|
b0479eb996 | ||
|
|
2bab375001 | ||
|
|
81020c7d6d | ||
|
|
4068c58417 | ||
|
|
a904ce109a | ||
|
|
ecea247e5d | ||
|
|
ae5bd9d0e3 | ||
|
|
ae2d0a67a4 | ||
|
|
0f1c78b185 | ||
|
|
300f5ce0f4 | ||
|
|
3240d89e81 | ||
|
|
63a649fd7d | ||
|
|
b9e014c044 | ||
|
|
9021cb4258 | ||
|
|
de52fe91f5 | ||
|
|
97ecbb53ff | ||
|
|
14b1a3300b | ||
|
|
f4903c2fe7 | ||
|
|
3d271c25c7 | ||
|
|
3c84010403 | ||
|
|
a69af611e3 | ||
|
|
1f126a2d8a | ||
|
|
c5b325de30 | ||
|
|
100a6f96e4 | ||
|
|
3c583ce7a4 | ||
|
|
0881810780 | ||
|
|
b7f32c5acd | ||
|
|
8f04529ba2 | ||
|
|
54b85b8644 | ||
|
|
562c620fec | ||
|
|
68280bad9e | ||
|
|
33bc86d71a | ||
|
|
3f20c1aa5d | ||
|
|
b265c92852 | ||
|
|
759beed560 | ||
|
|
5df125cbb7 | ||
|
|
74992560b0 | ||
|
|
2035f342f0 | ||
|
|
27fed758c2 | ||
|
|
3ead985caf | ||
|
|
e302e9edd3 | ||
|
|
1fdf820931 | ||
|
|
b4f2eeac0a | ||
|
|
7e3f2ab0c6 | ||
|
|
899be9c3ff | ||
|
|
444231e812 | ||
|
|
1ff6da63e8 | ||
|
|
b5158e1e83 | ||
|
|
3f1e172c6f | ||
|
|
2b5b41790e | ||
|
|
55cd3203fe | ||
|
|
c385cf985b | ||
|
|
45bb13bf43 | ||
|
|
095cba8fba | ||
|
|
2121819c66 | ||
|
|
2f33cd5f0a | ||
|
|
2f5101a1e4 | ||
|
|
3a9b08960a | ||
|
|
c4e7bf2e60 | ||
|
|
4f6a48c327 | ||
|
|
4c61a227ca | ||
|
|
3d2c204f2d | ||
|
|
8b27dec25c | ||
|
|
a9c924b433 | ||
|
|
6cb2296644 | ||
|
|
b2d157a74a | ||
|
|
386cf83285 | ||
|
|
8ef1a50086 | ||
|
|
84651ffd7d | ||
|
|
43c20bb3ed | ||
|
|
d340013d8b | ||
|
|
8a44d9faef | ||
|
|
afb367c7f4 | ||
|
|
84bcf9785f | ||
|
|
fc814b7537 | ||
|
|
0865d8af6c | ||
|
|
cac884401f | ||
|
|
7251cccd03 | ||
|
|
ddfcacbb62 | ||
|
|
3b26d64a5d | ||
|
|
3b0f576d56 | ||
|
|
454f8b36f4 | ||
|
|
1754745c42 | ||
|
|
6f30dfa41c | ||
|
|
33350248c8 | ||
|
|
69c59d3de3 | ||
|
|
8dfebbb3e7 | ||
|
|
737ad3ec19 | ||
|
|
4ec4710811 | ||
|
|
c5caac95dd | ||
|
|
7acbb1e140 | ||
|
|
a5e5afd123 | ||
|
|
c70e9abf70 | ||
|
|
f8d70249a7 | ||
|
|
a2c96d40d3 | ||
|
|
05dd8e0d62 | ||
|
|
4182e631d6 | ||
|
|
ddea0b1570 | ||
|
|
beb532e2a7 | ||
|
|
be045a7636 | ||
|
|
e3a6d63b52 | ||
|
|
6f8c414a75 | ||
|
|
2ec80a1ae2 | ||
|
|
ed147f80ac | ||
|
|
c37ed05f49 | ||
|
|
c1a5a545b6 | ||
|
|
35537e0b0b | ||
|
|
ee80fc87c9 | ||
|
|
bb43bf122e | ||
|
|
34590297c1 | ||
|
|
9e43f7b419 | ||
|
|
94733a4a18 | ||
|
|
ad68245186 | ||
|
|
29fb4d5e2a | ||
|
|
ca27bcaac7 | ||
|
|
53397e28fc | ||
|
|
7c2c17129f | ||
|
|
446fce6c16 | ||
|
|
a99538cd5f | ||
|
|
f67043801b | ||
|
|
941da56ee3 | ||
|
|
41262b008b | ||
|
|
fc4c5d2718 | ||
|
|
a75b327b37 | ||
|
|
c70ae91d34 | ||
|
|
e88480c7c4 | ||
|
|
b565ec1497 | ||
|
|
3e77c1d8c8 | ||
|
|
dc7af47371 | ||
|
|
064d9d5ff8 | ||
|
|
93f8b31eec | ||
|
|
466e1a7aac | ||
|
|
cc37eb870f | ||
|
|
5567653c96 | ||
|
|
5e867f7ce0 | ||
|
|
60acdf8574 | ||
|
|
93864009cc | ||
|
|
223df5a433 | ||
|
|
3580b3a4ef | ||
|
|
66b6e47494 | ||
|
|
6c3dd83ae5 | ||
|
|
48a5f4db2d | ||
|
|
224892e692 | ||
|
|
691a9ae4b1 | ||
|
|
e8a818f53d | ||
|
|
478f374b9d | ||
|
|
d4c88f28f3 | ||
|
|
d90c76d3cc | ||
|
|
f6bc6854f8 | ||
|
|
10567b150c | ||
|
|
a439f57d70 | ||
|
|
d243504296 | ||
|
|
a7fe2abca4 | ||
|
|
26da478b5b | ||
|
|
13d38d59bf | ||
|
|
4264abda23 | ||
|
|
dbb670a9ee | ||
|
|
a92e36ab83 | ||
|
|
ad06828685 | ||
|
|
8f1b697b91 | ||
|
|
bb4d573862 | ||
|
|
aa5a1f333a | ||
|
|
1f18f0ba77 | ||
|
|
44b24652d2 | ||
|
|
5dcf79233e | ||
|
|
42001a25ff | ||
|
|
846d27354b | ||
|
|
c1aa4120ac | ||
|
|
6394efc4c2 | ||
|
|
9716834380 | ||
|
|
2f2e42e72d | ||
|
|
776e55d209 | ||
|
|
3362fb8476 | ||
|
|
6d93b36279 | ||
|
|
982e989886 | ||
|
|
0014ed3114 | ||
|
|
080d5f94dd | ||
|
|
ba0f50e5ef | ||
|
|
ab07e9480e | ||
|
|
00e957051e | ||
|
|
ce6230aa85 | ||
|
|
6dc241f9de | ||
|
|
01d1ef65c4 | ||
|
|
3246667590 | ||
|
|
109395c199 | ||
|
|
a0b71a8785 | ||
|
|
00a5c86f13 | ||
|
|
366c37a686 | ||
|
|
afc164a271 | ||
|
|
bdc2d1e64d | ||
|
|
f3b60a1dab | ||
|
|
cd0523c3f1 | ||
|
|
7f318ee964 | ||
|
|
dc1656da8e | ||
|
|
dc0bd9f25d | ||
|
|
52d8007b12 | ||
|
|
4f8382b159 | ||
|
|
c2c82be556 | ||
|
|
0312fb22b8 | ||
|
|
b85657de1e | ||
|
|
626be0ef28 | ||
|
|
1b476b8a35 | ||
|
|
a1b42c10e2 | ||
|
|
d67db6e3c2 | ||
|
|
760ccffdbd | ||
|
|
338806283b | ||
|
|
fe15e11c9d | ||
|
|
f1d92bfead | ||
|
|
a005a062da | ||
|
|
fd8b2451d7 | ||
|
|
058f9ffda5 | ||
|
|
5d363205a5 | ||
|
|
a683faa882 | ||
|
|
421a23ee3d | ||
|
|
191ea340ed | ||
|
|
8887cbdcd5 | ||
|
|
8d22972d84 | ||
|
|
634865ff53 | ||
|
|
36fccf8525 | ||
|
|
d6bd60d569 | ||
|
|
48ad959fc1 | ||
|
|
1bc30cb4c8 | ||
|
|
77138a42d6 | ||
|
|
0791506124 | ||
|
|
2a015ac3b8 | ||
|
|
8772b5af87 | ||
|
|
6f248b78a9 | ||
|
|
d694e312ff | ||
|
|
d76dcc8998 | ||
|
|
e654f66223 | ||
|
|
34f2ab7093 | ||
|
|
1a9dbd364e | ||
|
|
662c5d9871 | ||
|
|
df2e7cde53 | ||
|
|
02b2ae6142 | ||
|
|
f813eb7ca4 | ||
|
|
d072edaa49 | ||
|
|
5cd61b50f9 | ||
|
|
9a9be76757 | ||
|
|
cfa6ba6c3b | ||
|
|
f4f333dbf6 | ||
|
|
1ade76ba10 | ||
|
|
ae26658913 | ||
|
|
aa09edb3fb | ||
|
|
3f42f1a036 | ||
|
|
9bdfdd395b | ||
|
|
78d0625a91 | ||
|
|
e3daa907c5 | ||
|
|
a39223822a | ||
|
|
1eb6cd38ce | ||
|
|
eb6ad3ef9c | ||
|
|
3bef4f4413 | ||
|
|
9f89881b0d | ||
|
|
3f655ea20e | ||
|
|
50bc1d55f3 | ||
|
|
f244439b4f | ||
|
|
30fd546c12 | ||
|
|
126aefc207 | ||
|
|
e7a60555d6 | ||
|
|
ae912c4c3f | ||
|
|
13ea29e511 | ||
|
|
5342df26fe | ||
|
|
a930977460 | ||
|
|
a3b8c2b71f | ||
|
|
39f808714d | ||
|
|
61bc95e8d6 | ||
|
|
8adf6141e0 | ||
|
|
df3f282e4d | ||
|
|
d81855015b | ||
|
|
feb53104e5 | ||
|
|
881c37393f | ||
|
|
9e98a25e45 | ||
|
|
0a4f2ef891 | ||
|
|
faa1f7c5b7 | ||
|
|
3cc5d86598 | ||
|
|
1ae47bec77 | ||
|
|
2f1be0ff86 | ||
|
|
9cee432255 | ||
|
|
ff8d48d2f1 | ||
|
|
a56c036994 | ||
|
|
fb73b83abe | ||
|
|
29b74424ad | ||
|
|
b4cafec8b3 | ||
|
|
d43cd40807 | ||
|
|
0301d8f239 | ||
|
|
074744b8a6 | ||
|
|
511c48f520 | ||
|
|
4623691d1f | ||
|
|
2d45124d9b | ||
|
|
40e7284d70 | ||
|
|
4d8d34cc93 | ||
|
|
5cced0af02 | ||
|
|
9c60e9689f | ||
|
|
3261aadcf2 | ||
|
|
073e9f2967 | ||
|
|
5f8f48ec95 | ||
|
|
ed2fe365a0 | ||
|
|
f7c8a77f89 | ||
|
|
2052537681 | ||
|
|
a9bb64c55a | ||
|
|
a8030850ee | ||
|
|
132065afda | ||
|
|
51c298662b | ||
|
|
70a860a0f0 | ||
|
|
a3254d7d7d | ||
|
|
73c9c1ebdc | ||
|
|
4c7a6e5c1b | ||
|
|
ef4c87accf | ||
|
|
ced7ea4a5c | ||
|
|
fa3990daf9 | ||
|
|
c5993196b3 | ||
|
|
16234e1313 | ||
|
|
be9f4f96df | ||
|
|
b274106ad3 | ||
|
|
48527761e7 | ||
|
|
6792d048b8 | ||
|
|
07bfed99e6 | ||
|
|
8dfded2993 | ||
|
|
3714f16696 | ||
|
|
d0cd3cacec | ||
|
|
fef089c7b6 | ||
|
|
d47e1e15de | ||
|
|
caccb51814 | ||
|
|
a76a3e8f11 | ||
|
|
32dede35c7 | ||
|
|
6397ef12a0 | ||
|
|
cf9b311f71 | ||
|
|
7423243be0 | ||
|
|
b5e41f0e46 | ||
|
|
5690700601 | ||
|
|
2faad504c6 | ||
|
|
2bcd69750f | ||
|
|
9f0d33ec99 | ||
|
|
de24e75be8 | ||
|
|
a3af9fe057 | ||
|
|
90683d0e4e | ||
|
|
5c79273748 | ||
|
|
90e6b6416f | ||
|
|
2b75072b09 | ||
|
|
6e6fd077d4 | ||
|
|
b45eea0d3e | ||
|
|
a051ab3d9a | ||
|
|
0b89ef1fd7 | ||
|
|
65ba7b47af | ||
|
|
8af76a65bf | ||
|
|
6b94033c97 | ||
|
|
dfe0c8664e | ||
|
|
0ca652de28 | ||
|
|
87f105747f | ||
|
|
735634e998 | ||
|
|
3740755d9c | ||
|
|
bbcabc47bd | ||
|
|
a06cb1bfd6 | ||
|
|
549dc985b8 | ||
|
|
428463e45c | ||
|
|
7113fcf63a | ||
|
|
aa6855cd4f | ||
|
|
895db76a51 | ||
|
|
45da2257ec | ||
|
|
a88146d59e | ||
|
|
91e77abf4f | ||
|
|
f60814b319 | ||
|
|
5a675bcb82 | ||
|
|
82a796aea7 | ||
|
|
f6287602e9 | ||
|
|
ede456c5b0 | ||
|
|
3f5b5df139 | ||
|
|
d72e5f5f69 | ||
|
|
aa366d593d | ||
|
|
205430854d | ||
|
|
be64006211 | ||
|
|
eda309d562 | ||
|
|
119d618a76 | ||
|
|
2b2e6c0b3a | ||
|
|
e6329e77e1 | ||
|
|
b086c51a23 | ||
|
|
9ce5598fef | ||
|
|
e30c24b5bf | ||
|
|
c1a132fa06 | ||
|
|
e54fc59248 | ||
|
|
11e7c0d75f | ||
|
|
c593fbe648 | ||
|
|
2b3327ea74 | ||
|
|
d14184f4da | ||
|
|
46bceb91f1 | ||
|
|
cab5e35ff7 | ||
|
|
f8232976ed | ||
|
|
22d363c05a | ||
|
|
41620d5325 | ||
|
|
f3d5c74c02 | ||
|
|
d48baece51 | ||
|
|
c45ede44a8 | ||
|
|
4235a82dcf | ||
|
|
e7b9b8f002 | ||
|
|
5716ab70f3 | ||
|
|
422a786ffd | ||
|
|
836ae19bec | ||
|
|
0b5bc41b79 | ||
|
|
b45059e8f2 | ||
|
|
c16c60b599 | ||
|
|
0114796d2a | ||
|
|
17a94c40dc | ||
|
|
76ca44b214 | ||
|
|
d2e4d6dd8a | ||
|
|
879cf85037 | ||
|
|
c2d5b20a42 | ||
|
|
600178c5ab | ||
|
|
b93ca3945e | ||
|
|
8fef48f8ca | ||
|
|
dedae94102 | ||
|
|
7ae9a4afee | ||
|
|
d2776efb11 | ||
|
|
9211e94c4f | ||
|
|
b7bebe9bbb | ||
|
|
37a692f942 | ||
|
|
25c19a306b | ||
|
|
c078efd730 | ||
|
|
9dac91efe0 | ||
|
|
074d509d92 | ||
|
|
d439a3cb9d | ||
|
|
e92b6beb20 | ||
|
|
27cc357362 | ||
|
|
73dfeefc7c | ||
|
|
d85480de89 | ||
|
|
9f55708d84 | ||
|
|
280c3907be | ||
|
|
8419fd9b3b | ||
|
|
283944ea89 | ||
|
|
8aacd6374a | ||
|
|
8326f34ad1 | ||
|
|
259fc067d3 | ||
|
|
e8b2bb3ea6 | ||
|
|
7dfb2071b5 | ||
|
|
9cfbef478e | ||
|
|
efd5fd96cc | ||
|
|
f4a908669c | ||
|
|
eb2c2815b6 | ||
|
|
0ef52941c7 | ||
|
|
0d85f8fcee | ||
|
|
f4bb6cbca8 | ||
|
|
ad03c86c44 | ||
|
|
85037352b9 | ||
|
|
29e9c74a49 | ||
|
|
1b54c866e1 | ||
|
|
e414284335 | ||
|
|
7a204609fe | ||
|
|
f6803dd7d1 | ||
|
|
f86f4f619f | ||
|
|
e35d58b531 | ||
|
|
63827bbee0 | ||
|
|
6b2b8ed676 | ||
|
|
6db5939f84 | ||
|
|
d35b2d8d33 | ||
|
|
0687cf058a | ||
|
|
340d9e6edc | ||
|
|
7219299436 | ||
|
|
657bbf5d1e | ||
|
|
28adbc0d18 | ||
|
|
e3fba62e13 | ||
|
|
fb9170b8e3 | ||
|
|
c15763f910 | ||
|
|
7fa1c41190 | ||
|
|
77802dabf6 | ||
|
|
a685eeafeb | ||
|
|
f16e6f7c37 | ||
|
|
900be0ccad | ||
|
|
51a087b764 | ||
|
|
31142b3663 | ||
|
|
e60b855a54 | ||
|
|
510a4b91be | ||
|
|
e704f4d1ec | ||
|
|
82fe80b360 | ||
|
|
0f1dd3614c | ||
|
|
3aa6c3c750 | ||
|
|
b956918c11 | ||
|
|
e3003c1609 | ||
|
|
bf13268649 | ||
|
|
0bb7866f1e | ||
|
|
e6e9a033aa | ||
|
|
63031219c5 | ||
|
|
44d6430bae | ||
|
|
4d26e9c6f2 | ||
|
|
2ff382c023 | ||
|
|
0f6dd133b2 | ||
|
|
29f6eeff8f | ||
|
|
ef007d547d | ||
|
|
3fc16c627d | ||
|
|
9422b6d654 | ||
|
|
ddba52414a | ||
|
|
4534dc2cab | ||
|
|
b05cb80803 | ||
|
|
6e0526090a | ||
|
|
a743da3061 | ||
|
|
c6216517c7 | ||
|
|
2d4f7c635e | ||
|
|
ee812b31c4 | ||
|
|
3329248a84 | ||
|
|
bc08cd0deb | ||
|
|
3e2f468213 | ||
|
|
7c448bcc00 | ||
|
|
acb7c0a449 | ||
|
|
e8795d2608 | ||
|
|
e023ee4b6b | ||
|
|
e74c3b692a | ||
|
|
1d3b18f774 | ||
|
|
00bc86e74b | ||
|
|
adc9976615 | ||
|
|
2090e9ea31 | ||
|
|
1c8f1c18f4 | ||
|
|
ae8c1461e1 | ||
|
|
5f62274f21 | ||
|
|
c4a96b40eb | ||
|
|
5f50fc9464 | ||
|
|
89498a2bea | ||
|
|
211c1b753f | ||
|
|
d08e89ea3d | ||
|
|
695877043a | ||
|
|
bc4d1530ee | ||
|
|
d7721fe607 | ||
|
|
4a179fb3c0 | ||
|
|
59a1c5d9a7 | ||
|
|
2f82d94502 | ||
|
|
bd2bd0f33b | ||
|
|
e02733df4a | ||
|
|
f373ecc96a | ||
|
|
748a327271 | ||
|
|
4925b30196 | ||
|
|
43c4a229b7 | ||
|
|
ca112a8b95 | ||
|
|
855fa555a3 | ||
|
|
a237c0797a | ||
|
|
5c46dc702a | ||
|
|
4cadc8113b | ||
|
|
2d6dc83940 | ||
|
|
ab768f379f | ||
|
|
705e9a9e5e | ||
|
|
c17031d3de | ||
|
|
67f2a30d7c | ||
|
|
99732f4084 | ||
|
|
5081d837ea | ||
|
|
9e1cb792f4 | ||
|
|
b6b7ede266 | ||
|
|
f50e586a4f | ||
|
|
11fedea788 | ||
|
|
032b34c377 | ||
|
|
b421c8e7de | ||
|
|
00eb258a53 | ||
|
|
fc6cc80705 | ||
|
|
138d20b277 | ||
|
|
7c1a9113f9 | ||
|
|
07ae297ffd | ||
|
|
4069dbcfca | ||
|
|
03eb50fbac | ||
|
|
2616d776f2 | ||
|
|
3004db95af | ||
|
|
9a729bf31d | ||
|
|
8bfa6a7f54 | ||
|
|
056f18bd02 | ||
|
|
fe9866aca8 | ||
|
|
60f105a4a3 | ||
|
|
abb399b802 | ||
|
|
aeaac7270e | ||
|
|
f45770a3ce | ||
|
|
0e10ff1aa3 | ||
|
|
6ee608c2d1 | ||
|
|
95e8a9bef1 | ||
|
|
0598320252 | ||
|
|
2269104337 | ||
|
|
6b4d69996c | ||
|
|
df4e3c2e43 | ||
|
|
e2b549c5ee | ||
|
|
8390006ebf | ||
|
|
7200437246 | ||
|
|
68e7bfb37f | ||
|
|
209c4bfc18 | ||
|
|
396d76046d | ||
|
|
9ae73e3c05 | ||
|
|
933e319364 | ||
|
|
596617dd31 | ||
|
|
f3dd6834c6 | ||
|
|
e8774ad079 | ||
|
|
5d191c479e | ||
|
|
c3368e6859 | ||
|
|
40776ed4cd | ||
|
|
9bda9a9a64 | ||
|
|
aefebdeb8b | ||
|
|
646e44ddf9 | ||
|
|
9275ce1503 | ||
|
|
48d2d3a5cd | ||
|
|
7ec0c9aa83 | ||
|
|
484fdd9ce2 | ||
|
|
7533a11143 | ||
|
|
19d077a4b1 | ||
|
|
b8845d1015 | ||
|
|
620867d611 | ||
|
|
77cc3678b5 | ||
|
|
a73d3c03e9 | ||
|
|
824f5b12ce | ||
|
|
bb4baf7fae | ||
|
|
0263eb0aec | ||
|
|
8a916a4e42 | ||
|
|
506ee40dc5 | ||
|
|
952fabf8a0 | ||
|
|
7ea2e4ec7b | ||
|
|
a0a4ac66ec | ||
|
|
b037e416d3 | ||
|
|
e9d547556d | ||
|
|
ab0eba2f72 | ||
|
|
5ceb3c6a10 | ||
|
|
34d572e3e5 | ||
|
|
28e6adc435 | ||
|
|
6a683975bf | ||
|
|
c60d11fb42 | ||
|
|
32207f9f19 | ||
|
|
7c1b15fd06 | ||
|
|
4352a924d7 | ||
|
|
bbe802c656 | ||
|
|
b32e30ad27 | ||
|
|
ae115cee78 | ||
|
|
1824fbd1b5 | ||
|
|
34d8a54c4b | ||
|
|
9e31d6ceff | ||
|
|
139ec8c782 | ||
|
|
2691999bd3 | ||
|
|
48460678df | ||
|
|
cb15e5c67e | ||
|
|
7380808b26 | ||
|
|
8fa6e8670a | ||
|
|
c640856cc1 | ||
|
|
1a1317ab0f | ||
|
|
9cab754942 | ||
|
|
4a0ec15ad2 | ||
|
|
985b892b7a | ||
|
|
605dea4f85 | ||
|
|
95d4775d4a | ||
|
|
416fcf47f1 | ||
|
|
6433e49882 | ||
|
|
85939ae8ad | ||
|
|
e654eddf56 | ||
|
|
170ad87e44 | ||
|
|
bc56087a17 | ||
|
|
29d82ade56 | ||
|
|
a7f5d3bb7a | ||
|
|
48e8356a16 | ||
|
|
1fda05c2fd | ||
|
|
8f96724adf | ||
|
|
01e5b0effa | ||
|
|
2ec9664878 | ||
|
|
7f5a0c0013 | ||
|
|
f5c3dad3ed | ||
|
|
10028515ac | ||
|
|
63ccd19ab1 | ||
|
|
1b4d344e18 | ||
|
|
89c0cf9b12 | ||
|
|
3770e70581 | ||
|
|
e497008161 | ||
|
|
a15ebb283f | ||
|
|
3f256a7959 | ||
|
|
b41af0d0f6 | ||
|
|
3ebff65ef3 | ||
|
|
717a026fdd | ||
|
|
70670c3be4 | ||
|
|
62e2a5a324 | ||
|
|
90d96ee415 | ||
|
|
38b317857d | ||
|
|
765e76857f | ||
|
|
204cf423b2 | ||
|
|
e575b5af74 | ||
|
|
4fc24cb691 | ||
|
|
8bc8484e95 | ||
|
|
7b49c30d8c | ||
|
|
239851046d | ||
|
|
60796dfb14 | ||
|
|
c7cb72a77a | ||
|
|
4d819ea636 | ||
|
|
4dfb89168b | ||
|
|
258e6a115b | ||
|
|
666680bd87 | ||
|
|
27527849bb | ||
|
|
cf2bc03bed | ||
|
|
1d02efeab9 | ||
|
|
53fc98d3b0 | ||
|
|
263300b3a3 | ||
|
|
ab3d92d163 | ||
|
|
ef9fc6c854 | ||
|
|
61b0f50d4d | ||
|
|
0557a4dd2f | ||
|
|
930d5a09a8 | ||
|
|
8b0c4291ae | ||
|
|
c9efdf8c88 | ||
|
|
72736c0ea9 | ||
|
|
92d0d36ff6 | ||
|
|
352ac759b5 | ||
|
|
28dc7b836b | ||
|
|
c4e1407e77 | ||
|
|
49317bbee4 | ||
|
|
82313a4444 | ||
|
|
8fdcdee0cc | ||
|
|
3c218cc3a0 | ||
|
|
7d574433b6 | ||
|
|
201a808fe2 | ||
|
|
f827c2442c | ||
|
|
87d2e213f3 | ||
|
|
3b931e75d9 | ||
|
|
ae135d1d46 | ||
|
|
0efb72fe66 | ||
|
|
bed442528f | ||
|
|
496685fa26 | ||
|
|
02cbcea3db | ||
|
|
0f7f5fa104 | ||
|
|
50fafbbc8b | ||
|
|
2821163b95 | ||
|
|
2da64e835e | ||
|
|
420c6e1932 | ||
|
|
2a067d3327 | ||
|
|
564cad1163 | ||
|
|
33dfd422db | ||
|
|
036a9d5dbc | ||
|
|
7b74810b03 | ||
|
|
3e53527bff | ||
|
|
7929872091 | ||
|
|
afb43d266e | ||
|
|
05828ff2c7 | ||
|
|
75c3f33478 | ||
|
|
c6930c8819 | ||
|
|
439146289e | ||
|
|
6bf214bb14 | ||
|
|
fcf694026d | ||
|
|
0b675bd530 | ||
|
|
7636365a65 | ||
|
|
46680585ae | ||
|
|
bcec8d8984 | ||
|
|
56c1bd3afe | ||
|
|
1a84f00fbf | ||
|
|
39320a6fce | ||
|
|
1d2dbcb51f | ||
|
|
341183cd57 | ||
|
|
b9716ec346 | ||
|
|
564f85280c | ||
|
|
7fa74b4931 | ||
|
|
7d8415448c | ||
|
|
c7839b5a84 | ||
|
|
a52b513023 | ||
|
|
77e03e3f8c | ||
|
|
148816a3da | ||
|
|
511eef87bf | ||
|
|
aef8448fc6 | ||
|
|
5fab2aee51 | ||
|
|
1235523918 | ||
|
|
d4a16f2349 | ||
|
|
0f05c0eb6f | ||
|
|
2cd85c732a | ||
|
|
82fa70da83 | ||
|
|
951be67060 | ||
|
|
5400f3941a | ||
|
|
af54c8381e | ||
|
|
693fcd5752 | ||
|
|
733175359a | ||
|
|
7c6162f0bf | ||
|
|
d6ae39bf0f | ||
|
|
e416bbc1de | ||
|
|
5d0d12dfbd | ||
|
|
2cfd363dc6 | ||
|
|
70aa78a2c2 | ||
|
|
96c81762ed | ||
|
|
0b1f634afa | ||
|
|
d3d5015854 | ||
|
|
f95f29c492 | ||
|
|
a50b69b868 | ||
|
|
3668f5f021 | ||
|
|
54fdf379bb | ||
|
|
41b1cd5a73 | ||
|
|
5c14a25d5a | ||
|
|
fda2843135 | ||
|
|
9347330f3a | ||
|
|
56c9190dab | ||
|
|
6b986dceaf | ||
|
|
cb7bb36080 | ||
|
|
161cb736ea | ||
|
|
ea6bb4df1d | ||
|
|
a3d2f64725 | ||
|
|
d5526cffff | ||
|
|
5cb75d1f2a | ||
|
|
921e3c4ffe | ||
|
|
52591761af | ||
|
|
f80182f0a9 | ||
|
|
3b30b6a57a | ||
|
|
5efc78db55 | ||
|
|
cffbe3fcb6 | ||
|
|
8d8fcb9846 | ||
|
|
20049669c9 | ||
|
|
db28d13cb1 | ||
|
|
5a7cfc57fd | ||
|
|
790621dc29 | ||
|
|
1d577ae98b | ||
|
|
88e9a55d44 | ||
|
|
dbe551cf99 | ||
|
|
a299fbd33b | ||
|
|
193119acb9 | ||
|
|
4c71118699 | ||
|
|
5fe2943d3c | ||
|
|
86ff502327 | ||
|
|
6b1a345dce | ||
|
|
b54ece690b | ||
|
|
3ea167bade | ||
|
|
1158d6689f | ||
|
|
d9b0463a0b | ||
|
|
ae9899f179 | ||
|
|
308fd7128e | ||
|
|
27e7c00622 | ||
|
|
58207da934 | ||
|
|
fb8b832192 | ||
|
|
17207b5405 | ||
|
|
bd95503eba | ||
|
|
8b8b0d802c | ||
|
|
d329e86250 | ||
|
|
d416b3b390 | ||
|
|
54f5e74744 | ||
|
|
fd4b192a39 | ||
|
|
3c13feebf7 | ||
|
|
1811168b96 | ||
|
|
b06cc1e0a2 | ||
|
|
44f812c36d | ||
|
|
c8e77b5f25 | ||
|
|
283f516e15 | ||
|
|
b4ca0a8c98 | ||
|
|
b658e38acd | ||
|
|
f87e46cc16 | ||
|
|
65354b414a | ||
|
|
025df397c0 | ||
|
|
f77abc9dc8 | ||
|
|
7e9909ee45 | ||
|
|
43ec97fe45 | ||
|
|
02929e241b | ||
|
|
c13efde042 | ||
|
|
36f0a1492c | ||
|
|
ce65ad213b | ||
|
|
3e0de6cb83 | ||
|
|
f3d691667d | ||
|
|
ce9c930d10 | ||
|
|
fc88b003b4 | ||
|
|
cf5d26124a | ||
|
|
38b1c57fa8 | ||
|
|
25c525b057 | ||
|
|
83cd28b60b | ||
|
|
48cad4132a | ||
|
|
4897ad99d0 | ||
|
|
5b67de0367 | ||
|
|
46ff78b4ec | ||
|
|
5810fb239f | ||
|
|
b007ed6be9 | ||
|
|
9ad43b6841 | ||
|
|
c9ec502ed9 | ||
|
|
18aed75d3b | ||
|
|
6738a4f6ee | ||
|
|
a1ff41cabb | ||
|
|
d2948adea3 | ||
|
|
f54b57e5be | ||
|
|
95821d0bde | ||
|
|
f690fa0686 | ||
|
|
24e94b28c1 | ||
|
|
34d58f35c8 | ||
|
|
1d5265caf4 | ||
|
|
97aeb6db4d | ||
|
|
ff64c64abe | ||
|
|
ee326a1ecc | ||
|
|
c204a7bb12 | ||
|
|
cf4798bd2b | ||
|
|
4d761d3444 | ||
|
|
c9b78970c9 | ||
|
|
ae3c4e27c4 | ||
|
|
1b718afd11 | ||
|
|
01ef055f40 | ||
|
|
f888f87635 | ||
|
|
293a425183 | ||
|
|
699ec18de8 | ||
|
|
73e4206b3c | ||
|
|
a964251cee | ||
|
|
8c8d98eeaa | ||
|
|
c5ae43cac6 | ||
|
|
57eecd6197 | ||
|
|
2fe5c78cb6 | ||
|
|
8068337b07 | ||
|
|
8047cfe438 | ||
|
|
f26826f115 | ||
|
|
5717e5c1af | ||
|
|
bb07038c31 | ||
|
|
d1a088ea0b | ||
|
|
b68e22c0e6 | ||
|
|
03a36f116e | ||
|
|
8a0bf24ed5 | ||
|
|
e2763471e5 | ||
|
|
b2f2c5d69f | ||
|
|
e547bfb428 | ||
|
|
1594c54e23 | ||
|
|
768cfb6c2d | ||
|
|
13b607bd68 | ||
|
|
3d130d31c8 | ||
|
|
4cda584b0c | ||
|
|
248c90bad5 | ||
|
|
0e9040e605 | ||
|
|
3e3c00f44c | ||
|
|
d986a3bbaf | ||
|
|
c2ceb8e41b | ||
|
|
a25eb9c136 | ||
|
|
cc2011a27f | ||
|
|
604e156c2b | ||
|
|
1d6777ee68 | ||
|
|
79db2e67fb | ||
|
|
0940f0e4f4 | ||
|
|
d40290aaaf | ||
|
|
865f24cfef | ||
|
|
fd2de7c668 | ||
|
|
448564b674 | ||
|
|
c5dd8e7d6f | ||
|
|
c9b4c1fb81 | ||
|
|
0f10ec96af | ||
|
|
8608d10fa2 | ||
|
|
83e71cd7b9 | ||
|
|
3fbe1df770 | ||
|
|
150d1db86b | ||
|
|
806e983aa5 | ||
|
|
e96c1d4b0f | ||
|
|
15cdc6924b | ||
|
|
677e8b122c | ||
|
|
75a7e40a27 | ||
|
|
d9a527854a | ||
|
|
e4f05326be | ||
|
|
d99419acfb | ||
|
|
f349630e78 | ||
|
|
c8939944c6 | ||
|
|
4e6252fb03 | ||
|
|
2d1412afce | ||
|
|
0f4536df2d | ||
|
|
3531efb169 | ||
|
|
8bd8e744f3 | ||
|
|
6ec430b633 | ||
|
|
4041978402 | ||
|
|
53f32a7dd7 | ||
|
|
47a7ed93d3 | ||
|
|
71ab11f1fe | ||
|
|
436776cdbf | ||
|
|
96bc519f9e | ||
|
|
2ac826edca | ||
|
|
8b23eddc10 | ||
|
|
185f2b8f74 | ||
|
|
c0e987979a | ||
|
|
89aff2081c | ||
|
|
032c67662d | ||
|
|
03f59786c2 | ||
|
|
f7c1f19dd8 | ||
|
|
1542ff30ae | ||
|
|
20d0aa499a | ||
|
|
0cb2bf34a5 | ||
|
|
de03b7e437 | ||
|
|
a315726f96 | ||
|
|
91d2a07499 | ||
|
|
3b773b3416 | ||
|
|
648b2876f6 | ||
|
|
c5360bcdbf | ||
|
|
1bdc08a73a | ||
|
|
63b5e21ae1 | ||
|
|
eb0b5239cb | ||
|
|
121c1ac1dd | ||
|
|
b82dda2d0d | ||
|
|
ea9330e9c9 | ||
|
|
b6a9d8d2ac | ||
|
|
a03eef6511 | ||
|
|
42fae9994d | ||
|
|
e1aa534389 | ||
|
|
49add50cb3 | ||
|
|
29b947ee43 | ||
|
|
3f683c4238 | ||
|
|
294ccb6f44 | ||
|
|
63a4dfa2a8 | ||
|
|
3b8965bc76 | ||
|
|
9fd9fcb03e | ||
|
|
30805bbed5 | ||
|
|
2984be880f | ||
|
|
fd0623c085 | ||
|
|
eeb33b913c | ||
|
|
3d93efc6aa | ||
|
|
425ef1b205 | ||
|
|
f607449cb7 | ||
|
|
e9b4794f2b | ||
|
|
c413855156 | ||
|
|
7cdb4aa473 | ||
|
|
bfe4968d7e | ||
|
|
7372083a5a | ||
|
|
8cecc6989a | ||
|
|
1f1edd6e25 | ||
|
|
bc5efa9a76 | ||
|
|
3ec5b9d488 | ||
|
|
b61eb19601 | ||
|
|
231a027c7d | ||
|
|
f8ff91ed30 | ||
|
|
b73660fa8e | ||
|
|
55adbac2dd | ||
|
|
fd7fbfa9eb | ||
|
|
3a93f88ba6 | ||
|
|
7c1c4f9c26 | ||
|
|
1f5412003d | ||
|
|
5da92a3d53 | ||
|
|
c4a8b84dc0 | ||
|
|
ffe3faeca7 | ||
|
|
0f07cfed14 | ||
|
|
326a728434 | ||
|
|
e4733dcd42 | ||
|
|
a500fa053c | ||
|
|
61db56f785 | ||
|
|
235556d699 | ||
|
|
a3a1065c16 | ||
|
|
b025f1bcf1 | ||
|
|
707d106a24 | ||
|
|
97d6726291 | ||
|
|
82fa571ef7 | ||
|
|
5d453e6049 | ||
|
|
9e7d7beb4a | ||
|
|
a225ab2637 | ||
|
|
94b43001db | ||
|
|
796a325972 | ||
|
|
1db550ec7f | ||
|
|
c3c5a928e4 | ||
|
|
c4787760d3 | ||
|
|
7ca2a8eb6f | ||
|
|
c1c065079f | ||
|
|
1cca4abf5a | ||
|
|
bd172bf68a | ||
|
|
70ed6ba798 | ||
|
|
f3ab940776 | ||
|
|
87547550f5 | ||
|
|
e067d796b3 | ||
|
|
c2ff4dd3b2 | ||
|
|
31bda976f2 | ||
|
|
fce0fa9c57 | ||
|
|
a10efedd2f | ||
|
|
55ec96d31a | ||
|
|
4249630791 | ||
|
|
19f4c1ac98 | ||
|
|
a0bfcf8872 | ||
|
|
64477aac60 | ||
|
|
4d90e3d2ec | ||
|
|
4ab547c6fa | ||
|
|
249da5846c | ||
|
|
ee15d4fe77 | ||
|
|
f0f6c3000f | ||
|
|
85efa6f493 | ||
|
|
ba6d755120 | ||
|
|
5607802fe1 | ||
|
|
a8afd5dbcb | ||
|
|
55f620a986 | ||
|
|
be6abb952d | ||
|
|
2f07afa97e | ||
|
|
bf3a29b60d | ||
|
|
3acf036526 | ||
|
|
eefefc482b | ||
|
|
43c8a206b4 | ||
|
|
a8c407fa36 | ||
|
|
18bc56f1fa | ||
|
|
38b3e03dde | ||
|
|
6b1c262b74 | ||
|
|
0f654e45c9 | ||
|
|
d71c6f3483 | ||
|
|
8b4166410c | ||
|
|
9d3037aa1a | ||
|
|
5414887bff | ||
|
|
03a0550b63 | ||
|
|
2800e42243 | ||
|
|
5759afac41 | ||
|
|
868c902935 | ||
|
|
e019ad7692 | ||
|
|
1f67f373d1 | ||
|
|
2c0bd35923 | ||
|
|
b3aaa64de5 | ||
|
|
7b3072ad28 | ||
|
|
db26c1e5bf | ||
|
|
9aee12c906 | ||
|
|
debd2b21b8 | ||
|
|
39aca661dd | ||
|
|
5b51e8a083 | ||
|
|
3928fb36b3 | ||
|
|
2ddc1d2258 | ||
|
|
7c267a8a0e | ||
|
|
d39d915a7e | ||
|
|
3160ddf9df | ||
|
|
d286e63f15 | ||
|
|
9ee6254eec | ||
|
|
e2c824a7cd | ||
|
|
0dd65caffe | ||
|
|
4397b7d170 | ||
|
|
15db203b7d | ||
|
|
041f635214 | ||
|
|
537bf27e7c | ||
|
|
cf31a65a88 | ||
|
|
0f7d71041f | ||
|
|
91d221ebe7 | ||
|
|
9162e8ba04 | ||
|
|
2118cc092e | ||
|
|
c7564d500f |
@@ -1,27 +1,29 @@
|
|||||||
---
|
---
|
||||||
name: New sprint issue
|
name: New feature issue
|
||||||
about: ⚠️ Should only be used by the engine team ⚠️
|
about: ⚠️ Should only be used by the internal Meili team ⚠️
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'missing usage in PRD, impacts docs'
|
labels: 'impacts docs, impacts integrations'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Related product team resources: [PRD]() (_internal only_)
|
Related product team resources: [PRD]() (_internal only_)
|
||||||
Related product discussion:
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
<!---Copy/paste the information in PRD or briefly detail the product motivation. Ask product team if any hesitation.-->
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
<!---Link to the public part of the PRD, or to the related product discussion for experimental features-->
|
<!---Link to the public part of the PRD, or to the related product discussion for experimental features-->
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
<!---If necessary, create a list with technical/product steps-->
|
<!---If necessary, create a list with technical/product steps-->
|
||||||
|
|
||||||
|
### Are you modifying a database?
|
||||||
|
|
||||||
|
- [ ] If not, add the `no db change` label to your PR, and you're good to merge.
|
||||||
|
- [ ] If yes, add the `db change` label to your PR. You'll receive a message explaining you what to do.
|
||||||
|
|
||||||
### Reminders when modifying the API
|
### Reminders when modifying the API
|
||||||
|
|
||||||
- [ ] Update the openAPI file with utoipa:
|
- [ ] Update the openAPI file with utoipa:
|
||||||
@@ -50,5 +52,5 @@ Related product discussion:
|
|||||||
|
|
||||||
## Impacted teams
|
## Impacted teams
|
||||||
|
|
||||||
<!---Ping the related teams. Ask for the engine manager if any hesitation-->
|
<!---Ping the related teams. Ask on Slack if any hesitation-->
|
||||||
<!---@meilisearch/docs-team when there is any API change, e.g. settings addition-->
|
<!---@meilisearch/docs-team and @meilisearch/integration-team when there is any API change, e.g. settings addition-->
|
||||||
16
.github/pull_request_template.md
vendored
Normal file
16
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
## Related issue
|
||||||
|
|
||||||
|
Fixes #...
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
⚠️ Ensure the following requirements before merging ⚠️
|
||||||
|
- [ ] Automated tests have been added.
|
||||||
|
- [ ] If some tests cannot be automated, manual rigorous tests should be applied.
|
||||||
|
- [ ] ⚠️ If there is any change in the DB:
|
||||||
|
- [ ] Test that any impacted DB still works as expected after using `--experimental-dumpless-upgrade` on a DB created with the last released Meilisearch
|
||||||
|
- [ ] Test that during the upgrade, **search is still available** (artificially make the upgrade longer if needed)
|
||||||
|
- [ ] Set the `db change` label.
|
||||||
|
- [ ] If necessary, the feature have been tested in the Cloud production environment (with [prototypes](./documentation/prototypes.md)) and the Cloud UI is ready.
|
||||||
|
- [ ] If necessary, the [documentation](https://github.com/meilisearch/documentation) related to the implemented feature in the PR is ready.
|
||||||
|
- [ ] If necessary, the [integrations](https://github.com/meilisearch/integration-guides) related to the implemented feature in the PR are ready.
|
||||||
33
.github/release-draft-template.yml
vendored
Normal file
33
.github/release-draft-template.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name-template: 'v$RESOLVED_VERSION'
|
||||||
|
tag-template: 'v$RESOLVED_VERSION'
|
||||||
|
exclude-labels:
|
||||||
|
- 'skip changelog'
|
||||||
|
version-resolver:
|
||||||
|
minor:
|
||||||
|
labels:
|
||||||
|
- 'enhancement'
|
||||||
|
default: patch
|
||||||
|
categories:
|
||||||
|
- title: '⚠️ Breaking changes'
|
||||||
|
label: 'breaking-change'
|
||||||
|
- title: '🚀 Enhancements'
|
||||||
|
label: 'enhancement'
|
||||||
|
- title: '🐛 Bug Fixes'
|
||||||
|
label: 'bug'
|
||||||
|
- title: '🔒 Security'
|
||||||
|
label: 'security'
|
||||||
|
- title: '⚙️ Maintenance/misc'
|
||||||
|
label:
|
||||||
|
- 'maintenance'
|
||||||
|
- 'documentation'
|
||||||
|
template: |
|
||||||
|
$CHANGES
|
||||||
|
|
||||||
|
❤️ Huge thanks to our contributors: $CONTRIBUTORS.
|
||||||
|
no-changes-template: 'Changes are coming soon 😎'
|
||||||
|
sort-direction: 'ascending'
|
||||||
|
replacers:
|
||||||
|
- search: '/(?:and )?@dependabot-preview(?:\[bot\])?,?/g'
|
||||||
|
replace: ''
|
||||||
|
- search: '/(?:and )?@dependabot(?:\[bot\])?,?/g'
|
||||||
|
replace: ''
|
||||||
22
.github/templates/dependency-issue.md
vendored
Normal file
22
.github/templates/dependency-issue.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
This issue is about updating Meilisearch dependencies:
|
||||||
|
- [ ] Update Meilisearch dependencies with the help of `cargo +nightly udeps --all-targets` (remove unused dependencies) and `cargo upgrade` (upgrade dependencies versions) - ⚠️ Some repositories may contain subdirectories (like heed, charabia, or deserr). Take care of updating these in the main crate as well. This won't be done automatically by `cargo upgrade`.
|
||||||
|
- [ ] [deserr](https://github.com/meilisearch/deserr)
|
||||||
|
- [ ] [charabia](https://github.com/meilisearch/charabia/)
|
||||||
|
- [ ] [heed](https://github.com/meilisearch/heed/)
|
||||||
|
- [ ] [roaring-rs](https://github.com/RoaringBitmap/roaring-rs/)
|
||||||
|
- [ ] [obkv](https://github.com/meilisearch/obkv)
|
||||||
|
- [ ] [grenad](https://github.com/meilisearch/grenad/)
|
||||||
|
- [ ] [arroy](https://github.com/meilisearch/arroy/)
|
||||||
|
- [ ] [segment](https://github.com/meilisearch/segment)
|
||||||
|
- [ ] [bumparaw-collections](https://github.com/meilisearch/bumparaw-collections)
|
||||||
|
- [ ] [bbqueue](https://github.com/meilisearch/bbqueue)
|
||||||
|
- [ ] Finally, [Meilisearch](https://github.com/meilisearch/MeiliSearch)
|
||||||
|
- [ ] If new Rust versions have been released, update the minimal Rust version in use at Meilisearch:
|
||||||
|
- [ ] in this [GitHub Action file](https://github.com/meilisearch/meilisearch/blob/main/.github/workflows/test-suite.yml), by changing the `toolchain` field of the `rustfmt` job to the latest available nightly (of the day before or the current day).
|
||||||
|
- [ ] in every [GitHub Action files](https://github.com/meilisearch/meilisearch/blob/main/.github/workflows), by changing all the `dtolnay/rust-toolchain@` references to use the latest stable version.
|
||||||
|
- [ ] in this [`rust-toolchain.toml`](https://github.com/meilisearch/meilisearch/blob/main/rust-toolchain.toml), by changing the `channel` field to the latest stable version.
|
||||||
|
- [ ] in the [Dockerfile](https://github.com/meilisearch/meilisearch/blob/main/Dockerfile), by changing the base image to `rust:<target_rust_version>-alpine<alpine_version>`. Check that the image exists on [Dockerhub](https://hub.docker.com/_/rust/tags?page=1&name=alpine). Also, build and run the image to check everything still works!
|
||||||
|
|
||||||
|
⚠️ This issue should be prioritized to avoid any deprecation and vulnerability issues.
|
||||||
|
|
||||||
|
The GitHub action dependencies are managed by [Dependabot](https://github.com/meilisearch/meilisearch/blob/main/.github/dependabot.yml), so no need to update them when solving this issue.
|
||||||
39
.github/workflows/bench-manual.yml
vendored
39
.github/workflows/bench-manual.yml
vendored
@@ -1,28 +1,27 @@
|
|||||||
name: Bench (manual)
|
name: Bench (manual)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
workload:
|
workload:
|
||||||
description: 'The path to the workloads to execute (workloads/...)'
|
description: "The path to the workloads to execute (workloads/...)"
|
||||||
required: true
|
required: true
|
||||||
default: 'workloads/movies.json'
|
default: "workloads/movies.json"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
WORKLOAD_NAME: ${{ github.event.inputs.workload }}
|
WORKLOAD_NAME: ${{ github.event.inputs.workload }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
benchmarks:
|
benchmarks:
|
||||||
name: Run and upload benchmarks
|
name: Run and upload benchmarks
|
||||||
runs-on: benchmarks
|
runs-on: benchmarks
|
||||||
timeout-minutes: 180 # 3h
|
timeout-minutes: 180 # 3h
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
- name: Run benchmarks - workload ${WORKLOAD_NAME} - branch ${{ github.ref }} - commit ${{ github.sha }}
|
|
||||||
run: |
|
|
||||||
cargo xtask bench --api-key "${{ secrets.BENCHMARK_API_KEY }}" --dashboard-url "${{ vars.BENCHMARK_DASHBOARD_URL }}" --reason "Manual [Run #${{ github.run_id }}](https://github.com/meilisearch/meilisearch/actions/runs/${{ github.run_id }})" -- ${WORKLOAD_NAME}
|
|
||||||
|
|
||||||
|
- name: Run benchmarks - workload ${WORKLOAD_NAME} - branch ${{ github.ref }} - commit ${{ github.sha }}
|
||||||
|
run: |
|
||||||
|
cargo xtask bench --api-key "${{ secrets.BENCHMARK_API_KEY }}" --dashboard-url "${{ vars.BENCHMARK_DASHBOARD_URL }}" --reason "Manual [Run #${{ github.run_id }}](https://github.com/meilisearch/meilisearch/actions/runs/${{ github.run_id }})" -- ${WORKLOAD_NAME}
|
||||||
|
|||||||
136
.github/workflows/bench-pr.yml
vendored
136
.github/workflows/bench-pr.yml
vendored
@@ -1,82 +1,82 @@
|
|||||||
name: Bench (PR)
|
name: Bench (PR)
|
||||||
on:
|
on:
|
||||||
issue_comment:
|
issue_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.MEILI_BOT_GH_PAT }}
|
GH_TOKEN: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-benchmarks-on-comment:
|
run-benchmarks-on-comment:
|
||||||
if: startsWith(github.event.comment.body, '/bench')
|
if: startsWith(github.event.comment.body, '/bench')
|
||||||
name: Run and upload benchmarks
|
name: Run and upload benchmarks
|
||||||
runs-on: benchmarks
|
runs-on: benchmarks
|
||||||
timeout-minutes: 180 # 3h
|
timeout-minutes: 180 # 3h
|
||||||
steps:
|
steps:
|
||||||
- name: Check permissions
|
- name: Check permissions
|
||||||
id: permission
|
id: permission
|
||||||
env:
|
env:
|
||||||
PR_AUTHOR: ${{github.event.issue.user.login }}
|
PR_AUTHOR: ${{github.event.issue.user.login }}
|
||||||
COMMENT_AUTHOR: ${{github.event.comment.user.login }}
|
COMMENT_AUTHOR: ${{github.event.comment.user.login }}
|
||||||
REPOSITORY: ${{github.repository}}
|
REPOSITORY: ${{github.repository}}
|
||||||
PR_ID: ${{github.event.issue.number}}
|
PR_ID: ${{github.event.issue.number}}
|
||||||
run: |
|
run: |
|
||||||
PR_REPOSITORY=$(gh api /repos/"$REPOSITORY"/pulls/"$PR_ID" --jq .head.repo.full_name)
|
PR_REPOSITORY=$(gh api /repos/"$REPOSITORY"/pulls/"$PR_ID" --jq .head.repo.full_name)
|
||||||
if $(gh api /repos/"$REPOSITORY"/collaborators/"$PR_AUTHOR"/permission --jq .user.permissions.push)
|
if $(gh api /repos/"$REPOSITORY"/collaborators/"$PR_AUTHOR"/permission --jq .user.permissions.push)
|
||||||
then
|
then
|
||||||
echo "::notice title=Authentication success::PR author authenticated"
|
echo "::notice title=Authentication success::PR author authenticated"
|
||||||
else
|
else
|
||||||
echo "::error title=Authentication error::PR author doesn't have push permission on this repository"
|
echo "::error title=Authentication error::PR author doesn't have push permission on this repository"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if $(gh api /repos/"$REPOSITORY"/collaborators/"$COMMENT_AUTHOR"/permission --jq .user.permissions.push)
|
if $(gh api /repos/"$REPOSITORY"/collaborators/"$COMMENT_AUTHOR"/permission --jq .user.permissions.push)
|
||||||
then
|
then
|
||||||
echo "::notice title=Authentication success::Comment author authenticated"
|
echo "::notice title=Authentication success::Comment author authenticated"
|
||||||
else
|
else
|
||||||
echo "::error title=Authentication error::Comment author doesn't have push permission on this repository"
|
echo "::error title=Authentication error::Comment author doesn't have push permission on this repository"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$PR_REPOSITORY" = "$REPOSITORY" ]
|
if [ "$PR_REPOSITORY" = "$REPOSITORY" ]
|
||||||
then
|
then
|
||||||
echo "::notice title=Authentication success::PR started from main repository"
|
echo "::notice title=Authentication success::PR started from main repository"
|
||||||
else
|
else
|
||||||
echo "::error title=Authentication error::PR started from a fork"
|
echo "::error title=Authentication error::PR started from a fork"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check for Command
|
- name: Check for Command
|
||||||
id: command
|
id: command
|
||||||
uses: xt0rted/slash-command-action@v2
|
uses: xt0rted/slash-command-action@v2
|
||||||
with:
|
with:
|
||||||
command: bench
|
command: bench
|
||||||
reaction-type: "rocket"
|
reaction-type: "rocket"
|
||||||
repo-token: ${{ env.GH_TOKEN }}
|
repo-token: ${{ env.GH_TOKEN }}
|
||||||
|
|
||||||
- uses: xt0rted/pull-request-comment-branch@v3
|
- uses: xt0rted/pull-request-comment-branch@v3
|
||||||
id: comment-branch
|
id: comment-branch
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ env.GH_TOKEN }}
|
repo_token: ${{ env.GH_TOKEN }}
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # fetch full history to be able to get main commit sha
|
fetch-depth: 0 # fetch full history to be able to get main commit sha
|
||||||
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
- name: Run benchmarks on PR ${{ github.event.issue.id }}
|
- name: Run benchmarks on PR ${{ github.event.issue.id }}
|
||||||
run: |
|
run: |
|
||||||
cargo xtask bench --api-key "${{ secrets.BENCHMARK_API_KEY }}" \
|
cargo xtask bench --api-key "${{ secrets.BENCHMARK_API_KEY }}" \
|
||||||
--dashboard-url "${{ vars.BENCHMARK_DASHBOARD_URL }}" \
|
--dashboard-url "${{ vars.BENCHMARK_DASHBOARD_URL }}" \
|
||||||
--reason "[Comment](${{ github.event.comment.html_url }}) on [#${{ github.event.issue.number }}](${{ github.event.issue.html_url }})" \
|
--reason "[Comment](${{ github.event.comment.html_url }}) on [#${{ github.event.issue.number }}](${{ github.event.issue.html_url }})" \
|
||||||
-- ${{ steps.command.outputs.command-arguments }} > benchlinks.txt
|
-- ${{ steps.command.outputs.command-arguments }} > benchlinks.txt
|
||||||
|
|
||||||
- name: Send comment in PR
|
- name: Send comment in PR
|
||||||
run: |
|
run: |
|
||||||
gh pr comment ${{github.event.issue.number}} --body-file benchlinks.txt
|
gh pr comment ${{github.event.issue.number}} --body-file benchlinks.txt
|
||||||
|
|||||||
33
.github/workflows/bench-push-indexing.yml
vendored
33
.github/workflows/bench-push-indexing.yml
vendored
@@ -1,23 +1,22 @@
|
|||||||
name: Indexing bench (push)
|
name: Indexing bench (push)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
benchmarks:
|
benchmarks:
|
||||||
name: Run and upload benchmarks
|
name: Run and upload benchmarks
|
||||||
runs-on: benchmarks
|
runs-on: benchmarks
|
||||||
timeout-minutes: 180 # 3h
|
timeout-minutes: 180 # 3h
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
# Run benchmarks
|
|
||||||
- name: Run benchmarks - Dataset ${BENCH_NAME} - Branch main - Commit ${{ github.sha }}
|
|
||||||
run: |
|
|
||||||
cargo xtask bench --api-key "${{ secrets.BENCHMARK_API_KEY }}" --dashboard-url "${{ vars.BENCHMARK_DASHBOARD_URL }}" --reason "Push on `main` [Run #${{ github.run_id }}](https://github.com/meilisearch/meilisearch/actions/runs/${{ github.run_id }})" -- workloads/*.json
|
|
||||||
|
|
||||||
|
# Run benchmarks
|
||||||
|
- name: Run benchmarks - Dataset ${BENCH_NAME} - Branch main - Commit ${{ github.sha }}
|
||||||
|
run: |
|
||||||
|
cargo xtask bench --api-key "${{ secrets.BENCHMARK_API_KEY }}" --dashboard-url "${{ vars.BENCHMARK_DASHBOARD_URL }}" --reason "Push on `main` [Run #${{ github.run_id }}](https://github.com/meilisearch/meilisearch/actions/runs/${{ github.run_id }})" -- workloads/*.json
|
||||||
|
|||||||
8
.github/workflows/benchmarks-manual.yml
vendored
8
.github/workflows/benchmarks-manual.yml
vendored
@@ -4,9 +4,9 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
dataset_name:
|
dataset_name:
|
||||||
description: 'The name of the dataset used to benchmark (search_songs, search_wiki, search_geo or indexing)'
|
description: "The name of the dataset used to benchmark (search_songs, search_wiki, search_geo or indexing)"
|
||||||
required: false
|
required: false
|
||||||
default: 'search_songs'
|
default: "search_songs"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BENCH_NAME: ${{ github.event.inputs.dataset_name }}
|
BENCH_NAME: ${{ github.event.inputs.dataset_name }}
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
timeout-minutes: 4320 # 72h
|
timeout-minutes: 4320 # 72h
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
out_dir: critcmp_results
|
out_dir: critcmp_results
|
||||||
|
|
||||||
# Helper
|
# Helper
|
||||||
- name: 'README: compare with another benchmark'
|
- name: "README: compare with another benchmark"
|
||||||
run: |
|
run: |
|
||||||
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
||||||
echo 'How to compare this benchmark with another one?'
|
echo 'How to compare this benchmark with another one?'
|
||||||
|
|||||||
2
.github/workflows/benchmarks-pr.yml
vendored
2
.github/workflows/benchmarks-pr.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
timeout-minutes: 4320 # 72h
|
timeout-minutes: 4320 # 72h
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ jobs:
|
|||||||
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
||||||
|
|
||||||
# Helper
|
# Helper
|
||||||
- name: 'README: compare with another benchmark'
|
- name: "README: compare with another benchmark"
|
||||||
run: |
|
run: |
|
||||||
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
||||||
echo 'How to compare this benchmark with another one?'
|
echo 'How to compare this benchmark with another one?'
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: benchmarks
|
runs-on: benchmarks
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
||||||
|
|
||||||
# Helper
|
# Helper
|
||||||
- name: 'README: compare with another benchmark'
|
- name: "README: compare with another benchmark"
|
||||||
run: |
|
run: |
|
||||||
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
||||||
echo 'How to compare this benchmark with another one?'
|
echo 'How to compare this benchmark with another one?'
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: benchmarks
|
runs-on: benchmarks
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
||||||
|
|
||||||
# Helper
|
# Helper
|
||||||
- name: 'README: compare with another benchmark'
|
- name: "README: compare with another benchmark"
|
||||||
run: |
|
run: |
|
||||||
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
||||||
echo 'How to compare this benchmark with another one?'
|
echo 'How to compare this benchmark with another one?'
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: benchmarks
|
runs-on: benchmarks
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
run: telegraf --config https://eu-central-1-1.aws.cloud2.influxdata.com/api/v2/telegrafs/08b52e34a370b000 --once --debug
|
||||||
|
|
||||||
# Helper
|
# Helper
|
||||||
- name: 'README: compare with another benchmark'
|
- name: "README: compare with another benchmark"
|
||||||
run: |
|
run: |
|
||||||
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
echo "${{ steps.file.outputs.basename }}.json has just been pushed."
|
||||||
echo 'How to compare this benchmark with another one?'
|
echo 'How to compare this benchmark with another one?'
|
||||||
|
|||||||
100
.github/workflows/check-valid-milestone.yml
vendored
100
.github/workflows/check-valid-milestone.yml
vendored
@@ -1,100 +0,0 @@
|
|||||||
name: PR Milestone Check
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, reopened, edited, synchronize, milestoned, demilestoned]
|
|
||||||
branches:
|
|
||||||
- "main"
|
|
||||||
- "release-v*.*.*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-milestone:
|
|
||||||
name: Check PR Milestone
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Validate PR milestone
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
// Get PR number directly from the event payload
|
|
||||||
const prNumber = context.payload.pull_request.number;
|
|
||||||
|
|
||||||
// Get PR details
|
|
||||||
const { data: prData } = await github.rest.pulls.get({
|
|
||||||
owner: 'meilisearch',
|
|
||||||
repo: 'meilisearch',
|
|
||||||
pull_number: prNumber
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get base branch name
|
|
||||||
const baseBranch = prData.base.ref;
|
|
||||||
console.log(`Base branch: ${baseBranch}`);
|
|
||||||
|
|
||||||
// Get PR milestone
|
|
||||||
const prMilestone = prData.milestone;
|
|
||||||
if (!prMilestone) {
|
|
||||||
core.setFailed('PR must have a milestone assigned');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`PR milestone: ${prMilestone.title}`);
|
|
||||||
|
|
||||||
// Validate milestone format: vx.y.z
|
|
||||||
const milestoneRegex = /^v\d+\.\d+\.\d+$/;
|
|
||||||
if (!milestoneRegex.test(prMilestone.title)) {
|
|
||||||
core.setFailed(`Milestone "${prMilestone.title}" does not follow the required format vx.y.z`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For main branch PRs, check if the milestone is the highest one
|
|
||||||
if (baseBranch === 'main') {
|
|
||||||
// Get all milestones
|
|
||||||
const { data: milestones } = await github.rest.issues.listMilestones({
|
|
||||||
owner: 'meilisearch',
|
|
||||||
repo: 'meilisearch',
|
|
||||||
state: 'open',
|
|
||||||
sort: 'due_on',
|
|
||||||
direction: 'desc'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort milestones by version number (vx.y.z)
|
|
||||||
const sortedMilestones = milestones
|
|
||||||
.filter(m => milestoneRegex.test(m.title))
|
|
||||||
.sort((a, b) => {
|
|
||||||
const versionA = a.title.substring(1).split('.').map(Number);
|
|
||||||
const versionB = b.title.substring(1).split('.').map(Number);
|
|
||||||
|
|
||||||
// Compare major version
|
|
||||||
if (versionA[0] !== versionB[0]) return versionB[0] - versionA[0];
|
|
||||||
// Compare minor version
|
|
||||||
if (versionA[1] !== versionB[1]) return versionB[1] - versionA[1];
|
|
||||||
// Compare patch version
|
|
||||||
return versionB[2] - versionA[2];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (sortedMilestones.length === 0) {
|
|
||||||
core.setFailed('No valid milestones found in the repository. Please create at least one milestone with the format vx.y.z');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const highestMilestone = sortedMilestones[0];
|
|
||||||
console.log(`Highest milestone: ${highestMilestone.title}`);
|
|
||||||
|
|
||||||
if (prMilestone.title !== highestMilestone.title) {
|
|
||||||
core.setFailed(`PRs targeting the main branch must use the highest milestone (${highestMilestone.title}), but this PR uses ${prMilestone.title}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// For release branches, the milestone should match the branch version
|
|
||||||
const branchVersion = baseBranch.substring(8); // remove 'release-'
|
|
||||||
if (prMilestone.title !== branchVersion) {
|
|
||||||
core.setFailed(`PRs targeting release branch "${baseBranch}" must use the matching milestone "${branchVersion}", but this PR uses "${prMilestone.title}"`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('PR milestone validation passed!');
|
|
||||||
57
.github/workflows/db-change-comments.yml
vendored
Normal file
57
.github/workflows/db-change-comments.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
name: Comment when db change labels are added
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [labeled]
|
||||||
|
|
||||||
|
env:
|
||||||
|
MESSAGE: |
|
||||||
|
### Hello, I'm a bot 🤖
|
||||||
|
|
||||||
|
You are receiving this message because you declared that this PR make changes to the Meilisearch database.
|
||||||
|
Depending on the nature of the change, additional actions might be required on your part. The following sections detail the additional actions depending on the nature of the change, please copy the relevant section in the description of your PR, and make sure to perform the required actions.
|
||||||
|
|
||||||
|
Thank you for contributing to Meilisearch :heart:
|
||||||
|
|
||||||
|
## This PR makes forward-compatible changes
|
||||||
|
|
||||||
|
*Forward-compatible changes are changes to the database such that databases created in an older version of Meilisearch are still valid in the new version of Meilisearch. They usually represent additive changes, like adding a new optional attribute or setting.*
|
||||||
|
|
||||||
|
- [ ] Detail the change to the DB format and why they are forward compatible
|
||||||
|
- [ ] Forward-compatibility: A database created before this PR and using the features touched by this PR was able to be opened by a Meilisearch produced by the code of this PR.
|
||||||
|
|
||||||
|
|
||||||
|
## This PR makes breaking changes
|
||||||
|
|
||||||
|
*Breaking changes are changes to the database such that databases created in an older version of Meilisearch need changes to remain valid in the new version of Meilisearch. This typically happens when the way to store the data changed (change of database, new required key, etc). This can also happen due to breaking changes in the API of an experimental feature. ⚠️ This kind of changes are more difficult to achieve safely, so proceed with caution and test dumpless upgrade right before merging the PR.*
|
||||||
|
|
||||||
|
- [ ] Detail the changes to the DB format,
|
||||||
|
- [ ] which are compatible, and why
|
||||||
|
- [ ] which are not compatible, why, and how they will be fixed up in the upgrade
|
||||||
|
- [ ] /!\ Ensure all the read operations still work!
|
||||||
|
- If the change happened in milli, you may need to check the version of the database before doing any read operation
|
||||||
|
- If the change happened in the index-scheduler, make sure the new code can immediately read the old database
|
||||||
|
- If the change happened in the meilisearch-auth database, reach out to the team; we don't know yet how to handle these changes
|
||||||
|
- [ ] Write the code to go from the old database to the new one
|
||||||
|
- If the change happened in milli, the upgrade function should be written and called [here](https://github.com/meilisearch/meilisearch/blob/3fd86e8d76d7d468b0095d679adb09211ca3b6c0/crates/milli/src/update/upgrade/mod.rs#L24-L47)
|
||||||
|
- If the change happened in the index-scheduler, we've never done it yet, but the right place to do it should be [here](https://github.com/meilisearch/meilisearch/blob/3fd86e8d76d7d468b0095d679adb09211ca3b6c0/crates/index-scheduler/src/scheduler/process_upgrade/mod.rs#L13)
|
||||||
|
- [ ] Write an integration test [here](https://github.com/meilisearch/meilisearch/blob/main/crates/meilisearch/tests/upgrade/mod.rs) ensuring you can read the old database, upgrade to the new database, and read the new database as expected
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
add-comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event.label.name == 'db change'
|
||||||
|
steps:
|
||||||
|
- name: Add comment
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const message = process.env.MESSAGE;
|
||||||
|
github.rest.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: message
|
||||||
|
})
|
||||||
28
.github/workflows/db-change-missing.yml
vendored
Normal file
28
.github/workflows/db-change-missing.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Check db change labels
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-labels:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Check db change labels
|
||||||
|
id: check_labels
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
URL=/repos/meilisearch/meilisearch/pulls/${{ github.event.pull_request.number }}/labels
|
||||||
|
echo ${{ github.event.pull_request.number }}
|
||||||
|
echo $URL
|
||||||
|
LABELS=$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels -q .[].name)
|
||||||
|
echo "Labels: $LABELS"
|
||||||
|
if [[ ! "$LABELS" =~ "db change" && ! "$LABELS" =~ "no db change" ]]; then
|
||||||
|
echo "::error::Pull request must contain either the 'db change' or 'no db change' label."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "The label is set"
|
||||||
|
fi
|
||||||
2
.github/workflows/dependency-issue.yml
vendored
2
.github/workflows/dependency-issue.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Download the issue template
|
- name: Download the issue template
|
||||||
run: curl -s https://raw.githubusercontent.com/meilisearch/engine-team/main/issue-templates/dependency-issue.md > $ISSUE_TEMPLATE
|
run: curl -s https://raw.githubusercontent.com/meilisearch/meilisearch/main/.github/templates/dependency-issue.md > $ISSUE_TEMPLATE
|
||||||
- name: Create issue
|
- name: Create issue
|
||||||
run: |
|
run: |
|
||||||
gh issue create \
|
gh issue create \
|
||||||
|
|||||||
4
.github/workflows/flaky-tests.yml
vendored
4
.github/workflows/flaky-tests.yml
vendored
@@ -3,7 +3,7 @@ name: Look for flaky tests
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 12 * * FRI" # Every Friday at 12:00PM
|
- cron: '0 4 * * *' # Every day at 4:00AM
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
flaky:
|
flaky:
|
||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
apt-get install build-essential -y
|
apt-get install build-essential -y
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Install cargo-flaky
|
- name: Install cargo-flaky
|
||||||
run: cargo install cargo-flaky
|
run: cargo install cargo-flaky
|
||||||
- name: Run cargo flaky in the dumps
|
- name: Run cargo flaky in the dumps
|
||||||
|
|||||||
2
.github/workflows/fuzzer-indexing.yml
vendored
2
.github/workflows/fuzzer-indexing.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
timeout-minutes: 4320 # 72h
|
timeout-minutes: 4320 # 72h
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
|
|
||||||
|
|||||||
192
.github/workflows/milestone-workflow.yml
vendored
192
.github/workflows/milestone-workflow.yml
vendored
@@ -1,192 +0,0 @@
|
|||||||
name: Milestone's workflow
|
|
||||||
|
|
||||||
# /!\ No git flow are handled here
|
|
||||||
|
|
||||||
# For each Milestone created (not opened!), and if the release is NOT a patch release (only the patch changed)
|
|
||||||
# - the roadmap issue is created, see https://github.com/meilisearch/engine-team/blob/main/issue-templates/roadmap-issue.md
|
|
||||||
# - the changelog issue is created, see https://github.com/meilisearch/engine-team/blob/main/issue-templates/changelog-issue.md
|
|
||||||
|
|
||||||
# For each Milestone closed
|
|
||||||
# - the `release_version` label is created
|
|
||||||
# - this label is applied to all issues/PRs in the Milestone
|
|
||||||
|
|
||||||
on:
|
|
||||||
milestone:
|
|
||||||
types: [created, closed]
|
|
||||||
|
|
||||||
env:
|
|
||||||
MILESTONE_VERSION: ${{ github.event.milestone.title }}
|
|
||||||
MILESTONE_URL: ${{ github.event.milestone.html_url }}
|
|
||||||
MILESTONE_DUE_ON: ${{ github.event.milestone.due_on }}
|
|
||||||
GH_TOKEN: ${{ secrets.MEILI_BOT_GH_PAT }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
# -----------------
|
|
||||||
# MILESTONE CREATED
|
|
||||||
# -----------------
|
|
||||||
|
|
||||||
get-release-version:
|
|
||||||
if: github.event.action == 'created'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
is-patch: ${{ steps.check-patch.outputs.is-patch }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Check if this release is a patch release only
|
|
||||||
id: check-patch
|
|
||||||
run: |
|
|
||||||
echo version: $MILESTONE_VERSION
|
|
||||||
if [[ $MILESTONE_VERSION =~ ^v[0-9]+\.[0-9]+\.0$ ]]; then
|
|
||||||
echo 'This is NOT a patch release'
|
|
||||||
echo "is-patch=false" >> $GITHUB_OUTPUT
|
|
||||||
elif [[ $MILESTONE_VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
||||||
echo 'This is a patch release'
|
|
||||||
echo "is-patch=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "Not a valid format of release, check the Milestone's title."
|
|
||||||
echo 'Should be vX.Y.Z'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
create-roadmap-issue:
|
|
||||||
needs: get-release-version
|
|
||||||
# Create the roadmap issue if the release is not only a patch release
|
|
||||||
if: github.event.action == 'created' && needs.get-release-version.outputs.is-patch == 'false'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
ISSUE_TEMPLATE: issue-template.md
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Download the issue template
|
|
||||||
run: curl -s https://raw.githubusercontent.com/meilisearch/engine-team/main/issue-templates/roadmap-issue.md > $ISSUE_TEMPLATE
|
|
||||||
- name: Replace all empty occurrences in the templates
|
|
||||||
run: |
|
|
||||||
# Replace all <<version>> occurrences
|
|
||||||
sed -i "s/<<version>>/$MILESTONE_VERSION/g" $ISSUE_TEMPLATE
|
|
||||||
|
|
||||||
# Replace all <<milestone_id>> occurrences
|
|
||||||
milestone_id=$(echo $MILESTONE_URL | cut -d '/' -f 7)
|
|
||||||
sed -i "s/<<milestone_id>>/$milestone_id/g" $ISSUE_TEMPLATE
|
|
||||||
|
|
||||||
# Replace release date if exists
|
|
||||||
if [[ ! -z $MILESTONE_DUE_ON ]]; then
|
|
||||||
date=$(echo $MILESTONE_DUE_ON | cut -d 'T' -f 1)
|
|
||||||
sed -i "s/Release date\: 20XX-XX-XX/Release date\: $date/g" $ISSUE_TEMPLATE
|
|
||||||
fi
|
|
||||||
- name: Create the issue
|
|
||||||
run: |
|
|
||||||
gh issue create \
|
|
||||||
--title "$MILESTONE_VERSION ROADMAP" \
|
|
||||||
--label 'epic,impacts docs,impacts integrations,impacts cloud' \
|
|
||||||
--body-file $ISSUE_TEMPLATE \
|
|
||||||
--milestone $MILESTONE_VERSION
|
|
||||||
|
|
||||||
create-changelog-issue:
|
|
||||||
needs: get-release-version
|
|
||||||
# Create the changelog issue if the release is not only a patch release
|
|
||||||
if: github.event.action == 'created' && needs.get-release-version.outputs.is-patch == 'false'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
ISSUE_TEMPLATE: issue-template.md
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Download the issue template
|
|
||||||
run: curl -s https://raw.githubusercontent.com/meilisearch/engine-team/main/issue-templates/changelog-issue.md > $ISSUE_TEMPLATE
|
|
||||||
- name: Replace all empty occurrences in the templates
|
|
||||||
run: |
|
|
||||||
# Replace all <<version>> occurrences
|
|
||||||
sed -i "s/<<version>>/$MILESTONE_VERSION/g" $ISSUE_TEMPLATE
|
|
||||||
|
|
||||||
# Replace all <<milestone_id>> occurrences
|
|
||||||
milestone_id=$(echo $MILESTONE_URL | cut -d '/' -f 7)
|
|
||||||
sed -i "s/<<milestone_id>>/$milestone_id/g" $ISSUE_TEMPLATE
|
|
||||||
- name: Create the issue
|
|
||||||
run: |
|
|
||||||
gh issue create \
|
|
||||||
--title "Create release changelogs for $MILESTONE_VERSION" \
|
|
||||||
--label 'impacts docs,documentation' \
|
|
||||||
--body-file $ISSUE_TEMPLATE \
|
|
||||||
--milestone $MILESTONE_VERSION \
|
|
||||||
--assignee curquiza
|
|
||||||
|
|
||||||
create-update-version-issue:
|
|
||||||
needs: get-release-version
|
|
||||||
# Create the update-version issue even if the release is a patch release
|
|
||||||
if: github.event.action == 'created'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
ISSUE_TEMPLATE: issue-template.md
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Download the issue template
|
|
||||||
run: curl -s https://raw.githubusercontent.com/meilisearch/engine-team/main/issue-templates/update-version-issue.md > $ISSUE_TEMPLATE
|
|
||||||
- name: Create the issue
|
|
||||||
run: |
|
|
||||||
gh issue create \
|
|
||||||
--title "Update version in Cargo.toml for $MILESTONE_VERSION" \
|
|
||||||
--label 'maintenance' \
|
|
||||||
--body-file $ISSUE_TEMPLATE \
|
|
||||||
--milestone $MILESTONE_VERSION
|
|
||||||
|
|
||||||
create-update-openapi-issue:
|
|
||||||
needs: get-release-version
|
|
||||||
# Create the openAPI issue if the release is not only a patch release
|
|
||||||
if: github.event.action == 'created' && needs.get-release-version.outputs.is-patch == 'false'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
ISSUE_TEMPLATE: issue-template.md
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Download the issue template
|
|
||||||
run: curl -s https://raw.githubusercontent.com/meilisearch/engine-team/main/issue-templates/update-openapi-issue.md > $ISSUE_TEMPLATE
|
|
||||||
- name: Create the issue
|
|
||||||
run: |
|
|
||||||
gh issue create \
|
|
||||||
--title "Update Open API file for $MILESTONE_VERSION" \
|
|
||||||
--label 'maintenance' \
|
|
||||||
--body-file $ISSUE_TEMPLATE \
|
|
||||||
--milestone $MILESTONE_VERSION
|
|
||||||
|
|
||||||
# ----------------
|
|
||||||
# MILESTONE CLOSED
|
|
||||||
# ----------------
|
|
||||||
|
|
||||||
create-release-label:
|
|
||||||
if: github.event.action == 'closed'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Create the ${{ env.MILESTONE_VERSION }} label
|
|
||||||
run: |
|
|
||||||
label_description="PRs/issues solved in $MILESTONE_VERSION"
|
|
||||||
if [[ ! -z $MILESTONE_DUE_ON ]]; then
|
|
||||||
date=$(echo $MILESTONE_DUE_ON | cut -d 'T' -f 1)
|
|
||||||
label_description="$label_description released on $date"
|
|
||||||
fi
|
|
||||||
|
|
||||||
gh api repos/meilisearch/meilisearch/labels \
|
|
||||||
--method POST \
|
|
||||||
-H "Accept: application/vnd.github+json" \
|
|
||||||
-f name="$MILESTONE_VERSION" \
|
|
||||||
-f description="$label_description" \
|
|
||||||
-f color='ff5ba3'
|
|
||||||
|
|
||||||
labelize-all-milestone-content:
|
|
||||||
if: github.event.action == 'closed'
|
|
||||||
needs: create-release-label
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Add label ${{ env.MILESTONE_VERSION }} to all PRs in the Milestone
|
|
||||||
run: |
|
|
||||||
prs=$(gh pr list --search milestone:"$MILESTONE_VERSION" --limit 1000 --state all --json number --template '{{range .}}{{tablerow (printf "%v" .number)}}{{end}}')
|
|
||||||
for pr in $prs; do
|
|
||||||
gh pr edit $pr --add-label $MILESTONE_VERSION
|
|
||||||
done
|
|
||||||
- name: Add label ${{ env.MILESTONE_VERSION }} to all issues in the Milestone
|
|
||||||
run: |
|
|
||||||
issues=$(gh issue list --search milestone:"$MILESTONE_VERSION" --limit 1000 --state all --json number --template '{{range .}}{{tablerow (printf "%v" .number)}}{{end}}')
|
|
||||||
for issue in $issues; do
|
|
||||||
gh issue edit $issue --add-label $MILESTONE_VERSION
|
|
||||||
done
|
|
||||||
4
.github/workflows/publish-apt-brew-pkg.yml
vendored
4
.github/workflows/publish-apt-brew-pkg.yml
vendored
@@ -25,14 +25,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
apt-get install build-essential -y
|
apt-get install build-essential -y
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Install cargo-deb
|
- name: Install cargo-deb
|
||||||
run: cargo install cargo-deb
|
run: cargo install cargo-deb
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Build deb package
|
- name: Build deb package
|
||||||
run: cargo deb -p meilisearch -o target/debian/meilisearch.deb
|
run: cargo deb -p meilisearch -o target/debian/meilisearch.deb
|
||||||
- name: Upload debian pkg to release
|
- name: Upload debian pkg to release
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
file: target/debian/meilisearch.deb
|
file: target/debian/meilisearch.deb
|
||||||
|
|||||||
36
.github/workflows/publish-docker-images.yml
vendored
36
.github/workflows/publish-docker-images.yml
vendored
@@ -16,6 +16,8 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
|
permissions:
|
||||||
|
id-token: write # This is needed to use Cosign in keyless mode
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
@@ -62,6 +64,9 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Install cosign
|
||||||
|
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # tag=v3.9.2
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -85,6 +90,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
|
id: build-and-push
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -94,6 +100,17 @@ jobs:
|
|||||||
COMMIT_DATE=${{ steps.build-metadata.outputs.date }}
|
COMMIT_DATE=${{ steps.build-metadata.outputs.date }}
|
||||||
GIT_TAG=${{ github.ref_name }}
|
GIT_TAG=${{ github.ref_name }}
|
||||||
|
|
||||||
|
- name: Sign the images with GitHub OIDC Token
|
||||||
|
env:
|
||||||
|
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||||
|
TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
|
run: |
|
||||||
|
images=""
|
||||||
|
for tag in ${TAGS}; do
|
||||||
|
images+="${tag}@${DIGEST} "
|
||||||
|
done
|
||||||
|
cosign sign --yes ${images}
|
||||||
|
|
||||||
# /!\ Don't touch this without checking with Cloud team
|
# /!\ Don't touch this without checking with Cloud team
|
||||||
- name: Send CI information to Cloud team
|
- name: Send CI information to Cloud team
|
||||||
# Do not send if nightly build (i.e. 'schedule' or 'workflow_dispatch' event)
|
# Do not send if nightly build (i.e. 'schedule' or 'workflow_dispatch' event)
|
||||||
@@ -104,3 +121,22 @@ jobs:
|
|||||||
repository: meilisearch/meilisearch-cloud
|
repository: meilisearch/meilisearch-cloud
|
||||||
event-type: cloud-docker-build
|
event-type: cloud-docker-build
|
||||||
client-payload: '{ "meilisearch_version": "${{ github.ref_name }}", "stable": "${{ steps.check-tag-format.outputs.stable }}" }'
|
client-payload: '{ "meilisearch_version": "${{ github.ref_name }}", "stable": "${{ steps.check-tag-format.outputs.stable }}" }'
|
||||||
|
|
||||||
|
# Send notification to Swarmia to notify of a deployment: https://app.swarmia.com
|
||||||
|
# - name: 'Setup jq'
|
||||||
|
# uses: dcarbone/install-jq-action
|
||||||
|
# - name: Send deployment to Swarmia
|
||||||
|
# if: github.event_name == 'push' && success()
|
||||||
|
# run: |
|
||||||
|
# JSON_STRING=$( jq --null-input --compact-output \
|
||||||
|
# --arg version "${{ github.ref_name }}" \
|
||||||
|
# --arg appName "meilisearch" \
|
||||||
|
# --arg environment "production" \
|
||||||
|
# --arg commitSha "${{ github.sha }}" \
|
||||||
|
# --arg repositoryFullName "${{ github.repository }}" \
|
||||||
|
# '{"version": $version, "appName": $appName, "environment": $environment, "commitSha": $commitSha, "repositoryFullName": $repositoryFullName}' )
|
||||||
|
|
||||||
|
# curl -H "Authorization: ${{ secrets.SWARMIA_DEPLOYMENTS_AUTHORIZATION }}" \
|
||||||
|
# -H "Content-Type: application/json" \
|
||||||
|
# -d "$JSON_STRING" \
|
||||||
|
# https://hook.swarmia.com/deployments
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Publish binaries to GitHub release
|
name: Publish assets to GitHub release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -45,13 +45,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
apt-get install build-essential -y
|
apt-get install build-essential -y
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release --locked
|
run: cargo build --release --locked
|
||||||
# No need to upload binaries for dry run (cron)
|
# No need to upload binaries for dry run (cron)
|
||||||
- name: Upload binaries to release
|
- name: Upload binaries to release
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
file: target/release/meilisearch
|
file: target/release/meilisearch
|
||||||
@@ -75,13 +75,13 @@ jobs:
|
|||||||
asset_name: meilisearch-windows-amd64.exe
|
asset_name: meilisearch-windows-amd64.exe
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release --locked
|
run: cargo build --release --locked
|
||||||
# No need to upload binaries for dry run (cron)
|
# No need to upload binaries for dry run (cron)
|
||||||
- name: Upload binaries to release
|
- name: Upload binaries to release
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
file: target/release/${{ matrix.artifact_name }}
|
file: target/release/${{ matrix.artifact_name }}
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Installing Rust toolchain
|
- name: Installing Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@1.81
|
uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
@@ -113,7 +113,7 @@ jobs:
|
|||||||
- name: Upload the binary to release
|
- name: Upload the binary to release
|
||||||
# No need to upload binaries for dry run (cron)
|
# No need to upload binaries for dry run (cron)
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
file: target/${{ matrix.target }}/release/meilisearch
|
file: target/${{ matrix.target }}/release/meilisearch
|
||||||
@@ -148,7 +148,7 @@ jobs:
|
|||||||
add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||||
apt-get update -y && apt-get install -y docker-ce
|
apt-get update -y && apt-get install -y docker-ce
|
||||||
- name: Installing Rust toolchain
|
- name: Installing Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@1.81
|
uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
@@ -178,9 +178,34 @@ jobs:
|
|||||||
- name: Upload the binary to release
|
- name: Upload the binary to release
|
||||||
# No need to upload binaries for dry run (cron)
|
# No need to upload binaries for dry run (cron)
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
file: target/${{ matrix.target }}/release/meilisearch
|
file: target/${{ matrix.target }}/release/meilisearch
|
||||||
asset_name: ${{ matrix.asset_name }}
|
asset_name: ${{ matrix.asset_name }}
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
|
|
||||||
|
publish-openapi-file:
|
||||||
|
name: Publish OpenAPI file
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- name: Generate OpenAPI file
|
||||||
|
run: |
|
||||||
|
cd crates/openapi-generator
|
||||||
|
cargo run --release -- --pretty --output ../../meilisearch.json
|
||||||
|
- name: Upload OpenAPI to Release
|
||||||
|
# No need to upload for dry run (cron)
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
uses: svenstaro/upload-release-action@2.11.2
|
||||||
|
with:
|
||||||
|
repo_token: ${{ secrets.MEILI_BOT_GH_PAT }}
|
||||||
|
file: ./meilisearch.json
|
||||||
|
asset_name: meilisearch-openapi.json
|
||||||
|
tag: ${{ github.ref }}
|
||||||
20
.github/workflows/release-drafter.yml
vendored
Normal file
20
.github/workflows/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Release Drafter
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_release_draft:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: release-drafter/release-drafter@v6
|
||||||
|
with:
|
||||||
|
config-name: release-draft-template.yml
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_DRAFTER_TOKEN }}
|
||||||
42
.github/workflows/sdks-tests.yml
vendored
42
.github/workflows/sdks-tests.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: nightly
|
default: nightly
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 6 * * MON" # Every Monday at 6:00AM
|
- cron: '0 6 * * *' # Every day at 6:00am
|
||||||
|
|
||||||
env:
|
env:
|
||||||
MEILI_MASTER_KEY: 'masterKey'
|
MEILI_MASTER_KEY: 'masterKey'
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
docker-image: ${{ steps.define-image.outputs.docker-image }}
|
docker-image: ${{ steps.define-image.outputs.docker-image }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Define the Docker image we need to use
|
- name: Define the Docker image we need to use
|
||||||
id: define-image
|
id: define-image
|
||||||
run: |
|
run: |
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
MEILISEARCH_VERSION: ${{ needs.define-docker-image.outputs.docker-image }}
|
MEILISEARCH_VERSION: ${{ needs.define-docker-image.outputs.docker-image }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-dotnet
|
repository: meilisearch/meilisearch-dotnet
|
||||||
- name: Setup .NET Core
|
- name: Setup .NET Core
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-dart
|
repository: meilisearch/meilisearch-dart
|
||||||
- uses: dart-lang/setup-dart@v1
|
- uses: dart-lang/setup-dart@v1
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: stable
|
go-version: stable
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-go
|
repository: meilisearch/meilisearch-go
|
||||||
- name: Get dependencies
|
- name: Get dependencies
|
||||||
@@ -114,7 +114,7 @@ jobs:
|
|||||||
dep ensure
|
dep ensure
|
||||||
fi
|
fi
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
run: go test -v ./...
|
run: go test --race -v ./integration
|
||||||
|
|
||||||
meilisearch-java-tests:
|
meilisearch-java-tests:
|
||||||
needs: define-docker-image
|
needs: define-docker-image
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-java
|
repository: meilisearch/meilisearch-java
|
||||||
- name: Set up Java
|
- name: Set up Java
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-js
|
repository: meilisearch/meilisearch-js
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-php
|
repository: meilisearch/meilisearch-php
|
||||||
- name: Install PHP
|
- name: Install PHP
|
||||||
@@ -220,7 +220,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-python
|
repository: meilisearch/meilisearch-python
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
@@ -245,7 +245,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-ruby
|
repository: meilisearch/meilisearch-ruby
|
||||||
- name: Set up Ruby 3
|
- name: Set up Ruby 3
|
||||||
@@ -270,7 +270,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-rust
|
repository: meilisearch/meilisearch-rust
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -291,7 +291,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-swift
|
repository: meilisearch/meilisearch-swift
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
@@ -314,7 +314,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-js-plugins
|
repository: meilisearch/meilisearch-js-plugins
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
@@ -344,15 +344,23 @@ jobs:
|
|||||||
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
MEILI_NO_ANALYTICS: ${{ env.MEILI_NO_ANALYTICS }}
|
||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
|
env:
|
||||||
|
RAILS_VERSION: '7.0'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-rails
|
repository: meilisearch/meilisearch-rails
|
||||||
- name: Set up Ruby 3
|
- name: Install SQLite dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y libsqlite3-dev
|
||||||
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 3
|
ruby-version: 3
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
|
- name: Start MongoDB
|
||||||
|
uses: supercharge/mongodb-github-action@1.12.0
|
||||||
|
with:
|
||||||
|
mongodb-version: 8.0
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bundle exec rspec
|
run: bundle exec rspec
|
||||||
|
|
||||||
@@ -369,7 +377,7 @@ jobs:
|
|||||||
ports:
|
ports:
|
||||||
- '7700:7700'
|
- '7700:7700'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: meilisearch/meilisearch-symfony
|
repository: meilisearch/meilisearch-symfony
|
||||||
- name: Install PHP
|
- name: Install PHP
|
||||||
|
|||||||
30
.github/workflows/test-suite.yml
vendored
30
.github/workflows/test-suite.yml
vendored
@@ -3,7 +3,7 @@ name: Test suite
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
# Everyday at 5:00am
|
# Every day at 5:00am
|
||||||
- cron: "0 5 * * *"
|
- cron: "0 5 * * *"
|
||||||
pull_request:
|
pull_request:
|
||||||
merge_group:
|
merge_group:
|
||||||
@@ -21,15 +21,15 @@ jobs:
|
|||||||
# Use ubuntu-22.04 to compile with glibc 2.35
|
# Use ubuntu-22.04 to compile with glibc 2.35
|
||||||
image: ubuntu:22.04
|
image: ubuntu:22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Install needed dependencies
|
- name: Install needed dependencies
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
apt-get install build-essential -y
|
apt-get install build-essential -y
|
||||||
- name: Setup test with Rust stable
|
- name: Setup test with Rust stable
|
||||||
uses: dtolnay/rust-toolchain@1.81
|
uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.7.7
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- name: Run cargo check without any default features
|
- name: Run cargo check without any default features
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
@@ -51,8 +51,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.7.7
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Run cargo check without any default features
|
- name: Run cargo check without any default features
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install --assume-yes build-essential curl
|
apt-get install --assume-yes build-essential curl
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Run cargo build with almost all features
|
- name: Run cargo build with almost all features
|
||||||
run: |
|
run: |
|
||||||
cargo build --workspace --locked --release --features "$(cargo xtask list-features --exclude-feature cuda,test-ollama)"
|
cargo build --workspace --locked --release --features "$(cargo xtask list-features --exclude-feature cuda,test-ollama)"
|
||||||
@@ -91,7 +91,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
MEILI_TEST_OLLAMA_SERVER: "http://localhost:11434"
|
MEILI_TEST_OLLAMA_SERVER: "http://localhost:11434"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
- name: Install Ollama
|
- name: Install Ollama
|
||||||
run: |
|
run: |
|
||||||
curl -fsSL https://ollama.com/install.sh | sudo -E sh
|
curl -fsSL https://ollama.com/install.sh | sudo -E sh
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install --assume-yes build-essential curl
|
apt-get install --assume-yes build-essential curl
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Run cargo tree without default features and check lindera is not present
|
- name: Run cargo tree without default features and check lindera is not present
|
||||||
run: |
|
run: |
|
||||||
if cargo tree -f '{p} {f}' -e normal --no-default-features | grep -qz lindera; then
|
if cargo tree -f '{p} {f}' -e normal --no-default-features | grep -qz lindera; then
|
||||||
@@ -153,9 +153,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl
|
apt-get update && apt-get install -y curl
|
||||||
apt-get install build-essential -y
|
apt-get install build-essential -y
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.7.7
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- name: Run tests in debug
|
- name: Run tests in debug
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
@@ -167,12 +167,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
components: clippy
|
components: clippy
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.7.7
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- name: Run cargo clippy
|
- name: Run cargo clippy
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
@@ -184,14 +184,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: nightly-2024-07-09
|
toolchain: nightly-2024-07-09
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: Swatinem/rust-cache@v2.7.7
|
uses: Swatinem/rust-cache@v2.8.0
|
||||||
- name: Run cargo fmt
|
- name: Run cargo fmt
|
||||||
# Since we never ran the `build.rs` script in the benchmark directory we are missing one auto-generated import file.
|
# Since we never ran the `build.rs` script in the benchmark directory we are missing one auto-generated import file.
|
||||||
# Since we want to trigger (and fail) this action as fast as possible, instead of building the benchmark crate
|
# Since we want to trigger (and fail) this action as fast as possible, instead of building the benchmark crate
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
new_version:
|
new_version:
|
||||||
description: 'The new version (vX.Y.Z)'
|
description: "The new version (vX.Y.Z)"
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@1.81
|
- uses: dtolnay/rust-toolchain@1.85
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
- name: Install sd
|
- name: Install sd
|
||||||
@@ -41,5 +41,4 @@ jobs:
|
|||||||
--title "Update version for the next release ($NEW_VERSION) in Cargo.toml" \
|
--title "Update version for the next release ($NEW_VERSION) in Cargo.toml" \
|
||||||
--body '⚠️ This PR is automatically generated. Check the new version is the expected one and Cargo.lock has been updated before merging.' \
|
--body '⚠️ This PR is automatically generated. Check the new version is the expected one and Cargo.lock has been updated before merging.' \
|
||||||
--label 'skip changelog' \
|
--label 'skip changelog' \
|
||||||
--milestone $NEW_VERSION \
|
|
||||||
--base $GITHUB_REF_NAME
|
--base $GITHUB_REF_NAME
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -5,18 +5,27 @@
|
|||||||
**/*.json_lines
|
**/*.json_lines
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
/*.mdb
|
/*.mdb
|
||||||
/data.ms
|
/*.ms
|
||||||
/snapshots
|
/snapshots
|
||||||
/dumps
|
/dumps
|
||||||
/bench
|
/bench
|
||||||
/_xtask_benchmark.ms
|
/_xtask_benchmark.ms
|
||||||
/benchmarks
|
/benchmarks
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Snapshots
|
# Snapshots
|
||||||
## ... large
|
## ... large
|
||||||
*.full.snap
|
*.full.snap
|
||||||
## ... unreviewed
|
## ... unreviewed
|
||||||
*.snap.new
|
*.snap.new
|
||||||
|
## ... pending
|
||||||
|
*.pending-snap
|
||||||
|
|
||||||
|
# Tmp files
|
||||||
|
.tmp*
|
||||||
|
|
||||||
|
# Database snapshot
|
||||||
|
crates/meilisearch/db.snapshot
|
||||||
|
|
||||||
# Fuzzcheck data for the facet indexing fuzz test
|
# Fuzzcheck data for the facet indexing fuzz test
|
||||||
crates/milli/fuzz/update::facet::incremental::fuzz::fuzz/
|
crates/milli/fuzz/update::facet::incremental::fuzz::fuzz/
|
||||||
|
|||||||
@@ -57,9 +57,17 @@ This command will be triggered to each PR as a requirement for merging it.
|
|||||||
You can set the `LINDERA_CACHE` environment variable to speed up your successive builds by up to 2 minutes.
|
You can set the `LINDERA_CACHE` environment variable to speed up your successive builds by up to 2 minutes.
|
||||||
It'll store some built artifacts in the directory of your choice.
|
It'll store some built artifacts in the directory of your choice.
|
||||||
|
|
||||||
We recommend using the standard `$HOME/.cache/lindera` directory:
|
We recommend using the `$HOME/.cache/meili/lindera` directory:
|
||||||
```sh
|
```sh
|
||||||
export LINDERA_CACHE=$HOME/.cache/lindera
|
export LINDERA_CACHE=$HOME/.cache/meili/lindera
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the `MILLI_BENCH_DATASETS_PATH` environment variable to further speed up your builds.
|
||||||
|
It'll store some big files used for the benchmarks in the directory of your choice.
|
||||||
|
|
||||||
|
We recommend using the `$HOME/.cache/meili/benches` directory:
|
||||||
|
```sh
|
||||||
|
export MILLI_BENCH_DATASETS_PATH=$HOME/.cache/meili/benches
|
||||||
```
|
```
|
||||||
|
|
||||||
Furthermore, you can improve incremental compilation by setting the `MEILI_NO_VERGEN` environment variable.
|
Furthermore, you can improve incremental compilation by setting the `MEILI_NO_VERGEN` environment variable.
|
||||||
@@ -98,7 +106,19 @@ Run `cargo xtask --help` from the root of the repository to find out what is ava
|
|||||||
#### Update the openAPI file if the API changed
|
#### Update the openAPI file if the API changed
|
||||||
|
|
||||||
To update the openAPI file in the code, see [sprint_issue.md](https://github.com/meilisearch/meilisearch/blob/main/.github/ISSUE_TEMPLATE/sprint_issue.md#reminders-when-modifying-the-api).
|
To update the openAPI file in the code, see [sprint_issue.md](https://github.com/meilisearch/meilisearch/blob/main/.github/ISSUE_TEMPLATE/sprint_issue.md#reminders-when-modifying-the-api).
|
||||||
If you want to update the openAPI file on the [open-api repository](https://github.com/meilisearch/open-api), see [update-openapi-issue.md](https://github.com/meilisearch/engine-team/blob/main/issue-templates/update-openapi-issue.md).
|
|
||||||
|
If you want to generate OpenAPI file manually:
|
||||||
|
|
||||||
|
With swagger:
|
||||||
|
- Starts Meilisearch with the `swagger` feature flag: `cargo run --features swagger`
|
||||||
|
- On a browser, open the following URL: http://localhost:7700/scalar
|
||||||
|
- Click the « Download openAPI file »
|
||||||
|
|
||||||
|
With the internal crate:
|
||||||
|
```bash
|
||||||
|
cd crates/openapi-generator
|
||||||
|
cargo run --release -- --pretty --output meilisearch.json
|
||||||
|
```
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
@@ -152,25 +172,37 @@ Some notes on GitHub PRs:
|
|||||||
The draft PRs are recommended when you want to show that you are working on something and make your work visible.
|
The draft PRs are recommended when you want to show that you are working on something and make your work visible.
|
||||||
- The branch related to the PR must be **up-to-date with `main`** before merging. Fortunately, this project uses [GitHub Merge Queues](https://github.blog/news-insights/product-news/github-merge-queue-is-generally-available/) to automatically enforce this requirement without the PR author having to rebase manually.
|
- The branch related to the PR must be **up-to-date with `main`** before merging. Fortunately, this project uses [GitHub Merge Queues](https://github.blog/news-insights/product-news/github-merge-queue-is-generally-available/) to automatically enforce this requirement without the PR author having to rebase manually.
|
||||||
|
|
||||||
## Release Process (for internal team only)
|
## Merging PRs
|
||||||
|
|
||||||
Meilisearch tools follow the [Semantic Versioning Convention](https://semver.org/).
|
|
||||||
|
|
||||||
### Automation to rebase and Merge the PRs
|
|
||||||
|
|
||||||
This project uses GitHub Merge Queues that helps us manage pull requests merging.
|
This project uses GitHub Merge Queues that helps us manage pull requests merging.
|
||||||
|
|
||||||
### How to Publish a new Release
|
Before merging a PR, the maintainer should ensure the following requirements are met
|
||||||
|
- Automated tests have been added.
|
||||||
|
- If some tests cannot be automated, manual rigorous tests should be applied.
|
||||||
|
- ⚠️ If there is an change in the DB: it's mandatory to manually test the `--experimental-dumpless-upgrade` on a DB of the previous Meilisearch minor version (e.g. v1.13 for the v1.14 release).
|
||||||
|
- If necessary, the feature have been tested in the Cloud production environment (with [prototypes](./documentation/prototypes.md)) and the Cloud UI is ready.
|
||||||
|
- If necessary, the [documentation](https://github.com/meilisearch/documentation) related to the implemented feature in the PR is ready.
|
||||||
|
- If necessary, the [integrations](https://github.com/meilisearch/integration-guides) related to the implemented feature in the PR are ready.
|
||||||
|
|
||||||
The full Meilisearch release process is described in [this guide](https://github.com/meilisearch/engine-team/blob/main/resources/meilisearch-release.md). Please follow it carefully before doing any release.
|
## Publish Process (for internal team only)
|
||||||
|
|
||||||
|
Meilisearch tools follow the [Semantic Versioning Convention](https://semver.org/).
|
||||||
|
|
||||||
|
### How to publish a new release
|
||||||
|
|
||||||
|
The full Meilisearch release process is described in [this guide](./documentation/release.md).
|
||||||
|
|
||||||
### How to publish a prototype
|
### How to publish a prototype
|
||||||
|
|
||||||
Depending on the developed feature, you might need to provide a prototyped version of Meilisearch to make it easier to test by the users.
|
Depending on the developed feature, you might need to provide a prototyped version of Meilisearch to make it easier to test by the users.
|
||||||
|
|
||||||
This happens in two steps:
|
This happens in two steps:
|
||||||
- [Release the prototype](https://github.com/meilisearch/engine-team/blob/main/resources/prototypes.md#how-to-publish-a-prototype)
|
- [Release the prototype](./documentation/prototypes.md#how-to-publish-a-prototype)
|
||||||
- [Communicate about it](https://github.com/meilisearch/engine-team/blob/main/resources/prototypes.md#communication)
|
- [Communicate about it](./documentation/prototypes.md#communication)
|
||||||
|
|
||||||
|
### How to implement and publish an experimental feature
|
||||||
|
|
||||||
|
Here is our [guidelines and process](./documentation/experimental-features.md) to implement and publish an experimental feature.
|
||||||
|
|
||||||
### Release assets
|
### Release assets
|
||||||
|
|
||||||
|
|||||||
3392
Cargo.lock
generated
3392
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,11 @@ members = [
|
|||||||
"crates/tracing-trace",
|
"crates/tracing-trace",
|
||||||
"crates/xtask",
|
"crates/xtask",
|
||||||
"crates/build-info",
|
"crates/build-info",
|
||||||
|
"crates/openapi-generator",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "1.14.0"
|
version = "1.19.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>",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Compile
|
# Compile
|
||||||
FROM rust:1.81.0-alpine3.20 AS compiler
|
FROM rust:1.85-alpine3.20 AS compiler
|
||||||
|
|
||||||
RUN apk add -q --no-cache build-base openssl-dev
|
RUN apk add -q --no-cache build-base openssl-dev
|
||||||
|
|
||||||
|
|||||||
8
LICENSE
8
LICENSE
@@ -19,3 +19,11 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
🔒 Meilisearch Enterprise Edition (EE)
|
||||||
|
|
||||||
|
Certain parts of this codebase are not licensed under the MIT license and governed by the Business Source License 1.1.
|
||||||
|
|
||||||
|
See the LICENSE-EE file for details.
|
||||||
|
|||||||
67
LICENSE-EE
Normal file
67
LICENSE-EE
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
Business Source License 1.1 – Adapted for Meili SAS
|
||||||
|
This license is based on the Business Source License version 1.1, as published by MariaDB Corporation Ab.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
|
||||||
|
Licensor: Meili SAS
|
||||||
|
|
||||||
|
Licensed Work: Any file explicitly marked as “Enterprise Edition (EE)” or “governed by the Business Source License” residing in enterprise_editions modules/folders.
|
||||||
|
|
||||||
|
Additional Use Grant:
|
||||||
|
You may use, modify, and distribute the Licensed Work for non-production purposes only, such as testing, development, or evaluation.
|
||||||
|
|
||||||
|
Production use of the Licensed Work requires a commercial license agreement with Meilisearch. Contact bonjour@meilisearch.com for licensing.
|
||||||
|
|
||||||
|
Change License: MIT
|
||||||
|
|
||||||
|
Change Date: Four years from the date the Licensed Work is published.
|
||||||
|
|
||||||
|
This License does not apply to any code outside of the Licensed Work, which remains under the MIT license.
|
||||||
|
|
||||||
|
For information about alternative licensing arrangements for the Licensed Work,
|
||||||
|
please contact bonjour@meilisearch.com or sales@meilisearch.com.
|
||||||
|
|
||||||
|
Notice
|
||||||
|
|
||||||
|
Business Source License 1.1
|
||||||
|
|
||||||
|
Terms
|
||||||
|
|
||||||
|
The Licensor hereby grants you the right to copy, modify, create derivative
|
||||||
|
works, redistribute, and make non-production use of the Licensed Work. The
|
||||||
|
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
||||||
|
|
||||||
|
Effective on the Change Date, or the fourth anniversary of the first publicly
|
||||||
|
available distribution of a specific version of the Licensed Work under this
|
||||||
|
License, whichever comes first, the Licensor hereby grants you rights under
|
||||||
|
the terms of the Change License, and the rights granted in the paragraph
|
||||||
|
above terminate.
|
||||||
|
|
||||||
|
If your use of the Licensed Work does not comply with the requirements
|
||||||
|
currently in effect as described in this License, you must purchase a
|
||||||
|
commercial license from the Licensor, its affiliated entities, or authorized
|
||||||
|
resellers, or you must refrain from using the Licensed Work.
|
||||||
|
|
||||||
|
All copies of the original and modified Licensed Work, and derivative works
|
||||||
|
of the Licensed Work, are subject to this License. This License applies
|
||||||
|
separately for each version of the Licensed Work and the Change Date may vary
|
||||||
|
for each version of the Licensed Work released by Licensor.
|
||||||
|
|
||||||
|
You must conspicuously display this License on each original or modified copy
|
||||||
|
of the Licensed Work. If you receive the Licensed Work in original or
|
||||||
|
modified form from a third party, the terms and conditions set forth in this
|
||||||
|
License apply to your use of that work.
|
||||||
|
|
||||||
|
Any use of the Licensed Work in violation of this License will automatically
|
||||||
|
terminate your rights under this License for the current and all other
|
||||||
|
versions of the Licensed Work.
|
||||||
|
|
||||||
|
This License does not grant you any right in any trademark or logo of
|
||||||
|
Licensor or its affiliates (provided that you may use a trademark or logo of
|
||||||
|
Licensor as expressly required by this License).
|
||||||
|
|
||||||
|
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
||||||
|
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
||||||
|
TITLE.
|
||||||
26
README.md
26
README.md
@@ -41,7 +41,7 @@
|
|||||||
- [**Movies**](https://where2watch.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=organization) — An application to help you find streaming platforms to watch movies using [hybrid search](https://www.meilisearch.com/solutions/hybrid-search?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos).
|
- [**Movies**](https://where2watch.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=organization) — An application to help you find streaming platforms to watch movies using [hybrid search](https://www.meilisearch.com/solutions/hybrid-search?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos).
|
||||||
- [**Ecommerce**](https://ecommerce.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) — Ecommerce website using disjunctive [facets](https://www.meilisearch.com/docs/learn/fine_tuning_results/faceted_search?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos), range and rating filtering, and pagination.
|
- [**Ecommerce**](https://ecommerce.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) — Ecommerce website using disjunctive [facets](https://www.meilisearch.com/docs/learn/fine_tuning_results/faceted_search?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos), range and rating filtering, and pagination.
|
||||||
- [**Songs**](https://music.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) — Search through 47 million of songs.
|
- [**Songs**](https://music.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) — Search through 47 million of songs.
|
||||||
- [**SaaS**](https://saas.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) — Search for contacts, deals, and companies in this [multi-tenant](https://www.meilisearch.com/docs/learn/security/multitenancy_tenant_tokens?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) CRM application.
|
- [**SaaS**](https://saas.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) — Search for contacts, deals, and companies in this [multi-tenant](https://www.meilisearch.com/docs/learn/security/multitenancy_tenant_tokens?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=demos) CRM application.
|
||||||
|
|
||||||
See the list of all our example apps in our [demos repository](https://github.com/meilisearch/demos).
|
See the list of all our example apps in our [demos repository](https://github.com/meilisearch/demos).
|
||||||
|
|
||||||
@@ -89,6 +89,26 @@ We also offer a wide range of dedicated guides to all Meilisearch features, such
|
|||||||
|
|
||||||
Finally, for more in-depth information, refer to our articles explaining fundamental Meilisearch concepts such as [documents](https://www.meilisearch.com/docs/learn/core_concepts/documents?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=advanced) and [indexes](https://www.meilisearch.com/docs/learn/core_concepts/indexes?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=advanced).
|
Finally, for more in-depth information, refer to our articles explaining fundamental Meilisearch concepts such as [documents](https://www.meilisearch.com/docs/learn/core_concepts/documents?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=advanced) and [indexes](https://www.meilisearch.com/docs/learn/core_concepts/indexes?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=advanced).
|
||||||
|
|
||||||
|
## 🧾 Editions & Licensing
|
||||||
|
|
||||||
|
Meilisearch is available in two editions:
|
||||||
|
|
||||||
|
### 🧪 Community Edition (CE)
|
||||||
|
|
||||||
|
- Fully open source under the [MIT license](./LICENSE)
|
||||||
|
- Core search engine with fast and relevant full-text, semantic or hybrid search
|
||||||
|
- Free to use for anyone, including commercial usage
|
||||||
|
|
||||||
|
### 🏢 Enterprise Edition (EE)
|
||||||
|
|
||||||
|
- Includes advanced features such as:
|
||||||
|
- Sharding
|
||||||
|
- Governed by a [commercial license](./LICENSE-EE) or the [Business Source License 1.1](https://mariadb.com/bsl11)
|
||||||
|
- Not allowed in production without a commercial agreement with Meilisearch.
|
||||||
|
- You may use, modify, and distribute the Licensed Work for non-production purposes only, such as testing, development, or evaluation.
|
||||||
|
|
||||||
|
Want access to Enterprise features? → Contact us at [sales@meilisearch.com](maito:sales@meilisearch.com).
|
||||||
|
|
||||||
## 📊 Telemetry
|
## 📊 Telemetry
|
||||||
|
|
||||||
Meilisearch collects **anonymized** user data to help us improve our product. You can [deactivate this](https://www.meilisearch.com/docs/learn/what_is_meilisearch/telemetry?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=telemetry#how-to-disable-data-collection) whenever you want.
|
Meilisearch collects **anonymized** user data to help us improve our product. You can [deactivate this](https://www.meilisearch.com/docs/learn/what_is_meilisearch/telemetry?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=telemetry#how-to-disable-data-collection) whenever you want.
|
||||||
@@ -99,7 +119,7 @@ If you want to know more about the kind of data we collect and what we use it fo
|
|||||||
|
|
||||||
## 📫 Get in touch!
|
## 📫 Get in touch!
|
||||||
|
|
||||||
Meilisearch is a search engine created by [Meili]([https://www.welcometothejungle.com/en/companies/meilisearch](https://www.meilisearch.com/careers)), a software development company headquartered in France and with team members all over the world. Want to know more about us? [Check out our blog!](https://blog.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=contact)
|
Meilisearch is a search engine created by [Meili](https://www.meilisearch.com/careers), a software development company headquartered in France and with team members all over the world. Want to know more about us? [Check out our blog!](https://blog.meilisearch.com/?utm_campaign=oss&utm_source=github&utm_medium=meilisearch&utm_content=contact)
|
||||||
|
|
||||||
🗞 [Subscribe to our newsletter](https://meilisearch.us2.list-manage.com/subscribe?u=27870f7b71c908a8b359599fb&id=79582d828e) if you don't want to miss any updates! We promise we won't clutter your mailbox: we only send one edition every two months.
|
🗞 [Subscribe to our newsletter](https://meilisearch.us2.list-manage.com/subscribe?u=27870f7b71c908a8b359599fb&id=79582d828e) if you don't want to miss any updates! We promise we won't clutter your mailbox: we only send one edition every two months.
|
||||||
|
|
||||||
@@ -119,6 +139,6 @@ Meilisearch is, and will always be, open-source! If you want to contribute to th
|
|||||||
|
|
||||||
Meilisearch releases and their associated binaries are available on the project's [releases page](https://github.com/meilisearch/meilisearch/releases).
|
Meilisearch releases and their associated binaries are available on the project's [releases page](https://github.com/meilisearch/meilisearch/releases).
|
||||||
|
|
||||||
The binaries are versioned following [SemVer conventions](https://semver.org/). To know more, read our [versioning policy](https://github.com/meilisearch/engine-team/blob/main/resources/versioning-policy.md).
|
The binaries are versioned following [SemVer conventions](https://semver.org/). To know more, read our [versioning policy](./documentation/versioning-policy.md).
|
||||||
|
|
||||||
Differently from the binaries, crates in this repository are not currently available on [crates.io](https://crates.io/) and do not follow [SemVer conventions](https://semver.org).
|
Differently from the binaries, crates in this repository are not currently available on [crates.io](https://crates.io/) and do not follow [SemVer conventions](https://semver.org).
|
||||||
|
|||||||
@@ -1501,6 +1501,300 @@
|
|||||||
"title": "Task queue latency",
|
"title": "Task queue latency",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 15,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "none"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 11,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 51
|
||||||
|
},
|
||||||
|
"id": 29,
|
||||||
|
"interval": "5s",
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "right",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "8.1.4",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"editorMode": "builder",
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "meilisearch_task_queue_used_size{instance=\"$instance\", job=\"$job\"}",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{value}} ",
|
||||||
|
"range": true,
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Task queue used size in bytes",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 15,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "none"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 11,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 51
|
||||||
|
},
|
||||||
|
"id": 29,
|
||||||
|
"interval": "5s",
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "right",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "8.1.4",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"editorMode": "builder",
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "meilisearch_task_queue_size_until_stop_registering{instance=\"$instance\", job=\"$job\"}",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{value}} ",
|
||||||
|
"range": true,
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Task queue available size until it stop receiving tasks.",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 15,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "none"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 11,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 51
|
||||||
|
},
|
||||||
|
"id": 29,
|
||||||
|
"interval": "5s",
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "right",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "8.1.4",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"editorMode": "builder",
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "meilisearch_task_queue_max_size{instance=\"$instance\", job=\"$job\"}",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "{{value}} ",
|
||||||
|
"range": true,
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Task queue maximum possible size",
|
||||||
|
"type": "stat"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"collapsed": true,
|
"collapsed": true,
|
||||||
"datasource": {
|
"datasource": {
|
||||||
|
|||||||
BIN
assets/ph-banner.png
Normal file
BIN
assets/ph-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 578 KiB |
@@ -11,27 +11,27 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.98"
|
||||||
bumpalo = "3.16.0"
|
bumpalo = "3.18.1"
|
||||||
csv = "1.3.1"
|
csv = "1.3.1"
|
||||||
memmap2 = "0.9.5"
|
memmap2 = "0.9.7"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
mimalloc = { version = "0.1.43", default-features = false }
|
mimalloc = { version = "0.1.47", default-features = false }
|
||||||
serde_json = { version = "1.0.135", features = ["preserve_order"] }
|
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
||||||
tempfile = "3.15.0"
|
tempfile = "3.20.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
criterion = { version = "0.6.0", features = ["html_reports"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rand_chacha = "0.3.1"
|
rand_chacha = "0.3.1"
|
||||||
roaring = "0.10.10"
|
roaring = "0.10.12"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.98"
|
||||||
bytes = "1.9.0"
|
bytes = "1.10.1"
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.8.0"
|
||||||
flate2 = "1.0.35"
|
flate2 = "1.1.2"
|
||||||
reqwest = { version = "0.12.12", features = ["blocking", "rustls-tls"], default-features = false }
|
reqwest = { version = "0.12.20", features = ["blocking", "rustls-tls"], default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["milli/all-tokenizations"]
|
default = ["milli/all-tokenizations"]
|
||||||
@@ -51,3 +51,11 @@ harness = false
|
|||||||
[[bench]]
|
[[bench]]
|
||||||
name = "indexing"
|
name = "indexing"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "sort"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "filter_starts_with"
|
||||||
|
harness = false
|
||||||
|
|||||||
66
crates/benchmarks/benches/filter_starts_with.rs
Normal file
66
crates/benchmarks/benches/filter_starts_with.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
mod datasets_paths;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use criterion::{criterion_group, criterion_main};
|
||||||
|
use milli::update::Settings;
|
||||||
|
use milli::FilterableAttributesRule;
|
||||||
|
use utils::Conf;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
fn base_conf(builder: &mut Settings) {
|
||||||
|
let displayed_fields = ["geonameid", "name"].iter().map(|s| s.to_string()).collect();
|
||||||
|
builder.set_displayed_fields(displayed_fields);
|
||||||
|
|
||||||
|
let filterable_fields =
|
||||||
|
["name"].iter().map(|s| FilterableAttributesRule::Field(s.to_string())).collect();
|
||||||
|
builder.set_filterable_fields(filterable_fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
const BASE_CONF: Conf = Conf {
|
||||||
|
dataset: datasets_paths::SMOL_ALL_COUNTRIES,
|
||||||
|
dataset_format: "jsonl",
|
||||||
|
queries: &[
|
||||||
|
"",
|
||||||
|
],
|
||||||
|
configure: base_conf,
|
||||||
|
primary_key: Some("geonameid"),
|
||||||
|
..Conf::BASE
|
||||||
|
};
|
||||||
|
|
||||||
|
fn filter_starts_with(c: &mut criterion::Criterion) {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let confs = &[
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "1 letter",
|
||||||
|
filter: Some("name STARTS WITH e"),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "2 letters",
|
||||||
|
filter: Some("name STARTS WITH es"),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "3 letters",
|
||||||
|
filter: Some("name STARTS WITH est"),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "6 letters",
|
||||||
|
filter: Some("name STARTS WITH estoni"),
|
||||||
|
..BASE_CONF
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
utils::run_benches(c, confs);
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, filter_starts_with);
|
||||||
|
criterion_main!(benches);
|
||||||
@@ -11,7 +11,7 @@ use milli::heed::{EnvOpenOptions, RwTxn};
|
|||||||
use milli::progress::Progress;
|
use milli::progress::Progress;
|
||||||
use milli::update::new::indexer;
|
use milli::update::new::indexer;
|
||||||
use milli::update::{IndexerConfig, Settings};
|
use milli::update::{IndexerConfig, Settings};
|
||||||
use milli::vector::EmbeddingConfigs;
|
use milli::vector::RuntimeEmbedders;
|
||||||
use milli::{FilterableAttributesRule, Index};
|
use milli::{FilterableAttributesRule, Index};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand_chacha::rand_core::SeedableRng;
|
use rand_chacha::rand_core::SeedableRng;
|
||||||
@@ -65,7 +65,7 @@ fn setup_settings<'t>(
|
|||||||
let sortable_fields = sortable_fields.iter().map(|s| s.to_string()).collect();
|
let sortable_fields = sortable_fields.iter().map(|s| s.to_string()).collect();
|
||||||
builder.set_sortable_fields(sortable_fields);
|
builder.set_sortable_fields(sortable_fields);
|
||||||
|
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(&|| false, &Progress::default(), Default::default()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_index_with_settings(
|
fn setup_index_with_settings(
|
||||||
@@ -154,6 +154,7 @@ fn indexing_songs_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -166,9 +167,10 @@ fn indexing_songs_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -220,6 +222,7 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -232,9 +235,10 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -264,6 +268,7 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -276,9 +281,10 @@ fn reindexing_songs_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -332,6 +338,7 @@ fn deleting_songs_in_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -344,9 +351,10 @@ fn deleting_songs_in_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -408,6 +416,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -420,9 +429,10 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -452,6 +462,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -464,9 +475,10 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -492,6 +504,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -504,9 +517,10 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -559,6 +573,7 @@ fn indexing_songs_without_faceted_numbers(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -571,9 +586,10 @@ fn indexing_songs_without_faceted_numbers(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -625,6 +641,7 @@ fn indexing_songs_without_faceted_fields(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -637,9 +654,10 @@ fn indexing_songs_without_faceted_fields(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -691,6 +709,7 @@ fn indexing_wiki(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -703,9 +722,10 @@ fn indexing_wiki(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -756,6 +776,7 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -768,9 +789,10 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -800,6 +822,7 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -812,9 +835,10 @@ fn reindexing_wiki(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -867,6 +891,7 @@ fn deleting_wiki_in_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -879,9 +904,10 @@ fn deleting_wiki_in_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -943,6 +969,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -955,9 +982,10 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -988,6 +1016,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1000,9 +1029,10 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1029,6 +1059,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1041,9 +1072,10 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1095,6 +1127,7 @@ fn indexing_movies_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1107,9 +1140,10 @@ fn indexing_movies_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1160,6 +1194,7 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1172,9 +1207,10 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1204,6 +1240,7 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1216,9 +1253,10 @@ fn reindexing_movies_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1271,6 +1309,7 @@ fn deleting_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1283,9 +1322,10 @@ fn deleting_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1331,9 +1371,10 @@ fn delete_documents_from_ids(index: Index, document_ids_to_delete: Vec<RoaringBi
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
Some(primary_key),
|
Some(primary_key),
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1383,6 +1424,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1395,9 +1437,10 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1427,6 +1470,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1439,9 +1483,10 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1467,6 +1512,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1479,9 +1525,10 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1556,6 +1603,7 @@ fn indexing_nested_movies_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1568,9 +1616,10 @@ fn indexing_nested_movies_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1646,6 +1695,7 @@ fn deleting_nested_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1658,9 +1708,10 @@ fn deleting_nested_movies_in_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1728,6 +1779,7 @@ fn indexing_nested_movies_without_faceted_fields(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1740,9 +1792,10 @@ fn indexing_nested_movies_without_faceted_fields(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1794,6 +1847,7 @@ fn indexing_geo(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1806,9 +1860,10 @@ fn indexing_geo(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1859,6 +1914,7 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1871,9 +1927,10 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1903,6 +1960,7 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1915,9 +1973,10 @@ fn reindexing_geo(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1970,6 +2029,7 @@ fn deleting_geo_in_batches_default(c: &mut Criterion) {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1982,9 +2042,10 @@ fn deleting_geo_in_batches_default(c: &mut Criterion) {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ mod datasets_paths;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main};
|
use criterion::{criterion_group, criterion_main};
|
||||||
use milli::{update::Settings, FilterableAttributesRule};
|
use milli::update::Settings;
|
||||||
|
use milli::FilterableAttributesRule;
|
||||||
use utils::Conf;
|
use utils::Conf;
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ mod datasets_paths;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main};
|
use criterion::{criterion_group, criterion_main};
|
||||||
use milli::{update::Settings, FilterableAttributesRule};
|
use milli::update::Settings;
|
||||||
|
use milli::FilterableAttributesRule;
|
||||||
use utils::Conf;
|
use utils::Conf;
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
|
|||||||
114
crates/benchmarks/benches/sort.rs
Normal file
114
crates/benchmarks/benches/sort.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
//! This benchmark module is used to compare the performance of sorting documents in /search VS /documents
|
||||||
|
//!
|
||||||
|
//! The tests/benchmarks were designed in the context of a query returning only 20 documents.
|
||||||
|
|
||||||
|
mod datasets_paths;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use criterion::{criterion_group, criterion_main};
|
||||||
|
use milli::update::Settings;
|
||||||
|
use utils::Conf;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
fn base_conf(builder: &mut Settings) {
|
||||||
|
let displayed_fields =
|
||||||
|
["geonameid", "name", "asciiname", "alternatenames", "_geo", "population"]
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
builder.set_displayed_fields(displayed_fields);
|
||||||
|
|
||||||
|
let sortable_fields =
|
||||||
|
["_geo", "name", "population", "elevation", "timezone", "modification-date"]
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
builder.set_sortable_fields(sortable_fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
const BASE_CONF: Conf = Conf {
|
||||||
|
dataset: datasets_paths::SMOL_ALL_COUNTRIES,
|
||||||
|
dataset_format: "jsonl",
|
||||||
|
configure: base_conf,
|
||||||
|
primary_key: Some("geonameid"),
|
||||||
|
queries: &[""],
|
||||||
|
offsets: &[
|
||||||
|
Some((0, 20)), // The most common query in the real world
|
||||||
|
Some((0, 500)), // A query that ranges over many documents
|
||||||
|
Some((980, 20)), // The worst query that could happen in the real world
|
||||||
|
Some((800_000, 20)) // The worst query
|
||||||
|
],
|
||||||
|
get_documents: true,
|
||||||
|
..Conf::BASE
|
||||||
|
};
|
||||||
|
|
||||||
|
fn bench_sort(c: &mut criterion::Criterion) {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let confs = &[
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "without sort",
|
||||||
|
sort: None,
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many different values",
|
||||||
|
sort: Some(vec!["name:asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many similar values",
|
||||||
|
sort: Some(vec!["timezone:desc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many similar then different values",
|
||||||
|
sort: Some(vec!["timezone:desc", "name:asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many different then similar values",
|
||||||
|
sort: Some(vec!["timezone:desc", "name:asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "geo sort",
|
||||||
|
sample_size: Some(10),
|
||||||
|
sort: Some(vec!["_geoPoint(45.4777599, 9.1967508):asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many similar values then geo sort",
|
||||||
|
sample_size: Some(50),
|
||||||
|
sort: Some(vec!["timezone:desc", "_geoPoint(45.4777599, 9.1967508):asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many different values then geo sort",
|
||||||
|
sample_size: Some(50),
|
||||||
|
sort: Some(vec!["name:desc", "_geoPoint(45.4777599, 9.1967508):asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
|
||||||
|
utils::Conf {
|
||||||
|
group_name: "sort on many fields",
|
||||||
|
sort: Some(vec!["population:asc", "name:asc", "elevation:asc", "timezone:asc"]),
|
||||||
|
..BASE_CONF
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
utils::run_benches(c, confs);
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_sort);
|
||||||
|
criterion_main!(benches);
|
||||||
@@ -9,11 +9,12 @@ use anyhow::Context;
|
|||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use criterion::BenchmarkId;
|
use criterion::BenchmarkId;
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
|
use milli::documents::sort::recursive_sort;
|
||||||
use milli::heed::EnvOpenOptions;
|
use milli::heed::EnvOpenOptions;
|
||||||
use milli::progress::Progress;
|
use milli::progress::Progress;
|
||||||
use milli::update::new::indexer;
|
use milli::update::new::indexer;
|
||||||
use milli::update::{IndexerConfig, Settings};
|
use milli::update::{IndexerConfig, Settings};
|
||||||
use milli::vector::EmbeddingConfigs;
|
use milli::vector::RuntimeEmbedders;
|
||||||
use milli::{Criterion, Filter, Index, Object, TermsMatchingStrategy};
|
use milli::{Criterion, Filter, Index, Object, TermsMatchingStrategy};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@@ -35,6 +36,12 @@ pub struct Conf<'a> {
|
|||||||
pub configure: fn(&mut Settings),
|
pub configure: fn(&mut Settings),
|
||||||
pub filter: Option<&'a str>,
|
pub filter: Option<&'a str>,
|
||||||
pub sort: Option<Vec<&'a str>>,
|
pub sort: Option<Vec<&'a str>>,
|
||||||
|
/// set to skip documents (offset, limit)
|
||||||
|
pub offsets: &'a [Option<(usize, usize)>],
|
||||||
|
/// enable if you want to bench getting documents without querying
|
||||||
|
pub get_documents: bool,
|
||||||
|
/// configure the benchmark sample size
|
||||||
|
pub sample_size: Option<usize>,
|
||||||
/// enable or disable the optional words on the query
|
/// enable or disable the optional words on the query
|
||||||
pub optional_words: bool,
|
pub optional_words: bool,
|
||||||
/// primary key, if there is None we'll auto-generate docids for every documents
|
/// primary key, if there is None we'll auto-generate docids for every documents
|
||||||
@@ -52,6 +59,9 @@ impl Conf<'_> {
|
|||||||
configure: |_| (),
|
configure: |_| (),
|
||||||
filter: None,
|
filter: None,
|
||||||
sort: None,
|
sort: None,
|
||||||
|
offsets: &[None],
|
||||||
|
get_documents: false,
|
||||||
|
sample_size: None,
|
||||||
optional_words: true,
|
optional_words: true,
|
||||||
primary_key: None,
|
primary_key: None,
|
||||||
};
|
};
|
||||||
@@ -90,7 +100,7 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
|
|
||||||
(conf.configure)(&mut builder);
|
(conf.configure)(&mut builder);
|
||||||
|
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(&|| false, &Progress::default(), Default::default()).unwrap();
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
|
|
||||||
let config = IndexerConfig::default();
|
let config = IndexerConfig::default();
|
||||||
@@ -113,6 +123,7 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -125,9 +136,10 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
new_fields_ids_map,
|
new_fields_ids_map,
|
||||||
primary_key,
|
primary_key,
|
||||||
&document_changes,
|
&document_changes,
|
||||||
EmbeddingConfigs::default(),
|
RuntimeEmbedders::default(),
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -144,25 +156,79 @@ pub fn run_benches(c: &mut criterion::Criterion, confs: &[Conf]) {
|
|||||||
let file_name = Path::new(conf.dataset).file_name().and_then(|f| f.to_str()).unwrap();
|
let file_name = Path::new(conf.dataset).file_name().and_then(|f| f.to_str()).unwrap();
|
||||||
let name = format!("{}: {}", file_name, conf.group_name);
|
let name = format!("{}: {}", file_name, conf.group_name);
|
||||||
let mut group = c.benchmark_group(&name);
|
let mut group = c.benchmark_group(&name);
|
||||||
|
if let Some(sample_size) = conf.sample_size {
|
||||||
|
group.sample_size(sample_size);
|
||||||
|
}
|
||||||
|
|
||||||
for &query in conf.queries {
|
for &query in conf.queries {
|
||||||
group.bench_with_input(BenchmarkId::from_parameter(query), &query, |b, &query| {
|
for offset in conf.offsets {
|
||||||
b.iter(|| {
|
let parameter = match offset {
|
||||||
let rtxn = index.read_txn().unwrap();
|
None => query.to_string(),
|
||||||
let mut search = index.search(&rtxn);
|
Some((offset, limit)) => format!("{query}[{offset}:{limit}]"),
|
||||||
search.query(query).terms_matching_strategy(TermsMatchingStrategy::default());
|
};
|
||||||
if let Some(filter) = conf.filter {
|
group.bench_with_input(
|
||||||
let filter = Filter::from_str(filter).unwrap().unwrap();
|
BenchmarkId::from_parameter(parameter),
|
||||||
search.filter(filter);
|
&query,
|
||||||
}
|
|b, &query| {
|
||||||
if let Some(sort) = &conf.sort {
|
b.iter(|| {
|
||||||
let sort = sort.iter().map(|sort| sort.parse().unwrap()).collect();
|
let rtxn = index.read_txn().unwrap();
|
||||||
search.sort_criteria(sort);
|
let mut search = index.search(&rtxn);
|
||||||
}
|
search
|
||||||
let _ids = search.execute().unwrap();
|
.query(query)
|
||||||
});
|
.terms_matching_strategy(TermsMatchingStrategy::default());
|
||||||
});
|
if let Some(filter) = conf.filter {
|
||||||
|
let filter = Filter::from_str(filter).unwrap().unwrap();
|
||||||
|
search.filter(filter);
|
||||||
|
}
|
||||||
|
if let Some(sort) = &conf.sort {
|
||||||
|
let sort = sort.iter().map(|sort| sort.parse().unwrap()).collect();
|
||||||
|
search.sort_criteria(sort);
|
||||||
|
}
|
||||||
|
if let Some((offset, limit)) = offset {
|
||||||
|
search.offset(*offset).limit(*limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ids = search.execute().unwrap();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.get_documents {
|
||||||
|
for offset in conf.offsets {
|
||||||
|
let parameter = match offset {
|
||||||
|
None => String::from("get_documents"),
|
||||||
|
Some((offset, limit)) => format!("get_documents[{offset}:{limit}]"),
|
||||||
|
};
|
||||||
|
group.bench_with_input(BenchmarkId::from_parameter(parameter), &(), |b, &()| {
|
||||||
|
b.iter(|| {
|
||||||
|
let rtxn = index.read_txn().unwrap();
|
||||||
|
if let Some(sort) = &conf.sort {
|
||||||
|
let sort = sort.iter().map(|sort| sort.parse().unwrap()).collect();
|
||||||
|
let all_docs = index.documents_ids(&rtxn).unwrap();
|
||||||
|
let facet_sort =
|
||||||
|
recursive_sort(&index, &rtxn, sort, &all_docs).unwrap();
|
||||||
|
let iter = facet_sort.iter().unwrap();
|
||||||
|
if let Some((offset, limit)) = offset {
|
||||||
|
let _results = iter.skip(*offset).take(*limit).collect::<Vec<_>>();
|
||||||
|
} else {
|
||||||
|
let _results = iter.collect::<Vec<_>>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let all_docs = index.documents_ids(&rtxn).unwrap();
|
||||||
|
if let Some((offset, limit)) = offset {
|
||||||
|
let _results =
|
||||||
|
all_docs.iter().skip(*offset).take(*limit).collect::<Vec<_>>();
|
||||||
|
} else {
|
||||||
|
let _results = all_docs.iter().collect::<Vec<_>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
group.finish();
|
group.finish();
|
||||||
|
|
||||||
index.prepare_for_closing().wait();
|
index.prepare_for_closing().wait();
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
writeln!(
|
writeln!(
|
||||||
&mut manifest_paths_file,
|
&mut manifest_paths_file,
|
||||||
r#"pub const {}: &str = {:?};"#,
|
r#"pub const {}: &str = {:?};"#,
|
||||||
dataset.to_case(Case::ScreamingSnake),
|
dataset.to_case(Case::UpperSnake),
|
||||||
out_file.display(),
|
out_file.display(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ license.workspace = true
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = { version = "0.3.37", features = ["parsing"] }
|
time = { version = "0.3.41", features = ["parsing"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.98"
|
||||||
vergen-git2 = "1.0.2"
|
vergen-git2 = "1.0.7"
|
||||||
|
|||||||
@@ -11,21 +11,21 @@ readme.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.98"
|
||||||
flate2 = "1.0.35"
|
flate2 = "1.1.2"
|
||||||
http = "1.2.0"
|
http = "1.3.1"
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
once_cell = "1.20.2"
|
once_cell = "1.21.3"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
roaring = { version = "0.10.10", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.135", features = ["preserve_order"] }
|
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
||||||
tar = "0.4.43"
|
tar = "0.4.44"
|
||||||
tempfile = "3.15.0"
|
tempfile = "3.20.0"
|
||||||
thiserror = "2.0.9"
|
thiserror = "2.0.12"
|
||||||
time = { version = "0.3.37", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
time = { version = "0.3.41", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
uuid = { version = "1.11.0", features = ["serde", "v4"] }
|
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![allow(clippy::wrong_self_convention)]
|
#![allow(clippy::wrong_self_convention)]
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use meilisearch_types::batches::BatchId;
|
use meilisearch_types::batches::BatchId;
|
||||||
|
use meilisearch_types::byte_unit::Byte;
|
||||||
use meilisearch_types::error::ResponseError;
|
use meilisearch_types::error::ResponseError;
|
||||||
use meilisearch_types::keys::Key;
|
use meilisearch_types::keys::Key;
|
||||||
use meilisearch_types::milli::update::IndexDocumentsMethod;
|
use meilisearch_types::milli::update::IndexDocumentsMethod;
|
||||||
use meilisearch_types::settings::Unchecked;
|
use meilisearch_types::settings::Unchecked;
|
||||||
use meilisearch_types::tasks::{Details, IndexSwap, KindWithContent, Status, Task, TaskId};
|
use meilisearch_types::tasks::{
|
||||||
|
Details, ExportIndexSettings, IndexSwap, KindWithContent, Status, Task, TaskId, TaskNetwork,
|
||||||
|
};
|
||||||
use meilisearch_types::InstanceUid;
|
use meilisearch_types::InstanceUid;
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -89,6 +94,8 @@ pub struct TaskDump {
|
|||||||
default
|
default
|
||||||
)]
|
)]
|
||||||
pub finished_at: Option<OffsetDateTime>,
|
pub finished_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub network: Option<TaskNetwork>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A `Kind` specific version made for the dump. If modified you may break the dump.
|
// A `Kind` specific version made for the dump. If modified you may break the dump.
|
||||||
@@ -124,6 +131,7 @@ pub enum KindDump {
|
|||||||
},
|
},
|
||||||
IndexUpdate {
|
IndexUpdate {
|
||||||
primary_key: Option<String>,
|
primary_key: Option<String>,
|
||||||
|
uid: Option<String>,
|
||||||
},
|
},
|
||||||
IndexSwap {
|
IndexSwap {
|
||||||
swaps: Vec<IndexSwap>,
|
swaps: Vec<IndexSwap>,
|
||||||
@@ -141,6 +149,12 @@ pub enum KindDump {
|
|||||||
instance_uid: Option<InstanceUid>,
|
instance_uid: Option<InstanceUid>,
|
||||||
},
|
},
|
||||||
SnapshotCreation,
|
SnapshotCreation,
|
||||||
|
Export {
|
||||||
|
url: String,
|
||||||
|
api_key: Option<String>,
|
||||||
|
payload_size: Option<Byte>,
|
||||||
|
indexes: BTreeMap<String, ExportIndexSettings>,
|
||||||
|
},
|
||||||
UpgradeDatabase {
|
UpgradeDatabase {
|
||||||
from: (u32, u32, u32),
|
from: (u32, u32, u32),
|
||||||
},
|
},
|
||||||
@@ -160,6 +174,7 @@ impl From<Task> for TaskDump {
|
|||||||
enqueued_at: task.enqueued_at,
|
enqueued_at: task.enqueued_at,
|
||||||
started_at: task.started_at,
|
started_at: task.started_at,
|
||||||
finished_at: task.finished_at,
|
finished_at: task.finished_at,
|
||||||
|
network: task.network,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,8 +214,8 @@ impl From<KindWithContent> for KindDump {
|
|||||||
KindWithContent::IndexCreation { primary_key, .. } => {
|
KindWithContent::IndexCreation { primary_key, .. } => {
|
||||||
KindDump::IndexCreation { primary_key }
|
KindDump::IndexCreation { primary_key }
|
||||||
}
|
}
|
||||||
KindWithContent::IndexUpdate { primary_key, .. } => {
|
KindWithContent::IndexUpdate { primary_key, new_index_uid: uid, .. } => {
|
||||||
KindDump::IndexUpdate { primary_key }
|
KindDump::IndexUpdate { primary_key, uid }
|
||||||
}
|
}
|
||||||
KindWithContent::IndexSwap { swaps } => KindDump::IndexSwap { swaps },
|
KindWithContent::IndexSwap { swaps } => KindDump::IndexSwap { swaps },
|
||||||
KindWithContent::TaskCancelation { query, tasks } => {
|
KindWithContent::TaskCancelation { query, tasks } => {
|
||||||
@@ -213,6 +228,15 @@ impl From<KindWithContent> for KindDump {
|
|||||||
KindDump::DumpCreation { keys, instance_uid }
|
KindDump::DumpCreation { keys, instance_uid }
|
||||||
}
|
}
|
||||||
KindWithContent::SnapshotCreation => KindDump::SnapshotCreation,
|
KindWithContent::SnapshotCreation => KindDump::SnapshotCreation,
|
||||||
|
KindWithContent::Export { url, api_key, payload_size, indexes } => KindDump::Export {
|
||||||
|
url,
|
||||||
|
api_key,
|
||||||
|
payload_size,
|
||||||
|
indexes: indexes
|
||||||
|
.into_iter()
|
||||||
|
.map(|(pattern, settings)| (pattern.to_string(), settings))
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
KindWithContent::UpgradeDatabase { from: version } => {
|
KindWithContent::UpgradeDatabase { from: version } => {
|
||||||
KindDump::UpgradeDatabase { from: version }
|
KindDump::UpgradeDatabase { from: version }
|
||||||
}
|
}
|
||||||
@@ -229,15 +253,16 @@ pub(crate) mod test {
|
|||||||
use big_s::S;
|
use big_s::S;
|
||||||
use maplit::{btreemap, btreeset};
|
use maplit::{btreemap, btreeset};
|
||||||
use meilisearch_types::batches::{Batch, BatchEnqueuedAt, BatchStats};
|
use meilisearch_types::batches::{Batch, BatchEnqueuedAt, BatchStats};
|
||||||
|
use meilisearch_types::enterprise_edition::network::{Network, Remote};
|
||||||
use meilisearch_types::facet_values_sort::FacetValuesSort;
|
use meilisearch_types::facet_values_sort::FacetValuesSort;
|
||||||
use meilisearch_types::features::{Network, Remote, RuntimeTogglableFeatures};
|
use meilisearch_types::features::RuntimeTogglableFeatures;
|
||||||
use meilisearch_types::index_uid_pattern::IndexUidPattern;
|
use meilisearch_types::index_uid_pattern::IndexUidPattern;
|
||||||
use meilisearch_types::keys::{Action, Key};
|
use meilisearch_types::keys::{Action, Key};
|
||||||
use meilisearch_types::milli::update::Setting;
|
use meilisearch_types::milli::update::Setting;
|
||||||
use meilisearch_types::milli::{self, FilterableAttributesRule};
|
use meilisearch_types::milli::{self, FilterableAttributesRule};
|
||||||
use meilisearch_types::settings::{Checked, FacetingSettings, Settings};
|
use meilisearch_types::settings::{Checked, FacetingSettings, Settings};
|
||||||
use meilisearch_types::task_view::DetailsView;
|
use meilisearch_types::task_view::DetailsView;
|
||||||
use meilisearch_types::tasks::{Details, Kind, Status};
|
use meilisearch_types::tasks::{BatchStopReason, Details, Kind, Status};
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
use time::macros::datetime;
|
use time::macros::datetime;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -305,6 +330,7 @@ pub(crate) mod test {
|
|||||||
localized_attributes: Setting::NotSet,
|
localized_attributes: Setting::NotSet,
|
||||||
facet_search: Setting::NotSet,
|
facet_search: Setting::NotSet,
|
||||||
prefix_search: Setting::NotSet,
|
prefix_search: Setting::NotSet,
|
||||||
|
chat: Setting::NotSet,
|
||||||
_kind: std::marker::PhantomData,
|
_kind: std::marker::PhantomData,
|
||||||
};
|
};
|
||||||
settings.check()
|
settings.check()
|
||||||
@@ -328,12 +354,14 @@ pub(crate) mod test {
|
|||||||
write_channel_congestion: None,
|
write_channel_congestion: None,
|
||||||
internal_database_sizes: Default::default(),
|
internal_database_sizes: Default::default(),
|
||||||
},
|
},
|
||||||
|
embedder_stats: Default::default(),
|
||||||
enqueued_at: Some(BatchEnqueuedAt {
|
enqueued_at: Some(BatchEnqueuedAt {
|
||||||
earliest: datetime!(2022-11-11 0:00 UTC),
|
earliest: datetime!(2022-11-11 0:00 UTC),
|
||||||
oldest: datetime!(2022-11-11 0:00 UTC),
|
oldest: datetime!(2022-11-11 0:00 UTC),
|
||||||
}),
|
}),
|
||||||
started_at: datetime!(2022-11-20 0:00 UTC),
|
started_at: datetime!(2022-11-20 0:00 UTC),
|
||||||
finished_at: Some(datetime!(2022-11-21 0:00 UTC)),
|
finished_at: Some(datetime!(2022-11-21 0:00 UTC)),
|
||||||
|
stop_reason: BatchStopReason::Unspecified.to_string(),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +388,7 @@ pub(crate) mod test {
|
|||||||
enqueued_at: datetime!(2022-11-11 0:00 UTC),
|
enqueued_at: datetime!(2022-11-11 0:00 UTC),
|
||||||
started_at: Some(datetime!(2022-11-20 0:00 UTC)),
|
started_at: Some(datetime!(2022-11-20 0:00 UTC)),
|
||||||
finished_at: Some(datetime!(2022-11-21 0:00 UTC)),
|
finished_at: Some(datetime!(2022-11-21 0:00 UTC)),
|
||||||
|
network: None,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
@@ -384,6 +413,7 @@ pub(crate) mod test {
|
|||||||
enqueued_at: datetime!(2022-11-11 0:00 UTC),
|
enqueued_at: datetime!(2022-11-11 0:00 UTC),
|
||||||
started_at: None,
|
started_at: None,
|
||||||
finished_at: None,
|
finished_at: None,
|
||||||
|
network: None,
|
||||||
},
|
},
|
||||||
Some(vec![
|
Some(vec![
|
||||||
json!({ "id": 4, "race": "leonberg" }).as_object().unwrap().clone(),
|
json!({ "id": 4, "race": "leonberg" }).as_object().unwrap().clone(),
|
||||||
@@ -403,6 +433,7 @@ pub(crate) mod test {
|
|||||||
enqueued_at: datetime!(2022-11-15 0:00 UTC),
|
enqueued_at: datetime!(2022-11-15 0:00 UTC),
|
||||||
started_at: None,
|
started_at: None,
|
||||||
finished_at: None,
|
finished_at: None,
|
||||||
|
network: None,
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
@@ -515,7 +546,8 @@ pub(crate) mod test {
|
|||||||
fn create_test_network() -> Network {
|
fn create_test_network() -> Network {
|
||||||
Network {
|
Network {
|
||||||
local: Some("myself".to_string()),
|
local: Some("myself".to_string()),
|
||||||
remotes: maplit::btreemap! {"other".to_string() => Remote { url: "http://test".to_string(), search_api_key: Some("apiKey".to_string()) }},
|
remotes: maplit::btreemap! {"other".to_string() => Remote { url: "http://test".to_string(), search_api_key: Some("apiKey".to_string()), write_api_key: Some("docApiKey".to_string()) }},
|
||||||
|
sharding: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::fs::File;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::v2_to_v3::CompatV2ToV3;
|
use super::v2_to_v3::CompatV2ToV3;
|
||||||
@@ -94,6 +95,10 @@ impl CompatIndexV1ToV2 {
|
|||||||
self.from.documents().map(|it| Box::new(it) as Box<dyn Iterator<Item = _>>)
|
self.from.documents().map(|it| Box::new(it) as Box<dyn Iterator<Item = _>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.from.documents_file()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<v2::settings::Settings<v2::settings::Checked>> {
|
pub fn settings(&mut self) -> Result<v2::settings::Settings<v2::settings::Checked>> {
|
||||||
Ok(v2::settings::Settings::<v2::settings::Unchecked>::from(self.from.settings()?).check())
|
Ok(v2::settings::Settings::<v2::settings::Unchecked>::from(self.from.settings()?).check())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::fs::File;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@@ -122,6 +123,13 @@ impl CompatIndexV2ToV3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
match self {
|
||||||
|
CompatIndexV2ToV3::V2(v2) => v2.documents_file(),
|
||||||
|
CompatIndexV2ToV3::Compat(compat) => compat.documents_file(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<v3::Settings<v3::Checked>> {
|
pub fn settings(&mut self) -> Result<v3::Settings<v3::Checked>> {
|
||||||
let settings = match self {
|
let settings = match self {
|
||||||
CompatIndexV2ToV3::V2(from) => from.settings()?,
|
CompatIndexV2ToV3::V2(from) => from.settings()?,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
use super::v2_to_v3::{CompatIndexV2ToV3, CompatV2ToV3};
|
use super::v2_to_v3::{CompatIndexV2ToV3, CompatV2ToV3};
|
||||||
use super::v4_to_v5::CompatV4ToV5;
|
use super::v4_to_v5::CompatV4ToV5;
|
||||||
use crate::reader::{v3, v4, UpdateFile};
|
use crate::reader::{v3, v4, UpdateFile};
|
||||||
@@ -252,6 +254,13 @@ impl CompatIndexV3ToV4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
match self {
|
||||||
|
CompatIndexV3ToV4::V3(v3) => v3.documents_file(),
|
||||||
|
CompatIndexV3ToV4::Compat(compat) => compat.documents_file(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<v4::Settings<v4::Checked>> {
|
pub fn settings(&mut self) -> Result<v4::Settings<v4::Checked>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
CompatIndexV3ToV4::V3(v3) => {
|
CompatIndexV3ToV4::V3(v3) => {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
use super::v3_to_v4::{CompatIndexV3ToV4, CompatV3ToV4};
|
use super::v3_to_v4::{CompatIndexV3ToV4, CompatV3ToV4};
|
||||||
use super::v5_to_v6::CompatV5ToV6;
|
use super::v5_to_v6::CompatV5ToV6;
|
||||||
use crate::reader::{v4, v5, Document};
|
use crate::reader::{v4, v5, Document};
|
||||||
@@ -241,6 +243,13 @@ impl CompatIndexV4ToV5 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
match self {
|
||||||
|
CompatIndexV4ToV5::V4(v4) => v4.documents_file(),
|
||||||
|
CompatIndexV4ToV5::Compat(compat) => compat.documents_file(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<v5::Settings<v5::Checked>> {
|
pub fn settings(&mut self) -> Result<v5::Settings<v5::Checked>> {
|
||||||
match self {
|
match self {
|
||||||
CompatIndexV4ToV5::V4(v4) => Ok(v5::Settings::from(v4.settings()?).check()),
|
CompatIndexV4ToV5::V4(v4) => Ok(v5::Settings::from(v4.settings()?).check()),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::v4_to_v5::{CompatIndexV4ToV5, CompatV4ToV5};
|
use super::v4_to_v5::{CompatIndexV4ToV5, CompatV4ToV5};
|
||||||
@@ -83,7 +85,7 @@ impl CompatV5ToV6 {
|
|||||||
v6::Kind::IndexCreation { primary_key }
|
v6::Kind::IndexCreation { primary_key }
|
||||||
}
|
}
|
||||||
v5::tasks::TaskContent::IndexUpdate { primary_key, .. } => {
|
v5::tasks::TaskContent::IndexUpdate { primary_key, .. } => {
|
||||||
v6::Kind::IndexUpdate { primary_key }
|
v6::Kind::IndexUpdate { primary_key, uid: None }
|
||||||
}
|
}
|
||||||
v5::tasks::TaskContent::IndexDeletion { .. } => v6::Kind::IndexDeletion,
|
v5::tasks::TaskContent::IndexDeletion { .. } => v6::Kind::IndexDeletion,
|
||||||
v5::tasks::TaskContent::DocumentAddition {
|
v5::tasks::TaskContent::DocumentAddition {
|
||||||
@@ -138,9 +140,11 @@ impl CompatV5ToV6 {
|
|||||||
v5::Details::Settings { settings } => {
|
v5::Details::Settings { settings } => {
|
||||||
v6::Details::SettingsUpdate { settings: Box::new(settings.into()) }
|
v6::Details::SettingsUpdate { settings: Box::new(settings.into()) }
|
||||||
}
|
}
|
||||||
v5::Details::IndexInfo { primary_key } => {
|
v5::Details::IndexInfo { primary_key } => v6::Details::IndexInfo {
|
||||||
v6::Details::IndexInfo { primary_key }
|
primary_key,
|
||||||
}
|
new_index_uid: None,
|
||||||
|
old_index_uid: None,
|
||||||
|
},
|
||||||
v5::Details::DocumentDeletion {
|
v5::Details::DocumentDeletion {
|
||||||
received_document_ids,
|
received_document_ids,
|
||||||
deleted_documents,
|
deleted_documents,
|
||||||
@@ -159,6 +163,7 @@ impl CompatV5ToV6 {
|
|||||||
enqueued_at: task_view.enqueued_at,
|
enqueued_at: task_view.enqueued_at,
|
||||||
started_at: task_view.started_at,
|
started_at: task_view.started_at,
|
||||||
finished_at: task_view.finished_at,
|
finished_at: task_view.finished_at,
|
||||||
|
network: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
(task, content_file)
|
(task, content_file)
|
||||||
@@ -200,6 +205,10 @@ impl CompatV5ToV6 {
|
|||||||
pub fn network(&self) -> Result<Option<&v6::Network>> {
|
pub fn network(&self) -> Result<Option<&v6::Network>> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn webhooks(&self) -> Option<&v6::Webhooks> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CompatIndexV5ToV6 {
|
pub enum CompatIndexV5ToV6 {
|
||||||
@@ -242,6 +251,13 @@ impl CompatIndexV5ToV6 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
match self {
|
||||||
|
CompatIndexV5ToV6::V5(v5) => v5.documents_file(),
|
||||||
|
CompatIndexV5ToV6::Compat(compat) => compat.documents_file(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
pub fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
||||||
match self {
|
match self {
|
||||||
CompatIndexV5ToV6::V5(v5) => Ok(v6::Settings::from(v5.settings()?).check()),
|
CompatIndexV5ToV6::V5(v5) => Ok(v6::Settings::from(v5.settings()?).check()),
|
||||||
@@ -373,6 +389,7 @@ impl<T> From<v5::Settings<T>> for v6::Settings<v6::Unchecked> {
|
|||||||
},
|
},
|
||||||
disable_on_words: typo.disable_on_words.into(),
|
disable_on_words: typo.disable_on_words.into(),
|
||||||
disable_on_attributes: typo.disable_on_attributes.into(),
|
disable_on_attributes: typo.disable_on_attributes.into(),
|
||||||
|
disable_on_numbers: v6::Setting::NotSet,
|
||||||
}),
|
}),
|
||||||
v5::Setting::Reset => v6::Setting::Reset,
|
v5::Setting::Reset => v6::Setting::Reset,
|
||||||
v5::Setting::NotSet => v6::Setting::NotSet,
|
v5::Setting::NotSet => v6::Setting::NotSet,
|
||||||
@@ -387,7 +404,13 @@ impl<T> From<v5::Settings<T>> for v6::Settings<v6::Unchecked> {
|
|||||||
},
|
},
|
||||||
pagination: match settings.pagination {
|
pagination: match settings.pagination {
|
||||||
v5::Setting::Set(pagination) => v6::Setting::Set(v6::PaginationSettings {
|
v5::Setting::Set(pagination) => v6::Setting::Set(v6::PaginationSettings {
|
||||||
max_total_hits: pagination.max_total_hits.into(),
|
max_total_hits: match pagination.max_total_hits {
|
||||||
|
v5::Setting::Set(max_total_hits) => v6::Setting::Set(
|
||||||
|
max_total_hits.try_into().unwrap_or(NonZeroUsize::new(1).unwrap()),
|
||||||
|
),
|
||||||
|
v5::Setting::Reset => v6::Setting::Reset,
|
||||||
|
v5::Setting::NotSet => v6::Setting::NotSet,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
v5::Setting::Reset => v6::Setting::Reset,
|
v5::Setting::Reset => v6::Setting::Reset,
|
||||||
v5::Setting::NotSet => v6::Setting::NotSet,
|
v5::Setting::NotSet => v6::Setting::NotSet,
|
||||||
@@ -397,6 +420,7 @@ impl<T> From<v5::Settings<T>> for v6::Settings<v6::Unchecked> {
|
|||||||
search_cutoff_ms: v6::Setting::NotSet,
|
search_cutoff_ms: v6::Setting::NotSet,
|
||||||
facet_search: v6::Setting::NotSet,
|
facet_search: v6::Setting::NotSet,
|
||||||
prefix_search: v6::Setting::NotSet,
|
prefix_search: v6::Setting::NotSet,
|
||||||
|
chat: v6::Setting::NotSet,
|
||||||
_kind: std::marker::PhantomData,
|
_kind: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,15 @@ impl DumpReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chat_completions_settings(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = Result<(String, v6::ChatCompletionSettings)>> + '_>> {
|
||||||
|
match self {
|
||||||
|
DumpReader::Current(current) => current.chat_completions_settings(),
|
||||||
|
DumpReader::Compat(_compat) => Ok(Box::new(std::iter::empty())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn features(&self) -> Result<Option<v6::RuntimeTogglableFeatures>> {
|
pub fn features(&self) -> Result<Option<v6::RuntimeTogglableFeatures>> {
|
||||||
match self {
|
match self {
|
||||||
DumpReader::Current(current) => Ok(current.features()),
|
DumpReader::Current(current) => Ok(current.features()),
|
||||||
@@ -129,6 +138,13 @@ impl DumpReader {
|
|||||||
DumpReader::Compat(compat) => compat.network(),
|
DumpReader::Compat(compat) => compat.network(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn webhooks(&self) -> Option<&v6::Webhooks> {
|
||||||
|
match self {
|
||||||
|
DumpReader::Current(current) => current.webhooks(),
|
||||||
|
DumpReader::Compat(compat) => compat.webhooks(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<V6Reader> for DumpReader {
|
impl From<V6Reader> for DumpReader {
|
||||||
@@ -183,6 +199,14 @@ impl DumpIndexReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A reference to a file in the NDJSON format containing all the documents of the index
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
match self {
|
||||||
|
DumpIndexReader::Current(v6) => v6.documents_file(),
|
||||||
|
DumpIndexReader::Compat(compat) => compat.documents_file(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
pub fn settings(&mut self) -> Result<v6::Settings<v6::Checked>> {
|
||||||
match self {
|
match self {
|
||||||
DumpIndexReader::Current(v6) => v6.settings(),
|
DumpIndexReader::Current(v6) => v6.settings(),
|
||||||
@@ -348,6 +372,7 @@ pub(crate) mod test {
|
|||||||
|
|
||||||
assert_eq!(dump.features().unwrap().unwrap(), RuntimeTogglableFeatures::default());
|
assert_eq!(dump.features().unwrap().unwrap(), RuntimeTogglableFeatures::default());
|
||||||
assert_eq!(dump.network().unwrap(), None);
|
assert_eq!(dump.network().unwrap(), None);
|
||||||
|
assert_eq!(dump.webhooks(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -418,6 +443,43 @@ pub(crate) mod test {
|
|||||||
insta::assert_snapshot!(network.remotes.get("ms-2").as_ref().unwrap().search_api_key.as_ref().unwrap(), @"foo");
|
insta::assert_snapshot!(network.remotes.get("ms-2").as_ref().unwrap().search_api_key.as_ref().unwrap(), @"foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_dump_v6_webhooks() {
|
||||||
|
let dump = File::open("tests/assets/v6-with-webhooks.dump").unwrap();
|
||||||
|
let dump = DumpReader::open(dump).unwrap();
|
||||||
|
|
||||||
|
// top level infos
|
||||||
|
insta::assert_snapshot!(dump.date().unwrap(), @"2025-07-31 9:21:30.479544 +00:00:00");
|
||||||
|
insta::assert_debug_snapshot!(dump.instance_uid().unwrap(), @r"
|
||||||
|
Some(
|
||||||
|
cb887dcc-34b3-48d1-addd-9815ae721a81,
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// webhooks
|
||||||
|
let webhooks = dump.webhooks().unwrap();
|
||||||
|
insta::assert_json_snapshot!(webhooks, @r#"
|
||||||
|
{
|
||||||
|
"webhooks": {
|
||||||
|
"627ea538-733d-4545-8d2d-03526eb381ce": {
|
||||||
|
"url": "https://example.com/authorization-less",
|
||||||
|
"headers": {}
|
||||||
|
},
|
||||||
|
"771b0a28-ef28-4082-b984-536f82958c65": {
|
||||||
|
"url": "https://example.com/hook",
|
||||||
|
"headers": {
|
||||||
|
"authorization": "TOKEN"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"f3583083-f8a7-4cbf-a5e7-fb3f1e28a7e9": {
|
||||||
|
"url": "https://third.com",
|
||||||
|
"headers": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_dump_v5() {
|
fn import_dump_v5() {
|
||||||
let dump = File::open("tests/assets/v5.dump").unwrap();
|
let dump = File::open("tests/assets/v5.dump").unwrap();
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ impl V1IndexReader {
|
|||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.documents.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<self::settings::Settings> {
|
pub fn settings(&mut self) -> Result<self::settings::Settings> {
|
||||||
Ok(serde_json::from_reader(&mut self.settings)?)
|
Ok(serde_json::from_reader(&mut self.settings)?)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ impl V2IndexReader {
|
|||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.documents.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
||||||
Ok(self.settings.clone())
|
Ok(self.settings.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,6 +215,10 @@ impl V3IndexReader {
|
|||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.documents.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
||||||
Ok(self.settings.clone())
|
Ok(self.settings.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ where
|
|||||||
/// not supported on untagged enums.
|
/// not supported on untagged enums.
|
||||||
struct StarOrVisitor<T>(PhantomData<T>);
|
struct StarOrVisitor<T>(PhantomData<T>);
|
||||||
|
|
||||||
impl<'de, T, FE> Visitor<'de> for StarOrVisitor<T>
|
impl<T, FE> Visitor<'_> for StarOrVisitor<T>
|
||||||
where
|
where
|
||||||
T: FromStr<Err = FE>,
|
T: FromStr<Err = FE>,
|
||||||
FE: Display,
|
FE: Display,
|
||||||
|
|||||||
@@ -210,6 +210,10 @@ impl V4IndexReader {
|
|||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.documents.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
||||||
Ok(self.settings.clone())
|
Ok(self.settings.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ impl Task {
|
|||||||
/// Return true when a task is finished.
|
/// Return true when a task is finished.
|
||||||
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
||||||
pub fn is_finished(&self) -> bool {
|
pub fn is_finished(&self) -> bool {
|
||||||
self.events.last().map_or(false, |event| {
|
self.events.last().is_some_and(|event| {
|
||||||
matches!(event, TaskEvent::Succeded { .. } | TaskEvent::Failed { .. })
|
matches!(event, TaskEvent::Succeded { .. } | TaskEvent::Failed { .. })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ where
|
|||||||
/// not supported on untagged enums.
|
/// not supported on untagged enums.
|
||||||
struct StarOrVisitor<T>(PhantomData<T>);
|
struct StarOrVisitor<T>(PhantomData<T>);
|
||||||
|
|
||||||
impl<'de, T, FE> Visitor<'de> for StarOrVisitor<T>
|
impl<T, FE> Visitor<'_> for StarOrVisitor<T>
|
||||||
where
|
where
|
||||||
T: FromStr<Err = FE>,
|
T: FromStr<Err = FE>,
|
||||||
FE: Display,
|
FE: Display,
|
||||||
|
|||||||
@@ -247,6 +247,10 @@ impl V5IndexReader {
|
|||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.documents.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
||||||
Ok(self.settings.clone())
|
Ok(self.settings.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ impl Task {
|
|||||||
/// Return true when a task is finished.
|
/// Return true when a task is finished.
|
||||||
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
/// A task is finished when its last state is either `Succeeded` or `Failed`.
|
||||||
pub fn is_finished(&self) -> bool {
|
pub fn is_finished(&self) -> bool {
|
||||||
self.events.last().map_or(false, |event| {
|
self.events.last().is_some_and(|event| {
|
||||||
matches!(event, TaskEvent::Succeeded { .. } | TaskEvent::Failed { .. })
|
matches!(event, TaskEvent::Succeeded { .. } | TaskEvent::Failed { .. })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::ffi::OsStr;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{BufRead, BufReader, ErrorKind};
|
use std::io::{BufRead, BufReader, ErrorKind};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -21,8 +22,10 @@ pub type Unchecked = meilisearch_types::settings::Unchecked;
|
|||||||
pub type Task = crate::TaskDump;
|
pub type Task = crate::TaskDump;
|
||||||
pub type Batch = meilisearch_types::batches::Batch;
|
pub type Batch = meilisearch_types::batches::Batch;
|
||||||
pub type Key = meilisearch_types::keys::Key;
|
pub type Key = meilisearch_types::keys::Key;
|
||||||
|
pub type ChatCompletionSettings = meilisearch_types::features::ChatCompletionSettings;
|
||||||
pub type RuntimeTogglableFeatures = meilisearch_types::features::RuntimeTogglableFeatures;
|
pub type RuntimeTogglableFeatures = meilisearch_types::features::RuntimeTogglableFeatures;
|
||||||
pub type Network = meilisearch_types::features::Network;
|
pub type Network = meilisearch_types::enterprise_edition::network::Network;
|
||||||
|
pub type Webhooks = meilisearch_types::webhooks::WebhooksDumpView;
|
||||||
|
|
||||||
// ===== Other types to clarify the code of the compat module
|
// ===== Other types to clarify the code of the compat module
|
||||||
// everything related to the tasks
|
// everything related to the tasks
|
||||||
@@ -57,6 +60,7 @@ pub struct V6Reader {
|
|||||||
keys: BufReader<File>,
|
keys: BufReader<File>,
|
||||||
features: Option<RuntimeTogglableFeatures>,
|
features: Option<RuntimeTogglableFeatures>,
|
||||||
network: Option<Network>,
|
network: Option<Network>,
|
||||||
|
webhooks: Option<Webhooks>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl V6Reader {
|
impl V6Reader {
|
||||||
@@ -91,8 +95,8 @@ impl V6Reader {
|
|||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let network_file = match fs::read(dump.path().join("network.json")) {
|
let network = match fs::read(dump.path().join("network.json")) {
|
||||||
Ok(network_file) => Some(network_file),
|
Ok(network_file) => Some(serde_json::from_reader(&*network_file)?),
|
||||||
Err(error) => match error.kind() {
|
Err(error) => match error.kind() {
|
||||||
// Allows the file to be missing, this will only result in all experimental features disabled.
|
// Allows the file to be missing, this will only result in all experimental features disabled.
|
||||||
ErrorKind::NotFound => {
|
ErrorKind::NotFound => {
|
||||||
@@ -102,10 +106,16 @@ impl V6Reader {
|
|||||||
_ => return Err(error.into()),
|
_ => return Err(error.into()),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let network = if let Some(network_file) = network_file {
|
|
||||||
Some(serde_json::from_reader(&*network_file)?)
|
let webhooks = match fs::read(dump.path().join("webhooks.json")) {
|
||||||
} else {
|
Ok(webhooks_file) => Some(serde_json::from_reader(&*webhooks_file)?),
|
||||||
None
|
Err(error) => match error.kind() {
|
||||||
|
ErrorKind::NotFound => {
|
||||||
|
debug!("`webhooks.json` not found in dump");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => return Err(error.into()),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(V6Reader {
|
Ok(V6Reader {
|
||||||
@@ -117,6 +127,7 @@ impl V6Reader {
|
|||||||
features,
|
features,
|
||||||
network,
|
network,
|
||||||
dump,
|
dump,
|
||||||
|
webhooks,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,6 +203,34 @@ impl V6Reader {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chat_completions_settings(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = Result<(String, ChatCompletionSettings)>> + '_>> {
|
||||||
|
let entries = match fs::read_dir(self.dump.path().join("chat-completions-settings")) {
|
||||||
|
Ok(entries) => entries,
|
||||||
|
Err(e) if e.kind() == ErrorKind::NotFound => return Ok(Box::new(std::iter::empty())),
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
Ok(Box::new(
|
||||||
|
entries
|
||||||
|
.map(|entry| -> Result<Option<_>> {
|
||||||
|
let entry = entry?;
|
||||||
|
let file_name = entry.file_name();
|
||||||
|
let path = Path::new(&file_name);
|
||||||
|
if entry.file_type()?.is_file() && path.extension() == Some(OsStr::new("json"))
|
||||||
|
{
|
||||||
|
let name = path.file_stem().unwrap().to_str().unwrap().to_string();
|
||||||
|
let file = File::open(entry.path())?;
|
||||||
|
let settings = serde_json::from_reader(file)?;
|
||||||
|
Ok(Some((name, settings)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter_map(|entry| entry.transpose()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn features(&self) -> Option<RuntimeTogglableFeatures> {
|
pub fn features(&self) -> Option<RuntimeTogglableFeatures> {
|
||||||
self.features
|
self.features
|
||||||
}
|
}
|
||||||
@@ -199,6 +238,10 @@ impl V6Reader {
|
|||||||
pub fn network(&self) -> Option<&Network> {
|
pub fn network(&self) -> Option<&Network> {
|
||||||
self.network.as_ref()
|
self.network.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn webhooks(&self) -> Option<&Webhooks> {
|
||||||
|
self.webhooks.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpdateFile {
|
pub struct UpdateFile {
|
||||||
@@ -254,6 +297,10 @@ impl V6IndexReader {
|
|||||||
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
.map(|line| -> Result<_> { Ok(serde_json::from_str(&line?)?) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn documents_file(&self) -> &File {
|
||||||
|
self.documents.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
pub fn settings(&mut self) -> Result<Settings<Checked>> {
|
||||||
let mut settings: Settings<Unchecked> = serde_json::from_reader(&mut self.settings)?;
|
let mut settings: Settings<Unchecked> = serde_json::from_reader(&mut self.settings)?;
|
||||||
patch_embedders(&mut settings);
|
patch_embedders(&mut settings);
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ use std::path::PathBuf;
|
|||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use meilisearch_types::batches::Batch;
|
use meilisearch_types::batches::Batch;
|
||||||
use meilisearch_types::features::{Network, RuntimeTogglableFeatures};
|
use meilisearch_types::enterprise_edition::network::Network;
|
||||||
|
use meilisearch_types::features::{ChatCompletionSettings, RuntimeTogglableFeatures};
|
||||||
use meilisearch_types::keys::Key;
|
use meilisearch_types::keys::Key;
|
||||||
use meilisearch_types::settings::{Checked, Settings};
|
use meilisearch_types::settings::{Checked, Settings};
|
||||||
|
use meilisearch_types::webhooks::WebhooksDumpView;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@@ -51,6 +53,10 @@ impl DumpWriter {
|
|||||||
KeyWriter::new(self.dir.path().to_path_buf())
|
KeyWriter::new(self.dir.path().to_path_buf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_chat_completions_settings(&self) -> Result<ChatCompletionsSettingsWriter> {
|
||||||
|
ChatCompletionsSettingsWriter::new(self.dir.path().join("chat-completions-settings"))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_tasks_queue(&self) -> Result<TaskWriter> {
|
pub fn create_tasks_queue(&self) -> Result<TaskWriter> {
|
||||||
TaskWriter::new(self.dir.path().join("tasks"))
|
TaskWriter::new(self.dir.path().join("tasks"))
|
||||||
}
|
}
|
||||||
@@ -70,6 +76,13 @@ impl DumpWriter {
|
|||||||
Ok(std::fs::write(self.dir.path().join("network.json"), serde_json::to_string(&network)?)?)
|
Ok(std::fs::write(self.dir.path().join("network.json"), serde_json::to_string(&network)?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_webhooks(&self, webhooks: WebhooksDumpView) -> Result<()> {
|
||||||
|
Ok(std::fs::write(
|
||||||
|
self.dir.path().join("webhooks.json"),
|
||||||
|
serde_json::to_string(&webhooks)?,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn persist_to(self, mut writer: impl Write) -> Result<()> {
|
pub fn persist_to(self, mut writer: impl Write) -> Result<()> {
|
||||||
let gz_encoder = GzEncoder::new(&mut writer, Compression::default());
|
let gz_encoder = GzEncoder::new(&mut writer, Compression::default());
|
||||||
let mut tar_encoder = tar::Builder::new(gz_encoder);
|
let mut tar_encoder = tar::Builder::new(gz_encoder);
|
||||||
@@ -104,6 +117,24 @@ impl KeyWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ChatCompletionsSettingsWriter {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatCompletionsSettingsWriter {
|
||||||
|
pub(crate) fn new(path: PathBuf) -> Result<Self> {
|
||||||
|
std::fs::create_dir(&path)?;
|
||||||
|
Ok(ChatCompletionsSettingsWriter { path })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_settings(&mut self, name: &str, settings: &ChatCompletionSettings) -> Result<()> {
|
||||||
|
let mut settings_file = File::create(self.path.join(name).with_extension("json"))?;
|
||||||
|
serde_json::to_writer(&mut settings_file, &settings)?;
|
||||||
|
settings_file.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TaskWriter {
|
pub struct TaskWriter {
|
||||||
queue: BufWriter<File>,
|
queue: BufWriter<File>,
|
||||||
update_files: PathBuf,
|
update_files: PathBuf,
|
||||||
|
|||||||
BIN
crates/dump/tests/assets/v6-with-webhooks.dump
Normal file
BIN
crates/dump/tests/assets/v6-with-webhooks.dump
Normal file
Binary file not shown.
@@ -11,7 +11,7 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tempfile = "3.15.0"
|
tempfile = "3.20.0"
|
||||||
thiserror = "2.0.9"
|
thiserror = "2.0.12"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
uuid = { version = "1.11.0", features = ["serde", "v4"] }
|
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
||||||
|
|||||||
@@ -148,11 +148,10 @@ impl File {
|
|||||||
Ok(Self { path: PathBuf::new(), file: None })
|
Ok(Self { path: PathBuf::new(), file: None })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn persist(self) -> Result<()> {
|
pub fn persist(self) -> Result<Option<StdFile>> {
|
||||||
if let Some(file) = self.file {
|
let Some(file) = self.file else { return Ok(None) };
|
||||||
file.persist(&self.path)?;
|
|
||||||
}
|
Ok(Some(file.persist(&self.path)?))
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ license.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
nom_locate = "4.2.0"
|
nom_locate = "4.2.0"
|
||||||
unescaper = "0.1.5"
|
unescaper = "0.1.6"
|
||||||
|
levenshtein_automata = { version = "0.2.1", features = ["fst_automaton"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# fixed version due to format breakages in v1.40
|
# fixed version due to format breakages in v1.40
|
||||||
|
|||||||
@@ -7,11 +7,22 @@
|
|||||||
|
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::character::complete::char;
|
||||||
|
use nom::character::complete::multispace0;
|
||||||
use nom::character::complete::multispace1;
|
use nom::character::complete::multispace1;
|
||||||
use nom::combinator::cut;
|
use nom::combinator::cut;
|
||||||
|
use nom::combinator::map;
|
||||||
|
use nom::combinator::value;
|
||||||
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::{terminated, tuple};
|
use nom::sequence::{terminated, tuple};
|
||||||
use Condition::*;
|
use Condition::*;
|
||||||
|
|
||||||
|
use crate::error::IResultExt;
|
||||||
|
use crate::value::parse_vector_value;
|
||||||
|
use crate::value::parse_vector_value_cut;
|
||||||
|
use crate::Error;
|
||||||
|
use crate::ErrorKind;
|
||||||
|
use crate::VectorFilter;
|
||||||
use crate::{parse_value, FilterCondition, IResult, Span, Token};
|
use crate::{parse_value, FilterCondition, IResult, Span, Token};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -113,6 +124,83 @@ pub fn parse_not_exists(input: Span) -> IResult<FilterCondition> {
|
|||||||
Ok((input, FilterCondition::Not(Box::new(FilterCondition::Condition { fid: key, op: Exists }))))
|
Ok((input, FilterCondition::Not(Box::new(FilterCondition::Condition { fid: key, op: Exists }))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_vectors(input: Span) -> IResult<(Token, Option<Token>, VectorFilter<'_>)> {
|
||||||
|
let (input, _) = multispace0(input)?;
|
||||||
|
let (input, fid) = tag("_vectors")(input)?;
|
||||||
|
|
||||||
|
if let Ok((input, _)) = multispace1::<_, crate::Error>(input) {
|
||||||
|
return Ok((input, (Token::from(fid), None, VectorFilter::None)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, _) = char('.')(input)?;
|
||||||
|
|
||||||
|
// From this point, we are certain this is a vector filter, so our errors must be final.
|
||||||
|
// We could use nom's `cut` but it's better to be explicit about the errors
|
||||||
|
|
||||||
|
if let Ok((_, space)) = tag::<_, _, ()>(" ")(input) {
|
||||||
|
return Err(crate::Error::failure_from_kind(space, ErrorKind::VectorFilterMissingEmbedder));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, embedder_name) =
|
||||||
|
parse_vector_value_cut(input, ErrorKind::VectorFilterInvalidEmbedder)?;
|
||||||
|
|
||||||
|
let (input, filter) = alt((
|
||||||
|
map(
|
||||||
|
preceded(tag(".fragments"), |input| {
|
||||||
|
let (input, _) = tag(".")(input).map_cut(ErrorKind::VectorFilterMissingFragment)?;
|
||||||
|
parse_vector_value_cut(input, ErrorKind::VectorFilterInvalidFragment)
|
||||||
|
}),
|
||||||
|
VectorFilter::Fragment,
|
||||||
|
),
|
||||||
|
value(VectorFilter::UserProvided, tag(".userProvided")),
|
||||||
|
value(VectorFilter::DocumentTemplate, tag(".documentTemplate")),
|
||||||
|
value(VectorFilter::Regenerate, tag(".regenerate")),
|
||||||
|
value(VectorFilter::None, nom::combinator::success("")),
|
||||||
|
))(input)?;
|
||||||
|
|
||||||
|
if let Ok((input, point)) = tag::<_, _, ()>(".")(input) {
|
||||||
|
let opt_value = parse_vector_value(input).ok().map(|(_, v)| v);
|
||||||
|
let value =
|
||||||
|
opt_value.as_ref().map(|v| v.value().to_owned()).unwrap_or_else(|| point.to_string());
|
||||||
|
let context = opt_value.map(|v| v.original_span()).unwrap_or(point);
|
||||||
|
let previous_kind = match filter {
|
||||||
|
VectorFilter::Fragment(_) => Some("fragments"),
|
||||||
|
VectorFilter::DocumentTemplate => Some("documentTemplate"),
|
||||||
|
VectorFilter::UserProvided => Some("userProvided"),
|
||||||
|
VectorFilter::Regenerate => Some("regenerate"),
|
||||||
|
VectorFilter::None => None,
|
||||||
|
};
|
||||||
|
return Err(Error::failure_from_kind(
|
||||||
|
context,
|
||||||
|
ErrorKind::VectorFilterUnknownSuffix(previous_kind, value),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, _) = multispace1(input).map_cut(ErrorKind::VectorFilterLeftover)?;
|
||||||
|
|
||||||
|
Ok((input, (Token::from(fid), Some(embedder_name), filter)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// vectors_exists = vectors ("EXISTS" | ("NOT" WS+ "EXISTS"))
|
||||||
|
pub fn parse_vectors_exists(input: Span) -> IResult<FilterCondition> {
|
||||||
|
let (input, (fid, embedder, filter)) = parse_vectors(input)?;
|
||||||
|
|
||||||
|
// Try parsing "EXISTS" first
|
||||||
|
if let Ok((input, _)) = tag::<_, _, ()>("EXISTS")(input) {
|
||||||
|
return Ok((input, FilterCondition::VectorExists { fid, embedder, filter }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try parsing "NOT EXISTS"
|
||||||
|
if let Ok((input, _)) = tuple::<_, _, (), _>((tag("NOT"), multispace1, tag("EXISTS")))(input) {
|
||||||
|
return Ok((
|
||||||
|
input,
|
||||||
|
FilterCondition::Not(Box::new(FilterCondition::VectorExists { fid, embedder, filter })),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(crate::Error::failure_from_kind(input, ErrorKind::VectorFilterOperation))
|
||||||
|
}
|
||||||
|
|
||||||
/// contains = value "CONTAINS" value
|
/// contains = value "CONTAINS" value
|
||||||
pub fn parse_contains(input: Span) -> IResult<FilterCondition> {
|
pub fn parse_contains(input: Span) -> IResult<FilterCondition> {
|
||||||
let (input, (fid, contains, value)) =
|
let (input, (fid, contains, value)) =
|
||||||
|
|||||||
@@ -35,13 +35,30 @@ impl<E> NomErrorExt<E> for nom::Err<E> {
|
|||||||
pub fn cut_with_err<'a, O>(
|
pub fn cut_with_err<'a, O>(
|
||||||
mut parser: impl FnMut(Span<'a>) -> IResult<'a, O>,
|
mut parser: impl FnMut(Span<'a>) -> IResult<'a, O>,
|
||||||
mut with: impl FnMut(Error<'a>) -> Error<'a>,
|
mut with: impl FnMut(Error<'a>) -> Error<'a>,
|
||||||
) -> impl FnMut(Span<'a>) -> IResult<O> {
|
) -> impl FnMut(Span<'a>) -> IResult<'a, O> {
|
||||||
move |input| match parser.parse(input) {
|
move |input| match parser.parse(input) {
|
||||||
Err(nom::Err::Error(e)) => Err(nom::Err::Failure(with(e))),
|
Err(nom::Err::Error(e)) => Err(nom::Err::Failure(with(e))),
|
||||||
rest => rest,
|
rest => rest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait IResultExt<'a> {
|
||||||
|
fn map_cut(self, kind: ErrorKind<'a>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> IResultExt<'a> for IResult<'a, T> {
|
||||||
|
fn map_cut(self, kind: ErrorKind<'a>) -> Self {
|
||||||
|
self.map_err(move |e: nom::Err<Error<'a>>| {
|
||||||
|
let input = match e {
|
||||||
|
nom::Err::Incomplete(_) => return e,
|
||||||
|
nom::Err::Error(e) => *e.context(),
|
||||||
|
nom::Err::Failure(e) => *e.context(),
|
||||||
|
};
|
||||||
|
Error::failure_from_kind(input, kind)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error<'a> {
|
pub struct Error<'a> {
|
||||||
context: Span<'a>,
|
context: Span<'a>,
|
||||||
@@ -61,6 +78,14 @@ pub enum ErrorKind<'a> {
|
|||||||
GeoBoundingBox,
|
GeoBoundingBox,
|
||||||
MisusedGeoRadius,
|
MisusedGeoRadius,
|
||||||
MisusedGeoBoundingBox,
|
MisusedGeoBoundingBox,
|
||||||
|
VectorFilterLeftover,
|
||||||
|
VectorFilterInvalidQuotes,
|
||||||
|
VectorFilterMissingEmbedder,
|
||||||
|
VectorFilterInvalidEmbedder,
|
||||||
|
VectorFilterMissingFragment,
|
||||||
|
VectorFilterInvalidFragment,
|
||||||
|
VectorFilterUnknownSuffix(Option<&'static str>, String),
|
||||||
|
VectorFilterOperation,
|
||||||
InvalidPrimary,
|
InvalidPrimary,
|
||||||
InvalidEscapedNumber,
|
InvalidEscapedNumber,
|
||||||
ExpectedEof,
|
ExpectedEof,
|
||||||
@@ -91,6 +116,10 @@ impl<'a> Error<'a> {
|
|||||||
Self { context, kind }
|
Self { context, kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn failure_from_kind(context: Span<'a>, kind: ErrorKind<'a>) -> nom::Err<Self> {
|
||||||
|
nom::Err::Failure(Self::new_from_kind(context, kind))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_from_external(context: Span<'a>, error: impl std::error::Error) -> Self {
|
pub fn new_from_external(context: Span<'a>, error: impl std::error::Error) -> Self {
|
||||||
Self::new_from_kind(context, ErrorKind::External(error.to_string()))
|
Self::new_from_kind(context, ErrorKind::External(error.to_string()))
|
||||||
}
|
}
|
||||||
@@ -121,13 +150,27 @@ impl<'a> ParseError<Span<'a>> for Error<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for Error<'a> {
|
impl Display for Error<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let input = self.context.fragment();
|
let input = self.context.fragment();
|
||||||
// When printing our error message we want to escape all `\n` to be sure we keep our format with the
|
// When printing our error message we want to escape all `\n` to be sure we keep our format with the
|
||||||
// first line being the diagnostic and the second line being the incriminated filter.
|
// first line being the diagnostic and the second line being the incriminated filter.
|
||||||
let escaped_input = input.escape_debug();
|
let escaped_input = input.escape_debug();
|
||||||
|
|
||||||
|
fn key_suggestion<'a>(key: &str, keys: &[&'a str]) -> Option<&'a str> {
|
||||||
|
let typos =
|
||||||
|
levenshtein_automata::LevenshteinAutomatonBuilder::new(2, true).build_dfa(key);
|
||||||
|
for key in keys.iter() {
|
||||||
|
match typos.eval(key) {
|
||||||
|
levenshtein_automata::Distance::Exact(_) => {
|
||||||
|
return Some(key);
|
||||||
|
}
|
||||||
|
levenshtein_automata::Distance::AtLeast(_) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ErrorKind::ExpectedValue(_) if input.trim().is_empty() => {
|
ErrorKind::ExpectedValue(_) if input.trim().is_empty() => {
|
||||||
writeln!(f, "Was expecting a value but instead got nothing.")?
|
writeln!(f, "Was expecting a value but instead got nothing.")?
|
||||||
@@ -169,6 +212,44 @@ impl<'a> Display for Error<'a> {
|
|||||||
ErrorKind::MisusedGeoBoundingBox => {
|
ErrorKind::MisusedGeoBoundingBox => {
|
||||||
writeln!(f, "The `_geoBoundingBox` filter is an operation and can't be used as a value.")?
|
writeln!(f, "The `_geoBoundingBox` filter is an operation and can't be used as a value.")?
|
||||||
}
|
}
|
||||||
|
ErrorKind::VectorFilterLeftover => {
|
||||||
|
writeln!(f, "The vector filter has leftover tokens.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterUnknownSuffix(_, value) if value.as_str() == "." => {
|
||||||
|
writeln!(f, "Was expecting one of `.fragments`, `.userProvided`, `.documentTemplate`, `.regenerate` or nothing, but instead found a point without a valid value.")?;
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterUnknownSuffix(None, value) if ["fragments", "userProvided", "documentTemplate", "regenerate"].contains(&value.as_str()) => {
|
||||||
|
// This will happen with "_vectors.rest.\"userProvided\"" for instance
|
||||||
|
writeln!(f, "Was expecting this part to be unquoted.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterUnknownSuffix(None, value) => {
|
||||||
|
if let Some(suggestion) = key_suggestion(value, &["fragments", "userProvided", "documentTemplate", "regenerate"]) {
|
||||||
|
writeln!(f, "Was expecting one of `fragments`, `userProvided`, `documentTemplate`, `regenerate` or nothing, but instead found `{value}`. Did you mean `{suggestion}`?")?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, "Was expecting one of `fragments`, `userProvided`, `documentTemplate`, `regenerate` or nothing, but instead found `{value}`.")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterUnknownSuffix(Some(previous_filter_kind), value) => {
|
||||||
|
writeln!(f, "Vector filter can only accept one of `fragments`, `userProvided`, `documentTemplate` or `regenerate`, but found both `{previous_filter_kind}` and `{value}`.")?
|
||||||
|
},
|
||||||
|
ErrorKind::VectorFilterInvalidFragment => {
|
||||||
|
writeln!(f, "The vector filter's fragment name is invalid.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterMissingFragment => {
|
||||||
|
writeln!(f, "The vector filter is missing a fragment name.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterMissingEmbedder => {
|
||||||
|
writeln!(f, "Was expecting embedder name but found nothing.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterInvalidEmbedder => {
|
||||||
|
writeln!(f, "The vector filter's embedder name is invalid.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterOperation => {
|
||||||
|
writeln!(f, "Was expecting an operation like `EXISTS` or `NOT EXISTS` after the vector filter.")?
|
||||||
|
}
|
||||||
|
ErrorKind::VectorFilterInvalidQuotes => {
|
||||||
|
writeln!(f, "The quotes in one of the values are inconsistent.")?
|
||||||
|
}
|
||||||
ErrorKind::ReservedKeyword(word) => {
|
ErrorKind::ReservedKeyword(word) => {
|
||||||
writeln!(f, "`{word}` is a reserved keyword and thus cannot be used as a field name unless it is put inside quotes. Use \"{word}\" or \'{word}\' instead.")?
|
writeln!(f, "`{word}` is a reserved keyword and thus cannot be used as a field name unless it is put inside quotes. Use \"{word}\" or \'{word}\' instead.")?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ use nom_locate::LocatedSpan;
|
|||||||
pub(crate) use value::parse_value;
|
pub(crate) use value::parse_value;
|
||||||
use value::word_exact;
|
use value::word_exact;
|
||||||
|
|
||||||
|
use crate::condition::parse_vectors_exists;
|
||||||
|
use crate::error::IResultExt;
|
||||||
|
|
||||||
pub type Span<'a> = LocatedSpan<&'a str, &'a str>;
|
pub type Span<'a> = LocatedSpan<&'a str, &'a str>;
|
||||||
|
|
||||||
type IResult<'a, Ret> = nom::IResult<Span<'a>, Ret, Error<'a>>;
|
type IResult<'a, Ret> = nom::IResult<Span<'a>, Ret, Error<'a>>;
|
||||||
@@ -80,7 +83,7 @@ pub struct Token<'a> {
|
|||||||
value: Option<String>,
|
value: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PartialEq for Token<'a> {
|
impl PartialEq for Token<'_> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.span.fragment() == other.span.fragment()
|
self.span.fragment() == other.span.fragment()
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,15 @@ impl<'a> From<&'a str> for Token<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum VectorFilter<'a> {
|
||||||
|
Fragment(Token<'a>),
|
||||||
|
DocumentTemplate,
|
||||||
|
UserProvided,
|
||||||
|
Regenerate,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum FilterCondition<'a> {
|
pub enum FilterCondition<'a> {
|
||||||
Not(Box<Self>),
|
Not(Box<Self>),
|
||||||
@@ -143,6 +155,7 @@ pub enum FilterCondition<'a> {
|
|||||||
In { fid: Token<'a>, els: Vec<Token<'a>> },
|
In { fid: Token<'a>, els: Vec<Token<'a>> },
|
||||||
Or(Vec<Self>),
|
Or(Vec<Self>),
|
||||||
And(Vec<Self>),
|
And(Vec<Self>),
|
||||||
|
VectorExists { fid: Token<'a>, embedder: Option<Token<'a>>, filter: VectorFilter<'a> },
|
||||||
GeoLowerThan { point: [Token<'a>; 2], radius: Token<'a> },
|
GeoLowerThan { point: [Token<'a>; 2], radius: Token<'a> },
|
||||||
GeoBoundingBox { top_right_point: [Token<'a>; 2], bottom_left_point: [Token<'a>; 2] },
|
GeoBoundingBox { top_right_point: [Token<'a>; 2], bottom_left_point: [Token<'a>; 2] },
|
||||||
}
|
}
|
||||||
@@ -165,17 +178,32 @@ impl<'a> FilterCondition<'a> {
|
|||||||
| Condition::Exists
|
| Condition::Exists
|
||||||
| Condition::LowerThan(_)
|
| Condition::LowerThan(_)
|
||||||
| Condition::LowerThanOrEqual(_)
|
| Condition::LowerThanOrEqual(_)
|
||||||
| Condition::Between { .. } => None,
|
| Condition::Between { .. }
|
||||||
Condition::Contains { keyword, word: _ }
|
| Condition::StartsWith { .. } => None,
|
||||||
| Condition::StartsWith { keyword, word: _ } => Some(keyword),
|
Condition::Contains { keyword, word: _ } => Some(keyword),
|
||||||
},
|
},
|
||||||
FilterCondition::Not(this) => this.use_contains_operator(),
|
FilterCondition::Not(this) => this.use_contains_operator(),
|
||||||
FilterCondition::Or(seq) | FilterCondition::And(seq) => {
|
FilterCondition::Or(seq) | FilterCondition::And(seq) => {
|
||||||
seq.iter().find_map(|filter| filter.use_contains_operator())
|
seq.iter().find_map(|filter| filter.use_contains_operator())
|
||||||
}
|
}
|
||||||
|
FilterCondition::VectorExists { .. }
|
||||||
|
| FilterCondition::GeoLowerThan { .. }
|
||||||
|
| FilterCondition::GeoBoundingBox { .. }
|
||||||
|
| FilterCondition::In { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use_vector_filter(&self) -> Option<&Token> {
|
||||||
|
match self {
|
||||||
|
FilterCondition::Condition { .. } => None,
|
||||||
|
FilterCondition::Not(this) => this.use_vector_filter(),
|
||||||
|
FilterCondition::Or(seq) | FilterCondition::And(seq) => {
|
||||||
|
seq.iter().find_map(|filter| filter.use_vector_filter())
|
||||||
|
}
|
||||||
FilterCondition::GeoLowerThan { .. }
|
FilterCondition::GeoLowerThan { .. }
|
||||||
| FilterCondition::GeoBoundingBox { .. }
|
| FilterCondition::GeoBoundingBox { .. }
|
||||||
| FilterCondition::In { .. } => None,
|
| FilterCondition::In { .. } => None,
|
||||||
|
FilterCondition::VectorExists { fid, .. } => Some(fid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +254,7 @@ impl<'a> FilterCondition<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(input: &'a str) -> Result<Option<Self>, Error> {
|
pub fn parse(input: &'a str) -> Result<Option<Self>, Error<'a>> {
|
||||||
if input.trim().is_empty() {
|
if input.trim().is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -263,10 +291,7 @@ fn parse_in_body(input: Span) -> IResult<Vec<Token>> {
|
|||||||
let (input, _) = ws(word_exact("IN"))(input)?;
|
let (input, _) = ws(word_exact("IN"))(input)?;
|
||||||
|
|
||||||
// everything after `IN` can be a failure
|
// everything after `IN` can be a failure
|
||||||
let (input, _) =
|
let (input, _) = tag("[")(input).map_cut(ErrorKind::InOpeningBracket)?;
|
||||||
cut_with_err(tag("["), |_| Error::new_from_kind(input, ErrorKind::InOpeningBracket))(
|
|
||||||
input,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (input, content) = cut(parse_value_list)(input)?;
|
let (input, content) = cut(parse_value_list)(input)?;
|
||||||
|
|
||||||
@@ -412,7 +437,7 @@ fn parse_geo_bounding_box(input: Span) -> IResult<FilterCondition> {
|
|||||||
let (input, args) = parsed?;
|
let (input, args) = parsed?;
|
||||||
|
|
||||||
if args.len() != 2 || args[0].len() != 2 || args[1].len() != 2 {
|
if args.len() != 2 || args[0].len() != 2 || args[1].len() != 2 {
|
||||||
return Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::GeoBoundingBox)));
|
return Err(Error::failure_from_kind(input, ErrorKind::GeoBoundingBox));
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = FilterCondition::GeoBoundingBox {
|
let res = FilterCondition::GeoBoundingBox {
|
||||||
@@ -433,7 +458,7 @@ fn parse_geo_point(input: Span) -> IResult<FilterCondition> {
|
|||||||
))(input)
|
))(input)
|
||||||
.map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoPoint"))))?;
|
.map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoPoint"))))?;
|
||||||
// if we succeeded we still return a `Failure` because geoPoints are not allowed
|
// if we succeeded we still return a `Failure` because geoPoints are not allowed
|
||||||
Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoPoint"))))
|
Err(Error::failure_from_kind(input, ErrorKind::ReservedGeo("_geoPoint")))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// geoPoint = WS* "_geoDistance(float WS* "," WS* float WS* "," WS* float)
|
/// geoPoint = WS* "_geoDistance(float WS* "," WS* float WS* "," WS* float)
|
||||||
@@ -447,7 +472,7 @@ fn parse_geo_distance(input: Span) -> IResult<FilterCondition> {
|
|||||||
))(input)
|
))(input)
|
||||||
.map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoDistance"))))?;
|
.map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoDistance"))))?;
|
||||||
// if we succeeded we still return a `Failure` because `geoDistance` filters are not allowed
|
// if we succeeded we still return a `Failure` because `geoDistance` filters are not allowed
|
||||||
Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::ReservedGeo("_geoDistance"))))
|
Err(Error::failure_from_kind(input, ErrorKind::ReservedGeo("_geoDistance")))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// geo = WS* "_geo(float WS* "," WS* float WS* "," WS* float)
|
/// geo = WS* "_geo(float WS* "," WS* float WS* "," WS* float)
|
||||||
@@ -461,7 +486,7 @@ fn parse_geo(input: Span) -> IResult<FilterCondition> {
|
|||||||
))(input)
|
))(input)
|
||||||
.map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geo"))))?;
|
.map_err(|e| e.map(|_| Error::new_from_kind(input, ErrorKind::ReservedGeo("_geo"))))?;
|
||||||
// if we succeeded we still return a `Failure` because `_geo` filter is not allowed
|
// if we succeeded we still return a `Failure` because `_geo` filter is not allowed
|
||||||
Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::ReservedGeo("_geo"))))
|
Err(Error::failure_from_kind(input, ErrorKind::ReservedGeo("_geo")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_error_reserved_keyword(input: Span) -> IResult<FilterCondition> {
|
fn parse_error_reserved_keyword(input: Span) -> IResult<FilterCondition> {
|
||||||
@@ -500,8 +525,7 @@ fn parse_primary(input: Span, depth: usize) -> IResult<FilterCondition> {
|
|||||||
parse_is_not_null,
|
parse_is_not_null,
|
||||||
parse_is_empty,
|
parse_is_empty,
|
||||||
parse_is_not_empty,
|
parse_is_not_empty,
|
||||||
parse_exists,
|
alt((parse_vectors_exists, parse_exists, parse_not_exists)),
|
||||||
parse_not_exists,
|
|
||||||
parse_to,
|
parse_to,
|
||||||
parse_contains,
|
parse_contains,
|
||||||
parse_not_contains,
|
parse_not_contains,
|
||||||
@@ -527,7 +551,7 @@ pub fn parse_filter(input: Span) -> IResult<FilterCondition> {
|
|||||||
terminated(|input| parse_expression(input, 0), eof)(input)
|
terminated(|input| parse_expression(input, 0), eof)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Display for FilterCondition<'a> {
|
impl std::fmt::Display for FilterCondition<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
FilterCondition::Not(filter) => {
|
FilterCondition::Not(filter) => {
|
||||||
@@ -557,6 +581,22 @@ impl<'a> std::fmt::Display for FilterCondition<'a> {
|
|||||||
}
|
}
|
||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
|
FilterCondition::VectorExists { fid: _, embedder, filter: inner } => {
|
||||||
|
write!(f, "_vectors")?;
|
||||||
|
if let Some(embedder) = embedder {
|
||||||
|
write!(f, ".{:?}", embedder.value())?;
|
||||||
|
}
|
||||||
|
match inner {
|
||||||
|
VectorFilter::Fragment(fragment) => {
|
||||||
|
write!(f, ".fragments.{:?}", fragment.value())?
|
||||||
|
}
|
||||||
|
VectorFilter::DocumentTemplate => write!(f, ".documentTemplate")?,
|
||||||
|
VectorFilter::UserProvided => write!(f, ".userProvided")?,
|
||||||
|
VectorFilter::Regenerate => write!(f, ".regenerate")?,
|
||||||
|
VectorFilter::None => (),
|
||||||
|
}
|
||||||
|
write!(f, " EXISTS")
|
||||||
|
}
|
||||||
FilterCondition::GeoLowerThan { point, radius } => {
|
FilterCondition::GeoLowerThan { point, radius } => {
|
||||||
write!(f, "_geoRadius({}, {}, {})", point[0], point[1], radius)
|
write!(f, "_geoRadius({}, {}, {})", point[0], point[1], radius)
|
||||||
}
|
}
|
||||||
@@ -576,7 +616,8 @@ impl<'a> std::fmt::Display for FilterCondition<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> std::fmt::Display for Condition<'a> {
|
|
||||||
|
impl std::fmt::Display for Condition<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Condition::GreaterThan(token) => write!(f, "> {token}"),
|
Condition::GreaterThan(token) => write!(f, "> {token}"),
|
||||||
@@ -594,7 +635,8 @@ impl<'a> std::fmt::Display for Condition<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> std::fmt::Display for Token<'a> {
|
|
||||||
|
impl std::fmt::Display for Token<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{{{}}}", self.value())
|
write!(f, "{{{}}}", self.value())
|
||||||
}
|
}
|
||||||
@@ -628,6 +670,9 @@ pub mod tests {
|
|||||||
insta::assert_snapshot!(p(r"title = 'foo\\\\\\\\'"), @r#"{title} = {foo\\\\}"#);
|
insta::assert_snapshot!(p(r"title = 'foo\\\\\\\\'"), @r#"{title} = {foo\\\\}"#);
|
||||||
// but it also works with other sequences
|
// but it also works with other sequences
|
||||||
insta::assert_snapshot!(p(r#"title = 'foo\x20\n\t\"\'"'"#), @"{title} = {foo \n\t\"\'\"}");
|
insta::assert_snapshot!(p(r#"title = 'foo\x20\n\t\"\'"'"#), @"{title} = {foo \n\t\"\'\"}");
|
||||||
|
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors." valid.name ".fragments."also.. valid! " EXISTS"#), @r#"_vectors." valid.name ".fragments."also.. valid! " EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p("_vectors.\"\n\t\r\\\"\" EXISTS"), @r#"_vectors."\n\t\r\"" EXISTS"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -690,6 +735,18 @@ pub mod tests {
|
|||||||
insta::assert_snapshot!(p("NOT subscribers IS NOT EMPTY"), @"{subscribers} IS EMPTY");
|
insta::assert_snapshot!(p("NOT subscribers IS NOT EMPTY"), @"{subscribers} IS EMPTY");
|
||||||
insta::assert_snapshot!(p("subscribers IS NOT EMPTY"), @"NOT ({subscribers} IS EMPTY)");
|
insta::assert_snapshot!(p("subscribers IS NOT EMPTY"), @"NOT ({subscribers} IS EMPTY)");
|
||||||
|
|
||||||
|
// Test _vectors EXISTS + _vectors NOT EXITS
|
||||||
|
insta::assert_snapshot!(p("_vectors EXISTS"), @"_vectors EXISTS");
|
||||||
|
insta::assert_snapshot!(p("_vectors.embedderName EXISTS"), @r#"_vectors."embedderName" EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p("_vectors.embedderName.documentTemplate EXISTS"), @r#"_vectors."embedderName".documentTemplate EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p("_vectors.embedderName.regenerate EXISTS"), @r#"_vectors."embedderName".regenerate EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p("_vectors.embedderName.regenerate EXISTS"), @r#"_vectors."embedderName".regenerate EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p("_vectors.embedderName.fragments.fragmentName EXISTS"), @r#"_vectors."embedderName".fragments."fragmentName" EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p(" _vectors.embedderName.fragments.fragmentName EXISTS"), @r#"_vectors."embedderName".fragments."fragmentName" EXISTS"#);
|
||||||
|
insta::assert_snapshot!(p("NOT _vectors EXISTS"), @"NOT (_vectors EXISTS)");
|
||||||
|
insta::assert_snapshot!(p(" NOT _vectors EXISTS"), @"NOT (_vectors EXISTS)");
|
||||||
|
insta::assert_snapshot!(p(" _vectors NOT EXISTS"), @"NOT (_vectors EXISTS)");
|
||||||
|
|
||||||
// Test EXISTS + NOT EXITS
|
// Test EXISTS + NOT EXITS
|
||||||
insta::assert_snapshot!(p("subscribers EXISTS"), @"{subscribers} EXISTS");
|
insta::assert_snapshot!(p("subscribers EXISTS"), @"{subscribers} EXISTS");
|
||||||
insta::assert_snapshot!(p("NOT subscribers EXISTS"), @"NOT ({subscribers} EXISTS)");
|
insta::assert_snapshot!(p("NOT subscribers EXISTS"), @"NOT ({subscribers} EXISTS)");
|
||||||
@@ -944,6 +1001,71 @@ pub mod tests {
|
|||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors _vectors EXISTS"#), @r"
|
||||||
|
Was expecting an operation like `EXISTS` or `NOT EXISTS` after the vector filter.
|
||||||
|
10:25 _vectors _vectors EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors. embedderName EXISTS"#), @r"
|
||||||
|
Was expecting embedder name but found nothing.
|
||||||
|
10:11 _vectors. embedderName EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors .embedderName EXISTS"#), @r"
|
||||||
|
Was expecting an operation like `EXISTS` or `NOT EXISTS` after the vector filter.
|
||||||
|
10:30 _vectors .embedderName EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName. EXISTS"#), @r"
|
||||||
|
Was expecting one of `.fragments`, `.userProvided`, `.documentTemplate`, `.regenerate` or nothing, but instead found a point without a valid value.
|
||||||
|
22:23 _vectors.embedderName. EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors."embedderName EXISTS"#), @r#"
|
||||||
|
The quotes in one of the values are inconsistent.
|
||||||
|
10:30 _vectors."embedderName EXISTS
|
||||||
|
"#);
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors."embedderNam"e EXISTS"#), @r#"
|
||||||
|
The vector filter has leftover tokens.
|
||||||
|
23:31 _vectors."embedderNam"e EXISTS
|
||||||
|
"#);
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.documentTemplate. EXISTS"#), @r"
|
||||||
|
Was expecting one of `.fragments`, `.userProvided`, `.documentTemplate`, `.regenerate` or nothing, but instead found a point without a valid value.
|
||||||
|
39:40 _vectors.embedderName.documentTemplate. EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.fragments EXISTS"#), @r"
|
||||||
|
The vector filter is missing a fragment name.
|
||||||
|
32:39 _vectors.embedderName.fragments EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.fragments. EXISTS"#), @r"
|
||||||
|
The vector filter's fragment name is invalid.
|
||||||
|
33:40 _vectors.embedderName.fragments. EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.fragments.test test EXISTS"#), @r"
|
||||||
|
Was expecting an operation like `EXISTS` or `NOT EXISTS` after the vector filter.
|
||||||
|
38:49 _vectors.embedderName.fragments.test test EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.fragments. test EXISTS"#), @r"
|
||||||
|
The vector filter's fragment name is invalid.
|
||||||
|
33:45 _vectors.embedderName.fragments. test EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName .fragments. test EXISTS"#), @r"
|
||||||
|
Was expecting an operation like `EXISTS` or `NOT EXISTS` after the vector filter.
|
||||||
|
23:46 _vectors.embedderName .fragments. test EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName .fragments.test EXISTS"#), @r"
|
||||||
|
Was expecting an operation like `EXISTS` or `NOT EXISTS` after the vector filter.
|
||||||
|
23:45 _vectors.embedderName .fragments.test EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.fargments.test EXISTS"#), @r"
|
||||||
|
Was expecting one of `fragments`, `userProvided`, `documentTemplate`, `regenerate` or nothing, but instead found `fargments`. Did you mean `fragments`?
|
||||||
|
23:32 _vectors.embedderName.fargments.test EXISTS
|
||||||
|
");
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName."userProvided" EXISTS"#), @r#"
|
||||||
|
Was expecting this part to be unquoted.
|
||||||
|
24:36 _vectors.embedderName."userProvided" EXISTS
|
||||||
|
"#);
|
||||||
|
insta::assert_snapshot!(p(r#"_vectors.embedderName.userProvided.fragments.test EXISTS"#), @r"
|
||||||
|
Vector filter can only accept one of `fragments`, `userProvided`, `documentTemplate` or `regenerate`, but found both `userProvided` and `fragments`.
|
||||||
|
36:45 _vectors.embedderName.userProvided.fragments.test EXISTS
|
||||||
|
");
|
||||||
|
|
||||||
insta::assert_snapshot!(p(r#"NOT OR EXISTS AND EXISTS NOT EXISTS"#), @r###"
|
insta::assert_snapshot!(p(r#"NOT OR EXISTS AND EXISTS NOT EXISTS"#), @r###"
|
||||||
Was expecting a value but instead got `OR`, which is a reserved keyword. To use `OR` as a field name or a value, surround it by quotes.
|
Was expecting a value but instead got `OR`, which is a reserved keyword. To use `OR` as a field name or a value, surround it by quotes.
|
||||||
5:7 NOT OR EXISTS AND EXISTS NOT EXISTS
|
5:7 NOT OR EXISTS AND EXISTS NOT EXISTS
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ fn quoted_by(quote: char, input: Span) -> IResult<Token> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// word = (alphanumeric | _ | - | .)+ except for reserved keywords
|
// word = (alphanumeric | _ | - | .)+ except for reserved keywords
|
||||||
pub fn word_not_keyword<'a>(input: Span<'a>) -> IResult<Token<'a>> {
|
pub fn word_not_keyword<'a>(input: Span<'a>) -> IResult<'a, Token<'a>> {
|
||||||
let (input, word): (_, Token<'a>) =
|
let (input, word): (_, Token<'a>) =
|
||||||
take_while1(is_value_component)(input).map(|(s, t)| (s, t.into()))?;
|
take_while1(is_value_component)(input).map(|(s, t)| (s, t.into()))?;
|
||||||
if is_keyword(word.value()) {
|
if is_keyword(word.value()) {
|
||||||
@@ -80,6 +80,51 @@ pub fn word_exact<'a, 'b: 'a>(tag: &'b str) -> impl Fn(Span<'a>) -> IResult<'a,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// vector_value = ( non_dot_word | singleQuoted | doubleQuoted)
|
||||||
|
pub fn parse_vector_value(input: Span) -> IResult<Token> {
|
||||||
|
pub fn non_dot_word(input: Span) -> IResult<Token> {
|
||||||
|
let (input, word) = take_while1(|c| is_value_component(c) && c != '.')(input)?;
|
||||||
|
Ok((input, word.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let (input, value) = alt((
|
||||||
|
delimited(char('\''), cut(|input| quoted_by('\'', input)), cut(char('\''))),
|
||||||
|
delimited(char('"'), cut(|input| quoted_by('"', input)), cut(char('"'))),
|
||||||
|
non_dot_word,
|
||||||
|
))(input)?;
|
||||||
|
|
||||||
|
match unescaper::unescape(value.value()) {
|
||||||
|
Ok(content) => {
|
||||||
|
if content.len() != value.value().len() {
|
||||||
|
Ok((input, Token::new(value.original_span(), Some(content))))
|
||||||
|
} else {
|
||||||
|
Ok((input, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(unescaper::Error::IncompleteStr(_)) => Err(nom::Err::Incomplete(nom::Needed::Unknown)),
|
||||||
|
Err(unescaper::Error::ParseIntError { .. }) => Err(nom::Err::Error(Error::new_from_kind(
|
||||||
|
value.original_span(),
|
||||||
|
ErrorKind::InvalidEscapedNumber,
|
||||||
|
))),
|
||||||
|
Err(unescaper::Error::InvalidChar { .. }) => Err(nom::Err::Error(Error::new_from_kind(
|
||||||
|
value.original_span(),
|
||||||
|
ErrorKind::MalformedValue,
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_vector_value_cut<'a>(input: Span<'a>, kind: ErrorKind<'a>) -> IResult<'a, Token<'a>> {
|
||||||
|
parse_vector_value(input).map_err(|e| match e {
|
||||||
|
nom::Err::Failure(e) => match e.kind() {
|
||||||
|
ErrorKind::Char(c) if *c == '"' || *c == '\'' => {
|
||||||
|
crate::Error::failure_from_kind(input, ErrorKind::VectorFilterInvalidQuotes)
|
||||||
|
}
|
||||||
|
_ => crate::Error::failure_from_kind(input, kind),
|
||||||
|
},
|
||||||
|
_ => crate::Error::failure_from_kind(input, kind),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// value = WS* ( word | singleQuoted | doubleQuoted) WS+
|
/// value = WS* ( word | singleQuoted | doubleQuoted) WS+
|
||||||
pub fn parse_value(input: Span) -> IResult<Token> {
|
pub fn parse_value(input: Span) -> IResult<Token> {
|
||||||
// to get better diagnostic message we are going to strip the left whitespaces from the input right now
|
// to get better diagnostic message we are going to strip the left whitespaces from the input right now
|
||||||
@@ -99,31 +144,21 @@ pub fn parse_value(input: Span) -> IResult<Token> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match parse_geo_radius(input) {
|
match parse_geo_radius(input) {
|
||||||
Ok(_) => {
|
Ok(_) => return Err(Error::failure_from_kind(input, ErrorKind::MisusedGeoRadius)),
|
||||||
return Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::MisusedGeoRadius)))
|
|
||||||
}
|
|
||||||
// if we encountered a failure it means the user badly wrote a _geoRadius filter.
|
// if we encountered a failure it means the user badly wrote a _geoRadius filter.
|
||||||
// But instead of showing them how to fix his syntax we are going to tell them they should not use this filter as a value.
|
// But instead of showing them how to fix his syntax we are going to tell them they should not use this filter as a value.
|
||||||
Err(e) if e.is_failure() => {
|
Err(e) if e.is_failure() => {
|
||||||
return Err(nom::Err::Failure(Error::new_from_kind(input, ErrorKind::MisusedGeoRadius)))
|
return Err(Error::failure_from_kind(input, ErrorKind::MisusedGeoRadius))
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
match parse_geo_bounding_box(input) {
|
match parse_geo_bounding_box(input) {
|
||||||
Ok(_) => {
|
Ok(_) => return Err(Error::failure_from_kind(input, ErrorKind::MisusedGeoBoundingBox)),
|
||||||
return Err(nom::Err::Failure(Error::new_from_kind(
|
|
||||||
input,
|
|
||||||
ErrorKind::MisusedGeoBoundingBox,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
// if we encountered a failure it means the user badly wrote a _geoBoundingBox filter.
|
// if we encountered a failure it means the user badly wrote a _geoBoundingBox filter.
|
||||||
// But instead of showing them how to fix his syntax we are going to tell them they should not use this filter as a value.
|
// But instead of showing them how to fix his syntax we are going to tell them they should not use this filter as a value.
|
||||||
Err(e) if e.is_failure() => {
|
Err(e) if e.is_failure() => {
|
||||||
return Err(nom::Err::Failure(Error::new_from_kind(
|
return Err(Error::failure_from_kind(input, ErrorKind::MisusedGeoBoundingBox))
|
||||||
input,
|
|
||||||
ErrorKind::MisusedGeoBoundingBox,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ license.workspace = true
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.5.1", features = ["html_reports"] }
|
criterion = { version = "0.6.0", features = ["html_reports"] }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmarks"
|
name = "benchmarks"
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arbitrary = { version = "1.4.1", features = ["derive"] }
|
arbitrary = { version = "1.4.1", features = ["derive"] }
|
||||||
bumpalo = "3.16.0"
|
bumpalo = "3.18.1"
|
||||||
clap = { version = "4.5.24", features = ["derive"] }
|
clap = { version = "4.5.40", features = ["derive"] }
|
||||||
either = "1.13.0"
|
either = "1.15.0"
|
||||||
fastrand = "2.3.0"
|
fastrand = "2.3.0"
|
||||||
milli = { path = "../milli" }
|
milli = { path = "../milli" }
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.135", features = ["preserve_order"] }
|
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
||||||
tempfile = "3.15.0"
|
tempfile = "3.20.0"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use milli::heed::EnvOpenOptions;
|
|||||||
use milli::progress::Progress;
|
use milli::progress::Progress;
|
||||||
use milli::update::new::indexer;
|
use milli::update::new::indexer;
|
||||||
use milli::update::IndexerConfig;
|
use milli::update::IndexerConfig;
|
||||||
use milli::vector::EmbeddingConfigs;
|
use milli::vector::RuntimeEmbedders;
|
||||||
use milli::Index;
|
use milli::Index;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
@@ -89,7 +89,7 @@ fn main() {
|
|||||||
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
let mut new_fields_ids_map = db_fields_ids_map.clone();
|
||||||
|
|
||||||
let indexer_alloc = Bump::new();
|
let indexer_alloc = Bump::new();
|
||||||
let embedders = EmbeddingConfigs::default();
|
let embedders = RuntimeEmbedders::default();
|
||||||
let mut indexer = indexer::DocumentOperation::new();
|
let mut indexer = indexer::DocumentOperation::new();
|
||||||
|
|
||||||
let mut operations = Vec::new();
|
let mut operations = Vec::new();
|
||||||
@@ -129,6 +129,7 @@ fn main() {
|
|||||||
&mut new_fields_ids_map,
|
&mut new_fields_ids_map,
|
||||||
&|| false,
|
&|| false,
|
||||||
Progress::default(),
|
Progress::default(),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -144,6 +145,7 @@ fn main() {
|
|||||||
embedders,
|
embedders,
|
||||||
&|| false,
|
&|| false,
|
||||||
&Progress::default(),
|
&Progress::default(),
|
||||||
|
&Default::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -11,31 +11,31 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.98"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
byte-unit = "5.1.6"
|
byte-unit = "5.1.6"
|
||||||
bumpalo = "3.16.0"
|
bumpalo = "3.18.1"
|
||||||
bumparaw-collections = "0.1.4"
|
bumparaw-collections = "0.1.4"
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.8.0"
|
||||||
csv = "1.3.1"
|
csv = "1.3.1"
|
||||||
derive_builder = "0.20.2"
|
derive_builder = "0.20.2"
|
||||||
dump = { path = "../dump" }
|
dump = { path = "../dump" }
|
||||||
enum-iterator = "2.1.0"
|
enum-iterator = "2.1.0"
|
||||||
file-store = { path = "../file-store" }
|
file-store = { path = "../file-store" }
|
||||||
flate2 = "1.0.35"
|
flate2 = "1.1.2"
|
||||||
indexmap = "2.7.0"
|
indexmap = "2.9.0"
|
||||||
meilisearch-auth = { path = "../meilisearch-auth" }
|
meilisearch-auth = { path = "../meilisearch-auth" }
|
||||||
meilisearch-types = { path = "../meilisearch-types" }
|
meilisearch-types = { path = "../meilisearch-types" }
|
||||||
memmap2 = "0.9.5"
|
memmap2 = "0.9.7"
|
||||||
page_size = "0.6.0"
|
page_size = "0.6.0"
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
roaring = { version = "0.10.10", features = ["serde"] }
|
roaring = { version = "0.10.12", features = ["serde"] }
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.138", features = ["preserve_order"] }
|
serde_json = { version = "1.0.140", features = ["preserve_order"] }
|
||||||
synchronoise = "1.0.1"
|
synchronoise = "1.0.1"
|
||||||
tempfile = "3.15.0"
|
tempfile = "3.20.0"
|
||||||
thiserror = "2.0.9"
|
thiserror = "2.0.12"
|
||||||
time = { version = "0.3.37", features = [
|
time = { version = "0.3.41", features = [
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
"formatting",
|
"formatting",
|
||||||
"parsing",
|
"parsing",
|
||||||
@@ -43,11 +43,12 @@ time = { version = "0.3.37", features = [
|
|||||||
] }
|
] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
ureq = "2.12.1"
|
ureq = "2.12.1"
|
||||||
uuid = { version = "1.11.0", features = ["serde", "v4"] }
|
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
||||||
|
backoff = "0.4.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
big_s = "1.0.2"
|
big_s = "1.0.2"
|
||||||
crossbeam-channel = "0.5.14"
|
crossbeam-channel = "0.5.15"
|
||||||
# fixed version due to format breakages in v1.40
|
# fixed version due to format breakages in v1.40
|
||||||
insta = { version = "=1.39.0", features = ["json", "redactions"] }
|
insta = { version = "=1.39.0", features = ["json", "redactions"] }
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::io;
|
|||||||
use dump::{KindDump, TaskDump, UpdateFile};
|
use dump::{KindDump, TaskDump, UpdateFile};
|
||||||
use meilisearch_types::batches::{Batch, BatchId};
|
use meilisearch_types::batches::{Batch, BatchId};
|
||||||
use meilisearch_types::heed::RwTxn;
|
use meilisearch_types::heed::RwTxn;
|
||||||
|
use meilisearch_types::index_uid_pattern::IndexUidPattern;
|
||||||
use meilisearch_types::milli;
|
use meilisearch_types::milli;
|
||||||
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task};
|
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task};
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
@@ -146,6 +147,7 @@ impl<'a> Dump<'a> {
|
|||||||
canceled_by: task.canceled_by,
|
canceled_by: task.canceled_by,
|
||||||
details: task.details,
|
details: task.details,
|
||||||
status: task.status,
|
status: task.status,
|
||||||
|
network: task.network,
|
||||||
kind: match task.kind {
|
kind: match task.kind {
|
||||||
KindDump::DocumentImport {
|
KindDump::DocumentImport {
|
||||||
primary_key,
|
primary_key,
|
||||||
@@ -196,9 +198,10 @@ impl<'a> Dump<'a> {
|
|||||||
index_uid: task.index_uid.ok_or(Error::CorruptedDump)?,
|
index_uid: task.index_uid.ok_or(Error::CorruptedDump)?,
|
||||||
primary_key,
|
primary_key,
|
||||||
},
|
},
|
||||||
KindDump::IndexUpdate { primary_key } => KindWithContent::IndexUpdate {
|
KindDump::IndexUpdate { primary_key, uid } => KindWithContent::IndexUpdate {
|
||||||
index_uid: task.index_uid.ok_or(Error::CorruptedDump)?,
|
index_uid: task.index_uid.ok_or(Error::CorruptedDump)?,
|
||||||
primary_key,
|
primary_key,
|
||||||
|
new_index_uid: uid,
|
||||||
},
|
},
|
||||||
KindDump::IndexSwap { swaps } => KindWithContent::IndexSwap { swaps },
|
KindDump::IndexSwap { swaps } => KindWithContent::IndexSwap { swaps },
|
||||||
KindDump::TaskCancelation { query, tasks } => {
|
KindDump::TaskCancelation { query, tasks } => {
|
||||||
@@ -211,6 +214,23 @@ impl<'a> Dump<'a> {
|
|||||||
KindWithContent::DumpCreation { keys, instance_uid }
|
KindWithContent::DumpCreation { keys, instance_uid }
|
||||||
}
|
}
|
||||||
KindDump::SnapshotCreation => KindWithContent::SnapshotCreation,
|
KindDump::SnapshotCreation => KindWithContent::SnapshotCreation,
|
||||||
|
KindDump::Export { url, api_key, payload_size, indexes } => {
|
||||||
|
KindWithContent::Export {
|
||||||
|
url,
|
||||||
|
api_key,
|
||||||
|
payload_size,
|
||||||
|
indexes: indexes
|
||||||
|
.into_iter()
|
||||||
|
.map(|(pattern, settings)| {
|
||||||
|
Ok((
|
||||||
|
IndexUidPattern::try_from(pattern)
|
||||||
|
.map_err(|_| Error::CorruptedDump)?,
|
||||||
|
settings,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<Result<_, Error>>()?,
|
||||||
|
}
|
||||||
|
}
|
||||||
KindDump::UpgradeDatabase { from } => KindWithContent::UpgradeDatabase { from },
|
KindDump::UpgradeDatabase { from } => KindWithContent::UpgradeDatabase { from },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::fmt::Display;
|
|||||||
|
|
||||||
use meilisearch_types::batches::BatchId;
|
use meilisearch_types::batches::BatchId;
|
||||||
use meilisearch_types::error::{Code, ErrorCode};
|
use meilisearch_types::error::{Code, ErrorCode};
|
||||||
|
use meilisearch_types::milli::index::RollbackOutcome;
|
||||||
use meilisearch_types::tasks::{Kind, Status};
|
use meilisearch_types::tasks::{Kind, Status};
|
||||||
use meilisearch_types::{heed, milli};
|
use meilisearch_types::{heed, milli};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -66,6 +67,8 @@ pub enum Error {
|
|||||||
SwapDuplicateIndexesFound(Vec<String>),
|
SwapDuplicateIndexesFound(Vec<String>),
|
||||||
#[error("Index `{0}` not found.")]
|
#[error("Index `{0}` not found.")]
|
||||||
SwapIndexNotFound(String),
|
SwapIndexNotFound(String),
|
||||||
|
#[error("Cannot rename `{0}` to `{1}` as the index already exists. Hint: You can remove `{1}` first and then do your remove.")]
|
||||||
|
SwapIndexFoundDuringRename(String, String),
|
||||||
#[error("Meilisearch cannot receive write operations because the limit of the task database has been reached. Please delete tasks to continue performing write operations.")]
|
#[error("Meilisearch cannot receive write operations because the limit of the task database has been reached. Please delete tasks to continue performing write operations.")]
|
||||||
NoSpaceLeftInTaskQueue,
|
NoSpaceLeftInTaskQueue,
|
||||||
#[error(
|
#[error(
|
||||||
@@ -73,6 +76,10 @@ pub enum Error {
|
|||||||
.0.iter().map(|s| format!("`{}`", s)).collect::<Vec<_>>().join(", ")
|
.0.iter().map(|s| format!("`{}`", s)).collect::<Vec<_>>().join(", ")
|
||||||
)]
|
)]
|
||||||
SwapIndexesNotFound(Vec<String>),
|
SwapIndexesNotFound(Vec<String>),
|
||||||
|
#[error("The following indexes are being renamed but cannot because their new name conflicts with an already existing index: {}. Renaming doesn't overwrite the other index name.",
|
||||||
|
.0.iter().map(|s| format!("`{}`", s)).collect::<Vec<_>>().join(", ")
|
||||||
|
)]
|
||||||
|
SwapIndexesFoundDuringRename(Vec<String>),
|
||||||
#[error("Corrupted dump.")]
|
#[error("Corrupted dump.")]
|
||||||
CorruptedDump,
|
CorruptedDump,
|
||||||
#[error(
|
#[error(
|
||||||
@@ -151,7 +158,27 @@ pub enum Error {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
DatabaseUpgrade(Box<Self>),
|
DatabaseUpgrade(Box<Self>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
Export(Box<Self>),
|
||||||
|
#[error("Failed to export documents to remote server {code} ({type}): {message} <{link}>")]
|
||||||
|
FromRemoteWhenExporting { message: String, code: String, r#type: String, link: String },
|
||||||
|
#[error("Failed to rollback for index `{index}`: {rollback_outcome} ")]
|
||||||
|
RollbackFailed { index: String, rollback_outcome: RollbackOutcome },
|
||||||
|
#[error(transparent)]
|
||||||
UnrecoverableError(Box<Self>),
|
UnrecoverableError(Box<Self>),
|
||||||
|
#[error("The index scheduler is in version v{}.{}.{}, but Meilisearch is in version v{}.{}.{}.\n - hint: start the correct version of Meilisearch, or consider updating your database. See also <https://www.meilisearch.com/docs/learn/update_and_migration/updating>",
|
||||||
|
index_scheduler_version.0, index_scheduler_version.1, index_scheduler_version.2,
|
||||||
|
package_version.0, package_version.1, package_version.2)]
|
||||||
|
IndexSchedulerVersionMismatch {
|
||||||
|
index_scheduler_version: (u32, u32, u32),
|
||||||
|
package_version: (u32, u32, u32),
|
||||||
|
},
|
||||||
|
#[error("Index `{index}` is in version v{}.{}.{}, but Meilisearch is in version v{}.{}.{}.\n - note: this is an internal error, please consider filing a bug report: <https://github.com/meilisearch/meilisearch/issues/new?template=bug_report.md>",
|
||||||
|
index_version.0, index_version.1, index_version.2, package_version.0, package_version.1, package_version.2)]
|
||||||
|
IndexVersionMismatch {
|
||||||
|
index: String,
|
||||||
|
index_version: (u32, u32, u32),
|
||||||
|
package_version: (u32, u32, u32),
|
||||||
|
},
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
HeedTransaction(heed::Error),
|
HeedTransaction(heed::Error),
|
||||||
|
|
||||||
@@ -182,6 +209,8 @@ impl Error {
|
|||||||
| Error::SwapIndexNotFound(_)
|
| Error::SwapIndexNotFound(_)
|
||||||
| Error::NoSpaceLeftInTaskQueue
|
| Error::NoSpaceLeftInTaskQueue
|
||||||
| Error::SwapIndexesNotFound(_)
|
| Error::SwapIndexesNotFound(_)
|
||||||
|
| Error::SwapIndexFoundDuringRename(_, _)
|
||||||
|
| Error::SwapIndexesFoundDuringRename(_)
|
||||||
| Error::CorruptedDump
|
| Error::CorruptedDump
|
||||||
| Error::InvalidTaskDate { .. }
|
| Error::InvalidTaskDate { .. }
|
||||||
| Error::InvalidTaskUid { .. }
|
| Error::InvalidTaskUid { .. }
|
||||||
@@ -195,6 +224,7 @@ impl Error {
|
|||||||
| Error::BatchNotFound(_)
|
| Error::BatchNotFound(_)
|
||||||
| Error::TaskDeletionWithEmptyQuery
|
| Error::TaskDeletionWithEmptyQuery
|
||||||
| Error::TaskCancelationWithEmptyQuery
|
| Error::TaskCancelationWithEmptyQuery
|
||||||
|
| Error::FromRemoteWhenExporting { .. }
|
||||||
| Error::AbortedTask
|
| Error::AbortedTask
|
||||||
| Error::Dump(_)
|
| Error::Dump(_)
|
||||||
| Error::Heed(_)
|
| Error::Heed(_)
|
||||||
@@ -204,11 +234,15 @@ impl Error {
|
|||||||
| Error::IoError(_)
|
| Error::IoError(_)
|
||||||
| Error::Persist(_)
|
| Error::Persist(_)
|
||||||
| Error::FeatureNotEnabled(_)
|
| Error::FeatureNotEnabled(_)
|
||||||
|
| Error::Export(_)
|
||||||
| Error::Anyhow(_) => true,
|
| Error::Anyhow(_) => true,
|
||||||
Error::CreateBatch(_)
|
Error::CreateBatch(_)
|
||||||
| Error::CorruptedTaskQueue
|
| Error::CorruptedTaskQueue
|
||||||
| Error::DatabaseUpgrade(_)
|
| Error::DatabaseUpgrade(_)
|
||||||
| Error::UnrecoverableError(_)
|
| Error::UnrecoverableError(_)
|
||||||
|
| Error::IndexSchedulerVersionMismatch { .. }
|
||||||
|
| Error::IndexVersionMismatch { .. }
|
||||||
|
| Error::RollbackFailed { .. }
|
||||||
| Error::HeedTransaction(_) => false,
|
| Error::HeedTransaction(_) => false,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
Error::PlannedFailure => false,
|
Error::PlannedFailure => false,
|
||||||
@@ -245,6 +279,8 @@ impl ErrorCode for Error {
|
|||||||
Error::SwapDuplicateIndexFound(_) => Code::InvalidSwapDuplicateIndexFound,
|
Error::SwapDuplicateIndexFound(_) => Code::InvalidSwapDuplicateIndexFound,
|
||||||
Error::SwapIndexNotFound(_) => Code::IndexNotFound,
|
Error::SwapIndexNotFound(_) => Code::IndexNotFound,
|
||||||
Error::SwapIndexesNotFound(_) => Code::IndexNotFound,
|
Error::SwapIndexesNotFound(_) => Code::IndexNotFound,
|
||||||
|
Error::SwapIndexFoundDuringRename(_, _) => Code::IndexAlreadyExists,
|
||||||
|
Error::SwapIndexesFoundDuringRename(_) => Code::IndexAlreadyExists,
|
||||||
Error::InvalidTaskDate { field, .. } => (*field).into(),
|
Error::InvalidTaskDate { field, .. } => (*field).into(),
|
||||||
Error::InvalidTaskUid { .. } => Code::InvalidTaskUids,
|
Error::InvalidTaskUid { .. } => Code::InvalidTaskUids,
|
||||||
Error::InvalidBatchUid { .. } => Code::InvalidBatchUids,
|
Error::InvalidBatchUid { .. } => Code::InvalidBatchUids,
|
||||||
@@ -262,6 +298,7 @@ impl ErrorCode for Error {
|
|||||||
Error::Dump(e) => e.error_code(),
|
Error::Dump(e) => e.error_code(),
|
||||||
Error::Milli { error, .. } => error.error_code(),
|
Error::Milli { error, .. } => error.error_code(),
|
||||||
Error::ProcessBatchPanicked(_) => Code::Internal,
|
Error::ProcessBatchPanicked(_) => Code::Internal,
|
||||||
|
Error::FromRemoteWhenExporting { .. } => Code::Internal,
|
||||||
Error::Heed(e) => e.error_code(),
|
Error::Heed(e) => e.error_code(),
|
||||||
Error::HeedTransaction(e) => e.error_code(),
|
Error::HeedTransaction(e) => e.error_code(),
|
||||||
Error::FileStore(e) => e.error_code(),
|
Error::FileStore(e) => e.error_code(),
|
||||||
@@ -274,7 +311,11 @@ impl ErrorCode for Error {
|
|||||||
Error::CorruptedTaskQueue => Code::Internal,
|
Error::CorruptedTaskQueue => Code::Internal,
|
||||||
Error::CorruptedDump => Code::Internal,
|
Error::CorruptedDump => Code::Internal,
|
||||||
Error::DatabaseUpgrade(_) => Code::Internal,
|
Error::DatabaseUpgrade(_) => Code::Internal,
|
||||||
|
Error::Export(_) => Code::Internal,
|
||||||
|
Error::RollbackFailed { .. } => Code::Internal,
|
||||||
Error::UnrecoverableError(_) => Code::Internal,
|
Error::UnrecoverableError(_) => Code::Internal,
|
||||||
|
Error::IndexSchedulerVersionMismatch { .. } => Code::Internal,
|
||||||
|
Error::IndexVersionMismatch { .. } => Code::Internal,
|
||||||
Error::CreateBatch(_) => Code::Internal,
|
Error::CreateBatch(_) => Code::Internal,
|
||||||
|
|
||||||
// This one should never be seen by the end user
|
// This one should never be seen by the end user
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use meilisearch_types::features::{InstanceTogglableFeatures, Network, RuntimeTogglableFeatures};
|
use meilisearch_types::enterprise_edition::network::Network;
|
||||||
|
use meilisearch_types::features::{InstanceTogglableFeatures, RuntimeTogglableFeatures};
|
||||||
use meilisearch_types::heed::types::{SerdeJson, Str};
|
use meilisearch_types::heed::types::{SerdeJson, Str};
|
||||||
use meilisearch_types::heed::{Database, Env, RwTxn, WithoutTls};
|
use meilisearch_types::heed::{Database, Env, RwTxn, WithoutTls};
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ impl RoFeatures {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(FeatureNotEnabledError {
|
Err(FeatureNotEnabledError {
|
||||||
disabled_action: "Using `CONTAINS` or `STARTS WITH` in a filter",
|
disabled_action: "Using `CONTAINS` in a filter",
|
||||||
feature: "contains filter",
|
feature: "contains filter",
|
||||||
issue_link: "https://github.com/orgs/meilisearch/discussions/763",
|
issue_link: "https://github.com/orgs/meilisearch/discussions/763",
|
||||||
}
|
}
|
||||||
@@ -131,6 +132,32 @@ impl RoFeatures {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_chat_completions(&self, disabled_action: &'static str) -> Result<()> {
|
||||||
|
if self.runtime.chat_completions {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(FeatureNotEnabledError {
|
||||||
|
disabled_action,
|
||||||
|
feature: "chat completions",
|
||||||
|
issue_link: "https://github.com/orgs/meilisearch/discussions/835",
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_multimodal(&self, disabled_action: &'static str) -> Result<()> {
|
||||||
|
if self.runtime.multimodal {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(FeatureNotEnabledError {
|
||||||
|
disabled_action,
|
||||||
|
feature: "multimodal",
|
||||||
|
issue_link: "https://github.com/orgs/meilisearch/discussions/846",
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureData {
|
impl FeatureData {
|
||||||
@@ -156,6 +183,7 @@ impl FeatureData {
|
|||||||
..persisted_features
|
..persisted_features
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Once this is stabilized, network should be stored along with webhooks in index-scheduler's persisted database
|
||||||
let network_db = runtime_features_db.remap_data_type::<SerdeJson<Network>>();
|
let network_db = runtime_features_db.remap_data_type::<SerdeJson<Network>>();
|
||||||
let network: Network = network_db.get(wtxn, db_keys::NETWORK)?.unwrap_or_default();
|
let network: Network = network_db.get(wtxn, db_keys::NETWORK)?.unwrap_or_default();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use meilisearch_types::heed::types::{SerdeJson, Str};
|
|||||||
use meilisearch_types::heed::{Database, Env, RoTxn, RwTxn, WithoutTls};
|
use meilisearch_types::heed::{Database, Env, RoTxn, RwTxn, WithoutTls};
|
||||||
use meilisearch_types::milli;
|
use meilisearch_types::milli;
|
||||||
use meilisearch_types::milli::database_stats::DatabaseStats;
|
use meilisearch_types::milli::database_stats::DatabaseStats;
|
||||||
|
use meilisearch_types::milli::index::RollbackOutcome;
|
||||||
use meilisearch_types::milli::update::IndexerConfig;
|
use meilisearch_types::milli::update::IndexerConfig;
|
||||||
use meilisearch_types::milli::{FieldDistribution, Index};
|
use meilisearch_types::milli::{FieldDistribution, Index};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -70,7 +71,7 @@ pub struct IndexMapper {
|
|||||||
/// Path to the folder where the LMDB environments of each index are.
|
/// Path to the folder where the LMDB environments of each index are.
|
||||||
base_path: PathBuf,
|
base_path: PathBuf,
|
||||||
/// The map size an index is opened with on the first time.
|
/// The map size an index is opened with on the first time.
|
||||||
index_base_map_size: usize,
|
pub(crate) index_base_map_size: usize,
|
||||||
/// The quantity by which the map size of an index is incremented upon reopening, in bytes.
|
/// The quantity by which the map size of an index is incremented upon reopening, in bytes.
|
||||||
index_growth_amount: usize,
|
index_growth_amount: usize,
|
||||||
/// Whether we open a meilisearch index with the MDB_WRITEMAP option or not.
|
/// Whether we open a meilisearch index with the MDB_WRITEMAP option or not.
|
||||||
@@ -431,6 +432,51 @@ impl IndexMapper {
|
|||||||
Ok(index)
|
Ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rollback_index(
|
||||||
|
&self,
|
||||||
|
rtxn: &RoTxn,
|
||||||
|
name: &str,
|
||||||
|
to: (u32, u32, u32),
|
||||||
|
) -> Result<RollbackOutcome> {
|
||||||
|
// remove any currently updating index to make sure that we aren't keeping a reference to the index somewhere
|
||||||
|
drop(self.currently_updating_index.write().unwrap().take());
|
||||||
|
|
||||||
|
let uuid = self
|
||||||
|
.index_mapping
|
||||||
|
.get(rtxn, name)?
|
||||||
|
.ok_or_else(|| Error::IndexNotFound(name.to_string()))?;
|
||||||
|
|
||||||
|
// take the lock to make sure noone is messing with the indexes while we rollback
|
||||||
|
// this will block any search or other operation, but we are rollbacking so this is probably acceptable.
|
||||||
|
let mut index_map = self.index_map.write().unwrap();
|
||||||
|
|
||||||
|
'close_index: loop {
|
||||||
|
match index_map.get(&uuid) {
|
||||||
|
Available(_) => {
|
||||||
|
index_map.close_for_resize(&uuid, self.enable_mdb_writemap, 0);
|
||||||
|
// index should now be `Closing`; try again
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// index already closed
|
||||||
|
Missing => break 'close_index,
|
||||||
|
// closing requested by this thread or another one; wait for closing to complete, then exit
|
||||||
|
Closing(closing_index) => {
|
||||||
|
if closing_index.wait_timeout(Duration::from_secs(100)).is_none() {
|
||||||
|
// release the lock so it doesn't get poisoned
|
||||||
|
drop(index_map);
|
||||||
|
panic!("cannot close index")
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BeingDeleted => return Err(Error::IndexNotFound(name.to_string())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let index_path = self.base_path.join(uuid.to_string());
|
||||||
|
Index::rollback(milli::heed::EnvOpenOptions::new().read_txn_without_tls(), index_path, to)
|
||||||
|
.map_err(|err| crate::Error::from_milli(err, Some(name.to_string())))
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts `f` for each index that exists in the index mapper.
|
/// Attempts `f` for each index that exists in the index mapper.
|
||||||
///
|
///
|
||||||
/// It is preferable to use this function rather than a loop that opens all indexes, as a way to avoid having all indexes opened,
|
/// It is preferable to use this function rather than a loop that opens all indexes, as a way to avoid having all indexes opened,
|
||||||
@@ -480,6 +526,20 @@ impl IndexMapper {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rename an index.
|
||||||
|
pub fn rename(&self, wtxn: &mut RwTxn, current: &str, new: &str) -> Result<()> {
|
||||||
|
let uuid = self
|
||||||
|
.index_mapping
|
||||||
|
.get(wtxn, current)?
|
||||||
|
.ok_or_else(|| Error::IndexNotFound(current.to_string()))?;
|
||||||
|
if self.index_mapping.get(wtxn, new)?.is_some() {
|
||||||
|
return Err(Error::IndexAlreadyExists(new.to_string()));
|
||||||
|
}
|
||||||
|
self.index_mapping.delete(wtxn, current)?;
|
||||||
|
self.index_mapping.put(wtxn, new, &uuid)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// The stats of an index.
|
/// The stats of an index.
|
||||||
///
|
///
|
||||||
/// If available in the cache, they are directly returned.
|
/// If available in the cache, they are directly returned.
|
||||||
|
|||||||
@@ -20,20 +20,22 @@ pub fn snapshot_index_scheduler(scheduler: &IndexScheduler) -> String {
|
|||||||
|
|
||||||
let IndexScheduler {
|
let IndexScheduler {
|
||||||
cleanup_enabled: _,
|
cleanup_enabled: _,
|
||||||
|
experimental_no_edition_2024_for_dumps: _,
|
||||||
processing_tasks,
|
processing_tasks,
|
||||||
env,
|
env,
|
||||||
version,
|
version,
|
||||||
queue,
|
queue,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
persisted,
|
||||||
|
|
||||||
index_mapper,
|
index_mapper,
|
||||||
features: _,
|
features: _,
|
||||||
webhook_url: _,
|
webhooks: _,
|
||||||
webhook_authorization_header: _,
|
|
||||||
test_breakpoint_sdr: _,
|
test_breakpoint_sdr: _,
|
||||||
planned_failures: _,
|
planned_failures: _,
|
||||||
run_loop_iteration: _,
|
run_loop_iteration: _,
|
||||||
embedders: _,
|
embedders: _,
|
||||||
|
chat_settings: _,
|
||||||
} = scheduler;
|
} = scheduler;
|
||||||
|
|
||||||
let rtxn = env.read_txn().unwrap();
|
let rtxn = env.read_txn().unwrap();
|
||||||
@@ -41,11 +43,8 @@ pub fn snapshot_index_scheduler(scheduler: &IndexScheduler) -> String {
|
|||||||
let mut snap = String::new();
|
let mut snap = String::new();
|
||||||
|
|
||||||
let indx_sched_version = version.get_version(&rtxn).unwrap();
|
let indx_sched_version = version.get_version(&rtxn).unwrap();
|
||||||
let latest_version = (
|
let latest_version =
|
||||||
versioning::VERSION_MAJOR.parse().unwrap(),
|
(versioning::VERSION_MAJOR, versioning::VERSION_MINOR, versioning::VERSION_PATCH);
|
||||||
versioning::VERSION_MINOR.parse().unwrap(),
|
|
||||||
versioning::VERSION_PATCH.parse().unwrap(),
|
|
||||||
);
|
|
||||||
if indx_sched_version != Some(latest_version) {
|
if indx_sched_version != Some(latest_version) {
|
||||||
snap.push_str(&format!("index scheduler running on version {indx_sched_version:?}\n"));
|
snap.push_str(&format!("index scheduler running on version {indx_sched_version:?}\n"));
|
||||||
}
|
}
|
||||||
@@ -63,6 +62,13 @@ pub fn snapshot_index_scheduler(scheduler: &IndexScheduler) -> String {
|
|||||||
}
|
}
|
||||||
snap.push_str("\n----------------------------------------------------------------------\n");
|
snap.push_str("\n----------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
let persisted_db_snapshot = snapshot_persisted_db(&rtxn, persisted);
|
||||||
|
if !persisted_db_snapshot.is_empty() {
|
||||||
|
snap.push_str("### Persisted:\n");
|
||||||
|
snap.push_str(&persisted_db_snapshot);
|
||||||
|
snap.push_str("----------------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
snap.push_str("### All Tasks:\n");
|
snap.push_str("### All Tasks:\n");
|
||||||
snap.push_str(&snapshot_all_tasks(&rtxn, queue.tasks.all_tasks));
|
snap.push_str(&snapshot_all_tasks(&rtxn, queue.tasks.all_tasks));
|
||||||
snap.push_str("----------------------------------------------------------------------\n");
|
snap.push_str("----------------------------------------------------------------------\n");
|
||||||
@@ -201,6 +207,16 @@ pub fn snapshot_date_db(rtxn: &RoTxn, db: Database<BEI128, CboRoaringBitmapCodec
|
|||||||
snap
|
snap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapshot_persisted_db(rtxn: &RoTxn, db: &Database<Str, Str>) -> String {
|
||||||
|
let mut snap = String::new();
|
||||||
|
let iter = db.iter(rtxn).unwrap();
|
||||||
|
for next in iter {
|
||||||
|
let (key, value) = next.unwrap();
|
||||||
|
snap.push_str(&format!("{key}: {value}\n"));
|
||||||
|
}
|
||||||
|
snap
|
||||||
|
}
|
||||||
|
|
||||||
pub fn snapshot_task(task: &Task) -> String {
|
pub fn snapshot_task(task: &Task) -> String {
|
||||||
let mut snap = String::new();
|
let mut snap = String::new();
|
||||||
let Task {
|
let Task {
|
||||||
@@ -214,6 +230,7 @@ pub fn snapshot_task(task: &Task) -> String {
|
|||||||
details,
|
details,
|
||||||
status,
|
status,
|
||||||
kind,
|
kind,
|
||||||
|
network,
|
||||||
} = task;
|
} = task;
|
||||||
snap.push('{');
|
snap.push('{');
|
||||||
snap.push_str(&format!("uid: {uid}, "));
|
snap.push_str(&format!("uid: {uid}, "));
|
||||||
@@ -231,6 +248,9 @@ pub fn snapshot_task(task: &Task) -> String {
|
|||||||
snap.push_str(&format!("details: {}, ", &snapshot_details(details)));
|
snap.push_str(&format!("details: {}, ", &snapshot_details(details)));
|
||||||
}
|
}
|
||||||
snap.push_str(&format!("kind: {kind:?}"));
|
snap.push_str(&format!("kind: {kind:?}"));
|
||||||
|
if let Some(network) = network {
|
||||||
|
snap.push_str(&format!("network: {network:?}, "))
|
||||||
|
}
|
||||||
|
|
||||||
snap.push('}');
|
snap.push('}');
|
||||||
snap
|
snap
|
||||||
@@ -258,8 +278,8 @@ fn snapshot_details(d: &Details) -> String {
|
|||||||
Details::SettingsUpdate { settings } => {
|
Details::SettingsUpdate { settings } => {
|
||||||
format!("{{ settings: {settings:?} }}")
|
format!("{{ settings: {settings:?} }}")
|
||||||
}
|
}
|
||||||
Details::IndexInfo { primary_key } => {
|
Details::IndexInfo { primary_key, new_index_uid, old_index_uid } => {
|
||||||
format!("{{ primary_key: {primary_key:?} }}")
|
format!("{{ primary_key: {primary_key:?}, old_new_uid: {old_index_uid:?}, new_index_uid: {new_index_uid:?} }}")
|
||||||
}
|
}
|
||||||
Details::DocumentDeletion {
|
Details::DocumentDeletion {
|
||||||
provided_ids: received_document_ids,
|
provided_ids: received_document_ids,
|
||||||
@@ -291,6 +311,9 @@ fn snapshot_details(d: &Details) -> String {
|
|||||||
Details::IndexSwap { swaps } => {
|
Details::IndexSwap { swaps } => {
|
||||||
format!("{{ swaps: {swaps:?} }}")
|
format!("{{ swaps: {swaps:?} }}")
|
||||||
}
|
}
|
||||||
|
Details::Export { url, api_key, payload_size, indexes } => {
|
||||||
|
format!("{{ url: {url:?}, api_key: {api_key:?}, payload_size: {payload_size:?}, indexes: {indexes:?} }}")
|
||||||
|
}
|
||||||
Details::UpgradeDatabase { from, to } => {
|
Details::UpgradeDatabase { from, to } => {
|
||||||
format!("{{ from: {from:?}, to: {to:?} }}")
|
format!("{{ from: {from:?}, to: {to:?} }}")
|
||||||
}
|
}
|
||||||
@@ -309,6 +332,7 @@ pub fn snapshot_status(
|
|||||||
}
|
}
|
||||||
snap
|
snap
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapshot_kind(rtxn: &RoTxn, db: Database<SerdeBincode<Kind>, RoaringBitmapCodec>) -> String {
|
pub fn snapshot_kind(rtxn: &RoTxn, db: Database<SerdeBincode<Kind>, RoaringBitmapCodec>) -> String {
|
||||||
let mut snap = String::new();
|
let mut snap = String::new();
|
||||||
let iter = db.iter(rtxn).unwrap();
|
let iter = db.iter(rtxn).unwrap();
|
||||||
@@ -329,6 +353,7 @@ pub fn snapshot_index_tasks(rtxn: &RoTxn, db: Database<Str, RoaringBitmapCodec>)
|
|||||||
}
|
}
|
||||||
snap
|
snap
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapshot_canceled_by(rtxn: &RoTxn, db: Database<BEU32, RoaringBitmapCodec>) -> String {
|
pub fn snapshot_canceled_by(rtxn: &RoTxn, db: Database<BEU32, RoaringBitmapCodec>) -> String {
|
||||||
let mut snap = String::new();
|
let mut snap = String::new();
|
||||||
let iter = db.iter(rtxn).unwrap();
|
let iter = db.iter(rtxn).unwrap();
|
||||||
@@ -341,7 +366,17 @@ pub fn snapshot_canceled_by(rtxn: &RoTxn, db: Database<BEU32, RoaringBitmapCodec
|
|||||||
|
|
||||||
pub fn snapshot_batch(batch: &Batch) -> String {
|
pub fn snapshot_batch(batch: &Batch) -> String {
|
||||||
let mut snap = String::new();
|
let mut snap = String::new();
|
||||||
let Batch { uid, details, stats, started_at, finished_at, progress: _, enqueued_at } = batch;
|
let Batch {
|
||||||
|
uid,
|
||||||
|
details,
|
||||||
|
stats,
|
||||||
|
embedder_stats,
|
||||||
|
started_at,
|
||||||
|
finished_at,
|
||||||
|
progress: _,
|
||||||
|
enqueued_at,
|
||||||
|
stop_reason,
|
||||||
|
} = batch;
|
||||||
let stats = BatchStats {
|
let stats = BatchStats {
|
||||||
progress_trace: Default::default(),
|
progress_trace: Default::default(),
|
||||||
internal_database_sizes: Default::default(),
|
internal_database_sizes: Default::default(),
|
||||||
@@ -359,6 +394,13 @@ pub fn snapshot_batch(batch: &Batch) -> String {
|
|||||||
snap.push_str(&format!("uid: {uid}, "));
|
snap.push_str(&format!("uid: {uid}, "));
|
||||||
snap.push_str(&format!("details: {}, ", serde_json::to_string(details).unwrap()));
|
snap.push_str(&format!("details: {}, ", serde_json::to_string(details).unwrap()));
|
||||||
snap.push_str(&format!("stats: {}, ", serde_json::to_string(&stats).unwrap()));
|
snap.push_str(&format!("stats: {}, ", serde_json::to_string(&stats).unwrap()));
|
||||||
|
if !embedder_stats.skip_serializing() {
|
||||||
|
snap.push_str(&format!(
|
||||||
|
"embedder stats: {}, ",
|
||||||
|
serde_json::to_string(&embedder_stats).unwrap()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
snap.push_str(&format!("stop reason: {}, ", serde_json::to_string(&stop_reason).unwrap()));
|
||||||
snap.push('}');
|
snap.push('}');
|
||||||
snap
|
snap
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,22 +51,31 @@ pub use features::RoFeatures;
|
|||||||
use flate2::bufread::GzEncoder;
|
use flate2::bufread::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use meilisearch_types::batches::Batch;
|
use meilisearch_types::batches::Batch;
|
||||||
use meilisearch_types::features::{InstanceTogglableFeatures, Network, RuntimeTogglableFeatures};
|
use meilisearch_types::enterprise_edition::network::Network;
|
||||||
|
use meilisearch_types::features::{
|
||||||
|
ChatCompletionSettings, InstanceTogglableFeatures, RuntimeTogglableFeatures,
|
||||||
|
};
|
||||||
use meilisearch_types::heed::byteorder::BE;
|
use meilisearch_types::heed::byteorder::BE;
|
||||||
use meilisearch_types::heed::types::I128;
|
use meilisearch_types::heed::types::{DecodeIgnore, SerdeJson, Str, I128};
|
||||||
use meilisearch_types::heed::{self, Env, RoTxn, WithoutTls};
|
use meilisearch_types::heed::{self, Database, Env, RoTxn, WithoutTls};
|
||||||
use meilisearch_types::milli::index::IndexEmbeddingConfig;
|
|
||||||
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::json_template::JsonTemplate;
|
||||||
|
use meilisearch_types::milli::vector::{
|
||||||
|
Embedder, EmbedderOptions, RuntimeEmbedder, RuntimeEmbedders, RuntimeFragment,
|
||||||
|
};
|
||||||
use meilisearch_types::milli::{self, Index};
|
use meilisearch_types::milli::{self, Index};
|
||||||
use meilisearch_types::task_view::TaskView;
|
use meilisearch_types::task_view::TaskView;
|
||||||
use meilisearch_types::tasks::{KindWithContent, Task};
|
use meilisearch_types::tasks::{KindWithContent, Task, TaskNetwork};
|
||||||
|
use meilisearch_types::webhooks::{Webhook, WebhooksDumpView, WebhooksView};
|
||||||
|
use milli::vector::db::IndexEmbeddingConfig;
|
||||||
use processing::ProcessingTasks;
|
use processing::ProcessingTasks;
|
||||||
pub use queue::Query;
|
pub use queue::Query;
|
||||||
use queue::Queue;
|
use queue::Queue;
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
use scheduler::Scheduler;
|
use scheduler::Scheduler;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
use uuid::Uuid;
|
||||||
use versioning::Versioning;
|
use versioning::Versioning;
|
||||||
|
|
||||||
use crate::index_mapper::IndexMapper;
|
use crate::index_mapper::IndexMapper;
|
||||||
@@ -74,6 +83,17 @@ use crate::utils::clamp_to_page_size;
|
|||||||
|
|
||||||
pub(crate) type BEI128 = I128<BE>;
|
pub(crate) type BEI128 = I128<BE>;
|
||||||
|
|
||||||
|
const TASK_SCHEDULER_SIZE_THRESHOLD_PERCENT_INT: u64 = 40;
|
||||||
|
|
||||||
|
mod db_name {
|
||||||
|
pub const CHAT_SETTINGS: &str = "chat-settings";
|
||||||
|
pub const PERSISTED: &str = "persisted";
|
||||||
|
}
|
||||||
|
|
||||||
|
mod db_keys {
|
||||||
|
pub const WEBHOOKS: &str = "webhooks";
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IndexSchedulerOptions {
|
pub struct IndexSchedulerOptions {
|
||||||
/// The path to the version file of Meilisearch.
|
/// The path to the version file of Meilisearch.
|
||||||
@@ -90,10 +110,10 @@ pub struct IndexSchedulerOptions {
|
|||||||
pub snapshots_path: PathBuf,
|
pub snapshots_path: PathBuf,
|
||||||
/// The path to the folder containing the dumps.
|
/// The path to the folder containing the dumps.
|
||||||
pub dumps_path: PathBuf,
|
pub dumps_path: PathBuf,
|
||||||
/// The URL on which we must send the tasks statuses
|
/// The webhook url that was set by the CLI.
|
||||||
pub webhook_url: Option<String>,
|
pub cli_webhook_url: Option<String>,
|
||||||
/// The value we will send into the Authorization HTTP header on the webhook URL
|
/// The Authorization header to send to the webhook URL that was set by the CLI.
|
||||||
pub webhook_authorization_header: Option<String>,
|
pub cli_webhook_authorization: Option<String>,
|
||||||
/// The maximum size, in bytes, of the task index.
|
/// The maximum size, in bytes, of the task index.
|
||||||
pub task_db_size: usize,
|
pub task_db_size: usize,
|
||||||
/// The size, in bytes, with which a meilisearch index is opened the first time of each meilisearch index.
|
/// The size, in bytes, with which a meilisearch index is opened the first time of each meilisearch index.
|
||||||
@@ -129,6 +149,8 @@ pub struct IndexSchedulerOptions {
|
|||||||
///
|
///
|
||||||
/// 0 disables the cache.
|
/// 0 disables the cache.
|
||||||
pub embedding_cache_cap: usize,
|
pub embedding_cache_cap: usize,
|
||||||
|
/// Snapshot compaction status.
|
||||||
|
pub experimental_no_snapshot_compaction: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure which holds meilisearch's indexes and schedules the tasks
|
/// Structure which holds meilisearch's indexes and schedules the tasks
|
||||||
@@ -149,16 +171,23 @@ pub struct IndexScheduler {
|
|||||||
/// In charge of fetching and setting the status of experimental features.
|
/// In charge of fetching and setting the status of experimental features.
|
||||||
features: features::FeatureData,
|
features: features::FeatureData,
|
||||||
|
|
||||||
|
/// Stores the custom chat prompts and other settings of the indexes.
|
||||||
|
pub(crate) chat_settings: Database<Str, SerdeJson<ChatCompletionSettings>>,
|
||||||
|
|
||||||
/// Everything related to the processing of the tasks
|
/// Everything related to the processing of the tasks
|
||||||
pub scheduler: scheduler::Scheduler,
|
pub scheduler: scheduler::Scheduler,
|
||||||
|
|
||||||
/// Whether we should automatically cleanup the task queue or not.
|
/// Whether we should automatically cleanup the task queue or not.
|
||||||
pub(crate) cleanup_enabled: bool,
|
pub(crate) cleanup_enabled: bool,
|
||||||
|
|
||||||
/// The webhook url we should send tasks to after processing every batches.
|
/// Whether we should use the old document indexer or the new one.
|
||||||
pub(crate) webhook_url: Option<String>,
|
pub(crate) experimental_no_edition_2024_for_dumps: bool,
|
||||||
/// The Authorization header to send to the webhook URL.
|
|
||||||
pub(crate) webhook_authorization_header: Option<String>,
|
/// A database to store single-keyed data that is persisted across restarts.
|
||||||
|
persisted: Database<Str, Str>,
|
||||||
|
|
||||||
|
/// Webhook, loaded and stored in the `persisted` database
|
||||||
|
webhooks: Arc<Webhooks>,
|
||||||
|
|
||||||
/// A map to retrieve the runtime representation of an embedder depending on its configuration.
|
/// A map to retrieve the runtime representation of an embedder depending on its configuration.
|
||||||
///
|
///
|
||||||
@@ -197,8 +226,10 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
index_mapper: self.index_mapper.clone(),
|
index_mapper: self.index_mapper.clone(),
|
||||||
cleanup_enabled: self.cleanup_enabled,
|
cleanup_enabled: self.cleanup_enabled,
|
||||||
webhook_url: self.webhook_url.clone(),
|
experimental_no_edition_2024_for_dumps: self.experimental_no_edition_2024_for_dumps,
|
||||||
webhook_authorization_header: self.webhook_authorization_header.clone(),
|
persisted: self.persisted,
|
||||||
|
|
||||||
|
webhooks: self.webhooks.clone(),
|
||||||
embedders: self.embedders.clone(),
|
embedders: self.embedders.clone(),
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
test_breakpoint_sdr: self.test_breakpoint_sdr.clone(),
|
test_breakpoint_sdr: self.test_breakpoint_sdr.clone(),
|
||||||
@@ -207,11 +238,17 @@ impl IndexScheduler {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
run_loop_iteration: self.run_loop_iteration.clone(),
|
run_loop_iteration: self.run_loop_iteration.clone(),
|
||||||
features: self.features.clone(),
|
features: self.features.clone(),
|
||||||
|
chat_settings: self.chat_settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn nb_db() -> u32 {
|
pub(crate) const fn nb_db() -> u32 {
|
||||||
Versioning::nb_db() + Queue::nb_db() + IndexMapper::nb_db() + features::FeatureData::nb_db()
|
Versioning::nb_db()
|
||||||
|
+ Queue::nb_db()
|
||||||
|
+ IndexMapper::nb_db()
|
||||||
|
+ features::FeatureData::nb_db()
|
||||||
|
+ 1 // chat-prompts
|
||||||
|
+ 1 // persisted
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an index scheduler and start its run loop.
|
/// Create an index scheduler and start its run loop.
|
||||||
@@ -262,9 +299,18 @@ impl IndexScheduler {
|
|||||||
let version = versioning::Versioning::new(&env, from_db_version)?;
|
let version = versioning::Versioning::new(&env, from_db_version)?;
|
||||||
|
|
||||||
let mut wtxn = env.write_txn()?;
|
let mut wtxn = env.write_txn()?;
|
||||||
|
|
||||||
let features = features::FeatureData::new(&env, &mut wtxn, options.instance_features)?;
|
let features = features::FeatureData::new(&env, &mut wtxn, options.instance_features)?;
|
||||||
let queue = Queue::new(&env, &mut wtxn, &options)?;
|
let queue = Queue::new(&env, &mut wtxn, &options)?;
|
||||||
let index_mapper = IndexMapper::new(&env, &mut wtxn, &options, budget)?;
|
let index_mapper = IndexMapper::new(&env, &mut wtxn, &options, budget)?;
|
||||||
|
let chat_settings = env.create_database(&mut wtxn, Some(db_name::CHAT_SETTINGS))?;
|
||||||
|
|
||||||
|
let persisted = env.create_database(&mut wtxn, Some(db_name::PERSISTED))?;
|
||||||
|
let webhooks_db = persisted.remap_data_type::<SerdeJson<Webhooks>>();
|
||||||
|
let mut webhooks = webhooks_db.get(&wtxn, db_keys::WEBHOOKS)?.unwrap_or_default();
|
||||||
|
webhooks
|
||||||
|
.with_cli(options.cli_webhook_url.clone(), options.cli_webhook_authorization.clone());
|
||||||
|
|
||||||
wtxn.commit()?;
|
wtxn.commit()?;
|
||||||
|
|
||||||
// allow unreachable_code to get rids of the warning in the case of a test build.
|
// allow unreachable_code to get rids of the warning in the case of a test build.
|
||||||
@@ -277,8 +323,11 @@ impl IndexScheduler {
|
|||||||
index_mapper,
|
index_mapper,
|
||||||
env,
|
env,
|
||||||
cleanup_enabled: options.cleanup_enabled,
|
cleanup_enabled: options.cleanup_enabled,
|
||||||
webhook_url: options.webhook_url,
|
experimental_no_edition_2024_for_dumps: options
|
||||||
webhook_authorization_header: options.webhook_authorization_header,
|
.indexer_config
|
||||||
|
.experimental_no_edition_2024_for_dumps,
|
||||||
|
persisted,
|
||||||
|
webhooks: Arc::new(webhooks),
|
||||||
embedders: Default::default(),
|
embedders: Default::default(),
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -288,12 +337,17 @@ impl IndexScheduler {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
run_loop_iteration: Arc::new(RwLock::new(0)),
|
run_loop_iteration: Arc::new(RwLock::new(0)),
|
||||||
features,
|
features,
|
||||||
|
chat_settings,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.run();
|
this.run();
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_txn(&self) -> Result<RoTxn<WithoutTls>> {
|
||||||
|
self.env.read_txn().map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Return `Ok(())` if the index scheduler is able to access one of its database.
|
/// Return `Ok(())` if the index scheduler is able to access one of its database.
|
||||||
pub fn health(&self) -> Result<()> {
|
pub fn health(&self) -> Result<()> {
|
||||||
let rtxn = self.env.read_txn()?;
|
let rtxn = self.env.read_txn()?;
|
||||||
@@ -370,15 +424,16 @@ impl IndexScheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_txn(&self) -> Result<RoTxn<WithoutTls>> {
|
|
||||||
self.env.read_txn().map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start the run loop for the given index scheduler.
|
/// Start the run loop for the given index scheduler.
|
||||||
///
|
///
|
||||||
/// This function will execute in a different thread and must be called
|
/// This function will execute in a different thread and must be called
|
||||||
/// only once per index scheduler.
|
/// only once per index scheduler.
|
||||||
fn run(&self) {
|
fn run(&self) {
|
||||||
|
// If the number of batched tasks is 0, we don't need to run the scheduler at all.
|
||||||
|
// It will never be able to process any tasks.
|
||||||
|
if self.scheduler.max_number_of_batched_tasks == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let run = self.private_clone();
|
let run = self.private_clone();
|
||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name(String::from("scheduler"))
|
.name(String::from("scheduler"))
|
||||||
@@ -396,9 +451,9 @@ impl IndexScheduler {
|
|||||||
Ok(Ok(TickOutcome::StopProcessingForever)) => break,
|
Ok(Ok(TickOutcome::StopProcessingForever)) => break,
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
tracing::error!("{e}");
|
tracing::error!("{e}");
|
||||||
// Wait one second when an irrecoverable error occurs.
|
// Wait when an irrecoverable error occurs.
|
||||||
if !e.is_recoverable() {
|
if !e.is_recoverable() {
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
std::thread::sleep(Duration::from_secs(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_panic) => {
|
Err(_panic) => {
|
||||||
@@ -425,6 +480,17 @@ impl IndexScheduler {
|
|||||||
Ok(self.env.non_free_pages_size()?)
|
Ok(self.env.non_free_pages_size()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the maximum possible database size
|
||||||
|
pub fn max_size(&self) -> Result<u64> {
|
||||||
|
Ok(self.env.info().map_size as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the max size of task allowed until the task queue stop receiving.
|
||||||
|
pub fn remaining_size_until_task_queue_stop(&self) -> Result<u64> {
|
||||||
|
Ok((self.env.info().map_size as u64 * TASK_SCHEDULER_SIZE_THRESHOLD_PERCENT_INT / 100)
|
||||||
|
.saturating_sub(self.used_size()?))
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the index corresponding to the name.
|
/// Return the index corresponding to the name.
|
||||||
///
|
///
|
||||||
/// * If the index wasn't opened before, the index will be opened.
|
/// * If the index wasn't opened before, the index will be opened.
|
||||||
@@ -475,7 +541,7 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
/// Returns the total number of indexes available for the specified filter.
|
/// Returns the total number of indexes available for the specified filter.
|
||||||
/// And a `Vec` of the index_uid + its stats
|
/// And a `Vec` of the index_uid + its stats
|
||||||
pub fn get_paginated_indexes_stats(
|
pub fn paginated_indexes_stats(
|
||||||
&self,
|
&self,
|
||||||
filters: &meilisearch_auth::AuthFilter,
|
filters: &meilisearch_auth::AuthFilter,
|
||||||
from: usize,
|
from: usize,
|
||||||
@@ -516,6 +582,24 @@ impl IndexScheduler {
|
|||||||
ret.map(|ret| (total, ret))
|
ret.map(|ret| (total, ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the total number of chat workspaces available ~~for the specified filter~~.
|
||||||
|
/// And a `Vec` of the workspace_uids
|
||||||
|
pub fn paginated_chat_workspace_uids(
|
||||||
|
&self,
|
||||||
|
from: usize,
|
||||||
|
limit: usize,
|
||||||
|
) -> Result<(usize, Vec<String>)> {
|
||||||
|
let rtxn = self.read_txn()?;
|
||||||
|
let total = self.chat_settings.len(&rtxn)?;
|
||||||
|
let mut iter = self.chat_settings.iter(&rtxn)?.skip(from);
|
||||||
|
iter.by_ref()
|
||||||
|
.take(limit)
|
||||||
|
.map(|ret| ret.map_err(Error::from))
|
||||||
|
.map(|ret| ret.map(|(uid, _)| uid.to_string()))
|
||||||
|
.collect::<Result<Vec<_>, Error>>()
|
||||||
|
.map(|ret| (total as usize, ret))
|
||||||
|
}
|
||||||
|
|
||||||
/// The returned structure contains:
|
/// The returned structure contains:
|
||||||
/// 1. The name of the property being observed can be `statuses`, `types`, or `indexes`.
|
/// 1. The name of the property being observed can be `statuses`, `types`, or `indexes`.
|
||||||
/// 2. The name of the specific data related to the property can be `enqueued` for the `statuses`, `settingsUpdate` for the `types`, or the name of the index for the `indexes`, for example.
|
/// 2. The name of the specific data related to the property can be `enqueued` for the `statuses`, `settingsUpdate` for the `types`, or the name of the index for the `indexes`, for example.
|
||||||
@@ -540,6 +624,11 @@ impl IndexScheduler {
|
|||||||
Ok(nbr_index_processing_tasks > 0)
|
Ok(nbr_index_processing_tasks > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the index should use the old document indexer.
|
||||||
|
pub fn no_edition_2024_for_dumps(&self) -> bool {
|
||||||
|
self.experimental_no_edition_2024_for_dumps
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the tasks matching the query from the user's point of view along
|
/// Return the tasks matching the query from the user's point of view along
|
||||||
/// with the total number of tasks matching the query, ignoring from and limit.
|
/// with the total number of tasks matching the query, ignoring from and limit.
|
||||||
///
|
///
|
||||||
@@ -578,6 +667,16 @@ impl IndexScheduler {
|
|||||||
self.queue.get_task_ids_from_authorized_indexes(&rtxn, query, filters, &processing)
|
self.queue.get_task_ids_from_authorized_indexes(&rtxn, query, filters, &processing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_task_network(&self, task_id: TaskId, network: TaskNetwork) -> Result<()> {
|
||||||
|
let mut wtxn = self.env.write_txn()?;
|
||||||
|
let mut task =
|
||||||
|
self.queue.tasks.get_task(&wtxn, task_id)?.ok_or(Error::TaskNotFound(task_id))?;
|
||||||
|
task.network = Some(network);
|
||||||
|
self.queue.tasks.all_tasks.put(&mut wtxn, &task_id, &task)?;
|
||||||
|
wtxn.commit()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the batches matching the query from the user's point of view along
|
/// Return the batches matching the query from the user's point of view along
|
||||||
/// with the total number of batches matching the query, ignoring from and limit.
|
/// with the total number of batches matching the query, ignoring from and limit.
|
||||||
///
|
///
|
||||||
@@ -625,9 +724,10 @@ impl IndexScheduler {
|
|||||||
task_id: Option<TaskId>,
|
task_id: Option<TaskId>,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
) -> Result<Task> {
|
) -> Result<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 the task doesn't delete or cancel anything and 40% of the task queue is full, we must refuse to enqueue the incoming task
|
||||||
if !matches!(&kind, KindWithContent::TaskDeletion { tasks, .. } if !tasks.is_empty())
|
if !matches!(&kind, KindWithContent::TaskDeletion { tasks, .. } | KindWithContent::TaskCancelation { tasks, .. } if !tasks.is_empty())
|
||||||
&& (self.env.non_free_pages_size()? * 100) / self.env.info().map_size as u64 > 40
|
&& (self.env.non_free_pages_size()? * 100) / self.env.info().map_size as u64
|
||||||
|
> TASK_SCHEDULER_SIZE_THRESHOLD_PERCENT_INT
|
||||||
{
|
{
|
||||||
return Err(Error::NoSpaceLeftInTaskQueue);
|
return Err(Error::NoSpaceLeftInTaskQueue);
|
||||||
}
|
}
|
||||||
@@ -685,86 +785,92 @@ impl IndexScheduler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Once the tasks changes have been committed we must send all the tasks that were updated to our webhook if there is one.
|
/// Once the tasks changes have been committed we must send all the tasks that were updated to our webhooks
|
||||||
fn notify_webhook(&self, updated: &RoaringBitmap) -> Result<()> {
|
fn notify_webhooks(&self, updated: RoaringBitmap) {
|
||||||
if let Some(ref url) = self.webhook_url {
|
struct TaskReader<'a, 'b> {
|
||||||
struct TaskReader<'a, 'b> {
|
rtxn: &'a RoTxn<'a>,
|
||||||
rtxn: &'a RoTxn<'a>,
|
index_scheduler: &'a IndexScheduler,
|
||||||
index_scheduler: &'a IndexScheduler,
|
tasks: &'b mut roaring::bitmap::Iter<'b>,
|
||||||
tasks: &'b mut roaring::bitmap::Iter<'b>,
|
buffer: Vec<u8>,
|
||||||
buffer: Vec<u8>,
|
written: usize,
|
||||||
written: usize,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> Read for TaskReader<'a, 'b> {
|
impl Read for TaskReader<'_, '_> {
|
||||||
fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result<usize> {
|
fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
if self.buffer.is_empty() {
|
if self.buffer.is_empty() {
|
||||||
match self.tasks.next() {
|
match self.tasks.next() {
|
||||||
None => return Ok(0),
|
None => return Ok(0),
|
||||||
Some(task_id) => {
|
Some(task_id) => {
|
||||||
let task = self
|
let task = self
|
||||||
.index_scheduler
|
.index_scheduler
|
||||||
.queue
|
.queue
|
||||||
.tasks
|
.tasks
|
||||||
.get_task(self.rtxn, task_id)
|
.get_task(self.rtxn, task_id)
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
io::Error::new(
|
io::Error::new(io::ErrorKind::Other, Error::CorruptedTaskQueue)
|
||||||
io::ErrorKind::Other,
|
})?;
|
||||||
Error::CorruptedTaskQueue,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
serde_json::to_writer(
|
serde_json::to_writer(&mut self.buffer, &TaskView::from_task(&task))?;
|
||||||
&mut self.buffer,
|
self.buffer.push(b'\n');
|
||||||
&TaskView::from_task(&task),
|
|
||||||
)?;
|
|
||||||
self.buffer.push(b'\n');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut to_write = &self.buffer[self.written..];
|
|
||||||
let wrote = io::copy(&mut to_write, &mut buf)?;
|
|
||||||
self.written += wrote as usize;
|
|
||||||
|
|
||||||
// we wrote everything and must refresh our buffer on the next call
|
|
||||||
if self.written == self.buffer.len() {
|
|
||||||
self.written = 0;
|
|
||||||
self.buffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(wrote as usize)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let rtxn = self.env.read_txn()?;
|
let mut to_write = &self.buffer[self.written..];
|
||||||
|
let wrote = io::copy(&mut to_write, &mut buf)?;
|
||||||
|
self.written += wrote as usize;
|
||||||
|
|
||||||
let task_reader = TaskReader {
|
// we wrote everything and must refresh our buffer on the next call
|
||||||
rtxn: &rtxn,
|
if self.written == self.buffer.len() {
|
||||||
index_scheduler: self,
|
self.written = 0;
|
||||||
tasks: &mut updated.into_iter(),
|
self.buffer.clear();
|
||||||
buffer: Vec::with_capacity(50), // on average a task is around ~100 bytes
|
}
|
||||||
written: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// let reader = GzEncoder::new(BufReader::new(task_reader), Compression::default());
|
Ok(wrote as usize)
|
||||||
let reader = GzEncoder::new(BufReader::new(task_reader), Compression::default());
|
|
||||||
let request = ureq::post(url)
|
|
||||||
.timeout(Duration::from_secs(30))
|
|
||||||
.set("Content-Encoding", "gzip")
|
|
||||||
.set("Content-Type", "application/x-ndjson");
|
|
||||||
let request = match &self.webhook_authorization_header {
|
|
||||||
Some(header) => request.set("Authorization", header),
|
|
||||||
None => request,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = request.send(reader) {
|
|
||||||
tracing::error!("While sending data to the webhook: {e}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
let webhooks = self.webhooks.get_all();
|
||||||
|
if webhooks.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let this = self.private_clone();
|
||||||
|
// We must take the RoTxn before entering the thread::spawn otherwise another batch may be
|
||||||
|
// processed before we had the time to take our txn.
|
||||||
|
let rtxn = match self.env.clone().static_read_txn() {
|
||||||
|
Ok(rtxn) => rtxn,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Couldn't get an rtxn to notify the webhook: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
for (uuid, Webhook { url, headers }) in webhooks.iter() {
|
||||||
|
let task_reader = TaskReader {
|
||||||
|
rtxn: &rtxn,
|
||||||
|
index_scheduler: &this,
|
||||||
|
tasks: &mut updated.iter(),
|
||||||
|
buffer: Vec::with_capacity(page_size::get()),
|
||||||
|
written: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let reader = GzEncoder::new(BufReader::new(task_reader), Compression::default());
|
||||||
|
|
||||||
|
let mut request = ureq::post(url)
|
||||||
|
.timeout(Duration::from_secs(30))
|
||||||
|
.set("Content-Encoding", "gzip")
|
||||||
|
.set("Content-Type", "application/x-ndjson");
|
||||||
|
for (header_name, header_value) in headers.iter() {
|
||||||
|
request = request.set(header_name, header_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = request.send(reader) {
|
||||||
|
tracing::error!("While sending data to the webhook {uuid}: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_stats(&self, index_uid: &str) -> Result<IndexStats> {
|
pub fn index_stats(&self, index_uid: &str) -> Result<IndexStats> {
|
||||||
@@ -795,33 +901,69 @@ impl IndexScheduler {
|
|||||||
self.features.network()
|
self.features.network()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_runtime_webhooks(&self, runtime: RuntimeWebhooks) -> Result<()> {
|
||||||
|
let webhooks = Webhooks::from_runtime(runtime);
|
||||||
|
let mut wtxn = self.env.write_txn()?;
|
||||||
|
let webhooks_db = self.persisted.remap_data_type::<SerdeJson<Webhooks>>();
|
||||||
|
webhooks_db.put(&mut wtxn, db_keys::WEBHOOKS, &webhooks)?;
|
||||||
|
wtxn.commit()?;
|
||||||
|
self.webhooks.update_runtime(webhooks.into_runtime());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn webhooks_dump_view(&self) -> WebhooksDumpView {
|
||||||
|
// We must not dump the cli api key
|
||||||
|
WebhooksDumpView { webhooks: self.webhooks.get_runtime() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn webhooks_view(&self) -> WebhooksView {
|
||||||
|
WebhooksView { webhooks: self.webhooks.get_all() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retrieve_runtime_webhooks(&self) -> RuntimeWebhooks {
|
||||||
|
self.webhooks.get_runtime()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn embedders(
|
pub fn embedders(
|
||||||
&self,
|
&self,
|
||||||
index_uid: String,
|
index_uid: String,
|
||||||
embedding_configs: Vec<IndexEmbeddingConfig>,
|
embedding_configs: Vec<IndexEmbeddingConfig>,
|
||||||
) -> Result<EmbeddingConfigs> {
|
) -> Result<RuntimeEmbedders> {
|
||||||
let res: Result<_> = embedding_configs
|
let res: Result<_> = embedding_configs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(
|
.map(
|
||||||
|IndexEmbeddingConfig {
|
|IndexEmbeddingConfig {
|
||||||
name,
|
name,
|
||||||
config: milli::vector::EmbeddingConfig { embedder_options, prompt, quantized },
|
config: milli::vector::EmbeddingConfig { embedder_options, prompt, quantized },
|
||||||
..
|
fragments,
|
||||||
}| {
|
}|
|
||||||
let prompt = Arc::new(
|
-> Result<(String, Arc<RuntimeEmbedder>)> {
|
||||||
prompt
|
let document_template = prompt
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(meilisearch_types::milli::Error::from)
|
.map_err(meilisearch_types::milli::Error::from)
|
||||||
.map_err(|err| Error::from_milli(err, Some(index_uid.clone())))?,
|
.map_err(|err| Error::from_milli(err, Some(index_uid.clone())))?;
|
||||||
);
|
|
||||||
|
let fragments = fragments
|
||||||
|
.into_inner()
|
||||||
|
.into_iter()
|
||||||
|
.map(|fragment| {
|
||||||
|
let value = embedder_options.fragment(&fragment.name).unwrap();
|
||||||
|
let template = JsonTemplate::new(value.clone()).unwrap();
|
||||||
|
RuntimeFragment { name: fragment.name, id: fragment.id, template }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
// optimistically return existing embedder
|
// optimistically return existing embedder
|
||||||
{
|
{
|
||||||
let embedders = self.embedders.read().unwrap();
|
let embedders = self.embedders.read().unwrap();
|
||||||
if let Some(embedder) = embedders.get(&embedder_options) {
|
if let Some(embedder) = embedders.get(&embedder_options) {
|
||||||
return Ok((
|
let runtime = Arc::new(RuntimeEmbedder::new(
|
||||||
name,
|
embedder.clone(),
|
||||||
(embedder.clone(), prompt, quantized.unwrap_or_default()),
|
document_template,
|
||||||
|
fragments,
|
||||||
|
quantized.unwrap_or_default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
return Ok((name, runtime));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,11 +979,44 @@ impl IndexScheduler {
|
|||||||
let mut embedders = self.embedders.write().unwrap();
|
let mut embedders = self.embedders.write().unwrap();
|
||||||
embedders.insert(embedder_options, embedder.clone());
|
embedders.insert(embedder_options, embedder.clone());
|
||||||
}
|
}
|
||||||
Ok((name, (embedder, prompt, quantized.unwrap_or_default())))
|
|
||||||
|
let runtime = Arc::new(RuntimeEmbedder::new(
|
||||||
|
embedder.clone(),
|
||||||
|
document_template,
|
||||||
|
fragments,
|
||||||
|
quantized.unwrap_or_default(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok((name, runtime))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
res.map(EmbeddingConfigs::new)
|
res.map(RuntimeEmbedders::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chat_settings(&self, uid: &str) -> Result<Option<ChatCompletionSettings>> {
|
||||||
|
let rtxn = self.env.read_txn()?;
|
||||||
|
self.chat_settings.get(&rtxn, uid).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true if chat workspace exists.
|
||||||
|
pub fn chat_workspace_exists(&self, name: &str) -> Result<bool> {
|
||||||
|
let rtxn = self.env.read_txn()?;
|
||||||
|
Ok(self.chat_settings.remap_data_type::<DecodeIgnore>().get(&rtxn, name)?.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_chat_settings(&self, uid: &str, settings: &ChatCompletionSettings) -> Result<()> {
|
||||||
|
let mut wtxn = self.env.write_txn()?;
|
||||||
|
self.chat_settings.put(&mut wtxn, uid, settings)?;
|
||||||
|
wtxn.commit()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_chat_settings(&self, uid: &str) -> Result<bool> {
|
||||||
|
let mut wtxn = self.env.write_txn()?;
|
||||||
|
let deleted = self.chat_settings.delete(&mut wtxn, uid)?;
|
||||||
|
wtxn.commit()?;
|
||||||
|
Ok(deleted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,3 +1052,72 @@ pub struct IndexStats {
|
|||||||
/// Internal stats computed from the index.
|
/// Internal stats computed from the index.
|
||||||
pub inner_stats: index_mapper::IndexStats,
|
pub inner_stats: index_mapper::IndexStats,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// These structure are not meant to be exposed to the end user, if needed, use the meilisearch-types::webhooks structure instead.
|
||||||
|
/// /!\ Everytime you deserialize this structure you should fill the cli_webhook later on with the `with_cli` method. /!\
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct Webhooks {
|
||||||
|
// The cli webhook should *never* be stored in a database.
|
||||||
|
// It represent a state that only exists for this execution of meilisearch
|
||||||
|
#[serde(skip)]
|
||||||
|
pub cli: Option<CliWebhook>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub runtime: RwLock<RuntimeWebhooks>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuntimeWebhooks = BTreeMap<Uuid, Webhook>;
|
||||||
|
|
||||||
|
impl Webhooks {
|
||||||
|
pub fn with_cli(&mut self, url: Option<String>, auth: Option<String>) {
|
||||||
|
if let Some(url) = url {
|
||||||
|
let webhook = CliWebhook { url, auth };
|
||||||
|
self.cli = Some(webhook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_runtime(webhooks: RuntimeWebhooks) -> Self {
|
||||||
|
Self { cli: None, runtime: RwLock::new(webhooks) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_runtime(self) -> RuntimeWebhooks {
|
||||||
|
// safe because we own self and it cannot be cloned
|
||||||
|
self.runtime.into_inner().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_runtime(&self, webhooks: RuntimeWebhooks) {
|
||||||
|
*self.runtime.write().unwrap() = webhooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all the webhooks in an unified view. The cli webhook is represented with an uuid set to 0
|
||||||
|
pub fn get_all(&self) -> BTreeMap<Uuid, Webhook> {
|
||||||
|
self.cli
|
||||||
|
.as_ref()
|
||||||
|
.map(|wh| (Uuid::nil(), Webhook::from(wh)))
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.runtime.read().unwrap().iter().map(|(uuid, wh)| (*uuid, wh.clone())))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all the runtime webhooks.
|
||||||
|
pub fn get_runtime(&self) -> BTreeMap<Uuid, Webhook> {
|
||||||
|
self.runtime.read().unwrap().iter().map(|(uuid, wh)| (*uuid, wh.clone())).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
|
||||||
|
struct CliWebhook {
|
||||||
|
pub url: String,
|
||||||
|
pub auth: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&CliWebhook> for Webhook {
|
||||||
|
fn from(webhook: &CliWebhook) -> Self {
|
||||||
|
let mut headers = BTreeMap::new();
|
||||||
|
if let Some(ref auth) = webhook.auth {
|
||||||
|
headers.insert("Authorization".to_string(), auth.to_string());
|
||||||
|
}
|
||||||
|
Self { url: webhook.url.to_string(), headers }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ make_enum_progress! {
|
|||||||
make_enum_progress! {
|
make_enum_progress! {
|
||||||
pub enum TaskCancelationProgress {
|
pub enum TaskCancelationProgress {
|
||||||
RetrievingTasks,
|
RetrievingTasks,
|
||||||
|
CancelingUpgrade,
|
||||||
UpdatingTasks,
|
UpdatingTasks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,10 +103,12 @@ make_enum_progress! {
|
|||||||
pub enum DumpCreationProgress {
|
pub enum DumpCreationProgress {
|
||||||
StartTheDumpCreation,
|
StartTheDumpCreation,
|
||||||
DumpTheApiKeys,
|
DumpTheApiKeys,
|
||||||
|
DumpTheChatCompletionSettings,
|
||||||
DumpTheTasks,
|
DumpTheTasks,
|
||||||
DumpTheBatches,
|
DumpTheBatches,
|
||||||
DumpTheIndexes,
|
DumpTheIndexes,
|
||||||
DumpTheExperimentalFeatures,
|
DumpTheExperimentalFeatures,
|
||||||
|
DumpTheWebhooks,
|
||||||
CompressTheDump,
|
CompressTheDump,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,8 +177,17 @@ make_enum_progress! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
make_enum_progress! {
|
||||||
|
pub enum Export {
|
||||||
|
EnsuringCorrectnessOfTheTarget,
|
||||||
|
ExportingTheSettings,
|
||||||
|
ExportingTheDocuments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
make_atomic_progress!(Task alias AtomicTaskStep => "task" );
|
make_atomic_progress!(Task alias AtomicTaskStep => "task" );
|
||||||
make_atomic_progress!(Document alias AtomicDocumentStep => "document" );
|
make_atomic_progress!(Document alias AtomicDocumentStep => "document" );
|
||||||
|
make_atomic_progress!(Index alias AtomicIndexStep => "index" );
|
||||||
make_atomic_progress!(Batch alias AtomicBatchStep => "batch" );
|
make_atomic_progress!(Batch alias AtomicBatchStep => "batch" );
|
||||||
make_atomic_progress!(UpdateFile alias AtomicUpdateFileStep => "update file" );
|
make_atomic_progress!(UpdateFile alias AtomicUpdateFileStep => "update file" );
|
||||||
|
|
||||||
|
|||||||
@@ -179,9 +179,11 @@ impl BatchQueue {
|
|||||||
progress: None,
|
progress: None,
|
||||||
details: batch.details,
|
details: batch.details,
|
||||||
stats: batch.stats,
|
stats: batch.stats,
|
||||||
|
embedder_stats: batch.embedder_stats.as_ref().into(),
|
||||||
started_at: batch.started_at,
|
started_at: batch.started_at,
|
||||||
finished_at: batch.finished_at,
|
finished_at: batch.finished_at,
|
||||||
enqueued_at: batch.enqueued_at,
|
enqueued_at: batch.enqueued_at,
|
||||||
|
stop_reason: batch.reason.to_string(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ fn query_batches_simple() {
|
|||||||
batches[0].enqueued_at = None;
|
batches[0].enqueued_at = None;
|
||||||
// Insta cannot snapshot our batches because the batch stats contains an enum as key: https://github.com/mitsuhiko/insta/issues/689
|
// 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();
|
let batch = serde_json::to_string_pretty(&batches[0]).unwrap();
|
||||||
snapshot!(batch, @r#"
|
snapshot!(batch, @r###"
|
||||||
{
|
{
|
||||||
"uid": 0,
|
"uid": 0,
|
||||||
"details": {
|
"details": {
|
||||||
@@ -126,9 +126,10 @@ fn query_batches_simple() {
|
|||||||
},
|
},
|
||||||
"startedAt": "1970-01-01T00:00:00Z",
|
"startedAt": "1970-01-01T00:00:00Z",
|
||||||
"finishedAt": null,
|
"finishedAt": null,
|
||||||
"enqueuedAt": null
|
"enqueuedAt": null,
|
||||||
|
"stopReason": "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task."
|
||||||
}
|
}
|
||||||
"#);
|
"###);
|
||||||
|
|
||||||
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
|
||||||
@@ -333,11 +334,11 @@ fn query_batches_special_rules() {
|
|||||||
let kind = index_creation_task("doggo", "sheep");
|
let kind = index_creation_task("doggo", "sheep");
|
||||||
let _task = index_scheduler.register(kind, None, false).unwrap();
|
let _task = index_scheduler.register(kind, None, false).unwrap();
|
||||||
let kind = KindWithContent::IndexSwap {
|
let kind = KindWithContent::IndexSwap {
|
||||||
swaps: vec![IndexSwap { indexes: ("catto".to_owned(), "doggo".to_owned()) }],
|
swaps: vec![IndexSwap { indexes: ("catto".to_owned(), "doggo".to_owned()), rename: false }],
|
||||||
};
|
};
|
||||||
let _task = index_scheduler.register(kind, None, false).unwrap();
|
let _task = index_scheduler.register(kind, None, false).unwrap();
|
||||||
let kind = KindWithContent::IndexSwap {
|
let kind = KindWithContent::IndexSwap {
|
||||||
swaps: vec![IndexSwap { indexes: ("catto".to_owned(), "whalo".to_owned()) }],
|
swaps: vec![IndexSwap { indexes: ("catto".to_owned(), "whalo".to_owned()), rename: false }],
|
||||||
};
|
};
|
||||||
let _task = index_scheduler.register(kind, None, false).unwrap();
|
let _task = index_scheduler.register(kind, None, false).unwrap();
|
||||||
|
|
||||||
@@ -441,7 +442,7 @@ fn query_batches_canceled_by() {
|
|||||||
let kind = index_creation_task("doggo", "sheep");
|
let kind = index_creation_task("doggo", "sheep");
|
||||||
let _ = index_scheduler.register(kind, None, false).unwrap();
|
let _ = index_scheduler.register(kind, None, false).unwrap();
|
||||||
let kind = KindWithContent::IndexSwap {
|
let kind = KindWithContent::IndexSwap {
|
||||||
swaps: vec![IndexSwap { indexes: ("catto".to_owned(), "doggo".to_owned()) }],
|
swaps: vec![IndexSwap { indexes: ("catto".to_owned(), "doggo".to_owned()), rename: false }],
|
||||||
};
|
};
|
||||||
let _task = index_scheduler.register(kind, None, false).unwrap();
|
let _task = index_scheduler.register(kind, None, false).unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ impl Queue {
|
|||||||
details: kind.default_details(),
|
details: kind.default_details(),
|
||||||
status: Status::Enqueued,
|
status: Status::Enqueued,
|
||||||
kind: kind.clone(),
|
kind: kind.clone(),
|
||||||
|
network: None,
|
||||||
};
|
};
|
||||||
// For deletion and cancelation tasks, we want to make extra sure that they
|
// For deletion and cancelation tasks, we want to make extra sure that they
|
||||||
// don't attempt to delete/cancel tasks that are newer than themselves.
|
// don't attempt to delete/cancel tasks that are newer than themselves.
|
||||||
@@ -292,8 +293,6 @@ impl Queue {
|
|||||||
return Ok(task);
|
return Ok(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get rid of the mutability.
|
|
||||||
let task = task;
|
|
||||||
self.tasks.register(wtxn, &task)?;
|
self.tasks.register(wtxn, &task)?;
|
||||||
|
|
||||||
Ok(task)
|
Ok(task)
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { 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"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, batch_uid: 1, status: canceled, canceled_by: 3, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, batch_uid: 1, status: canceled, canceled_by: 3, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, batch_uid: 1, status: canceled, canceled_by: 3, details: { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }}
|
2 {uid: 2, batch_uid: 1, status: canceled, canceled_by: 3, details: { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }}
|
||||||
3 {uid: 3, batch_uid: 1, status: succeeded, details: { matched_tasks: 3, canceled_tasks: Some(2), original_filter: "test_query" }, kind: TaskCancelation { query: "test_query", tasks: RoaringBitmap<[0, 1, 2]> }}
|
3 {uid: 3, batch_uid: 1, status: succeeded, details: { matched_tasks: 3, canceled_tasks: Some(2), original_filter: "test_query" }, kind: TaskCancelation { query: "test_query", tasks: RoaringBitmap<[0, 1, 2]> }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
@@ -49,8 +48,8 @@ catto: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [1,2,3,]
|
[timestamp] [1,2,3,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"sheep","matchedTasks":3,"canceledTasks":2,"originalFilter":"test_query","swaps":[{"indexes":["catto","doggo"]}]}, stats: {"totalNbTasks":3,"status":{"succeeded":1,"canceled":2},"types":{"indexCreation":1,"indexSwap":1,"taskCancelation":1},"indexUids":{"doggo":1}}, }
|
1 {uid: 1, details: {"primaryKey":"sheep","matchedTasks":3,"canceledTasks":2,"originalFilter":"test_query","swaps":[{"indexes":["catto","doggo"],"rename":false}]}, stats: {"totalNbTasks":3,"status":{"succeeded":1,"canceled":2},"types":{"indexCreation":1,"indexSwap":1,"taskCancelation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 3 of type `taskCancelation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { primary_key: Some("bone") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("plankton") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("plankton"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
||||||
2 {uid: 2, batch_uid: 2, status: succeeded, details: { primary_key: Some("his_own_vomit") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("his_own_vomit") }}
|
2 {uid: 2, batch_uid: 2, status: succeeded, details: { primary_key: Some("his_own_vomit"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("his_own_vomit") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
@@ -48,9 +47,9 @@ whalo: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [2,]
|
[timestamp] [2,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, }
|
0 {uid: 0, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"plankton"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"whalo":1}}, }
|
1 {uid: 1, details: {"primaryKey":"plankton"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"whalo":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
2 {uid: 2, details: {"primaryKey":"his_own_vomit"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
2 {uid: 2, details: {"primaryKey":"his_own_vomit"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 2 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { primary_key: Some("bone") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,]
|
enqueued [0,]
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { primary_key: Some("bone") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("plankton") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("plankton"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,1,]
|
enqueued [0,1,]
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { primary_key: Some("bone") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("plankton") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("plankton"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
||||||
2 {uid: 2, status: enqueued, details: { primary_key: Some("his_own_vomit") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("his_own_vomit") }}
|
2 {uid: 2, status: enqueued, details: { primary_key: Some("his_own_vomit"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("his_own_vomit") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,1,2,]
|
enqueued [0,1,2,]
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.rs
|
||||||
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":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, }
|
{uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### 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"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, status: enqueued, details: { primary_key: Some("fish") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("fish") }}
|
2 {uid: 2, status: enqueued, details: { primary_key: Some("fish"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("fish") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [1,2,]
|
enqueued [1,2,]
|
||||||
@@ -43,7 +42,7 @@ catto: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [0,]
|
[timestamp] [0,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { 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"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { primary_key: Some("fish") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("fish") }}
|
2 {uid: 2, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { primary_key: Some("fish"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("fish") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
@@ -48,9 +47,9 @@ doggo: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [2,]
|
[timestamp] [2,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, }
|
1 {uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
2 {uid: 2, details: {"primaryKey":"fish"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexCreation":1},"indexUids":{"whalo":1}}, }
|
2 {uid: 2, details: {"primaryKey":"fish"}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexCreation":1},"indexUids":{"whalo":1}}, stop reason: "created batch containing only task with id 2 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, status: enqueued, details: { primary_key: Some("fish") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("fish") }}
|
2 {uid: 2, status: enqueued, details: { primary_key: Some("fish"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("fish") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,1,2,]
|
enqueued [0,1,2,]
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { 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"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }}
|
2 {uid: 2, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Planned failure for tests.", error_code: "internal", error_type: "internal", error_link: "https://docs.meilisearch.com/errors#internal" }, details: { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }}
|
||||||
3 {uid: 3, batch_uid: 3, status: failed, error: ResponseError { code: 200, message: "Index `whalo` not found.", error_code: "index_not_found", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#index_not_found" }, details: { swaps: [IndexSwap { indexes: ("catto", "whalo") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "whalo") }] }}
|
3 {uid: 3, batch_uid: 3, status: failed, error: ResponseError { code: 200, message: "Index `whalo` not found.", error_code: "index_not_found", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#index_not_found" }, details: { swaps: [IndexSwap { indexes: ("catto", "whalo"), rename: false }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "whalo"), rename: false }] }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
@@ -53,10 +52,10 @@ doggo: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [3,]
|
[timestamp] [3,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, }
|
1 {uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
2 {uid: 2, details: {"swaps":[{"indexes":["catto","doggo"]}]}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexSwap":1},"indexUids":{}}, }
|
2 {uid: 2, details: {"swaps":[{"indexes":["catto","doggo"],"rename":false}]}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexSwap":1},"indexUids":{}}, stop reason: "created batch containing only task with id 2 of type `indexSwap` that cannot be batched with any other task.", }
|
||||||
3 {uid: 3, details: {"swaps":[{"indexes":["catto","whalo"]}]}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexSwap":1},"indexUids":{}}, }
|
3 {uid: 3, details: {"swaps":[{"indexes":["catto","whalo"],"rename":false}]}, stats: {"totalNbTasks":1,"status":{"failed":1},"types":{"indexSwap":1},"indexUids":{}}, stop reason: "created batch containing only task with id 3 of type `indexSwap` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/batches_test.rs
|
source: crates/index-scheduler/src/queue/batches_test.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: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("mouse"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, status: enqueued, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, status: enqueued, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, status: enqueued, details: { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }}
|
2 {uid: 2, status: enqueued, details: { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }}
|
||||||
3 {uid: 3, status: enqueued, details: { swaps: [IndexSwap { indexes: ("catto", "whalo") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "whalo") }] }}
|
3 {uid: 3, status: enqueued, details: { swaps: [IndexSwap { indexes: ("catto", "whalo"), rename: false }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "whalo"), rename: false }] }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,1,2,3,]
|
enqueued [0,1,2,3,]
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/tasks_test.rs
|
source: crates/index-scheduler/src/queue/tasks_test.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: { 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"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }}
|
||||||
1 {uid: 1, batch_uid: 1, status: canceled, canceled_by: 3, details: { primary_key: Some("sheep") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
1 {uid: 1, batch_uid: 1, status: canceled, canceled_by: 3, details: { primary_key: Some("sheep"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("sheep") }}
|
||||||
2 {uid: 2, batch_uid: 1, status: canceled, canceled_by: 3, details: { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo") }] }}
|
2 {uid: 2, batch_uid: 1, status: canceled, canceled_by: 3, details: { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("catto", "doggo"), rename: false }] }}
|
||||||
3 {uid: 3, batch_uid: 1, status: succeeded, details: { matched_tasks: 3, canceled_tasks: Some(2), original_filter: "test_query" }, kind: TaskCancelation { query: "test_query", tasks: RoaringBitmap<[0, 1, 2]> }}
|
3 {uid: 3, batch_uid: 1, status: succeeded, details: { matched_tasks: 3, canceled_tasks: Some(2), original_filter: "test_query" }, kind: TaskCancelation { query: "test_query", tasks: RoaringBitmap<[0, 1, 2]> }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
@@ -49,8 +48,8 @@ catto: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [1,2,3,]
|
[timestamp] [1,2,3,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
0 {uid: 0, details: {"primaryKey":"mouse"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"sheep","matchedTasks":3,"canceledTasks":2,"originalFilter":"test_query","swaps":[{"indexes":["catto","doggo"]}]}, stats: {"totalNbTasks":3,"status":{"succeeded":1,"canceled":2},"types":{"indexCreation":1,"indexSwap":1,"taskCancelation":1},"indexUids":{"doggo":1}}, }
|
1 {uid: 1, details: {"primaryKey":"sheep","matchedTasks":3,"canceledTasks":2,"originalFilter":"test_query","swaps":[{"indexes":["catto","doggo"],"rename":false}]}, stats: {"totalNbTasks":3,"status":{"succeeded":1,"canceled":2},"types":{"indexCreation":1,"indexSwap":1,"taskCancelation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 3 of type `taskCancelation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/tasks_test.rs
|
source: crates/index-scheduler/src/queue/tasks_test.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: { primary_key: Some("bone") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
0 {uid: 0, batch_uid: 0, status: succeeded, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("plankton") }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
1 {uid: 1, batch_uid: 1, status: succeeded, details: { primary_key: Some("plankton"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "whalo", primary_key: Some("plankton") }}
|
||||||
2 {uid: 2, batch_uid: 2, status: succeeded, details: { primary_key: Some("his_own_vomit") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("his_own_vomit") }}
|
2 {uid: 2, batch_uid: 2, status: succeeded, details: { primary_key: Some("his_own_vomit"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "catto", primary_key: Some("his_own_vomit") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
@@ -48,9 +47,9 @@ whalo: { number_of_documents: 0, field_distribution: {} }
|
|||||||
[timestamp] [2,]
|
[timestamp] [2,]
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### All Batches:
|
### All Batches:
|
||||||
0 {uid: 0, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, }
|
0 {uid: 0, details: {"primaryKey":"bone"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, stop reason: "created batch containing only task with id 0 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
1 {uid: 1, details: {"primaryKey":"plankton"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"whalo":1}}, }
|
1 {uid: 1, details: {"primaryKey":"plankton"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"whalo":1}}, stop reason: "created batch containing only task with id 1 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
2 {uid: 2, details: {"primaryKey":"his_own_vomit"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, }
|
2 {uid: 2, details: {"primaryKey":"his_own_vomit"}, stats: {"totalNbTasks":1,"status":{"succeeded":1},"types":{"indexCreation":1},"indexUids":{"catto":1}}, stop reason: "created batch containing only task with id 2 of type `indexCreation` that cannot be batched with any other task.", }
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Batch to tasks mapping:
|
### Batch to tasks mapping:
|
||||||
0 [0,]
|
0 [0,]
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
source: crates/index-scheduler/src/queue/tasks_test.rs
|
source: crates/index-scheduler/src/queue/tasks_test.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: { primary_key: Some("bone") }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
0 {uid: 0, status: enqueued, details: { primary_key: Some("bone"), old_new_uid: None, new_index_uid: None }, kind: IndexCreation { index_uid: "doggo", primary_key: Some("bone") }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued [0,]
|
enqueued [0,]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user