diff --git a/src/collector/chained_collector.rs b/src/collector/chained_collector.rs index a0b73e4b1..e52164f6a 100644 --- a/src/collector/chained_collector.rs +++ b/src/collector/chained_collector.rs @@ -5,6 +5,9 @@ use std::io; use ScoredDoc; +/// Collector that does nothing. +/// This is used in the chain Collector and will hopefully +/// be optimized away by the compiler. pub struct DoNothingCollector; impl Collector for DoNothingCollector { #[inline] @@ -15,24 +18,20 @@ impl Collector for DoNothingCollector { fn collect(&mut self, _: ScoredDoc) {} } +/// Zero-cost abstraction used to collect on multiple collectors. +/// This contraption is only usable if the type of your collectors +/// are known at compile time. pub struct ChainedCollector { left: Left, right: Right } impl ChainedCollector { - - pub fn start() -> ChainedCollector { - ChainedCollector { - left: DoNothingCollector, - right: DoNothingCollector - } - } - - pub fn push(self, new_collector: &mut C) -> ChainedCollector> { + /// Adds a collector + pub fn push(self, new_collector: &mut C) -> ChainedCollector { ChainedCollector { left: self, - right: MutRefCollector(new_collector), + right: new_collector, } } } @@ -50,6 +49,7 @@ impl Collector for ChainedCollector ChainedCollector { ChainedCollector { left: DoNothingCollector, @@ -57,17 +57,6 @@ pub fn chain() -> ChainedCollector { } } -pub struct MutRefCollector<'a, C: Collector + 'a>(&'a mut C); - -impl<'a, C: Collector> Collector for MutRefCollector<'a, C> { - fn set_segment(&mut self, segment_local_id: SegmentLocalId, segment: &SegmentReader) -> io::Result<()> { - self.0.set_segment(segment_local_id, segment) - } - - fn collect(&mut self, scored_doc: ScoredDoc) { - self.0.collect(scored_doc) - } -} #[cfg(test)] mod tests { diff --git a/src/collector/collector.rs b/src/collector/collector.rs deleted file mode 100644 index 58a653a50..000000000 --- a/src/collector/collector.rs +++ /dev/null @@ -1,9 +0,0 @@ -use SegmentReader; -use SegmentLocalId; -use std::io; -use ScoredDoc; - -pub trait Collector { - fn set_segment(&mut self, segment_local_id: SegmentLocalId, segment: &SegmentReader) -> io::Result<()>; - fn collect(&mut self, scored_doc: ScoredDoc); -} \ No newline at end of file diff --git a/src/collector/count_collector.rs b/src/collector/count_collector.rs index 4cbd709e5..9eaf74165 100644 --- a/src/collector/count_collector.rs +++ b/src/collector/count_collector.rs @@ -4,15 +4,15 @@ use ScoredDoc; use SegmentReader; use SegmentLocalId; -// `CountCollector` collector only counts how many -// document are matching the query. +/// `CountCollector` collector only counts how many +/// document are matching the query. pub struct CountCollector { count: usize, } impl CountCollector { - // Returns the count of document that where - // collected. + /// Returns the count of document that where + /// collected. pub fn count(&self,) -> usize { self.count } diff --git a/src/collector/mod.rs b/src/collector/mod.rs index 792ddc908..de4092738 100644 --- a/src/collector/mod.rs +++ b/src/collector/mod.rs @@ -1,15 +1,8 @@ - -use DocId; use SegmentReader; use SegmentLocalId; -use fastfield::U32FastFieldReader; -use schema::Field; use ScoredDoc; use std::io; -mod collector; -pub use self::collector::Collector; - mod count_collector; pub use self::count_collector::CountCollector; @@ -22,83 +15,147 @@ pub use self::top_collector::TopCollector; mod chained_collector; pub use self::chained_collector::chain; -pub struct TestCollector { - offset: DocId, - segment_max_doc: DocId, - docs: Vec, +/// Collectors are in charge of collecting and retaining relevant +/// information from the document found and scored by the query. +/// +/// +/// For instance, +/// - keeping track of the top 10 best documents +/// - computing a break down over a fast field +/// - computing the number of documents matching the query +/// +/// +/// Queries are in charge of pushing the `DocSet` to the collector. +/// +/// As they work on multiple segment, they first inform +/// the collector of a change in segment and then +/// call the collect method to push document to the collector. +/// +/// Temporally, our collector will receive calls +/// - `.set_segment(0, segment_reader_0)` +/// - `.collect(doc0_of_segment_0)` +/// - `.collect(...)` +/// - `.collect(last_doc_of_segment_0)` +/// - `.set_segment(1, segment_reader_1)` +/// - `.collect(doc0_of_segment_1)` +/// - `.collect(...)` +/// - `.collect(last_doc_of_segment_1)` +/// - `...` +/// - `.collect(last_doc_of_last_segment)` +/// +/// Segments are not guaranteed to be visited in any specific order. +pub trait Collector { + /// `set_segment` is called before starting enumerating + /// on this segment. + fn set_segment(&mut self, segment_local_id: SegmentLocalId, segment: &SegmentReader) -> io::Result<()>; + /// The query pushes scored document to the collector via this method. + fn collect(&mut self, scored_doc: ScoredDoc); } -impl TestCollector { - pub fn docs(self,) -> Vec { - self.docs + +impl<'a, C: Collector> Collector for &'a mut C { + fn set_segment(&mut self, segment_local_id: SegmentLocalId, segment: &SegmentReader) -> io::Result<()> { + (*self).set_segment(segment_local_id, segment) } -} - -impl Default for TestCollector { - fn default() -> TestCollector { - TestCollector { - docs: Vec::new(), - offset: 0, - segment_max_doc: 0, - } - } -} - -impl Collector for TestCollector { - - fn set_segment(&mut self, _: SegmentLocalId, reader: &SegmentReader) -> io::Result<()> { - self.offset += self.segment_max_doc; - self.segment_max_doc = reader.max_doc(); - Ok(()) - } - + /// The query pushes scored document to the collector via this method. fn collect(&mut self, scored_doc: ScoredDoc) { - self.docs.push(scored_doc.doc() + self.offset); + (*self).collect(scored_doc); } } -pub struct FastFieldTestCollector { - vals: Vec, - field: Field, - ff_reader: Option, -} - -impl FastFieldTestCollector { - pub fn for_field(field: Field) -> FastFieldTestCollector { - FastFieldTestCollector { - vals: Vec::new(), - field: field, - ff_reader: None, - } - } - - pub fn vals(&self,) -> &Vec { - &self.vals - } -} - -impl Collector for FastFieldTestCollector { - - fn set_segment(&mut self, _: SegmentLocalId, reader: &SegmentReader) -> io::Result<()> { - self.ff_reader = Some(try!(reader.get_fast_field_reader(self.field))); - Ok(()) - } - - fn collect(&mut self, scored_doc: ScoredDoc) { - let val = self.ff_reader.as_ref().unwrap().get(scored_doc.doc()); - self.vals.push(val); - } -} - - - #[cfg(test)] -mod tests { +pub mod tests { use super::*; use test::Bencher; use ScoredDoc; + use DocId; + use core::SegmentReader; + use std::io; + use SegmentLocalId; + use fastfield::U32FastFieldReader; + use schema::Field; + + /// Stores all of the doc ids. + /// This collector is only used for tests. + /// It is unusable in practise, as it does not store + /// the segment ordinals + pub struct TestCollector { + offset: DocId, + segment_max_doc: DocId, + docs: Vec, + } + + impl TestCollector { + /// Return the exhalist of documents. + pub fn docs(self,) -> Vec { + self.docs + } + } + + impl Default for TestCollector { + fn default() -> TestCollector { + TestCollector { + docs: Vec::new(), + offset: 0, + segment_max_doc: 0, + } + } + } + + impl Collector for TestCollector { + + fn set_segment(&mut self, _: SegmentLocalId, reader: &SegmentReader) -> io::Result<()> { + self.offset += self.segment_max_doc; + self.segment_max_doc = reader.max_doc(); + Ok(()) + } + + fn collect(&mut self, scored_doc: ScoredDoc) { + self.docs.push(scored_doc.doc() + self.offset); + } + } + + + + + /// Collects in order all of the fast field for all of the + /// doc of the `DocSet` + /// + /// This collector is essentially useful for tests. + pub struct FastFieldTestCollector { + vals: Vec, + field: Field, + ff_reader: Option, + } + + impl FastFieldTestCollector { + pub fn for_field(field: Field) -> FastFieldTestCollector { + FastFieldTestCollector { + vals: Vec::new(), + field: field, + ff_reader: None, + } + } + + pub fn vals(&self,) -> &Vec { + &self.vals + } + } + + impl Collector for FastFieldTestCollector { + fn set_segment(&mut self, _: SegmentLocalId, reader: &SegmentReader) -> io::Result<()> { + self.ff_reader = Some(try!(reader.get_fast_field_reader(self.field))); + Ok(()) + } + + fn collect(&mut self, scored_doc: ScoredDoc) { + let val = self.ff_reader.as_ref().unwrap().get(scored_doc.doc()); + self.vals.push(val); + } + } + #[bench] fn build_collector(b: &mut Bencher) { diff --git a/src/collector/multi_collector.rs b/src/collector/multi_collector.rs index 1a393d5a4..092547c78 100644 --- a/src/collector/multi_collector.rs +++ b/src/collector/multi_collector.rs @@ -4,11 +4,17 @@ use ScoredDoc; use SegmentReader; use SegmentLocalId; + +/// Multicollector makes it possible to collect on more than one collector +/// It should only be used for use cases where the Collector types is unknown +/// at compile time. +/// If the type of the collectors is known, you should prefer to use `ChainedCollector`. pub struct MultiCollector<'a> { collectors: Vec<&'a mut Collector>, } impl<'a> MultiCollector<'a> { + /// Constructor pub fn from(collectors: Vec<&'a mut Collector>) -> MultiCollector { MultiCollector { collectors: collectors, @@ -16,8 +22,8 @@ impl<'a> MultiCollector<'a> { } } -impl<'a> Collector for MultiCollector<'a> { +impl<'a> Collector for MultiCollector<'a> { fn set_segment(&mut self, segment_local_id: SegmentLocalId, segment: &SegmentReader) -> io::Result<()> { for collector in &mut self.collectors { try!(collector.set_segment(segment_local_id, segment)); diff --git a/src/collector/top_collector.rs b/src/collector/top_collector.rs index 45f79f82a..c04c82774 100644 --- a/src/collector/top_collector.rs +++ b/src/collector/top_collector.rs @@ -36,6 +36,12 @@ impl PartialEq for GlobalScoredDoc { impl Eq for GlobalScoredDoc {} + +/// The Top Collector keeps track of the K documents +/// with the best scores. +/// +/// The implementation is based on a `BinaryHeap`. +/// The theorical complexity is `O(n log K)`. pub struct TopCollector { limit: usize, heap: BinaryHeap, @@ -58,7 +64,11 @@ impl TopCollector { segment_id: 0, } } - + + /// Returns the decreasingly sorted K-best documents. + /// + /// Calling this method will triggers the sort. + /// The result of the sort is not cached. pub fn docs(&self) -> Vec { self.score_docs() .into_iter() @@ -66,6 +76,10 @@ impl TopCollector { .collect() } + /// Returns the decreasingly sorted K-best ScoredDocument. + /// + /// Calling this method will triggers the sort. + /// The result of the sort is not cached. pub fn score_docs(&self) -> Vec<(Score, DocAddress)> { let mut scored_docs: Vec = self.heap .iter() @@ -76,7 +90,9 @@ impl TopCollector { .map(|GlobalScoredDoc(score, doc_address)| (score, doc_address)) .collect() } - + + /// Return true iff at least K document have gone through + /// the collector. #[inline] pub fn at_capacity(&self, ) -> bool { self.heap.len() >= self.limit diff --git a/src/core/segment_reader.rs b/src/core/segment_reader.rs index 2859d65a0..3a027045a 100644 --- a/src/core/segment_reader.rs +++ b/src/core/segment_reader.rs @@ -140,7 +140,7 @@ impl SegmentReader { // is it because the term is not here, or because the // field does not handle this functionality. pub fn read_postings(&self, term: &Term, option: SegmentPostingsOption) -> Option { - let field = term.get_field(); + let field = term.field(); let field_entry = self.schema.get_field_entry(field); let term_info = get!(self.get_term_info(&term)); let offset = term_info.postings_offset as usize; @@ -183,7 +183,7 @@ impl SegmentReader { } pub fn read_postings_all_info(&self, term: &Term) -> SegmentPostings { - let field_entry = self.schema.get_field_entry(term.get_field()); + let field_entry = self.schema.get_field_entry(term.field()); let segment_posting_option = match *field_entry.field_type() { FieldType::Str(ref text_options) => { match text_options.get_indexing_options() { diff --git a/src/indexer/merger.rs b/src/indexer/merger.rs index b05f48eda..b96820ef8 100644 --- a/src/indexer/merger.rs +++ b/src/indexer/merger.rs @@ -274,8 +274,8 @@ mod tests { use schema::Term; use core::Index; use DocAddress; - use collector::FastFieldTestCollector; - use collector::TestCollector; + use collector::tests::FastFieldTestCollector; + use collector::tests::TestCollector; use query::MultiTermQuery; use schema::TextIndexingOptions; diff --git a/src/indexer/segment_writer.rs b/src/indexer/segment_writer.rs index 07af613cc..574f76f31 100644 --- a/src/indexer/segment_writer.rs +++ b/src/indexer/segment_writer.rs @@ -107,7 +107,7 @@ impl<'a> SegmentWriter<'a> { pub fn add_document(&mut self, doc: &Document, schema: &Schema) -> io::Result<()> { let doc_id = self.max_doc; - for (field, field_values) in doc.get_sorted_fields() { + for (field, field_values) in doc.get_sorted_field_values() { let field_posting_writer: &mut Box = &mut self.per_field_postings_writers[field.0 as usize]; let field_options = schema.get_field_entry(field); match *field_options.field_type() { @@ -144,7 +144,7 @@ impl<'a> SegmentWriter<'a> { self.fast_field_writers.add_document(doc); let stored_fieldvalues: Vec<&FieldValue> = doc - .get_fields() + .field_values() .iter() .filter(|field_value| schema.get_field_entry(field_value.field()).is_stored()) .collect(); diff --git a/src/lib.rs b/src/lib.rs index c94b0c832..cf2675253 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,7 @@ impl ScoredDoc { #[cfg(test)] mod tests { - use collector::TestCollector; + use collector::tests::TestCollector; use query::MultiTermQuery; use Index; use core::SegmentReader; diff --git a/src/postings/serializer.rs b/src/postings/serializer.rs index a390e8b70..f42d857e8 100644 --- a/src/postings/serializer.rs +++ b/src/postings/serializer.rs @@ -82,7 +82,7 @@ impl PostingsSerializer { panic!("Called new_term, while the previous term was not closed."); } self.term_open = true; - self.load_indexing_options(term.get_field()); + self.load_indexing_options(term.field()); self.doc_ids.clear(); self.last_doc_id_encoded = 0; self.term_freqs.clear(); diff --git a/src/query/multi_term_query.rs b/src/query/multi_term_query.rs index eaca20fa0..be7b2e0d7 100644 --- a/src/query/multi_term_query.rs +++ b/src/query/multi_term_query.rs @@ -74,7 +74,7 @@ impl MultiTermQuery { for &(occur, ref term) in &self.occur_terms { let _decode_one_timer = decode_timer.open("decode_one"); if let Some(postings) = reader.read_postings(term, SegmentPostingsOption::Freq) { - let field = term.get_field(); + let field = term.field(); let fieldnorm_reader = try!(reader.get_fieldnorms_reader(field)); postings_and_fieldnorms.push((occur, postings, fieldnorm_reader)); } diff --git a/src/schema/document.rs b/src/schema/document.rs index eaa9b83da..671237155 100644 --- a/src/schema/document.rs +++ b/src/schema/document.rs @@ -4,12 +4,11 @@ use itertools::Itertools; /// Tantivy's Document is the object that can /// be indexed and then searched for. /// -/// Documents are really fundamentally a collection of unordered couple `(field, value)`. +/// Documents are fundamentally a collection of unordered couple `(field, value)`. /// In this list, one field may appear more than once. /// /// - /// Documents are really just a list of couple `(field, value)`. /// In this list, one field may appear more than once. #[derive(Debug, RustcEncodable, RustcDecodable)] @@ -44,30 +43,31 @@ impl Document { /// Add a text field. pub fn add_text(&mut self, field: Field, text: &str) { - self.add(FieldValue { - field: field, - value: Value::Str(String::from(text)), - }); + let value = Value::Str(String::from(text)); + self.add(FieldValue::new(field, value)); } /// Add a u32 field pub fn add_u32(&mut self, field: Field, value: u32) { - self.add(FieldValue { - field: field, - value: Value::U32(value), - }); + self.add(FieldValue::new(field, Value::U32(value))); } + /// Add a field value pub fn add(&mut self, field_value: FieldValue) { self.field_values.push(field_value); } - - pub fn get_fields(&self) -> &Vec { + + /// field_values accessor + pub fn field_values(&self) -> &[FieldValue] { &self.field_values } - pub fn get_sorted_fields(&self) -> Vec<(Field, Vec<&FieldValue>)> { - let mut field_values: Vec<&FieldValue> = self.get_fields().iter().collect(); + /// Sort and groups the field_values by field. + /// + /// The result of this method is not cached and is + /// computed on the fly when this method is called. + pub fn get_sorted_field_values(&self) -> Vec<(Field, Vec<&FieldValue>)> { + let mut field_values: Vec<&FieldValue> = self.field_values().iter().collect(); field_values.sort_by_key(|field_value| field_value.field()); field_values .into_iter() @@ -79,6 +79,7 @@ impl Document { .collect::)>>() } + /// Returns all of the `FieldValue`s associated the given field pub fn get_all(&self, field: Field) -> Vec<&Value> { self.field_values .iter() @@ -87,12 +88,12 @@ impl Document { .collect() } + /// Returns the first `FieldValue` associated the given field pub fn get_first(&self, field: Field) -> Option<&Value> { self.field_values .iter() - .filter(|field_value| field_value.field() == field) + .find(|field_value| field_value.field() == field) .map(|field_value| field_value.value()) - .next() } } @@ -125,7 +126,7 @@ mod tests { let text_field = schema_builder.add_text_field("title", TEXT); let mut doc = Document::default(); doc.add_text(text_field, "My title"); - assert_eq!(doc.get_fields().len(), 1); + assert_eq!(doc.field_values().len(), 1); } } \ No newline at end of file diff --git a/src/schema/field_entry.rs b/src/schema/field_entry.rs index 69bd26ebd..142a3d5e9 100644 --- a/src/schema/field_entry.rs +++ b/src/schema/field_entry.rs @@ -10,15 +10,23 @@ use schema::Value; /// A `FieldType` describes the type (text, u32) of a field as well as -/// how it should be handled by tantivy. +/// how it should be handled by tantivy. #[derive(Clone, Debug, RustcDecodable, RustcEncodable)] pub enum FieldType { + /// String field type configuration Str(TextOptions), + /// U32 field type configuration U32(U32Options), } impl FieldType { - pub fn value_from_json(&self, json: &Json) -> Result { + + /// Parses a field value from json, given the target FieldType. + /// + /// Tantivy will not try to cast values. + /// For instance, If the json value is the integer `3` and the + /// target field is a `Str`, this method will return an Error. + pub fn value_from_json(&self, json: &Json) -> Result { match *json { Json::String(ref field_text) => { match *self { @@ -66,9 +74,15 @@ pub struct FieldEntry { field_type: FieldType, } + +/// Possible error that may occur while parsing a field value +/// At this point the JSON is known to be valid. #[derive(Debug)] pub enum ValueParsingError { + /// Encounterred a numerical value that overflows or underflow its integer type. OverflowError(String), + /// The json node is not of the correct type. (e.g. 3 for a `Str` type or `"abc"` for a u32 type) + /// Tantivy will try to autocast values. TypeError(String), } diff --git a/src/schema/field_value.rs b/src/schema/field_value.rs index 90780543a..6c0e49684 100644 --- a/src/schema/field_value.rs +++ b/src/schema/field_value.rs @@ -9,15 +9,26 @@ use schema::Value; /// `FieldValue` holds together a `Field` and its `Value`. #[derive(Debug, Clone, Ord, PartialEq, Eq, PartialOrd, RustcEncodable, RustcDecodable)] pub struct FieldValue { - pub field: Field, - pub value: Value, + field: Field, + value: Value, } impl FieldValue { + + /// Constructor + pub fn new(field: Field, value: Value) -> FieldValue { + FieldValue { + field: field, + value: value, + } + } + + /// Field accessor pub fn field(&self) -> Field { self.field } + /// Value accessor pub fn value(&self,) -> &Value { &self.value } @@ -34,10 +45,7 @@ impl BinarySerializable for FieldValue { fn deserialize(reader: &mut Read) -> io::Result { let field = try!(Field::deserialize(reader)); let value = try!(Value::deserialize(reader)); - Ok(FieldValue { - field: field, - value: value, - }) + Ok(FieldValue::new(field, value)) } } diff --git a/src/schema/mod.rs b/src/schema/mod.rs index e58f19e21..2abde20ba 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -133,6 +133,12 @@ pub use self::u32_options::FAST; use regex::Regex; + +/// Validator for a potential `field_name`. +/// Returns true iff the name can be use for a field name. +/// +/// Anything containing more than one (alphanumeric or `_`) character +/// is a valid field name. pub fn is_valid_field_name(field_name: &str) -> bool { lazy_static! { static ref FIELD_NAME_PTN: Regex = Regex::new("[_a-zA-Z0-9]+").unwrap(); diff --git a/src/schema/schema.rs b/src/schema/schema.rs index 9c6e49088..ab52b2ae8 100644 --- a/src/schema/schema.rs +++ b/src/schema/schema.rs @@ -79,7 +79,9 @@ impl SchemaBuilder { let field_entry = FieldEntry::new_text(field_name, field_options); self.add_field(field_entry) } - + + + /// Adds a field entry to the schema in build. fn add_field(&mut self, field_entry: FieldEntry) -> Field { let field = Field(self.fields.len() as u8); let field_name = field_entry.name().clone(); @@ -87,7 +89,10 @@ impl SchemaBuilder { self.fields_map.insert(field_name, field); field } - + + + /// Finalize the creation of a `Schema` + /// This will consume your `SchemaBuilder` pub fn build(self,) -> Schema { Schema(Arc::new(InnerSchema { fields: self.fields, @@ -138,16 +143,19 @@ struct InnerSchema { pub struct Schema(Arc); impl Schema { - + + /// Return the `FieldEntry` associated to a `Field`. pub fn get_field_entry(&self, field: Field) -> &FieldEntry { &self.0.fields[field.0 as usize] } + /// Return the field name for a given `Field`. pub fn get_field_name(&self, field: Field) -> &String { self.get_field_entry(field).name() } - - pub fn fields(&self,) -> &Vec { + + /// Return the list of all the `Field`s. + pub fn fields(&self,) -> &[FieldEntry] { &self.0.fields } @@ -166,7 +174,7 @@ impl Schema { pub fn to_named_doc(&self, doc: &Document) -> NamedFieldDocument { let mut field_map = BTreeMap::new(); - for (field, field_values) in doc.get_sorted_fields() { + for (field, field_values) in doc.get_sorted_field_values() { let field_name = self.get_field_name(field); let values: Vec = field_values .into_iter() @@ -177,9 +185,12 @@ impl Schema { } NamedFieldDocument(field_map) } - + + + /// Encode the schema in JSON. + /// + /// Encoding a document cannot fail. pub fn to_json(&self, doc: &Document) -> String { - // encoding a document cannot fail. json::encode(&self.to_named_doc(doc)).unwrap() } @@ -212,10 +223,7 @@ impl Schema { .value_from_json(&json_item) .map_err(|e| DocParsingError::ValueError(field_name.clone(), e)) ); - doc.add(FieldValue { - field: field, - value: value - }); + doc.add(FieldValue::new(field, value)); } } _ => { @@ -224,10 +232,7 @@ impl Schema { .value_from_json(&json_value) .map_err(|e| DocParsingError::ValueError(field_name.clone(), e)) ); - doc.add(FieldValue { - field: field, - value: value - }); + doc.add(FieldValue::new(field, value)); } } @@ -285,11 +290,17 @@ impl From for Schema { +/// Error that may happen when deserializing +/// a document from JSON. #[derive(Debug)] pub enum DocParsingError { + /// The payload given is not valid JSON. NotJSON(json::ParserError), + /// The payload given is not a JSON Object (`{...}`). NotJSONObject(String), + /// One of the value node could not be parsed. ValueError(String, ValueParsingError), + /// The json-document contains a field that is not declared in the schema. NoSuchFieldInSchema(String), } @@ -379,7 +390,7 @@ mod tests { let schema = schema_builder.build(); { let doc = schema.parse_document("{}").unwrap(); - assert!(doc.get_fields().is_empty()); + assert!(doc.field_values().is_empty()); } { let doc = schema.parse_document(r#"{ diff --git a/src/schema/term.rs b/src/schema/term.rs index 66c50ef2b..777e3044c 100644 --- a/src/schema/term.rs +++ b/src/schema/term.rs @@ -5,31 +5,42 @@ use super::Field; - +/// Term represents the value that the token can take. +/// +/// It actually wraps a `Vec`. #[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct Term(Vec); impl Term { + /// Pre-allocate a term buffer. pub fn allocate(field: Field, num_bytes: usize) -> Term { let mut term = Term(Vec::with_capacity(num_bytes)); field.serialize(&mut term.0).expect("Serializing term in a Vec should never fail"); term } + /// Set the content of the term. pub fn set_content(&mut self, content: &[u8]) { self.0.resize(content.len(), 0u8); (&mut self.0[..]).clone_from_slice(content); } + /// Returns the field id. fn field_id(&self,) -> u8 { self.0[0] } - pub fn get_field(&self,) -> Field { + /// Returns the field. + pub fn field(&self,) -> Field { Field(self.field_id()) } + /// Builds a term given a field, and a u32-value + /// + /// Assuming the term has a field id of 1, and a u32 value of 3234, + /// the Term will have 5 bytes. + /// The first byte is `1`, and the 4 following bytes are that of the u32. pub fn from_field_u32(field: Field, val: u32) -> Term { let mut buffer = Vec::with_capacity(1 + 4); buffer.clear(); @@ -37,7 +48,13 @@ impl Term { val.serialize(&mut buffer).unwrap(); Term(buffer) } - + + /// Builds a term given a field, and a string value + /// + /// Assuming the term has a field id of 2, and a text value of "abc", + /// the Term will have 4 bytes. + /// The first byte is 2, and the three following bytes are the utf-8 + /// representation of "abc". pub fn from_field_text(field: Field, text: &str) -> Term { let mut buffer = Vec::with_capacity(1 + text.len()); buffer.clear(); @@ -46,11 +63,13 @@ impl Term { Term(buffer) } + /// Set the texts only, keeping the field untouched. pub fn set_text(&mut self, text: &str) { self.0.resize(1, 0u8); self.0.extend(text.as_bytes()); } + /// Returns the underlying `&[u8]` pub fn as_slice(&self,)->&[u8] { &self.0 } @@ -89,13 +108,13 @@ mod tests { let count_field = schema_builder.add_text_field("count", STRING); { let term = Term::from_field_text(title_field, "test"); - assert_eq!(term.get_field(), title_field); + assert_eq!(term.field(), title_field); assert_eq!(term.as_slice()[0], 1u8); assert_eq!(&term.as_slice()[1..], "test".as_bytes()); } { let term = Term::from_field_u32(count_field, 983u32); - assert_eq!(term.get_field(), count_field); + assert_eq!(term.field(), count_field); assert_eq!(term.as_slice()[0], 2u8); assert_eq!(term.as_slice().len(), 5); assert_eq!(term.as_slice()[1], (983u32 % 256u32) as u8); diff --git a/src/schema/text_options.rs b/src/schema/text_options.rs index cd21c94bc..91000de81 100644 --- a/src/schema/text_options.rs +++ b/src/schema/text_options.rs @@ -5,7 +5,7 @@ use rustc_serialize::Encodable; use rustc_serialize::Encoder; -/// Text options +/// Define how a text field should be handled by tantivy. #[derive(Clone,Debug,PartialEq,Eq, RustcDecodable, RustcEncodable)] pub struct TextOptions { indexing: TextIndexingOptions, diff --git a/src/schema/u32_options.rs b/src/schema/u32_options.rs index 5efc73321..39be904ed 100644 --- a/src/schema/u32_options.rs +++ b/src/schema/u32_options.rs @@ -1,3 +1,4 @@ +/// Define how a U32 field should be handled by tantivy. #[derive(Clone,Debug,PartialEq,Eq, RustcDecodable, RustcEncodable)] pub struct U32Options { indexed: bool, diff --git a/src/schema/value.rs b/src/schema/value.rs index 354934799..36f21bc86 100644 --- a/src/schema/value.rs +++ b/src/schema/value.rs @@ -4,13 +4,21 @@ use std::io; use std::io::Write; use std::io::Read; +/// Value represents the value of a any field. +/// It is an enum over all over all of the possible field type. #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable)] pub enum Value { + /// The str type is used for any text information. Str(String), + /// Unsigned 32-bits Integer `u32` U32(u32), } impl Value { + /// Returns the text value, provided the value is of the `Str` type. + /// + /// # Panics + /// If the value is not of type `Str` pub fn text(&self) -> &str { match *self { Value::Str(ref text) => { @@ -22,6 +30,10 @@ impl Value { } } + /// Returns the u32-value, provided the value is of the `U32` type. + /// + /// # Panics + /// If the value is not of type `U32` pub fn u32_value(&self) -> u32 { match *self { Value::U32(ref value) => { diff --git a/src/store/mod.rs b/src/store/mod.rs index 4572e9d4a..fd62741ce 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -30,18 +30,12 @@ mod tests { for i in 0..1000 { let mut fields: Vec = Vec::new(); { - let field_value = FieldValue { - field: field_body, - value: From::from(lorem.clone()) - }; + let field_value = FieldValue::new(field_body, From::from(lorem.clone())); fields.push(field_value); } { let title_text = format!("Doc {}", i); - let field_value = FieldValue { - field: field_title, - value: From::from(title_text) - }; + let field_value = FieldValue::new(field_title, From::from(title_text)); fields.push(field_value); } let fields_refs: Vec<&FieldValue> = fields.iter().collect();