Added documentation

This commit is contained in:
Paul Masurel
2016-09-20 00:43:52 +09:00
parent 49e5114413
commit 17715fe84c
22 changed files with 298 additions and 173 deletions
+10 -21
View File
@@ -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: Collector, Right: Collector> {
left: Left,
right: Right
}
impl<Left: Collector, Right: Collector> ChainedCollector<Left, Right> {
pub fn start() -> ChainedCollector<DoNothingCollector, DoNothingCollector> {
ChainedCollector {
left: DoNothingCollector,
right: DoNothingCollector
}
}
pub fn push<C: Collector>(self, new_collector: &mut C) -> ChainedCollector<Self, MutRefCollector<C>> {
/// Adds a collector
pub fn push<C: Collector>(self, new_collector: &mut C) -> ChainedCollector<Self, &mut C> {
ChainedCollector {
left: self,
right: MutRefCollector(new_collector),
right: new_collector,
}
}
}
@@ -50,6 +49,7 @@ impl<Left: Collector, Right: Collector> Collector for ChainedCollector<Left, Rig
}
}
/// Creates a `ChainedCollector`
pub fn chain() -> ChainedCollector<DoNothingCollector, DoNothingCollector> {
ChainedCollector {
left: DoNothingCollector,
@@ -57,17 +57,6 @@ pub fn chain() -> ChainedCollector<DoNothingCollector, DoNothingCollector> {
}
}
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 {
-9
View File
@@ -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);
}
+4 -4
View File
@@ -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
}
+128 -71
View File
@@ -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<DocId>,
/// 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<DocId> {
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<u32>,
field: Field,
ff_reader: Option<U32FastFieldReader>,
}
impl FastFieldTestCollector {
pub fn for_field(field: Field) -> FastFieldTestCollector {
FastFieldTestCollector {
vals: Vec::new(),
field: field,
ff_reader: None,
}
}
pub fn vals(&self,) -> &Vec<u32> {
&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<DocId>,
}
impl TestCollector {
/// Return the exhalist of documents.
pub fn docs(self,) -> Vec<DocId> {
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<u32>,
field: Field,
ff_reader: Option<U32FastFieldReader>,
}
impl FastFieldTestCollector {
pub fn for_field(field: Field) -> FastFieldTestCollector {
FastFieldTestCollector {
vals: Vec::new(),
field: field,
ff_reader: None,
}
}
pub fn vals(&self,) -> &Vec<u32> {
&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) {
+7 -1
View File
@@ -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));
+18 -2
View File
@@ -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<GlobalScoredDoc>,
@@ -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<DocAddress> {
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<GlobalScoredDoc> = 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
+2 -2
View File
@@ -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<SegmentPostings> {
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() {
+2 -2
View File
@@ -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;
+2 -2
View File
@@ -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<PostingsWriter> = &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();
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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));
}
+18 -17
View File
@@ -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<FieldValue> {
/// 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::<Vec<(Field, Vec<&FieldValue>)>>()
}
/// 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);
}
}
+16 -2
View File
@@ -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<Value, ValueParsingError> {
/// 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<Value, ValueParsingError> {
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),
}
+14 -6
View File
@@ -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<Self> {
let field = try!(Field::deserialize(reader));
let value = try!(Value::deserialize(reader));
Ok(FieldValue {
field: field,
value: value,
})
Ok(FieldValue::new(field, value))
}
}
+6
View File
@@ -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();
+28 -17
View File
@@ -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<InnerSchema>);
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<FieldEntry> {
/// 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<Value> = 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<SchemaBuilder> 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#"{
+24 -5
View File
@@ -5,31 +5,42 @@ use super::Field;
/// Term represents the value that the token can take.
///
/// It actually wraps a `Vec<u8>`.
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
pub struct Term(Vec<u8>);
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);
+1 -1
View File
@@ -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,
+1
View File
@@ -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,
+12
View File
@@ -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) => {
+2 -8
View File
@@ -30,18 +30,12 @@ mod tests {
for i in 0..1000 {
let mut fields: Vec<FieldValue> = 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();