/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.service.text;
import com.foundationdb.qp.operator.CursorLifecycle;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.qp.operator.RowCursorImpl;
import com.foundationdb.qp.row.HKey;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.row.ValuesHKey;
import com.foundationdb.qp.rowtype.HKeyRowType;
import com.foundationdb.server.error.AkibanInternalException;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class FullTextCursor extends RowCursorImpl
{
private final QueryContext context;
private final HKeyRowType rowType;
private final SearcherManager searcherManager;
private final Query query;
private final int limit;
//private final StoreAdapter adapter;
private IndexSearcher searcher;
private TopDocs results = null;
private int position;
public static final Sort SORT = new Sort(SortField.FIELD_SCORE,
new SortField(IndexedField.KEY_FIELD,
SortField.Type.STRING));
private static final Logger logger = LoggerFactory.getLogger(FullTextCursor.class);
public FullTextCursor(QueryContext context, HKeyRowType rowType,
SearcherManager searcherManager, Query query, int limit) {
this.context = context;
this.rowType = rowType;
this.searcherManager = searcherManager;
this.query = query;
this.limit = limit;
//adapter = context.getStore();
searcher = searcherManager.acquire();
}
@Override
public void open() {
super.open();
logger.debug("FullTextCursor: open {}", query);
if (query == null) {
setIdle();
}
else {
try {
results = searcher.search(query, limit, SORT);
}
catch (IOException ex) {
throw new AkibanInternalException("Error searching index", ex);
}
}
position = 0;
}
@Override
public Row next() {
CursorLifecycle.checkIdleOrActive(this);
if (isIdle())
return null;
if (position >= results.scoreDocs.length) {
setIdle();
results = null;
return null;
}
Document doc;
try {
doc = searcher.doc(results.scoreDocs[position++].doc);
}
catch (IOException ex) {
throw new AkibanInternalException("Error reading document", ex);
}
Row row = toHkeyRow(doc.get(IndexedField.KEY_FIELD));
logger.debug("FullTextCursor: yield {}", row);
return row;
}
@Override
public void close() {
super.close();
results = null;
try {
searcherManager.release(searcher);
}
catch (IOException ex) {
throw new AkibanInternalException("Error releasing searcher", ex);
}
searcher = null;
}
/* Allocate a new <code>HKey</code> and copy the given
* key bytes into it. */
protected Row toHkeyRow(String encoded) {
HKey hkey = context.getStore().getKeyCreator().newHKey(rowType.hKey());
byte decodedBytes[] = RowIndexer.decodeString(encoded);
hkey.copyFrom(decodedBytes);
return (Row)(ValuesHKey)hkey;
}
}