blob: 2c2e6211775efc52219279419f3cee5ea4bed9d3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to you 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 org.apache.geode.cache.lucene.internal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.execute.Execution;
import org.apache.geode.cache.execute.FunctionException;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.cache.lucene.LuceneResultStruct;
import org.apache.geode.cache.lucene.PageableLuceneQueryResults;
import org.apache.geode.cache.lucene.internal.distributed.EntryScore;
import org.apache.geode.cache.lucene.internal.results.LuceneGetPageFunction;
import org.apache.geode.cache.lucene.internal.results.MapResultCollector;
/**
* Implementation of PageableLuceneQueryResults that fetches a page at a time from the server, given
* a set of EntryScores (key and score).
*
* @param <K> The type of the key
* @param <V> The type of the value
*/
public class PageableLuceneQueryResultsImpl<K, V> implements PageableLuceneQueryResults<K, V> {
/**
* list of docs matching search query
*/
private final List<EntryScore<K>> hits;
/**
* * Current page of results
*/
private List<LuceneResultStruct<K, V>> currentPage;
/**
* The maximum score. Lazily evaluated
*/
private float maxScore = Float.MIN_VALUE;
/**
* The user region where values are stored.
*/
private final Region<K, V> userRegion;
/**
* The start of the next page of results we want to fetch
*/
private int currentHit = 0;
/**
* The page size the user wants.
*/
private int pageSize;
public PageableLuceneQueryResultsImpl(List<EntryScore<K>> hits, Region<K, V> userRegion,
int pageSize) {
this.hits = hits;
this.userRegion = userRegion;
this.pageSize = pageSize == 0 ? Integer.MAX_VALUE : pageSize;
}
public List<LuceneResultStruct<K, V>> getHitEntries(int fromIndex, int toIndex) {
ArrayList<LuceneResultStruct<K, V>> results = null;
try {
List<EntryScore<K>> scores = hits.subList(fromIndex, toIndex);
Set<K> keys = new HashSet<K>(scores.size());
for (EntryScore<K> score : scores) {
keys.add(score.getKey());
}
Map<K, V> values = getValues(keys);
results = new ArrayList<LuceneResultStruct<K, V>>(values.size());
for (EntryScore<K> score : scores) {
V value = values.get(score.getKey());
if (value != null)
results.add(new LuceneResultStructImpl(score.getKey(), value, score.getScore()));
}
} catch (FunctionException functionException) {
if (functionException.getCause() instanceof RuntimeException) {
throw (RuntimeException) functionException.getCause();
}
throw functionException;
}
return results;
}
protected Map<K, V> getValues(final Set<K> keys) {
ResultCollector resultCollector = onRegion().withFilter(keys)
.withCollector(new MapResultCollector()).execute(LuceneGetPageFunction.ID);
return (Map<K, V>) resultCollector.getResult();
}
protected Execution onRegion() {
return FunctionService.onRegion(userRegion);
}
@Override
public List<LuceneResultStruct<K, V>> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
List<LuceneResultStruct<K, V>> result = advancePage();
currentPage = null;
return result;
}
private List<LuceneResultStruct<K, V>> advancePage() {
if (currentPage != null) {
return currentPage;
}
int resultSize = (pageSize != Integer.MAX_VALUE) ? pageSize : hits.size();
currentPage = new ArrayList<LuceneResultStruct<K, V>>(resultSize);
while (currentPage.size() < pageSize && currentHit < hits.size()) {
int end = currentHit + pageSize - currentPage.size();
end = end > hits.size() ? hits.size() : end;
currentPage.addAll(getHitEntries(currentHit, end));
currentHit = end;
}
return currentPage;
}
@Override
public boolean hasNext() {
advancePage();
if (currentPage.isEmpty()) {
return false;
}
return true;
}
@Override
public int size() {
return hits.size();
}
@Override
public float getMaxScore() {
if (maxScore == Float.MIN_VALUE) {
for (EntryScore<K> score : hits) {
maxScore = Math.max(maxScore, score.getScore());
}
}
return maxScore;
}
}