Index: lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java (revision 0) +++ lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java (working copy) @@ -0,0 +1,412 @@ +package org.apache.lucene.facet.range; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.List; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.DoubleDocValuesField; +import org.apache.lucene.document.DoubleField; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.FloatDocValuesField; +import org.apache.lucene.document.FloatField; +import org.apache.lucene.document.LongDocValuesField; +import org.apache.lucene.document.LongField; +import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.facet.FacetTestCase; +import org.apache.lucene.facet.FacetTestUtils; +import org.apache.lucene.facet.params.FacetIndexingParams; +import org.apache.lucene.facet.params.FacetSearchParams; +import org.apache.lucene.facet.search.DrillDownQuery; +import org.apache.lucene.facet.search.FacetResult; +import org.apache.lucene.facet.search.FacetResultNode; +import org.apache.lucene.facet.search.FacetsCollector; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.NumericRangeQuery; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util._TestUtil; + +public class TestRangeAccumulator extends FacetTestCase { + + public void testBasicLong() throws Exception { + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), d); + Document doc = new Document(); + NumericDocValuesField field = new NumericDocValuesField("field", 0L); + doc.add(field); + for(long l=0;l<100;l++) { + field.setLongValue(l); + w.addDocument(doc); + } + + IndexReader r = w.getReader(); + w.close(); + + FacetSearchParams fsp = new FacetSearchParams( + new RangeFacetRequest("field", + new LongRange("less than 10", 0L, true, 10L, false), + new LongRange("less than or equal to 10", 0L, true, 10L, true), + new LongRange("over 90", 90L, false, 100L, false), + new LongRange("90 or above", 90L, true, 100L, false), + new LongRange("over 1000", 1000L, false, Long.MAX_VALUE, false))); + + RangeAccumulator a = new RangeAccumulator(fsp, r); + + FacetsCollector fc = FacetsCollector.create(a); + + IndexSearcher s = newSearcher(r); + s.search(new MatchAllDocsQuery(), fc); + List result = fc.getFacetResults(); + assertEquals(1, result.size()); + assertEquals("field (0)\n less than 10 (10)\n less than or equal to 10 (11)\n over 90 (9)\n 90 or above (10)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(result.get(0))); + + r.close(); + d.close(); + } + + public void testBasicDouble() throws Exception { + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), d); + Document doc = new Document(); + DoubleDocValuesField field = new DoubleDocValuesField("field", 0.0); + doc.add(field); + for(long l=0;l<100;l++) { + field.setDoubleValue((double) l); + w.addDocument(doc); + } + + IndexReader r = w.getReader(); + w.close(); + + FacetSearchParams fsp = new FacetSearchParams( + new RangeFacetRequest("field", + new DoubleRange("less than 10", 0.0, true, 10.0, false), + new DoubleRange("less than or equal to 10", 0.0, true, 10.0, true), + new DoubleRange("over 90", 90.0, false, 100.0, false), + new DoubleRange("90 or above", 90.0, true, 100.0, false), + new DoubleRange("over 1000", 1000.0, false, Double.POSITIVE_INFINITY, false))); + + RangeAccumulator a = new RangeAccumulator(fsp, r); + + FacetsCollector fc = FacetsCollector.create(a); + + IndexSearcher s = newSearcher(r); + s.search(new MatchAllDocsQuery(), fc); + List result = fc.getFacetResults(); + assertEquals(1, result.size()); + assertEquals("field (0)\n less than 10 (10)\n less than or equal to 10 (11)\n over 90 (9)\n 90 or above (10)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(result.get(0))); + + r.close(); + d.close(); + } + + public void testBasicFloat() throws Exception { + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), d); + Document doc = new Document(); + FloatDocValuesField field = new FloatDocValuesField("field", 0.0f); + doc.add(field); + for(long l=0;l<100;l++) { + field.setFloatValue((float) l); + w.addDocument(doc); + } + + IndexReader r = w.getReader(); + w.close(); + + FacetSearchParams fsp = new FacetSearchParams( + new RangeFacetRequest("field", + new FloatRange("less than 10", 0.0f, true, 10.0f, false), + new FloatRange("less than or equal to 10", 0.0f, true, 10.0f, true), + new FloatRange("over 90", 90.0f, false, 100.0f, false), + new FloatRange("90 or above", 90.0f, true, 100.0f, false), + new FloatRange("over 1000", 1000.0f, false, Float.POSITIVE_INFINITY, false))); + + RangeAccumulator a = new RangeAccumulator(fsp, r); + + FacetsCollector fc = FacetsCollector.create(a); + + IndexSearcher s = newSearcher(r); + s.search(new MatchAllDocsQuery(), fc); + List result = fc.getFacetResults(); + assertEquals(1, result.size()); + assertEquals("field (0)\n less than 10 (10)\n less than or equal to 10 (11)\n over 90 (9)\n 90 or above (10)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(result.get(0))); + + r.close(); + d.close(); + } + + public void testRandomLongs() throws Exception { + Directory dir = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir); + + int numDocs = atLeast(1000); + long[] values = new long[numDocs]; + for(int i=0;i= min; + } else { + accept &= values[i] > min; + } + if (maxIncl) { + accept &= values[i] <= max; + } else { + accept &= values[i] < max; + } + if (accept) { + expectedCounts[rangeID]++; + } + } + } + + FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest("field", ranges)); + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r)); + s.search(new MatchAllDocsQuery(), fc); + List results = fc.getFacetResults(); + assertEquals(1, results.size()); + List nodes = results.get(0).getFacetResultNode().subResults; + assertEquals(numRange, nodes.size()); + for(int rangeID=0;rangeID= min; + } else { + accept &= values[i] > min; + } + if (maxIncl) { + accept &= values[i] <= max; + } else { + accept &= values[i] < max; + } + if (accept) { + expectedCounts[rangeID]++; + } + } + } + + FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest("field", ranges)); + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r)); + s.search(new MatchAllDocsQuery(), fc); + List results = fc.getFacetResults(); + assertEquals(1, results.size()); + List nodes = results.get(0).getFacetResultNode().subResults; + assertEquals(numRange, nodes.size()); + for(int rangeID=0;rangeID= min; + } else { + accept &= values[i] > min; + } + if (maxIncl) { + accept &= values[i] <= max; + } else { + accept &= values[i] < max; + } + if (accept) { + expectedCounts[rangeID]++; + } + } + } + + FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest("field", ranges)); + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r)); + s.search(new MatchAllDocsQuery(), fc); + List results = fc.getFacetResults(); + assertEquals(1, results.size()); + List nodes = results.get(0).getFacetResultNode().subResults; + assertEquals(numRange, nodes.size()); + for(int rangeID=0;rangeID= minIncl && value <= maxIncl; + } +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/range/Range.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/Range.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/range/Range.java (working copy) @@ -0,0 +1,33 @@ +package org.apache.lucene.facet.range; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Represents a single labelled range, one facet label in + * the facets computed by {@link RangeAccumulator}. + * + * @lucene.experimental */ + +public abstract class Range { + public final String label; + + protected Range(String label) { + this.label = label; + } + + public abstract boolean accept(long value); +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/range/Range.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java (working copy) @@ -0,0 +1,130 @@ +package org.apache.lucene.facet.range; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.lucene.facet.params.FacetSearchParams; +import org.apache.lucene.facet.search.FacetRequest; +import org.apache.lucene.facet.search.FacetResult; +import org.apache.lucene.facet.search.FacetResultNode; +import org.apache.lucene.facet.search.FacetsAccumulator; +import org.apache.lucene.facet.search.FacetsAggregator; +import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.NumericDocValues; + +// nocommit add to example + +/** Uses a {@link NumericDocValues} and accumulates + * counts for provided ranges. This is dynamic (does not + * use the taxonomy index or anything from the index + * except the NumericDocValuesField). */ + +public class RangeAccumulator extends FacetsAccumulator { + + static class RangeSet { + final Range[] ranges; + final String field; + //final int[] counts; + //NumericDocValues ndv; + + public RangeSet(Range[] ranges, String field) { + this.ranges = ranges; + this.field = field; + //this.counts = new int[ranges.length]; + } + } + + final List requests = new ArrayList(); + + public RangeAccumulator(FacetSearchParams fsp, IndexReader reader) { + super(fsp, reader, null, null); + + for(FacetRequest fr : fsp.facetRequests) { + + if (!(fr instanceof RangeFacetRequest)) { + throw new IllegalArgumentException("only RangeFacetRequest is supported; got " + fsp.facetRequests.get(0).getClass()); + } + + if (fr.categoryPath.length != 1) { + throw new IllegalArgumentException("only flat (dimension only) CategoryPath is allowed"); + } + + RangeFacetRequest rfr = (RangeFacetRequest) fr; + + requests.add(new RangeSet(rfr.ranges, rfr.categoryPath.components[0])); + } + } + + @Override + public FacetsAggregator getAggregator() { + throw new UnsupportedOperationException(); + } + + @Override + public List accumulate(List matchingDocs) throws IOException { + + // TODO: test if this is faster (in the past it was + // faster to do MachingDocs on the inside) ... see + // patches on LUCENE-4965): + List results = new ArrayList(); + for(int i=0;i nodes = new ArrayList(ranges.ranges.length); + for(int j=0;j= minIncl && floatValue <= maxIncl; + } +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/range/FloatRange.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetResultNode.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetResultNode.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetResultNode.java (working copy) @@ -0,0 +1,31 @@ +package org.apache.lucene.facet.range; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.facet.search.FacetResultNode; +import org.apache.lucene.facet.taxonomy.CategoryPath; + +public class RangeFacetResultNode extends FacetResultNode { + public final Range range; + + public RangeFacetResultNode(String field, Range range, int count) { + super(-1, count); + this.range = range; + this.label = new CategoryPath(field, range.label); + } +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetResultNode.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java (working copy) @@ -0,0 +1,71 @@ +package org.apache.lucene.facet.range; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.apache.lucene.document.DoubleDocValuesField; // javadocs + +/** Represents a range over double values indexed as {@link + * DoubleDocValuesField}. */ +public final class DoubleRange extends Range { + private final double minIncl; + private final double maxIncl; + + public final double min; + public final double max; + public final boolean minInclusive; + public final boolean maxInclusive; + + /** Create a DoubleRange. */ + public DoubleRange(String label, double min, boolean minInclusive, double max, boolean maxInclusive) { + super(label); + this.min = min; + this.max = max; + this.minInclusive = minInclusive; + this.maxInclusive = maxInclusive; + + // TODO: if DoubleDocValuesField used + // NumericUtils.doubleToSortableLong format (instead of + // Double.doubleToRawLongBits) we could do comparisons + // in long space + + if (Double.isNaN(min)) { + throw new IllegalArgumentException("min cannot be NaN"); + } + if (!minInclusive) { + min = Math.nextUp(min); + } + + if (Double.isNaN(max)) { + throw new IllegalArgumentException("max cannot be NaN"); + } + if (!maxInclusive) { + // Why no Math.nextDown? + max = Math.nextAfter(max, Double.NEGATIVE_INFINITY); + } + + this.minIncl = min; + this.maxIncl = max; + } + + @Override + public boolean accept(long value) { + double doubleValue = Double.longBitsToDouble(value); + return doubleValue >= minIncl && doubleValue <= maxIncl; + } +} + Property changes on: lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java (working copy) @@ -0,0 +1,64 @@ +package org.apache.lucene.facet.range; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.List; + +import org.apache.lucene.facet.search.Aggregator; +import org.apache.lucene.facet.search.FacetArrays; +import org.apache.lucene.facet.search.FacetRequest; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; + +/** + * Facet request for dynamic ranges based on a + * NumericDocValues field. This does not use the taxonomy + * index nor any indexed facet values. + * + * @lucene.experimental + */ +public class RangeFacetRequest extends FacetRequest { + + public final Range[] ranges; + + public RangeFacetRequest(String field, T...ranges) { + super(new CategoryPath(field), 1); + this.ranges = ranges; + } + + @SuppressWarnings("unchecked") + public RangeFacetRequest(String field, List ranges) { + this(field, (T[]) ranges.toArray(new Range[ranges.size()])); + } + + @Override + public Aggregator createAggregator(boolean useComplements, FacetArrays arrays, TaxonomyReader taxonomy) { + throw new UnsupportedOperationException(); + } + + @Override + public double getValueOf(FacetArrays arrays, int ordinal) { + throw new UnsupportedOperationException(); + } + + @Override + public FacetArraysSource getFacetArraysSource() { + throw new UnsupportedOperationException(); + } + +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/demo/src/test/org/apache/lucene/demo/facet/TestRangeFacetsExample.java =================================================================== --- lucene/demo/src/test/org/apache/lucene/demo/facet/TestRangeFacetsExample.java (revision 0) +++ lucene/demo/src/test/org/apache/lucene/demo/facet/TestRangeFacetsExample.java (working copy) @@ -0,0 +1,70 @@ +package org.apache.lucene.demo.facet; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.List; + +import org.apache.lucene.facet.collections.ObjectToIntMap; +import org.apache.lucene.facet.range.LongRange; +import org.apache.lucene.facet.range.RangeFacetRequest; +import org.apache.lucene.facet.search.FacetResult; +import org.apache.lucene.facet.search.FacetResultNode; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.util.LuceneTestCase.SuppressCodecs; +import org.apache.lucene.util.LuceneTestCase; +import org.junit.Test; + +@SuppressCodecs("Lucene3x") +public class TestRangeFacetsExample extends LuceneTestCase { + + private static final ObjectToIntMap expectedCounts = new ObjectToIntMap(); + static { + expectedCounts.put(new CategoryPath("timestamp", "Past hour"), 4); + expectedCounts.put(new CategoryPath("timestamp", "Past six hours"), 22); + expectedCounts.put(new CategoryPath("timestamp", "Past day"), 87); + } + + private void assertExpectedCounts(FacetResult res, ObjectToIntMap expCounts) { + FacetResultNode root = res.getFacetResultNode(); + for (FacetResultNode node : root.subResults) { + assertEquals("incorrect count for " + node.label, expCounts.get(node.label), (int) node.value); + } + } + + @Test + public void testSimple() throws Exception { + RangeFacetsExample example = new RangeFacetsExample(); + example.index(); + List facetResults = example.search(); + assertEquals(1, facetResults.size()); + assertExpectedCounts(facetResults.get(0), expectedCounts); + example.close(); + } + + @Test + @SuppressWarnings("unchecked") + public void testDrillDown() throws Exception { + RangeFacetsExample example = new RangeFacetsExample(); + example.index(); + List facetResults = example.search(); + TopDocs hits = example.drillDown((LongRange) ((RangeFacetRequest) facetResults.get(0).getFacetRequest()).ranges[1]); + assertEquals(22, hits.totalHits); + example.close(); + } +} Property changes on: lucene/demo/src/test/org/apache/lucene/demo/facet/TestRangeFacetsExample.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleFacetsExample.java =================================================================== --- lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleFacetsExample.java (revision 1477345) +++ lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleFacetsExample.java (working copy) @@ -119,7 +119,10 @@ // Now user drills down on Publish Date/2010: FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("Author"), 10)); - DrillDownQuery q = new DrillDownQuery(fsp.indexingParams, new MatchAllDocsQuery()); + + // Passing no baseQuery means we drill down on all + // documents ("browse only"): + DrillDownQuery q = new DrillDownQuery(fsp.indexingParams); q.add(new CategoryPath("Publish Date/2010", '/')); FacetsCollector fc = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader); searcher.search(q, fc); Index: lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java =================================================================== --- lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java (revision 0) +++ lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java (working copy) @@ -0,0 +1,142 @@ +package org.apache.lucene.demo.facet; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.Closeable; +import java.io.IOException; +import java.util.Date; +import java.util.List; + +import org.apache.lucene.analysis.core.WhitespaceAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.LongDocValuesField; +import org.apache.lucene.document.LongField; +import org.apache.lucene.facet.params.FacetIndexingParams; +import org.apache.lucene.facet.params.FacetSearchParams; +import org.apache.lucene.facet.range.LongRange; +import org.apache.lucene.facet.range.RangeAccumulator; +import org.apache.lucene.facet.range.RangeFacetRequest; +import org.apache.lucene.facet.search.DrillDownQuery; +import org.apache.lucene.facet.search.FacetResult; +import org.apache.lucene.facet.search.FacetsCollector; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.NumericRangeQuery; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.RAMDirectory; + + +/** Shows simple usage of dynamic range faceting. */ +public class RangeFacetsExample implements Closeable { + + private final Directory indexDir = new RAMDirectory(); + private IndexSearcher searcher; + private final long nowSec = new Date().getTime()/1000; + + /** Empty constructor */ + public RangeFacetsExample() {} + + /** Build the example index. */ + public void index() throws IOException { + IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(FacetExamples.EXAMPLES_VER, + new WhitespaceAnalyzer(FacetExamples.EXAMPLES_VER))); + + // Add documents with a fake timestamp, 1000 sec before + // "now", 2000 sec before "now", ...: + for(int i=0;i<1000;i++) { + Document doc = new Document(); + long then = nowSec - i * 1000; + // Add as doc values field, so we can compute range facets: + doc.add(new LongDocValuesField("timestamp", then)); + // Add as numeric field so we can drill-down: + doc.add(new LongField("timestamp", then, Field.Store.NO)); + indexWriter.addDocument(doc); + } + + // Open near-real-time searcher + searcher = new IndexSearcher(DirectoryReader.open(indexWriter, true)); + indexWriter.close(); + } + + /** User runs a query and counts facets. */ + public List search() throws IOException { + + FacetSearchParams fsp = new FacetSearchParams( + new RangeFacetRequest("timestamp", + new LongRange("Past hour", nowSec-3600, true, nowSec, true), + new LongRange("Past six hours", nowSec-6*3600, true, nowSec, true), + new LongRange("Past day", nowSec-24*3600, true, nowSec, true))); + // Aggregatses the facet counts + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, searcher.getIndexReader())); + + // MatchAllDocsQuery is for "browsing" (counts facets + // for all non-deleted docs in the index); normally + // you'd use a "normal" query, and use MultiCollector to + // wrap collecting the "normal" hits and also facets: + searcher.search(new MatchAllDocsQuery(), fc); + + // Retrieve results + return fc.getFacetResults(); + } + + /** User drills down on the specified range. */ + public TopDocs drillDown(LongRange range) throws IOException { + + // Passing no baseQuery means we drill down on all + // documents ("browse only"): + DrillDownQuery q = new DrillDownQuery(FacetIndexingParams.DEFAULT); + + // Use FieldCacheRangeFilter; this will use + // NumericDocValues: + q.add("timestamp", NumericRangeQuery.newLongRange("timestamp", range.min, range.max, range.minInclusive, range.maxInclusive)); + + return searcher.search(q, 10); + } + + public void close() throws IOException { + searcher.getIndexReader().close(); + indexDir.close(); + } + + /** Runs the search and drill-down examples and prints the results. */ + @SuppressWarnings("unchecked") + public static void main(String[] args) throws Exception { + RangeFacetsExample example = new RangeFacetsExample(); + example.index(); + + System.out.println("Facet counting example:"); + System.out.println("-----------------------"); + List results = example.search(); + for (FacetResult res : results) { + System.out.println(res); + } + + System.out.println("\n"); + System.out.println("Facet drill-down example (timestamp/Past six hours):"); + System.out.println("---------------------------------------------"); + TopDocs hits = example.drillDown((LongRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[1]); + System.out.println(hits.totalHits + " totalHits"); + + example.close(); + } +} Property changes on: lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property