package com.feedly.cassandra.dao; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.List; import com.feedly.cassandra.IKeyspaceFactory; import com.feedly.cassandra.entity.EIndexType; import com.feedly.cassandra.entity.EPropertyType; import com.feedly.cassandra.entity.EntityMetadata; import com.feedly.cassandra.entity.IndexMetadata; import com.feedly.cassandra.entity.PropertyMetadataBase; import com.feedly.cassandra.entity.SimplePropertyMetadata; /* * helper class to do finds (reads by index). */ @SuppressWarnings("unchecked") class FindHelper<K, V> extends LoadHelper<K, V> { private final HashIndexFindHelper<K, V> _hashIndexFinder; private final RangeIndexFindHelper<K, V> _rangeIndexFinder; FindHelper(EntityMetadata<V> meta, IKeyspaceFactory factory, IStaleIndexValueStrategy staleValueStrategy, int statsSize) { super(meta, factory, 0); _hashIndexFinder = new HashIndexFindHelper<K, V>(meta, factory, statsSize); _rangeIndexFinder = new RangeIndexFindHelper<K, V>(meta, factory, staleValueStrategy, statsSize); } @Override public OperationStatistics stats() { throw new UnsupportedOperationException("use hash or range specific stats"); } public OperationStatistics rangeFindStats() { return _rangeIndexFinder.stats(); } public OperationStatistics rangeFindIndexStats() { return _rangeIndexFinder.indexStats(); } public OperationStatistics hashFindStats() { return _hashIndexFinder.stats(); } public OperationStatistics hashFindIndexStats() { return _hashIndexFinder.indexStats(); } private IndexMetadata chooseIndex(boolean rangeOnly, V... templates) { List<SimplePropertyMetadata> props = new ArrayList<SimplePropertyMetadata>(); BitSet dirty = asEntity(templates[0]).getModifiedFields(); if(templates.length > 1) { dirty = (BitSet) dirty.clone(); for(int i = templates.length-1; i > 0; i--) dirty.and(asEntity(templates[i]).getModifiedFields()); } for(int i = dirty.nextSetBit(0); i >= 0; i = dirty.nextSetBit(i + 1)) { PropertyMetadataBase pmb = _entityMeta.getProperties().get(i); if(pmb.getPropertyType() == EPropertyType.SIMPLE) //only simple props can be indexed props.add((SimplePropertyMetadata) pmb); } if(props.isEmpty()) throw new IllegalArgumentException("no properties set"); IndexMetadata matching = null; int matchCnt = 0; for(IndexMetadata im : _entityMeta.getIndexes()) { if(rangeOnly && im.getType() != EIndexType.RANGE) continue; if(props.equals(im.getIndexedProperties())) { matching = im; matchCnt = im.getIndexedProperties().size(); break; } int cnt = 0; for(SimplePropertyMetadata indexedProp : im.getIndexedProperties()) { if(props.contains(indexedProp)) cnt++; else break; } if(cnt > matchCnt) { matchCnt = cnt; matching = im; } else if(cnt > 0 && cnt == matchCnt && im.getIndexedProperties().size() < matching.getIndexedProperties().size()) { //smaller number of columns matching = im; } } if(matching != null) { _logger.debug("selected index {} [{} of {} col(s)]", new Object[] {matching, matchCnt, matching.getIndexedProperties().size()}); return matching; } throw new IllegalStateException("no applicable index for properties " + props); } public V find(V template, FindOptions options) { IndexMetadata index = chooseIndex(false, template); if(index.getType() == EIndexType.HASH) { return _hashIndexFinder.find(template, options, index); } else { return _rangeIndexFinder.find(template, options, index); } } public Collection<V> mfind(V template, FindOptions options) { IndexMetadata index = chooseIndex(false, template); if(index.getType() == EIndexType.HASH) { return _hashIndexFinder.mfind(template, options, index); } else { return _rangeIndexFinder.mfind(template, options, index); } } public Collection<V> mfindBetween(V startTemplate, V endTemplate) { return mfindBetween(startTemplate, endTemplate, null); } public Collection<V> mfindBetween(V startTemplate, V endTemplate, FindBetweenOptions options) { if(options == null) options = new FindBetweenOptions(); IndexMetadata index = chooseIndex(true, startTemplate, endTemplate); return _rangeIndexFinder.mfindBetween(startTemplate, endTemplate, options, index); } }