SearchResultImpl.java 8.86 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Node
 *  javax.jcr.RepositoryException
 *  javax.jcr.query.Row
 *  javax.jcr.query.RowIterator
 *  org.apache.sling.api.resource.Resource
 *  org.slf4j.Logger
 */
package com.day.cq.search.impl.result;

import com.day.cq.search.facets.Facet;
import com.day.cq.search.impl.builder.QueryImpl;
import com.day.cq.search.impl.result.HitImpl;
import com.day.cq.search.impl.result.ResultPageImpl;
import com.day.cq.search.result.Hit;
import com.day.cq.search.result.ResultPage;
import com.day.cq.search.result.SearchResult;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
import org.apache.sling.api.resource.Resource;
import org.slf4j.Logger;

public class SearchResultImpl
implements SearchResult {
    private final Logger log;
    private final QueryImpl query;
    private final long executionTime;
    private boolean guessedTotal;
    private long totalMatches;
    private final boolean hasMore;
    private final long startIndex;
    private final long hitsPerPage;
    private final List<Hit> hits;
    private List<ResultPage> resultPages;
    private Map<String, Facet> facets;

    public SearchResultImpl(QueryImpl query, RowIterator rows, long startIndex, long hitsPerPage, long startTime, QueryImpl.Optional<QueryImpl.FilteringRowIterator> filteringIterator, Logger log) {
        this.log = log;
        this.query = query;
        this.hitsPerPage = hitsPerPage;
        try {
            rows.skip(startIndex);
        }
        catch (NoSuchElementException e) {
            // empty catch block
        }
        this.startIndex = rows.getPosition();
        ArrayList<HitImpl> hits = new ArrayList<HitImpl>();
        for (long i = 0; (hitsPerPage <= 0 || i < hitsPerPage) && rows.hasNext(); ++i) {
            hits.add(new HitImpl(query, rows.nextRow(), this.startIndex + i));
        }
        this.hits = Collections.unmodifiableList(hits);
        this.guessedTotal = false;
        long size = rows.getSize();
        if (size >= 0) {
            this.totalMatches = size;
            this.hasMore = false;
        } else {
            this.guessedTotal = true;
            this.totalMatches = rows.getPosition();
            long maxTotal = query.getMaxTotal();
            if (maxTotal >= 0) {
                while (this.totalMatches < maxTotal && rows.hasNext()) {
                    ++this.totalMatches;
                    rows.nextRow();
                }
            }
            this.hasMore = rows.hasNext();
            log.debug(">> total is at least {} or more", (Object)this.totalMatches);
        }
        QueryImpl.FilteringRowIterator filterIter = filteringIterator.get();
        if (filterIter != null) {
            log.debug("filtering overhead was {} ms", (Object)filterIter.getFilteringTime());
        }
        this.executionTime = System.currentTimeMillis() - startTime;
        log.debug("entire query execution took {} ms", (Object)this.executionTime);
    }

    @Override
    public long getTotalMatches() {
        return this.totalMatches;
    }

    @Override
    public boolean hasMore() {
        return this.hasMore;
    }

    @Override
    public long getStartIndex() {
        return this.startIndex;
    }

    @Override
    public long getHitsPerPage() {
        return this.hitsPerPage;
    }

    @Override
    public List<Hit> getHits() {
        return this.hits;
    }

    @Override
    public List<ResultPage> getResultPages() {
        if (this.resultPages == null) {
            ArrayList<ResultPageImpl> resultPages = new ArrayList<ResultPageImpl>();
            if (this.hitsPerPage <= 0) {
                if (this.startIndex == 0) {
                    resultPages.add(new ResultPageImpl(0, 0, true));
                } else {
                    resultPages.add(new ResultPageImpl(0, 0, false));
                    resultPages.add(new ResultPageImpl(1, this.startIndex, true));
                }
            } else if (this.startIndex % this.hitsPerPage != 0) {
                int i = 0;
                if (this.startIndex > 0) {
                    resultPages.add(new ResultPageImpl(i++, 0, false));
                }
                resultPages.add(new ResultPageImpl(i++, this.startIndex, true));
                if (this.guessedTotal || this.totalMatches > this.startIndex + this.hitsPerPage) {
                    resultPages.add(new ResultPageImpl(i++, this.startIndex + this.hitsPerPage, false));
                }
            } else {
                long maxPage = this.totalMatches / this.hitsPerPage;
                if (this.totalMatches % this.hitsPerPage > 0) {
                    ++maxPage;
                }
                if (this.guessedTotal) {
                    ++maxPage;
                }
                long currentPageIndex = this.startIndex / this.hitsPerPage;
                maxPage = Math.min(maxPage, currentPageIndex + 10);
                for (long i = minPage = Math.max((long)0, (long)(currentPageIndex - 10)); i < maxPage; ++i) {
                    resultPages.add(new ResultPageImpl(i, i * this.hitsPerPage, i == currentPageIndex));
                }
            }
            this.resultPages = Collections.unmodifiableList(resultPages);
        }
        return this.resultPages;
    }

    @Override
    public ResultPage getPreviousPage() {
        ResultPage previous = null;
        for (ResultPage p : this.getResultPages()) {
            if (p.isCurrentPage()) break;
            previous = p;
        }
        return previous;
    }

    @Override
    public ResultPage getNextPage() {
        Iterator<ResultPage> it = this.getResultPages().iterator();
        while (it.hasNext()) {
            ResultPage p = it.next();
            if (!p.isCurrentPage() || !it.hasNext()) continue;
            return it.next();
        }
        return null;
    }

    public static String formatExecutionTime(long time) {
        double seconds = (double)time / 1000.0;
        NumberFormat format = NumberFormat.getNumberInstance();
        format.setMinimumFractionDigits(2);
        format.setMaximumFractionDigits(2);
        return format.format(seconds);
    }

    @Override
    public String getExecutionTime() {
        return SearchResultImpl.formatExecutionTime(this.executionTime);
    }

    @Override
    public long getExecutionTimeMillis() {
        return this.executionTime;
    }

    @Override
    public Map<String, Facet> getFacets() throws RepositoryException {
        if (this.facets == null) {
            this.facets = this.query.extractFacets();
        }
        return this.facets;
    }

    @Override
    public String getQueryStatement() {
        return this.query.getStatement();
    }

    @Override
    public String getFilteringPredicates() {
        return this.query.getFilteringPredicates();
    }

    @Override
    public Iterator<Node> getNodes() {
        return new HitBasedNodeIterator(this.hits.iterator());
    }

    @Override
    public Iterator<Resource> getResources() {
        return new HitBasedResourceIterator(this.hits.iterator());
    }

    private class HitBasedResourceIterator
    implements Iterator<Resource> {
        private Iterator<Hit> hits;

        public HitBasedResourceIterator(Iterator<Hit> hits) {
            this.hits = hits;
        }

        @Override
        public boolean hasNext() {
            return this.hits.hasNext();
        }

        @Override
        public Resource next() {
            try {
                return this.hits.next().getResource();
            }
            catch (RepositoryException e) {
                SearchResultImpl.this.log.error("Could not get resource behind search result hit", (Throwable)e);
                return null;
            }
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("remove not allowed for resource iterator on search result");
        }
    }

    private class HitBasedNodeIterator
    implements Iterator<Node> {
        private Iterator<Hit> hits;

        public HitBasedNodeIterator(Iterator<Hit> hits) {
            this.hits = hits;
        }

        @Override
        public boolean hasNext() {
            return this.hits.hasNext();
        }

        @Override
        public Node next() {
            try {
                return this.hits.next().getNode();
            }
            catch (RepositoryException e) {
                SearchResultImpl.this.log.error("Could not get node behind search result hit", (Throwable)e);
                return null;
            }
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("remove not allowed for node iterator on search result");
        }
    }

}