package de.danielbasedow.prospecter.core.index; import de.danielbasedow.prospecter.core.Matcher; import de.danielbasedow.prospecter.core.Token; import de.danielbasedow.prospecter.core.document.Field; import gnu.trove.list.TLongList; import gnu.trove.list.array.TLongArrayList; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.ConcurrentSkipListMap; public class RangeIndex<T> { protected final SortedMap<T, TLongList> indexEquals = new ConcurrentSkipListMap<T, TLongList>(); protected final SortedMap<T, TLongList> indexLessThan = new ConcurrentSkipListMap<T, TLongList>(); protected final SortedMap<T, TLongList> indexGreaterThan = new ConcurrentSkipListMap<T, TLongList>(); public void match(Field field, Matcher matcher) { List<Token> tokens = field.getTokens(); for (Token token : tokens) { T tToken = (T) token.getToken(); collectEqualMatches(matcher, tToken); collectLessThanMatches(matcher, tToken); collectGreaterThanMatches(matcher, tToken); } } protected void collectEqualMatches(Matcher matcher, T token) { TLongList postings = indexEquals.get(token); if (postings != null) { matcher.addHits(indexEquals.get(token)); } } protected void collectGreaterThanMatches(Matcher matcher, T token) { /** * The indexGreaterThan contains postings for "field > x" so if x is 10 a posting would be added for 10 * in order to get the relevant postings we have to look at all key that are LESS than the field value * A field containing y has to return all fields less than y so: -n < 0 < x < y < n */ Map<T, TLongList> navigableMap = indexGreaterThan.headMap(token); if (navigableMap.size() > 0) { for (Map.Entry<T, TLongList> entry : navigableMap.entrySet()) { matcher.addHits(entry.getValue()); } } } protected void collectLessThanMatches(Matcher matcher, T token) { /** * The indexLessThan contains postings for "field < x" so if x is 10 a posting would be added for 10 * in order to get the relevant postings we have to look at all key that are GREATER than the field value * A field containing y has to return all fields greater than y so: -n < 0 < x < y < n */ Map<T, TLongList> navigableMap = indexLessThan.tailMap(token); if (navigableMap.size() > 0) { for (Map.Entry<T, TLongList> entry : navigableMap.entrySet()) { matcher.addHits(entry.getValue()); } } } public void addPosting(Token token, Long posting) { T intToken = (T) token.getToken(); switch (token.getCondition()) { case EQUALS: getOrCreate(indexEquals, intToken).add(posting); break; case GREATER_THAN: getOrCreate(indexGreaterThan, intToken).add(posting); break; case GREATER_THAN_EQUALS: getOrCreate(indexGreaterThan, intToken).add(posting); getOrCreate(indexEquals, intToken).add(posting); break; case LESS_THAN: getOrCreate(indexLessThan, intToken).add(posting); break; case LESS_THAN_EQUALS: getOrCreate(indexLessThan, intToken).add(posting); getOrCreate(indexEquals, intToken).add(posting); break; } } public void removePosting(Token token, Long posting) { T intToken = (T) token.getToken(); switch (token.getCondition()) { case EQUALS: getOrCreate(indexEquals, intToken).remove(posting); break; case GREATER_THAN: getOrCreate(indexGreaterThan, intToken).remove(posting); break; case GREATER_THAN_EQUALS: getOrCreate(indexGreaterThan, intToken).remove(posting); getOrCreate(indexEquals, intToken).remove(posting); break; case LESS_THAN: getOrCreate(indexLessThan, intToken).remove(posting); break; case LESS_THAN_EQUALS: getOrCreate(indexLessThan, intToken).remove(posting); getOrCreate(indexEquals, intToken).remove(posting); break; } } private TLongList getOrCreate(Map<T, TLongList> map, T key) { TLongList postings = map.get(key); if (postings == null) { postings = new TLongArrayList(); map.put(key, postings); } return postings; } }