diff --git a/src/cli/serve.rs b/src/cli/serve.rs new file mode 100644 index 000000000..c1857c74e --- /dev/null +++ b/src/cli/serve.rs @@ -0,0 +1,155 @@ +extern crate tantivy; +extern crate persistent; +extern crate time; +extern crate urlencoded; +extern crate rustc_serialize; +extern crate iron; +extern crate staticfile; +extern crate mount; + +use tantivy::schema::Field; +use tantivy::collector::CountCollector; +use tantivy::Index; +use tantivy::collector; +use urlencoded::UrlEncodedQuery; +use iron::status; +use rustc_serialize::json::as_pretty_json; +use std::path::Path; +use staticfile::Static; +use iron::mime::Mime; +use mount::Mount; +use tantivy::query::Query; +use tantivy::query::QueryParser; +use tantivy::Document; +use tantivy::collector::TopCollector; +use persistent::Read; +use iron::typemap::Key; +use iron::prelude::*; +use std::io; + + +#[derive(RustcDecodable, RustcEncodable)] +struct Serp { + q: String, + num_hits: usize, + hits: Vec, + timings: Vec, +} + +#[derive(RustcDecodable, RustcEncodable)] +struct Hit { + title: String, + body: String, +} + +#[derive(RustcDecodable, RustcEncodable)] +struct Timing { + name: String, + duration: i64, +} + +struct IndexServer { + index: Index, + query_parser: QueryParser, + body_field: Field, + title_field: Field, +} + +impl IndexServer { + + fn load(path: &Path) -> IndexServer { + let index = Index::open(path).unwrap(); + let schema = index.schema(); + let body_field = schema.get_field("body").unwrap(); + let title_field = schema.get_field("title").unwrap(); + let query_parser = QueryParser::new(schema, vec!(body_field, title_field)); + IndexServer { + index: index, + query_parser: query_parser, + title_field: title_field, + body_field: body_field, + } + } + + fn create_hit(&self, doc: &Document) -> Hit { + Hit { + title: String::from(doc.get_first(self.title_field).unwrap().text()), + body: String::from(doc.get_first(self.body_field).unwrap().text().clone()), + + } + } + + fn search(&self, q: String) -> io::Result { + let query = self.query_parser.parse_query(&q).unwrap(); + let searcher = self.index.searcher().unwrap(); + let mut count_collector = CountCollector::new(); + let mut top_collector = TopCollector::with_limit(10); + + { + let mut chained_collector = collector::chain() + .add(&mut top_collector) + .add(&mut count_collector); + try!(query.search(&searcher, &mut chained_collector)); + } + let hits: Vec = try!( + top_collector.docs() + .iter() + .map(|doc_address| searcher.doc(doc_address)) + .map(|doc_result| doc_result.map(|doc| self.create_hit(&doc) )) + .collect() + ); + Ok(Serp { + q: q, + hits: hits, + num_hits: count_collector.count(), + timings: Vec::new(), + }) + } + + +} + +impl Key for IndexServer { + type Value = IndexServer; +} + + +fn search(req: &mut Request) -> IronResult { + let index_server = req.get::>().unwrap(); + match req.get_ref::() { + Ok(ref qs_map) => { + match qs_map.get("q") { + Some(qs) => { + let query = qs[0].clone(); + let serp = index_server.search(query).unwrap(); + let resp_json = as_pretty_json(&serp).indent(4); + let content_type = "application/json".parse::().unwrap(); + Ok( + Response::with((content_type, status::Ok, format!("{}", resp_json))) + ) + } + None => { + Ok(Response::with((status::BadRequest, "Query not defined"))) + } + } + } + Err(_) => Ok(Response::with((status::BadRequest, "Failed to parse query string"))) + } +} + + + +fn main() { + let mut mount = Mount::new(); + let server = IndexServer::load(&Path::new("/data/wiki-index/")); + + mount.mount("/api", search); + mount.mount("/", Static::new(Path::new("static/"))); + + let mut middleware = Chain::new(mount); + middleware.link(Read::::both(server)); + + let host = "127.0.0.1:3000"; + println!("listening on http://{}", host); + Iron::new(middleware).http("127.0.0.1:3000").unwrap(); +} diff --git a/src/query/multi_term_query.rs b/src/query/multi_term_query.rs index 69d8323a9..5e4cf8789 100644 --- a/src/query/multi_term_query.rs +++ b/src/query/multi_term_query.rs @@ -12,7 +12,6 @@ use postings::UnionPostings; use postings::ScoredDocSet; use postings::DocSet; use query::MultiTermScorer; -use std::iter; use fastfield::U32FastFieldReader; use ScoredDoc; @@ -74,10 +73,12 @@ impl MultiTermQuery { } }) .collect(); - let query_coord = iter::repeat(1f32).take(self.terms.len()).collect(); - MultiTermScorer::new(query_coord, idfs) + let query_coords = (0..self.terms.len() + 1) + .map(|i| i as f32 / self.terms.len() as f32) + .collect(); + MultiTermScorer::new(query_coords, idfs) } - + pub fn new(terms: Vec) -> MultiTermQuery { MultiTermQuery { terms: terms, @@ -96,7 +97,7 @@ impl MultiTermQuery { let field = term.get_field(); fieldnorms_readers.push(reader.get_fieldnorms_reader(field).unwrap()); segment_postings.push(postings); - }); + }); } } UnionPostings::new(fieldnorms_readers, segment_postings, multi_term_scorer)