/*
* Copyright (C) 2014 Indeed Inc.
*
* Licensed 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 com.indeed.flamdex.reader;
import com.google.common.primitives.Ints;
import com.indeed.flamdex.api.DocIdStream;
import com.indeed.flamdex.api.FlamdexOutOfMemoryException;
import com.indeed.flamdex.api.FlamdexReader;
import com.indeed.flamdex.api.GenericIntTermDocIterator;
import com.indeed.flamdex.api.GenericStringTermDocIterator;
import com.indeed.flamdex.api.IntTermDocIterator;
import com.indeed.flamdex.api.IntTermIterator;
import com.indeed.flamdex.api.IntValueLookup;
import com.indeed.flamdex.api.StringTermDocIterator;
import com.indeed.flamdex.api.StringTermIterator;
import com.indeed.flamdex.api.StringValueLookup;
import com.indeed.flamdex.api.TermIterator;
import com.indeed.flamdex.utils.FlamdexUtils;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @author jsgroth
*/
public class MockFlamdexReader implements FlamdexReader {
private final Collection<String> intFields;
private final Collection<String> stringFields;
private final Collection<String> metrics;
private final int numDocs;
private String directory;
private final Map<String, SortedMap<Long, List<Integer>>> intTerms = new HashMap<String, SortedMap<Long, List<Integer>>>();
private final Map<String, SortedMap<String, List<Integer>>> stringTerms = new HashMap<String, SortedMap<String, List<Integer>>>();
public MockFlamdexReader() {
this(Arrays.asList("if1"), Arrays.asList("sf1"), Arrays.asList("if1"), 10);
}
public MockFlamdexReader(Collection<String> intFields,
Collection<String> stringFields,
Collection<String> metrics,
int numDocs) {
this.directory = ".";
this.intFields = intFields;
this.stringFields = stringFields;
this.metrics = metrics;
this.numDocs = numDocs;
for (final String intField : intFields) {
intTerms.put(intField, new TreeMap<Long, List<Integer>>());
}
for (final String stringField : stringFields) {
stringTerms.put(stringField, new TreeMap<String, List<Integer>>());
}
}
public MockFlamdexReader(Collection<String> intFields,
Collection<String> stringFields,
Collection<String> metrics,
int numDocs,
String directory) {
this(intFields, stringFields, metrics, numDocs);
this.directory = directory;
}
private class MockIntTermIterator implements IntTermIterator {
private final Set<Map.Entry<Long, List<Integer>>> set;
private Iterator<Map.Entry<Long, List<Integer>>> iterator;
private long term;
private List<Integer> docs;
private boolean bufferNext;
private MockIntTermIterator(final String field) {
set = intTerms.get(field).entrySet();
iterator = set.iterator();
}
@Override
public void reset(long term) {
iterator = set.iterator();
while (iterator.hasNext()) {
final Map.Entry<Long, List<Integer>> entry = iterator.next();
if (entry.getKey() >= term) {
bufferNext = true;
this.term = entry.getKey();
this.docs = entry.getValue();
break;
}
}
}
@Override
public long term() {
return term;
}
@Override
public boolean next() {
if (bufferNext) {
bufferNext = false;
return true;
}
if (!iterator.hasNext()) return false;
final Map.Entry<Long, List<Integer>> e = iterator.next();
term = e.getKey();
docs = e.getValue();
return true;
}
@Override
public int docFreq() {
return docs.size();
}
@Override
public void close() {
}
}
private class MockStringTermIterator implements StringTermIterator {
private final Set<Map.Entry<String, List<Integer>>> set;
private Iterator<Map.Entry<String, List<Integer>>> iterator;
private String term;
private List<Integer> docs;
private boolean bufferNext;
private MockStringTermIterator(final String field) {
set = stringTerms.get(field).entrySet();
iterator = stringTerms.get(field).entrySet().iterator();
}
@Override
public void reset(String term) {
iterator = set.iterator();
bufferNext = false;
while (iterator.hasNext()) {
final Map.Entry<String, List<Integer>> entry = iterator.next();
if (entry.getKey().compareTo(term) >= 0) {
bufferNext = true;
this.term = entry.getKey();
this.docs = entry.getValue();
break;
}
}
}
@Override
public String term() {
return term;
}
@Override
public boolean next() {
if (bufferNext) {
bufferNext = false;
return true;
}
if (!iterator.hasNext()) return false;
final Map.Entry<String, List<Integer>> e = iterator.next();
term = e.getKey();
docs = e.getValue();
return true;
}
@Override
public int docFreq() {
return docs.size();
}
@Override
public void close() {
}
}
private abstract class AbstractMockEmptyIterator implements TermIterator {
@Override
public boolean next() {
return false;
}
@Override
public int docFreq() {
return 0;
}
@Override
public void close() {
}
}
private class MockEmptyIntTermIterator extends AbstractMockEmptyIterator implements IntTermIterator {
@Override
public void reset(long term) {
}
@Override
public long term() {
return 0;
}
}
private class MockEmptyStringTermIterator extends AbstractMockEmptyIterator implements StringTermIterator {
@Override
public void reset(String term) {
}
@Override
public String term() {
return null;
}
}
private class MockDocIdStream implements DocIdStream {
private List<Integer> docs;
private int index;
@Override
public void reset(TermIterator term) {
if (term instanceof MockIntTermIterator) {
docs = ((MockIntTermIterator)term).docs;
} else if (term instanceof MockStringTermIterator) {
docs = ((MockStringTermIterator)term).docs;
} else {
throw new IllegalArgumentException("invalid term iterator: iterator is of type "+term.getClass().getName());
}
index = 0;
}
@Override
public int fillDocIdBuffer(int[] docIdBuffer) {
final int n = Math.min(docs.size() - index, docIdBuffer.length);
for (int i = 0; i < n; ++i) {
docIdBuffer[i] = docs.get(index++);
}
return n;
}
@Override
public void close() {
}
}
private class MockIntValueLookup implements IntValueLookup {
private final long[] lookup;
private final long min;
private final long max;
private MockIntValueLookup(String metric) {
if (!intTerms.containsKey(metric)) {
throw new IllegalArgumentException("don't have int field "+metric);
}
lookup = new long[numDocs];
long tmin = Long.MAX_VALUE;
long tmax = Long.MIN_VALUE;
for (final Map.Entry<Long, List<Integer>> e : intTerms.get(metric).entrySet()) {
final long term = e.getKey();
tmin = Math.min(tmin, term);
tmax = Math.max(tmax, term);
final List<Integer> docs = e.getValue();
for (final int doc : docs) {
lookup[doc] = term;
}
}
min = tmin;
max = tmax;
}
@Override
public long getMin() {
return min;
}
@Override
public long getMax() {
return max;
}
@Override
public void lookup(int[] docIds, long[] values, int n) {
for (int i = 0; i < n; ++i) {
values[i] = lookup[docIds[i]];
}
}
@Override
public long memoryUsed() {
return 0L;
}
@Override
public void close() {
}
}
private class MockStringValueLookup implements StringValueLookup {
private final String[] lookup;
private MockStringValueLookup(String metric) {
if (!stringTerms.containsKey(metric)) {
throw new IllegalArgumentException("don't have int field "+metric);
}
lookup = new String[numDocs];
for (final Map.Entry<String, List<Integer>> e : stringTerms.get(metric).entrySet()) {
final String term = e.getKey();
final List<Integer> docs = e.getValue();
for (final int doc : docs) {
lookup[doc] = term;
}
}
}
public String getString(final int docId) {
return lookup[docId];
}
@Override
public long memoryUsed() {
return 0L;
}
@Override
public void close() {
}
}
@Override
public Collection<String> getIntFields() {
return intFields;
}
@Override
public Collection<String> getStringFields() {
return stringFields;
}
@Override
public int getNumDocs() {
return numDocs;
}
@Override
public String getDirectory() {
return directory;
}
@Override
public DocIdStream getDocIdStream() {
return new MockDocIdStream();
}
@Override
public IntTermIterator getIntTermIterator(String field) {
return intTerms.containsKey(field) ? new MockIntTermIterator(field) : new MockEmptyIntTermIterator();
}
@Override
public StringTermIterator getStringTermIterator(String field) {
return stringTerms.containsKey(field) ? new MockStringTermIterator(field) : new MockEmptyStringTermIterator();
}
@Override
public IntTermDocIterator getIntTermDocIterator(final String field) {
return new GenericIntTermDocIterator(getIntTermIterator(field), getDocIdStream());
}
@Override
public StringTermDocIterator getStringTermDocIterator(final String field) {
return new GenericStringTermDocIterator(getStringTermIterator(field), getDocIdStream());
}
@Override
public long getIntTotalDocFreq(String field) {
return FlamdexUtils.getIntTotalDocFreq(this, field);
}
@Override
public long getStringTotalDocFreq(String field) {
return FlamdexUtils.getStringTotalDocFreq(this, field);
}
@Override
public Collection<String> getAvailableMetrics() {
return metrics;
}
@Override
public IntValueLookup getMetric(String metric) throws FlamdexOutOfMemoryException {
return new MockIntValueLookup(metric);
}
public StringValueLookup getStringLookup(final String field) throws FlamdexOutOfMemoryException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public long memoryRequired(String metric) {
return 0L;
}
@Override
public void close() throws IOException {
}
public void addIntTerm(final String field, final long term, final List<Integer> docs) {
intTerms.get(field).put(term, docs);
}
public void addIntTerm(final String field, final long term, final int... docs) {
addIntTerm(field, term, Ints.asList(docs));
}
public void addStringTerm(final String field, final String term, final List<Integer> docs) {
stringTerms.get(field).put(term, docs);
}
public void addStringTerm(final String field, final String term, final int... docs) {
addStringTerm(field, term, Ints.asList(docs));
}
}