Started to create TypedQuery

This commit is contained in:
Lassi Immonen 2011-01-05 13:15:00 +00:00
parent e44cea842f
commit ca07ba45c3
11 changed files with 353 additions and 215 deletions

View File

@ -0,0 +1,211 @@
package com.mysema.query.lucene;
import java.io.IOException;
import java.util.List;
import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.MatchAllDocsQuery;
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 com.mysema.commons.lang.CloseableIterator;
import com.mysema.commons.lang.EmptyCloseableIterator;
import com.mysema.commons.lang.IteratorAdapter;
import com.mysema.query.QueryException;
import com.mysema.query.QueryMetadata;
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;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.ParamExpression;
import com.mysema.query.types.Predicate;
public class AbstractLuceneQuery<T,Q extends AbstractLuceneQuery<T,Q>> implements SimpleQuery<Q>,
SimpleProjectable<T> {
private final QueryMixin<Q> queryMixin;
private final Searcher searcher;
private final LuceneSerializer serializer;
private final Transformer<Document, T> transformer;
@SuppressWarnings("unchecked")
public AbstractLuceneQuery(final LuceneSerializer serializer, final Searcher searcher,
final Transformer<Document, T> transformer) {
queryMixin = new QueryMixin<Q>((Q) this);
this.serializer = serializer;
this.searcher = searcher;
this.transformer = transformer;
}
public AbstractLuceneQuery(final Searcher searcher, final Transformer<Document, T> transformer) {
this(LuceneSerializer.DEFAULT, searcher, transformer);
}
private long innerCount(){
try {
final int maxDoc = searcher.maxDoc();
if (maxDoc == 0) {
return 0;
}
return searcher.search(createQuery(), maxDoc).totalHits;
} catch (final IOException e) {
throw new QueryException(e);
}
}
@Override
public long count() {
return innerCount();
}
@Override
public long countDistinct() {
return innerCount();
}
private Query createQuery() {
if (queryMixin.getMetadata().getWhere() == null) {
return new MatchAllDocsQuery();
}
return serializer.toQuery(queryMixin.getMetadata().getWhere(),
queryMixin.getMetadata());
}
@Override
public Q distinct(){
return queryMixin.distinct();
}
@Override
public Q limit(final long limit) {
return queryMixin.limit(limit);
}
@Override
public CloseableIterator<T> iterate() {
final QueryMetadata metadata = queryMixin.getMetadata();
final List<OrderSpecifier<?>> orderBys = metadata.getOrderBy();
final Long queryLimit = metadata.getModifiers().getLimit();
final Long queryOffset = metadata.getModifiers().getOffset();
Sort sort = null;
int limit;
final int offset = queryOffset != null ? queryOffset.intValue() : 0;
try {
limit = searcher.maxDoc();
} catch (final IOException e) {
throw new QueryException(e);
}
if (queryLimit != null && queryLimit.intValue() < limit) {
limit = queryLimit.intValue();
}
if (!orderBys.isEmpty()) {
sort = serializer.toSort(orderBys);
}
try {
ScoreDoc[] scoreDocs;
if (sort != null) {
scoreDocs = searcher.search(createQuery(), null,
limit + offset, sort).scoreDocs;
} else {
scoreDocs = searcher.search(createQuery(), limit + offset).scoreDocs;
}
if (offset < scoreDocs.length) {
return new ResultIterator<T>(scoreDocs, offset, searcher, transformer);
} else {
return new EmptyCloseableIterator<T>();
}
} catch (final IOException e) {
throw new QueryException(e);
}
}
@Override
public CloseableIterator<T> iterateDistinct() {
return iterate();
}
private List<T> innerList(){
return new IteratorAdapter<T>(iterate()).asList();
}
@Override
public List<T> list() {
return innerList();
}
@Override
public List<T> listDistinct() {
return list();
}
@Override
public SearchResults<T> listDistinctResults() {
return listResults();
}
@Override
public SearchResults<T> listResults() {
List<T> documents = innerList();
/*
* TODO Get rid of count(). It could be implemented by iterating the
* list results in list* from n to m.
*/
return new SearchResults<T>(documents, queryMixin.getMetadata().getModifiers(), innerCount());
}
@Override
public Q offset(final long offset) {
return queryMixin.offset(offset);
}
@Override
public Q orderBy(final OrderSpecifier<?>... o) {
return queryMixin.orderBy(o);
}
@Override
public Q restrict(final QueryModifiers modifiers) {
return queryMixin.restrict(modifiers);
}
@Override
public <P> Q set(final ParamExpression<P> param, final P value) {
return queryMixin.set(param, value);
}
@Override
public T uniqueResult() {
try {
int maxDoc = searcher.maxDoc();
if (maxDoc == 0) {
return null;
}
final ScoreDoc[] scoreDocs = searcher.search(createQuery(), maxDoc).scoreDocs;
if (scoreDocs.length > 1) {
throw new QueryException("More than one result found!");
} else if (scoreDocs.length == 1) {
return transformer.transform(searcher.doc(scoreDocs[0].doc));
} else {
return null;
}
} catch (IOException e) {
throw new QueryException(e);
}
}
@Override
public Q where(final Predicate... e) {
return queryMixin.where(e);
}
}

View File

@ -5,211 +5,32 @@
*/
package com.mysema.query.lucene;
import java.io.IOException;
import java.util.List;
import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.MatchAllDocsQuery;
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 com.mysema.commons.lang.CloseableIterator;
import com.mysema.commons.lang.EmptyCloseableIterator;
import com.mysema.commons.lang.IteratorAdapter;
import com.mysema.query.QueryException;
import com.mysema.query.QueryMetadata;
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;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.ParamExpression;
import com.mysema.query.types.Predicate;
/**
* LuceneQuery is a Querydsl query implementation for Lucene queries.
*
* @author vema
*/
public class LuceneQuery implements SimpleQuery<LuceneQuery>,
SimpleProjectable<Document> {
private final QueryMixin<LuceneQuery> queryMixin;
private final Searcher searcher;
private final LuceneSerializer serializer;
public LuceneQuery(final LuceneSerializer serializer,
final Searcher searcher) {
queryMixin = new QueryMixin<LuceneQuery>(this);
this.serializer = serializer;
this.searcher = searcher;
}
public LuceneQuery(final Searcher searcher) {
this(LuceneSerializer.DEFAULT, searcher);
}
private long innerCount(){
try {
final int maxDoc = searcher.maxDoc();
if (maxDoc == 0) {
return 0;
}
return searcher.search(createQuery(), maxDoc).totalHits;
} catch (final IOException e) {
throw new QueryException(e);
}
}
@Override
public long count() {
return innerCount();
}
@Override
public long countDistinct() {
return innerCount();
}
private Query createQuery() {
if (queryMixin.getMetadata().getWhere() == null) {
return new MatchAllDocsQuery();
}
return serializer.toQuery(queryMixin.getMetadata().getWhere(),
queryMixin.getMetadata());
}
@Override
public LuceneQuery distinct(){
return queryMixin.distinct();
}
public class LuceneQuery extends AbstractLuceneQuery<Document, LuceneQuery>{
@Override
public LuceneQuery limit(final long limit) {
return queryMixin.limit(limit);
}
private static final Transformer<Document,Document> TRANSFORMER = new Transformer<Document,Document>() {
@Override
public CloseableIterator<Document> iterate() {
final QueryMetadata metadata = queryMixin.getMetadata();
final List<OrderSpecifier<?>> orderBys = metadata.getOrderBy();
final Long queryLimit = metadata.getModifiers().getLimit();
final Long queryOffset = metadata.getModifiers().getOffset();
Sort sort = null;
int limit;
final int offset = queryOffset != null ? queryOffset.intValue() : 0;
try {
limit = searcher.maxDoc();
} catch (final IOException e) {
throw new QueryException(e);
}
if (queryLimit != null && queryLimit.intValue() < limit) {
limit = queryLimit.intValue();
}
if (!orderBys.isEmpty()) {
sort = serializer.toSort(orderBys);
}
try {
ScoreDoc[] scoreDocs;
if (sort != null) {
scoreDocs = searcher.search(createQuery(), null,
limit + offset, sort).scoreDocs;
} else {
scoreDocs = searcher.search(createQuery(), limit + offset).scoreDocs;
}
if (offset < scoreDocs.length) {
return new DocumentIterator(scoreDocs, offset, searcher);
} else {
return new EmptyCloseableIterator<Document>();
}
} catch (final IOException e) {
throw new QueryException(e);
@Override
public Document transform(Document input) {
return input;
}
};
public LuceneQuery(Searcher searcher) {
super(searcher, TRANSFORMER);
}
@Override
public CloseableIterator<Document> iterateDistinct() {
return iterate();
}
private List<Document> innerList(){
return new IteratorAdapter<Document>(iterate()).asList();
}
@Override
public List<Document> list() {
return innerList();
}
@Override
public List<Document> listDistinct() {
return list();
}
@Override
public SearchResults<Document> listDistinctResults() {
return listResults();
}
@Override
public SearchResults<Document> listResults() {
List<Document> documents = innerList();
/*
* TODO Get rid of count(). It could be implemented by iterating the
* list results in list* from n to m.
*/
return new SearchResults<Document>(documents, queryMixin.getMetadata().getModifiers(), innerCount());
}
@Override
public LuceneQuery offset(final long offset) {
return queryMixin.offset(offset);
}
@Override
public LuceneQuery orderBy(final OrderSpecifier<?>... o) {
return queryMixin.orderBy(o);
}
@Override
public LuceneQuery restrict(final QueryModifiers modifiers) {
return queryMixin.restrict(modifiers);
}
@Override
public <T> LuceneQuery set(final ParamExpression<T> param, final T value) {
return queryMixin.set(param, value);
}
@Override
public Document uniqueResult() {
try {
int maxDoc = searcher.maxDoc();
if (maxDoc == 0) {
return null;
}
final ScoreDoc[] scoreDocs = searcher.search(createQuery(), maxDoc).scoreDocs;
if (scoreDocs.length > 1) {
throw new QueryException("More than one result found!");
} else if (scoreDocs.length == 1) {
return searcher.doc(scoreDocs[0].doc);
} else {
return null;
}
} catch (IOException e) {
throw new QueryException(e);
}
}
@Override
public LuceneQuery where(final Predicate... e) {
return queryMixin.where(e);
public LuceneQuery(LuceneSerializer luceneSerializer, Searcher searcher) {
super(luceneSerializer, searcher, TRANSFORMER);
}
}

View File

@ -5,6 +5,7 @@ package com.mysema.query.lucene;
import java.io.IOException;
import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Searcher;
@ -12,18 +13,21 @@ import org.apache.lucene.search.Searcher;
import com.mysema.commons.lang.CloseableIterator;
import com.mysema.query.QueryException;
public final class DocumentIterator implements CloseableIterator<Document> {
public final class ResultIterator<T> implements CloseableIterator<T> {
private final ScoreDoc[] scoreDocs;
private int cursor;
private final Searcher searcher;
private final Transformer<Document,T> transformer;
public DocumentIterator(ScoreDoc[] scoreDocs, int offset, Searcher searcher) {
public ResultIterator(ScoreDoc[] scoreDocs, int offset, Searcher searcher, Transformer<Document, T> transformer) {
this.scoreDocs = scoreDocs;
cursor = offset;
this.searcher = searcher;
this.transformer = transformer;
}
@Override
@ -32,9 +36,9 @@ public final class DocumentIterator implements CloseableIterator<Document> {
}
@Override
public Document next() {
public T next() {
try {
return searcher.doc(scoreDocs[cursor++].doc);
return transformer.transform(searcher.doc(scoreDocs[cursor++].doc));
} catch (IOException e) {
throw new QueryException(e);
}

View File

@ -0,0 +1,15 @@
package com.mysema.query.lucene;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Searcher;
import com.mysema.commons.lang.CloseableIterator;
public interface ResultTransformer<T> {
CloseableIterator<T> getIterator(ScoreDoc[] scoreDocs, int offset, Searcher searcher);
T transform(Document doc);
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2010 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.lucene;
import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.Searcher;
/**
* LuceneQuery is a typed query implementation for Lucene queries.
*
* @author vema
*/
public class TypedQuery<T> extends AbstractLuceneQuery<T, TypedQuery<T>> {
public TypedQuery(Searcher searcher, Transformer<Document, T> transformer) {
super(searcher, transformer);
}
public TypedQuery(LuceneSerializer serializer, Searcher searcher, Transformer<Document, T> transformer) {
super(serializer, searcher, transformer);
}
}

View File

@ -1,6 +1,7 @@
package com.mysema.query.lucene.session;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.TypedQuery;
public interface LuceneSession {
@ -12,6 +13,16 @@ public interface LuceneSession {
*/
LuceneQuery createQuery();
/**
* Creates a new typed query.
*
* @param <T> The result type
* @param clazz The result class
* @return A new typed query instance
* @throws SessionClosedException if session is closed
*/
<T> TypedQuery<T> createQuery(Class<T> clazz);
/**
* Adds documents to index. Creates a new index if the index is not
* available.

View File

@ -12,6 +12,8 @@ public interface LuceneWriter {
LuceneWriter addDocument(Document doc);
LuceneWriter addObject(Object object);
LuceneWriter deleteDocuments(Term term);
}

View File

@ -24,7 +24,10 @@ public class FileLockingWriter implements LuceneWriter, Leasable {
protected IndexWriter writer;
public FileLockingWriter(Directory directory, boolean createNew, long defaultLockTimeout) {
protected final LuceneSessionFactoryImpl sessionFactory;
public FileLockingWriter(Directory directory, boolean createNew, long defaultLockTimeout,
LuceneSessionFactoryImpl sessionFactory) {
IndexWriter.setDefaultWriteLockTimeout(defaultLockTimeout);
boolean create = createNew;
try {
@ -55,6 +58,7 @@ public class FileLockingWriter implements LuceneWriter, Leasable {
if (logger.isDebugEnabled()) {
logger.debug("Created writer " + writer);
}
this.sessionFactory = sessionFactory;
}
@Override
@ -67,6 +71,11 @@ public class FileLockingWriter implements LuceneWriter, Leasable {
}
}
@Override
public LuceneWriter addObject(Object object) {
return addDocument(sessionFactory.transformToDocument(object));
}
@Override
public LuceneWriter deleteDocuments(Term term) {
try {

View File

@ -6,6 +6,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.slf4j.Logger;
@ -72,7 +74,8 @@ public class LuceneSessionFactoryImpl implements LuceneSessionFactory {
}
public FileLockingWriter leaseWriter(boolean createNew) {
FileLockingWriter writer = new FileLockingWriter(directory, createNew, defaultLockTimeout);
FileLockingWriter writer =
new FileLockingWriter(directory, createNew, defaultLockTimeout, this);
lease(writer);
return writer;
}
@ -145,4 +148,17 @@ public class LuceneSessionFactoryImpl implements LuceneSessionFactory {
this.defaultLockTimeout = defaultLockTimeout;
}
public <T> Transformer<Document, T> getDocumentToObjectTransformer(Class<T> clazz) {
//Luodaan transformer laiskasti, säilytetään tallessa
//Tsek morphia
//Convertterit rdfbeanistä, uusi moduuli?
return null;
}
public Document transformToDocument(Object object) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -5,6 +5,7 @@ import javax.annotation.Nullable;
import com.mysema.query.QueryException;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.LuceneSerializer;
import com.mysema.query.lucene.TypedQuery;
import com.mysema.query.lucene.session.LuceneSession;
import com.mysema.query.lucene.session.LuceneWriter;
import com.mysema.query.lucene.session.SessionClosedException;
@ -37,11 +38,11 @@ public class LuceneSessionImpl implements LuceneSession {
return new LuceneQuery(serializer, getSearcher().getIndexSearcer());
}
private LuceneSearcher getSearcher() {
if (searcher == null) {
searcher = sessionFactory.leaseSearcher();
}
return searcher;
@Override
public <T> TypedQuery<T> createQuery(Class<T> clazz) {
checkClosed();
return new TypedQuery<T>(serializer, getSearcher().getIndexSearcer(),
sessionFactory.getDocumentToObjectTransformer(clazz));
}
@Override
@ -56,18 +57,6 @@ public class LuceneSessionImpl implements LuceneSession {
return getWriter(true);
}
private LuceneWriter getWriter(boolean createNew) {
if (readOnly) {
throw new SessionReadOnlyException("Read only session, cannot create writer");
}
if (writer == null) {
writer = sessionFactory.leaseWriter(createNew);
}
return writer;
}
@Override
public void close() {
checkClosed();
@ -110,6 +99,25 @@ public class LuceneSessionImpl implements LuceneSession {
}
private LuceneSearcher getSearcher() {
if (searcher == null) {
searcher = sessionFactory.leaseSearcher();
}
return searcher;
}
private LuceneWriter getWriter(boolean createNew) {
if (readOnly) {
throw new SessionReadOnlyException("Read only session, cannot create writer");
}
if (writer == null) {
writer = sessionFactory.leaseWriter(createNew);
}
return writer;
}
private void checkClosed() {
if (closed) {
throw new SessionClosedException("Session is closed");

View File

@ -0,0 +1,14 @@
package com.mysema.query.lucene.session.impl;
import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
public class ObjectToDocumentTransformer implements Transformer<Object, Document>{
@Override
public Document transform(Object input) {
// TODO Auto-generated method stub
return null;
}
}