Fixes a bug in TermQuery::explain.

Closes #915
This commit is contained in:
Paul Masurel
2020-10-22 14:22:29 +09:00
parent 2c56f4b583
commit 730ccefffb
6 changed files with 84 additions and 11 deletions

View File

@@ -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
===================

View File

@@ -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<dyn Query> = Box::new(TermQuery::new(
Term::from_field_text(text, "a"),
IndexRecordOption::Basic,
));
let term_b: Box<dyn Query> = 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(())
}
}

View File

@@ -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}"
)
}
}

View File

@@ -17,6 +17,7 @@ pub struct Explanation {
description: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
details: Vec<Explanation>,
context: Vec<String>,
}
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<T: ToString>(&mut self, name: T, value: Score) {
self.details.push(Explanation::new(name, value));

View File

@@ -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(())
}
}

View File

@@ -25,10 +25,16 @@ impl Weight for TermWeight {
fn explain(&self, reader: &SegmentReader, doc: DocId) -> crate::Result<Explanation> {
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<u32> {