com.browseengine.bobo.test.BoboTestCase.java Source code

Java tutorial

Introduction

Here is the source code for com.browseengine.bobo.test.BoboTestCase.java

Source

/**
 * 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.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;

import junit.framework.TestCase;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Payload;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Explanation;
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.Version;

import com.browseengine.bobo.api.BoboBrowser;
import com.browseengine.bobo.api.BoboCustomSortField;
import com.browseengine.bobo.api.BoboIndexReader;
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.BrowseRequest;
import com.browseengine.bobo.api.BrowseResult;
import com.browseengine.bobo.api.BrowseSelection;
import com.browseengine.bobo.api.ComparatorFactory;
import com.browseengine.bobo.api.FacetAccessible;
import com.browseengine.bobo.api.FacetSpec;
import com.browseengine.bobo.api.FieldValueAccessor;
import com.browseengine.bobo.api.MultiBoboBrowser;
import com.browseengine.bobo.api.BrowseSelection.ValueOperation;
import com.browseengine.bobo.api.FacetSpec.FacetSortSpec;
import com.browseengine.bobo.client.GeoExample;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.FacetHandler.TermCountSize;
import com.browseengine.bobo.facets.data.PredefinedTermListFactory;
import com.browseengine.bobo.facets.data.TermListFactory;
import com.browseengine.bobo.facets.impl.CompactMultiValueFacetHandler;
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.MultiValueFacetHandler;
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.index.BoboIndexer;
import com.browseengine.bobo.index.digest.DataDigester;
import com.browseengine.bobo.query.scoring.FacetTermQuery;
import com.browseengine.bobo.sort.DocComparator;
import com.browseengine.bobo.sort.DocComparatorSource;

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

    private static class TestDataDigester extends DataDigester {
        private List<FacetHandler<?>> _fconf;
        private Document[] _data;

        TestDataDigester(List<FacetHandler<?>> fConf, Document[] data) {
            super();
            _fconf = fConf;
            _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);
        _fconf = buildFieldConf();
        _indexDir = createIndex();
    }

    private BoboIndexReader newIndexReader() throws IOException {
        return newIndexReader(true);
    }

    private BoboIndexReader newIndexReader(boolean readonly) throws IOException {
        IndexReader srcReader = IndexReader.open(_indexDir, readonly);
        try {
            BoboIndexReader reader = BoboIndexReader.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 Field(name, val, Field.Store.NO, Index.NOT_ANALYZED_NO_NORMS);
        f.setOmitTermFreqAndPositions(true);
        return f;
    }

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

        private PayloadAttribute payloadAttr;
        private TermAttribute 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 = (PayloadAttribute) addAttribute(PayloadAttribute.class);
            payloadAttr.setPayload(new Payload(buffer));
            termAttr = (TermAttribute) addAttribute(TermAttribute.class);
            termAttr.setTermBuffer(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 Field(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("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"));

        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("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"));

        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("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"));

        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"));

        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"));

        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"));

        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"));

        Document d8 = new Document();
        d8.add(buildMetaField("latitude", "35"));
        d8.add(buildMetaField("longitude", "120"));

        dataList.add(d1);
        dataList.add(d2);
        dataList.add(d3);
        dataList.add(d4);
        dataList.add(d5);
        dataList.add(d6);
        dataList.add(d7);
        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();
            IndexReader r = IndexReader.open(idxDir, false);
            r.deleteDocument(r.maxDoc() - 1);
            //r.flush();
            r.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return idxDir;

    }

    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");
        colorHandler.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 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 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 GeoExample("distance", "latitude", "longitude"));

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

        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()) {
                                if (!iter1.next().equals(iter2.next())) {
                                    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;
    }

    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(choiceMap).append('\n');
            buffer.append(Arrays.toString(ids)).append('\n');
            buffer.append("gotten: \n");
            buffer.append(result.getNumHits()).append(" hits\n");

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

            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');

            BrowseHit[] hits = result.getHits();
            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 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 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("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, 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("distance");
        sel2.addValue("<60,120,1>");
        HashMap map = new HashMap<String, Float>();
        map.put("<60,120,1>", 3.0f);
        FacetTermQuery geoQ = new FacetTermQuery(sel2, map);

        System.out.println("Testing GeoFacet Filters");
        BoboBrowser b = newBrowser();
        Explanation expl = b.explain(geoQ, 0);
        System.out.println(expl);

        br2.setQuery(geoQ);
        doTest(br2, 2, null, new String[] { "1", "5" });
        expl = b.explain(geoQ, 1);
        System.out.println(expl);

        // 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 map3 = new HashMap<String, Float>();
        map3.put("red", 3.0f);
        FacetTermQuery colorQ = new FacetTermQuery(sel3, map3);

        BoboBrowser b2 = newBrowser();
        Explanation expl2 = b.explain(colorQ, 0);
        System.out.println(expl2);

        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);

    }

    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() {

            public Comparator<Integer> newComparator(FieldValueAccessor fieldValueAccessor, final int[] counts) {
                return new Comparator<Integer>() {

                    public int compare(Integer f1, Integer f2) {
                        int val = counts[f2] - counts[f1];
                        if (val == 0) {
                            val = f2 - f1;
                        }
                        return val;
                    }

                };
            }

            public Comparator<BrowseFacet> newComparator() {
                return new Comparator<BrowseFacet>() {
                    public int compare(BrowseFacet f1, BrowseFacet f2) {
                        int val = f2.getHitCount() - f1.getHitCount();
                        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.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.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.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.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" });
    }

    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;
        try {
            if (boboBrowser == null) {
                boboBrowser = newBrowser();
            }
            result = boboBrowser.browse(req);
            doTest(result, req, numHits, choiceMap, ids);
            return result;
        } catch (BrowseException e) {
            e.printStackTrace();
            fail(e.getMessage());
        } catch (IOException ioe) {
            fail(ioe.getMessage());
        } finally {
            if (boboBrowser != null) {
                try {
                    boboBrowser.close();
                } catch (IOException e) {
                    fail(e.getMessage());
                }
            }
        }
        return null;
    }

    public void testLuceneSort() throws IOException {

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

            BoboIndexReader reader = BoboIndexReader.getInstance(srcReader, facetHandlers); // not facet handlers to help
            BoboBrowser browser = new BoboBrowser(reader);

            BrowseRequest browseRequest = new BrowseRequest();
            browseRequest.setCount(10);
            browseRequest.setOffset(0);
            browseRequest.addSortField(new SortField("date", SortField.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.CUSTOM, false));

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

    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.CUSTOM, false));

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

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

        br.setSort(new SortField[] { new SortField("number", SortField.CUSTOM, true) });
        doTest(br, 7, null, new String[] { "6", "5", "4", "3", "2", "1", "7" });
        br.setSort(new SortField[] { new SortField("name", SortField.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.CUSTOM, true) });
        doTest(br, 3, null, new String[] { "2", "1", "7" });
        br.setSort(new SortField[] { new SortField("name", SortField.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.CUSTOM, true) });
        doTest(br, 3, null, new String[] { "5", "1", "7" });
        br.setSort(new SortField[] { new SortField("name", SortField.STRING, false) });
        doTest(br, 3, null, new String[] { "7", "1", "5" });
    }

    public void testCustomSort() {

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

            final class CustomSortDocComparator extends DocComparator {

                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;
                }

                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.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.getHitCount(), 1);
    }

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

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

    }

    public void testBrowseWithQuery() {
        try {
            BrowseRequest br = new BrowseRequest();
            QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "shape",
                    new StandardAnalyzer(Version.LUCENE_CURRENT));
            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.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.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 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.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() {
        BoboIndexReader reader = null;

        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" });

        try {
            reader = newIndexReader(false);
            reader.deleteDocuments(new Term("id", "1"));
            reader.deleteDocuments(new Term("id", "2"));

            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, null);
        } 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, null);
    }

    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) }));
        BrowseResult result = doTest(br, 7, answer, 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) }));
        BrowseResult result = 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.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.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.CUSTOM, true) });
        mergedResult = multiBoboBrowser.browse(browseRequest);
        doTest(mergedResult, browseRequest, 4, answer, new String[] { "7", "7", "1", "1" });
    }

    public void testFacetQuery() throws Exception {
        BrowseSelection sel = new BrowseSelection("color");
        sel.addValue("red");
        sel.addValue("blue");
        HashMap 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 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);
        System.out.println(expl);

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

    }

    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() {

            public Comparator<Integer> newComparator(final FieldValueAccessor fieldValueAccessor,
                    final int[] counts) {

                return new Comparator<Integer>() {

                    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[v1] - counts[v2];
                        }
                        return val;
                    }

                };
            }

            public Comparator<BrowseFacet> newComparator() {
                return new Comparator<BrowseFacet>() {
                    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.getHitCount() - o1.getHitCount();
                        }
                        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);

    }

    public static void main(String[] args) throws Exception {
        BoboTestCase test = new BoboTestCase("testSimpleGroupbyFacetHandler");
        test.setUp();
        test.testSimpleGroupbyFacetHandler();
        test.tearDown();
    }
}