From 59c67f6bc88dc27e5b07e8b8ab5cdc937bcca696 Mon Sep 17 00:00:00 2001 From: mpostma Date: Tue, 5 May 2020 22:19:34 +0200 Subject: [PATCH] setting up facets --- Cargo.lock | 195 +++++++------- meilisearch-core/Cargo.toml | 2 + meilisearch-core/src/error.rs | 49 +++- meilisearch-core/src/facets.rs | 353 ++++++++++++++++++++++++++ meilisearch-core/src/lib.rs | 5 +- meilisearch-core/src/store/cow_set.rs | 32 +++ meilisearch-core/src/store/facets.rs | 53 ++++ meilisearch-core/src/store/main.rs | 17 +- meilisearch-core/src/store/mod.rs | 45 ++-- meilisearch-http/src/error.rs | 10 + meilisearch-schema/src/lib.rs | 11 +- 11 files changed, 633 insertions(+), 139 deletions(-) create mode 100644 meilisearch-core/src/facets.rs create mode 100644 meilisearch-core/src/store/cow_set.rs create mode 100644 meilisearch-core/src/store/facets.rs diff --git a/Cargo.lock b/Cargo.lock index 8be56eb68..1eb95126d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,15 +138,16 @@ dependencies = [ [[package]] name = "actix-rt" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20066d9200ef8d441ac156c76dd36c3f1e9a15976c34e69ae97f7f570b331882" +checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" dependencies = [ "actix-macros", "actix-threadpool", "copyless", "futures-channel", "futures-util", + "smallvec", "tokio", ] @@ -309,12 +310,9 @@ checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] name = "ahash" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b909d1c126f78ace756fc337133356c499eebeefcce930fa5fb018823f2b2d" -dependencies = [ - "const-random", -] +checksum = "9c251dce3391a07b43218ca070203ecb8f9f520d35ab71312296a59dbceab154" [[package]] name = "aho-corasick" @@ -436,9 +434,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7" +checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" dependencies = [ "cc", "libc", @@ -452,9 +450,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" +checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" [[package]] name = "bincode" @@ -646,26 +644,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ab08c5bed92075075d5db5149887a477b2dc0318c40882a0dfbd34315ac6141" -[[package]] -name = "const-random" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" -dependencies = [ - "const-random-macro", - "proc-macro-hack", -] - -[[package]] -name = "const-random-macro" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" -dependencies = [ - "getrandom", - "proc-macro-hack", -] - [[package]] name = "copyless" version = "0.1.4" @@ -688,6 +666,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +[[package]] +name = "cow-utils" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79bb3adfaf5f75d24b01aee375f7555907840fa2800e5ec8fa3b9e2031830173" + [[package]] name = "crc32fast" version = "1.2.0" @@ -699,16 +683,16 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" +checksum = "63f696897c88b57f4ffe3c69d8e1a0613c7d0e6c4833363c8560fbde9c47b966" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", - "itertools 0.8.2", + "itertools", "lazy_static", "num-traits", "oorandom", @@ -724,12 +708,12 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" +checksum = "ddeaf7989f00f2e1d871a26a110f3ed713632feac17f65f03ca938c542618b60" dependencies = [ "cast", - "itertools 0.8.2", + "itertools", ] [[package]] @@ -926,9 +910,9 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ "backtrace", "failure_derive", @@ -936,9 +920,9 @@ dependencies = [ [[package]] name = "failure_derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", "quote", @@ -1153,9 +1137,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" +checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" dependencies = [ "bytes 0.5.4", "fnv", @@ -1172,9 +1156,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479e9d9a1a3f8c489868a935b557ab5710e3e223836da2ecd52901d88935cb56" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ "ahash", "autocfg", @@ -1432,9 +1416,9 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" dependencies = [ "socket2", "widestring", @@ -1442,15 +1426,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "itertools" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.9.0" @@ -1489,9 +1464,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" +checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7" dependencies = [ "wasm-bindgen", ] @@ -1535,9 +1510,9 @@ checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lmdb-rkv-sys" @@ -1617,17 +1592,19 @@ dependencies = [ "byteorder", "chrono", "compact_arena", + "cow-utils", "criterion", "crossbeam-channel", "csv", "deunicode", + "either", "env_logger", "fst", "hashbrown", "heed", "indexmap", "intervaltree", - "itertools 0.9.0", + "itertools", "jemallocator", "levenshtein_automata", "log", @@ -1768,9 +1745,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -1787,9 +1764,9 @@ dependencies = [ [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", @@ -1828,9 +1805,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", @@ -1906,9 +1883,9 @@ checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" [[package]] name = "oorandom" -version = "11.1.0" +version = "11.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" +checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" [[package]] name = "opaque-debug" @@ -2058,18 +2035,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" +checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63" dependencies = [ "proc-macro2", "quote", @@ -2078,9 +2055,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" [[package]] name = "pin-utils" @@ -2096,9 +2073,9 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "plotters" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +checksum = "f9b1d9ca091d370ea3a78d5619145d1b59426ab0c9eedbad2514a4cee08bf389" dependencies = [ "js-sys", "num-traits", @@ -2162,9 +2139,9 @@ checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" dependencies = [ "unicode-xid", ] @@ -2186,9 +2163,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" dependencies = [ "proc-macro2", ] @@ -2398,13 +2375,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.12" +version = "0.16.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196" dependencies = [ "cc", - "lazy_static", "libc", + "once_cell", "spin", "untrusted", "web-sys", @@ -2615,9 +2592,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" dependencies = [ "indexmap", "itoa", @@ -2773,9 +2750,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" dependencies = [ "proc-macro2", "quote", @@ -2902,9 +2879,9 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9c43f1bb96970e153bcbae39a65e249ccb942bd9d36dbdf086024920417c9c" +checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" dependencies = [ "bytes 0.5.4", "fnv", @@ -3113,9 +3090,9 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" @@ -3123,7 +3100,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd754afd5f60388b4188210c3795392c5f2fd69a1cc947ec4505dbfee955b902" dependencies = [ - "base64 0.12.0", + "base64 0.12.1", "chunked_transfer", "lazy_static", "qstring", @@ -3200,9 +3177,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "vergen" @@ -3261,9 +3238,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" +checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551" dependencies = [ "cfg-if", "serde", @@ -3273,9 +3250,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" +checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" dependencies = [ "bumpalo", "lazy_static", @@ -3288,9 +3265,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" +checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04" dependencies = [ "cfg-if", "js-sys", @@ -3300,9 +3277,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" +checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3310,9 +3287,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" +checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" dependencies = [ "proc-macro2", "quote", @@ -3323,15 +3300,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" +checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" [[package]] name = "web-sys" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" +checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/meilisearch-core/Cargo.toml b/meilisearch-core/Cargo.toml index e616c8328..00e8640d0 100644 --- a/meilisearch-core/Cargo.toml +++ b/meilisearch-core/Cargo.toml @@ -11,8 +11,10 @@ bincode = "1.2.1" byteorder = "1.3.4" chrono = { version = "0.4.11", features = ["serde"] } compact_arena = "0.4.0" +cow-utils = "0.1.2" crossbeam-channel = "0.4.2" deunicode = "1.1.0" +either = "1.5.3" env_logger = "0.7.1" fst = { version = "0.3.5", default-features = false } hashbrown = { version = "0.7.1", features = ["serde"] } diff --git a/meilisearch-core/src/error.rs b/meilisearch-core/src/error.rs index 110d6ab76..d871d782e 100644 --- a/meilisearch-core/src/error.rs +++ b/meilisearch-core/src/error.rs @@ -28,7 +28,8 @@ pub enum Error { Serializer(SerializerError), Deserializer(DeserializerError), UnsupportedOperation(UnsupportedOperation), - FilterParseError(PestError) + FilterParseError(PestError), + FacetError(FacetError), } impl From for Error { @@ -57,7 +58,13 @@ impl From> for Error { s.to_string() })) } -} +} + +impl From for Error { + fn from(error: FacetError) -> Error { + Error::FacetError(error) + } +} impl From for Error { fn from(error: meilisearch_schema::Error) -> Error { @@ -127,6 +134,7 @@ impl fmt::Display for Error { Deserializer(e) => write!(f, "deserializer error; {}", e), UnsupportedOperation(op) => write!(f, "unsupported operation; {}", op), FilterParseError(e) => write!(f, "error parsing filter; {}", e), + FacetError(e) => write!(f, "error processing facet filter: {}", e), } } } @@ -156,3 +164,40 @@ impl fmt::Display for UnsupportedOperation { } } } + +#[derive(Debug)] +pub enum FacetError { + EmptyArray, + ParsingError(String), + UnexpectedToken { expected: &'static [&'static str], found: String }, + InvalidFormat(String), + AttributeNotFound(String), + AttributeNotSet { expected: Vec, found: String }, + InvalidDocumentAttribute(String), +} + +impl FacetError { + pub fn unexpected_token(expected: &'static [&'static str], found: impl ToString) -> FacetError { + FacetError::UnexpectedToken{ expected, found: found.to_string() } + } + + pub fn attribute_not_set(expected: Vec, found: impl ToString) -> FacetError { + FacetError::AttributeNotSet{ expected, found: found.to_string() } + } +} + +impl fmt::Display for FacetError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use FacetError::*; + + match self { + EmptyArray => write!(f, "empty array in facet filter is unspecified behavior"), + ParsingError(msg) => write!(f, "parsing error: {}", msg), + UnexpectedToken { expected, found } => write!(f, "unexpected token {}, expected {}", found, expected.join("or")), + InvalidFormat(found) => write!(f, "invalid facet: {}, facets should be \"facetName:facetValue\"", found), + AttributeNotFound(attr) => write!(f, "unknown {:?} attribute", attr), + AttributeNotSet { found, expected } => write!(f, "`{}` is not set as a faceted attribute. available facet attributes: {}", found, expected.join(", ")), + InvalidDocumentAttribute(attr) => write!(f, "invalid document attribute {}, accepted types: string and [string]", attr), + } + } +} diff --git a/meilisearch-core/src/facets.rs b/meilisearch-core/src/facets.rs new file mode 100644 index 000000000..a71991468 --- /dev/null +++ b/meilisearch-core/src/facets.rs @@ -0,0 +1,353 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use std::hash::Hash; +use std::ops::Deref; + +use cow_utils::CowUtils; +use either::Either; +use heed::types::{Str, OwnedType}; +use indexmap::IndexMap; +use serde_json::Value; + +use meilisearch_schema::{FieldId, Schema}; +use meilisearch_types::DocumentId; + +use crate::database::MainT; +use crate::error::{FacetError, Error}; +use crate::store::BEU16; + +/// Data structure used to represent a boolean expression in the form of nested arrays. +/// Values in the outer array are and-ed together, values in the inner arrays are or-ed together. +#[derive(Debug, PartialEq)] +pub struct FacetFilter(Vec, FacetKey>>); + +impl Deref for FacetFilter { + type Target = Vec, FacetKey>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FacetFilter { + pub fn from_str( + s: &str, + schema: &Schema, + attributes_for_faceting: &[FieldId], + ) -> Result { + + let parsed = serde_json::from_str::(s).map_err(|e| FacetError::ParsingError(e.to_string()))?; + let mut filter = Vec::new(); + match parsed { + Value::Array(and_exprs) => { + if and_exprs.is_empty() { + return Err(FacetError::EmptyArray); + } + for expr in and_exprs { + match expr { + Value::String(s) => { + let key = FacetKey::from_str( &s, schema, attributes_for_faceting)?; + filter.push(Either::Right(key)); + } + Value::Array(or_exprs) => { + if or_exprs.is_empty() { + return Err(FacetError::EmptyArray); + } + let mut inner = Vec::new(); + for expr in or_exprs { + match expr { + Value::String(s) => { + let key = FacetKey::from_str( &s, schema, attributes_for_faceting)?; + inner.push(key); + } + bad_value => return Err(FacetError::unexpected_token(&["String"], bad_value)), + } + } + filter.push(Either::Left(inner)); + } + bad_value => return Err(FacetError::unexpected_token(&["Array", "String"], bad_value)), + } + } + return Ok(Self(filter)); + } + bad_value => Err(FacetError::unexpected_token(&["String"], bad_value)), + } + } +} + +#[derive(Debug, Eq, PartialEq, Hash)] +#[repr(C)] +pub struct FacetKey(FieldId, String); + +impl FacetKey { + pub fn new(field_id: FieldId, value: String) -> Self { + let value = match value.cow_to_lowercase() { + Cow::Borrowed(_) => value, + Cow::Owned(s) => s, + }; + Self(field_id, value) + } + + pub fn key(&self) -> FieldId { + self.0 + } + + pub fn value(&self) -> &str { + &self.1 + } + + // TODO improve parser + fn from_str( + s: &str, + schema: &Schema, + attributes_for_faceting: &[FieldId], + ) -> Result { + let mut split = s.splitn(2, ':'); + let key = split + .next() + .ok_or_else(|| FacetError::InvalidFormat(s.to_string()))? + .trim(); + let field_id = schema + .id(key) + .ok_or_else(|| FacetError::AttributeNotFound(key.to_string()))?; + + if !attributes_for_faceting.contains(&field_id) { + return Err(FacetError::attribute_not_set( + attributes_for_faceting + .iter() + .filter_map(|&id| schema.name(id)) + .map(str::to_string) + .collect::>(), + key)) + } + let value = split + .next() + .ok_or_else(|| FacetError::InvalidFormat(s.to_string()))? + .trim(); + // unquoting the string if need be: + let mut indices = value.char_indices(); + let value = match (indices.next(), indices.last()) { + (Some((s, '\'')), Some((e, '\''))) | + (Some((s, '\"')), Some((e, '\"'))) => value[s + 1..e].to_string(), + _ => value.to_string(), + }; + Ok(Self::new(field_id, value)) + } +} + +impl<'a> heed::BytesEncode<'a> for FacetKey { + type EItem = FacetKey; + + fn bytes_encode(item: &'a Self::EItem) -> Option> { + let mut buffer = Vec::with_capacity(2 + item.1.len()); + let id = BEU16::new(item.key().into()); + let id_bytes = OwnedType::bytes_encode(&id)?; + let value_bytes = Str::bytes_encode(item.value())?; + buffer.extend_from_slice(id_bytes.as_ref()); + buffer.extend_from_slice(value_bytes.as_ref()); + Some(Cow::Owned(buffer)) + } +} + +impl<'a> heed::BytesDecode<'a> for FacetKey { + type DItem = FacetKey; + + fn bytes_decode(bytes: &'a [u8]) -> Option { + let (id_bytes, value_bytes) = bytes.split_at(2); + let id = OwnedType::::bytes_decode(id_bytes)?; + let id = id.get().into(); + let string = Str::bytes_decode(&value_bytes)?; + Some(FacetKey(id, string.to_string())) + } +} + +pub fn add_to_facet_map( + facet_map: &mut HashMap>, + field_id: FieldId, + value: Value, + document_id: DocumentId, +) -> Result<(), FacetError> { + let value = match value { + Value::String(s) => s, + // ignore null + Value::Null => return Ok(()), + value => return Err(FacetError::InvalidDocumentAttribute(value.to_string())), + }; + let key = FacetKey::new(field_id, value); + facet_map.entry(key).or_insert_with(Vec::new).push(document_id); + Ok(()) +} + +pub fn facet_map_from_docids( + rtxn: &heed::RoTxn, + index: &crate::Index, + document_ids: &[DocumentId], + attributes_for_facetting: &[FieldId], +) -> Result>, Error> { + let mut facet_map = HashMap::new(); + for document_id in document_ids { + for result in index + .documents_fields + .document_fields(rtxn, *document_id)? + { + let (field_id, bytes) = result?; + if attributes_for_facetting.contains(&field_id) { + match serde_json::from_slice(bytes)? { + Value::Array(values) => { + for v in values { + add_to_facet_map(&mut facet_map, field_id, v, *document_id)?; + } + } + v => add_to_facet_map(&mut facet_map, field_id, v, *document_id)?, + }; + } + } + } + Ok(facet_map) +} + +pub fn facet_map_from_docs( + schema: &Schema, + documents: &HashMap>, + attributes_for_facetting: &[FieldId], +) -> Result>, Error> { + let mut facet_map = HashMap::new(); + let attributes_for_facetting = attributes_for_facetting + .iter() + .filter_map(|&id| schema.name(id).map(|name| (id, name))) + .collect::>(); + + for (id, document) in documents { + for (field_id, name) in &attributes_for_facetting { + if let Some(value) = document.get(*name) { + match value { + Value::Array(values) => { + for v in values { + add_to_facet_map(&mut facet_map, *field_id, v.clone(), *id)?; + } + } + v => add_to_facet_map(&mut facet_map, *field_id, v.clone(), *id)?, + } + } + } + } + Ok(facet_map) +} + +#[cfg(test)] +mod test { + use super::*; + use meilisearch_schema::Schema; + + #[test] + fn test_facet_key() { + let mut schema = Schema::new(); + let id = schema.insert_and_index("hello").unwrap(); + let facet_list = [schema.id("hello").unwrap()]; + assert_eq!( + FacetKey::from_str("hello:12", &schema, &facet_list).unwrap(), + FacetKey::new(id, "12".to_string()) + ); + assert_eq!( + FacetKey::from_str("hello:\"foo bar\"", &schema, &facet_list).unwrap(), + FacetKey::new(id, "foo bar".to_string()) + ); + assert_eq!( + FacetKey::from_str("hello:'foo bar'", &schema, &facet_list).unwrap(), + FacetKey::new(id, "foo bar".to_string()) + ); + // weird case + assert_eq!( + FacetKey::from_str("hello:blabla:machin", &schema, &facet_list).unwrap(), + FacetKey::new(id, "blabla:machin".to_string()) + ); + + assert_eq!( + FacetKey::from_str("hello:\"\"", &schema, &facet_list).unwrap(), + FacetKey::new(id, "".to_string()) + ); + + assert_eq!( + FacetKey::from_str("hello:'", &schema, &facet_list).unwrap(), + FacetKey::new(id, "'".to_string()) + ); + assert_eq!( + FacetKey::from_str("hello:''", &schema, &facet_list).unwrap(), + FacetKey::new(id, "".to_string()) + ); + assert!(FacetKey::from_str("hello", &schema, &facet_list).is_err()); + assert!(FacetKey::from_str("toto:12", &schema, &facet_list).is_err()); + } + + #[test] + fn test_parse_facet_array() { + use either::Either::{Left, Right}; + let mut schema = Schema::new(); + let _id = schema.insert_and_index("hello").unwrap(); + let facet_list = [schema.id("hello").unwrap()]; + assert_eq!( + FacetFilter::from_str("[[\"hello:12\"]]", &schema, &facet_list).unwrap(), + FacetFilter(vec![Left(vec![FacetKey(FieldId(0), "12".to_string())])]) + ); + assert_eq!( + FacetFilter::from_str("[\"hello:12\"]", &schema, &facet_list).unwrap(), + FacetFilter(vec![Right(FacetKey(FieldId(0), "12".to_string()))]) + ); + assert_eq!( + FacetFilter::from_str("[\"hello:12\", \"hello:13\"]", &schema, &facet_list).unwrap(), + FacetFilter(vec![ + Right(FacetKey(FieldId(0), "12".to_string())), + Right(FacetKey(FieldId(0), "13".to_string())) + ]) + ); + assert_eq!( + FacetFilter::from_str("[[\"hello:12\", \"hello:13\"]]", &schema, &facet_list).unwrap(), + FacetFilter(vec![Left(vec![ + FacetKey(FieldId(0), "12".to_string()), + FacetKey(FieldId(0), "13".to_string()) + ])]) + ); + assert_eq!( + FacetFilter::from_str( + "[[\"hello:12\", \"hello:13\"], \"hello:14\"]", + &schema, + &facet_list + ) + .unwrap(), + FacetFilter(vec![ + Left(vec![ + FacetKey(FieldId(0), "12".to_string()), + FacetKey(FieldId(0), "13".to_string()) + ]), + Right(FacetKey(FieldId(0), "14".to_string())) + ]) + ); + + // invalid array depths + assert!(FacetFilter::from_str( + "[[[\"hello:12\", \"hello:13\"], \"hello:14\"]]", + &schema, + &facet_list + ) + .is_err()); + assert!(FacetFilter::from_str( + "[[[\"hello:12\", \"hello:13\"]], \"hello:14\"]]", + &schema, + &facet_list + ) + .is_err()); + assert!(FacetFilter::from_str("\"hello:14\"", &schema, &facet_list).is_err()); + + // unexisting key + assert!(FacetFilter::from_str("[\"foo:12\"]", &schema, &facet_list).is_err()); + + // invalid facet key + assert!(FacetFilter::from_str("[\"foo=12\"]", &schema, &facet_list).is_err()); + assert!(FacetFilter::from_str("[\"foo12\"]", &schema, &facet_list).is_err()); + assert!(FacetFilter::from_str("[\"\"]", &schema, &facet_list).is_err()); + + // empty array error + assert!(FacetFilter::from_str("[]", &schema, &facet_list).is_err()); + assert!(FacetFilter::from_str("[\"hello:12\", []]", &schema, &facet_list).is_err()); + } +} diff --git a/meilisearch-core/src/lib.rs b/meilisearch-core/src/lib.rs index 87db276a0..179c7746c 100644 --- a/meilisearch-core/src/lib.rs +++ b/meilisearch-core/src/lib.rs @@ -19,14 +19,15 @@ mod ranked_map; mod raw_document; mod reordered_attrs; mod update; -pub mod settings; pub mod criterion; +pub mod facets; pub mod raw_indexer; +pub mod settings; pub mod serde; pub mod store; pub use self::database::{BoxUpdateFn, Database, DatabaseOptions, MainT, UpdateT}; -pub use self::error::{Error, HeedError, FstError, MResult, pest_error}; +pub use self::error::{Error, HeedError, FstError, MResult, pest_error, FacetError}; pub use self::filters::Filter; pub use self::number::{Number, ParseNumberError}; pub use self::ranked_map::RankedMap; diff --git a/meilisearch-core/src/store/cow_set.rs b/meilisearch-core/src/store/cow_set.rs new file mode 100644 index 000000000..063f73198 --- /dev/null +++ b/meilisearch-core/src/store/cow_set.rs @@ -0,0 +1,32 @@ +use std::borrow::Cow; + +use heed::{types::CowSlice, BytesEncode, BytesDecode}; +use sdset::{Set, SetBuf}; +use zerocopy::{AsBytes, FromBytes}; + +pub struct CowSet(std::marker::PhantomData); + +impl<'a, T: 'a> BytesEncode<'a> for CowSet +where + T: AsBytes, +{ + type EItem = Set; + + fn bytes_encode(item: &'a Self::EItem) -> Option> { + CowSlice::bytes_encode(item.as_slice()) + } +} + +impl<'a, T: 'a> BytesDecode<'a> for CowSet +where + T: FromBytes + Copy, +{ + type DItem = Cow<'a, Set>; + + fn bytes_decode(bytes: &'a [u8]) -> Option { + match CowSlice::::bytes_decode(bytes)? { + Cow::Owned(vec) => Some(Cow::Owned(SetBuf::new_unchecked(vec))), + Cow::Borrowed(slice) => Some(Cow::Borrowed(Set::new_unchecked(slice))), + } + } +} diff --git a/meilisearch-core/src/store/facets.rs b/meilisearch-core/src/store/facets.rs new file mode 100644 index 000000000..c9811f765 --- /dev/null +++ b/meilisearch-core/src/store/facets.rs @@ -0,0 +1,53 @@ +use std::borrow::Cow; +use std::collections::HashMap; + +use heed::{RwTxn, RoTxn, Result as ZResult}; +use sdset::{SetBuf, Set, SetOperation}; + +use meilisearch_types::DocumentId; + +use crate::database::MainT; +use crate::facets::FacetKey; +use super::cow_set::CowSet; + +/// contains facet info +#[derive(Clone, Copy)] +pub struct Facets { + pub(crate) facets: heed::Database>, +} + +impl Facets { + // we use sdset::SetBuf to ensure the docids are sorted. + pub fn put_facet_document_ids(&self, writer: &mut RwTxn, facet_key: FacetKey, doc_ids: &Set) -> ZResult<()> { + self.facets.put(writer, &facet_key, doc_ids) + } + + pub fn facet_document_ids<'txn>(&self, reader: &'txn RoTxn, facet_key: &FacetKey) -> ZResult>>> { + self.facets.get(reader, &facet_key) + } + + /// updates the facets store, revmoving the documents from the facets provided in the + /// `facet_map` argument + pub fn remove(&self, writer: &mut RwTxn, facet_map: HashMap>) -> ZResult<()> { + for (key, document_ids) in facet_map { + if let Some(old) = self.facets.get(writer, &key)? { + let to_remove = SetBuf::from_dirty(document_ids); + let new = sdset::duo::OpBuilder::new(old.as_ref(), to_remove.as_set()).difference().into_set_buf(); + self.facets.put(writer, &key, new.as_set())?; + } + } + Ok(()) + } + + pub fn add(&self, writer: &mut RwTxn, facet_map: HashMap>) -> ZResult<()> { + for (key, document_ids) in facet_map { + let set = SetBuf::from_dirty(document_ids); + self.put_facet_document_ids(writer, key, set.as_set())?; + } + Ok(()) + } + + pub fn clear(self, writer: &mut heed::RwTxn) -> ZResult<()> { + self.facets.clear(writer) + } +} diff --git a/meilisearch-core/src/store/main.rs b/meilisearch-core/src/store/main.rs index a156cee4b..33737a002 100644 --- a/meilisearch-core/src/store/main.rs +++ b/meilisearch-core/src/store/main.rs @@ -4,13 +4,16 @@ use std::collections::HashMap; use chrono::{DateTime, Utc}; use heed::types::{ByteSlice, OwnedType, SerdeBincode, Str}; use heed::Result as ZResult; -use meilisearch_schema::Schema; +use meilisearch_schema::{FieldId, Schema}; +use sdset::Set; use crate::database::MainT; use crate::RankedMap; use crate::settings::RankingRule; +use super::cow_set::CowSet; const CREATED_AT_KEY: &str = "created-at"; +const ATTRIBUTES_FOR_FACETING: &str = "attributes-for-faceting"; const RANKING_RULES_KEY: &str = "ranking-rules"; const DISTINCT_ATTRIBUTE_KEY: &str = "distinct-attribute"; const STOP_WORDS_KEY: &str = "stop-words"; @@ -188,6 +191,18 @@ impl Main { } } + pub fn attributes_for_faceting<'txn>(&self, reader: &'txn heed::RoTxn) -> ZResult>>> { + self.main.get::<_, Str, CowSet>(reader, ATTRIBUTES_FOR_FACETING) + } + + pub fn put_attributes_for_faceting(self, writer: &mut heed::RwTxn, attributes: &Set) -> ZResult<()> { + self.main.put::<_, Str, CowSet>(writer, ATTRIBUTES_FOR_FACETING, attributes) + } + + pub fn delete_attributes_for_faceting(self, writer: &mut heed::RwTxn) -> ZResult { + self.main.delete::<_, Str>(writer, ATTRIBUTES_FOR_FACETING) + } + pub fn ranking_rules(&self, reader: &heed::RoTxn) -> ZResult>> { self.main.get::<_, Str, SerdeBincode>>(reader, RANKING_RULES_KEY) } diff --git a/meilisearch-core/src/store/mod.rs b/meilisearch-core/src/store/mod.rs index 48bcfcef8..c0df09809 100644 --- a/meilisearch-core/src/store/mod.rs +++ b/meilisearch-core/src/store/mod.rs @@ -1,3 +1,4 @@ +mod cow_set; mod docs_words; mod prefix_documents_cache; mod prefix_postings_lists_cache; @@ -8,8 +9,10 @@ mod postings_lists; mod synonyms; mod updates; mod updates_results; +mod facets; pub use self::docs_words::DocsWords; +pub use self::facets::Facets; pub use self::prefix_documents_cache::PrefixDocumentsCache; pub use self::prefix_postings_lists_cache::PrefixPostingsListsCache; pub use self::documents_fields::{DocumentFieldsIter, DocumentsFields}; @@ -42,7 +45,7 @@ use crate::settings::SettingsUpdate; use crate::{query_builder::QueryBuilder, update, DocIndex, DocumentId, Error, MResult}; type BEU64 = zerocopy::U64; -type BEU16 = zerocopy::U16; +pub type BEU16 = zerocopy::U16; #[derive(Debug, Copy, Clone, AsBytes, FromBytes)] #[repr(C)] @@ -197,12 +200,17 @@ fn updates_results_name(name: &str) -> String { format!("store-{}-updates-results", name) } +fn facets_name(name: &str) -> String { + format!("store-{}-facets", name) +} + #[derive(Clone)] pub struct Index { pub main: Main, pub postings_lists: PostingsLists, pub documents_fields: DocumentsFields, pub documents_fields_counts: DocumentsFieldsCounts, + pub facets: Facets, pub synonyms: Synonyms, pub docs_words: DocsWords, pub prefix_documents_cache: PrefixDocumentsCache, @@ -352,29 +360,14 @@ impl Index { } pub fn query_builder(&self) -> QueryBuilder { - QueryBuilder::new( - self.main, - self.postings_lists, - self.documents_fields_counts, - self.synonyms, - self.prefix_documents_cache, - self.prefix_postings_lists_cache, - ) + QueryBuilder::new(self) } - pub fn query_builder_with_criteria<'c, 'f, 'd>( - &self, + pub fn query_builder_with_criteria<'c, 'f, 'd, 'fa, 'i>( + &'i self, criteria: Criteria<'c>, - ) -> QueryBuilder<'c, 'f, 'd> { - QueryBuilder::with_criteria( - self.main, - self.postings_lists, - self.documents_fields_counts, - self.synonyms, - self.prefix_documents_cache, - self.prefix_postings_lists_cache, - criteria, - ) + ) -> QueryBuilder<'c, 'f, 'd, 'fa, 'i> { + QueryBuilder::with_criteria(self, criteria) } } @@ -395,12 +388,14 @@ pub fn create( let prefix_postings_lists_cache_name = prefix_postings_lists_cache_name(name); let updates_name = updates_name(name); let updates_results_name = updates_results_name(name); + let facets_name = facets_name(name); // open all the stores let main = env.create_poly_database(Some(&main_name))?; let postings_lists = env.create_database(Some(&postings_lists_name))?; let documents_fields = env.create_database(Some(&documents_fields_name))?; let documents_fields_counts = env.create_database(Some(&documents_fields_counts_name))?; + let facets = env.create_database(Some(&facets_name))?; let synonyms = env.create_database(Some(&synonyms_name))?; let docs_words = env.create_database(Some(&docs_words_name))?; let prefix_documents_cache = env.create_database(Some(&prefix_documents_cache_name))?; @@ -417,6 +412,8 @@ pub fn create( docs_words: DocsWords { docs_words }, prefix_postings_lists_cache: PrefixPostingsListsCache { prefix_postings_lists_cache }, prefix_documents_cache: PrefixDocumentsCache { prefix_documents_cache }, + facets: Facets { facets }, + updates: Updates { updates }, updates_results: UpdatesResults { updates_results }, updates_notifier, @@ -437,6 +434,7 @@ pub fn open( let synonyms_name = synonyms_name(name); let docs_words_name = docs_words_name(name); let prefix_documents_cache_name = prefix_documents_cache_name(name); + let facets_name = facets_name(name); let prefix_postings_lists_cache_name = prefix_postings_lists_cache_name(name); let updates_name = updates_name(name); let updates_results_name = updates_results_name(name); @@ -470,6 +468,10 @@ pub fn open( Some(prefix_documents_cache) => prefix_documents_cache, None => return Ok(None), }; + let facets = match env.open_database(Some(&facets_name))? { + Some(facets) => facets, + None => return Ok(None), + }; let prefix_postings_lists_cache = match env.open_database(Some(&prefix_postings_lists_cache_name))? { Some(prefix_postings_lists_cache) => prefix_postings_lists_cache, None => return Ok(None), @@ -491,6 +493,7 @@ pub fn open( synonyms: Synonyms { synonyms }, docs_words: DocsWords { docs_words }, prefix_documents_cache: PrefixDocumentsCache { prefix_documents_cache }, + facets: Facets { facets }, prefix_postings_lists_cache: PrefixPostingsListsCache { prefix_postings_lists_cache }, updates: Updates { updates }, updates_results: UpdatesResults { updates_results }, diff --git a/meilisearch-http/src/error.rs b/meilisearch-http/src/error.rs index 638726ece..273694fb6 100644 --- a/meilisearch-http/src/error.rs +++ b/meilisearch-http/src/error.rs @@ -23,6 +23,7 @@ pub enum ResponseError { FilterParsing(String), RetrieveDocument(u64, String), SearchDocuments(String), + FacetExpression(String), } impl ResponseError { @@ -106,6 +107,7 @@ impl fmt::Display for ResponseError { Self::OpenIndex(err) => write!(f, "Impossible to open index; {}", err), Self::RetrieveDocument(id, err) => write!(f, "impossible to retrieve the document with id: {}; {}", id, err), Self::SearchDocuments(err) => write!(f, "impossible to search documents; {}", err), + Self::FacetExpression(e) => write!(f, "error parsing facet filter expression: {}", e), } } } @@ -125,6 +127,7 @@ impl aweb::error::ResponseError for ResponseError { | Self::InvalidIndexUid | Self::OpenIndex(_) | Self::RetrieveDocument(_, _) + | Self::FacetExpression(_) | Self::SearchDocuments(_) | Self::FilterParsing(_) => StatusCode::BAD_REQUEST, Self::DocumentNotFound(_) @@ -151,6 +154,12 @@ impl From for ResponseError { } } +impl From for ResponseError { + fn from(error: meilisearch_core::FacetError) -> ResponseError { + ResponseError::FacetExpression(error.to_string()) + } +} + impl From for ResponseError { fn from(err: meilisearch_core::Error) -> ResponseError { use meilisearch_core::pest_error::LineColLocation::*; @@ -164,6 +173,7 @@ impl From for ResponseError { ResponseError::FilterParsing(message) }, + meilisearch_core::Error::FacetError(e) => ResponseError::FacetExpression(e.to_string()), _ => ResponseError::Internal(err.to_string()), } } diff --git a/meilisearch-schema/src/lib.rs b/meilisearch-schema/src/lib.rs index c56ac151e..7f17d5a89 100644 --- a/meilisearch-schema/src/lib.rs +++ b/meilisearch-schema/src/lib.rs @@ -36,7 +36,10 @@ impl Into for IndexedPos { } } -#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize)] +#[derive(AsBytes, FromBytes)] +#[repr(C)] pub struct FieldId(pub u16); impl FieldId { @@ -63,8 +66,8 @@ impl From for FieldId { } } -impl Into for FieldId { - fn into(self) -> u16 { - self.0 +impl From for u16 { + fn from(other: FieldId) -> u16 { + other.0 } }