Package com.browseengine.bobo.test

Source Code of com.browseengine.bobo.test.BoboTestCase$TestDataDigester

/**
* Bobo Browse Engine - High performance faceted/parametric search implementation
* that handles various types of semi-structured data.  Written in Java.
*
* Copyright (C) 2005-2006  John Wang
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*
* To contact the project administrators for the bobo-browse project,
* please go to https://sourceforge.net/projects/bobo-browse/, or
* send mail to owner@browseengine.com.
*/

package com.browseengine.bobo.test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import junit.framework.TestCase;

import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.Version;

import com.browseengine.bobo.api.BoboBrowser;
import com.browseengine.bobo.api.BoboCustomSortField;
import com.browseengine.bobo.api.BoboMultiReader;
import com.browseengine.bobo.api.BoboSegmentReader;
import com.browseengine.bobo.api.Browsable;
import com.browseengine.bobo.api.BrowseException;
import com.browseengine.bobo.api.BrowseFacet;
import com.browseengine.bobo.api.BrowseHit;
import com.browseengine.bobo.api.BrowseHit.BoboTerm;
import com.browseengine.bobo.api.BrowseHit.SerializableField;
import com.browseengine.bobo.api.BrowseRequest;
import com.browseengine.bobo.api.BrowseResult;
import com.browseengine.bobo.api.BrowseSelection;
import com.browseengine.bobo.api.BrowseSelection.ValueOperation;
import com.browseengine.bobo.api.ComparatorFactory;
import com.browseengine.bobo.api.FacetAccessible;
import com.browseengine.bobo.api.FacetSpec;
import com.browseengine.bobo.api.FacetSpec.FacetSortSpec;
import com.browseengine.bobo.api.FieldValueAccessor;
import com.browseengine.bobo.api.MultiBoboBrowser;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.FacetHandler.TermCountSize;
import com.browseengine.bobo.facets.data.FacetDataCache;
import com.browseengine.bobo.facets.data.FacetDataFetcher;
import com.browseengine.bobo.facets.data.PredefinedTermListFactory;
import com.browseengine.bobo.facets.data.TermListFactory;
import com.browseengine.bobo.facets.impl.BucketFacetHandler;
import com.browseengine.bobo.facets.impl.ComboFacetHandler;
import com.browseengine.bobo.facets.impl.CompactMultiValueFacetHandler;
import com.browseengine.bobo.facets.impl.DynamicTimeRangeFacetHandler;
import com.browseengine.bobo.facets.impl.FacetHitcountComparatorFactory;
import com.browseengine.bobo.facets.impl.FacetValueComparatorFactory;
import com.browseengine.bobo.facets.impl.FilteredRangeFacetHandler;
import com.browseengine.bobo.facets.impl.GeoFacetHandler;
import com.browseengine.bobo.facets.impl.GeoSimpleFacetHandler;
import com.browseengine.bobo.facets.impl.HistogramFacetHandler;
import com.browseengine.bobo.facets.impl.MultiValueFacetHandler;
import com.browseengine.bobo.facets.impl.MultiValueWithWeightFacetHandler;
import com.browseengine.bobo.facets.impl.PathFacetHandler;
import com.browseengine.bobo.facets.impl.RangeFacetHandler;
import com.browseengine.bobo.facets.impl.SimpleFacetHandler;
import com.browseengine.bobo.facets.impl.SimpleGroupbyFacetHandler;
import com.browseengine.bobo.facets.impl.VirtualSimpleFacetHandler;
import com.browseengine.bobo.index.BoboIndexer;
import com.browseengine.bobo.index.digest.DataDigester;
import com.browseengine.bobo.query.FacetBasedBoostScorerBuilder;
import com.browseengine.bobo.query.RecencyBoostScorerBuilder;
import com.browseengine.bobo.query.ScoreAdjusterQuery;
import com.browseengine.bobo.query.scoring.FacetTermQuery;
import com.browseengine.bobo.sort.DocComparator;
import com.browseengine.bobo.sort.DocComparatorSource;
import com.browseengine.bobo.util.BigSegmentedArray;
import com.browseengine.bobo.util.IntBoundedPriorityQueue.IntComparator;

public class BoboTestCase extends TestCase {
  static Logger log = Logger.getLogger(BoboTestCase.class);
  private final Directory _indexDir;
  private final List<FacetHandler<?>> _fconf;
  static final private Term tagSizePayloadTerm = new Term("tagSizePayload", "size");

  private static class TestDataDigester extends DataDigester {
    private final Document[] _data;

    TestDataDigester(List<FacetHandler<?>> fConf, Document[] data) {
      super();
      _data = data;
    }

    @Override
    public void digest(DataHandler handler) throws IOException {
      for (int i = 0; i < _data.length; ++i) {
        handler.handleDocument(_data[i]);
      }
    }
  }

  public BoboTestCase(String testName) {
    super(testName);
    String confdir = System.getProperty("conf.dir");
    if (confdir == null) confdir = "./resource";
    System.setProperty("log.home", ".");
    org.apache.log4j.PropertyConfigurator.configure(confdir + "/log4j.properties");
    _fconf = buildFieldConf();
    _indexDir = createIndex();
  }

  private BoboMultiReader newIndexReader() throws IOException {
    DirectoryReader srcReader = DirectoryReader.open(_indexDir);
    try {
      BoboMultiReader reader = BoboMultiReader.getInstance(srcReader, _fconf);
      return reader;
    } catch (IOException ioe) {
      if (srcReader != null) {
        srcReader.close();
      }
      throw ioe;
    }
  }

  private BoboBrowser newBrowser() throws IOException {
    return new BoboBrowser(newIndexReader());
  }

  public static Field buildMetaField(String name, String val) {
    Field f = new StringField(name, val, Field.Store.NO);
    return f;
  }

  static final class MetaTokenStream extends TokenStream {
    private boolean returnToken = false;

    private final PayloadAttribute payloadAttr;
    private final CharTermAttribute termAttr;

    MetaTokenStream(Term term, int size) {
      byte[] buffer = new byte[4];
      buffer[0] = (byte) (size);
      buffer[1] = (byte) (size >> 8);
      buffer[2] = (byte) (size >> 16);
      buffer[3] = (byte) (size >> 24);
      payloadAttr = addAttribute(PayloadAttribute.class);
      payloadAttr.setPayload(new BytesRef(buffer));
      termAttr = addAttribute(CharTermAttribute.class);
      termAttr.append(term.text());
      returnToken = true;
    }

    @Override
    public boolean incrementToken() throws IOException {
      if (returnToken) {
        returnToken = false;
        return true;
      } else {
        return false;
      }
    }
  }

  public static Field buildMetaSizePayloadField(final Term term, final int size) {
    Field f = new TextField(term.field(), new MetaTokenStream(term, size));
    return f;
  }

  public static Document[] buildData() {
    ArrayList<Document> dataList = new ArrayList<Document>();

    Document d1 = new Document();
    d1.add(buildMetaField("id", "1"));
    d1.add(buildMetaField("shape", "square"));
    d1.add(buildMetaField("color", "red"));
    d1.add(buildMetaField("size", "4"));
    d1.add(buildMetaField("location", "toy/lego/block/"));
    d1.add(buildMetaField("tag", "rabbit"));
    d1.add(buildMetaField("tag", "pet"));
    d1.add(buildMetaField("tag", "animal"));
    d1.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d1.add(buildMetaField("number", "0010"));
    d1.add(buildMetaField("date", "2000/01/01"));
    d1.add(buildMetaField("name", "ken"));
    d1.add(buildMetaField("char", "k"));
    d1.add(buildMetaField("multinum", "001"));
    d1.add(buildMetaField("multinum", "003"));
    d1.add(buildMetaField("multiwithweight", "cool\u0000200"));
    d1.add(buildMetaField("multiwithweight", "good\u0000100"));
    d1.add(buildMetaField("compactnum", "001"));
    d1.add(buildMetaField("compactnum", "003"));
    d1.add(buildMetaField("numendorsers", "000003"));
    d1.add(buildMetaField("path", "a-b"));
    d1.add(buildMetaField("multipath", "a-b"));
    d1.add(buildMetaField("custom", "000003"));
    d1.add(buildMetaField("latitude", "60"));
    d1.add(buildMetaField("longitude", "120"));
    d1.add(buildMetaField("salary", "04500"));

    Field sf = new StringField("testStored", "stored", Store.YES);
    d1.add(sf);

    FieldType ft = new FieldType();
    ft.setStored(false);
    ft.setIndexed(true);
    ft.setTokenized(true);
    ft.setStoreTermVectors(true);
    ft.setStoreTermVectorPositions(false);
    ft.setStoreTermVectorOffsets(false);
    Field tvf = new Field("tv", "bobo bobo lucene lucene lucene test", ft);
    d1.add(tvf);

    FieldType ftPositions = new FieldType();
    ftPositions.setStored(false);
    ftPositions.setIndexed(true);
    ftPositions.setTokenized(true);
    ftPositions.setStoreTermVectors(true);
    ftPositions.setStoreTermVectorPositions(true);
    ftPositions.setStoreTermVectorOffsets(false);
    tvf = new Field("tvPositions", "bobo bobo lucene lucene lucene test", ftPositions);
    d1.add(tvf);

    FieldType ftOffsets = new FieldType();
    ftOffsets.setStored(false);
    ftOffsets.setIndexed(true);
    ftOffsets.setTokenized(true);
    ftOffsets.setStoreTermVectors(true);
    ftOffsets.setStoreTermVectorPositions(false);
    ftOffsets.setStoreTermVectorOffsets(true);
    tvf = new Field("tvOffsets", "bobo bobo lucene lucene lucene test", ftOffsets);
    d1.add(tvf);

    FieldType ftPositionsAndOffsets = new FieldType();
    ftPositionsAndOffsets.setStored(false);
    ftPositionsAndOffsets.setIndexed(true);
    ftPositionsAndOffsets.setTokenized(true);
    ftPositionsAndOffsets.setStoreTermVectors(true);
    ftPositionsAndOffsets.setStoreTermVectorPositions(true);
    ftPositionsAndOffsets.setStoreTermVectorOffsets(true);
    tvf = new Field("tvPositionsAndOffsets", "bobo bobo lucene lucene lucene test",
        ftPositionsAndOffsets);
    d1.add(tvf);

    Document d2 = new Document();
    d2.add(buildMetaField("id", "2"));
    d2.add(buildMetaField("shape", "rectangle"));
    d2.add(buildMetaField("color", "red"));
    d2.add(buildMetaField("size", "2"));
    d2.add(buildMetaField("location", "toy/lego/block/"));
    d2.add(buildMetaField("tag", "dog"));
    d2.add(buildMetaField("tag", "pet"));
    d2.add(buildMetaField("tag", "poodle"));
    d2.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d2.add(buildMetaField("number", "0011"));
    d2.add(buildMetaField("date", "2003/02/14"));
    d2.add(buildMetaField("name", "igor"));
    d2.add(buildMetaField("char", "i"));
    d2.add(buildMetaField("multinum", "002"));
    d2.add(buildMetaField("multinum", "004"));
    d2.add(buildMetaField("multiwithweight", "cool\u0000300"));
    d2.add(buildMetaField("multiwithweight", "good\u0000200"));
    d2.add(buildMetaField("compactnum", "002"));
    d2.add(buildMetaField("compactnum", "004"));
    d2.add(buildMetaField("numendorsers", "000010"));
    d2.add(buildMetaField("path", "a-c-d"));
    d2.add(buildMetaField("multipath", "a-c-d"));
    d2.add(buildMetaField("multipath", "a-b"));
    d2.add(buildMetaField("custom", "000010"));
    d2.add(buildMetaField("latitude", "50"));
    d2.add(buildMetaField("longitude", "110"));
    d2.add(buildMetaField("salary", "08500"));

    Document d3 = new Document();
    d3.add(buildMetaField("id", "3"));
    d3.add(buildMetaField("shape", "circle"));
    d3.add(buildMetaField("color", "green"));
    d3.add(buildMetaField("size", "3"));
    d3.add(buildMetaField("location", "toy/lego/"));
    d3.add(buildMetaField("tag", "rabbit"));
    d3.add(buildMetaField("tag", "cartoon"));
    d3.add(buildMetaField("tag", "funny"));
    d3.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d3.add(buildMetaField("number", "0230"));
    d3.add(buildMetaField("date", "2001/12/25"));
    d3.add(buildMetaField("name", "john"));
    d3.add(buildMetaField("char", "j"));
    d3.add(buildMetaField("multinum", "007"));
    d3.add(buildMetaField("multinum", "012"));
    d3.add(buildMetaField("multiwithweight", "cool\u0000200"));
    d3.add(buildMetaField("compactnum", "007"));
    d3.add(buildMetaField("compactnum", "012"));
    d3.add(buildMetaField("numendorsers", "000015"));
    d3.add(buildMetaField("path", "a-e"));
    d3.add(buildMetaField("multipath", "a-e"));
    d3.add(buildMetaField("multipath", "a-b"));
    d3.add(buildMetaField("custom", "000015"));
    d3.add(buildMetaField("latitude", "35"));
    d3.add(buildMetaField("longitude", "70"));
    d3.add(buildMetaField("salary", "06500"));

    Document d4 = new Document();
    d4.add(buildMetaField("id", "4"));
    d4.add(buildMetaField("shape", "circle"));
    d4.add(buildMetaField("color", "blue"));
    d4.add(buildMetaField("size", "1"));
    d4.add(buildMetaField("location", "toy/"));
    d4.add(buildMetaField("tag", "store"));
    d4.add(buildMetaField("tag", "pet"));
    d4.add(buildMetaField("tag", "animal"));
    d4.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d4.add(buildMetaField("number", "0913"));
    d4.add(buildMetaField("date", "2004/11/24"));
    d4.add(buildMetaField("name", "cathy"));
    d4.add(buildMetaField("char", "c"));
    d4.add(buildMetaField("multinum", "007"));
    d4.add(buildMetaField("multinum", "007"));
    d4.add(buildMetaField("compactnum", "007"));
    d4.add(buildMetaField("numendorsers", "000019"));
    d4.add(buildMetaField("path", "a-c"));
    d4.add(buildMetaField("multipath", "a-c"));
    d4.add(buildMetaField("multipath", "a-b"));
    d4.add(buildMetaField("custom", "000019"));
    d4.add(buildMetaField("latitude", "30"));
    d4.add(buildMetaField("longitude", "75"));
    d4.add(buildMetaField("salary", "11200"));

    Document d5 = new Document();
    d5.add(buildMetaField("id", "5"));
    d5.add(buildMetaField("shape", "square"));
    d5.add(buildMetaField("color", "blue"));
    d5.add(buildMetaField("size", "5"));
    d5.add(buildMetaField("location", "toy/lego/"));
    d5.add(buildMetaField("tag", "cartoon"));
    d5.add(buildMetaField("tag", "funny"));
    d5.add(buildMetaField("tag", "disney"));
    d5.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d5.add(buildMetaField("number", "1013"));
    d5.add(buildMetaField("date", "2002/03/08"));
    d5.add(buildMetaField("name", "mike"));
    d5.add(buildMetaField("char", "m"));
    d5.add(buildMetaField("multinum", "001"));
    d5.add(buildMetaField("multinum", "001"));
    d5.add(buildMetaField("compactnum", "001"));
    d5.add(buildMetaField("compactnum", "001"));
    d5.add(buildMetaField("numendorsers", "000002"));
    d5.add(buildMetaField("path", "a-e-f"));
    d5.add(buildMetaField("multipath", "a-e-f"));
    d5.add(buildMetaField("multipath", "a-b"));
    d5.add(buildMetaField("custom", "000002"));
    d5.add(buildMetaField("latitude", "60"));
    d5.add(buildMetaField("longitude", "120"));
    d5.add(buildMetaField("salary", "10500"));

    Document d6 = new Document();
    d6.add(buildMetaField("id", "6"));
    d6.add(buildMetaField("shape", "rectangle"));
    d6.add(buildMetaField("color", "green"));
    d6.add(buildMetaField("size", "6"));
    d6.add(buildMetaField("location", "toy/lego/block/"));
    d6.add(buildMetaField("tag", "funny"));
    d6.add(buildMetaField("tag", "humor"));
    d6.add(buildMetaField("tag", "joke"));
    d6.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d6.add(buildMetaField("number", "2130"));
    d6.add(buildMetaField("date", "2007/08/01"));
    d6.add(buildMetaField("name", "doug"));
    d6.add(buildMetaField("char", "d"));
    d6.add(buildMetaField("multinum", "001"));
    d6.add(buildMetaField("multinum", "002"));
    d6.add(buildMetaField("multinum", "003"));
    d6.add(buildMetaField("compactnum", "001"));
    d6.add(buildMetaField("compactnum", "002"));
    d6.add(buildMetaField("compactnum", "003"));
    d6.add(buildMetaField("numendorsers", "000009"));
    d6.add(buildMetaField("path", "a-c-d"));
    d6.add(buildMetaField("multipath", "a-c-d"));
    d6.add(buildMetaField("multipath", "a-b"));
    d6.add(buildMetaField("custom", "000009"));
    d6.add(buildMetaField("latitude", "80"));
    d6.add(buildMetaField("longitude", "-90"));
    d6.add(buildMetaField("salary", "08900"));

    Document d7 = new Document();
    d7.add(buildMetaField("id", "7"));
    d7.add(buildMetaField("shape", "square"));
    d7.add(buildMetaField("color", "red"));
    d7.add(buildMetaField("size", "7"));
    d7.add(buildMetaField("location", "toy/lego/"));
    d7.add(buildMetaField("tag", "humane"));
    d7.add(buildMetaField("tag", "dog"));
    d7.add(buildMetaField("tag", "rabbit"));
    d7.add(buildMetaSizePayloadField(tagSizePayloadTerm, 3));
    d7.add(buildMetaField("number", "0005"));
    d7.add(buildMetaField("date", "2006/06/01"));
    d7.add(buildMetaField("name", "abe"));
    d7.add(buildMetaField("char", "a"));
    d7.add(buildMetaField("multinum", "008"));
    d7.add(buildMetaField("multinum", "003"));
    d7.add(buildMetaField("compactnum", "008"));
    d7.add(buildMetaField("compactnum", "003"));
    d7.add(buildMetaField("numendorsers", "000013"));
    d7.add(buildMetaField("path", "a-c"));
    d7.add(buildMetaField("multipath", "a-c"));
    d7.add(buildMetaField("multipath", "a-b"));
    d7.add(buildMetaField("custom", "000013"));
    d7.add(buildMetaField("latitude", "70"));
    d7.add(buildMetaField("longitude", "-60"));
    d7.add(buildMetaField("salary", "28500"));

    dataList.add(d1);
    dataList.add(d2);
    dataList.add(d3);
    dataList.add(d4);
    dataList.add(d5);
    dataList.add(d6);
    dataList.add(d7);
    return dataList.toArray(new Document[dataList.size()]);
  }

  private Directory createIndex() {
    RAMDirectory idxDir = new RAMDirectory();

    try {
      Document[] data = buildData();

      TestDataDigester testDigester = new TestDataDigester(_fconf, data);
      BoboIndexer indexer = new BoboIndexer(testDigester, idxDir);
      indexer.index();
      DirectoryReader r = DirectoryReader.open(idxDir);
      r.close();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    return idxDir;

  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  public static List<FacetHandler<?>> buildFieldConf() {
    List<FacetHandler<?>> facetHandlers = new ArrayList<FacetHandler<?>>();
    facetHandlers.add(new SimpleFacetHandler("id"));
    SimpleFacetHandler colorHandler = new SimpleFacetHandler("color");
    colorHandler.setTermCountSize(TermCountSize.small);
    facetHandlers.add(colorHandler);

    SimpleFacetHandler shapeHandler = new SimpleFacetHandler("shape");
    shapeHandler.setTermCountSize(TermCountSize.medium);
    facetHandlers.add(new SimpleFacetHandler("shape"));
    facetHandlers.add(new RangeFacetHandler("size", Arrays.asList(new String[] { "[* TO 4]",
        "[5 TO 8]", "[9 TO *]" })));
    String[] ranges = new String[] { "[000000 TO 000005]", "[000006 TO 000010]",
        "[000011 TO 000020]" };
    facetHandlers.add(new RangeFacetHandler("numendorsers", new PredefinedTermListFactory(
        Integer.class, "000000"), Arrays.asList(ranges)));

    PredefinedTermListFactory numTermFactory = new PredefinedTermListFactory(Integer.class, "0000");

    facetHandlers.add(new PathFacetHandler("location"));

    PathFacetHandler pathHandler = new PathFacetHandler("path");
    pathHandler.setSeparator("-");
    facetHandlers.add(pathHandler);

    PathFacetHandler multipathHandler = new PathFacetHandler("multipath", true);
    multipathHandler.setSeparator("-");
    facetHandlers.add(multipathHandler);

    facetHandlers.add(new SimpleFacetHandler("number", numTermFactory));
    facetHandlers.add(new VirtualSimpleFacetHandler("virtual", numTermFactory,
        new FacetDataFetcher() {
          @Override
          public Object fetch(BoboSegmentReader reader, int doc) {
            FacetDataCache sourceCache = (FacetDataCache) reader.getFacetData("number");
            if (sourceCache == null) return null;
            return sourceCache.valArray.getRawValue(sourceCache.orderArray.get(doc));
          }

          @Override
          public void cleanup(BoboSegmentReader reader) {
            // do nothing here.
          }
        }, new HashSet<String>(Arrays.asList(new String[] { "number" }))));
    facetHandlers.add(new SimpleFacetHandler("testStored"));

    facetHandlers.add(new SimpleFacetHandler("name"));
    facetHandlers.add(new RangeFacetHandler("date", new PredefinedTermListFactory(Date.class,
        "yyyy/MM/dd"), Arrays.asList(new String[] { "[2000/01/01 TO 2003/05/05]",
        "[2003/05/06 TO 2005/04/04]" })));
    facetHandlers.add(new SimpleFacetHandler("char", (TermListFactory) null));
    facetHandlers.add(new MultiValueFacetHandler("tag", (String) null, (TermListFactory) null,
        tagSizePayloadTerm));
    facetHandlers.add(new MultiValueFacetHandler("multinum", new PredefinedTermListFactory(
        Integer.class, "000")));
    facetHandlers.add(new MultiValueFacetHandler("diffname", "multinum",
        new PredefinedTermListFactory(Integer.class, "000")));
    facetHandlers.add(new MultiValueWithWeightFacetHandler("multiwithweight"));
    facetHandlers.add(new CompactMultiValueFacetHandler("compactnum",
        new PredefinedTermListFactory(Integer.class, "000")));
    facetHandlers.add(new SimpleFacetHandler("storenum", new PredefinedTermListFactory(Long.class,
        null)));
    /*
     * New FacetHandler for geographic locations. Depends on two RangeFacetHandlers on latitude and
     * longitude
     */
    facetHandlers.add(new RangeFacetHandler("latitude", Arrays.asList(new String[] { "[* TO 30]",
        "[35 TO 60]", "[70 TO 120]" })));
    facetHandlers.add(new RangeFacetHandler("longitude", Arrays.asList(new String[] { "[* TO 30]",
        "[35 TO 60]", "[70 TO 120]" })));
    facetHandlers.add(new GeoSimpleFacetHandler("distance", "latitude", "longitude"));
    facetHandlers.add(new GeoFacetHandler("correctDistance", "latitude", "longitude"));
    /* Underlying time facet for DynamicTimeRangeFacetHandler */
    facetHandlers.add(new RangeFacetHandler("timeinmillis", new PredefinedTermListFactory(
        Long.class, DynamicTimeRangeFacetHandler.NUMBER_FORMAT), null));

    String[] predefinedSalaryRanges = new String[4];
    predefinedSalaryRanges[0] = new String("[04000 TO 05999]");
    predefinedSalaryRanges[1] = new String("[06000 TO 07999]");
    predefinedSalaryRanges[2] = new String("[08000 TO 09999]");
    predefinedSalaryRanges[3] = new String("[10000 TO *]");
    RangeFacetHandler dependedRangeFacet = new RangeFacetHandler("salary",
        Arrays.asList(predefinedSalaryRanges));
    facetHandlers.add(dependedRangeFacet);

    String[][] predefinedBuckets = new String[4][];
    predefinedBuckets[0] = new String[] { "ken", "igor", "abe" };
    predefinedBuckets[1] = new String[] { "ken", "john", "mike" };
    predefinedBuckets[2] = new String[] { "john", "cathy" };
    predefinedBuckets[3] = new String[] { "doug" };

    Map<String, String[]> predefinedGroups = new HashMap<String, String[]>();
    predefinedGroups.put("g1", predefinedBuckets[0]);
    predefinedGroups.put("g2", predefinedBuckets[1]);
    predefinedGroups.put("g3", predefinedBuckets[2]);
    predefinedGroups.put("g4", predefinedBuckets[3]);

    facetHandlers.add(new BucketFacetHandler("groups", predefinedGroups, "name"));

    String[][] predefinedBuckets2 = new String[3][];
    predefinedBuckets2[0] = new String[] { "2", "3" };
    predefinedBuckets2[1] = new String[] { "1", "4" };
    predefinedBuckets2[2] = new String[] { "7", "8" };

    Map<String, String[]> predefinedNumberSets = new HashMap<String, String[]>();
    predefinedNumberSets.put("s1", predefinedBuckets2[0]);
    predefinedNumberSets.put("s2", predefinedBuckets2[1]);
    predefinedNumberSets.put("s3", predefinedBuckets2[2]);

    facetHandlers.add(new BucketFacetHandler("sets", predefinedNumberSets, "multinum"));

    // histogram

    HistogramFacetHandler<Integer> histoHandler = new HistogramFacetHandler<Integer>("numberhisto",
        "number", new Integer(0), new Integer(5000), new Integer(100));

    facetHandlers.add(histoHandler);

    LinkedHashSet<String> dependsNames = new LinkedHashSet<String>();
    dependsNames.add("color");
    dependsNames.add("shape");
    dependsNames.add("number");
    facetHandlers.add(new SimpleGroupbyFacetHandler("groupby", dependsNames));

    ComboFacetHandler colorShape = new ComboFacetHandler("colorShape", new HashSet(
        Arrays.asList(new String[] { "color", "shape" })));
    ComboFacetHandler colorShapeMultinum = new ComboFacetHandler("colorShapeMultinum", new HashSet(
        Arrays.asList(new String[] { "color", "shape", "multinum" })));

    facetHandlers.add(colorShape);
    facetHandlers.add(colorShapeMultinum);

    return facetHandlers;
  }

  private static boolean check(BrowseResult res, int numHits,
      HashMap<String, List<BrowseFacet>> choiceMap, String[] ids) {
    boolean match = false;
    if (numHits == res.getNumHits()) {
      if (choiceMap != null) {
        Set<Entry<String, FacetAccessible>> entries = res.getFacetMap().entrySet();
        if (entries.size() == choiceMap.size()) {
          for (Entry<String, FacetAccessible> entry : entries) {
            String name = entry.getKey();
            FacetAccessible c1 = entry.getValue();
            List<BrowseFacet> l1 = c1.getFacets();
            List<BrowseFacet> l2 = choiceMap.get(name);

            if (l1.size() == l2.size()) {
              Iterator<BrowseFacet> iter1 = l1.iterator();
              Iterator<BrowseFacet> iter2 = l2.iterator();
              while (iter1.hasNext()) {
                BrowseFacet bf1 = iter1.next();
                BrowseFacet bf2 = iter2.next();
                // if (!iter1.next().equals(iter2.next()))
                if (!bf1.equals(bf2)) {
                  return false;
                }
              }
              match = true;
            } else {
              return false;
            }
          }
        } else {
          return false;
        }
      }
      if (ids != null) {
        BrowseHit[] hits = res.getHits();
        try {
          if (hits.length != ids.length) return false;
          for (int i = 0; i < hits.length; ++i) {
            String id = hits[i].getField("id");
            if (!ids[i].equals(id)) return false;
          }
        } catch (Exception e) {
          return false;
        }
      }
      match = true;
    }
    return match;
  }

  /**
   * check results
   * @param result
   * @param req
   * @param numHits
   * @param choiceMap
   * @param ids
   */
  private void doTest(BrowseResult result, BrowseRequest req, int numHits,
      HashMap<String, List<BrowseFacet>> choiceMap, String[] ids) {
    if (!check(result, numHits, choiceMap, ids)) {
      StringBuilder buffer = new StringBuilder();
      buffer.append("Test: ").append(getName()).append("\n");
      buffer.append("Result check failed: \n");
      buffer.append("expected: \n");
      buffer.append(numHits).append(" hits\n");
      buffer.append(Arrays.toString(ids)).append('\n');
      buffer.append("choiceMap: \n");
      buffer.append(choiceMap).append('\n');
      buffer.append("gotten: \n");
      buffer.append(result.getNumHits()).append(" hits\n");
      BrowseHit[] hits = result.getHits();
      String[] resultIds = new String[hits.length];
      for (int i = 0; i < hits.length; ++i) {
        resultIds[i] = hits[i].getField("id");
      }
      buffer.append(Arrays.toString(resultIds)).append('\n');

      Map<String, FacetAccessible> map = result.getFacetMap();

      Set<Entry<String, FacetAccessible>> entries = map.entrySet();

      buffer.append("choiceMap: \n");
      buffer.append("{");
      for (Entry<String, FacetAccessible> entry : entries) {
        String name = entry.getKey();
        FacetAccessible facetAccessor = entry.getValue();
        buffer.append("name=").append(name).append(",");
        buffer.append("facets=").append(facetAccessor.getFacets()).append(";");
      }
      buffer.append("}").append('\n');

      for (int i = 0; i < hits.length; ++i) {
        if (i != 0) {
          buffer.append('\n');
        }
        buffer.append(hits[i]);
      }
      fail(buffer.toString());
    }
  }

  public static String toString(Map<String, FacetAccessible> map) {
    StringBuilder buffer = new StringBuilder();
    Set<Entry<String, FacetAccessible>> entries = map.entrySet();

    buffer.append("{");
    for (Entry<String, FacetAccessible> entry : entries) {
      String name = entry.getKey();
      FacetAccessible facetAccessor = entry.getValue();
      buffer.append("name=").append(name).append(",");
      buffer.append("facets=").append(facetAccessor.getFacets()).append(";");
    }
    buffer.append("}").append('\n');
    return buffer.toString();
  }

  public void testStoredFacetField() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection colorSel = new BrowseSelection("testStored");
    colorSel.addValue("stored");
    br.addSelection(colorSel);
    br.setFetchStoredFields(true);

    BrowseResult result = null;
    BoboBrowser boboBrowser = null;
    try {
      boboBrowser = newBrowser();

      result = boboBrowser.browse(br);
      assertEquals(1, result.getNumHits());
      BrowseHit hit = result.getHits()[0];
      List<SerializableField> storedFields = hit.getStoredFields();
      assertNotNull(storedFields);

      List<String> fieldValues = new ArrayList<String>();
      for (SerializableField field : storedFields) {
        if (field.name().equals("testStored") && field.stringValue() != null) {
          fieldValues.add(field.stringValue());
        }
      }
      String[] values = fieldValues.toArray(new String[fieldValues.size()]);

      assertNotNull(values);
      assertEquals(1, values.length);
      assertTrue("stored".equals(values[0]));

    } catch (BrowseException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (IOException ioe) {
      fail(ioe.getMessage());
    } finally {
      if (boboBrowser != null) {
        try {
          if (result != null) result.close();
          boboBrowser.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }

  }

  public void testStoredField() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection colorSel = new BrowseSelection("color");
    colorSel.addValue("red");
    br.addSelection(colorSel);

    BrowseSelection shapeSel = new BrowseSelection("shape");
    shapeSel.addValue("square");
    br.addSelection(shapeSel);

    BrowseSelection sizeSel = new BrowseSelection("size");
    sizeSel.addValue("[4 TO 4]");
    br.addSelection(sizeSel);

    BrowseResult result = null;
    BoboBrowser boboBrowser = null;
    try {
      boboBrowser = newBrowser();

      result = boboBrowser.browse(br);
      assertEquals(1, result.getNumHits());
      BrowseHit hit = result.getHits()[0];
      assertNull(hit.getStoredFields());

      br.setFetchStoredFields(true);
      result = boboBrowser.browse(br);
      assertEquals(1, result.getNumHits());
      hit = result.getHits()[0];
      String stored = hit.getFieldStringValue("testStored");
      assertTrue("stored".equals(stored));
    } catch (BrowseException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (IOException ioe) {
      fail(ioe.getMessage());
    } finally {
      if (boboBrowser != null) {
        try {
          if (result != null) result.close();
          boboBrowser.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }

  }

  public void testRetrieveTermVector() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection colorSel = new BrowseSelection("color");
    colorSel.addValue("red");
    br.addSelection(colorSel);

    BrowseSelection shapeSel = new BrowseSelection("shape");
    shapeSel.addValue("square");
    br.addSelection(shapeSel);

    BrowseSelection sizeSel = new BrowseSelection("size");
    sizeSel.addValue("[4 TO 4]");
    br.addSelection(sizeSel);

    br.setTermVectorsToFetch(new HashSet<String>(Arrays.asList(new String[] { "tv", "tvPositions",
        "tvOffsets", "tvPositionsAndOffsets" })));

    BrowseResult result = null;
    BoboBrowser boboBrowser = null;
    try {
      boboBrowser = newBrowser();

      result = boboBrowser.browse(br);
      assertEquals(1, result.getNumHits());
      BrowseHit hit = result.getHits()[0];
      assertNull(hit.getStoredFields());

      br.setFetchStoredFields(true);
      result = boboBrowser.browse(br);
      assertEquals(1, result.getNumHits());
      hit = result.getHits()[0];
      Map<String, List<BoboTerm>> tvMap = hit.getTermVectorMap();
      assertNotNull(tvMap);

      assertEquals(4, tvMap.size());

      List<BoboTerm> tv = tvMap.get("tv");
      assertNotNull(tv);

      assertEquals("bobo", tv.get(0).term);
      assertEquals(2, tv.get(0).freq.intValue());
      assertEquals(null, tv.get(0).positions);
      assertEquals(null, tv.get(0).startOffsets);
      assertEquals(null, tv.get(0).endOffsets);

      assertEquals("lucene", tv.get(1).term);
      assertEquals(3, tv.get(1).freq.intValue());
      assertEquals(null, tv.get(1).positions);
      assertEquals(null, tv.get(1).startOffsets);
      assertEquals(null, tv.get(1).endOffsets);

      assertEquals("test", tv.get(2).term);
      assertEquals(1, tv.get(2).freq.intValue());
      assertEquals(null, tv.get(2).positions);
      assertEquals(null, tv.get(2).startOffsets);
      assertEquals(null, tv.get(2).endOffsets);

      tv = tvMap.get("tvPositions");
      assertNotNull(tv);

      assertEquals("bobo", tv.get(0).term);
      assertEquals(2, tv.get(0).freq.intValue());
      List<Integer> positions = new ArrayList<Integer>(Arrays.asList(0, 1));
      assertEquals(positions, tv.get(0).positions);
      List<Integer> startOffsets = new ArrayList<Integer>(Arrays.asList(-1, -1));
      assertEquals(startOffsets, tv.get(0).startOffsets);
      List<Integer> endOffsets = new ArrayList<Integer>(Arrays.asList(-1, -1));
      assertEquals(endOffsets, tv.get(0).endOffsets);

      assertEquals("lucene", tv.get(1).term);
      assertEquals(3, tv.get(1).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(2, 3, 4));
      assertEquals(positions, tv.get(1).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(-1, -1, -1));
      assertEquals(startOffsets, tv.get(1).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(-1, -1, -1));
      assertEquals(endOffsets, tv.get(1).endOffsets);

      assertEquals("test", tv.get(2).term);
      assertEquals(1, tv.get(2).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(5));
      assertEquals(positions, tv.get(2).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(-1));
      assertEquals(startOffsets, tv.get(2).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(-1));
      assertEquals(endOffsets, tv.get(2).endOffsets);

      tv = tvMap.get("tvOffsets");
      assertNotNull(tv);

      assertEquals("bobo", tv.get(0).term);
      assertEquals(2, tv.get(0).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(-1, -1));
      assertEquals(positions, tv.get(0).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(0, 5));
      assertEquals(startOffsets, tv.get(0).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(4, 9));
      assertEquals(endOffsets, tv.get(0).endOffsets);

      assertEquals("lucene", tv.get(1).term);
      assertEquals(3, tv.get(1).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(-1, -1, -1));
      assertEquals(positions, tv.get(1).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(10, 17, 24));
      assertEquals(startOffsets, tv.get(1).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(16, 23, 30));
      assertEquals(endOffsets, tv.get(1).endOffsets);

      assertEquals("test", tv.get(2).term);
      assertEquals(1, tv.get(2).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(-1));
      assertEquals(positions, tv.get(2).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(31));
      assertEquals(startOffsets, tv.get(2).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(35));
      assertEquals(endOffsets, tv.get(2).endOffsets);

      tv = tvMap.get("tvPositionsAndOffsets");
      assertNotNull(tv);
      assertEquals("bobo", tv.get(0).term);
      assertEquals(2, tv.get(0).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(0, 1));
      assertEquals(positions, tv.get(0).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(0, 5));
      assertEquals(startOffsets, tv.get(0).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(4, 9));
      assertEquals(endOffsets, tv.get(0).endOffsets);

      assertEquals("lucene", tv.get(1).term);
      assertEquals(3, tv.get(1).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(2, 3, 4));
      assertEquals(positions, tv.get(1).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(10, 17, 24));
      assertEquals(startOffsets, tv.get(1).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(16, 23, 30));
      assertEquals(endOffsets, tv.get(1).endOffsets);

      assertEquals("test", tv.get(2).term);
      assertEquals(1, tv.get(2).freq.intValue());
      positions = new ArrayList<Integer>(Arrays.asList(5));
      assertEquals(positions, tv.get(2).positions);
      startOffsets = new ArrayList<Integer>(Arrays.asList(31));
      assertEquals(startOffsets, tv.get(2).startOffsets);
      endOffsets = new ArrayList<Integer>(Arrays.asList(35));
      assertEquals(endOffsets, tv.get(2).endOffsets);
    } catch (BrowseException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (IOException ioe) {
      fail(ioe.getMessage());
    } finally {
      if (boboBrowser != null) {
        try {
          if (result != null) result.close();
          boboBrowser.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }

  }

  public void testRawDataRetrieval() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    br.setSort(new SortField[] { new SortField("date", SortField.Type.CUSTOM, false) });
    BrowseResult result = null;
    BoboBrowser boboBrowser = null;
    SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy/MM/dd");
    try {
      boboBrowser = newBrowser();

      result = boboBrowser.browse(br);
      assertEquals(7, result.getNumHits());

      BrowseHit hit = result.getHits()[0];
      assertEquals(0, hit.getDocid());
      Object lowDate = hit.getRawField("date");
      Date date = dateFormatter.parse("2000/01/01");
      assertTrue(lowDate.equals(date.getTime()));

      hit = result.getHits()[6];
      assertEquals(5, hit.getDocid());
      Object highDate = hit.getRawField("date");
      date = dateFormatter.parse("2007/08/01");
      assertTrue(highDate.equals(date.getTime()));
    } catch (BrowseException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (IOException ioe) {
      fail(ioe.getMessage());
    } finally {
      if (boboBrowser != null) {
        try {
          if (result != null) result.close();
          boboBrowser.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }

  }

  public void testExpandSelection() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);

    FacetSpec output = new FacetSpec();
    output.setExpandSelection(true);
    br.setFacetSpec("color", output);
    br.setFacetSpec("shape", output);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("blue", 2), new BrowseFacet("green", 2),
          new BrowseFacet("red", 3) }));
    answer.put(
      "shape",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("rectangle", 1),
          new BrowseFacet("square", 2) }));

    doTest(br, 3, answer, new String[] { "1", "2", "7" });

    sel = new BrowseSelection("shape");
    sel.addValue("square");
    br.addSelection(sel);

    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("blue", 1), new BrowseFacet("red", 2) }));
    answer.put(
      "shape",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("rectangle", 1),
          new BrowseFacet("square", 2) }));

    doTest(br, 2, answer, new String[] { "1", "7" });
  }

  public void testPath() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("path");
    sel.addValue("a");
    Properties prop = sel.getSelectionProperties();
    PathFacetHandler.setDepth(prop, 1);
    br.addSelection(sel);

    FacetSpec pathSpec = new FacetSpec();
    pathSpec.setOrderBy(FacetSortSpec.OrderValueAsc);
    br.setFacetSpec("path", pathSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "path",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("a-b", 1), new BrowseFacet("a-c", 4),
          new BrowseFacet("a-e", 2) }));
    doTest(br, 7, answer, null);

    pathSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "path",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("a-c", 4), new BrowseFacet("a-e", 2),
          new BrowseFacet("a-b", 1) }));
    doTest(br, 7, answer, null);

    pathSpec.setMaxCount(2);
    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("path",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("a-c", 4), new BrowseFacet("a-e", 2) }));
    doTest(br, 7, answer, null);
  }

  public void testComboFacetHandlerSelectionOnly() {

    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("colorShape");
    sel.addValue("color:green");
    sel.addValue("shape:rectangle");
    sel.addValue("shape:square");
    sel.setSelectionOperation(ValueOperation.ValueOperationOr);
    br.addSelection(sel);

    doTest(br, 6, null, new String[] { "1", "2", "3", "5", "6", "7" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("colorShape");
    sel.addValue("color:green");
    sel.addValue("shape:rectangle");
    sel.setSelectionOperation(ValueOperation.ValueOperationAnd);
    br.addSelection(sel);

    doTest(br, 1, null, new String[] { "6" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("colorShapeMultinum");
    sel.addValue("color:red");
    sel.addValue("shape:square");
    sel.setSelectionOperation(ValueOperation.ValueOperationOr);
    sel.addNotValue("multinum:001");
    sel.addNotValue("multinum:003");
    br.addSelection(sel);

    doTest(br, 1, null, new String[] { "2" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("colorShapeMultinum");
    sel.addValue("color:red");
    sel.addValue("shape:square");
    sel.setSelectionOperation(ValueOperation.ValueOperationOr);
    sel.addNotValue("multinum:003");
    br.addSelection(sel);
    doTest(br, 2, null, new String[] { "2", "5" });
  }

  /**
   * This tests GeoSimpleFacetHandler
   * @throws Exception
   */
  public void testSimpleGeo() throws Exception {
    // testing facet counts for two distance facets - <30,70,5>, <60,120,1>
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("distance");
    sel.addValue("30,70:5");
    sel.addValue("60,120:1");
    br.addSelection(sel);

    FacetSpec geoSpec = new FacetSpec();
    geoSpec.setOrderBy(FacetSortSpec.OrderValueAsc);
    br.setFacetSpec("distance", geoSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "distance",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("30,70:5", 2),
          new BrowseFacet("60,120:1", 2) }));
    doTest(br, 4, answer, new String[] { "1", "3", "4", "5" });
    // testing for selection of facet <60,120,1> and verifying that 2 documents match this facet.
    BrowseRequest br2 = new BrowseRequest();
    br2.setCount(10);
    br2.setOffset(0);

    BrowseSelection sel2 = new BrowseSelection("distance");
    sel2.addValue("60,120:1");
    HashMap<String, Float> map = new HashMap<String, Float>();
    map.put("0,120:1", 3.0f);
    FacetTermQuery geoQ = new FacetTermQuery(sel2, map);

    br2.setQuery(geoQ);
    doTest(br2, 2, null, new String[] { "1", "5" });

    // facet query for color "red" and getting facet counts for the distance facet.
    BrowseRequest br3 = new BrowseRequest();
    br3.setCount(10);
    br3.setOffset(0);

    BrowseSelection sel3 = new BrowseSelection("color");
    sel3.addValue("red");
    HashMap<String, Float> map3 = new HashMap<String, Float>();
    map3.put("red", 3.0f);
    FacetTermQuery colorQ = new FacetTermQuery(sel3, map3);

    br3.setFacetSpec("distance", geoSpec);
    geoSpec.setMinHitCount(0);
    br3.setQuery(colorQ); // query is color=red
    br3.addSelection(sel); // count facets <30,70,5> and <60,120,1>
    answer.clear();
    answer.put(
      "distance",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("30,70:5", 0),
          new BrowseFacet("60,120:1", 1) }));
    doTest(br3, 1, answer, null);

  }

  /**
   * This tests GeoFacetHandler
   * @throws Exception
   */
  public void testGeo() throws Exception {
    // testing facet counts for two distance facets - <30,70,5>, <60,120,1>
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("correctDistance");
    sel.addValue("30,75:100");
    sel.addValue("60,120:1");
    br.addSelection(sel);

    FacetSpec geoSpec = new FacetSpec();
    geoSpec.setMinHitCount(0);
    geoSpec.setOrderBy(FacetSortSpec.OrderValueAsc);
    br.setFacetSpec("correctDistance", geoSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "correctDistance",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("30,75:100", 1),
          new BrowseFacet("60,120:1", 2) }));
    doTest(br, 3, answer, null);

    // testing for selection of facet <60,120,1> and verifying that 2 documents match this facet.
    BrowseRequest br2 = new BrowseRequest();
    br2.setCount(10);
    br2.setOffset(0);

    BrowseSelection sel2 = new BrowseSelection("correctDistance");
    sel2.addValue("60,120:1");
    HashMap<String, Float> map = new HashMap<String, Float>();
    map.put("60,120:1", 3.0f);
    FacetTermQuery geoQ = new FacetTermQuery(sel2, map);

    br2.setQuery(geoQ);
    doTest(br2, 2, null, new String[] { "1", "5" });

    // facet query for color "red" and getting facet counts for the distance facet.
    BrowseRequest br3 = new BrowseRequest();
    br3.setCount(10);
    br3.setOffset(0);

    BrowseSelection sel3 = new BrowseSelection("color");
    sel3.addValue("red");
    HashMap<String, Float> map3 = new HashMap<String, Float>();
    map3.put("red", 3.0f);
    FacetTermQuery colorQ = new FacetTermQuery(sel3, map3);

    br3.setFacetSpec("correctDistance", geoSpec);
    geoSpec.setMinHitCount(1);
    br3.setQuery(colorQ); // query is color=red
    br3.addSelection(sel); // count facets <30,70,5> and <60,120,1>
    answer.clear();
    answer.put("correctDistance",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("60,120:1", 1) }));
    doTest(br3, 1, answer, null);
  }

  public void testMultiPath() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("multipath");
    sel.addValue("a");
    Properties prop = sel.getSelectionProperties();
    PathFacetHandler.setDepth(prop, 1);
    br.addSelection(sel);

    FacetSpec pathSpec = new FacetSpec();
    pathSpec.setMaxCount(3);

    pathSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("multipath", pathSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "multipath",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("a-b", 7), new BrowseFacet("a-c", 4),
          new BrowseFacet("a-e", 2) }));
    doTest(br, 7, answer, null);
  }

  public void testMultiSelectedPaths() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("path");
    sel.addValue("a-c");
    sel.addValue("a-e");
    Properties prop = sel.getSelectionProperties();
    PathFacetHandler.setDepth(prop, 1);
    PathFacetHandler.setStrict(prop, true);
    br.addSelection(sel);

    FacetSpec pathSpec = new FacetSpec();
    pathSpec.setMaxCount(3);

    pathSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("path", pathSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer = new HashMap<String, List<BrowseFacet>>();
    answer
        .put(
          "path",
          Arrays.asList(new BrowseFacet[] { new BrowseFacet("a-c-d", 2),
              new BrowseFacet("a-e-f", 1) }));
    doTest(br, 3, answer, null);

    pathSpec.setOrderBy(FacetSortSpec.OrderByCustom);
    pathSpec.setCustomComparatorFactory(new ComparatorFactory() {

      @Override
      public IntComparator newComparator(FieldValueAccessor fieldValueAccessor,
          final BigSegmentedArray counts) {
        return new IntComparator() {

          @Override
          public int compare(Integer f1, Integer f2) {
            int val = counts.get(f2) - counts.get(f1);
            if (val == 0) {
              val = f2 - f1;
            }
            return val;
          }

          @Override
          public int compare(int f1, int f2) {
            int val = counts.get(f2) - counts.get(f1);
            if (val == 0) {
              val = f2 - f1;
            }
            return val;
          }

        };
      }

      @Override
      public Comparator<BrowseFacet> newComparator() {
        return new Comparator<BrowseFacet>() {
          @Override
          public int compare(BrowseFacet f1, BrowseFacet f2) {
            int val = f2.getFacetValueHitCount() - f1.getFacetValueHitCount();
            if (val == 0) {
              val = f1.getValue().compareTo(f2.getValue());
            }
            return val;
          }
        };
      }

    });

    answer = new HashMap<String, List<BrowseFacet>>();
    answer
        .put(
          "path",
          Arrays.asList(new BrowseFacet[] { new BrowseFacet("a-c-d", 2),
              new BrowseFacet("a-e-f", 1) }));
    doTest(br, 3, answer, null);
  }

  public void testTagRollup() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("location");
    Properties prop = sel.getSelectionProperties();
    PathFacetHandler.setDepth(prop, 1);
    PathFacetHandler.setStrict(prop, true);
    sel.addValue("toy/lego");
    br.addSelection(sel);

    FacetSpec locationOutput = new FacetSpec();

    br.setFacetSpec("location", locationOutput);

    FacetSpec tagOutput = new FacetSpec();
    tagOutput.setMaxCount(50);
    tagOutput.setOrderBy(FacetSortSpec.OrderHitsDesc);

    br.setFacetSpec("tag", tagOutput);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("location",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("toy/lego/block", 3) }));
    answer
        .put(
          "tag",
          Arrays.asList(new BrowseFacet[] { new BrowseFacet("pet", 2),
              new BrowseFacet("animal", 1), new BrowseFacet("dog", 1), new BrowseFacet("funny", 1),
              new BrowseFacet("humor", 1), new BrowseFacet("joke", 1),
              new BrowseFacet("poodle", 1), new BrowseFacet("rabbit", 1) }));
    doTest(br, 3, answer, null);
  }

  public void testChar() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("char");
    sel.addValue("j");
    br.addSelection(sel);
    doTest(br, 1, null, new String[] { "3" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);

    FacetSpec charOutput = new FacetSpec();
    charOutput.setMaxCount(50);
    charOutput.setOrderBy(FacetSortSpec.OrderHitsDesc);

    br.setFacetSpec("char", charOutput);
    br.addSortField(new SortField("date", SortField.Type.CUSTOM, true));

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "char",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("a", 1), new BrowseFacet("i", 1),
          new BrowseFacet("k", 1) }));

    doTest(br, 3, answer, new String[] { "7", "2", "1" });
  }

  public void testDate() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("date");
    sel.addValue("[2001/01/01 TO 2005/01/01]");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    ospec.setExpandSelection(false);
    br.setFacetSpec("color", ospec);

    br.addSortField(new SortField("date", SortField.Type.CUSTOM, true));

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("blue", 2), new BrowseFacet("green", 1),
          new BrowseFacet("red", 1) }));
    doTest(br, 4, answer, new String[] { "4", "2", "5", "3" });
  }

  public void testDate2() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("date");
    sel.addValue("[2005/01/01 TO *]");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    ospec.setExpandSelection(false);
    br.setFacetSpec("color", ospec);

    br.addSortField(new SortField("date", SortField.Type.CUSTOM, true));

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("green", 1), new BrowseFacet("red", 1) }));
    doTest(br, 2, answer, new String[] { "6", "7" });
  }

  public void testDate3() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("date");
    sel.addValue("[* TO 2002/01/01]");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    ospec.setExpandSelection(false);
    br.setFacetSpec("color", ospec);

    br.addSortField(new SortField("date", SortField.Type.CUSTOM, true));

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("green", 1), new BrowseFacet("red", 1) }));
    doTest(br, 2, answer, new String[] { "3", "1" });
  }

  /**
   * Do the test and check result.
   * @param req
   * @param numHits
   * @param choiceMap
   * @param ids
   */
  private BrowseResult doTest(BrowseRequest req, int numHits,
      HashMap<String, List<BrowseFacet>> choiceMap, String[] ids) {
    return doTest((BoboBrowser) null, req, numHits, choiceMap, ids);
  }

  private BrowseResult doTest(BoboBrowser boboBrowser, BrowseRequest req, int numHits,
      HashMap<String, List<BrowseFacet>> choiceMap, String[] ids) {
    BrowseResult result = null;
    try {
      if (boboBrowser == null) {
        boboBrowser = newBrowser();
      }
      result = boboBrowser.browse(req);
      doTest(result, req, numHits, choiceMap, ids);
      return result;// result;
    } catch (BrowseException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (IOException ioe) {
      fail(ioe.getMessage());
    } finally {
      if (boboBrowser != null) {
        try {
          if (result != null) result.close();
          boboBrowser.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }
    return null;
  }

  public void testLuceneSort() throws IOException {

    DirectoryReader srcReader = DirectoryReader.open(_indexDir);
    try {
      List<FacetHandler<?>> facetHandlers = new ArrayList<FacetHandler<?>>();
      facetHandlers.add(new SimpleFacetHandler("id"));

      BoboMultiReader reader = BoboMultiReader.getInstance(srcReader, facetHandlers);
      BoboBrowser browser = new BoboBrowser(reader);

      BrowseRequest browseRequest = new BrowseRequest();
      browseRequest.setCount(10);
      browseRequest.setOffset(0);
      browseRequest.addSortField(new SortField("date", SortField.Type.STRING));

      doTest(browser, browseRequest, 7, null, new String[] { "1", "3", "5", "2", "4", "7", "6" });

    } catch (IOException ioe) {
      if (srcReader != null) {
        srcReader.close();
      }
      throw ioe;
    }
  }

  public void testFacetSort() {

    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    FacetSpec colorSpec = new FacetSpec();
    colorSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("color", colorSpec);

    FacetSpec shapeSpec = new FacetSpec();
    shapeSpec.setOrderBy(FacetSortSpec.OrderValueAsc);
    br.setFacetSpec("shape", shapeSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 3), new BrowseFacet("blue", 2),
          new BrowseFacet("green", 2) }));
    answer.put(
      "shape",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("circle", 2),
          new BrowseFacet("rectangle", 2), new BrowseFacet("square", 3) }));

    doTest(br, 7, answer, null);

    Comparator<BrowseFacet> valComp = new FacetValueComparatorFactory().newComparator();

    int v = valComp.compare(new BrowseFacet("red", 3), new BrowseFacet("blue", 2));
    assertTrue(v > 0);

    valComp = new FacetHitcountComparatorFactory().newComparator();
    v = valComp.compare(new BrowseFacet("red", 3), new BrowseFacet("blue", 2));
    assertTrue(v < 0);

    v = valComp.compare(new BrowseFacet("red", 3), new BrowseFacet("blue", 3));
    assertTrue(v > 0);
  }

  public void testMultiDate() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("date");
    sel.addValue("[2000/01/01 TO 2002/07/07]");
    sel.addValue("[2003/01/01 TO 2005/01/01]");
    br.addSelection(sel);

    br.addSortField(new SortField("date", SortField.Type.CUSTOM, false));

    doTest(br, 5, null, new String[] { "1", "3", "5", "2", "4" });
  }

  public void testNoCount() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(0);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    ospec.setExpandSelection(false);
    br.setFacetSpec("color", ospec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color", Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 3) }));

    doTest(br, 3, null, new String[0]);
  }

  public void testDate4() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("date");
    sel.addValue("[* TO *]");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    ospec.setExpandSelection(false);
    br.setFacetSpec("color", ospec);

    br.addSortField(new SortField("date", SortField.Type.CUSTOM, false));

    doTest(br, 7, null, new String[] { "1", "3", "5", "2", "4", "7", "6" });
  }

  public void testMultiSort() {
    // no sel
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    br.setSort(new SortField[] { new SortField("color", SortField.Type.CUSTOM, false),
        new SortField("number", SortField.Type.CUSTOM, true) });

    doTest(br, 7, null, new String[] { "5", "4", "6", "3", "2", "1", "7" });

    // now test with serialization

    BrowseResult result = null;
    BoboBrowser boboBrowser = null;
    try {
      boboBrowser = newBrowser();

      result = boboBrowser.browse(br);

      ByteArrayOutputStream bout = new ByteArrayOutputStream();
      ObjectOutputStream oout = new ObjectOutputStream(bout);
      oout.writeObject(result);
      oout.flush();
      byte[] serialized = bout.toByteArray();

      ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
      ObjectInputStream oin = new ObjectInputStream(bin);

      result = (BrowseResult) oin.readObject();

      doTest(result, br, 7, null, new String[] { "5", "4", "6", "3", "2", "1", "7" });

    } catch (BrowseException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (Exception ioe) {
      ioe.printStackTrace();
      fail(ioe.getMessage());
    } finally {
      if (boboBrowser != null) {
        try {
          if (result != null) result.close();
          boboBrowser.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }
  }

  public void testSort() {
    // no sel
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    br.setSort(new SortField[] { new SortField("number", SortField.Type.CUSTOM, true) });
    doTest(br, 7, null, new String[] { "6", "5", "4", "3", "2", "1", "7" });
    br.setSort(new SortField[] { new SortField("name", SortField.Type.STRING, false) });
    doTest(br, 7, null, new String[] { "7", "4", "6", "2", "3", "1", "5" });

    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);
    br.setSort(new SortField[] { new SortField("number", SortField.Type.CUSTOM, true) });
    doTest(br, 3, null, new String[] { "2", "1", "7" });
    br.setSort(new SortField[] { new SortField("name", SortField.Type.STRING, false) });
    doTest(br, 3, null, new String[] { "7", "2", "1" });

    sel.addValue("blue");
    br.setQuery(new TermQuery(new Term("shape", "square")));
    br.setSort(new SortField[] { new SortField("number", SortField.Type.CUSTOM, true) });
    doTest(br, 3, null, new String[] { "5", "1", "7" });
    br.setSort(new SortField[] { new SortField("name", SortField.Type.STRING, false) });
    doTest(br, 3, null, new String[] { "7", "1", "5" });
  }

  public void testCustomSort() {

    final class CustomSortComparatorSource extends DocComparatorSource {
      @Override
      public DocComparator getComparator(AtomicReader reader, int docbase) throws IOException {
        return new CustomSortDocComparator();
      }

      final class CustomSortDocComparator extends DocComparator implements Serializable {

        private static final long serialVersionUID = 1L;

        @Override
        public int compare(ScoreDoc doc1, ScoreDoc doc2) {
          int id1 = Math.abs(doc1.doc - 4);
          int id2 = Math.abs(doc2.doc - 4);
          int val = id1 - id2;
          if (val == 0) {
            return doc1.doc - doc2.doc;
          }
          return val;
        }

        @Override
        public Comparable<?> value(ScoreDoc doc) {
          return new Integer(Math.abs(doc.doc - 4));
        }

      }

    }
    // no sel
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    br.setSort(new SortField[] { new BoboCustomSortField("custom", false,
        new CustomSortComparatorSource()) });
    doTest(br, 7, null, new String[] { "5", "4", "6", "3", "7", "2", "1" });

  }

  public void testDefaultBrowse() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(3);
    br.setOffset(0);

    FacetSpec spec = new FacetSpec();
    spec.setMaxCount(2);
    spec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("color", spec);

    br.setSort(new SortField[] { new SortField("number", SortField.Type.CUSTOM, false) });

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 3), new BrowseFacet("blue", 2) }));

    doTest(br, 7, answer, new String[] { "7", "1", "2" });
  }

  public void testMinHit() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(3);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("shape");
    sel.addValue("square");
    br.addSelection(sel);

    FacetSpec spec = new FacetSpec();
    spec.setMinHitCount(0);
    spec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("color", spec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 2), new BrowseFacet("blue", 1),
          new BrowseFacet("green", 0) }));

    doTest(br, 3, answer, null);
  }

  public void testRandomAccessFacet() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    br.setFacetSpec("number", new FacetSpec());

    BoboBrowser browser = newBrowser();

    BrowseResult res = browser.browse(br);
    FacetAccessible facetAccessor = res.getFacetAccessor("number");
    BrowseFacet facet = facetAccessor.getFacet("5");

    assertEquals(facet.getValue(), "0005");
    assertEquals(facet.getFacetValueHitCount(), 1);
    res.close();
  }

  public void testQueryWithScore() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setShowExplanation(false);
    QueryParser parser = new QueryParser(Version.LUCENE_43, "color", new StandardAnalyzer(
        Version.LUCENE_43));
    br.setQuery(parser.parse("color:red OR shape:square"));
    br.setCount(10);
    br.setOffset(0);

    br.setSort(new SortField[] { SortField.FIELD_SCORE });
    BrowseResult res = doTest(br, 4, null, new String[] { "1", "7", "2", "5" });

    BrowseHit[] hits = res.getHits();
    for (BrowseHit hit : hits) {
      assertNull(hit.getExplanation());
    }

    br.setShowExplanation(true);
    res = doTest(br, 4, null, new String[] { "1", "7", "2", "5" });
    hits = res.getHits();

    for (BrowseHit hit : hits) {
      assertNotNull(hit.getExplanation());
    }

    Query rawQuery = br.getQuery();

    SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
    Date d = format.parse("2006/06/01");
    long fromTime = d.getTime();

    RecencyBoostScorerBuilder recencyBuilder = new RecencyBoostScorerBuilder("date", 2.0f,
        TimeUnit.DAYS.convert(fromTime, TimeUnit.MILLISECONDS), 30L, TimeUnit.DAYS);
    ScoreAdjusterQuery sq = new ScoreAdjusterQuery(rawQuery, recencyBuilder);
    br.setQuery(sq);

    res = doTest(br, 4, null, new String[] { "7", "1", "2", "5" });

    hits = res.getHits();
    for (BrowseHit hit : hits) {
      assertNotNull(hit.getExplanation());
      System.out.println(hit.getExplanation());
    }
  }

  public void testBrowseWithQuery() {
    try {
      BrowseRequest br = new BrowseRequest();
      QueryParser parser = new QueryParser(Version.LUCENE_43, "shape", new StandardAnalyzer(
          Version.LUCENE_43));
      br.setQuery(parser.parse("square OR circle"));
      br.setCount(10);
      br.setOffset(0);

      BrowseSelection sel = new BrowseSelection("color");
      sel.addValue("red");
      br.addSelection(sel);

      br.setSort(new SortField[] { new SortField("number", SortField.Type.CUSTOM, false) });
      doTest(br, 2, null, new String[] { "7", "1" });

      FacetSpec ospec = new FacetSpec();
      ospec.setExpandSelection(true);
      br.setFacetSpec("color", ospec);
      HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
      answer.put(
        "color",
        Arrays.asList(new BrowseFacet[] { new BrowseFacet("blue", 2), new BrowseFacet("green", 1),
            new BrowseFacet("red", 2) }));
      doTest(br, 2, answer, new String[] { "7", "1" });

      br.clearSelections();
      answer.put(
        "color",
        Arrays.asList(new BrowseFacet[] { new BrowseFacet("blue", 2), new BrowseFacet("green", 1),
            new BrowseFacet("red", 2) }));
      doTest(br, 5, answer, new String[] { "7", "1", "3", "4", "5" });

    } catch (Exception e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
  }

  public void testBrowseCompactMultiVal() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    BrowseSelection sel = new BrowseSelection("compactnum");
    sel.addValue("001");
    sel.addValue("003");
    sel.addValue("007");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    br.setFacetSpec("compactnum", ospec);

    br.setSort(new SortField[] { new SortField("compactnum", SortField.Type.CUSTOM, true) });

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();

    answer.put(
      "compactnum",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("001", 3), new BrowseFacet("002", 1),
          new BrowseFacet("003", 3), new BrowseFacet("007", 2), new BrowseFacet("008", 1),
          new BrowseFacet("012", 1) }));

    doTest(br, 6, answer, new String[] { "3", "7", "4", "6", "1", "5" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    sel = new BrowseSelection("compactnum");
    sel.addValue("001");
    sel.addValue("002");
    sel.addValue("003");
    br.addSelection(sel);
    sel.setSelectionOperation(ValueOperation.ValueOperationAnd);
    doTest(br, 1, null, new String[] { "6" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    sel = new BrowseSelection("compactnum");
    sel.addValue("001");
    sel.addValue("003");
    sel.addValue("008");
    sel.setSelectionOperation(ValueOperation.ValueOperationOr);
    br.addSelection(sel);

    sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);

    ospec = new FacetSpec();
    br.setFacetSpec("color", ospec);

    ospec = new FacetSpec();
    br.setFacetSpec("compactnum", ospec);
    answer = new HashMap<String, List<BrowseFacet>>();

    answer.put(
      "compactnum",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("001", 1), new BrowseFacet("003", 2),
          new BrowseFacet("008", 1) }));
    answer.put("color", Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 2) }));
    doTest(br, 2, answer, new String[] { "1", "7" });

    doTest(br, 2, answer, new String[] { "1", "7" });
  }

  public void testBrowseMultiValWithWeight() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    BrowseSelection sel = new BrowseSelection("multiwithweight");
    sel.addValue("cool");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    br.setFacetSpec("multiwithweight", ospec);
    br.setSort(new SortField[] { new SortField("multiwithweight", SortField.Type.CUSTOM, true) });
    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();

    answer.put("multiwithweight",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("cool", 3), new BrowseFacet("good", 2) }));

    doTest(br, 3, answer, new String[] { "1", "2", "3" });
  }

  public void testMultiWithDiffName() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("diffname");
    sel.addValue("001");

    br.addSelection(sel);

    doTest(br, 3, null, new String[] { "1", "5", "6" });
  }

  public void testBrowseMultiVal() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    BrowseSelection sel = new BrowseSelection("multinum");
    sel.addValue("001");
    sel.addValue("003");
    sel.addValue("007");
    br.addSelection(sel);

    FacetSpec ospec = new FacetSpec();
    br.setFacetSpec("multinum", ospec);
    br.setSort(new SortField[] { new SortField("multinum", SortField.Type.CUSTOM, true) });
    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();

    answer.put(
      "multinum",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("001", 3), new BrowseFacet("002", 1),
          new BrowseFacet("003", 3), new BrowseFacet("007", 2), new BrowseFacet("008", 1),
          new BrowseFacet("012", 1) }));

    doTest(br, 6, answer, new String[] { "3", "4", "7", "1", "6", "5" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    sel = new BrowseSelection("multinum");
    sel.addValue("001");
    sel.addValue("002");
    sel.addValue("003");
    br.addSelection(sel);
    sel.setSelectionOperation(ValueOperation.ValueOperationAnd);
    doTest(br, 1, null, new String[] { "6" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    sel = new BrowseSelection("multinum");
    sel.addValue("001");
    sel.addValue("003");
    sel.addValue("008");
    sel.setSelectionOperation(ValueOperation.ValueOperationOr);
    br.addSelection(sel);

    sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);

    ospec = new FacetSpec();
    br.setFacetSpec("color", ospec);

    ospec = new FacetSpec();
    br.setFacetSpec("multinum", ospec);
    answer = new HashMap<String, List<BrowseFacet>>();

    answer.put(
      "multinum",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("001", 1), new BrowseFacet("003", 2),
          new BrowseFacet("008", 1) }));
    answer.put("color", Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 2) }));
    doTest(br, 2, answer, new String[] { "1", "7" });

  }

  public void testBrowseWithDeletes() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);
    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();

    doTest(br, 3, answer, new String[] { "1", "2", "7" });

    BoboMultiReader reader = null;
    try {
      Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_43);
      IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_43, analyzer);
      IndexWriter idxWriter = new IndexWriter(_indexDir, config);
      idxWriter.deleteDocuments(new Term("id", "1"));
      idxWriter.deleteDocuments(new Term("id", "2"));
      idxWriter.commit();
      reader = newIndexReader();

      br = new BrowseRequest();
      br.setCount(10);
      br.setOffset(0);

      sel = new BrowseSelection("color");
      sel.addValue("red");
      br.addSelection(sel);
      answer = new HashMap<String, List<BrowseFacet>>();

      doTest(new BoboBrowser(reader), br, 1, answer, new String[] { "7" });
    } catch (IOException ioe) {
      fail(ioe.getMessage());
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException e) {
          fail(e.getMessage());
        }
      }
    }
    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);
    answer = new HashMap<String, List<BrowseFacet>>();

    doTest(br, 1, answer, new String[] { "7" });
  }

  public void testNotSupport() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("color");
    sel.addNotValue("red");
    br.addSelection(sel);

    FacetSpec simpleOutput = new FacetSpec();
    br.setFacetSpec("shape", simpleOutput);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "shape",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("circle", 2),
          new BrowseFacet("rectangle", 1), new BrowseFacet("square", 1) }));

    doTest(br, 4, answer, new String[] { "3", "4", "5", "6" });

    sel.addNotValue("green");

    answer.put("shape", Arrays.asList(new BrowseFacet[] { new BrowseFacet("circle", 1),
        new BrowseFacet("square", 1) }));

    doTest(br, 2, answer, new String[] { "4", "5" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    sel = new BrowseSelection("compactnum");
    sel.addNotValue("3");
    sel.addNotValue("4");
    sel.addValue("1");
    sel.addValue("2");
    sel.addValue("7");

    br.addSelection(sel);
    doTest(br, 3, null, new String[] { "3", "4", "5" });

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    sel = new BrowseSelection("multinum");
    sel.addNotValue("3");
    sel.addNotValue("4");
    sel.addValue("1");
    sel.addValue("2");
    sel.addValue("7");

    br.addSelection(sel);

    doTest(br, 3, null, new String[] { "3", "4", "5" });

  }

  public void testMissedSelection() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);
    BrowseSelection sel = new BrowseSelection("location");
    sel.addValue("something/stupid");
    br.addSelection(sel);
    doTest(br, 0, null, null);
  }

  public void testDateRange() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    FacetSpec simpleOutput = new FacetSpec();
    simpleOutput.setExpandSelection(true);
    br.setFacetSpec("date", simpleOutput);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "date",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("[2000/01/01 TO 2003/05/05]", 4),
          new BrowseFacet("[2003/05/06 TO 2005/04/04]", 1) }));
    doTest(br, 7, answer, null);
  }

  public void testNewRangeFacet() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    FacetSpec simpleOutput = new FacetSpec();
    simpleOutput.setExpandSelection(true);
    br.setFacetSpec("date", simpleOutput);

    BrowseSelection sel1 = new BrowseSelection("date");
    sel1.setValues(new String[] { "(2000/01/01 TO 2003/02/14]" });
    BrowseSelection sel2 = new BrowseSelection("date");
    sel2.setValues(new String[] { "(2000/01/01 TO 2003/02/14)" });

    br.addSelection(sel1);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "date",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("[2000/01/01 TO 2003/02/14]", 4),
          new BrowseFacet("[2003/05/06 TO 2005/04/04]", 1) }));
    doTest(br, 3, null, null);

    br.clearSelections();
    br.addSelection(sel2);
    doTest(br, 2, null, null);

  }

  /**
   * Verifies the range facet numbers are returned correctly (as they were passed in)
   */
  public void testNumEndorsers() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    FacetSpec simpleOutput = new FacetSpec();
    simpleOutput.setExpandSelection(true);
    br.setFacetSpec("numendorsers", simpleOutput);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "numendorsers",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("[000000 TO 000005]", 2),
          new BrowseFacet("[000006 TO 000010]", 2), new BrowseFacet("[000011 TO 000020]", 3) }));
    doTest(br, 7, answer, null);
  }

  public void testBrowse() {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    br.addSelection(sel);

    sel = new BrowseSelection("location");
    sel.addValue("toy/lego");

    Properties prop = sel.getSelectionProperties();
    PathFacetHandler.setDepth(prop, 1);
    br.addSelection(sel);

    sel = new BrowseSelection("size");
    sel.addValue("[* TO 4]");

    sel = new BrowseSelection("tag");
    sel.addValue("rabbit");
    br.addSelection(sel);

    FacetSpec output = new FacetSpec();
    output.setMaxCount(5);

    FacetSpec simpleOutput = new FacetSpec();
    simpleOutput.setExpandSelection(true);

    br.setFacetSpec("color", simpleOutput);
    br.setFacetSpec("size", output);
    br.setFacetSpec("shape", simpleOutput);
    br.setFacetSpec("location", output);

    FacetSpec tagOutput = new FacetSpec();
    tagOutput.setMaxCount(5);
    tagOutput.setOrderBy(FacetSortSpec.OrderHitsDesc);

    br.setFacetSpec("tag", tagOutput);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("green", 1), new BrowseFacet("red", 2) }));
    answer.put(
      "size",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("[* TO 4]", 1),
          new BrowseFacet("[5 TO 8]", 1) }));
    answer.put("shape", Arrays.asList(new BrowseFacet[] { new BrowseFacet("square", 2) }));
    answer.put(
      "location",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("toy/lego/", 1),
          new BrowseFacet("toy/lego/block", 1) }));
    answer.put(
      "tag",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("rabbit", 2), new BrowseFacet("animal", 1),
          new BrowseFacet("dog", 1), new BrowseFacet("humane", 1), new BrowseFacet("pet", 1) }));
    doTest(br, 2, answer, null);

  }

  /**
   * Tests the MultiBoboBrowser functionality by creating a BoboBrowser and
   * submitting the same browserequest 2 times generating 2 BrowseResults.
   * The 2 BoboBrowsers are instantiated with the MultiBoboBrowser and the browse method is called.
   * The BrowseResult generated is submitted to the doTest method which compares the result
   */
  public void testMultiBrowser() throws Exception {
    BrowseRequest browseRequest = new BrowseRequest();
    browseRequest.setCount(10);
    browseRequest.setOffset(0);
    browseRequest.addSortField(new SortField("date", SortField.Type.CUSTOM));

    BrowseSelection colorSel = new BrowseSelection("color");
    colorSel.addValue("red");
    browseRequest.addSelection(colorSel);

    BrowseSelection tageSel = new BrowseSelection("tag");
    tageSel.addValue("rabbit");
    browseRequest.addSelection(tageSel);

    FacetSpec colorFacetSpec = new FacetSpec();
    colorFacetSpec.setExpandSelection(true);
    colorFacetSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);

    FacetSpec tagFacetSpec = new FacetSpec();

    browseRequest.setFacetSpec("color", colorFacetSpec);
    browseRequest.setFacetSpec("tag", tagFacetSpec);

    FacetSpec shapeSpec = new FacetSpec();
    shapeSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    browseRequest.setFacetSpec("shape", shapeSpec);

    FacetSpec dateSpec = new FacetSpec();
    dateSpec.setExpandSelection(true);
    browseRequest.setFacetSpec("date", dateSpec);

    BoboBrowser boboBrowser = newBrowser();

    browseRequest
        .setSort(new SortField[] { new SortField("compactnum", SortField.Type.CUSTOM, true) });

    MultiBoboBrowser multiBoboBrowser = new MultiBoboBrowser(new Browsable[] { boboBrowser,
        boboBrowser });
    BrowseResult mergedResult = multiBoboBrowser.browse(browseRequest);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("color",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red", 4), new BrowseFacet("green", 2) }));
    answer.put(
      "tag",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("animal", 2), new BrowseFacet("dog", 2),
          new BrowseFacet("humane", 2), new BrowseFacet("pet", 2), new BrowseFacet("rabbit", 4) }));
    answer.put("shape", Arrays.asList(new BrowseFacet[] { new BrowseFacet("square", 4) }));
    answer.put("date",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("[2000/01/01 TO 2003/05/05]", 2) }));

    doTest(mergedResult, browseRequest, 4, answer, new String[] { "7", "7", "1", "1" });

    browseRequest
        .setSort(new SortField[] { new SortField("multinum", SortField.Type.CUSTOM, true) });
    mergedResult = multiBoboBrowser.browse(browseRequest);
    doTest(mergedResult, browseRequest, 4, answer, new String[] { "7", "7", "1", "1" });
    mergedResult.close();
    multiBoboBrowser.close();
  }

  public void testFacetQueryBoost() throws Exception {
    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    sel.addValue("blue");
    HashMap<String, Float> map = new HashMap<String, Float>();
    map.put("red", 5.0f);
    map.put("blue", 4.0f);
    FacetTermQuery colorQ = new FacetTermQuery(sel, map);

    BrowseSelection sel2 = new BrowseSelection("shape");
    sel2.addValue("circle");
    sel2.addValue("square");
    HashMap<String, Float> map2 = new HashMap<String, Float>();
    map2.put("circle", 3.0f);
    map2.put("square", 2.0f);
    FacetTermQuery shapeQ = new FacetTermQuery(sel2, map2);
    shapeQ.setBoost(3.0f);

    BooleanQuery bq = new BooleanQuery();
    bq.add(shapeQ, Occur.SHOULD);
    bq.add(colorQ, Occur.SHOULD);

    BrowseRequest br = new BrowseRequest();
    br.setSort(new SortField[] { SortField.FIELD_SCORE });
    br.setQuery(bq);
    br.setOffset(0);
    br.setCount(10);

    BrowseResult res = doTest(br, 6, null, new String[] { "4", "1", "7", "5", "3", "2" });
    BrowseHit[] hits = res.getHits();
    float[] scores = new float[] { 13, 11, 11, 10, 4.5f, 2.5f }; // default coord = 1/2
    for (int i = 0; i < hits.length; ++i) {
      assertEquals(scores[i], hits[i].getScore());
    }
  }

  public void testFacetQuery() throws Exception {
    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    sel.addValue("blue");
    HashMap<String, Float> map = new HashMap<String, Float>();
    map.put("red", 3.0f);
    map.put("blue", 2.0f);
    FacetTermQuery colorQ = new FacetTermQuery(sel, map);

    BrowseSelection sel2 = new BrowseSelection("tag");
    sel2.addValue("rabbit");
    sel2.addValue("dog");
    HashMap<String, Float> map2 = new HashMap<String, Float>();
    map2.put("rabbit", 100.0f);
    map2.put("dog", 50.0f);
    FacetTermQuery tagQ = new FacetTermQuery(sel2, map2);

    BrowseRequest br = new BrowseRequest();
    br.setQuery(colorQ);
    br.setOffset(0);
    br.setCount(10);

    doTest(br, 5, null, new String[] { "1", "2", "7", "4", "5" });

    // BoboBrowser b = newBrowser();
    // Explanation expl = b.explain(colorQ, 0);

    br.setQuery(tagQ);
    doTest(br, 4, null, new String[] { "7", "1", "3", "2" });
    // expl = b.explain(tagQ, 6);

  }

  public void testFacetQueryBoolean() throws Exception {
    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("red");
    sel.addValue("blue");
    HashMap<String, Float> map = new HashMap<String, Float>();
    map.put("red", 3.0f);
    map.put("blue", 2.0f);
    FacetTermQuery colorQ = new FacetTermQuery(sel, map);

    BrowseSelection sel2 = new BrowseSelection("tag");
    sel2.addValue("rabbit");
    sel2.addValue("dog");
    HashMap<String, Float> map2 = new HashMap<String, Float>();
    map2.put("rabbit", 100.0f);
    map2.put("dog", 50.0f);
    FacetTermQuery tagQ = new FacetTermQuery(sel2, map2);

    BrowseRequest br = new BrowseRequest();

    br.setOffset(0);
    br.setCount(10);

    BooleanQuery bq = new BooleanQuery(true);
    bq.add(colorQ, Occur.SHOULD);
    bq.add(tagQ, Occur.SHOULD);

    br.setQuery(bq);
    doTest(br, 6, null, new String[] { "7", "1", "3", "2", "4", "5" });

  }

  public void testFacetRangeQuery() throws Exception {
    BrowseSelection sel = new BrowseSelection("numendorsers");
    sel.addValue("[* TO 000010]");

    HashMap<String, Float> map = new HashMap<String, Float>();
    map.put("000002", 100.0f);
    map.put("000010", 50.0f);
    FacetTermQuery numberQ = new FacetTermQuery(sel, map);

    BrowseRequest br = new BrowseRequest();
    br.setQuery(numberQ);
    br.setOffset(0);
    br.setCount(10);

    doTest(br, 4, null, new String[] { "5", "2", "1", "6" });
  }

  public void testFacetBoost() throws Exception {
    Map<String, Map<String, Float>> boostMaps = new HashMap<String, Map<String, Float>>();
    HashMap<String, Float> map;

    map = new HashMap<String, Float>();
    map.put("red", 3.0f);
    map.put("blue", 2.0f);
    boostMaps.put("color", map);

    map = new HashMap<String, Float>();
    map.put("rabbit", 5.0f);
    map.put("dog", 7.0f);
    boostMaps.put("tag", map);

    Query q = new ScoreAdjusterQuery(new MatchAllDocsQuery(), new FacetBasedBoostScorerBuilder(
        boostMaps));

    BrowseRequest br = new BrowseRequest();
    br.setQuery(q);
    br.setOffset(0);
    br.setCount(10);
    br.setSort(new SortField[] { SortField.FIELD_SCORE });
    BoboBrowser b = newBrowser();

    BrowseResult r = b.browse(br);

    doTest(r, br, 7, null, new String[] { "7", "2", "1", "3", "4", "5", "6" });
  }

  public void testRuntimeFilteredDateRange() throws Exception {
    BoboBrowser browser = newBrowser();
    String[] ranges = new String[] { "[2001/01/01 TO 2001/12/30]", "[2007/01/01 TO 2007/12/30]" };
    FilteredRangeFacetHandler handler = new FilteredRangeFacetHandler("filtered_date", "date",
        Arrays.asList(ranges));
    browser.setFacetHandler(handler);

    BrowseRequest req = new BrowseRequest();
    req.setFacetSpec("filtered_date", new FacetSpec());
    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "filtered_date",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("[2001/01/01 TO 2001/12/30]", 1),
          new BrowseFacet("[2007/01/01 TO 2007/12/30]", 1) }));

    doTest(browser, req, 7, answer, null);
  }

  public void testCustomFacetSort() throws Exception {
    BrowseRequest req = new BrowseRequest();
    FacetSpec numberSpec = new FacetSpec();
    numberSpec.setCustomComparatorFactory(new ComparatorFactory() {

      @Override
      public IntComparator newComparator(final FieldValueAccessor fieldValueAccessor,
          final BigSegmentedArray counts) {

        return new IntComparator() {

          @Override
          public int compare(Integer v1, Integer v2) {
            Integer size1 = (Integer) fieldValueAccessor.getRawValue(v1);
            Integer size2 = (Integer) fieldValueAccessor.getRawValue(v2);

            int val = size1 - size2;
            if (val == 0) {
              val = counts.get(v1) - counts.get(v2);
            }
            return val;
          }

          @Override
          public int compare(int v1, int v2) {
            int size1 = (Integer) fieldValueAccessor.getRawValue(v1);
            int size2 = (Integer) fieldValueAccessor.getRawValue(v2);

            int val = size1 - size2;
            if (val == 0) {
              val = counts.get(v1) - counts.get(v2);
            }
            return val;
          }
        };
      }

      @Override
      public Comparator<BrowseFacet> newComparator() {
        return new Comparator<BrowseFacet>() {
          @Override
          public int compare(BrowseFacet o1, BrowseFacet o2) {
            int v1 = Integer.parseInt(o1.getValue());
            int v2 = Integer.parseInt(o2.getValue());
            int val = v2 - v1;
            if (val == 0) {
              val = o2.getFacetValueHitCount() - o1.getFacetValueHitCount();
            }
            return val;
          }
        };
      }
    });
    numberSpec.setOrderBy(FacetSortSpec.OrderByCustom);
    numberSpec.setMaxCount(3);
    req.setFacetSpec("number", numberSpec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "number",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("2130", 1), new BrowseFacet("1013", 1),
          new BrowseFacet("0913", 1) }));

    doTest(req, 7, answer, null);

    numberSpec.setOrderBy(FacetSortSpec.OrderValueAsc);
    answer.put(
      "number",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("0005", 1), new BrowseFacet("0010", 1),
          new BrowseFacet("0011", 1) }));

    doTest(req, 7, answer, null);
  }

  public void testSimpleGroupbyFacetHandler() throws Exception {
    BrowseRequest req = new BrowseRequest();
    FacetSpec fspec = new FacetSpec();
    req.setFacetSpec("groupby", fspec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put(
      "groupby",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red,rectangle,0011", 1),
          new BrowseFacet("red,square,0005", 1), new BrowseFacet("red,square,0010", 1) }));

    BrowseSelection sel = new BrowseSelection("groupby");
    sel.addValue("red");
    req.addSelection(sel);

    doTest(req, 3, answer, null);

    sel.setValues(new String[] { "red,square" });
    answer.put(
      "groupby",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red,square,0005", 1),
          new BrowseFacet("red,square,0010", 1) }));

    doTest(req, 2, answer, null);

    sel.setValues(new String[] { "red,square,0005" });
    answer.put("groupby",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("red,square,0005", 1) }));

    doTest(req, 1, answer, null);

    req.removeSelection("groupby");
    fspec.setMaxCount(2);
    answer.put(
      "groupby",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("blue,circle,0913", 1),
          new BrowseFacet("blue,square,1013", 1) }));

    doTest(req, 7, answer, null);

  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  public void testTime() throws Exception {
    List<FacetHandler<?>> facetHandlers = new ArrayList<FacetHandler<?>>();
    /* Underlying time facet for DynamicTimeRangeFacetHandler */
    facetHandlers.add(new RangeFacetHandler("timeinmillis", new PredefinedTermListFactory(
        Long.class, DynamicTimeRangeFacetHandler.NUMBER_FORMAT), null));
    Directory idxDir = new RAMDirectory();
    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_43);
    IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_43, analyzer);
    IndexWriter idxWriter = new IndexWriter(idxDir, config);

    long now = System.currentTimeMillis();
    DecimalFormat df = new DecimalFormat(DynamicTimeRangeFacetHandler.NUMBER_FORMAT);
    for (long l = 0; l < 53; l++) {
      Document d = new Document();
      d.add(buildMetaField("timeinmillis", df.format(now - l * 3500000)));
      idxWriter.addDocument(d);
      idxWriter.forceMerge(1);
      idxWriter.commit();
    }
    idxWriter.close();
    DirectoryReader idxReader = DirectoryReader.open(idxDir);
    BoboMultiReader boboReader = BoboMultiReader.getInstance(idxReader, facetHandlers);
    BoboBrowser browser = new BoboBrowser(boboReader);
    List<String> ranges = new ArrayList<String>();
    ranges.add("000000001");
    ranges.add("000010000");// one hour
    ranges.add("000020000");// two hours
    ranges.add("000030000");
    ranges.add("000040000");
    ranges.add("001000000");// one day
    ranges.add("002000000");// two days
    ranges.add("003000000");
    ranges.add("004000000");
    FacetHandler<?> facetHandler = new DynamicTimeRangeFacetHandler("timerange", "timeinmillis",
        now, ranges);
    browser.setFacetHandler(facetHandler);
    //
    BrowseRequest req = new BrowseRequest();
    BrowseFacet facet = null;
    FacetSpec facetSpec = new FacetSpec();
    req.setFacetSpec("timerange", facetSpec);
    BrowseResult result = browser.browse(req);
    FacetAccessible facetholder = result.getFacetAccessor("timerange");
    List<BrowseFacet> facets = facetholder.getFacets();
    facet = facets.get(0);
    assertEquals("order by value", "000000001", facet.getValue());
    assertEquals("order by value", 1, facet.getFacetValueHitCount());
    facet = facets.get(1);
    assertEquals("order by value", "000010000", facet.getValue());
    assertEquals("order by value", 1, facet.getFacetValueHitCount());
    facet = facets.get(5);
    assertEquals("order by value", "001000000", facet.getValue());
    assertEquals("order by value", 20, facet.getFacetValueHitCount());
    facet = facets.get(7);
    assertEquals("order by value", "003000000", facet.getValue());
    assertEquals("order by value", 3, facet.getFacetValueHitCount());
    //
    req = new BrowseRequest();
    facetSpec = new FacetSpec();
    facetSpec.setMinHitCount(0);
    facetSpec.setOrderBy(FacetSortSpec.OrderHitsDesc);
    req.setFacetSpec("timerange", facetSpec);
    result = browser.browse(req);
    facetholder = result.getFacetAccessor("timerange");
    facets = facetholder.getFacets();
    facet = facets.get(0);
    assertEquals("", "002000000", facet.getValue());
    assertEquals("", 25, facet.getFacetValueHitCount());
    facet = facets.get(1);
    assertEquals("", "001000000", facet.getValue());
    assertEquals("", 20, facet.getFacetValueHitCount());
    facet = facets.get(2);
    assertEquals("", "003000000", facet.getValue());
    assertEquals("", 3, facet.getFacetValueHitCount());
    facet = facets.get(8);
    assertEquals("minCount=0", "004000000", facet.getValue());
    assertEquals("minCount=0", 0, facet.getFacetValueHitCount());
    //
    req = new BrowseRequest();
    facetSpec = new FacetSpec();
    BrowseSelection sel = new BrowseSelection("timerange");
    sel.addValue("001000000");
    req.addSelection(sel);
    facetSpec.setExpandSelection(true);
    req.setFacetSpec("timerange", facetSpec);
    result = browser.browse(req);
    facetholder = result.getFacetAccessor("timerange");
    facets = facetholder.getFacets();
    facet = facets.get(0);
    assertEquals("", "000000001", facet.getValue());
    assertEquals("", 1, facet.getFacetValueHitCount());
    facet = facets.get(6);
    assertEquals("", "002000000", facet.getValue());
    assertEquals("", 25, facet.getFacetValueHitCount());
    facet = facets.get(7);
    assertEquals("", "003000000", facet.getValue());
    assertEquals("", 3, facet.getFacetValueHitCount());
    //
    req = new BrowseRequest();
    facetSpec = new FacetSpec();
    sel = new BrowseSelection("timerange");
    sel.addValue("001000000");
    sel.addValue("003000000");
    sel.addValue("004000000");
    req.addSelection(sel);
    facetSpec.setExpandSelection(false);
    req.setFacetSpec("timerange", facetSpec);
    result = browser.browse(req);
    facetholder = result.getFacetAccessor("timerange");
    facet = facetholder.getFacet("001000000");
    assertEquals("001000000", 20, facet.getFacetValueHitCount());
    facet = facetholder.getFacet("003000000");
    assertEquals("003000000", 3, facet.getFacetValueHitCount());
    facet = facetholder.getFacet("004000000");
    assertEquals("004000000", 0, facet.getFacetValueHitCount());
    assertEquals("", 23, result.getNumHits());
  }

  public void testHistogramFacetHandler() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(0);
    br.setOffset(0);

    FacetSpec output = new FacetSpec();
    output.setMaxCount(100);
    output.setMinHitCount(1);
    br.setFacetSpec("numberhisto", output);

    BrowseFacet[] answerBucketFacets = new BrowseFacet[5];
    answerBucketFacets[0] = new BrowseFacet("0000000000", 3);
    answerBucketFacets[1] = new BrowseFacet("0000000002", 1);
    answerBucketFacets[2] = new BrowseFacet("0000000009", 1);
    answerBucketFacets[3] = new BrowseFacet("0000000010", 1);
    answerBucketFacets[4] = new BrowseFacet("0000000021", 1);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("numberhisto", Arrays.asList(answerBucketFacets));

    doTest(br, 7, answer, null);

    // now with selection

    BrowseSelection sel = new BrowseSelection("color");
    sel.addValue("green");
    br.addSelection(sel);

    answerBucketFacets = new BrowseFacet[2];
    answerBucketFacets[0] = new BrowseFacet("0000000002", 1);
    answerBucketFacets[1] = new BrowseFacet("0000000021", 1);

    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("numberhisto", Arrays.asList(answerBucketFacets));

    doTest(br, 2, answer, null);
  }

  public void testBucketFacetHandlerForNumbers() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    FacetSpec output = new FacetSpec();
    output.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("sets", output);

    BrowseFacet[] answerBucketFacets = new BrowseFacet[3];
    answerBucketFacets[0] = new BrowseFacet("s1", 5);
    answerBucketFacets[1] = new BrowseFacet("s2", 4);
    answerBucketFacets[2] = new BrowseFacet("s3", 3);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("sets", Arrays.asList(answerBucketFacets));
    doTest(br, 7, answer, null);

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("sets");
    sel.addValue("s1");
    br.addSelection(sel);

    output = new FacetSpec();
    output.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("sets", output);

    answerBucketFacets = new BrowseFacet[3];
    answerBucketFacets[0] = new BrowseFacet("s1", 5);
    answerBucketFacets[1] = new BrowseFacet("s2", 3);
    answerBucketFacets[2] = new BrowseFacet("s3", 1);

    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("sets", Arrays.asList(answerBucketFacets));
    doTest(br, 4, answer, null);
  }

  public void testBucketFacetHandlerForStrings() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("groups");
    sel.addValue("g2");
    br.addSelection(sel);

    FacetSpec output = new FacetSpec();
    output.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("groups", output);

    BrowseFacet[] answerBucketFacets = new BrowseFacet[3];
    answerBucketFacets[0] = new BrowseFacet("g2", 3);
    answerBucketFacets[1] = new BrowseFacet("g1", 1);
    answerBucketFacets[2] = new BrowseFacet("g3", 1);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("groups", Arrays.asList(answerBucketFacets));
    doTest(br, 3, answer, null);

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("groups");
    sel.addValue("g2");
    sel.addValue("g1");
    sel.setSelectionOperation(ValueOperation.ValueOperationAnd);
    br.addSelection(sel);

    output = new FacetSpec();
    output.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("groups", output);

    answerBucketFacets = new BrowseFacet[2];
    answerBucketFacets[0] = new BrowseFacet("g1", 1);
    answerBucketFacets[1] = new BrowseFacet("g2", 1);

    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("groups", Arrays.asList(answerBucketFacets));
    doTest(br, 1, answer, null);

    br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    sel = new BrowseSelection("groups");
    sel.addValue("g2");
    sel.addValue("g1");
    sel.setSelectionOperation(ValueOperation.ValueOperationOr);
    br.addSelection(sel);

    output = new FacetSpec();
    output.setOrderBy(FacetSortSpec.OrderHitsDesc);
    br.setFacetSpec("groups", output);

    answerBucketFacets = new BrowseFacet[3];
    answerBucketFacets[0] = new BrowseFacet("g1", 3);
    answerBucketFacets[1] = new BrowseFacet("g2", 3);
    answerBucketFacets[2] = new BrowseFacet("g3", 1);

    answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("groups", Arrays.asList(answerBucketFacets));
    doTest(br, 5, answer, null);
  }

  public void testVirtual() throws Exception {
    BrowseRequest br = new BrowseRequest();
    br.setCount(10);
    br.setOffset(0);

    BrowseSelection sel = new BrowseSelection("virtual");
    sel.addValue("10");
    sel.addValue("11");
    br.addSelection(sel);

    FacetSpec spec = new FacetSpec();
    spec.setOrderBy(FacetSortSpec.OrderValueAsc);
    br.setFacetSpec("virtual", spec);

    HashMap<String, List<BrowseFacet>> answer = new HashMap<String, List<BrowseFacet>>();
    answer.put("virtual",
      Arrays.asList(new BrowseFacet[] { new BrowseFacet("0010", 1), new BrowseFacet("0011", 1) }));
    doTest(br, 2, answer, new String[] { "1", "2" });
  }
}
TOP

Related Classes of com.browseengine.bobo.test.BoboTestCase$TestDataDigester

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.