public Query page(int offset, int pageSize) {
this.offset = offset;
this.pageSize = pageSize;
return this;
}
public Query all() {
pageSize = -1;
return this;
}
public Query reverse() {
this.reverse = true;
return this;
}
public Query orderBy(String... order) {
this.order = new String[order.length];
for (int i = 0; i < order.length; i++) {
this.order[i] = order[i] + (ConvertionUtils.isForcedUntokenized(clazz, order[i]) ? "_untokenized" : "");
}
return this;
}
private Sort getSort() throws SearchException {
Sort sort = new Sort();
if (order.length > 0) {
if (reverse) {
if (order.length != 1)
throw new SearchException("reverse can be used while sorting only one field with oderBy");
sort.setSort(new SortField(order[0], ConvertionUtils.getSortType(clazz, order[0]), true));
} else {
SortField[] fields = new SortField[order.length];
for (int i = 0; i < fields.length; i++) {
fields[i] = new SortField(order[i], ConvertionUtils.getSortType(clazz, order[i]));
}
sort.setSort(fields);
}
}
return sort;
}
/**
Executes the query and return directly JPABase objects (No score
information)
@return
*/
@SuppressWarnings("unchecked")
public List fetch() throws SearchException {
try {
List results = executeQuery(true);
List objects = new ArrayList();
for (QueryResult queryResult : results) {
objects.add(queryResult.object);
}
return (List) objects;
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
public List fetchIds() throws SearchException {
try {
List results = executeQuery(false);
List objects = new ArrayList();
for (QueryResult queryResult : results) {
objects.add(Long.parseLong(queryResult.id));
}
return objects;
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
public long count() throws SearchException {
try {
QueryParser qp= new QueryParser(Search.getLuceneVersion(), "_docID", Search.getAnalyser());
qp.setAllowLeadingWildcard(Boolean.parseBoolean(Play.configuration.getProperty(
"play.search.allowContainsRequest", "false")));
org.apache.lucene.search.Query luceneQuery =qp.parse(query);
topDocs = store.getIndexSearcher(clazz.getName()).search(luceneQuery, null, Integer.MAX_VALUE, getSort());
return topDocs.totalHits;
} catch (ParseException e) {
throw new SearchException(e);
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
/**
Executes the lucene query against the index. You get QueryResults.
@param fetch load the corresponding JPABase objects in the QueryResult
Object
@return
*/
public List executeQuery(boolean fetch) throws SearchException {
try {
if (topDocs == null) {
QueryParser qp= new QueryParser(Search.getLuceneVersion(), "_docID", Search.getAnalyser());
qp.setAllowLeadingWildcard(Boolean.parseBoolean(Play.configuration.getProperty(
"play.search.allowContainsRequest", "false")));
org.apache.lucene.search.Query luceneQuery =qp.parse(query);
BooleanQuery.setMaxClauseCount(Integer.parseInt(Play.configuration.getProperty(
"play.search.maxClauseCount", "1024")));
topDocs = indexSearcher.search(luceneQuery, null, Integer.MAX_VALUE, getSort());
}
List results = new ArrayList();
if (topDocs == null)
return results;
int l = topDocs.totalHits;
if (offset > l) {
return results;
}
List<Long> ids = new ArrayList<Long>();
if (pageSize > 0) {
for (int i = offset; i < (offset + pageSize > l ? l : offset + pageSize); i++) {
QueryResult qresult = new QueryResult();
qresult.score = topDocs.scoreDocs[i].score;
qresult.id = indexSearcher.doc(topDocs.scoreDocs[i].doc).get("_docID");
if (fetch) {
Object objectId = ConvertionUtils.getIdValueFromIndex(clazz, qresult.id);
qresult.object = (JPABase)JPA.em().find(clazz, objectId);
if (qresult.object == null)
throw new SearchException("Please re-index");
}
results.add(qresult);
}
} else {
for (int i = 0; i < l; i++) {
QueryResult qresult = new QueryResult();
qresult.score = topDocs.scoreDocs[i].score;
qresult.id = indexSearcher.doc(topDocs.scoreDocs[i].doc).get("_docID");
if (fetch) {
Object objectId = ConvertionUtils.getIdValueFromIndex(clazz, qresult.id);
qresult.object = (JPABase)JPA.em().find(clazz, objectId);
if (qresult.object == null)
throw new SearchException("Please re-index");
}
results.add(qresult);
}
}
return results;
} catch (ParseException e) {
throw new SearchException(e);
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
public static class QueryResult {
public String id;
public float score;
public JPABase object;
}
public static class SearchException extends RuntimeException {
public SearchException(String message, Throwable cause) {
super(message, cause);
}
public SearchException(Throwable cause) {
super(cause);
}
public SearchException(String message) {
super(message);
}
The patch is very simple for play.module.search In the functions count and executeQuery
allow you to perform "contains" search AND start with search
the best could be : using a parameter to manage this aspect in search
package play.modules.search;
import java.util.ArrayList; import java.util.List;
import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.search.TopDocs;
import play.Play; import play.db.jpa.JPA; import play.db.jpa.JPABase; import play.exceptions.UnexpectedException; import play.modules.search.store.ConvertionUtils; import play.modules.search.store.Store; import play.modules.search.Search; /**
@author jfp */ public class Query { private Class clazz;
protected String query;
protected Store store;
protected String[] order = new String[0];
protected int offset = 0;
protected int pageSize = 10;
protected boolean reverse = false;
protected IndexSearcher indexSearcher;
protected TopDocs topDocs = null;
protected Query(String query, Class clazz, Store store) {
this.query = query;
this.clazz = clazz;
this.store = store;
indexSearcher = store.getIndexSearcher(clazz.getName());
}
public Query page(int offset, int pageSize) { this.offset = offset; this.pageSize = pageSize; return this; }
public Query all() { pageSize = -1; return this; }
public Query reverse() { this.reverse = true; return this; }
public Query orderBy(String... order) { this.order = new String[order.length]; for (int i = 0; i < order.length; i++) { this.order[i] = order[i] + (ConvertionUtils.isForcedUntokenized(clazz, order[i]) ? "_untokenized" : ""); } return this; }
private Sort getSort() throws SearchException { Sort sort = new Sort(); if (order.length > 0) { if (reverse) { if (order.length != 1) throw new SearchException("reverse can be used while sorting only one field with oderBy"); sort.setSort(new SortField(order[0], ConvertionUtils.getSortType(clazz, order[0]), true)); } else { SortField[] fields = new SortField[order.length]; for (int i = 0; i < fields.length; i++) { fields[i] = new SortField(order[i], ConvertionUtils.getSortType(clazz, order[i])); } sort.setSort(fields); } } return sort; }
/**
public List fetchIds() throws SearchException {
try {
List results = executeQuery(false);
List objects = new ArrayList();
for (QueryResult queryResult : results) {
objects.add(Long.parseLong(queryResult.id));
}
return objects;
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
public long count() throws SearchException { try { QueryParser qp= new QueryParser(Search.getLuceneVersion(), "_docID", Search.getAnalyser()); qp.setAllowLeadingWildcard(Boolean.parseBoolean(Play.configuration.getProperty( "play.search.allowContainsRequest", "false"))); org.apache.lucene.search.Query luceneQuery =qp.parse(query); topDocs = store.getIndexSearcher(clazz.getName()).search(luceneQuery, null, Integer.MAX_VALUE, getSort()); return topDocs.totalHits; } catch (ParseException e) { throw new SearchException(e); } catch (Exception e) { throw new UnexpectedException(e); } }
/**
} catch (ParseException e) { throw new SearchException(e); } catch (Exception e) { throw new UnexpectedException(e); } }
public static class QueryResult { public String id;
}
public static class SearchException extends RuntimeException { public SearchException(String message, Throwable cause) { super(message, cause); }
} }