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,72 @@ +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.LongDocValuesField; +import org.apache.lucene.facet.FacetTestCase; +import org.apache.lucene.facet.FacetTestUtils; +import org.apache.lucene.facet.params.FacetSearchParams; +import org.apache.lucene.facet.search.FacetResult; +import org.apache.lucene.facet.search.FacetsCollector; +import org.apache.lucene.facet.taxonomy.CategoryPath; +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.store.Directory; + +public class TestRangeAccumulator extends FacetTestCase { + + public void testBasic() throws Exception { + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), d); + Document doc = new Document(); + LongDocValuesField field = new LongDocValuesField("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", + Range.newLongRange("less than 10", 0L, true, 10L, false), + Range.newLongRange("over 90", 90L, false, 100L, false), + Range.newLongRange("over 1000", 1000L, false, null, false))); + + RangeAccumulator a = new RangeAccumulator(fsp, r); + + FacetsCollector fc = FacetsCollector.create(a); + + IndexSearcher s = newSearcher(r); + s.search(new MatchAllDocsQuery(), fc); + List result = a.accumulate(fc.getMatchingDocs()); + assertEquals(1, result.size()); + assertEquals("field (0)\n less than 10 (10)\n over 90 (9)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(result.get(0))); + + r.close(); + d.close(); + } +} + Property changes on: lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.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,60 @@ +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.complements.ComplementCountingAggregator; +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; + +// nocommit rename to NDV? + +/** + * 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, Range...ranges) { + super(new CategoryPath(field), 1); + this.ranges = ranges; + } + + @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/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,62 @@ +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. + */ + +// nocommit move all this to new package? oal.facet.dynamic? + +public final class Range { + public final String label; + public final long minIncl; + public final long maxIncl; + + private Range(String label, long minIncl, long maxIncl) { + this.label = label; + this.minIncl = minIncl; + this.maxIncl = maxIncl; + } + + public boolean accept(long value) { + return value >= minIncl && value <= maxIncl; + } + + // nocommit .newIntRange/etc., use generics + + public static Range newLongRange(String label, Long min, boolean minInclusive, Long max, boolean maxInclusive) { + long minLong; + if (min == null) { + minLong = Long.MIN_VALUE; + } else { + minLong = min.longValue(); + if (!minInclusive) { + minLong++; + } + } + + long maxLong; + if (max == null) { + maxLong = Long.MAX_VALUE; + } else { + maxLong = max.longValue(); + if (!maxInclusive) { + maxLong--; + } + } + + return new Range(label, minLong, maxLong); + } +} 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,119 @@ +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.Collections; +import java.util.List; + +import org.apache.lucene.facet.params.CategoryListParams; +import org.apache.lucene.facet.params.FacetSearchParams; +import org.apache.lucene.facet.search.Aggregator; +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; + +/** 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 { + final Range[] ranges; + final String field; + + // nocommit sugar to take Range... directly? + + public RangeAccumulator(FacetSearchParams fsp, IndexReader reader) { + super(fsp, reader, null, null); + + // ncoommit allow more than one request? against more + // than one field? + if (fsp.facetRequests.size() != 1) { + throw new IllegalArgumentException("only single facet request allowed (got " + fsp.facetRequests.size() + ")"); + } + + if (!(fsp.facetRequests.get(0) instanceof RangeFacetRequest)) { + throw new IllegalArgumentException("only RangeFacetRequest is supported; got " + fsp.facetRequests.get(0).getClass()); + } + + RangeFacetRequest fr = (RangeFacetRequest) fsp.facetRequests.get(0); + + this.field = fr.categoryPath.components[0]; + + ranges = fr.ranges; + } + + @Override + public FacetsAggregator getAggregator() { + throw new UnsupportedOperationException(); + } + + @Override + public List accumulate(List matchingDocs) throws IOException { + + int[] counts = new int[ranges.length]; + + for(MatchingDocs hits : matchingDocs) { + NumericDocValues ndv = hits.context.reader().getNumericDocValues(field); + if (ndv == null) { + continue; + } + + final int length = hits.bits.length(); + int doc = 0; + while (doc < length && (doc = hits.bits.nextSetBit(doc)) != -1) { + long v = ndv.get(doc); + + // nocommit use interval tree: + for(int i=0;i nodes = new ArrayList(); + for(int i=0;i