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.SetUtil;
024    import com.liferay.portal.kernel.util.Time;
025    
026    import java.util.ArrayList;
027    import java.util.List;
028    import java.util.Set;
029    
030    /**
031     * @author Tina Tian
032     */
033    public abstract class BaseSearchResultPermissionFilter
034            implements SearchResultPermissionFilter {
035    
036            @Override
037            public Hits search(SearchContext searchContext) throws SearchException {
038                    QueryConfig queryConfig = searchContext.getQueryConfig();
039    
040                    if (!queryConfig.isAllFieldsSelected()) {
041                            Set<String> selectedFieldNameSet = SetUtil.fromArray(
042                                    queryConfig.getSelectedFieldNames());
043    
044                            for (String selectedFieldName : _PERMISSION_SELECTED_FIELD_NAMES) {
045                                    selectedFieldNameSet.add(selectedFieldName);
046                            }
047    
048                            queryConfig.setSelectedFieldNames(
049                                    selectedFieldNameSet.toArray(
050                                            new String[selectedFieldNameSet.size()]));
051                    }
052    
053                    int end = searchContext.getEnd();
054                    int start = searchContext.getStart();
055    
056                    if ((end == QueryUtil.ALL_POS) && (start == QueryUtil.ALL_POS)) {
057                            Hits hits = getHits(searchContext);
058    
059                            filterHits(hits, searchContext);
060    
061                            return hits;
062                    }
063    
064                    if ((start < 0) || (start > end)) {
065                            return new HitsImpl();
066                    }
067    
068                    int excludedDocsSize = 0;
069                    int hitsSize = 0;
070                    int offset = 0;
071                    long startTime = 0;
072    
073                    List<Document> documents = new ArrayList<Document>();
074                    List<Float> scores = new ArrayList<Float>();
075    
076                    while (true) {
077                            int count = end - documents.size();
078    
079                            int amplifiedCount = (int)(
080                                    count * _INDEX_PERMISSION_FILTER_SEARCH_AMPLIFICATION_FACTOR);
081    
082                            int amplifiedEnd = offset + amplifiedCount;
083    
084                            searchContext.setEnd(amplifiedEnd);
085                            searchContext.setStart(offset);
086    
087                            Hits hits = getHits(searchContext);
088    
089                            if (startTime == 0) {
090                                    hitsSize = hits.getLength();
091                                    startTime = hits.getStart();
092                            }
093    
094                            Document[] oldDocs = hits.getDocs();
095    
096                            filterHits(hits, searchContext);
097    
098                            Document[] newDocs = hits.getDocs();
099    
100                            excludedDocsSize += oldDocs.length - newDocs.length;
101    
102                            collectHits(hits, documents, scores, count);
103    
104                            if ((newDocs.length >= count) ||
105                                    (oldDocs.length < amplifiedCount) ||
106                                    (amplifiedEnd >= hitsSize)) {
107    
108                                    updateHits(
109                                            hits, documents, scores, start, end,
110                                            hitsSize - excludedDocsSize, startTime);
111    
112                                    return hits;
113                            }
114    
115                            offset = amplifiedEnd;
116                    }
117            }
118    
119            protected void collectHits(
120                    Hits hits, List<Document> documents, List<Float> scores, int count) {
121    
122                    Document[] docs = hits.getDocs();
123    
124                    if (docs.length < count) {
125                            count = docs.length;
126                    }
127    
128                    for (int i = 0; i < count; i++) {
129                            documents.add(docs[i]);
130    
131                            scores.add(hits.score(i));
132                    }
133            }
134    
135            protected abstract void filterHits(Hits hits, SearchContext searchContext);
136    
137            protected abstract Hits getHits(SearchContext searchContext)
138                    throws SearchException;
139    
140            protected void updateHits(
141                    Hits hits, List<Document> documents, List<Float> scores, int start,
142                    int end, int size, long startTime) {
143    
144                    int[] startAndEnd = SearchPaginationUtil.calculateStartAndEnd(
145                            start, end, documents.size());
146    
147                    start = startAndEnd[0];
148                    end = startAndEnd[1];
149    
150                    documents = documents.subList(start, end);
151                    scores = scores.subList(start, end);
152    
153                    hits.setDocs(documents.toArray(new Document[documents.size()]));
154                    hits.setScores(ArrayUtil.toFloatArray(scores));
155                    hits.setLength(size);
156                    hits.setSearchTime(
157                            (float)(System.currentTimeMillis() - startTime) / Time.SECOND);
158            }
159    
160            private static final double
161                    _INDEX_PERMISSION_FILTER_SEARCH_AMPLIFICATION_FACTOR =
162                            GetterUtil.getDouble(
163                                    PropsUtil.get(
164                                            PropsKeys.
165                                                    INDEX_PERMISSION_FILTER_SEARCH_AMPLIFICATION_FACTOR));
166    
167            private static final String[] _PERMISSION_SELECTED_FIELD_NAMES =
168                    {Field.ENTRY_CLASS_NAME, Field.ENTRY_CLASS_PK};
169    
170    }