001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.search;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.dao.search.SearchPaginationUtil;
019    import com.liferay.portal.kernel.util.ArrayUtil;
020    import com.liferay.portal.kernel.util.GetterUtil;
021    import com.liferay.portal.kernel.util.PropsKeys;
022    import com.liferay.portal.kernel.util.PropsUtil;
023    import com.liferay.portal.kernel.util.Time;
024    
025    import java.util.ArrayList;
026    import java.util.List;
027    
028    /**
029     * @author Tina Tian
030     */
031    public abstract class BaseSearchResultPermissionFilter
032            implements SearchResultPermissionFilter {
033    
034            @Override
035            public Hits search(SearchContext searchContext) throws SearchException {
036                    int end = searchContext.getEnd();
037                    int start = searchContext.getStart();
038    
039                    if ((end == QueryUtil.ALL_POS) && (start == QueryUtil.ALL_POS)) {
040                            Hits hits = getHits(searchContext);
041    
042                            filterHits(hits, searchContext);
043    
044                            return hits;
045                    }
046    
047                    if ((start < 0) || (start > end)) {
048                            return new HitsImpl();
049                    }
050    
051                    int excludedDocsSize = 0;
052                    int hitsSize = 0;
053                    int offset = 0;
054                    long startTime = 0;
055    
056                    List<Document> documents = new ArrayList<Document>();
057                    List<Float> scores = new ArrayList<Float>();
058    
059                    while (true) {
060                            int count = end - documents.size();
061    
062                            int amplifiedCount = (int)(
063                                    count * _INDEX_PERMISSION_FILTER_SEARCH_AMPLIFICATION_FACTOR);
064    
065                            int amplifiedEnd = offset + amplifiedCount;
066    
067                            searchContext.setEnd(amplifiedEnd);
068                            searchContext.setStart(offset);
069    
070                            Hits hits = getHits(searchContext);
071    
072                            if (startTime == 0) {
073                                    hitsSize = hits.getLength();
074                                    startTime = hits.getStart();
075                            }
076    
077                            Document[] oldDocs = hits.getDocs();
078    
079                            filterHits(hits, searchContext);
080    
081                            Document[] newDocs = hits.getDocs();
082    
083                            excludedDocsSize += oldDocs.length - newDocs.length;
084    
085                            collectHits(hits, documents, scores, count);
086    
087                            if ((newDocs.length >= count) ||
088                                    (oldDocs.length < amplifiedCount) ||
089                                    (amplifiedEnd >= hitsSize)) {
090    
091                                    updateHits(
092                                            hits, documents, scores, start, end,
093                                            hitsSize - excludedDocsSize, startTime);
094    
095                                    return hits;
096                            }
097    
098                            offset = amplifiedEnd;
099                    }
100            }
101    
102            protected void collectHits(
103                    Hits hits, List<Document> documents, List<Float> scores, int count) {
104    
105                    Document[] docs = hits.getDocs();
106    
107                    if (docs.length < count) {
108                            count = docs.length;
109                    }
110    
111                    for (int i = 0; i < count; i++) {
112                            documents.add(docs[i]);
113    
114                            scores.add(hits.score(i));
115                    }
116            }
117    
118            protected abstract void filterHits(Hits hits, SearchContext searchContext);
119    
120            protected abstract Hits getHits(SearchContext searchContext)
121                    throws SearchException;
122    
123            protected void updateHits(
124                    Hits hits, List<Document> documents, List<Float> scores, int start,
125                    int end, int size, long startTime) {
126    
127                    int[] startAndEnd = SearchPaginationUtil.calculateStartAndEnd(
128                            start, end, documents.size());
129    
130                    start = startAndEnd[0];
131                    end = startAndEnd[1];
132    
133                    documents = documents.subList(start, end);
134                    scores = scores.subList(start, end);
135    
136                    hits.setDocs(documents.toArray(new Document[documents.size()]));
137                    hits.setScores(ArrayUtil.toFloatArray(scores));
138                    hits.setLength(size);
139                    hits.setSearchTime(
140                            (float)(System.currentTimeMillis() - startTime) / Time.SECOND);
141            }
142    
143            private static final double
144                    _INDEX_PERMISSION_FILTER_SEARCH_AMPLIFICATION_FACTOR =
145                            GetterUtil.getDouble(
146                                    PropsUtil.get(
147                                            PropsKeys.
148                                                    INDEX_PERMISSION_FILTER_SEARCH_AMPLIFICATION_FACTOR));
149    
150    }