/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.query.clustered;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.util.PriorityQueue;
import org.infinispan.AdvancedCache;
import org.infinispan.query.impl.AbstractIterator;
import org.infinispan.util.ReflectionUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.UUID;
/**
* DistributedIterator.
*
* Iterates on a distributed query.
*
* @author Israel Lacerra <israeldl@gmail.com>
* @since 5.1
*/
public class DistributedIterator extends AbstractIterator {
private int currentIndex = -1;
// this array keeps all values (ordered) fetched by this iterator...
private final ArrayList<Object> orderedValues = new ArrayList<Object>();
private final Sort sort;
private HashMap<UUID, ClusteredTopDocs> topDocsResponses;
private PriorityQueue<ScoreDoc> hq;
private final int resultSize;
public DistributedIterator(Sort sort, int fetchSize, int resultSize,
HashMap<UUID, ClusteredTopDocs> topDocsResponses,
AdvancedCache<?, ?> cache) {
this.sort = sort;
this.fetchSize = fetchSize;
this.resultSize = resultSize;
this.cache = cache;
setTopDocs(topDocsResponses);
}
private void setTopDocs(HashMap<UUID, ClusteredTopDocs> topDocsResponses) {
this.topDocsResponses = topDocsResponses;
if (sort != null) {
// reversing sort fields to FieldDocSortedHitQueue work properly
for (SortField sf : sort.getSort()) {
boolean reverse = (Boolean) ReflectionUtil.getValue(sf, "reverse");
ReflectionUtil.setValue(sf, "reverse", !reverse);
}
hq = ISPNPriorityQueueFactory.getFieldDocSortedHitQueue(
topDocsResponses.size(), sort.getSort());
} else
hq = ISPNPriorityQueueFactory.getHitQueue(topDocsResponses.size());
// taking the first value of each queue
for (ClusteredTopDocs ctp : topDocsResponses.values()) {
if (ctp.hasNext())
hq.add(ctp.getNext());
}
}
@Override
public void close() {
// Nothing to do...
}
@Override
public void jumpToResult(int index) throws IndexOutOfBoundsException {
currentIndex = index;
}
@Override
public void add(Object arg0) {
throw new UnsupportedOperationException(
"Not supported as you are trying to change something in the cache. Please use searchableCache.put()");
}
@Override
public Object next() {
if (!hasNext())
throw new NoSuchElementException("Out of boundaries");
currentIndex++;
return current();
}
@Override
public int nextIndex() {
if (!hasNext())
throw new NoSuchElementException("Out of boundaries");
return currentIndex + 1;
}
@Override
public Object previous() {
currentIndex--;
return current();
}
private Object current() {
// if already fecthed
if (orderedValues.size() > currentIndex) {
return orderedValues.get(currentIndex);
}
// fetch and return the value
loadTo(currentIndex);
return orderedValues.get(currentIndex);
}
private void loadTo(int index) {
int fetched = 0;
while (orderedValues.size() <= index || fetched < fetchSize) {
// getting the next scoreDoc. If null, then there is no more results
ClusteredDoc scoreDoc = (ClusteredDoc) hq.pop();
if (scoreDoc == null) {
return;
}
// "recharging" the queue
ClusteredTopDocs topDoc = topDocsResponses.get(scoreDoc.getNodeUuid());
ScoreDoc score = topDoc.getNext();
if (score != null) {
hq.add(score);
}
// fetching the value
Object value = fetchValue(scoreDoc, topDoc);
orderedValues.add(value);
fetched++;
}
}
protected Object fetchValue(ClusteredDoc scoreDoc, ClusteredTopDocs topDoc) {
ISPNEagerTopDocs eagerTopDocs = (ISPNEagerTopDocs) topDoc.getTopDocs();
return cache.get(eagerTopDocs.keys[scoreDoc.getIndex()]);
}
@Override
public int previousIndex() {
return currentIndex - 1;
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"Not supported as you are trying to change something in the cache. Please use searchableCache.put()");
}
@Override
public void set(Object arg0) {
throw new UnsupportedOperationException(
"Not supported as you are trying to change something in the cache. Please use searchableCache.put()");
}
@Override
public boolean hasNext() {
if (currentIndex + 1 >= resultSize) {
return false;
}
return true;
}
}