diff --git a/CHANGELOG.md b/CHANGELOG.md index f3975206c..c69f86bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Tantivy 0.14.0 - Large API Change in the Directory API. Tantivy used to assume that all files could be somehow memory mapped. After this change, Directory return a `FileSlice` that can be reduced and eventually read into an `OwnedBytes` object. Long and blocking io operation are still required by they do not span over the entire file. - Added support for Brotli compression in the DocStore. (@ppodolsky) - Added helper for building intersections and unions in BooleanQuery (@guilload) +- Bugfix in `Query::explain` Tantivy 0.13.2 =================== diff --git a/src/query/boolean_query/mod.rs b/src/query/boolean_query/mod.rs index 3a7b74eb0..d8a56f07b 100644 --- a/src/query/boolean_query/mod.rs +++ b/src/query/boolean_query/mod.rs @@ -33,13 +33,11 @@ mod tests { { // writing the segment let mut index_writer = index.writer_for_tests().unwrap(); - { - index_writer.add_document(doc!(text_field => "a b c")); - index_writer.add_document(doc!(text_field => "a c")); - index_writer.add_document(doc!(text_field => "b c")); - index_writer.add_document(doc!(text_field => "a b c d")); - index_writer.add_document(doc!(text_field => "d")); - } + index_writer.add_document(doc!(text_field => "a b c")); + index_writer.add_document(doc!(text_field => "a c")); + index_writer.add_document(doc!(text_field => "b c")); + index_writer.add_document(doc!(text_field => "a b c d")); + index_writer.add_document(doc!(text_field => "d")); assert!(index_writer.commit().is_ok()); } (index, text_field) @@ -290,4 +288,29 @@ mod tests { assert_nearly_equals!(scores[1], 0.84699446); } } + + #[test] + pub fn test_explain() -> crate::Result<()> { + let mut schema_builder = Schema::builder(); + let text = schema_builder.add_text_field("text", STRING); + let schema = schema_builder.build(); + let index = Index::create_in_ram(schema); + let mut index_writer = index.writer_with_num_threads(1, 5_000_000)?; + index_writer.add_document(doc!(text=>"a")); + index_writer.add_document(doc!(text=>"b")); + index_writer.commit()?; + let searcher = index.reader()?.searcher(); + let term_a: Box = Box::new(TermQuery::new( + Term::from_field_text(text, "a"), + IndexRecordOption::Basic, + )); + let term_b: Box = Box::new(TermQuery::new( + Term::from_field_text(text, "b"), + IndexRecordOption::Basic, + )); + let query = BooleanQuery::from(vec![(Occur::Should, term_a), (Occur::Should, term_b)]); + let explanation = query.explain(&searcher, DocAddress(0, 0u32))?; + assert_nearly_equals!(explanation.value(), 0.6931472f32); + Ok(()) + } } diff --git a/src/query/boost_query.rs b/src/query/boost_query.rs index 825399c08..750b5f5de 100644 --- a/src/query/boost_query.rs +++ b/src/query/boost_query.rs @@ -153,7 +153,7 @@ mod tests { let explanation = query.explain(&searcher, DocAddress(0, 0u32)).unwrap(); assert_eq!( explanation.to_pretty_json(), - "{\n \"value\": 0.2,\n \"description\": \"Boost x0.2 of ...\",\n \"details\": [\n {\n \"value\": 1.0,\n \"description\": \"AllQuery\"\n }\n ]\n}" + "{\n \"value\": 0.2,\n \"description\": \"Boost x0.2 of ...\",\n \"details\": [\n {\n \"value\": 1.0,\n \"description\": \"AllQuery\",\n \"context\": []\n }\n ],\n \"context\": []\n}" ) } } diff --git a/src/query/explanation.rs b/src/query/explanation.rs index 4f2553941..b065afce4 100644 --- a/src/query/explanation.rs +++ b/src/query/explanation.rs @@ -17,6 +17,7 @@ pub struct Explanation { description: String, #[serde(skip_serializing_if = "Vec::is_empty")] details: Vec, + context: Vec, } impl fmt::Debug for Explanation { @@ -32,6 +33,7 @@ impl Explanation { value, description: description.to_string(), details: vec![], + context: vec![], } } @@ -47,6 +49,11 @@ impl Explanation { self.details.push(child_explanation); } + /// Adds some extra context to the explanation. + pub fn add_context(&mut self, context: String) { + self.context.push(context); + } + /// Shortcut for `self.details.push(Explanation::new(name, value));` pub fn add_const(&mut self, name: T, value: Score) { self.details.push(Explanation::new(name, value)); diff --git a/src/query/term_query/mod.rs b/src/query/term_query/mod.rs index 6b105a644..09a3a992c 100644 --- a/src/query/term_query/mod.rs +++ b/src/query/term_query/mod.rs @@ -9,12 +9,12 @@ pub use self::term_weight::TermWeight; #[cfg(test)] mod tests { - use crate::assert_nearly_equals; use crate::collector::TopDocs; use crate::docset::DocSet; use crate::postings::compression::COMPRESSION_BLOCK_SIZE; use crate::query::{Query, QueryParser, Scorer, TermQuery}; use crate::schema::{Field, IndexRecordOption, Schema, STRING, TEXT}; + use crate::{assert_nearly_equals, DocAddress}; use crate::{Index, Term, TERMINATED}; #[test] @@ -179,4 +179,40 @@ mod tests { "TermQuery(Term(field=1,bytes=[104, 101, 108, 108, 111]))" ); } + + #[test] + fn test_term_query_explain() -> crate::Result<()> { + let mut schema_builder = Schema::builder(); + let text_field = schema_builder.add_text_field("text", TEXT); + let schema = schema_builder.build(); + let index = Index::create_in_ram(schema); + let mut index_writer = index.writer_for_tests().unwrap(); + index_writer.add_document(doc!(text_field=>"b")); + index_writer.add_document(doc!(text_field=>"a")); + index_writer.add_document(doc!(text_field=>"a")); + index_writer.add_document(doc!(text_field=>"b")); + index_writer.commit()?; + let term_a = Term::from_field_text(text_field, "a"); + let term_query = TermQuery::new(term_a, IndexRecordOption::Basic); + let searcher = index.reader()?.searcher(); + { + let explanation = term_query.explain(&searcher, DocAddress(0u32, 1u32))?; + assert_nearly_equals!(explanation.value(), 0.6931472f32); + } + { + let explanation_err = term_query.explain(&searcher, DocAddress(0u32, 0u32)); + assert!(matches!( + explanation_err, + Err(crate::TantivyError::InvalidArgument(_msg)) + )); + } + { + let explanation_err = term_query.explain(&searcher, DocAddress(0u32, 3u32)); + assert!(matches!( + explanation_err, + Err(crate::TantivyError::InvalidArgument(_msg)) + )); + } + Ok(()) + } } diff --git a/src/query/term_query/term_weight.rs b/src/query/term_query/term_weight.rs index 339cf2033..936c6e7ca 100644 --- a/src/query/term_query/term_weight.rs +++ b/src/query/term_query/term_weight.rs @@ -25,10 +25,16 @@ impl Weight for TermWeight { fn explain(&self, reader: &SegmentReader, doc: DocId) -> crate::Result { let mut scorer = self.specialized_scorer(reader, 1.0)?; - if scorer.seek(doc) != doc { + if scorer.doc() > doc || scorer.seek(doc) != doc { return Err(does_not_match(doc)); } - Ok(scorer.explain()) + let mut explanation = scorer.explain(); + explanation.add_context(format!( + "Term ={:?}:{:?}", + self.term.field(), + self.term.value_bytes() + )); + Ok(explanation) } fn count(&self, reader: &SegmentReader) -> crate::Result {