Added serve command

This commit is contained in:
Paul Masurel
2016-08-04 10:16:24 +09:00
parent 66ebbae85a
commit 86f105bd7a
2 changed files with 161 additions and 5 deletions
+155
View File
@@ -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<Hit>,
timings: Vec<Timing>,
}
#[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<Serp> {
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<Hit> = 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<Response> {
let index_server = req.get::<Read<IndexServer>>().unwrap();
match req.get_ref::<UrlEncodedQuery>() {
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::<Mime>().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::<IndexServer>::both(server));
let host = "127.0.0.1:3000";
println!("listening on http://{}", host);
Iron::new(middleware).http("127.0.0.1:3000").unwrap();
}
+6 -5
View File
@@ -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<Term>) -> 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)