Implemented LuceneQuery.list, LuceneQuery.uniqueResult and LuceneQuery.count, created tests for these as well.

This commit is contained in:
Vesa Martilla 2010-03-23 13:42:09 +00:00
parent 9a1cb7bc00
commit 845b7dd5d4
2 changed files with 305 additions and 23 deletions

View File

@ -1,13 +1,25 @@
/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*
*/
package com.mysema.query.search;
import org.hibernate.Session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import com.mysema.query.QueryException;
import com.mysema.query.QueryModifiers;
import com.mysema.query.SearchResults;
import com.mysema.query.SimpleProjectable;
import com.mysema.query.SimpleQuery;
import com.mysema.query.support.QueryMixin;
@ -18,47 +30,148 @@ import com.mysema.query.types.expr.EBoolean;
/**
* @author tiwe
*
* @param <T>
* @param <Document>
*/
public abstract class LuceneQuery<T> implements SimpleQuery<LuceneQuery<T>>, SimpleProjectable<T>{
private final Path<?> entityPath;
private final QueryMixin<LuceneQuery<T>> queryMixin;
private final Session session;
public LuceneQuery(Session session, Path<?> entityPath) {
this.session = session;
this.entityPath = entityPath;
this.queryMixin = new QueryMixin<LuceneQuery<T>>(this);
public class LuceneQuery implements SimpleQuery<LuceneQuery>, SimpleProjectable<Document>{
// private final Path<?> entityPath;
private final QueryMixin<LuceneQuery> queryMixin;
private final LuceneSerializer serializer;
private final Searcher searcher;
// TODO Is there an alternative for this?
private static final int MAX_RESULT_COUNT = 30000;
public LuceneQuery(Path<?> entityPath, LuceneSerializer serializer, Searcher searcher) {
// this.entityPath = entityPath;
this.queryMixin = new QueryMixin<LuceneQuery>(this);
this.serializer = serializer;
this.searcher = searcher;
}
@Override
public LuceneQuery<T> limit(long limit) {
public LuceneQuery limit(long limit) {
return queryMixin.limit(limit);
}
@Override
public LuceneQuery<T> offset(long offset) {
public LuceneQuery offset(long offset) {
return queryMixin.offset(offset);
}
@Override
public LuceneQuery<T> orderBy(OrderSpecifier<?>... o) {
public LuceneQuery orderBy(OrderSpecifier<?>... o) {
return queryMixin.orderBy(o);
}
@Override
public LuceneQuery<T> restrict(QueryModifiers modifiers) {
public LuceneQuery restrict(QueryModifiers modifiers) {
return queryMixin.restrict(modifiers);
}
@Override
public LuceneQuery<T> where(EBoolean... e) {
public LuceneQuery where(EBoolean... e) {
return queryMixin.where(e);
}
// TODO : implementations of Projectable methods
private Query createQuery() {
return serializer.toQuery(queryMixin.getMetadata().getWhere());
}
@Override
public long count() {
try {
return searcher.search(createQuery(), MAX_RESULT_COUNT).totalHits;
} catch (IOException e) {
// TODO
return 0;
}
}
@Override
public long countDistinct() {
// TODO Auto-generated method stub
return 0;
}
@Override
public List<Document> list() {
List<OrderSpecifier<?>> orderBys = queryMixin.getMetadata().getOrderBy();
if (!orderBys.isEmpty()) {
return listSorted(orderBys);
}
List<Document> documents = new ArrayList<Document>();
try {
ScoreDoc[] scoreDocs = searcher.search(createQuery(), MAX_RESULT_COUNT).scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
} catch (IOException e) {
// TODO ?
}
return documents;
}
private List<Document> listSorted(List<OrderSpecifier<?>> orderBys) {
List<Document> documents = new ArrayList<Document>();
List<SortField> sortFields = new ArrayList<SortField>();
for (OrderSpecifier<?> orderSpecifier : orderBys) {
if (!(orderSpecifier.getTarget() instanceof Path<?>)) {
throw new IllegalArgumentException("argument was not of type Path.");
}
sortFields.add(new SortField(toField((Path<?>)orderSpecifier.getTarget()), Locale.ENGLISH, !orderSpecifier.isAscending()));
}
Sort sort = new Sort();
sort.setSort(sortFields.toArray(new SortField[sortFields.size()]));
try {
ScoreDoc[] scoreDocs = searcher.search(createQuery(), null, MAX_RESULT_COUNT, sort).scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
} catch (IOException e) {
// TODO ?
}
return documents;
}
@Override
public List<Document> listDistinct() {
// TODO Auto-generated method stub
return null;
}
@Override
public SearchResults<Document> listDistinctResults() {
// TODO Auto-generated method stub
return null;
}
@Override
public SearchResults<Document> listResults() {
// TODO Auto-generated method stub
return null;
}
@Override
public Document uniqueResult() {
try {
ScoreDoc[] scoreDocs = searcher.search(createQuery(), MAX_RESULT_COUNT).scoreDocs;
if (scoreDocs.length > 1) {
throw new QueryException("More than one result found!");
}
return searcher.doc(scoreDocs[0].doc);
} catch (IOException e) {
// TODO ?
return null;
}
}
public String toField(Path<?> path) {
return path.getMetadata().getExpression().toString();
}
}

View File

@ -0,0 +1,169 @@
package com.mysema.query.search;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.List;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.mysema.query.QueryException;
import com.mysema.query.types.path.PString;
import com.mysema.query.types.path.PathBuilder;
/*
* TODO Refactor SimpleTest and LuceneQuery into same test class, lot of the setUp stuff is similar?
*/
public class LuceneQueryTest {
private LuceneQuery query;
private PathBuilder<Object> entityPath;
private PString title;
private PString year;
private RAMDirectory idx;
private IndexWriter writer;
private Searcher searcher;
private Document createDocument(String docTitle, String docAuthor, String docText, String docYear) {
Document doc = new Document();
doc.add(new Field("title", docTitle, Store.YES, Index.ANALYZED));
doc.add(new Field("author", docAuthor, Store.YES, Index.ANALYZED));
doc.add(new Field("text", docText, Store.YES, Index.ANALYZED));
doc.add(new Field("year", docYear, Store.YES, Index.ANALYZED));
return doc;
}
@Before
public void setUp() throws Exception {
entityPath = new PathBuilder<Object>(Object.class, "obj");
title = entityPath.getString("title");
year = entityPath.getString("year");
idx = new RAMDirectory();
writer = new IndexWriter(idx, new StandardAnalyzer(Version.LUCENE_CURRENT), true, MaxFieldLength.UNLIMITED);
writer.addDocument(createDocument("Jurassic Park", "Michael Crichton",
"It's a UNIX system! I know this!", "1990"));
writer.addDocument(createDocument("Nummisuutarit", "Aleksis Kivi",
"ESKO. Ja iloitset ja riemuitset?", "1864"));
writer.addDocument(createDocument(
"The Lord of the Rings",
"John R. R. Tolkien",
"One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them",
"1954"));
writer.addDocument(createDocument(
"Introduction to Algorithms",
"Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein",
"Bubble sort",
"1990"));
writer.optimize();
writer.close();
searcher = new IndexSearcher(idx);
query = new LuceneQuery(null, new LuceneSerializer(true), searcher);
}
@After
public void tearDown() throws Exception {
searcher.close();
}
@Test
public void count() {
query.where(title.eq("Jurassic Park"));
assertEquals(1, query.count());
}
@Test
public void list() {
query.where(year.between("1800", "2000"));
query.orderBy(year.asc());
List<Document> documents = query.list();
assertFalse(documents.isEmpty());
assertEquals(4, documents.size());
}
@Test
public void list_Sorted_Ascending_By_Year() {
query.where(year.between("1800", "2000"));
query.orderBy(year.asc());
List<Document> documents = query.list();
assertFalse(documents.isEmpty());
assertEquals(4, documents.size());
assertEquals("1864", documents.get(0).get("year"));
assertEquals("1954", documents.get(1).get("year"));
assertEquals("1990", documents.get(2).get("year"));
assertEquals("1990", documents.get(3).get("year"));
}
@Test
public void list_Sorted_Descending_By_Year() {
query.where(year.between("1800", "2000"));
query.orderBy(year.desc());
List<Document> documents = query.list();
assertFalse(documents.isEmpty());
assertEquals(4, documents.size());
assertEquals("1990", documents.get(0).get("year"));
assertEquals("1990", documents.get(1).get("year"));
assertEquals("1954", documents.get(2).get("year"));
assertEquals("1864", documents.get(3).get("year"));
}
@Test
public void list_Sorted_Descending_By_Year_And_Ascending_By_Title() {
query.where(year.between("1800", "2000"));
query.orderBy(year.desc());
query.orderBy(title.asc());
List<Document> documents = query.list();
assertFalse(documents.isEmpty());
assertEquals(4, documents.size());
assertEquals("1990", documents.get(0).get("year"));
assertEquals("1990", documents.get(1).get("year"));
assertEquals("Introduction to Algorithms", documents.get(0).get("title"));
assertEquals("Jurassic Park", documents.get(1).get("title"));
}
@Test
public void list_Sorted_Descending_By_Year_And_Descending_By_Title() {
query.where(year.between("1800", "2000"));
query.orderBy(year.desc());
query.orderBy(title.desc());
List<Document> documents = query.list();
assertFalse(documents.isEmpty());
assertEquals(4, documents.size());
assertEquals("1990", documents.get(0).get("year"));
assertEquals("1990", documents.get(1).get("year"));
assertEquals("Jurassic Park", documents.get(0).get("title"));
assertEquals("Introduction to Algorithms", documents.get(1).get("title"));
}
@Test
public void uniqueResult() {
query.where(title.startsWith("Nummi"));
Document document = query.uniqueResult();
assertEquals("Nummisuutarit", document.get("title"));
}
@Test(expected = QueryException.class)
public void uniqueResult_Finds_More_Than_One_Result() {
query.where(year.eq("1990"));
query.uniqueResult();
}
}