All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysema.query.lucene.AbstractLuceneQuery Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
package com.mysema.query.lucene;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

import org.apache.commons.collections15.Transformer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.MapFieldSelector;
import org.apache.lucene.search.DuplicateFilter;
import org.apache.lucene.search.Filter;
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.DefaultQueryMetadata;
import com.mysema.query.NonUniqueResultException;
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.Path;
import com.mysema.query.types.Predicate;

/**
 * AbstractLuceneQuery is an abstract super class for Lucene query implementations
 *
 * @author tiwe
 *
 * @param  projection type
 * @param  concrete subtype of query
 */
public abstract class AbstractLuceneQuery> implements SimpleQuery,
SimpleProjectable {

    private final QueryMixin queryMixin;

    private final Searcher searcher;

    private final LuceneSerializer serializer;

    private final Transformer transformer;

    @Nullable
    private FieldSelector fieldSelector;

    @Nullable
    private Filter filter;

    @SuppressWarnings("unchecked")
    public AbstractLuceneQuery(LuceneSerializer serializer, Searcher searcher,
                               Transformer transformer) {
        queryMixin = new QueryMixin((Q) this, new DefaultQueryMetadata(false));
        this.serializer = serializer;
        this.searcher = searcher;
        this.transformer = transformer;
    }

    public AbstractLuceneQuery(Searcher searcher, Transformer transformer) {
        this(LuceneSerializer.DEFAULT, searcher, transformer);
    }

    @Override
    public boolean exists() {
        return innerCount() > 0;
    }

    @Override
    public boolean notExists() {
        return innerCount() == 0;
    }

    private long innerCount(){
        try {
            final int maxDoc = searcher.maxDoc();
            if (maxDoc == 0) {
                return 0;
            }
            return searcher.search(createQuery(), filter, maxDoc).totalHits;
        } catch (final IOException e) {
            throw new QueryException(e);
        }
    }

    @Override
    public long count() {
        return innerCount();
    }

    @Override
    public long countDistinct() {
        throw new UnsupportedOperationException("use distinct(path) instead");
    }

    private Query createQuery() {
        if (queryMixin.getMetadata().getWhere() == null) {
            return new MatchAllDocsQuery();
        }
        return serializer.toQuery(queryMixin.getMetadata().getWhere(), queryMixin.getMetadata());
    }

    @Override
    public Q distinct(){
        throw new UnsupportedOperationException("use distinct(path) instead");
    }

    /**
     * Add a DuplicateFilter for the field of the given property path
     *
     * @param property
     * @return
     */
    public Q distinct(Path property){
        return filter(new DuplicateFilter(serializer.toField(property)));
    }

    /**
     * Apply the given Lucene filter to the search results
     *
     * @param filter
     * @return
     */
    @SuppressWarnings("unchecked")
    public Q filter(Filter filter){
        this.filter = filter;
        return (Q)this;
    }

    @Override
    public Q limit(long limit) {
        return queryMixin.limit(limit);
    }

    @Override
    public CloseableIterator iterate() {
        final QueryMetadata metadata = queryMixin.getMetadata();
        final List> 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 = maxDoc();
            if (limit == 0) {
                return new EmptyCloseableIterator();
            }
        } 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;
            int sumOfLimitAndOffset = limit + offset;
            if (sumOfLimitAndOffset < 1) {
                throw new QueryException("The given limit (" + limit + ") and offset (" + offset + ") cause an integer overflow.");
            }
            if (sort != null) {
                scoreDocs = searcher.search(createQuery(), filter, sumOfLimitAndOffset, sort).scoreDocs;
            } else {
                scoreDocs = searcher.search(createQuery(), filter, sumOfLimitAndOffset).scoreDocs;
            }
            if (offset < scoreDocs.length) {
                return new ResultIterator(scoreDocs, offset, searcher, fieldSelector, transformer);
            }
            return new EmptyCloseableIterator();
        } catch (final IOException e) {
            throw new QueryException(e);
        }
    }

    @Override
    public CloseableIterator iterateDistinct() {
        throw new UnsupportedOperationException("use distinct(path) instead");
    }

    private List innerList(){
        return new IteratorAdapter(iterate()).asList();
    }

    @Override
    public List list() {
        return innerList();
    }

    /**
     * Set the given FieldSelector to the query
     *
     * @param fieldSelector
     * @return
     */
    @SuppressWarnings("unchecked")
    public Q load(FieldSelector fieldSelector){
        this.fieldSelector = fieldSelector;
        return (Q)this;
    }

    /**
     * Load only the fields of the given paths
     *
     * @param paths
     * @return
     */
    @SuppressWarnings("unchecked")
    public Q load(Path... paths){
        List fields = new ArrayList(paths.length);
        for (Path path : paths){
            fields.add(serializer.toField(path));
        }
        this.fieldSelector = new MapFieldSelector(fields);
        return (Q)this;
    }

    @Override
    public List listDistinct() {
        throw new UnsupportedOperationException("use distinct(path) instead");
    }

    @Override
    public SearchResults listDistinctResults() {
        throw new UnsupportedOperationException("use distinct(path) instead");
    }

    @Override
    public SearchResults listResults() {
        List 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(documents, queryMixin.getMetadata().getModifiers(), innerCount());
    }

    @Override
    public Q offset(long offset) {
        return queryMixin.offset(offset);
    }

    @Override
    public Q orderBy(OrderSpecifier... o) {
        return queryMixin.orderBy(o);
    }

    @Override
    public Q restrict(QueryModifiers modifiers) {
        return queryMixin.restrict(modifiers);
    }

    @Override
    public 

Q set(ParamExpression

param, P value) { return queryMixin.set(param, value); } @Nullable private T oneResult(boolean unique) { try { int maxDoc = maxDoc(); if (maxDoc == 0) { return null; } final ScoreDoc[] scoreDocs = searcher.search(createQuery(), filter, maxDoc).scoreDocs; int index = 0; QueryModifiers modifiers = queryMixin.getMetadata().getModifiers(); Long offset = modifiers.getOffset(); if (offset != null) { index = offset.intValue(); } Long limit = modifiers.getLimit(); if (unique && (limit == null ? scoreDocs.length - index > 1 : limit > 1 && scoreDocs.length > 1)) { throw new NonUniqueResultException("Unique result requested, but " + scoreDocs.length + " found."); } else if (scoreDocs.length > index) { Document document; if (fieldSelector != null){ document = searcher.doc(scoreDocs[index].doc, fieldSelector); }else{ document = searcher.doc(scoreDocs[index].doc); } return transformer.transform(document); } else { return null; } } catch (IOException e) { throw new QueryException(e); } } @Override public T singleResult() { return oneResult(false); } @Override public T uniqueResult() { return oneResult(true); } @Override public Q where(Predicate... e) { return queryMixin.where(e); } @Override public String toString() { return createQuery().toString(); } private int maxDoc() throws IOException { return searcher.maxDoc(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy