mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-12-04 11:45:44 +00:00
Compare commits
8 Commits
document-b
...
panic-repo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b06e04fb9b | ||
|
|
905dc241ae | ||
|
|
146908f062 | ||
|
|
84f701679d | ||
|
|
355d3b7e45 | ||
|
|
69354a6144 | ||
|
|
2b5d9042d1 | ||
|
|
5b57fbab08 |
3
.github/workflows/benchmarks-pr.yml
vendored
3
.github/workflows/benchmarks-pr.yml
vendored
@@ -90,7 +90,8 @@ jobs:
|
|||||||
set -x
|
set -x
|
||||||
export base_ref=$(git merge-base origin/main ${{ steps.comment-branch.outputs.head_ref }} | head -c8)
|
export base_ref=$(git merge-base origin/main ${{ steps.comment-branch.outputs.head_ref }} | head -c8)
|
||||||
export base_filename=$(echo ${{ steps.command.outputs.command-arguments }}_main_${base_ref}.json)
|
export base_filename=$(echo ${{ steps.command.outputs.command-arguments }}_main_${base_ref}.json)
|
||||||
echo 'Here are your benchmarks diff 👊' >> body.txt
|
export bench_name=$(echo ${{ steps.command.outputs.command-arguments }})
|
||||||
|
echo "Here are your $bench_name benchmarks diff 👊" >> body.txt
|
||||||
echo '```' >> body.txt
|
echo '```' >> body.txt
|
||||||
./benchmarks/scripts/compare.sh $base_filename ${{ steps.file.outputs.basename }}.json >> body.txt
|
./benchmarks/scripts/compare.sh $base_filename ${{ steps.file.outputs.basename }}.json >> body.txt
|
||||||
echo '```' >> body.txt
|
echo '```' >> body.txt
|
||||||
|
|||||||
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -231,9 +231,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.20.0"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
|
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
@@ -435,9 +435,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.68"
|
version = "0.3.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
|
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -1638,9 +1638,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.27.3"
|
version = "0.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "git2"
|
name = "git2"
|
||||||
@@ -1894,6 +1894,7 @@ name = "index-scheduler"
|
|||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"backtrace",
|
||||||
"big_s",
|
"big_s",
|
||||||
"bincode",
|
"bincode",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
@@ -2856,9 +2857,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.31.1"
|
version = "0.32.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
|
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -3640,9 +3641,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.104"
|
version = "1.0.108"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.0.0",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ license = "MIT"
|
|||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
debug = true
|
||||||
|
|
||||||
[profile.dev.package.flate2]
|
[profile.dev.package.flate2]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use milli::heed::EnvOpenOptions;
|
|||||||
use milli::update::{
|
use milli::update::{
|
||||||
IndexDocuments, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Settings,
|
IndexDocuments, IndexDocumentsConfig, IndexDocumentsMethod, IndexerConfig, Settings,
|
||||||
};
|
};
|
||||||
use milli::{Filter, Index, Object, RankingRule, TermsMatchingStrategy};
|
use milli::{Criterion, Filter, Index, Object, TermsMatchingStrategy};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
pub struct Conf<'a> {
|
pub struct Conf<'a> {
|
||||||
@@ -78,11 +78,11 @@ pub fn base_setup(conf: &Conf) -> Index {
|
|||||||
|
|
||||||
if let Some(criterion) = conf.criterion {
|
if let Some(criterion) = conf.criterion {
|
||||||
builder.reset_filterable_fields();
|
builder.reset_filterable_fields();
|
||||||
builder.reset_ranking_rules();
|
builder.reset_criteria();
|
||||||
builder.reset_stop_words();
|
builder.reset_stop_words();
|
||||||
|
|
||||||
let criterion = criterion.iter().map(|s| RankingRule::from_str(s).unwrap()).collect();
|
let criterion = criterion.iter().map(|s| Criterion::from_str(s).unwrap()).collect();
|
||||||
builder.set_ranking_rules(criterion);
|
builder.set_criteria(criterion);
|
||||||
}
|
}
|
||||||
|
|
||||||
(conf.configure)(&mut builder);
|
(conf.configure)(&mut builder);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.70"
|
anyhow = "1.0.70"
|
||||||
|
backtrace = "0.3.69"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
csv = "1.2.1"
|
csv = "1.2.1"
|
||||||
derive_builder = "0.12.0"
|
derive_builder = "0.12.0"
|
||||||
|
|||||||
@@ -825,6 +825,10 @@ impl IndexScheduler {
|
|||||||
// 2. dump the tasks
|
// 2. dump the tasks
|
||||||
let mut dump_tasks = dump.create_tasks_queue()?;
|
let mut dump_tasks = dump.create_tasks_queue()?;
|
||||||
for ret in self.all_tasks.iter(&rtxn)? {
|
for ret in self.all_tasks.iter(&rtxn)? {
|
||||||
|
if self.must_stop_processing.get() {
|
||||||
|
return Err(Error::AbortedTask);
|
||||||
|
}
|
||||||
|
|
||||||
let (_, mut t) = ret?;
|
let (_, mut t) = ret?;
|
||||||
let status = t.status;
|
let status = t.status;
|
||||||
let content_file = t.content_uuid();
|
let content_file = t.content_uuid();
|
||||||
@@ -845,6 +849,9 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
// 2.1. Dump the `content_file` associated with the task if there is one and the task is not finished yet.
|
// 2.1. Dump the `content_file` associated with the task if there is one and the task is not finished yet.
|
||||||
if let Some(content_file) = content_file {
|
if let Some(content_file) = content_file {
|
||||||
|
if self.must_stop_processing.get() {
|
||||||
|
return Err(Error::AbortedTask);
|
||||||
|
}
|
||||||
if status == Status::Enqueued {
|
if status == Status::Enqueued {
|
||||||
let content_file = self.file_store.get_update(content_file)?;
|
let content_file = self.file_store.get_update(content_file)?;
|
||||||
|
|
||||||
@@ -884,6 +891,9 @@ impl IndexScheduler {
|
|||||||
|
|
||||||
// 3.1. Dump the documents
|
// 3.1. Dump the documents
|
||||||
for ret in index.all_documents(&rtxn)? {
|
for ret in index.all_documents(&rtxn)? {
|
||||||
|
if self.must_stop_processing.get() {
|
||||||
|
return Err(Error::AbortedTask);
|
||||||
|
}
|
||||||
let (_id, doc) = ret?;
|
let (_id, doc) = ret?;
|
||||||
let document = milli::obkv_to_json(&all_fields, &fields_ids_map, doc)?;
|
let document = milli::obkv_to_json(&all_fields, &fields_ids_map, doc)?;
|
||||||
index_dumper.push_document(&document)?;
|
index_dumper.push_document(&document)?;
|
||||||
@@ -903,6 +913,9 @@ impl IndexScheduler {
|
|||||||
"[year repr:full][month repr:numerical][day padding:zero]-[hour padding:zero][minute padding:zero][second padding:zero][subsecond digits:3]"
|
"[year repr:full][month repr:numerical][day padding:zero]-[hour padding:zero][minute padding:zero][second padding:zero][subsecond digits:3]"
|
||||||
)).unwrap();
|
)).unwrap();
|
||||||
|
|
||||||
|
if self.must_stop_processing.get() {
|
||||||
|
return Err(Error::AbortedTask);
|
||||||
|
}
|
||||||
let path = self.dumps_path.join(format!("{}.dump", dump_uid));
|
let path = self.dumps_path.join(format!("{}.dump", dump_uid));
|
||||||
let file = File::create(path)?;
|
let file = File::create(path)?;
|
||||||
dump.persist_to(BufWriter::new(file))?;
|
dump.persist_to(BufWriter::new(file))?;
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ pub enum Error {
|
|||||||
TaskDeletionWithEmptyQuery,
|
TaskDeletionWithEmptyQuery,
|
||||||
#[error("Query parameters to filter the tasks to cancel are missing. Available query parameters are: `uids`, `indexUids`, `statuses`, `types`, `canceledBy`, `beforeEnqueuedAt`, `afterEnqueuedAt`, `beforeStartedAt`, `afterStartedAt`, `beforeFinishedAt`, `afterFinishedAt`.")]
|
#[error("Query parameters to filter the tasks to cancel are missing. Available query parameters are: `uids`, `indexUids`, `statuses`, `types`, `canceledBy`, `beforeEnqueuedAt`, `afterEnqueuedAt`, `beforeStartedAt`, `afterStartedAt`, `beforeFinishedAt`, `afterFinishedAt`.")]
|
||||||
TaskCancelationWithEmptyQuery,
|
TaskCancelationWithEmptyQuery,
|
||||||
|
#[error("Aborted task")]
|
||||||
|
AbortedTask,
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Dump(#[from] dump::Error),
|
Dump(#[from] dump::Error),
|
||||||
@@ -115,8 +117,13 @@ pub enum Error {
|
|||||||
Heed(#[from] heed::Error),
|
Heed(#[from] heed::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Milli(#[from] milli::Error),
|
Milli(#[from] milli::Error),
|
||||||
#[error("An unexpected crash occurred when processing the task.")]
|
#[error("An unexpected crash occurred when processing the task. {}", {
|
||||||
ProcessBatchPanicked,
|
match .0 {
|
||||||
|
Some(report) => format!("Get /reports/{}", report),
|
||||||
|
None => "No report was saved.".into(),
|
||||||
|
}
|
||||||
|
})]
|
||||||
|
ProcessBatchPanicked(Option<uuid::Uuid>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FileStore(#[from] file_store::Error),
|
FileStore(#[from] file_store::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@@ -175,10 +182,11 @@ impl Error {
|
|||||||
| Error::TaskNotFound(_)
|
| Error::TaskNotFound(_)
|
||||||
| Error::TaskDeletionWithEmptyQuery
|
| Error::TaskDeletionWithEmptyQuery
|
||||||
| Error::TaskCancelationWithEmptyQuery
|
| Error::TaskCancelationWithEmptyQuery
|
||||||
|
| Error::AbortedTask
|
||||||
| Error::Dump(_)
|
| Error::Dump(_)
|
||||||
| Error::Heed(_)
|
| Error::Heed(_)
|
||||||
| Error::Milli(_)
|
| Error::Milli(_)
|
||||||
| Error::ProcessBatchPanicked
|
| Error::ProcessBatchPanicked(_)
|
||||||
| Error::FileStore(_)
|
| Error::FileStore(_)
|
||||||
| Error::IoError(_)
|
| Error::IoError(_)
|
||||||
| Error::Persist(_)
|
| Error::Persist(_)
|
||||||
@@ -221,7 +229,7 @@ impl ErrorCode for Error {
|
|||||||
Error::NoSpaceLeftInTaskQueue => Code::NoSpaceLeftOnDevice,
|
Error::NoSpaceLeftInTaskQueue => Code::NoSpaceLeftOnDevice,
|
||||||
Error::Dump(e) => e.error_code(),
|
Error::Dump(e) => e.error_code(),
|
||||||
Error::Milli(e) => e.error_code(),
|
Error::Milli(e) => e.error_code(),
|
||||||
Error::ProcessBatchPanicked => Code::Internal,
|
Error::ProcessBatchPanicked(_) => 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(),
|
||||||
@@ -236,6 +244,9 @@ impl ErrorCode for Error {
|
|||||||
Error::TaskDatabaseUpdate(_) => Code::Internal,
|
Error::TaskDatabaseUpdate(_) => Code::Internal,
|
||||||
Error::CreateBatch(_) => Code::Internal,
|
Error::CreateBatch(_) => Code::Internal,
|
||||||
|
|
||||||
|
// This one should never be seen by the end user
|
||||||
|
Error::AbortedTask => Code::Internal,
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
Error::PlannedFailure => Code::Internal,
|
Error::PlannedFailure => Code::Internal,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ pub fn snapshot_index_scheduler(scheduler: &IndexScheduler) -> String {
|
|||||||
test_breakpoint_sdr: _,
|
test_breakpoint_sdr: _,
|
||||||
planned_failures: _,
|
planned_failures: _,
|
||||||
run_loop_iteration: _,
|
run_loop_iteration: _,
|
||||||
|
panic_reader: _,
|
||||||
} = scheduler;
|
} = scheduler;
|
||||||
|
|
||||||
let rtxn = env.read_txn().unwrap();
|
let rtxn = env.read_txn().unwrap();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ mod index_mapper;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod insta_snapshot;
|
mod insta_snapshot;
|
||||||
mod lru;
|
mod lru;
|
||||||
|
mod panic_hook;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod uuid_codec;
|
mod uuid_codec;
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ use meilisearch_types::milli::documents::DocumentsBatchBuilder;
|
|||||||
use meilisearch_types::milli::update::IndexerConfig;
|
use meilisearch_types::milli::update::IndexerConfig;
|
||||||
use meilisearch_types::milli::{self, CboRoaringBitmapCodec, Index, RoaringBitmapCodec, BEU32};
|
use meilisearch_types::milli::{self, CboRoaringBitmapCodec, Index, RoaringBitmapCodec, BEU32};
|
||||||
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task};
|
use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task};
|
||||||
|
use panic_hook::ReportReader;
|
||||||
|
pub use panic_hook::{Panic, Report, ReportRegistry};
|
||||||
use puffin::FrameView;
|
use puffin::FrameView;
|
||||||
use roaring::RoaringBitmap;
|
use roaring::RoaringBitmap;
|
||||||
use synchronoise::SignalEvent;
|
use synchronoise::SignalEvent;
|
||||||
@@ -331,6 +334,8 @@ pub struct IndexScheduler {
|
|||||||
/// The path to the version file of Meilisearch.
|
/// The path to the version file of Meilisearch.
|
||||||
pub(crate) version_file_path: PathBuf,
|
pub(crate) version_file_path: PathBuf,
|
||||||
|
|
||||||
|
pub(crate) panic_reader: ReportReader,
|
||||||
|
|
||||||
// ================= test
|
// ================= test
|
||||||
// The next entry is dedicated to the tests.
|
// The next entry is dedicated to the tests.
|
||||||
/// Provide a way to set a breakpoint in multiple part of the scheduler.
|
/// Provide a way to set a breakpoint in multiple part of the scheduler.
|
||||||
@@ -381,6 +386,7 @@ 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(),
|
||||||
|
panic_reader: self.panic_reader.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,6 +444,12 @@ impl IndexScheduler {
|
|||||||
let finished_at = env.create_database(&mut wtxn, Some(db_name::FINISHED_AT))?;
|
let finished_at = env.create_database(&mut wtxn, Some(db_name::FINISHED_AT))?;
|
||||||
wtxn.commit()?;
|
wtxn.commit()?;
|
||||||
|
|
||||||
|
const MAX_REPORT_COUNT: usize = 20;
|
||||||
|
|
||||||
|
let panic_reader = panic_hook::ReportReader::install_panic_hook(
|
||||||
|
std::num::NonZeroUsize::new(MAX_REPORT_COUNT).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
// 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.
|
||||||
let this = Self {
|
let this = Self {
|
||||||
must_stop_processing: MustStopProcessing::default(),
|
must_stop_processing: MustStopProcessing::default(),
|
||||||
@@ -478,6 +490,7 @@ impl IndexScheduler {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
run_loop_iteration: Arc::new(RwLock::new(0)),
|
run_loop_iteration: Arc::new(RwLock::new(0)),
|
||||||
features,
|
features,
|
||||||
|
panic_reader,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.run();
|
this.run();
|
||||||
@@ -1130,7 +1143,10 @@ impl IndexScheduler {
|
|||||||
.name(String::from("batch-operation"))
|
.name(String::from("batch-operation"))
|
||||||
.spawn(move || cloned_index_scheduler.process_batch(batch))
|
.spawn(move || cloned_index_scheduler.process_batch(batch))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
handle.join().unwrap_or(Err(Error::ProcessBatchPanicked))
|
|
||||||
|
self.panic_reader
|
||||||
|
.join_thread(handle)
|
||||||
|
.unwrap_or_else(|maybe_report| Err(Error::ProcessBatchPanicked(maybe_report)))
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -1167,7 +1183,8 @@ impl IndexScheduler {
|
|||||||
// If we have an abortion error we must stop the tick here and re-schedule tasks.
|
// If we have an abortion error we must stop the tick here and re-schedule tasks.
|
||||||
Err(Error::Milli(milli::Error::InternalError(
|
Err(Error::Milli(milli::Error::InternalError(
|
||||||
milli::InternalError::AbortedIndexation,
|
milli::InternalError::AbortedIndexation,
|
||||||
))) => {
|
)))
|
||||||
|
| Err(Error::AbortedTask) => {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
self.breakpoint(Breakpoint::AbortedIndexation);
|
self.breakpoint(Breakpoint::AbortedIndexation);
|
||||||
wtxn.abort().map_err(Error::HeedTransaction)?;
|
wtxn.abort().map_err(Error::HeedTransaction)?;
|
||||||
@@ -1310,6 +1327,10 @@ impl IndexScheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reports(&self) -> Arc<RwLock<ReportRegistry>> {
|
||||||
|
self.panic_reader.registry()
|
||||||
|
}
|
||||||
|
|
||||||
/// Blocks the thread until the test handle asks to progress to/through this breakpoint.
|
/// Blocks the thread until the test handle asks to progress to/through this breakpoint.
|
||||||
///
|
///
|
||||||
/// Two messages are sent through the channel for each breakpoint.
|
/// Two messages are sent through the channel for each breakpoint.
|
||||||
@@ -4323,4 +4344,26 @@ mod tests {
|
|||||||
}
|
}
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cancel_processing_dump() {
|
||||||
|
let (index_scheduler, mut handle) = IndexScheduler::test(true, vec![]);
|
||||||
|
|
||||||
|
let dump_creation = KindWithContent::DumpCreation { keys: Vec::new(), instance_uid: None };
|
||||||
|
let dump_cancellation = KindWithContent::TaskCancelation {
|
||||||
|
query: "cancel dump".to_owned(),
|
||||||
|
tasks: RoaringBitmap::from_iter([0]),
|
||||||
|
};
|
||||||
|
let _ = index_scheduler.register(dump_creation).unwrap();
|
||||||
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "after_dump_register");
|
||||||
|
handle.advance_till([Start, BatchCreated, InsideProcessBatch]);
|
||||||
|
|
||||||
|
let _ = index_scheduler.register(dump_cancellation).unwrap();
|
||||||
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "cancel_registered");
|
||||||
|
|
||||||
|
snapshot!(format!("{:?}", handle.advance()), @"AbortedIndexation");
|
||||||
|
|
||||||
|
handle.advance_one_successful_batch();
|
||||||
|
snapshot!(snapshot_index_scheduler(&index_scheduler), name: "cancel_processed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
211
index-scheduler/src/panic_hook.rs
Normal file
211
index-scheduler/src/panic_hook.rs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
//! Panic hook designed to fetch a panic from a subthread and recover it on join.
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
use std::panic::PanicInfo;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread::{JoinHandle, ThreadId};
|
||||||
|
|
||||||
|
use backtrace::Backtrace;
|
||||||
|
|
||||||
|
// Represents a panic in a shallowy structured fashion
|
||||||
|
pub struct Panic {
|
||||||
|
pub payload: Option<String>,
|
||||||
|
pub location: Option<String>,
|
||||||
|
pub thread_name: Option<String>,
|
||||||
|
pub thread_id: ThreadId,
|
||||||
|
pub backtrace: Backtrace,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A panic enriched with a unique id
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct Report {
|
||||||
|
pub id: uuid::Uuid,
|
||||||
|
#[serde(serialize_with = "serialize_panic")]
|
||||||
|
pub panic: Panic,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_panic<S>(panic: &Panic, s: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
panic.to_json().serialize(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
pub fn new(panic: Panic) -> Self {
|
||||||
|
Self { id: uuid::Uuid::new_v4(), panic }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Panic {
|
||||||
|
pub fn to_json(&self) -> serde_json::Value {
|
||||||
|
json::panic_to_json(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod json {
|
||||||
|
use backtrace::{Backtrace, BacktraceFrame, BacktraceSymbol};
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
use super::Panic;
|
||||||
|
|
||||||
|
fn symbol_to_json(symbol: &BacktraceSymbol) -> Value {
|
||||||
|
let address = symbol.addr().map(|addr| format!("{:p}", addr));
|
||||||
|
let column = symbol.colno();
|
||||||
|
let line = symbol.lineno();
|
||||||
|
let function = symbol.name().map(|name| name.to_string());
|
||||||
|
let filename = symbol.filename();
|
||||||
|
json!({
|
||||||
|
"function": function,
|
||||||
|
"filename": filename,
|
||||||
|
"line": line,
|
||||||
|
"column": column,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame_to_json(frame: &BacktraceFrame) -> Value {
|
||||||
|
let symbols: Vec<_> = frame.symbols().iter().map(symbol_to_json).collect();
|
||||||
|
match symbols.as_slice() {
|
||||||
|
[] => {
|
||||||
|
let address = format!("{:p}", frame.ip());
|
||||||
|
json!({"address": address})
|
||||||
|
}
|
||||||
|
[symbol] => json!(symbol),
|
||||||
|
symbols => json!(symbols),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backtrace_to_json(backtrace: &Backtrace) -> Value {
|
||||||
|
let frames: Vec<_> = backtrace.frames().iter().map(frame_to_json).collect();
|
||||||
|
json!(frames)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn panic_to_json(panic: &Panic) -> Value {
|
||||||
|
let thread_id = format!("{:?}", panic.thread_id);
|
||||||
|
serde_json::json!({
|
||||||
|
"payload": panic.payload,
|
||||||
|
"location": panic.location,
|
||||||
|
"thread": {
|
||||||
|
"id": thread_id,
|
||||||
|
"name": panic.thread_name,
|
||||||
|
},
|
||||||
|
"backtrace": backtrace_to_json(&panic.backtrace),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReportWriter(Arc<RwLock<ReportRegistry>>);
|
||||||
|
|
||||||
|
/// A FIFO queue of reports.
|
||||||
|
pub struct ReportRegistry {
|
||||||
|
reports: std::collections::VecDeque<Report>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReportRegistry {
|
||||||
|
pub fn new(capacity: NonZeroUsize) -> Self {
|
||||||
|
Self { reports: VecDeque::with_capacity(capacity.get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, report: Report) -> Option<Report> {
|
||||||
|
let popped = if self.reports.len() == self.reports.capacity() {
|
||||||
|
self.reports.pop_back()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.reports.push_front(report);
|
||||||
|
popped
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &Report> {
|
||||||
|
self.reports.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find(&self, report_id: uuid::Uuid) -> Option<&Report> {
|
||||||
|
self.iter().find(|report| report.id == report_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReportWriter {
|
||||||
|
#[track_caller]
|
||||||
|
fn write_panic(&self, panic_info: &PanicInfo<'_>) {
|
||||||
|
let payload = panic_info
|
||||||
|
.payload()
|
||||||
|
.downcast_ref::<&str>()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.or_else(|| panic_info.payload().downcast_ref::<String>().cloned());
|
||||||
|
let location = panic_info.location().map(|loc| {
|
||||||
|
format!(
|
||||||
|
"{file}:{line}:{column}",
|
||||||
|
file = loc.file(),
|
||||||
|
line = loc.line(),
|
||||||
|
column = loc.column()
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let thread_name = std::thread::current().name().map(ToString::to_string);
|
||||||
|
let thread_id = std::thread::current().id();
|
||||||
|
let backtrace = backtrace::Backtrace::new();
|
||||||
|
|
||||||
|
let panic = Panic { payload, location, thread_name, thread_id, backtrace };
|
||||||
|
|
||||||
|
let report = Report::new(panic);
|
||||||
|
|
||||||
|
log::error!(
|
||||||
|
"An unexpected panic occurred on thread {name} at {location}: {payload}. See report '{report}' for details.",
|
||||||
|
payload = report.panic.payload.as_deref().unwrap_or("Box<dyn Any>"),
|
||||||
|
name = report.panic.thread_name.as_deref().unwrap_or("<unnamed>"),
|
||||||
|
location = report.panic.location.as_deref().unwrap_or("<unknown>"),
|
||||||
|
report = report.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(mut registry) = self.0.write() {
|
||||||
|
if let Some(old_report) = registry.push(report) {
|
||||||
|
log::trace!("Forgetting report {} to make space for new report.", old_report.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the reports written in case of a panic.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReportReader(Arc<RwLock<ReportRegistry>>);
|
||||||
|
|
||||||
|
impl ReportReader {
|
||||||
|
/// Installs a new global panic hook, overriding any existing hook.
|
||||||
|
///
|
||||||
|
/// The hook writes any incoming panic in reports.
|
||||||
|
/// The reports can then be read by the returned [`ReportReader`].
|
||||||
|
pub fn install_panic_hook(capacity: NonZeroUsize) -> Self {
|
||||||
|
let registry = Arc::new(RwLock::new(ReportRegistry::new(capacity)));
|
||||||
|
let reader = ReportReader(registry.clone());
|
||||||
|
let writer = ReportWriter(registry.clone());
|
||||||
|
|
||||||
|
std::panic::set_hook(Box::new(move |panic_info| writer.write_panic(panic_info)));
|
||||||
|
reader
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Join the thread corresponding to the passed handle, recovering either its value
|
||||||
|
/// or, in case the thread panicked, the id of the report corresponding to the panic.
|
||||||
|
///
|
||||||
|
/// The id can be used to read the report from the [`self.registry()`].
|
||||||
|
pub fn join_thread<T>(&self, thread: JoinHandle<T>) -> Result<T, Option<uuid::Uuid>> {
|
||||||
|
let thread_id = thread.thread().id();
|
||||||
|
thread.join().map_err(|_e| {
|
||||||
|
self.0
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|report| report.panic.thread_id == thread_id)
|
||||||
|
.map(|report| report.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a registry that can be used to read the reports written during a panic.
|
||||||
|
pub fn registry(&self) -> Arc<RwLock<ReportRegistry>> {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
source: index-scheduler/src/lib.rs
|
||||||
|
---
|
||||||
|
### Autobatching Enabled = true
|
||||||
|
### Processing Tasks:
|
||||||
|
[]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### All Tasks:
|
||||||
|
0 {uid: 0, status: enqueued, details: { dump_uid: None }, kind: DumpCreation { keys: [], instance_uid: None }}
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Status:
|
||||||
|
enqueued [0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Kind:
|
||||||
|
"dumpCreation" [0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Index Tasks:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Index Mapper:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Canceled By:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Enqueued At:
|
||||||
|
[timestamp] [0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Started At:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Finished At:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### File Store:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
source: index-scheduler/src/lib.rs
|
||||||
|
---
|
||||||
|
### Autobatching Enabled = true
|
||||||
|
### Processing Tasks:
|
||||||
|
[]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### All Tasks:
|
||||||
|
0 {uid: 0, status: canceled, canceled_by: 1, details: { dump_uid: None }, kind: DumpCreation { keys: [], instance_uid: None }}
|
||||||
|
1 {uid: 1, status: succeeded, details: { matched_tasks: 1, canceled_tasks: Some(0), original_filter: "cancel dump" }, kind: TaskCancelation { query: "cancel dump", tasks: RoaringBitmap<[0]> }}
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Status:
|
||||||
|
enqueued []
|
||||||
|
succeeded [1,]
|
||||||
|
canceled [0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Kind:
|
||||||
|
"taskCancelation" [1,]
|
||||||
|
"dumpCreation" [0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Index Tasks:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Index Mapper:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Canceled By:
|
||||||
|
1 [0,]
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Enqueued At:
|
||||||
|
[timestamp] [0,]
|
||||||
|
[timestamp] [1,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Started At:
|
||||||
|
[timestamp] [0,]
|
||||||
|
[timestamp] [1,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Finished At:
|
||||||
|
[timestamp] [0,]
|
||||||
|
[timestamp] [1,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### File Store:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
source: index-scheduler/src/lib.rs
|
||||||
|
---
|
||||||
|
### Autobatching Enabled = true
|
||||||
|
### Processing Tasks:
|
||||||
|
[0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### All Tasks:
|
||||||
|
0 {uid: 0, status: enqueued, details: { dump_uid: None }, kind: DumpCreation { keys: [], instance_uid: None }}
|
||||||
|
1 {uid: 1, status: enqueued, details: { matched_tasks: 1, canceled_tasks: None, original_filter: "cancel dump" }, kind: TaskCancelation { query: "cancel dump", tasks: RoaringBitmap<[0]> }}
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Status:
|
||||||
|
enqueued [0,1,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Kind:
|
||||||
|
"taskCancelation" [1,]
|
||||||
|
"dumpCreation" [0,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Index Tasks:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Index Mapper:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Canceled By:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Enqueued At:
|
||||||
|
[timestamp] [0,]
|
||||||
|
[timestamp] [1,]
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Started At:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### Finished At:
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
### File Store:
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -88,7 +88,6 @@ pub trait ErrorCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
enum ErrorType {
|
enum ErrorType {
|
||||||
Internal,
|
Internal,
|
||||||
InvalidRequest,
|
InvalidRequest,
|
||||||
@@ -235,7 +234,6 @@ InvalidSearchCropMarker , InvalidRequest , BAD_REQUEST ;
|
|||||||
InvalidSearchFacets , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchFacets , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidFacetSearchFacetName , InvalidRequest , BAD_REQUEST ;
|
InvalidFacetSearchFacetName , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchFilter , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchFilter , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchBoostingFilter , InvalidRequest , BAD_REQUEST ;
|
|
||||||
InvalidSearchHighlightPostTag , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchHighlightPostTag , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchHighlightPreTag , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchHighlightPreTag , InvalidRequest , BAD_REQUEST ;
|
||||||
InvalidSearchHitsPerPage , InvalidRequest , BAD_REQUEST ;
|
InvalidSearchHitsPerPage , InvalidRequest , BAD_REQUEST ;
|
||||||
@@ -299,6 +297,7 @@ MissingSwapIndexes , InvalidRequest , BAD_REQUEST ;
|
|||||||
MissingTaskFilters , InvalidRequest , BAD_REQUEST ;
|
MissingTaskFilters , InvalidRequest , BAD_REQUEST ;
|
||||||
NoSpaceLeftOnDevice , System , UNPROCESSABLE_ENTITY;
|
NoSpaceLeftOnDevice , System , UNPROCESSABLE_ENTITY;
|
||||||
PayloadTooLarge , InvalidRequest , PAYLOAD_TOO_LARGE ;
|
PayloadTooLarge , InvalidRequest , PAYLOAD_TOO_LARGE ;
|
||||||
|
ReportNotFound , InvalidRequest , NOT_FOUND ;
|
||||||
TaskNotFound , InvalidRequest , NOT_FOUND ;
|
TaskNotFound , InvalidRequest , NOT_FOUND ;
|
||||||
TooManyOpenFiles , System , UNPROCESSABLE_ENTITY ;
|
TooManyOpenFiles , System , UNPROCESSABLE_ENTITY ;
|
||||||
UnretrievableDocument , Internal , BAD_REQUEST ;
|
UnretrievableDocument , Internal , BAD_REQUEST ;
|
||||||
@@ -332,7 +331,6 @@ impl ErrorCode for milli::Error {
|
|||||||
UserError::MaxDatabaseSizeReached => Code::DatabaseSizeLimitReached,
|
UserError::MaxDatabaseSizeReached => Code::DatabaseSizeLimitReached,
|
||||||
UserError::AttributeLimitReached => Code::MaxFieldsLimitExceeded,
|
UserError::AttributeLimitReached => Code::MaxFieldsLimitExceeded,
|
||||||
UserError::InvalidFilter(_) => Code::InvalidSearchFilter,
|
UserError::InvalidFilter(_) => Code::InvalidSearchFilter,
|
||||||
UserError::InvalidBoostingFilter(_) => Code::InvalidSearchBoostingFilter,
|
|
||||||
UserError::InvalidFilterExpression(..) => Code::InvalidSearchFilter,
|
UserError::InvalidFilterExpression(..) => Code::InvalidSearchFilter,
|
||||||
UserError::MissingDocumentId { .. } => Code::MissingDocumentId,
|
UserError::MissingDocumentId { .. } => Code::MissingDocumentId,
|
||||||
UserError::InvalidDocumentId { .. } | UserError::TooManyDocumentIds { .. } => {
|
UserError::InvalidDocumentId { .. } | UserError::TooManyDocumentIds { .. } => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use std::str::FromStr;
|
|||||||
use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef};
|
use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef};
|
||||||
use fst::IntoStreamer;
|
use fst::IntoStreamer;
|
||||||
use milli::update::Setting;
|
use milli::update::Setting;
|
||||||
use milli::{Index, RankingRule, RankingRuleError, DEFAULT_VALUES_PER_FACET};
|
use milli::{Criterion, CriterionError, Index, DEFAULT_VALUES_PER_FACET};
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::deserr::DeserrJsonError;
|
use crate::deserr::DeserrJsonError;
|
||||||
@@ -117,10 +117,10 @@ pub struct PaginationSettings {
|
|||||||
pub max_total_hits: Setting<usize>,
|
pub max_total_hits: Setting<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MergeWithError<milli::RankingRuleError> for DeserrJsonError<InvalidSettingsRankingRules> {
|
impl MergeWithError<milli::CriterionError> for DeserrJsonError<InvalidSettingsRankingRules> {
|
||||||
fn merge(
|
fn merge(
|
||||||
_self_: Option<Self>,
|
_self_: Option<Self>,
|
||||||
other: milli::RankingRuleError,
|
other: milli::CriterionError,
|
||||||
merge_location: ValuePointerRef,
|
merge_location: ValuePointerRef,
|
||||||
) -> ControlFlow<Self, Self> {
|
) -> ControlFlow<Self, Self> {
|
||||||
Self::error::<Infallible>(
|
Self::error::<Infallible>(
|
||||||
@@ -344,9 +344,9 @@ pub fn apply_settings_to_builder(
|
|||||||
|
|
||||||
match settings.ranking_rules {
|
match settings.ranking_rules {
|
||||||
Setting::Set(ref criteria) => {
|
Setting::Set(ref criteria) => {
|
||||||
builder.set_ranking_rules(criteria.iter().map(|c| c.clone().into()).collect())
|
builder.set_criteria(criteria.iter().map(|c| c.clone().into()).collect())
|
||||||
}
|
}
|
||||||
Setting::Reset => builder.reset_ranking_rules(),
|
Setting::Reset => builder.reset_criteria(),
|
||||||
Setting::NotSet => (),
|
Setting::NotSet => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,13 +578,11 @@ pub fn settings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserr)]
|
#[derive(Debug, Clone, PartialEq, Eq, Deserr)]
|
||||||
#[deserr(try_from(&String) = FromStr::from_str -> RankingRuleError)]
|
#[deserr(try_from(&String) = FromStr::from_str -> CriterionError)]
|
||||||
pub enum RankingRuleView {
|
pub enum RankingRuleView {
|
||||||
/// Sorted by decreasing number of matched query terms.
|
/// Sorted by decreasing number of matched query terms.
|
||||||
/// Query words at the front of an attribute is considered better than if it was at the back.
|
/// Query words at the front of an attribute is considered better than if it was at the back.
|
||||||
Words,
|
Words,
|
||||||
/// Sorted by documents matching the given filter and then documents not matching it.
|
|
||||||
Boost(String),
|
|
||||||
/// Sorted by increasing number of typos.
|
/// Sorted by increasing number of typos.
|
||||||
Typo,
|
Typo,
|
||||||
/// Sorted by increasing distance between matched query terms.
|
/// Sorted by increasing distance between matched query terms.
|
||||||
@@ -607,7 +605,7 @@ impl Serialize for RankingRuleView {
|
|||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
serializer.serialize_str(&format!("{}", RankingRule::from(self.clone())))
|
serializer.serialize_str(&format!("{}", Criterion::from(self.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'de> Deserialize<'de> for RankingRuleView {
|
impl<'de> Deserialize<'de> for RankingRuleView {
|
||||||
@@ -625,7 +623,7 @@ impl<'de> Deserialize<'de> for RankingRuleView {
|
|||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
let criterion = RankingRule::from_str(v).map_err(|_| {
|
let criterion = Criterion::from_str(v).map_err(|_| {
|
||||||
E::invalid_value(serde::de::Unexpected::Str(v), &"a valid ranking rule")
|
E::invalid_value(serde::de::Unexpected::Str(v), &"a valid ranking rule")
|
||||||
})?;
|
})?;
|
||||||
Ok(RankingRuleView::from(criterion))
|
Ok(RankingRuleView::from(criterion))
|
||||||
@@ -635,44 +633,42 @@ impl<'de> Deserialize<'de> for RankingRuleView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl FromStr for RankingRuleView {
|
impl FromStr for RankingRuleView {
|
||||||
type Err = <RankingRule as FromStr>::Err;
|
type Err = <Criterion as FromStr>::Err;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(RankingRuleView::from(RankingRule::from_str(s)?))
|
Ok(RankingRuleView::from(Criterion::from_str(s)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for RankingRuleView {
|
impl fmt::Display for RankingRuleView {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
fmt::Display::fmt(&RankingRule::from(self.clone()), f)
|
fmt::Display::fmt(&Criterion::from(self.clone()), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<RankingRule> for RankingRuleView {
|
impl From<Criterion> for RankingRuleView {
|
||||||
fn from(value: RankingRule) -> Self {
|
fn from(value: Criterion) -> Self {
|
||||||
match value {
|
match value {
|
||||||
RankingRule::Words => RankingRuleView::Words,
|
Criterion::Words => RankingRuleView::Words,
|
||||||
RankingRule::FilterBoosting(filter) => RankingRuleView::Boost(filter),
|
Criterion::Typo => RankingRuleView::Typo,
|
||||||
RankingRule::Typo => RankingRuleView::Typo,
|
Criterion::Proximity => RankingRuleView::Proximity,
|
||||||
RankingRule::Proximity => RankingRuleView::Proximity,
|
Criterion::Attribute => RankingRuleView::Attribute,
|
||||||
RankingRule::Attribute => RankingRuleView::Attribute,
|
Criterion::Sort => RankingRuleView::Sort,
|
||||||
RankingRule::Sort => RankingRuleView::Sort,
|
Criterion::Exactness => RankingRuleView::Exactness,
|
||||||
RankingRule::Exactness => RankingRuleView::Exactness,
|
Criterion::Asc(x) => RankingRuleView::Asc(x),
|
||||||
RankingRule::Asc(x) => RankingRuleView::Asc(x),
|
Criterion::Desc(x) => RankingRuleView::Desc(x),
|
||||||
RankingRule::Desc(x) => RankingRuleView::Desc(x),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<RankingRuleView> for RankingRule {
|
impl From<RankingRuleView> for Criterion {
|
||||||
fn from(value: RankingRuleView) -> Self {
|
fn from(value: RankingRuleView) -> Self {
|
||||||
match value {
|
match value {
|
||||||
RankingRuleView::Words => RankingRule::Words,
|
RankingRuleView::Words => Criterion::Words,
|
||||||
RankingRuleView::Boost(filter) => RankingRule::FilterBoosting(filter),
|
RankingRuleView::Typo => Criterion::Typo,
|
||||||
RankingRuleView::Typo => RankingRule::Typo,
|
RankingRuleView::Proximity => Criterion::Proximity,
|
||||||
RankingRuleView::Proximity => RankingRule::Proximity,
|
RankingRuleView::Attribute => Criterion::Attribute,
|
||||||
RankingRuleView::Attribute => RankingRule::Attribute,
|
RankingRuleView::Sort => Criterion::Sort,
|
||||||
RankingRuleView::Sort => RankingRule::Sort,
|
RankingRuleView::Exactness => Criterion::Exactness,
|
||||||
RankingRuleView::Exactness => RankingRule::Exactness,
|
RankingRuleView::Asc(x) => Criterion::Asc(x),
|
||||||
RankingRuleView::Asc(x) => RankingRule::Asc(x),
|
RankingRuleView::Desc(x) => Criterion::Desc(x),
|
||||||
RankingRuleView::Desc(x) => RankingRule::Desc(x),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -629,7 +629,6 @@ impl SearchAggregator {
|
|||||||
show_ranking_score,
|
show_ranking_score,
|
||||||
show_ranking_score_details,
|
show_ranking_score_details,
|
||||||
filter,
|
filter,
|
||||||
boosting_filter,
|
|
||||||
sort,
|
sort,
|
||||||
facets: _,
|
facets: _,
|
||||||
highlight_pre_tag,
|
highlight_pre_tag,
|
||||||
@@ -1003,7 +1002,6 @@ impl MultiSearchAggregator {
|
|||||||
show_ranking_score_details: _,
|
show_ranking_score_details: _,
|
||||||
show_matches_position: _,
|
show_matches_position: _,
|
||||||
filter: _,
|
filter: _,
|
||||||
boosting_filter: _,
|
|
||||||
sort: _,
|
sort: _,
|
||||||
facets: _,
|
facets: _,
|
||||||
highlight_pre_tag: _,
|
highlight_pre_tag: _,
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ pub enum MeilisearchHttpError {
|
|||||||
DocumentFormat(#[from] DocumentFormatError),
|
DocumentFormat(#[from] DocumentFormatError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Join(#[from] JoinError),
|
Join(#[from] JoinError),
|
||||||
|
#[error("Report `{0}` not found. Either its id is incorrect, or it was deleted. To save on memory, only a limited amount of reports are kept.")]
|
||||||
|
ReportNotFound(uuid::Uuid),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorCode for MeilisearchHttpError {
|
impl ErrorCode for MeilisearchHttpError {
|
||||||
@@ -74,6 +76,7 @@ impl ErrorCode for MeilisearchHttpError {
|
|||||||
MeilisearchHttpError::FileStore(_) => Code::Internal,
|
MeilisearchHttpError::FileStore(_) => Code::Internal,
|
||||||
MeilisearchHttpError::DocumentFormat(e) => e.error_code(),
|
MeilisearchHttpError::DocumentFormat(e) => e.error_code(),
|
||||||
MeilisearchHttpError::Join(_) => Code::Internal,
|
MeilisearchHttpError::Join(_) => Code::Internal,
|
||||||
|
MeilisearchHttpError::ReportNotFound(_) => Code::ReportNotFound,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ impl From<FacetSearchQuery> for SearchQuery {
|
|||||||
show_ranking_score: false,
|
show_ranking_score: false,
|
||||||
show_ranking_score_details: false,
|
show_ranking_score_details: false,
|
||||||
filter,
|
filter,
|
||||||
boosting_filter: None,
|
|
||||||
sort: None,
|
sort: None,
|
||||||
facets: None,
|
facets: None,
|
||||||
highlight_pre_tag: DEFAULT_HIGHLIGHT_PRE_TAG(),
|
highlight_pre_tag: DEFAULT_HIGHLIGHT_PRE_TAG(),
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ pub struct SearchQueryGet {
|
|||||||
attributes_to_highlight: Option<CS<String>>,
|
attributes_to_highlight: Option<CS<String>>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFilter>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFilter>)]
|
||||||
filter: Option<String>,
|
filter: Option<String>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchBoostingFilter>)]
|
|
||||||
boosting_filter: Option<String>,
|
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchSort>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchSort>)]
|
||||||
sort: Option<String>,
|
sort: Option<String>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>)]
|
||||||
@@ -88,14 +86,6 @@ impl From<SearchQueryGet> for SearchQuery {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let boosting_filter = match other.boosting_filter {
|
|
||||||
Some(f) => match serde_json::from_str(&f) {
|
|
||||||
Ok(v) => Some(v),
|
|
||||||
_ => Some(Value::String(f)),
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
q: other.q,
|
q: other.q,
|
||||||
vector: other.vector.map(CS::into_inner),
|
vector: other.vector.map(CS::into_inner),
|
||||||
@@ -108,7 +98,6 @@ impl From<SearchQueryGet> for SearchQuery {
|
|||||||
crop_length: other.crop_length.0,
|
crop_length: other.crop_length.0,
|
||||||
attributes_to_highlight: other.attributes_to_highlight.map(|o| o.into_iter().collect()),
|
attributes_to_highlight: other.attributes_to_highlight.map(|o| o.into_iter().collect()),
|
||||||
filter,
|
filter,
|
||||||
boosting_filter,
|
|
||||||
sort: other.sort.map(|attr| fix_sort_query_parameters(&attr)),
|
sort: other.sort.map(|attr| fix_sort_query_parameters(&attr)),
|
||||||
show_matches_position: other.show_matches_position.0,
|
show_matches_position: other.show_matches_position.0,
|
||||||
show_ranking_score: other.show_ranking_score.0,
|
show_ranking_score: other.show_ranking_score.0,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub mod features;
|
|||||||
pub mod indexes;
|
pub mod indexes;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod multi_search;
|
mod multi_search;
|
||||||
|
mod reports;
|
||||||
mod snapshot;
|
mod snapshot;
|
||||||
mod swap_indexes;
|
mod swap_indexes;
|
||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
@@ -40,7 +41,8 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||||||
.service(web::scope("/multi-search").configure(multi_search::configure))
|
.service(web::scope("/multi-search").configure(multi_search::configure))
|
||||||
.service(web::scope("/swap-indexes").configure(swap_indexes::configure))
|
.service(web::scope("/swap-indexes").configure(swap_indexes::configure))
|
||||||
.service(web::scope("/metrics").configure(metrics::configure))
|
.service(web::scope("/metrics").configure(metrics::configure))
|
||||||
.service(web::scope("/experimental-features").configure(features::configure));
|
.service(web::scope("/experimental-features").configure(features::configure))
|
||||||
|
.service(web::scope("/reports").configure(reports::configure));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
|||||||
39
meilisearch/src/routes/reports.rs
Normal file
39
meilisearch/src/routes/reports.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use actix_web::web::{self, Data};
|
||||||
|
use actix_web::HttpResponse;
|
||||||
|
use index_scheduler::{IndexScheduler, Report};
|
||||||
|
use meilisearch_types::error::ResponseError;
|
||||||
|
use meilisearch_types::keys::actions;
|
||||||
|
|
||||||
|
use crate::extractors::authentication::policies::ActionPolicy;
|
||||||
|
use crate::extractors::authentication::GuardedData;
|
||||||
|
use crate::extractors::sequential_extractor::SeqHandler;
|
||||||
|
|
||||||
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||||
|
cfg.service(web::resource("").route(web::get().to(list_reports))).service(
|
||||||
|
web::scope("/{report_uid}")
|
||||||
|
.service(web::resource("").route(web::get().to(SeqHandler(get_report)))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_reports(
|
||||||
|
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_ALL }>, Data<IndexScheduler>>,
|
||||||
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
|
let reports = &index_scheduler.reports();
|
||||||
|
let reports = &reports.read().unwrap();
|
||||||
|
let reports: Vec<&Report> = reports.iter().collect();
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(reports))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_report(
|
||||||
|
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_ALL }>, Data<IndexScheduler>>,
|
||||||
|
report_id: web::Path<uuid::Uuid>,
|
||||||
|
) -> Result<HttpResponse, ResponseError> {
|
||||||
|
let reports = &index_scheduler.reports();
|
||||||
|
let reports = &reports.read().unwrap();
|
||||||
|
let report = reports
|
||||||
|
.find(*report_id)
|
||||||
|
.ok_or(crate::error::MeilisearchHttpError::ReportNotFound(*report_id))?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(report))
|
||||||
|
}
|
||||||
@@ -71,8 +71,6 @@ pub struct SearchQuery {
|
|||||||
pub show_ranking_score_details: bool,
|
pub show_ranking_score_details: bool,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
||||||
pub filter: Option<Value>,
|
pub filter: Option<Value>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchBoostingFilter>)]
|
|
||||||
pub boosting_filter: Option<Value>,
|
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
||||||
pub sort: Option<Vec<String>>,
|
pub sort: Option<Vec<String>>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
||||||
@@ -132,8 +130,6 @@ pub struct SearchQueryWithIndex {
|
|||||||
pub show_matches_position: bool,
|
pub show_matches_position: bool,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
||||||
pub filter: Option<Value>,
|
pub filter: Option<Value>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchBoostingFilter>)]
|
|
||||||
pub boosting_filter: Option<Value>,
|
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
||||||
pub sort: Option<Vec<String>>,
|
pub sort: Option<Vec<String>>,
|
||||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
||||||
@@ -168,7 +164,6 @@ impl SearchQueryWithIndex {
|
|||||||
show_ranking_score_details,
|
show_ranking_score_details,
|
||||||
show_matches_position,
|
show_matches_position,
|
||||||
filter,
|
filter,
|
||||||
boosting_filter,
|
|
||||||
sort,
|
sort,
|
||||||
facets,
|
facets,
|
||||||
highlight_pre_tag,
|
highlight_pre_tag,
|
||||||
@@ -194,7 +189,6 @@ impl SearchQueryWithIndex {
|
|||||||
show_ranking_score_details,
|
show_ranking_score_details,
|
||||||
show_matches_position,
|
show_matches_position,
|
||||||
filter,
|
filter,
|
||||||
boosting_filter,
|
|
||||||
sort,
|
sort,
|
||||||
facets,
|
facets,
|
||||||
highlight_pre_tag,
|
highlight_pre_tag,
|
||||||
@@ -403,14 +397,8 @@ fn prepare_search<'t>(
|
|||||||
search.limit(limit);
|
search.limit(limit);
|
||||||
|
|
||||||
if let Some(ref filter) = query.filter {
|
if let Some(ref filter) = query.filter {
|
||||||
if let Some(filter) = parse_filter(filter)? {
|
if let Some(facets) = parse_filter(filter)? {
|
||||||
search.filter(filter);
|
search.filter(facets);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref boosting_filter) = query.boosting_filter {
|
|
||||||
if let Some(boosting_filter) = parse_filter(boosting_filter)? {
|
|
||||||
search.boosting_filter(boosting_filter);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
false,
|
false,
|
||||||
&None,
|
&None,
|
||||||
&None,
|
&None,
|
||||||
&None,
|
|
||||||
GeoSortStrategy::default(),
|
GeoSortStrategy::default(),
|
||||||
0,
|
0,
|
||||||
20,
|
20,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use heed::EnvOpenOptions;
|
|||||||
// use maplit::hashset;
|
// use maplit::hashset;
|
||||||
use milli::{
|
use milli::{
|
||||||
update::{IndexerConfig, Settings},
|
update::{IndexerConfig, Settings},
|
||||||
Index, RankingRule,
|
Criterion, Index,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -19,13 +19,13 @@ fn main() {
|
|||||||
// builder.set_min_word_len_one_typo(5);
|
// builder.set_min_word_len_one_typo(5);
|
||||||
// builder.set_min_word_len_two_typos(7);
|
// builder.set_min_word_len_two_typos(7);
|
||||||
// builder.set_sortable_fields(hashset! { S("release_date") });
|
// builder.set_sortable_fields(hashset! { S("release_date") });
|
||||||
builder.set_ranking_rules(vec![
|
builder.set_criteria(vec![
|
||||||
RankingRule::Words,
|
Criterion::Words,
|
||||||
RankingRule::Typo,
|
Criterion::Typo,
|
||||||
RankingRule::Proximity,
|
Criterion::Proximity,
|
||||||
RankingRule::Attribute,
|
Criterion::Attribute,
|
||||||
RankingRule::Sort,
|
Criterion::Sort,
|
||||||
RankingRule::Exactness,
|
Criterion::Exactness,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(|_| (), || false).unwrap();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use thiserror::Error;
|
|||||||
|
|
||||||
use crate::error::is_reserved_keyword;
|
use crate::error::is_reserved_keyword;
|
||||||
use crate::search::facet::BadGeoError;
|
use crate::search::facet::BadGeoError;
|
||||||
use crate::{Error, RankingRuleError, UserError};
|
use crate::{CriterionError, Error, UserError};
|
||||||
|
|
||||||
/// This error type is never supposed to be shown to the end user.
|
/// This error type is never supposed to be shown to the end user.
|
||||||
/// You must always cast it to a sort error or a criterion error.
|
/// You must always cast it to a sort error or a criterion error.
|
||||||
@@ -28,23 +28,23 @@ impl From<BadGeoError> for AscDescError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AscDescError> for RankingRuleError {
|
impl From<AscDescError> for CriterionError {
|
||||||
fn from(error: AscDescError) -> Self {
|
fn from(error: AscDescError) -> Self {
|
||||||
match error {
|
match error {
|
||||||
AscDescError::GeoError(_) => {
|
AscDescError::GeoError(_) => {
|
||||||
RankingRuleError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
CriterionError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::InvalidSyntax { name } => RankingRuleError::InvalidName { name },
|
AscDescError::InvalidSyntax { name } => CriterionError::InvalidName { name },
|
||||||
AscDescError::ReservedKeyword { name } if name.starts_with("_geoPoint") => {
|
AscDescError::ReservedKeyword { name } if name.starts_with("_geoPoint") => {
|
||||||
RankingRuleError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
CriterionError::ReservedNameForSort { name: "_geoPoint".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::ReservedKeyword { name } if name.starts_with("_geoRadius") => {
|
AscDescError::ReservedKeyword { name } if name.starts_with("_geoRadius") => {
|
||||||
RankingRuleError::ReservedNameForFilter { name: "_geoRadius".to_string() }
|
CriterionError::ReservedNameForFilter { name: "_geoRadius".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::ReservedKeyword { name } if name.starts_with("_geoBoundingBox") => {
|
AscDescError::ReservedKeyword { name } if name.starts_with("_geoBoundingBox") => {
|
||||||
RankingRuleError::ReservedNameForFilter { name: "_geoBoundingBox".to_string() }
|
CriterionError::ReservedNameForFilter { name: "_geoBoundingBox".to_string() }
|
||||||
}
|
}
|
||||||
AscDescError::ReservedKeyword { name } => RankingRuleError::ReservedName { name },
|
AscDescError::ReservedKeyword { name } => CriterionError::ReservedName { name },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use thiserror::Error;
|
|||||||
use crate::{AscDesc, Member};
|
use crate::{AscDesc, Member};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum RankingRuleError {
|
pub enum CriterionError {
|
||||||
#[error("`{name}` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.")]
|
#[error("`{name}` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.")]
|
||||||
InvalidName { name: String },
|
InvalidName { name: String },
|
||||||
#[error("`{name}` is a reserved keyword and thus can't be used as a ranking rule")]
|
#[error("`{name}` is a reserved keyword and thus can't be used as a ranking rule")]
|
||||||
@@ -25,9 +25,7 @@ pub enum RankingRuleError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub enum RankingRule {
|
pub enum Criterion {
|
||||||
/// Sorted by documents matching the given filter and then documents not matching it.
|
|
||||||
FilterBoosting(String),
|
|
||||||
/// Sorted by decreasing number of matched query terms.
|
/// Sorted by decreasing number of matched query terms.
|
||||||
/// Query words at the front of an attribute is considered better than if it was at the back.
|
/// Query words at the front of an attribute is considered better than if it was at the back.
|
||||||
Words,
|
Words,
|
||||||
@@ -49,68 +47,62 @@ pub enum RankingRule {
|
|||||||
Desc(String),
|
Desc(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RankingRule {
|
impl Criterion {
|
||||||
/// Returns the field name parameter of this criterion.
|
/// Returns the field name parameter of this criterion.
|
||||||
pub fn field_name(&self) -> Option<&str> {
|
pub fn field_name(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
RankingRule::Asc(name) | RankingRule::Desc(name) => Some(name),
|
Criterion::Asc(name) | Criterion::Desc(name) => Some(name),
|
||||||
_otherwise => None,
|
_otherwise => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for RankingRule {
|
impl FromStr for Criterion {
|
||||||
type Err = RankingRuleError;
|
type Err = CriterionError;
|
||||||
|
|
||||||
fn from_str(text: &str) -> Result<RankingRule, Self::Err> {
|
fn from_str(text: &str) -> Result<Criterion, Self::Err> {
|
||||||
match text {
|
match text {
|
||||||
"words" => Ok(RankingRule::Words),
|
"words" => Ok(Criterion::Words),
|
||||||
"typo" => Ok(RankingRule::Typo),
|
"typo" => Ok(Criterion::Typo),
|
||||||
"proximity" => Ok(RankingRule::Proximity),
|
"proximity" => Ok(Criterion::Proximity),
|
||||||
"attribute" => Ok(RankingRule::Attribute),
|
"attribute" => Ok(Criterion::Attribute),
|
||||||
"sort" => Ok(RankingRule::Sort),
|
"sort" => Ok(Criterion::Sort),
|
||||||
"exactness" => Ok(RankingRule::Exactness),
|
"exactness" => Ok(Criterion::Exactness),
|
||||||
text => match AscDesc::from_str(text) {
|
text => match AscDesc::from_str(text)? {
|
||||||
Ok(asc_desc) => match asc_desc {
|
AscDesc::Asc(Member::Field(field)) => Ok(Criterion::Asc(field)),
|
||||||
AscDesc::Asc(Member::Field(field)) => Ok(RankingRule::Asc(field)),
|
AscDesc::Desc(Member::Field(field)) => Ok(Criterion::Desc(field)),
|
||||||
AscDesc::Desc(Member::Field(field)) => Ok(RankingRule::Desc(field)),
|
AscDesc::Asc(Member::Geo(_)) | AscDesc::Desc(Member::Geo(_)) => {
|
||||||
AscDesc::Asc(Member::Geo(_)) | AscDesc::Desc(Member::Geo(_)) => {
|
Err(CriterionError::ReservedNameForSort { name: "_geoPoint".to_string() })?
|
||||||
Err(RankingRuleError::ReservedNameForSort {
|
}
|
||||||
name: "_geoPoint".to_string(),
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_criteria() -> Vec<RankingRule> {
|
pub fn default_criteria() -> Vec<Criterion> {
|
||||||
vec![
|
vec![
|
||||||
RankingRule::Words,
|
Criterion::Words,
|
||||||
RankingRule::Typo,
|
Criterion::Typo,
|
||||||
RankingRule::Proximity,
|
Criterion::Proximity,
|
||||||
RankingRule::Attribute,
|
Criterion::Attribute,
|
||||||
RankingRule::Sort,
|
Criterion::Sort,
|
||||||
RankingRule::Exactness,
|
Criterion::Exactness,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for RankingRule {
|
impl fmt::Display for Criterion {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use RankingRule::*;
|
use Criterion::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Words => f.write_str("words"),
|
Words => f.write_str("words"),
|
||||||
FilterBoosting(_) => write!(f, "filterBoosting"),
|
|
||||||
Typo => f.write_str("typo"),
|
Typo => f.write_str("typo"),
|
||||||
Proximity => f.write_str("proximity"),
|
Proximity => f.write_str("proximity"),
|
||||||
Attribute => f.write_str("attribute"),
|
Attribute => f.write_str("attribute"),
|
||||||
Sort => f.write_str("sort"),
|
Sort => f.write_str("sort"),
|
||||||
Exactness => f.write_str("exactness"),
|
Exactness => f.write_str("exactness"),
|
||||||
Asc(attr) => write!(f, "{attr}:asc"),
|
Asc(attr) => write!(f, "{}:asc", attr),
|
||||||
Desc(attr) => write!(f, "{attr}:desc"),
|
Desc(attr) => write!(f, "{}:desc", attr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,29 +110,29 @@ impl fmt::Display for RankingRule {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use big_s::S;
|
use big_s::S;
|
||||||
use RankingRuleError::*;
|
use CriterionError::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_criterion() {
|
fn parse_criterion() {
|
||||||
let valid_criteria = [
|
let valid_criteria = [
|
||||||
("words", RankingRule::Words),
|
("words", Criterion::Words),
|
||||||
("typo", RankingRule::Typo),
|
("typo", Criterion::Typo),
|
||||||
("proximity", RankingRule::Proximity),
|
("proximity", Criterion::Proximity),
|
||||||
("attribute", RankingRule::Attribute),
|
("attribute", Criterion::Attribute),
|
||||||
("sort", RankingRule::Sort),
|
("sort", Criterion::Sort),
|
||||||
("exactness", RankingRule::Exactness),
|
("exactness", Criterion::Exactness),
|
||||||
("price:asc", RankingRule::Asc(S("price"))),
|
("price:asc", Criterion::Asc(S("price"))),
|
||||||
("price:desc", RankingRule::Desc(S("price"))),
|
("price:desc", Criterion::Desc(S("price"))),
|
||||||
("price:asc:desc", RankingRule::Desc(S("price:asc"))),
|
("price:asc:desc", Criterion::Desc(S("price:asc"))),
|
||||||
("truc:machin:desc", RankingRule::Desc(S("truc:machin"))),
|
("truc:machin:desc", Criterion::Desc(S("truc:machin"))),
|
||||||
("hello-world!:desc", RankingRule::Desc(S("hello-world!"))),
|
("hello-world!:desc", Criterion::Desc(S("hello-world!"))),
|
||||||
("it's spacy over there:asc", RankingRule::Asc(S("it's spacy over there"))),
|
("it's spacy over there:asc", Criterion::Asc(S("it's spacy over there"))),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (input, expected) in valid_criteria {
|
for (input, expected) in valid_criteria {
|
||||||
let res = input.parse::<RankingRule>();
|
let res = input.parse::<Criterion>();
|
||||||
assert!(
|
assert!(
|
||||||
res.is_ok(),
|
res.is_ok(),
|
||||||
"Failed to parse `{}`, was expecting `{:?}` but instead got `{:?}`",
|
"Failed to parse `{}`, was expecting `{:?}` but instead got `{:?}`",
|
||||||
@@ -175,7 +167,7 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (input, expected) in invalid_criteria {
|
for (input, expected) in invalid_criteria {
|
||||||
let res = input.parse::<RankingRule>();
|
let res = input.parse::<Criterion>();
|
||||||
assert!(
|
assert!(
|
||||||
res.is_err(),
|
res.is_err(),
|
||||||
"Should no be able to parse `{}`, was expecting an error but instead got: `{:?}`",
|
"Should no be able to parse `{}`, was expecting an error but instead got: `{:?}`",
|
||||||
@@ -9,7 +9,7 @@ use serde_json::Value;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::documents::{self, DocumentsBatchCursorError};
|
use crate::documents::{self, DocumentsBatchCursorError};
|
||||||
use crate::{DocumentId, FieldId, Object, RankingRuleError, SortError};
|
use crate::{CriterionError, DocumentId, FieldId, Object, SortError};
|
||||||
|
|
||||||
pub fn is_reserved_keyword(keyword: &str) -> bool {
|
pub fn is_reserved_keyword(keyword: &str) -> bool {
|
||||||
["_geo", "_geoDistance", "_geoPoint", "_geoRadius", "_geoBoundingBox"].contains(&keyword)
|
["_geo", "_geoDistance", "_geoPoint", "_geoRadius", "_geoBoundingBox"].contains(&keyword)
|
||||||
@@ -94,7 +94,7 @@ pub enum UserError {
|
|||||||
#[error("A document cannot contain more than 65,535 fields.")]
|
#[error("A document cannot contain more than 65,535 fields.")]
|
||||||
AttributeLimitReached,
|
AttributeLimitReached,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
CriterionError(#[from] RankingRuleError),
|
CriterionError(#[from] CriterionError),
|
||||||
#[error("Maximum number of documents reached.")]
|
#[error("Maximum number of documents reached.")]
|
||||||
DocumentLimitReached,
|
DocumentLimitReached,
|
||||||
#[error(
|
#[error(
|
||||||
@@ -116,8 +116,6 @@ only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and undersco
|
|||||||
InvalidVectorsType { document_id: Value, value: Value },
|
InvalidVectorsType { document_id: Value, value: Value },
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidFilter(String),
|
InvalidFilter(String),
|
||||||
#[error("{0}")]
|
|
||||||
InvalidBoostingFilter(String),
|
|
||||||
#[error("Invalid type for filter subexpression: expected: {}, found: {1}.", .0.join(", "))]
|
#[error("Invalid type for filter subexpression: expected: {}, found: {1}.", .0.join(", "))]
|
||||||
InvalidFilterExpression(&'static [&'static str], Value),
|
InvalidFilterExpression(&'static [&'static str], Value),
|
||||||
#[error("Attribute `{}` is not sortable. {}",
|
#[error("Attribute `{}` is not sortable. {}",
|
||||||
@@ -282,7 +280,7 @@ error_from_sub_error! {
|
|||||||
ThreadPoolBuildError => InternalError,
|
ThreadPoolBuildError => InternalError,
|
||||||
SerializationError => InternalError,
|
SerializationError => InternalError,
|
||||||
GeoError => UserError,
|
GeoError => UserError,
|
||||||
RankingRuleError => UserError,
|
CriterionError => UserError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> From<grenad::Error<E>> for Error
|
impl<E> From<grenad::Error<E>> for Error
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ use crate::heed_codec::{
|
|||||||
};
|
};
|
||||||
use crate::readable_slices::ReadableSlices;
|
use crate::readable_slices::ReadableSlices;
|
||||||
use crate::{
|
use crate::{
|
||||||
default_criteria, CboRoaringBitmapCodec, DocumentId, ExternalDocumentsIds, FacetDistribution,
|
default_criteria, CboRoaringBitmapCodec, Criterion, DocumentId, ExternalDocumentsIds,
|
||||||
FieldDistribution, FieldId, FieldIdWordCountCodec, GeoPoint, ObkvCodec, OrderBy, RankingRule,
|
FacetDistribution, FieldDistribution, FieldId, FieldIdWordCountCodec, GeoPoint, ObkvCodec,
|
||||||
Result, RoaringBitmapCodec, RoaringBitmapLenCodec, Search, U8StrStrCodec, BEU16, BEU32,
|
OrderBy, Result, RoaringBitmapCodec, RoaringBitmapLenCodec, Search, U8StrStrCodec, BEU16,
|
||||||
|
BEU32,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The HNSW data-structure that we serialize, fill and search in.
|
/// The HNSW data-structure that we serialize, fill and search in.
|
||||||
@@ -894,7 +895,7 @@ impl Index {
|
|||||||
let distinct_field = self.distinct_field(rtxn)?;
|
let distinct_field = self.distinct_field(rtxn)?;
|
||||||
let asc_desc_fields =
|
let asc_desc_fields =
|
||||||
self.criteria(rtxn)?.into_iter().filter_map(|criterion| match criterion {
|
self.criteria(rtxn)?.into_iter().filter_map(|criterion| match criterion {
|
||||||
RankingRule::Asc(field) | RankingRule::Desc(field) => Some(field),
|
Criterion::Asc(field) | Criterion::Desc(field) => Some(field),
|
||||||
_otherwise => None,
|
_otherwise => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1022,17 +1023,17 @@ impl Index {
|
|||||||
pub(crate) fn put_criteria(
|
pub(crate) fn put_criteria(
|
||||||
&self,
|
&self,
|
||||||
wtxn: &mut RwTxn,
|
wtxn: &mut RwTxn,
|
||||||
criteria: &[RankingRule],
|
criteria: &[Criterion],
|
||||||
) -> heed::Result<()> {
|
) -> heed::Result<()> {
|
||||||
self.main.put::<_, Str, SerdeJson<&[RankingRule]>>(wtxn, main_key::CRITERIA_KEY, &criteria)
|
self.main.put::<_, Str, SerdeJson<&[Criterion]>>(wtxn, main_key::CRITERIA_KEY, &criteria)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn delete_criteria(&self, wtxn: &mut RwTxn) -> heed::Result<bool> {
|
pub(crate) fn delete_criteria(&self, wtxn: &mut RwTxn) -> heed::Result<bool> {
|
||||||
self.main.delete::<_, Str>(wtxn, main_key::CRITERIA_KEY)
|
self.main.delete::<_, Str>(wtxn, main_key::CRITERIA_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn criteria(&self, rtxn: &RoTxn) -> heed::Result<Vec<RankingRule>> {
|
pub fn criteria(&self, rtxn: &RoTxn) -> heed::Result<Vec<Criterion>> {
|
||||||
match self.main.get::<_, Str, SerdeJson<Vec<RankingRule>>>(rtxn, main_key::CRITERIA_KEY)? {
|
match self.main.get::<_, Str, SerdeJson<Vec<Criterion>>>(rtxn, main_key::CRITERIA_KEY)? {
|
||||||
Some(criteria) => Ok(criteria),
|
Some(criteria) => Ok(criteria),
|
||||||
None => Ok(default_criteria()),
|
None => Ok(default_criteria()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
|||||||
pub mod documents;
|
pub mod documents;
|
||||||
|
|
||||||
mod asc_desc;
|
mod asc_desc;
|
||||||
|
mod criterion;
|
||||||
pub mod distance;
|
pub mod distance;
|
||||||
mod error;
|
mod error;
|
||||||
mod external_documents_ids;
|
mod external_documents_ids;
|
||||||
@@ -17,7 +18,6 @@ mod fields_ids_map;
|
|||||||
pub mod heed_codec;
|
pub mod heed_codec;
|
||||||
pub mod index;
|
pub mod index;
|
||||||
pub mod proximity;
|
pub mod proximity;
|
||||||
mod ranking_rule;
|
|
||||||
mod readable_slices;
|
mod readable_slices;
|
||||||
pub mod score_details;
|
pub mod score_details;
|
||||||
mod search;
|
mod search;
|
||||||
@@ -44,6 +44,7 @@ use serde_json::Value;
|
|||||||
pub use {charabia as tokenizer, heed};
|
pub use {charabia as tokenizer, heed};
|
||||||
|
|
||||||
pub use self::asc_desc::{AscDesc, AscDescError, Member, SortError};
|
pub use self::asc_desc::{AscDesc, AscDescError, Member, SortError};
|
||||||
|
pub use self::criterion::{default_criteria, Criterion, CriterionError};
|
||||||
pub use self::error::{
|
pub use self::error::{
|
||||||
Error, FieldIdMapMissingEntry, InternalError, SerializationError, UserError,
|
Error, FieldIdMapMissingEntry, InternalError, SerializationError, UserError,
|
||||||
};
|
};
|
||||||
@@ -56,7 +57,6 @@ pub use self::heed_codec::{
|
|||||||
UncheckedU8StrStrCodec,
|
UncheckedU8StrStrCodec,
|
||||||
};
|
};
|
||||||
pub use self::index::Index;
|
pub use self::index::Index;
|
||||||
pub use self::ranking_rule::{default_criteria, RankingRule, RankingRuleError};
|
|
||||||
pub use self::search::{
|
pub use self::search::{
|
||||||
FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder,
|
FacetDistribution, FacetValueHit, Filter, FormatOptions, MatchBounds, MatcherBuilder,
|
||||||
MatchingWords, OrderBy, Search, SearchForFacetValues, SearchResult, TermsMatchingStrategy,
|
MatchingWords, OrderBy, Search, SearchForFacetValues, SearchResult, TermsMatchingStrategy,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::distance_between_two_points;
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ScoreDetails {
|
pub enum ScoreDetails {
|
||||||
Words(Words),
|
Words(Words),
|
||||||
FilterBoosting(FilterBoosting),
|
|
||||||
Typo(Typo),
|
Typo(Typo),
|
||||||
Proximity(Rank),
|
Proximity(Rank),
|
||||||
Fid(Rank),
|
Fid(Rank),
|
||||||
@@ -24,7 +23,6 @@ impl ScoreDetails {
|
|||||||
pub fn rank(&self) -> Option<Rank> {
|
pub fn rank(&self) -> Option<Rank> {
|
||||||
match self {
|
match self {
|
||||||
ScoreDetails::Words(details) => Some(details.rank()),
|
ScoreDetails::Words(details) => Some(details.rank()),
|
||||||
ScoreDetails::FilterBoosting(_) => None,
|
|
||||||
ScoreDetails::Typo(details) => Some(details.rank()),
|
ScoreDetails::Typo(details) => Some(details.rank()),
|
||||||
ScoreDetails::Proximity(details) => Some(*details),
|
ScoreDetails::Proximity(details) => Some(*details),
|
||||||
ScoreDetails::Fid(details) => Some(*details),
|
ScoreDetails::Fid(details) => Some(*details),
|
||||||
@@ -62,11 +60,6 @@ impl ScoreDetails {
|
|||||||
details_map.insert("words".into(), words_details);
|
details_map.insert("words".into(), words_details);
|
||||||
order += 1;
|
order += 1;
|
||||||
}
|
}
|
||||||
ScoreDetails::FilterBoosting(FilterBoosting { matching }) => {
|
|
||||||
let sort_details = serde_json::json!({ "matching": matching });
|
|
||||||
details_map.insert("filterBoosting".into(), sort_details);
|
|
||||||
order += 1;
|
|
||||||
}
|
|
||||||
ScoreDetails::Typo(typo) => {
|
ScoreDetails::Typo(typo) => {
|
||||||
let typo_details = serde_json::json!({
|
let typo_details = serde_json::json!({
|
||||||
"order": order,
|
"order": order,
|
||||||
@@ -228,11 +221,6 @@ impl Words {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct FilterBoosting {
|
|
||||||
pub matching: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Structure that is super similar to [`Words`], but whose semantics is a bit distinct.
|
/// Structure that is super similar to [`Words`], but whose semantics is a bit distinct.
|
||||||
///
|
///
|
||||||
/// In exactness, the number of matching words can actually be 0 with a non-zero score,
|
/// In exactness, the number of matching words can actually be 0 with a non-zero score,
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ pub struct Search<'a> {
|
|||||||
vector: Option<Vec<f32>>,
|
vector: Option<Vec<f32>>,
|
||||||
// this should be linked to the String in the query
|
// this should be linked to the String in the query
|
||||||
filter: Option<Filter<'a>>,
|
filter: Option<Filter<'a>>,
|
||||||
boosting_filter: Option<Filter<'a>>,
|
|
||||||
offset: usize,
|
offset: usize,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
sort_criteria: Option<Vec<AscDesc>>,
|
sort_criteria: Option<Vec<AscDesc>>,
|
||||||
@@ -58,7 +57,6 @@ impl<'a> Search<'a> {
|
|||||||
query: None,
|
query: None,
|
||||||
vector: None,
|
vector: None,
|
||||||
filter: None,
|
filter: None,
|
||||||
boosting_filter: None,
|
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
sort_criteria: None,
|
sort_criteria: None,
|
||||||
@@ -123,11 +121,6 @@ impl<'a> Search<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn boosting_filter(&mut self, condition: Filter<'a>) -> &mut Search<'a> {
|
|
||||||
self.boosting_filter = Some(condition);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn geo_sort_strategy(&mut self, strategy: new::GeoSortStrategy) -> &mut Search<'a> {
|
pub fn geo_sort_strategy(&mut self, strategy: new::GeoSortStrategy) -> &mut Search<'a> {
|
||||||
self.geo_strategy = strategy;
|
self.geo_strategy = strategy;
|
||||||
@@ -157,7 +150,6 @@ impl<'a> Search<'a> {
|
|||||||
self.scoring_strategy,
|
self.scoring_strategy,
|
||||||
self.exhaustive_number_hits,
|
self.exhaustive_number_hits,
|
||||||
&self.filter,
|
&self.filter,
|
||||||
&self.boosting_filter,
|
|
||||||
&self.sort_criteria,
|
&self.sort_criteria,
|
||||||
self.geo_strategy,
|
self.geo_strategy,
|
||||||
self.offset,
|
self.offset,
|
||||||
@@ -183,7 +175,6 @@ impl fmt::Debug for Search<'_> {
|
|||||||
query,
|
query,
|
||||||
vector: _,
|
vector: _,
|
||||||
filter,
|
filter,
|
||||||
boosting_filter,
|
|
||||||
offset,
|
offset,
|
||||||
limit,
|
limit,
|
||||||
sort_criteria,
|
sort_criteria,
|
||||||
@@ -200,7 +191,6 @@ impl fmt::Debug for Search<'_> {
|
|||||||
.field("query", query)
|
.field("query", query)
|
||||||
.field("vector", &"[...]")
|
.field("vector", &"[...]")
|
||||||
.field("filter", filter)
|
.field("filter", filter)
|
||||||
.field("boosting_filter", boosting_filter)
|
|
||||||
.field("offset", offset)
|
.field("offset", offset)
|
||||||
.field("limit", limit)
|
.field("limit", limit)
|
||||||
.field("sort_criteria", sort_criteria)
|
.field("sort_criteria", sort_criteria)
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
use roaring::RoaringBitmap;
|
|
||||||
|
|
||||||
use super::logger::SearchLogger;
|
|
||||||
use super::{RankingRule, RankingRuleOutput, RankingRuleQueryTrait, SearchContext};
|
|
||||||
use crate::score_details::{self, ScoreDetails};
|
|
||||||
use crate::{Filter, Result};
|
|
||||||
|
|
||||||
pub struct FilterBoosting<'f, Query> {
|
|
||||||
filter: Filter<'f>,
|
|
||||||
original_query: Option<Query>,
|
|
||||||
matching: Option<RankingRuleOutput<Query>>,
|
|
||||||
non_matching: Option<RankingRuleOutput<Query>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'f, Query> FilterBoosting<'f, Query> {
|
|
||||||
pub fn new(filter: Filter<'f>) -> Result<Self> {
|
|
||||||
Ok(Self { filter, original_query: None, matching: None, non_matching: None })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, 'f, Query: RankingRuleQueryTrait> RankingRule<'ctx, Query>
|
|
||||||
for FilterBoosting<'f, Query>
|
|
||||||
{
|
|
||||||
fn id(&self) -> String {
|
|
||||||
// TODO improve this
|
|
||||||
let Self { filter: original_expression, .. } = self;
|
|
||||||
format!("boost:{original_expression:?}")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_iteration(
|
|
||||||
&mut self,
|
|
||||||
ctx: &mut SearchContext<'ctx>,
|
|
||||||
_logger: &mut dyn SearchLogger<Query>,
|
|
||||||
parent_candidates: &RoaringBitmap,
|
|
||||||
parent_query: &Query,
|
|
||||||
) -> Result<()> {
|
|
||||||
let universe_matching = match self.filter.evaluate(ctx.txn, ctx.index) {
|
|
||||||
Ok(documents) => documents,
|
|
||||||
Err(e) => return Err(e), // TODO manage the invalid_search_boosting_filter
|
|
||||||
};
|
|
||||||
let matching = parent_candidates & universe_matching;
|
|
||||||
let non_matching = parent_candidates - &matching;
|
|
||||||
|
|
||||||
self.original_query = Some(parent_query.clone());
|
|
||||||
|
|
||||||
self.matching = Some(RankingRuleOutput {
|
|
||||||
query: parent_query.clone(),
|
|
||||||
candidates: matching,
|
|
||||||
score: ScoreDetails::FilterBoosting(score_details::FilterBoosting { matching: true }),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.non_matching = Some(RankingRuleOutput {
|
|
||||||
query: parent_query.clone(),
|
|
||||||
candidates: non_matching,
|
|
||||||
score: ScoreDetails::FilterBoosting(score_details::FilterBoosting { matching: false }),
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_bucket(
|
|
||||||
&mut self,
|
|
||||||
_ctx: &mut SearchContext<'ctx>,
|
|
||||||
_logger: &mut dyn SearchLogger<Query>,
|
|
||||||
_universe: &RoaringBitmap,
|
|
||||||
) -> Result<Option<RankingRuleOutput<Query>>> {
|
|
||||||
Ok(self.matching.take().or_else(|| self.non_matching.take()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn end_iteration(
|
|
||||||
&mut self,
|
|
||||||
_ctx: &mut SearchContext<'ctx>,
|
|
||||||
_logger: &mut dyn SearchLogger<Query>,
|
|
||||||
) {
|
|
||||||
self.original_query = None;
|
|
||||||
self.matching = None;
|
|
||||||
self.non_matching = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -512,7 +512,6 @@ mod tests {
|
|||||||
false,
|
false,
|
||||||
&None,
|
&None,
|
||||||
&None,
|
&None,
|
||||||
&None,
|
|
||||||
crate::search::new::GeoSortStrategy::default(),
|
crate::search::new::GeoSortStrategy::default(),
|
||||||
0,
|
0,
|
||||||
100,
|
100,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ mod resolve_query_graph;
|
|||||||
mod small_bitmap;
|
mod small_bitmap;
|
||||||
|
|
||||||
mod exact_attribute;
|
mod exact_attribute;
|
||||||
mod filter_boosting;
|
|
||||||
mod sort;
|
mod sort;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -27,7 +26,6 @@ use bucket_sort::{bucket_sort, BucketSortOutput};
|
|||||||
use charabia::TokenizerBuilder;
|
use charabia::TokenizerBuilder;
|
||||||
use db_cache::DatabaseCache;
|
use db_cache::DatabaseCache;
|
||||||
use exact_attribute::ExactAttribute;
|
use exact_attribute::ExactAttribute;
|
||||||
use filter_boosting::FilterBoosting;
|
|
||||||
use graph_based_ranking_rule::{Exactness, Fid, Position, Proximity, Typo};
|
use graph_based_ranking_rule::{Exactness, Fid, Position, Proximity, Typo};
|
||||||
use heed::RoTxn;
|
use heed::RoTxn;
|
||||||
use instant_distance::Search;
|
use instant_distance::Search;
|
||||||
@@ -192,30 +190,25 @@ fn resolve_universe(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the list of initialised ranking rules to be used for a placeholder search.
|
/// Return the list of initialised ranking rules to be used for a placeholder search.
|
||||||
fn get_ranking_rules_for_placeholder_search<'ctx, 'f: 'ctx>(
|
fn get_ranking_rules_for_placeholder_search<'ctx>(
|
||||||
ctx: &SearchContext<'ctx>,
|
ctx: &SearchContext<'ctx>,
|
||||||
sort_criteria: &Option<Vec<AscDesc>>,
|
sort_criteria: &Option<Vec<AscDesc>>,
|
||||||
geo_strategy: geo_sort::Strategy,
|
geo_strategy: geo_sort::Strategy,
|
||||||
boosting_filter: &Option<Filter<'f>>,
|
|
||||||
) -> Result<Vec<BoxRankingRule<'ctx, PlaceholderQuery>>> {
|
) -> Result<Vec<BoxRankingRule<'ctx, PlaceholderQuery>>> {
|
||||||
let mut sort = false;
|
let mut sort = false;
|
||||||
let mut sorted_fields = HashSet::new();
|
let mut sorted_fields = HashSet::new();
|
||||||
let mut geo_sorted = false;
|
let mut geo_sorted = false;
|
||||||
let mut ranking_rules: Vec<BoxRankingRule<_>> = match boosting_filter {
|
let mut ranking_rules: Vec<BoxRankingRule<PlaceholderQuery>> = vec![];
|
||||||
Some(filter) => vec![Box::new(FilterBoosting::new(filter.clone())?)],
|
|
||||||
None => Vec::new(),
|
|
||||||
};
|
|
||||||
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
||||||
for rr in settings_ranking_rules {
|
for rr in settings_ranking_rules {
|
||||||
match rr {
|
match rr {
|
||||||
// These rules need a query to have an effect; ignore them in placeholder search
|
// These rules need a query to have an effect; ignore them in placeholder search
|
||||||
crate::RankingRule::FilterBoosting(_)
|
crate::Criterion::Words
|
||||||
| crate::RankingRule::Words
|
| crate::Criterion::Typo
|
||||||
| crate::RankingRule::Typo
|
| crate::Criterion::Attribute
|
||||||
| crate::RankingRule::Attribute
|
| crate::Criterion::Proximity
|
||||||
| crate::RankingRule::Proximity
|
| crate::Criterion::Exactness => continue,
|
||||||
| crate::RankingRule::Exactness => continue,
|
crate::Criterion::Sort => {
|
||||||
crate::RankingRule::Sort => {
|
|
||||||
if sort {
|
if sort {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -229,14 +222,14 @@ fn get_ranking_rules_for_placeholder_search<'ctx, 'f: 'ctx>(
|
|||||||
)?;
|
)?;
|
||||||
sort = true;
|
sort = true;
|
||||||
}
|
}
|
||||||
crate::RankingRule::Asc(field_name) => {
|
crate::Criterion::Asc(field_name) => {
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sorted_fields.insert(field_name.clone());
|
sorted_fields.insert(field_name.clone());
|
||||||
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
||||||
}
|
}
|
||||||
crate::RankingRule::Desc(field_name) => {
|
crate::Criterion::Desc(field_name) => {
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -249,12 +242,11 @@ fn get_ranking_rules_for_placeholder_search<'ctx, 'f: 'ctx>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the list of initialised ranking rules to be used for a query graph search.
|
/// Return the list of initialised ranking rules to be used for a query graph search.
|
||||||
fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
fn get_ranking_rules_for_query_graph_search<'ctx>(
|
||||||
ctx: &SearchContext<'ctx>,
|
ctx: &SearchContext<'ctx>,
|
||||||
sort_criteria: &Option<Vec<AscDesc>>,
|
sort_criteria: &Option<Vec<AscDesc>>,
|
||||||
geo_strategy: geo_sort::Strategy,
|
geo_strategy: geo_sort::Strategy,
|
||||||
terms_matching_strategy: TermsMatchingStrategy,
|
terms_matching_strategy: TermsMatchingStrategy,
|
||||||
boosting_filter: &Option<Filter<'f>>,
|
|
||||||
) -> Result<Vec<BoxRankingRule<'ctx, QueryGraph>>> {
|
) -> Result<Vec<BoxRankingRule<'ctx, QueryGraph>>> {
|
||||||
// query graph search
|
// query graph search
|
||||||
let mut words = false;
|
let mut words = false;
|
||||||
@@ -271,18 +263,15 @@ fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
|||||||
words = true;
|
words = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ranking_rules: Vec<BoxRankingRule<QueryGraph>> = match boosting_filter {
|
let mut ranking_rules: Vec<BoxRankingRule<QueryGraph>> = vec![];
|
||||||
Some(filter) => vec![Box::new(FilterBoosting::new(filter.clone())?)],
|
|
||||||
None => Vec::new(),
|
|
||||||
};
|
|
||||||
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
let settings_ranking_rules = ctx.index.criteria(ctx.txn)?;
|
||||||
for rr in settings_ranking_rules {
|
for rr in settings_ranking_rules {
|
||||||
// Add Words before any of: typo, proximity, attribute
|
// Add Words before any of: typo, proximity, attribute
|
||||||
match rr {
|
match rr {
|
||||||
crate::RankingRule::Typo
|
crate::Criterion::Typo
|
||||||
| crate::RankingRule::Attribute
|
| crate::Criterion::Attribute
|
||||||
| crate::RankingRule::Proximity
|
| crate::Criterion::Proximity
|
||||||
| crate::RankingRule::Exactness => {
|
| crate::Criterion::Exactness => {
|
||||||
if !words {
|
if !words {
|
||||||
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
||||||
words = true;
|
words = true;
|
||||||
@@ -291,33 +280,28 @@ fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
match rr {
|
match rr {
|
||||||
crate::RankingRule::Words => {
|
crate::Criterion::Words => {
|
||||||
if words {
|
if words {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
ranking_rules.push(Box::new(Words::new(terms_matching_strategy)));
|
||||||
words = true;
|
words = true;
|
||||||
}
|
}
|
||||||
crate::RankingRule::FilterBoosting(_) => {
|
crate::Criterion::Typo => {
|
||||||
// that is not possible to define the filterBoosting ranking rule by hand,
|
|
||||||
// or by using the seetings. It is always inserted by the engine itself.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
crate::RankingRule::Typo => {
|
|
||||||
if typo {
|
if typo {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
typo = true;
|
typo = true;
|
||||||
ranking_rules.push(Box::new(Typo::new(None)));
|
ranking_rules.push(Box::new(Typo::new(None)));
|
||||||
}
|
}
|
||||||
crate::RankingRule::Proximity => {
|
crate::Criterion::Proximity => {
|
||||||
if proximity {
|
if proximity {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
proximity = true;
|
proximity = true;
|
||||||
ranking_rules.push(Box::new(Proximity::new(None)));
|
ranking_rules.push(Box::new(Proximity::new(None)));
|
||||||
}
|
}
|
||||||
crate::RankingRule::Attribute => {
|
crate::Criterion::Attribute => {
|
||||||
if attribute {
|
if attribute {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -325,7 +309,7 @@ fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
|||||||
ranking_rules.push(Box::new(Fid::new(None)));
|
ranking_rules.push(Box::new(Fid::new(None)));
|
||||||
ranking_rules.push(Box::new(Position::new(None)));
|
ranking_rules.push(Box::new(Position::new(None)));
|
||||||
}
|
}
|
||||||
crate::RankingRule::Sort => {
|
crate::Criterion::Sort => {
|
||||||
if sort {
|
if sort {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -339,7 +323,7 @@ fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
|||||||
)?;
|
)?;
|
||||||
sort = true;
|
sort = true;
|
||||||
}
|
}
|
||||||
crate::RankingRule::Exactness => {
|
crate::Criterion::Exactness => {
|
||||||
if exactness {
|
if exactness {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -347,15 +331,14 @@ fn get_ranking_rules_for_query_graph_search<'ctx, 'f: 'ctx>(
|
|||||||
ranking_rules.push(Box::new(Exactness::new()));
|
ranking_rules.push(Box::new(Exactness::new()));
|
||||||
exactness = true;
|
exactness = true;
|
||||||
}
|
}
|
||||||
crate::RankingRule::Asc(field_name) => {
|
crate::Criterion::Asc(field_name) => {
|
||||||
// TODO Question: Why would it be invalid to sort price:asc, typo, price:desc?
|
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sorted_fields.insert(field_name.clone());
|
sorted_fields.insert(field_name.clone());
|
||||||
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
ranking_rules.push(Box::new(Sort::new(ctx.index, ctx.txn, field_name, true)?));
|
||||||
}
|
}
|
||||||
crate::RankingRule::Desc(field_name) => {
|
crate::Criterion::Desc(field_name) => {
|
||||||
if sorted_fields.contains(&field_name) {
|
if sorted_fields.contains(&field_name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -423,15 +406,14 @@ fn resolve_sort_criteria<'ctx, Query: RankingRuleQueryTrait>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn execute_search<'ctx, 'f: 'ctx>(
|
pub fn execute_search(
|
||||||
ctx: &mut SearchContext<'ctx>,
|
ctx: &mut SearchContext,
|
||||||
query: &Option<String>,
|
query: &Option<String>,
|
||||||
vector: &Option<Vec<f32>>,
|
vector: &Option<Vec<f32>>,
|
||||||
terms_matching_strategy: TermsMatchingStrategy,
|
terms_matching_strategy: TermsMatchingStrategy,
|
||||||
scoring_strategy: ScoringStrategy,
|
scoring_strategy: ScoringStrategy,
|
||||||
exhaustive_number_hits: bool,
|
exhaustive_number_hits: bool,
|
||||||
filter: &Option<Filter>,
|
filters: &Option<Filter>,
|
||||||
boosting_filter: &Option<Filter<'f>>,
|
|
||||||
sort_criteria: &Option<Vec<AscDesc>>,
|
sort_criteria: &Option<Vec<AscDesc>>,
|
||||||
geo_strategy: geo_sort::Strategy,
|
geo_strategy: geo_sort::Strategy,
|
||||||
from: usize,
|
from: usize,
|
||||||
@@ -440,8 +422,8 @@ pub fn execute_search<'ctx, 'f: 'ctx>(
|
|||||||
placeholder_search_logger: &mut dyn SearchLogger<PlaceholderQuery>,
|
placeholder_search_logger: &mut dyn SearchLogger<PlaceholderQuery>,
|
||||||
query_graph_logger: &mut dyn SearchLogger<QueryGraph>,
|
query_graph_logger: &mut dyn SearchLogger<QueryGraph>,
|
||||||
) -> Result<PartialSearchResult> {
|
) -> Result<PartialSearchResult> {
|
||||||
let mut universe = if let Some(filter) = filter {
|
let mut universe = if let Some(filters) = filters {
|
||||||
filter.evaluate(ctx.txn, ctx.index)?
|
filters.evaluate(ctx.txn, ctx.index)?
|
||||||
} else {
|
} else {
|
||||||
ctx.index.documents_ids(ctx.txn)?
|
ctx.index.documents_ids(ctx.txn)?
|
||||||
};
|
};
|
||||||
@@ -534,7 +516,6 @@ pub fn execute_search<'ctx, 'f: 'ctx>(
|
|||||||
sort_criteria,
|
sort_criteria,
|
||||||
geo_strategy,
|
geo_strategy,
|
||||||
terms_matching_strategy,
|
terms_matching_strategy,
|
||||||
boosting_filter,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
universe =
|
universe =
|
||||||
@@ -551,13 +532,8 @@ pub fn execute_search<'ctx, 'f: 'ctx>(
|
|||||||
query_graph_logger,
|
query_graph_logger,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
let ranking_rules = get_ranking_rules_for_placeholder_search(
|
let ranking_rules =
|
||||||
ctx,
|
get_ranking_rules_for_placeholder_search(ctx, sort_criteria, geo_strategy)?;
|
||||||
sort_criteria,
|
|
||||||
geo_strategy,
|
|
||||||
boosting_filter,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
bucket_sort(
|
bucket_sort(
|
||||||
ctx,
|
ctx,
|
||||||
ranking_rules,
|
ranking_rules,
|
||||||
@@ -604,8 +580,7 @@ fn check_sort_criteria(ctx: &SearchContext, sort_criteria: Option<&Vec<AscDesc>>
|
|||||||
|
|
||||||
// We check that the sort ranking rule exists and throw an
|
// We check that the sort ranking rule exists and throw an
|
||||||
// error if we try to use it and that it doesn't.
|
// error if we try to use it and that it doesn't.
|
||||||
let sort_ranking_rule_missing =
|
let sort_ranking_rule_missing = !ctx.index.criteria(ctx.txn)?.contains(&crate::Criterion::Sort);
|
||||||
!ctx.index.criteria(ctx.txn)?.contains(&crate::RankingRule::Sort);
|
|
||||||
if sort_ranking_rule_missing {
|
if sort_ranking_rule_missing {
|
||||||
return Err(UserError::SortRankingRuleMissing.into());
|
return Err(UserError::SortRankingRuleMissing.into());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -12,7 +12,7 @@ fn create_index() -> TempIndex {
|
|||||||
"description".to_owned(),
|
"description".to_owned(),
|
||||||
"plot".to_owned(),
|
"plot".to_owned(),
|
||||||
]);
|
]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Attribute]);
|
s.set_criteria(vec![Criterion::Attribute]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::{db_snap, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{db_snap, Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -12,7 +12,7 @@ fn create_index() -> TempIndex {
|
|||||||
"text2".to_owned(),
|
"text2".to_owned(),
|
||||||
"other".to_owned(),
|
"other".to_owned(),
|
||||||
]);
|
]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Attribute]);
|
s.set_criteria(vec![Criterion::Attribute]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use maplit::hashset;
|
|||||||
|
|
||||||
use super::collect_field_values;
|
use super::collect_field_values;
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::{AscDesc, Index, Member, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{AscDesc, Criterion, Index, Member, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -30,7 +30,7 @@ fn create_index() -> TempIndex {
|
|||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_sortable_fields(hashset! { S("rank1"), S("letter") });
|
s.set_sortable_fields(hashset! { S("rank1"), S("letter") });
|
||||||
s.set_distinct_field("letter".to_owned());
|
s.set_distinct_field("letter".to_owned());
|
||||||
s.set_ranking_rules(vec![RankingRule::Words]);
|
s.set_criteria(vec![Criterion::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ fn test_distinct_placeholder_sort() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Sort]);
|
s.set_criteria(vec![Criterion::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -387,7 +387,7 @@ fn test_distinct_words() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words]);
|
s.set_criteria(vec![Criterion::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -440,11 +440,7 @@ fn test_distinct_sort_words() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Sort, Criterion::Words, Criterion::Desc(S("rank1"))]);
|
||||||
RankingRule::Sort,
|
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Desc(S("rank1")),
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -517,7 +513,7 @@ fn test_distinct_all_candidates() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Sort]);
|
s.set_criteria(vec![Criterion::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -540,7 +536,7 @@ fn test_distinct_typo() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Typo]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Then these rules will only work with
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index_simple_ordered() -> TempIndex {
|
fn create_index_simple_ordered() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -30,7 +30,7 @@ fn create_index_simple_ordered() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ fn create_index_simple_reversed() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ fn create_index_simple_random() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ fn create_index_attribute_starts_with() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ fn create_index_simple_ordered_with_typos() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -350,11 +350,7 @@ fn create_index_with_varying_proximities() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
||||||
RankingRule::Exactness,
|
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Proximity,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -408,7 +404,7 @@ fn create_index_with_typo_and_prefix() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -446,11 +442,7 @@ fn create_index_all_equal_except_proximity_between_ignored_terms() -> TempIndex
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
||||||
RankingRule::Exactness,
|
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Proximity,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -706,7 +698,7 @@ fn test_exactness_after_words() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Exactness]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Exactness]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -755,7 +747,7 @@ fn test_words_after_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Exactness, RankingRule::Words]);
|
s.set_criteria(vec![Criterion::Exactness, Criterion::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -804,11 +796,7 @@ fn test_proximity_after_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
||||||
RankingRule::Exactness,
|
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Proximity,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -846,11 +834,7 @@ fn test_proximity_after_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Proximity]);
|
||||||
RankingRule::Exactness,
|
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Proximity,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -884,11 +868,7 @@ fn test_exactness_followed_by_typo_prefer_no_typo_prefix() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Exactness, Criterion::Words, Criterion::Typo]);
|
||||||
RankingRule::Exactness,
|
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Typo,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -924,11 +904,7 @@ fn test_typo_followed_by_exactness() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Words, Criterion::Typo, Criterion::Exactness]);
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Typo,
|
|
||||||
RankingRule::Exactness,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use maplit::hashset;
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::score_details::ScoreDetails;
|
use crate::score_details::ScoreDetails;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{AscDesc, GeoSortStrategy, Member, RankingRule, Search, SearchResult};
|
use crate::{AscDesc, Criterion, GeoSortStrategy, Member, Search, SearchResult};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -18,7 +18,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_sortable_fields(hashset! { S("_geo") });
|
s.set_sortable_fields(hashset! { S("_geo") });
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Sort]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
index
|
index
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ use maplit::{btreemap, hashset};
|
|||||||
|
|
||||||
use crate::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use crate::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
||||||
use crate::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use crate::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use crate::{db_snap, Index, Object, RankingRule};
|
use crate::{db_snap, Criterion, Index, Object};
|
||||||
pub const CONTENT: &str = include_str!("../../../../tests/assets/test_set.ndjson");
|
pub const CONTENT: &str = include_str!("../../../../tests/assets/test_set.ndjson");
|
||||||
|
|
||||||
pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
||||||
let path = tempfile::tempdir().unwrap();
|
let path = tempfile::tempdir().unwrap();
|
||||||
let mut options = EnvOpenOptions::new();
|
let mut options = EnvOpenOptions::new();
|
||||||
options.map_size(10 * 1024 * 1024); // 10 MB
|
options.map_size(10 * 1024 * 1024); // 10 MB
|
||||||
@@ -20,7 +20,7 @@ pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
|||||||
|
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
|
|
||||||
builder.set_ranking_rules(criteria.to_vec());
|
builder.set_criteria(criteria.to_vec());
|
||||||
builder.set_filterable_fields(hashset! {
|
builder.set_filterable_fields(hashset! {
|
||||||
S("tag"),
|
S("tag"),
|
||||||
S("asc_desc_rank"),
|
S("asc_desc_rank"),
|
||||||
@@ -70,6 +70,6 @@ pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn snapshot_integration_dataset() {
|
fn snapshot_integration_dataset() {
|
||||||
let index = setup_search_index_with_criteria(&[RankingRule::Attribute]);
|
let index = setup_search_index_with_criteria(&[Criterion::Attribute]);
|
||||||
db_snap!(index, word_position_docids, @"3c9347a767bceef3beb31465f1e5f3ae");
|
db_snap!(index, word_position_docids, @"3c9347a767bceef3beb31465f1e5f3ae");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ This module tests the following properties:
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -28,7 +28,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Words]);
|
s.set_criteria(vec![Criterion::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_simple_index() -> TempIndex {
|
fn create_simple_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -28,7 +28,7 @@ fn create_simple_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ fn create_edge_cases_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ implemented.
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -17,11 +17,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Words, Criterion::Proximity, Criterion::Typo]);
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Proximity,
|
|
||||||
RankingRule::Typo,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use maplit::hashset;
|
|||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{
|
use crate::{
|
||||||
score_details, AscDesc, Member, RankingRule, Search, SearchResult, TermsMatchingStrategy,
|
score_details, AscDesc, Criterion, Member, Search, SearchResult, TermsMatchingStrategy,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
@@ -28,7 +28,7 @@ fn create_index() -> TempIndex {
|
|||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
||||||
s.set_ranking_rules(vec![RankingRule::Sort]);
|
s.set_criteria(vec![Criterion::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ fn test_redacted() {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_displayed_fields(vec!["text".to_owned(), "vague".to_owned()]);
|
s.set_displayed_fields(vec!["text".to_owned(), "vague".to_owned()]);
|
||||||
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
s.set_sortable_fields(hashset! { S("rank"), S("vague"), S("letter") });
|
||||||
s.set_ranking_rules(vec![RankingRule::Sort]);
|
s.set_criteria(vec![Criterion::Sort]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -31,7 +31,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Words]);
|
s.set_criteria(vec![Criterion::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ fn test_typo_ranking_rule_not_preceded_by_words_ranking_rule() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Typo]);
|
s.set_criteria(vec![Criterion::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -495,7 +495,7 @@ fn test_typo_ranking_rule_not_preceded_by_words_ranking_rule() {
|
|||||||
|
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Typo]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -540,7 +540,7 @@ fn test_typo_bucketing() {
|
|||||||
drop(txn);
|
drop(txn);
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Typo]);
|
s.set_criteria(vec![Criterion::Typo]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let txn = index.read_txn().unwrap();
|
let txn = index.read_txn().unwrap();
|
||||||
@@ -589,7 +589,7 @@ fn test_typo_synonyms() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Typo]);
|
s.set_criteria(vec![Criterion::Typo]);
|
||||||
|
|
||||||
let mut synonyms = BTreeMap::new();
|
let mut synonyms = BTreeMap::new();
|
||||||
synonyms.insert("lackadaisical".to_owned(), vec!["lazy".to_owned()]);
|
synonyms.insert("lackadaisical".to_owned(), vec!["lazy".to_owned()]);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ because the typo ranking rule before it only used the derivation `beautiful`.
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -26,11 +26,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![
|
s.set_criteria(vec![Criterion::Words, Criterion::Typo, Criterion::Proximity]);
|
||||||
RankingRule::Words,
|
|
||||||
RankingRule::Typo,
|
|
||||||
RankingRule::Proximity,
|
|
||||||
]);
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ account by the proximity ranking rule.
|
|||||||
|
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::search::new::tests::collect_field_values;
|
use crate::search::new::tests::collect_field_values;
|
||||||
use crate::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use crate::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
|
|
||||||
fn create_index() -> TempIndex {
|
fn create_index() -> TempIndex {
|
||||||
let index = TempIndex::new();
|
let index = TempIndex::new();
|
||||||
@@ -23,7 +23,7 @@ fn create_index() -> TempIndex {
|
|||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_primary_key("id".to_owned());
|
s.set_primary_key("id".to_owned());
|
||||||
s.set_searchable_fields(vec!["text".to_owned()]);
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||||||
s.set_ranking_rules(vec![RankingRule::Words]);
|
s.set_criteria(vec![Criterion::Words]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ fn test_words_proximity_tms_last_simple() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ fn test_words_proximity_tms_last_phrase() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -416,7 +416,7 @@ fn test_words_tms_all() {
|
|||||||
let index = create_index();
|
let index = create_index();
|
||||||
index
|
index
|
||||||
.update_settings(|s| {
|
.update_settings(|s| {
|
||||||
s.set_ranking_rules(vec![RankingRule::Words, RankingRule::Proximity]);
|
s.set_criteria(vec![Criterion::Words, Criterion::Proximity]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use time::OffsetDateTime;
|
|||||||
|
|
||||||
use super::index_documents::{IndexDocumentsConfig, Transform};
|
use super::index_documents::{IndexDocumentsConfig, Transform};
|
||||||
use super::IndexerConfig;
|
use super::IndexerConfig;
|
||||||
|
use crate::criterion::Criterion;
|
||||||
use crate::error::UserError;
|
use crate::error::UserError;
|
||||||
use crate::index::{DEFAULT_MIN_WORD_LEN_ONE_TYPO, DEFAULT_MIN_WORD_LEN_TWO_TYPOS};
|
use crate::index::{DEFAULT_MIN_WORD_LEN_ONE_TYPO, DEFAULT_MIN_WORD_LEN_TWO_TYPOS};
|
||||||
use crate::ranking_rule::RankingRule;
|
|
||||||
use crate::update::index_documents::IndexDocumentsMethod;
|
use crate::update::index_documents::IndexDocumentsMethod;
|
||||||
use crate::update::{IndexDocuments, UpdateIndexingStep};
|
use crate::update::{IndexDocuments, UpdateIndexingStep};
|
||||||
use crate::{FieldsIdsMap, Index, OrderBy, Result};
|
use crate::{FieldsIdsMap, Index, OrderBy, Result};
|
||||||
@@ -110,7 +110,7 @@ pub struct Settings<'a, 't, 'u, 'i> {
|
|||||||
displayed_fields: Setting<Vec<String>>,
|
displayed_fields: Setting<Vec<String>>,
|
||||||
filterable_fields: Setting<HashSet<String>>,
|
filterable_fields: Setting<HashSet<String>>,
|
||||||
sortable_fields: Setting<HashSet<String>>,
|
sortable_fields: Setting<HashSet<String>>,
|
||||||
ranking_rules: Setting<Vec<RankingRule>>,
|
criteria: Setting<Vec<Criterion>>,
|
||||||
stop_words: Setting<BTreeSet<String>>,
|
stop_words: Setting<BTreeSet<String>>,
|
||||||
non_separator_tokens: Setting<BTreeSet<String>>,
|
non_separator_tokens: Setting<BTreeSet<String>>,
|
||||||
separator_tokens: Setting<BTreeSet<String>>,
|
separator_tokens: Setting<BTreeSet<String>>,
|
||||||
@@ -142,7 +142,7 @@ impl<'a, 't, 'u, 'i> Settings<'a, 't, 'u, 'i> {
|
|||||||
displayed_fields: Setting::NotSet,
|
displayed_fields: Setting::NotSet,
|
||||||
filterable_fields: Setting::NotSet,
|
filterable_fields: Setting::NotSet,
|
||||||
sortable_fields: Setting::NotSet,
|
sortable_fields: Setting::NotSet,
|
||||||
ranking_rules: Setting::NotSet,
|
criteria: Setting::NotSet,
|
||||||
stop_words: Setting::NotSet,
|
stop_words: Setting::NotSet,
|
||||||
non_separator_tokens: Setting::NotSet,
|
non_separator_tokens: Setting::NotSet,
|
||||||
separator_tokens: Setting::NotSet,
|
separator_tokens: Setting::NotSet,
|
||||||
@@ -194,12 +194,12 @@ impl<'a, 't, 'u, 'i> Settings<'a, 't, 'u, 'i> {
|
|||||||
self.sortable_fields = Setting::Reset;
|
self.sortable_fields = Setting::Reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_ranking_rules(&mut self) {
|
pub fn reset_criteria(&mut self) {
|
||||||
self.ranking_rules = Setting::Reset;
|
self.criteria = Setting::Reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ranking_rules(&mut self, ranking_rules: Vec<RankingRule>) {
|
pub fn set_criteria(&mut self, criteria: Vec<Criterion>) {
|
||||||
self.ranking_rules = Setting::Set(ranking_rules);
|
self.criteria = Setting::Set(criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_stop_words(&mut self) {
|
pub fn reset_stop_words(&mut self) {
|
||||||
@@ -696,7 +696,7 @@ impl<'a, 't, 'u, 'i> Settings<'a, 't, 'u, 'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_criteria(&mut self) -> Result<()> {
|
fn update_criteria(&mut self) -> Result<()> {
|
||||||
match &self.ranking_rules {
|
match &self.criteria {
|
||||||
Setting::Set(criteria) => {
|
Setting::Set(criteria) => {
|
||||||
self.index.put_criteria(self.wtxn, criteria)?;
|
self.index.put_criteria(self.wtxn, criteria)?;
|
||||||
}
|
}
|
||||||
@@ -924,7 +924,7 @@ mod tests {
|
|||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::index::tests::TempIndex;
|
use crate::index::tests::TempIndex;
|
||||||
use crate::update::{ClearDocuments, DeleteDocuments};
|
use crate::update::{ClearDocuments, DeleteDocuments};
|
||||||
use crate::{Filter, RankingRule, SearchResult};
|
use crate::{Criterion, Filter, SearchResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_and_reset_searchable_fields() {
|
fn set_and_reset_searchable_fields() {
|
||||||
@@ -1167,7 +1167,7 @@ mod tests {
|
|||||||
index
|
index
|
||||||
.update_settings(|settings| {
|
.update_settings(|settings| {
|
||||||
settings.set_displayed_fields(vec![S("name")]);
|
settings.set_displayed_fields(vec![S("name")]);
|
||||||
settings.set_ranking_rules(vec![RankingRule::Asc("age".to_owned())]);
|
settings.set_criteria(vec![Criterion::Asc("age".to_owned())]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1473,7 +1473,7 @@ mod tests {
|
|||||||
.update_settings(|settings| {
|
.update_settings(|settings| {
|
||||||
settings.set_displayed_fields(vec!["hello".to_string()]);
|
settings.set_displayed_fields(vec!["hello".to_string()]);
|
||||||
settings.set_filterable_fields(hashset! { S("age"), S("toto") });
|
settings.set_filterable_fields(hashset! { S("age"), S("toto") });
|
||||||
settings.set_ranking_rules(vec![RankingRule::Asc(S("toto"))]);
|
settings.set_criteria(vec![Criterion::Asc(S("toto"))]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1482,7 +1482,7 @@ mod tests {
|
|||||||
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
||||||
// since no documents have been pushed the primary key is still unset
|
// since no documents have been pushed the primary key is still unset
|
||||||
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
||||||
assert_eq!(vec![RankingRule::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
assert_eq!(vec![Criterion::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
||||||
drop(rtxn);
|
drop(rtxn);
|
||||||
|
|
||||||
// We set toto and age as searchable to force reordering of the fields
|
// We set toto and age as searchable to force reordering of the fields
|
||||||
@@ -1495,7 +1495,7 @@ mod tests {
|
|||||||
let rtxn = index.read_txn().unwrap();
|
let rtxn = index.read_txn().unwrap();
|
||||||
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
assert_eq!(&["hello"][..], index.displayed_fields(&rtxn).unwrap().unwrap());
|
||||||
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
assert!(index.primary_key(&rtxn).unwrap().is_none());
|
||||||
assert_eq!(vec![RankingRule::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
assert_eq!(vec![Criterion::Asc("toto".to_string())], index.criteria(&rtxn).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1507,7 +1507,7 @@ mod tests {
|
|||||||
.update_settings(|settings| {
|
.update_settings(|settings| {
|
||||||
settings.set_displayed_fields(vec!["hello".to_string()]);
|
settings.set_displayed_fields(vec!["hello".to_string()]);
|
||||||
// It is only Asc(toto), there is a facet database but it is denied to filter with toto.
|
// It is only Asc(toto), there is a facet database but it is denied to filter with toto.
|
||||||
settings.set_ranking_rules(vec![RankingRule::Asc(S("toto"))]);
|
settings.set_criteria(vec![Criterion::Asc(S("toto"))]);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1715,7 +1715,7 @@ mod tests {
|
|||||||
displayed_fields,
|
displayed_fields,
|
||||||
filterable_fields,
|
filterable_fields,
|
||||||
sortable_fields,
|
sortable_fields,
|
||||||
ranking_rules: criteria,
|
criteria,
|
||||||
stop_words,
|
stop_words,
|
||||||
non_separator_tokens,
|
non_separator_tokens,
|
||||||
separator_tokens,
|
separator_tokens,
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
use big_s::S;
|
use big_s::S;
|
||||||
use milli::update::Settings;
|
use milli::update::Settings;
|
||||||
use milli::{RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use milli::{Criterion, Search, SearchResult, TermsMatchingStrategy};
|
||||||
use RankingRule::*;
|
use Criterion::*;
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use either::{Either, Left, Right};
|
use either::{Either, Left, Right};
|
||||||
use milli::{Filter, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use milli::{Criterion, Filter, Search, SearchResult, TermsMatchingStrategy};
|
||||||
use RankingRule::*;
|
use Criterion::*;
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use heed::EnvOpenOptions;
|
|||||||
use maplit::{btreemap, hashset};
|
use maplit::{btreemap, hashset};
|
||||||
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use milli::{AscDesc, DocumentId, Index, Member, Object, RankingRule, TermsMatchingStrategy};
|
use milli::{AscDesc, Criterion, DocumentId, Index, Member, Object, TermsMatchingStrategy};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use slice_group_by::GroupBy;
|
use slice_group_by::GroupBy;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ pub const EXTERNAL_DOCUMENTS_IDS: &[&str; 17] =
|
|||||||
|
|
||||||
pub const CONTENT: &str = include_str!("../assets/test_set.ndjson");
|
pub const CONTENT: &str = include_str!("../assets/test_set.ndjson");
|
||||||
|
|
||||||
pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
pub fn setup_search_index_with_criteria(criteria: &[Criterion]) -> Index {
|
||||||
let path = tempfile::tempdir().unwrap();
|
let path = tempfile::tempdir().unwrap();
|
||||||
let mut options = EnvOpenOptions::new();
|
let mut options = EnvOpenOptions::new();
|
||||||
options.map_size(10 * 1024 * 1024); // 10 MB
|
options.map_size(10 * 1024 * 1024); // 10 MB
|
||||||
@@ -38,7 +38,7 @@ pub fn setup_search_index_with_criteria(criteria: &[RankingRule]) -> Index {
|
|||||||
|
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
|
|
||||||
builder.set_ranking_rules(criteria.to_vec());
|
builder.set_criteria(criteria.to_vec());
|
||||||
builder.set_filterable_fields(hashset! {
|
builder.set_filterable_fields(hashset! {
|
||||||
S("tag"),
|
S("tag"),
|
||||||
S("asc_desc_rank"),
|
S("asc_desc_rank"),
|
||||||
@@ -95,7 +95,7 @@ pub fn internal_to_external_ids(index: &Index, internal_ids: &[DocumentId]) -> V
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expected_order(
|
pub fn expected_order(
|
||||||
criteria: &[RankingRule],
|
criteria: &[Criterion],
|
||||||
optional_words: TermsMatchingStrategy,
|
optional_words: TermsMatchingStrategy,
|
||||||
sort_by: &[AscDesc],
|
sort_by: &[AscDesc],
|
||||||
) -> Vec<TestDocument> {
|
) -> Vec<TestDocument> {
|
||||||
@@ -107,56 +107,47 @@ pub fn expected_order(
|
|||||||
let mut new_groups = Vec::new();
|
let mut new_groups = Vec::new();
|
||||||
for group in groups.iter_mut() {
|
for group in groups.iter_mut() {
|
||||||
match criterion {
|
match criterion {
|
||||||
RankingRule::Attribute => {
|
Criterion::Attribute => {
|
||||||
group.sort_by_key(|d| d.attribute_rank);
|
group.sort_by_key(|d| d.attribute_rank);
|
||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.attribute_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.attribute_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Exactness => {
|
Criterion::Exactness => {
|
||||||
group.sort_by_key(|d| d.exact_rank);
|
group.sort_by_key(|d| d.exact_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.exact_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.exact_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Proximity => {
|
Criterion::Proximity => {
|
||||||
group.sort_by_key(|d| d.proximity_rank);
|
group.sort_by_key(|d| d.proximity_rank);
|
||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.proximity_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.proximity_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Sort if sort_by == [AscDesc::Asc(Member::Field(S("tag")))] => {
|
Criterion::Sort if sort_by == [AscDesc::Asc(Member::Field(S("tag")))] => {
|
||||||
group.sort_by_key(|d| d.sort_by_rank);
|
group.sort_by_key(|d| d.sort_by_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Sort if sort_by == [AscDesc::Desc(Member::Field(S("tag")))] => {
|
Criterion::Sort if sort_by == [AscDesc::Desc(Member::Field(S("tag")))] => {
|
||||||
group.sort_by_key(|d| Reverse(d.sort_by_rank));
|
group.sort_by_key(|d| Reverse(d.sort_by_rank));
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.sort_by_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Typo => {
|
Criterion::Typo => {
|
||||||
group.sort_by_key(|d| d.typo_rank);
|
group.sort_by_key(|d| d.typo_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.typo_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.typo_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Words => {
|
Criterion::Words => {
|
||||||
group.sort_by_key(|d| d.word_rank);
|
group.sort_by_key(|d| d.word_rank);
|
||||||
new_groups.extend(group.linear_group_by_key(|d| d.word_rank).map(Vec::from));
|
new_groups.extend(group.linear_group_by_key(|d| d.word_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Asc(field_name) if field_name == "asc_desc_rank" => {
|
Criterion::Asc(field_name) if field_name == "asc_desc_rank" => {
|
||||||
group.sort_by_key(|d| d.asc_desc_rank);
|
group.sort_by_key(|d| d.asc_desc_rank);
|
||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.asc_desc_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.asc_desc_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::Desc(field_name) if field_name == "asc_desc_rank" => {
|
Criterion::Desc(field_name) if field_name == "asc_desc_rank" => {
|
||||||
group.sort_by_key(|d| Reverse(d.asc_desc_rank));
|
group.sort_by_key(|d| Reverse(d.asc_desc_rank));
|
||||||
new_groups
|
new_groups
|
||||||
.extend(group.linear_group_by_key(|d| d.asc_desc_rank).map(Vec::from));
|
.extend(group.linear_group_by_key(|d| d.asc_desc_rank).map(Vec::from));
|
||||||
}
|
}
|
||||||
RankingRule::FilterBoosting(filter) => {
|
Criterion::Asc(_) | Criterion::Desc(_) | Criterion::Sort => {
|
||||||
// move the matching documents first, then the ones that don't match
|
|
||||||
group.sort_by_key(|d| if execute_filter(filter, d).is_some() { 0 } else { 1 });
|
|
||||||
new_groups.extend(
|
|
||||||
group
|
|
||||||
.linear_group_by_key(|d| execute_filter(filter, d).is_some())
|
|
||||||
.map(Vec::from),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
RankingRule::Asc(_) | RankingRule::Desc(_) | RankingRule::Sort => {
|
|
||||||
new_groups.push(group.clone())
|
new_groups.push(group.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use milli::update::{IndexerConfig, Settings};
|
use milli::update::{IndexerConfig, Settings};
|
||||||
use milli::{Index, RankingRule, Search, TermsMatchingStrategy};
|
use milli::{Criterion, Index, Search, TermsMatchingStrategy};
|
||||||
|
|
||||||
use crate::search::RankingRule::{Attribute, Exactness, Proximity};
|
use crate::search::Criterion::{Attribute, Exactness, Proximity};
|
||||||
|
|
||||||
fn set_stop_words(index: &Index, stop_words: &[&str]) {
|
fn set_stop_words(index: &Index, stop_words: &[&str]) {
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
@@ -14,7 +14,7 @@ fn set_stop_words(index: &Index, stop_words: &[&str]) {
|
|||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_phrase_search_with_stop_words_given_criteria(criteria: &[RankingRule]) {
|
fn test_phrase_search_with_stop_words_given_criteria(criteria: &[Criterion]) {
|
||||||
let index = super::setup_search_index_with_criteria(criteria);
|
let index = super::setup_search_index_with_criteria(criteria);
|
||||||
|
|
||||||
// Add stop_words
|
// Add stop_words
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ use itertools::Itertools;
|
|||||||
use maplit::hashset;
|
use maplit::hashset;
|
||||||
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
use milli::documents::{DocumentsBatchBuilder, DocumentsBatchReader};
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use milli::{AscDesc, Index, Member, RankingRule, Search, SearchResult, TermsMatchingStrategy};
|
use milli::{AscDesc, Criterion, Index, Member, Search, SearchResult, TermsMatchingStrategy};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use RankingRule::*;
|
use Criterion::*;
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ test_criterion!(
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn criteria_mixup() {
|
fn criteria_mixup() {
|
||||||
use RankingRule::*;
|
use Criterion::*;
|
||||||
let index = search::setup_search_index_with_criteria(&[
|
let index = search::setup_search_index_with_criteria(&[
|
||||||
Words,
|
Words,
|
||||||
Attribute,
|
Attribute,
|
||||||
@@ -233,7 +233,7 @@ fn criteria_mixup() {
|
|||||||
//update criteria
|
//update criteria
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
builder.set_ranking_rules(criteria.clone());
|
builder.set_criteria(criteria.clone());
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(|_| (), || false).unwrap();
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ fn criteria_ascdesc() {
|
|||||||
|
|
||||||
let mut wtxn = index.write_txn().unwrap();
|
let mut wtxn = index.write_txn().unwrap();
|
||||||
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
let mut builder = Settings::new(&mut wtxn, &index, &config);
|
||||||
builder.set_ranking_rules(vec![criterion.clone()]);
|
builder.set_criteria(vec![criterion.clone()]);
|
||||||
builder.execute(|_| (), || false).unwrap();
|
builder.execute(|_| (), || false).unwrap();
|
||||||
wtxn.commit().unwrap();
|
wtxn.commit().unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use big_s::S;
|
use big_s::S;
|
||||||
use milli::RankingRule::{Attribute, Exactness, Proximity, Typo, Words};
|
use milli::Criterion::{Attribute, Exactness, Proximity, Typo, Words};
|
||||||
use milli::{AscDesc, Error, Member, Search, TermsMatchingStrategy, UserError};
|
use milli::{AscDesc, Error, Member, Search, TermsMatchingStrategy, UserError};
|
||||||
|
|
||||||
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
use heed::EnvOpenOptions;
|
use heed::EnvOpenOptions;
|
||||||
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
use milli::update::{IndexDocuments, IndexDocumentsConfig, IndexerConfig, Settings};
|
||||||
use milli::{Index, RankingRule, Search, TermsMatchingStrategy};
|
use milli::{Criterion, Index, Search, TermsMatchingStrategy};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use RankingRule::*;
|
use Criterion::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_typo_tolerance_one_typo() {
|
fn test_typo_tolerance_one_typo() {
|
||||||
|
|||||||
Reference in New Issue
Block a user