changeset 545:ddcd4296107a

clean up lucene search
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 14 Jun 2015 01:34:42 -0600
parents c5a93767cc5c
children eaef1005ab87
files lucene/src/luan/modules/lucene/Ab_testing.luan lucene/src/luan/modules/lucene/Lucene.luan lucene/src/luan/modules/lucene/LuceneIndex.java lucene/src/luan/modules/lucene/LuceneSearcher.java lucene/src/luan/modules/lucene/LuceneWriter.java lucene/src/luan/modules/lucene/Web_search.luan
diffstat 6 files changed, 171 insertions(+), 222 deletions(-) [+]
line wrap: on
line diff
--- a/lucene/src/luan/modules/lucene/Ab_testing.luan	Fri Jun 12 19:11:44 2015 -0600
+++ b/lucene/src/luan/modules/lucene/Ab_testing.luan	Sun Jun 14 01:34:42 2015 -0600
@@ -41,37 +41,36 @@
 
 		-- returns map of event name to (map of value to result) and "start_date"
 		function test.results()
-			return index.Searcher( function(searcher)
-				local results = {}
-				for name in pairs(test.aggregator_factories) do
-					results[name] = {}
+			local results = {}
+			for name in pairs(test.aggregator_factories) do
+				results[name] = {}
+			end
+			local date_field = test.date_field
+			local start_date = nil
+			for _, value in ipairs(test.values) do
+				local aggregators = {}
+				for name, factory in pairs(test.aggregator_factories) do
+					aggregators[name] = factory()
 				end
-				local date_field = test.date_field
-				local start_date = nil
-				for _, value in ipairs(test.values) do
-					local aggregators = {}
-					for name, factory in pairs(test.aggregator_factories) do
-						aggregators[name] = factory()
+				local query = field..":"..value
+				index.advanced_search(query, function(_,doc_fn)
+					local doc = doc_fn()
+					for _, aggregator in pairs(aggregators) do
+						aggregator.aggregate(doc)
 					end
-					local query = field..":"..value
-					searcher.search(query, function(doc)
-						for _, aggregator in pairs(aggregators) do
-							aggregator.aggregate(doc)
+					if date_field ~= nil then
+						local date = doc[date_field]
+						if date ~= nil and (start_date==nil or start_date > date) then
+							start_date = date
 						end
-						if date_field ~= nil then
-							local date = doc[date_field]
-							if date ~= nil and (start_date==nil or start_date > date) then
-								start_date = date
-							end
-						end
-					end)
-					for name, aggregator in pairs(aggregators) do
-						results[name][value] = aggregator.result
 					end
+				end)
+				for name, aggregator in pairs(aggregators) do
+					results[name][value] = aggregator.result
 				end
-				results.start_date = start_date
-				return results
-			end )
+			end
+			results.start_date = start_date
+			return results
 		end
 
 		function test.fancy_results()
--- a/lucene/src/luan/modules/lucene/Lucene.luan	Fri Jun 12 19:11:44 2015 -0600
+++ b/lucene/src/luan/modules/lucene/Lucene.luan	Sun Jun 14 01:34:42 2015 -0600
@@ -16,47 +16,51 @@
 
 function M.index(indexDir)
 	local index = {}
-	local java_index = LuceneIndex.new(indexDir)
+	local java_index = LuceneIndex.new(indexDir,index)
 	index.indexed_fields = java_index.indexedFieldsMeta.newTable()
 	index.to_string = java_index.to_string
 	index.backup = java_index.backup
-	index.Writer = java_index.Writer
-	index.Searcher = java_index.Searcher
+	index.writer = java_index.writer
+	index.advanced_search = java_index.advanced_search
+	index.search_in_transaction = java_index.search_in_transaction
 	index.delete_all = java_index.delete_all
 	index.close = java_index.close
 
 	function index.save_document(doc)
-		index.Writer( function(writer)
+		index.writer( function(writer)
 			writer.save_document(doc)
 		end )
 	end
 
 	function index.delete_documents(terms)
-		index.Writer( function(writer)
+		index.writer( function(writer)
 			writer.delete_documents(terms)
 		end )
 	end
 
-	function index.get_first(query, sort)
-		return index.Searcher( function(searcher)
-			local results, _, total_hits = searcher.search(query,1,sort)
-			return results(), total_hits
-		end )
+	function index.search(query, from, to, sort)
+		local results = {}
+		local function fn(i,doc_fn)
+			if i >= from then
+				results[#results+1] = doc_fn()
+			end
+		end
+		local total_hits = index.advanced_search(query,fn,to,sort)
+		return results, total_hits
 	end
 
 	function index.get_document(query)
-		local doc, total_hits = index.get_first(query);
-		if total_hits > 1 then
-			error( "found " .. total_hits .. " documents" )
+		local doc
+		local function fn(_,doc_fn)
+			doc = doc_fn()
 		end
+		local total_hits = index.advanced_search(query,fn,1)
+		total_hits <= 1 or error( "found " .. total_hits .. " documents" )
 		return doc
 	end
 
 	function index.count(query)
-		return index.Searcher( function(searcher)
-			local _, _, total_hits = searcher.search(query,0)
-			return total_hits
-		end )
+		return index.advanced_search(query)
 	end
 
 	return index
--- a/lucene/src/luan/modules/lucene/LuceneIndex.java	Fri Jun 12 19:11:44 2015 -0600
+++ b/lucene/src/luan/modules/lucene/LuceneIndex.java	Sun Jun 14 01:34:42 2015 -0600
@@ -18,6 +18,7 @@
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.SnapshotDeletionPolicy;
+import org.apache.lucene.index.AtomicReaderContext;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
 import org.apache.lucene.util.Version;
@@ -27,6 +28,10 @@
 import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.TotalHitCountCollector;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.Scorer;
 import sane.lucene.queryparser.SaneQueryParser;
 import sane.lucene.queryparser.FieldParser;
 import sane.lucene.queryparser.MultiFieldParser;
@@ -41,6 +46,7 @@
 import luan.LuanJavaFunction;
 import luan.LuanException;
 import luan.LuanMeta;
+import luan.LuanRuntimeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,16 +58,19 @@
 	private static final Analyzer analyzer = new KeywordAnalyzer();
 	public static final FieldParser STRING_FIELD_PARSER = new StringFieldParser(analyzer);
 
+	final LuanTable myTable;
 	final Lock writeLock = new ReentrantLock();
 	private final File indexDir;
 	final SnapshotDeletionPolicy snapshotDeletionPolicy;
 	final IndexWriter writer;
 	private DirectoryReader reader;
-	private LuceneSearcher searcher;
+	private IndexSearcher searcher;
+	private final ThreadLocal<IndexSearcher> threadLocalSearcher = new ThreadLocal<IndexSearcher>();
 	private boolean isClosed = false;
 	private final MultiFieldParser mfp = new MultiFieldParser();
 
-	public LuceneIndex(LuanState luan,String indexDirStr) throws LuanException, IOException {
+	public LuceneIndex(LuanState luan,String indexDirStr,LuanTable myTable) throws LuanException, IOException {
+		this.myTable = myTable;
 		mfp.fields.put( "type", STRING_FIELD_PARSER );
 		mfp.fields.put( "id", NumberFieldParser.LONG );
 		File indexDir = new File(indexDirStr);
@@ -75,7 +84,7 @@
 		writer.commit();  // commit index creation
 		reader = DirectoryReader.open(dir);
 		luan.onClose(this);
-		searcher = new LuceneSearcher(this,reader);
+		searcher = new IndexSearcher(reader);
 		initId(luan);
 	}
 
@@ -87,17 +96,22 @@
 		return new LuceneWriter(this);
 	}
 
-	synchronized LuceneSearcher openSearcher() throws IOException {
+	private synchronized IndexSearcher openSearcher() throws IOException {
 		DirectoryReader newReader = DirectoryReader.openIfChanged(reader);
 		if( newReader != null ) {
 			reader.decRef();
 			reader = newReader;
-			searcher = new LuceneSearcher(this,reader);
+			searcher = new IndexSearcher(reader);
 		}
 		reader.incRef();
 		return searcher;
 	}
 
+	// call in finally block
+	private static void close(IndexSearcher searcher) throws IOException {
+		searcher.getIndexReader().decRef();
+	}
+
 	LuceneSnapshot openSnapshot() throws IOException {
 		return new LuceneSnapshot(this);
 	}
@@ -119,7 +133,6 @@
 	private final int idBatch = 10;
 
 	private void initId(LuanState luan) throws LuanException, IOException {
-		IndexSearcher searcher = this.searcher.searcher;
 		TopDocs td = searcher.search(new TermQuery(new Term("type","next_id")),1);
 		switch(td.totalHits) {
 		case 0:
@@ -172,7 +185,7 @@
 		return writer.getDirectory().toString();
 	}
 
-	public void Writer(LuanState luan,LuanFunction fn) throws LuanException, IOException {
+	public void writer(LuanState luan,LuanFunction fn) throws LuanException, IOException {
 		LuceneWriter writer = openWriter();
 		try {
 			luan.call( fn, new Object[]{writer.table()} );
@@ -182,15 +195,6 @@
 		}
 	}
 
-	public Object Searcher(LuanState luan,LuanFunction fn) throws LuanException, IOException {
-		LuceneSearcher searcher = openSearcher();
-		try {
-			return luan.call( fn, new Object[]{searcher.table()} );
-		} finally {
-			searcher.close();
-		}
-	}
-
 	public void close() throws IOException {
 		if( !isClosed ) {
 			writer.close();
@@ -209,6 +213,100 @@
 
 
 
+	private static class DocFn extends LuanFunction {
+		final IndexSearcher searcher;
+		int docID;
+
+		DocFn(IndexSearcher searcher) {
+			this.searcher = searcher;
+		}
+
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			try {
+				return LuceneDocument.toTable(luan,searcher.doc(docID));
+			} catch(IOException e) {
+				throw luan.exception(e);
+			}
+		}
+	}
+
+	private static abstract class MyCollector extends Collector {
+		int docBase;
+		int i = 0;
+
+		@Override public void setScorer(Scorer scorer) {}
+		@Override public void setNextReader(AtomicReaderContext context) {
+			this.docBase = context.docBase;
+		}
+		@Override public boolean acceptsDocsOutOfOrder() {
+			return true;
+		}
+	}
+
+	public int advanced_search( final LuanState luan, String queryStr, LuanFunction fn, Integer n, String sortStr ) throws LuanException, IOException, ParseException {
+		Utils.checkNotNull(luan,queryStr);
+		Query query = parseQuery(queryStr);
+		IndexSearcher searcher = threadLocalSearcher.get();
+		boolean inTransaction = searcher != null;
+		if( !inTransaction )
+			searcher = openSearcher();
+		try {
+			if( fn!=null && n==null ) {
+				if( sortStr != null )
+					throw luan.exception("sort must be nil when n is nil");
+				final DocFn docFn = new DocFn(searcher);
+				MyCollector col = new MyCollector() {
+					@Override public void collect(int doc) {
+						try {
+							docFn.docID = doc;
+							luan.call(fn,new Object[]{++i,docFn});
+						} catch(LuanException e) {
+							throw new LuanRuntimeException(e);
+						}
+					}
+				};
+				try {
+					searcher.search(query,col);
+				} catch(LuanRuntimeException e) {
+					throw (LuanException)e.getCause();
+				}
+				return col.i;
+			}
+			if( fn==null || n==0 ) {
+				TotalHitCountCollector thcc = new TotalHitCountCollector();
+				searcher.search(query,thcc);
+				return thcc.getTotalHits();
+			}
+			Sort sort = sortStr==null ? null : parseSort(sortStr);
+			TopDocs td = sort==null ? searcher.search(query,n) : searcher.search(query,n,sort);
+			final ScoreDoc[] scoreDocs = td.scoreDocs;
+			DocFn docFn = new DocFn(searcher);
+			for( int i=0; i<scoreDocs.length; i++ ) {
+				docFn.docID = scoreDocs[i].doc;
+				luan.call(fn,new Object[]{i+1,docFn});
+			}
+			return td.totalHits;
+		} finally {
+			if( !inTransaction )
+				close(searcher);
+		}
+	}
+
+	public Object search_in_transaction(LuanState luan,LuanFunction fn) throws LuanException, IOException {
+		if( threadLocalSearcher.get() != null )
+			throw luan.exception("can't nest search_in_transaction calls");
+		IndexSearcher searcher = openSearcher();
+		threadLocalSearcher.set(searcher);
+		try {
+			return luan.call(fn);
+		} finally {
+			threadLocalSearcher.set(null);
+			close(searcher);
+		}
+	}
+
+
+
 	public final LuanMeta indexedFieldsMeta = new LuanMeta() {
 
 		@Override public boolean canNewindex() {
--- a/lucene/src/luan/modules/lucene/LuceneSearcher.java	Fri Jun 12 19:11:44 2015 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-package luan.modules.lucene;
-
-import java.io.IOException;
-import java.util.NoSuchElementException;
-import java.util.Map;
-import java.util.List;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.search.TopFieldDocs;
-import org.apache.lucene.search.Sort;
-import org.apache.lucene.search.SortField;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.TotalHitCountCollector;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.index.AtomicReaderContext;
-import sane.lucene.queryparser.ParseException;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-import luan.LuanRuntimeException;
-import luan.LuanMethod;
-import luan.modules.Utils;
-
-
-public final class LuceneSearcher {
-	private final LuceneIndex index;
-	final IndexSearcher searcher;
-
-	LuceneSearcher(LuceneIndex index,IndexReader reader) {
-		this.index = index;
-		this.searcher = new IndexSearcher(reader);
-	}
-
-	// call in finally block
-	void close() throws IOException {
-		searcher.getIndexReader().decRef();
-	}
-
-	private LuanTable doc(LuanState luan,int docID) throws LuanException, IOException {
-		return LuceneDocument.toTable(luan,searcher.doc(docID));
-	}
-/*
-	TopDocs search(Query query,int n) throws IOException {
-		return searcher.search(query,n);
-	}
-
-	TopFieldDocs search(Query query,int n,Sort sort) throws IOException {
-		return searcher.search(query,n,sort);
-	}
-*/
-	// luan
-
-	private static final LuanFunction nothingFn = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			return LuanFunction.NOTHING;
-		}
-	};
-
-	private static abstract class MyCollector extends Collector {
-		int docBase;
-
-		@Override public void setScorer(Scorer scorer) {}
-		@Override public void setNextReader(AtomicReaderContext context) {
-			this.docBase = context.docBase;
-		}
-		@Override public boolean acceptsDocsOutOfOrder() {
-			return true;
-		}
-	}
-
-	@LuanMethod public Object[] search( final LuanState luan, String queryStr, Object nObj, String sortStr ) throws LuanException, IOException, ParseException {
-		Utils.checkNotNull(luan,queryStr);
-		Query query = index.parseQuery(queryStr);
-		if( nObj instanceof LuanFunction ) {
-			final LuanFunction fn = (LuanFunction)nObj;
-			Collector col = new MyCollector() {
-				@Override public void collect(int doc) {
-					try {
-						try {
-							LuanTable docTbl = doc(luan,docBase+doc);
-							luan.call(fn,new Object[]{docTbl});
-						} catch(IOException e) {
-							throw luan.exception(e);
-						}
-					} catch(LuanException e) {
-						throw new LuanRuntimeException(e);
-					}
-				}
-			};
-			try {
-				searcher.search(query,col);
-			} catch(LuanRuntimeException e) {
-				throw (LuanException)e.getCause();
-			}
-			return LuanFunction.NOTHING;
-		}
-		Integer nI = Luan.asInteger(nObj);
-		if( nI == null )
-			throw luan.exception("bad argument #2 (integer or function expected, got "+Luan.type(nObj)+")");
-		int n = nI;
-		if( n==0 ) {
-			TotalHitCountCollector thcc = new TotalHitCountCollector();
-			searcher.search(query,thcc);
-			return new Object[]{ nothingFn, 0, thcc.getTotalHits() };
-		}
-		Sort sort = sortStr==null ? null : index.parseSort(sortStr);
-		TopDocs td = sort==null ? searcher.search(query,n) : searcher.search(query,n,sort);
-		final ScoreDoc[] scoreDocs = td.scoreDocs;
-		LuanFunction results = new LuanFunction() {
-			int i = 0;
-
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				if( i >= scoreDocs.length )
-					return LuanFunction.NOTHING;
-				try {
-					LuanTable doc = doc(luan,scoreDocs[i++].doc);
-					return doc;
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-		return new Object[]{ results, scoreDocs.length, td.totalHits };
-	}
-
-	private void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.rawPut( method, new LuanJavaFunction(LuceneSearcher.class.getMethod(method,parameterTypes),this) );
-	}
-
-	LuanTable table() {
-		LuanTable tbl = new LuanTable();
-		try {
-			add( tbl, "search", LuanState.class, String.class, Object.class, String.class );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return tbl;
-	}
-
-}
--- a/lucene/src/luan/modules/lucene/LuceneWriter.java	Fri Jun 12 19:11:44 2015 -0600
+++ b/lucene/src/luan/modules/lucene/LuceneWriter.java	Sun Jun 14 01:34:42 2015 -0600
@@ -98,6 +98,7 @@
 		} catch(NoSuchMethodException e) {
 			throw new RuntimeException(e);
 		}
+		tbl.rawPut("index",index.myTable);
 		return tbl;
 	}
 
--- a/lucene/src/luan/modules/lucene/Web_search.luan	Fri Jun 12 19:11:44 2015 -0600
+++ b/lucene/src/luan/modules/lucene/Web_search.luan	Sun Jun 14 01:34:42 2015 -0600
@@ -133,19 +133,17 @@
 		end
 		local rows = string_to_number(Http.request.parameter.rows)
 		local sort = Http.request.parameter.sort
-		index.Searcher( function(searcher)
-			local results, length, total_hits = searcher.search(query,rows,sort)
-			local headers = {}
-			local table = {}
-			for doc in results do
-				local row = {}
-				for field, value in pairs(doc) do
-					row[index_of(headers,field)] = value
-				end
-				table[#table+1] = row
+		local results = index.search(query,1,rows,sort)
+		local headers = {}
+		local table = {}
+		for _, doc in ipairs(results) do
+			local row = {}
+			for field, value in pairs(doc) do
+				row[index_of(headers,field)] = value
 			end
-			result(query,sort,headers,table)
-		end )
+			table[#table+1] = row
+		end
+		result(query,sort,headers,table)
 	end
 
 end