/** * This software is licensed to you under the Apache License, Version 2.0 (the * "Apache License"). * * LinkedIn's contributions are made under the Apache License. If you contribute * to the Software, the contributions will be deemed to have been made under the * Apache License, unless you expressly indicate otherwise. Please do not make any * contributions that would be inconsistent with the Apache License. * * You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, this software * distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache * License for the specific language governing permissions and limitations for the * software governed under the Apache License. * * © 2012 LinkedIn Corp. All Rights Reserved. */ package com.senseidb.search.facet; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.Properties; import org.apache.log4j.Logger; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.ScoreDoc; import proj.zoie.api.DocIDMapper; import proj.zoie.api.ZoieIndexReader; import proj.zoie.api.ZoieSegmentReader; import com.browseengine.bobo.api.BoboIndexReader; import com.browseengine.bobo.api.BrowseSelection; import com.browseengine.bobo.api.FacetSpec; import com.browseengine.bobo.docidset.EmptyDocIdSet; import com.browseengine.bobo.docidset.RandomAccessDocIdSet; import com.browseengine.bobo.facets.FacetCountCollectorSource; import com.browseengine.bobo.facets.FacetHandler; import com.browseengine.bobo.facets.filter.EmptyFilter; import com.browseengine.bobo.facets.filter.RandomAccessFilter; import com.browseengine.bobo.facets.filter.RandomAccessNotFilter; import com.browseengine.bobo.sort.DocComparator; import com.browseengine.bobo.sort.DocComparatorSource; import com.kamikaze.docidset.impl.IntArrayDocIdSet; public class UIDFacetHandler extends FacetHandler<long[]> { private static Logger logger = Logger.getLogger(UIDFacetHandler.class); public UIDFacetHandler(String name) { super(name); } private static class SingleDocRandmAccessDocIdSet extends RandomAccessDocIdSet{ final int docid; SingleDocRandmAccessDocIdSet(int doc){ docid = doc; } @Override public DocIdSetIterator iterator() throws IOException { return new DocIdSetIterator() { protected int _doc = -1; @Override public int advance(int id) throws IOException { _doc = id-1; return nextDoc(); } @Override public int docID() { return _doc; } @Override public int nextDoc() throws IOException { if (_doc<docid){ return _doc = docid; } return _doc = DocIdSetIterator.NO_MORE_DOCS; } }; } @Override public boolean get(int doc) { return doc == docid; } } private RandomAccessFilter buildRandomAccessFilter(final long val) throws IOException { return new RandomAccessFilter() { /** * */ private static final long serialVersionUID = 1L; @Override public RandomAccessDocIdSet getRandomAccessDocIdSet(BoboIndexReader reader) throws IOException { ZoieIndexReader<?> zoieReader = (ZoieIndexReader<?>)(reader.getInnerReader()); DocIDMapper<?> docidMapper = zoieReader.getDocIDMaper(); final int docid = docidMapper.getDocID(val); if (docid == DocIDMapper.NOT_FOUND){ return EmptyDocIdSet.getInstance(); } return new SingleDocRandmAccessDocIdSet(docid); } }; } private RandomAccessFilter buildRandomAccessFilter(final LongSet valSet) throws IOException { return new RandomAccessFilter() { /** * */ private static final long serialVersionUID = 1L; @Override public RandomAccessDocIdSet getRandomAccessDocIdSet(BoboIndexReader reader) throws IOException { ZoieSegmentReader<?> zoieReader = (ZoieSegmentReader<?>)(reader.getInnerReader()); DocIDMapper<?> docidMapper = zoieReader.getDocIDMaper(); final IntArrayList docidList = new IntArrayList(valSet.size()); LongIterator iter = valSet.iterator(); while(iter.hasNext()){ int docid = docidMapper.getDocID(iter.nextLong()); if (docid!=DocIDMapper.NOT_FOUND){ docidList.add(docid); } } if (docidList.size()==0) return EmptyDocIdSet.getInstance(); int[] delDocIds = zoieReader.getDelDocIds(); if (docidList.size()==1) { int docId = docidList.getInt(0); if (delDocIds == null || delDocIds.length ==0 || Arrays.binarySearch(delDocIds, docId) < 0) { return new SingleDocRandmAccessDocIdSet(docidList.getInt(0)); } else { return EmptyDocIdSet.getInstance(); } } Collections.sort(docidList); final IntArrayDocIdSet intArraySet = new IntArrayDocIdSet(docidList.size()); boolean deletesPresent = delDocIds != null && delDocIds.length > 0; for (int docid : docidList){ if (!deletesPresent || Arrays.binarySearch(delDocIds,docid) < 0) { intArraySet.addDoc(docid); } } return new RandomAccessDocIdSet(){ @Override public boolean get(int docid) { return docidList.contains(docid); } @Override public DocIdSetIterator iterator() throws IOException { return intArraySet.iterator(); } }; } }; } @Override public RandomAccessFilter buildRandomAccessFilter(String value, Properties selectionProperty) throws IOException { try{ long val = Long.parseLong(value); return buildRandomAccessFilter(val); } catch(Exception e){ throw new IOException(e.getMessage()); } } @Override public RandomAccessFilter buildRandomAccessAndFilter(String[] vals, Properties prop) throws IOException { LongSet longSet = new LongOpenHashSet(); for (String val : vals){ try{ longSet.add(Long.parseLong(val)); } catch(Exception e){ throw new IOException(e.getMessage()); } } if (longSet.size()!=1){ return EmptyFilter.getInstance(); } else{ return buildRandomAccessFilter(longSet.iterator().nextLong()); } } @Override public RandomAccessFilter buildRandomAccessOrFilter(String[] vals, Properties prop, boolean isNot) throws IOException { LongSet longSet = new LongOpenHashSet(); for (String val : vals){ try{ longSet.add(Long.parseLong(val)); } catch(Exception e){ throw new IOException(e.getMessage()); } } RandomAccessFilter filter; if (longSet.size()==1){ filter = buildRandomAccessFilter(longSet.iterator().nextLong()); } else{ filter = buildRandomAccessFilter(longSet); } if (filter == null) return filter; if (isNot) { filter = new RandomAccessNotFilter(filter); } return filter; } @Override public DocComparatorSource getDocComparatorSource() { return new DocComparatorSource() { @Override public DocComparator getComparator(IndexReader reader, int docbase) throws IOException { final UIDFacetHandler uidFacetHandler = UIDFacetHandler.this; if (reader instanceof BoboIndexReader){ BoboIndexReader boboReader = (BoboIndexReader)reader; final long[] uidArray = uidFacetHandler.getFacetData(boboReader); return new DocComparator() { @Override public Comparable value(ScoreDoc doc) { int docid = doc.doc; return Long.valueOf(uidArray[docid]); } @Override public int compare(ScoreDoc doc1, ScoreDoc doc2) { long uid1 = uidArray[doc1.doc]; long uid2 = uidArray[doc2.doc]; if (uid1==uid2){ return 0; } else{ if (uid1<uid2) return -1; return 1; } } }; } else{ throw new IOException("reader must be instance of: "+BoboIndexReader.class); } } }; } @Override public FacetCountCollectorSource getFacetCountCollectorSource( BrowseSelection sel, FacetSpec fspec) { throw new UnsupportedOperationException("not supported"); } @Override public String[] getFieldValues(BoboIndexReader reader, int id) { long[] uidArray = getFacetData(reader); return new String[]{String.valueOf(uidArray[id])}; } @Override public Object[] getRawFieldValues(BoboIndexReader reader, int id) { long[] uidArray = getFacetData(reader); return new Long[]{uidArray[id]}; } @Override public long[] load(BoboIndexReader reader) throws IOException { IndexReader innerReader = reader.getInnerReader(); if (innerReader instanceof ZoieSegmentReader){ ZoieSegmentReader zoieReader = (ZoieSegmentReader)innerReader; return zoieReader.getUIDArray(); } else{ throw new IOException("inner reader not instance of "+ZoieSegmentReader.class); } } }