net.conquiris.search.AbstractSearcher.java Source code

Java tutorial

Introduction

Here is the source code for net.conquiris.search.AbstractSearcher.java

Source

/*
 * Copyright (C) the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.conquiris.search;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import net.conquiris.api.search.CountResult;
import net.conquiris.api.search.Highlight;
import net.conquiris.api.search.Highlight.HighlightedQuery;
import net.conquiris.api.search.HitMapper;
import net.conquiris.api.search.IndexNotAvailableException;
import net.conquiris.api.search.ItemResult;
import net.conquiris.api.search.PageResult;
import net.conquiris.api.search.SearchException;
import net.conquiris.api.search.Searcher;
import net.conquiris.lucene.search.Hit;
import net.conquiris.lucene.search.ScoredTotalHitCountCollector;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldDocs;
import org.apache.lucene.search.TotalHitCountCollector;

import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;

/**
 * Abstract searcher implementation.
 * @author Andres Rodriguez
 */

abstract class AbstractSearcher implements Searcher {
    /** Constructor. */
    AbstractSearcher() {
    }

    /**
     * Fetch searcher to use.
     * @throws IndexNotAvailableException if an error occurs.
     */
    abstract IndexSearcher getIndexSearcher();

    /**
     * Dispose used searcher.
     * @param searcher Searcher to dispose.
     */
    abstract void disposeIndexSearcher(IndexSearcher searcher);

    /**
     * Performs a primitive operation.
     * @param operation Operation to perform.
     * @return Operation return value.
     */
    <T> T perform(Op<T> operation) {
        final IndexSearcher searcher = getIndexSearcher();
        try {
            return operation.apply(searcher);
        } finally {
            disposeIndexSearcher(searcher);
        }
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#doc(int)
     */
    public final Document doc(final int i) {
        return perform(new Op<Document>() {
            @Override
            Document perform(IndexSearcher searcher) throws Exception {
                return searcher.doc(i);
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#rewrite(org.apache.lucene.search.Query)
     */
    public final Query rewrite(final Query query) {
        return perform(new Op<Query>() {
            @Override
            Query perform(IndexSearcher searcher) throws Exception {
                return searcher.rewrite(query);
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#search(org.apache.lucene.search.Query,
     * org.apache.lucene.search.Filter, org.apache.lucene.search.Collector)
     */
    public final void search(final Query query, final Filter filter, final Collector results) {
        perform(new Op<Object>() {
            @Override
            Object perform(IndexSearcher searcher) throws Exception {
                searcher.search(query, filter, results);
                return null;
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#search(org.apache.lucene.search.Query,
     * org.apache.lucene.search.Filter, int)
     */
    public final TopDocs search(final Query query, final Filter filter, final int n) {
        return perform(new Op<TopDocs>() {
            @Override
            TopDocs perform(IndexSearcher searcher) throws Exception {
                return searcher.search(query, filter, n);
            }
        });
    }

    public final TopFieldDocs search(final Query query, final Filter filter, final int n, final Sort sort) {
        return perform(new Op<TopFieldDocs>() {
            @Override
            TopFieldDocs perform(IndexSearcher searcher) throws Exception {
                return searcher.search(query, filter, n, sort);
            }
        });
    }

    /** TopDocs helper method. */
    private TopDocs getTopDocs(IndexSearcher searcher, Query query, Filter filter, Sort sort, int hits)
            throws IOException {
        final TopDocs docs;
        if (sort == null) {
            docs = searcher.search(query, filter, hits);
        } else {
            docs = searcher.search(query, filter, hits, sort);
        }
        return docs;
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#getFirst(net.conquiris.api.search.DocMapper,
     * org.apache.lucene.search.Query, org.apache.lucene.search.Filter, org.apache.lucene.search.Sort,
     * net.conquiris.api.search.Highlight)
     */
    public final <T> ItemResult<T> getFirst(final HitMapper<T> mapper, final Query query,
            final @Nullable Filter filter, final @Nullable Sort sort, final @Nullable Highlight highlight) {
        return perform(new Op<ItemResult<T>>() {
            public ItemResult<T> perform(IndexSearcher searcher) throws Exception {
                Stopwatch w = Stopwatch.createStarted();
                Query rewritten = searcher.rewrite(query);
                TopDocs docs = getTopDocs(searcher, query, filter, sort, 1);
                if (docs.totalHits > 0) {
                    ScoreDoc sd = docs.scoreDocs[0];
                    HighlightedQuery highlighted = MoreObjects.firstNonNull(highlight, Highlight.no())
                            .highlight(rewritten);
                    float score = sd.score;
                    T item = map(searcher, sd, highlighted, mapper);
                    return ItemResult.found(docs.totalHits, score, w.elapsed(TimeUnit.MILLISECONDS), item);
                } else {
                    return ItemResult.notFound(w.elapsed(TimeUnit.MILLISECONDS));
                }
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#getPage(net.conquiris.api.search.DocMapper,
     * org.apache.lucene.search.Query, int, int, org.apache.lucene.search.Filter,
     * org.apache.lucene.search.Sort, net.conquiris.api.search.Highlight)
     */
    public final <T> PageResult<T> getPage(final HitMapper<T> mapper, final Query query, final int firstRecord,
            final int maxRecords, final @Nullable Filter filter, final @Nullable Sort sort,
            final @Nullable Highlight highlight) {

        // Corner case
        if (maxRecords < 1) {
            CountResult r = getCount(query, filter, true);
            return PageResult.notFound(r.getTotalHits(), r.getMaxScore(), r.getTime(), firstRecord);
        }

        // Normal operation
        return perform(new Op<PageResult<T>>() {
            public PageResult<T> perform(IndexSearcher searcher) throws Exception {
                Stopwatch w = Stopwatch.createStarted();
                int total = firstRecord + maxRecords;
                Query rewritten = searcher.rewrite(query);
                TopDocs docs = getTopDocs(searcher, rewritten, filter, sort, total);
                if (docs.totalHits > 0) {
                    int n = Math.min(total, docs.scoreDocs.length);
                    float score = docs.getMaxScore();
                    if (n > firstRecord) {
                        final List<T> items = Lists.newArrayListWithCapacity(n - firstRecord);
                        HighlightedQuery highlighted = MoreObjects.firstNonNull(highlight, Highlight.no())
                                .highlight(rewritten);
                        for (int i = firstRecord; i < n; i++) {
                            ScoreDoc sd = docs.scoreDocs[i];
                            T item = map(searcher, sd, highlighted, mapper);
                            items.add(item);
                        }
                        return PageResult.found(docs.totalHits, score, w.elapsed(TimeUnit.MILLISECONDS),
                                firstRecord, items);
                    } else {
                        return PageResult.notFound(docs.totalHits, score, w.elapsed(TimeUnit.MILLISECONDS),
                                firstRecord);
                    }
                } else {
                    return PageResult.notFound(w.elapsed(TimeUnit.MILLISECONDS), firstRecord);
                }
            }
        });
    }

    /*
     * (non-Javadoc)
     * @see net.conquiris.api.search.Searcher#getCount(org.apache.lucene.search.Query,
     * org.apache.lucene.search.Filter, boolean)
     */
    @Override
    public CountResult getCount(final Query query, final @Nullable Filter filter, final boolean score) {
        return perform(new Op<CountResult>() {
            public CountResult perform(IndexSearcher searcher) throws Exception {
                final Stopwatch w = Stopwatch.createStarted();
                final ScoredTotalHitCountCollector scoredCollector;
                final TotalHitCountCollector collector;
                if (score) {
                    scoredCollector = new ScoredTotalHitCountCollector();
                    collector = scoredCollector;
                } else {
                    scoredCollector = null;
                    collector = new TotalHitCountCollector();
                }
                searcher.search(query, filter, collector);
                final float maxScore = score ? scoredCollector.getMaxScore() : 1.0f;
                return CountResult.of(collector.getTotalHits(), maxScore, w.elapsed(TimeUnit.MILLISECONDS));
            }
        });
    }

    /**
     * Searcher primitive operation.
     * @param <T> Return type.
     */
    abstract class Op<T> implements Function<IndexSearcher, T> {
        @Override
        public final T apply(IndexSearcher searcher) {
            try {
                return perform(searcher);
            } catch (SearchException e) {
                throw e;
            } catch (Exception e) {
                throw new IndexNotAvailableException(e);
            }
        }

        final <H> H map(IndexSearcher searcher, ScoreDoc sd, HighlightedQuery q, HitMapper<H> mapper)
                throws Exception {
            final int id = sd.doc;
            final float score = sd.score;
            final Document doc;
            FieldSelector selector = mapper.getFieldSelector();
            if (selector == null) {
                doc = searcher.doc(id);
            } else {
                doc = searcher.doc(id, selector);
            }
            final Hit hit = Hit.of(id, score, doc, q.getFragments(doc));
            return mapper.apply(hit);
        }

        abstract T perform(IndexSearcher searcher) throws Exception;
    }

}