You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2014/11/25 19:52:12 UTC
[11/16] lucenenet git commit: Move facets into src folder
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Range/LongRange.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Range/LongRange.cs b/Lucene.Net.Facet/Range/LongRange.cs
deleted file mode 100644
index 52da204..0000000
--- a/Lucene.Net.Facet/Range/LongRange.cs
+++ /dev/null
@@ -1,239 +0,0 @@
-using System.Collections.Generic;
-using Lucene.Net.Support;
-
-namespace Lucene.Net.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.
- */
-
-
- using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
- using FunctionValues = Lucene.Net.Queries.Function.FunctionValues;
- using ValueSource = Lucene.Net.Queries.Function.ValueSource;
- using DocIdSet = Lucene.Net.Search.DocIdSet;
- using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
- using Filter = Lucene.Net.Search.Filter;
- using Bits = Lucene.Net.Util.Bits;
-
- /// <summary>
- /// Represents a range over long values.
- ///
- /// @lucene.experimental
- /// </summary>
- public sealed class LongRange : Range
- {
- internal readonly long minIncl;
- internal readonly long maxIncl;
-
- /// <summary>
- /// Minimum. </summary>
- public readonly long min;
-
- /// <summary>
- /// Maximum. </summary>
- public readonly long max;
-
- /// <summary>
- /// True if the minimum value is inclusive. </summary>
- public readonly bool minInclusive;
-
- /// <summary>
- /// True if the maximum value is inclusive. </summary>
- public readonly bool maxInclusive;
-
- // TODO: can we require fewer args? (same for
- // Double/FloatRange too)
-
- /// <summary>
- /// Create a LongRange. </summary>
- public LongRange(string label, long minIn, bool minInclusive, long maxIn, bool maxInclusive)
- : base(label)
- {
- this.min = minIn;
- this.max = maxIn;
- this.minInclusive = minInclusive;
- this.maxInclusive = maxInclusive;
-
- if (!minInclusive)
- {
- if (minIn != long.MaxValue)
- {
- minIn++;
- }
- else
- {
- FailNoMatch();
- }
- }
-
- if (!maxInclusive)
- {
- if (maxIn != long.MinValue)
- {
- maxIn--;
- }
- else
- {
- FailNoMatch();
- }
- }
-
- if (minIn > maxIn)
- {
- FailNoMatch();
- }
-
- this.minIncl = minIn;
- this.maxIncl = maxIn;
- }
-
- /// <summary>
- /// True if this range accepts the provided value. </summary>
- public bool accept(long value)
- {
- return value >= minIncl && value <= maxIncl;
- }
-
- public override string ToString()
- {
- return "LongRange(" + minIncl + " to " + maxIncl + ")";
- }
-
- public override Filter GetFilter(Filter fastMatchFilter, ValueSource valueSource)
- {
- return new FilterAnonymousInnerClassHelper(this, fastMatchFilter, valueSource);
- }
-
- private class FilterAnonymousInnerClassHelper : Filter
- {
- private readonly LongRange outerInstance;
-
- private Filter fastMatchFilter;
- private ValueSource valueSource;
-
- public FilterAnonymousInnerClassHelper(LongRange outerInstance, Filter fastMatchFilter, ValueSource valueSource)
- {
- this.outerInstance = outerInstance;
- this.fastMatchFilter = fastMatchFilter;
- this.valueSource = valueSource;
- }
-
-
- public override string ToString()
- {
- return "Filter(" + outerInstance.ToString() + ")";
- }
-
- public override DocIdSet GetDocIdSet(AtomicReaderContext context, Bits acceptDocs)
- {
-
- // TODO: this is just like ValueSourceScorer,
- // ValueSourceFilter (spatial),
- // ValueSourceRangeFilter (solr); also,
- // https://issues.apache.org/jira/browse/LUCENE-4251
-
- FunctionValues values = valueSource.GetValues(new Dictionary<string,object>(), context);
-
- int maxDoc = context.Reader.MaxDoc;
-
- Bits fastMatchBits;
- if (fastMatchFilter != null)
- {
- DocIdSet dis = fastMatchFilter.GetDocIdSet(context, null);
- if (dis == null)
- {
- // No documents match
- return null;
- }
- fastMatchBits = dis.GetBits();
- if (fastMatchBits == null)
- {
- throw new System.ArgumentException("fastMatchFilter does not implement DocIdSet.bits");
- }
- }
- else
- {
- fastMatchBits = null;
- }
-
- return new DocIdSetAnonymousInnerClassHelper(this, acceptDocs, values, maxDoc, fastMatchBits);
- }
-
- private class DocIdSetAnonymousInnerClassHelper : DocIdSet
- {
- private readonly FilterAnonymousInnerClassHelper outerInstance;
-
- private Bits acceptDocs;
- private FunctionValues values;
- private int maxDoc;
- private Bits fastMatchBits;
-
- public DocIdSetAnonymousInnerClassHelper(FilterAnonymousInnerClassHelper outerInstance, Bits acceptDocs, FunctionValues values, int maxDoc, Bits fastMatchBits)
- {
- this.outerInstance = outerInstance;
- this.acceptDocs = acceptDocs;
- this.values = values;
- this.maxDoc = maxDoc;
- this.fastMatchBits = fastMatchBits;
- }
-
-
- public override Bits GetBits()
- {
- return new BitsAnonymousInnerClassHelper(this);
- }
-
- private class BitsAnonymousInnerClassHelper : Bits
- {
- private readonly DocIdSetAnonymousInnerClassHelper outerInstance;
-
- public BitsAnonymousInnerClassHelper(DocIdSetAnonymousInnerClassHelper outerInstance)
- {
- this.outerInstance = outerInstance;
- }
-
- public virtual bool Get(int docID)
- {
- if (outerInstance.acceptDocs != null && outerInstance.acceptDocs.Get(docID) == false)
- {
- return false;
- }
- if (outerInstance.fastMatchBits != null && outerInstance.fastMatchBits.Get(docID) == false)
- {
- return false;
- }
- return outerInstance.outerInstance.outerInstance.accept(outerInstance.values.LongVal(docID));
- }
-
-
- public virtual int Length()
- {
- return outerInstance.maxDoc;
- }
- }
-
- public override DocIdSetIterator GetIterator()
- {
- throw new System.NotSupportedException("this filter can only be accessed via bits()");
- }
-
- }
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Range/LongRangeCounter.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Range/LongRangeCounter.cs b/Lucene.Net.Facet/Range/LongRangeCounter.cs
deleted file mode 100644
index 98123c2..0000000
--- a/Lucene.Net.Facet/Range/LongRangeCounter.cs
+++ /dev/null
@@ -1,385 +0,0 @@
-using System.Diagnostics;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Lucene.Net.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.
- */
-
-
- /// <summary>
- /// Counts how many times each range was seen;
- /// per-hit it's just a binary search (<seealso cref="#add"/>)
- /// against the elementary intervals, and in the end we
- /// rollup back to the original ranges.
- /// </summary>
-
- internal sealed class LongRangeCounter
- {
-
- internal readonly LongRangeNode root;
- internal readonly long[] boundaries;
- internal readonly int[] leafCounts;
-
- // Used during rollup
- private int leafUpto;
- private int missingCount;
-
- public LongRangeCounter(LongRange[] ranges)
- {
- // Maps all range inclusive endpoints to int flags; 1
- // = start of interval, 2 = end of interval. We need to
- // track the start vs end case separately because if a
- // given point is both, then it must be its own
- // elementary interval:
- IDictionary<long?, int?> endsMap = new Dictionary<long?, int?>();
-
- endsMap[long.MinValue] = 1;
- endsMap[long.MaxValue] = 2;
-
- foreach (LongRange range in ranges)
- {
- int? cur;
- if (!endsMap.TryGetValue(range.minIncl,out cur))
- {
- endsMap[range.minIncl] = 1;
- }
- else
- {
- endsMap[range.minIncl] = (int)cur | 1;
- }
-
- if (!endsMap.TryGetValue(range.maxIncl,out cur))
- {
- endsMap[range.maxIncl] = 2;
- }
- else
- {
- endsMap[range.maxIncl] = (int)cur | 2;
- }
- }
-
- var endsList = new List<long?>(endsMap.Keys);
- endsList.Sort();
-
- // Build elementaryIntervals (a 1D Venn diagram):
- IList<InclusiveRange> elementaryIntervals = new List<InclusiveRange>();
- int upto0 = 1;
- long v = endsList[0].HasValue ? endsList[0].Value : 0;
- long prev;
- if (endsMap[v] == 3)
- {
- elementaryIntervals.Add(new InclusiveRange(v, v));
- prev = v + 1;
- }
- else
- {
- prev = v;
- }
-
- while (upto0 < endsList.Count)
- {
- v = endsList[upto0].HasValue ? endsList[upto0].Value : 0;
- int flags = endsMap[v].HasValue ? endsMap[v].Value : 0;
- //System.out.println(" v=" + v + " flags=" + flags);
- if (flags == 3)
- {
- // This point is both an end and a start; we need to
- // separate it:
- if (v > prev)
- {
- elementaryIntervals.Add(new InclusiveRange(prev, v - 1));
- }
- elementaryIntervals.Add(new InclusiveRange(v, v));
- prev = v + 1;
- }
- else if (flags == 1)
- {
- // This point is only the start of an interval;
- // attach it to next interval:
- if (v > prev)
- {
- elementaryIntervals.Add(new InclusiveRange(prev, v - 1));
- }
- prev = v;
- }
- else
- {
- Debug.Assert(flags == 2);
- // This point is only the end of an interval; attach
- // it to last interval:
- elementaryIntervals.Add(new InclusiveRange(prev, v));
- prev = v + 1;
- }
- //System.out.println(" ints=" + elementaryIntervals);
- upto0++;
- }
-
- // Build binary tree on top of intervals:
- root = Split(0, elementaryIntervals.Count, elementaryIntervals);
-
- // Set outputs, so we know which range to output for
- // each node in the tree:
- for (int i = 0; i < ranges.Length; i++)
- {
- root.addOutputs(i, ranges[i]);
- }
-
- // Set boundaries (ends of each elementary interval):
- boundaries = new long[elementaryIntervals.Count];
- for (int i = 0; i < boundaries.Length; i++)
- {
- boundaries[i] = elementaryIntervals[i].end;
- }
-
- leafCounts = new int[boundaries.Length];
-
- //System.out.println("ranges: " + Arrays.toString(ranges));
- //System.out.println("intervals: " + elementaryIntervals);
- //System.out.println("boundaries: " + Arrays.toString(boundaries));
- //System.out.println("root:\n" + root);
- }
-
- public void add(long v)
- {
- //System.out.println("add v=" + v);
-
- // NOTE: this works too, but it's ~6% slower on a simple
- // test with a high-freq TermQuery w/ range faceting on
- // wikimediumall:
- /*
- int index = Arrays.binarySearch(boundaries, v);
- if (index < 0) {
- index = -index-1;
- }
- leafCounts[index]++;
- */
-
- // Binary search to find matched elementary range; we
- // are guaranteed to find a match because the last
- // boundary is Long.MAX_VALUE:
-
- int lo = 0;
- int hi = boundaries.Length - 1;
- while (true)
- {
- int mid = (int)((uint)(lo + hi) >> 1);
- //System.out.println(" cycle lo=" + lo + " hi=" + hi + " mid=" + mid + " boundary=" + boundaries[mid] + " to " + boundaries[mid+1]);
- if (v <= boundaries[mid])
- {
- if (mid == 0)
- {
- leafCounts[0]++;
- return;
- }
- else
- {
- hi = mid - 1;
- }
- }
- else if (v > boundaries[mid + 1])
- {
- lo = mid + 1;
- }
- else
- {
- leafCounts[mid + 1]++;
- //System.out.println(" incr @ " + (mid+1) + "; now " + leafCounts[mid+1]);
- return;
- }
- }
- }
-
- /// <summary>
- /// Fills counts corresponding to the original input
- /// ranges, returning the missing count (how many hits
- /// didn't match any ranges).
- /// </summary>
- public int fillCounts(int[] counts)
- {
- //System.out.println(" rollup");
- missingCount = 0;
- leafUpto = 0;
- rollup(root, counts, false);
- return missingCount;
- }
-
- private int rollup(LongRangeNode node, int[] counts, bool sawOutputs)
- {
- int count;
- sawOutputs |= node.outputs != null;
- if (node.left != null)
- {
- count = rollup(node.left, counts, sawOutputs);
- count += rollup(node.right, counts, sawOutputs);
- }
- else
- {
- // Leaf:
- count = leafCounts[leafUpto];
- leafUpto++;
- if (!sawOutputs)
- {
- // This is a missing count (no output ranges were
- // seen "above" us):
- missingCount += count;
- }
- }
- if (node.outputs != null)
- {
- foreach (int rangeIndex in node.outputs)
- {
- counts[rangeIndex] += count;
- }
- }
- //System.out.println(" rollup node=" + node.start + " to " + node.end + ": count=" + count);
- return count;
- }
-
- private static LongRangeNode Split(int start, int end, IList<InclusiveRange> elementaryIntervals)
- {
- if (start == end - 1)
- {
- // leaf
- InclusiveRange range = elementaryIntervals[start];
- return new LongRangeNode(range.start, range.end, null, null, start);
- }
- else
- {
- int mid = (int)((uint)(start + end) >> 1);
- LongRangeNode left = Split(start, mid, elementaryIntervals);
- LongRangeNode right = Split(mid, end, elementaryIntervals);
- return new LongRangeNode(left.start, right.end, left, right, -1);
- }
- }
-
- private sealed class InclusiveRange
- {
- public readonly long start;
- public readonly long end;
-
- public InclusiveRange(long start, long end)
- {
- Debug.Assert(end >= start);
- this.start = start;
- this.end = end;
- }
-
- public override string ToString()
- {
- return start + " to " + end;
- }
- }
-
- /// <summary>
- /// Holds one node of the segment tree. </summary>
- public sealed class LongRangeNode
- {
- internal readonly LongRangeNode left;
- internal readonly LongRangeNode right;
-
- // Our range, inclusive:
- internal readonly long start;
- internal readonly long end;
-
- // If we are a leaf, the index into elementary ranges that
- // we point to:
- internal readonly int leafIndex;
-
- // Which range indices to output when a query goes
- // through this node:
- internal IList<int?> outputs;
-
- public LongRangeNode(long start, long end, LongRangeNode left, LongRangeNode right, int leafIndex)
- {
- this.start = start;
- this.end = end;
- this.left = left;
- this.right = right;
- this.leafIndex = leafIndex;
- }
-
- public override string ToString()
- {
- StringBuilder sb = new StringBuilder();
- ToString(sb, 0);
- return sb.ToString();
- }
-
- internal static void indent(StringBuilder sb, int depth)
- {
- for (int i = 0; i < depth; i++)
- {
- sb.Append(" ");
- }
- }
-
- /// <summary>
- /// Recursively assigns range outputs to each node. </summary>
- internal void addOutputs(int index, LongRange range)
- {
- if (start >= range.minIncl && end <= range.maxIncl)
- {
- // Our range is fully included in the incoming
- // range; add to our output list:
- if (outputs == null)
- {
- outputs = new List<int?>();
- }
- outputs.Add(index);
- }
- else if (left != null)
- {
- Debug.Assert(right != null);
- // Recurse:
- left.addOutputs(index, range);
- right.addOutputs(index, range);
- }
- }
-
- internal void ToString(StringBuilder sb, int depth)
- {
- indent(sb, depth);
- if (left == null)
- {
- Debug.Assert(right == null);
- sb.Append("leaf: " + start + " to " + end);
- }
- else
- {
- sb.Append("node: " + start + " to " + end);
- }
- if (outputs != null)
- {
- sb.Append(" outputs=");
- sb.Append(outputs);
- }
- sb.Append('\n');
-
- if (left != null)
- {
- Debug.Assert(right != null);
- left.ToString(sb, depth + 1);
- right.ToString(sb, depth + 1);
- }
- }
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Range/LongRangeFacetCounts.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Range/LongRangeFacetCounts.cs b/Lucene.Net.Facet/Range/LongRangeFacetCounts.cs
deleted file mode 100644
index 8435bbe..0000000
--- a/Lucene.Net.Facet/Range/LongRangeFacetCounts.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System.Collections.Generic;
-using Lucene.Net.Facet;
-
-namespace Lucene.Net.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.
- */
-
-
- using MatchingDocs = FacetsCollector.MatchingDocs;
- using FunctionValues = Lucene.Net.Queries.Function.FunctionValues;
- using ValueSource = Lucene.Net.Queries.Function.ValueSource;
- using LongFieldSource = Lucene.Net.Queries.Function.ValueSources.LongFieldSource;
- using DocIdSet = Lucene.Net.Search.DocIdSet;
- using Filter = Lucene.Net.Search.Filter;
- using Bits = Lucene.Net.Util.Bits;
- using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
-
- /// <summary>
- /// <seealso cref="Facets"/> implementation that computes counts for
- /// dynamic long ranges from a provided <seealso cref="ValueSource"/>,
- /// using <seealso cref="FunctionValues#longVal"/>. Use
- /// this for dimensions that change in real-time (e.g. a
- /// relative time based dimension like "Past day", "Past 2
- /// days", etc.) or that change for each request (e.g.
- /// distance from the user's location, "< 1 km", "< 2 km",
- /// etc.).
- ///
- /// @lucene.experimental
- /// </summary>
- public class LongRangeFacetCounts : RangeFacetCounts
- {
-
- /// <summary>
- /// Create {@code LongRangeFacetCounts}, using {@link
- /// LongFieldSource} from the specified field.
- /// </summary>
- public LongRangeFacetCounts(string field, FacetsCollector hits, params LongRange[] ranges)
- : this(field, new LongFieldSource(field), hits, ranges)
- {
- }
-
- /// <summary>
- /// Create {@code RangeFacetCounts}, using the provided
- /// <seealso cref="ValueSource"/>.
- /// </summary>
- public LongRangeFacetCounts(string field, ValueSource valueSource, FacetsCollector hits, params LongRange[] ranges)
- : this(field, valueSource, hits, null, ranges)
- {
- }
-
- /// <summary>
- /// Create {@code RangeFacetCounts}, using the provided
- /// <seealso cref="ValueSource"/>, and using the provided Filter as
- /// a fastmatch: only documents passing the filter are
- /// checked for the matching ranges. The filter must be
- /// random access (implement <seealso cref="DocIdSet#bits"/>).
- /// </summary>
- public LongRangeFacetCounts(string field, ValueSource valueSource, FacetsCollector hits, Filter fastMatchFilter, params LongRange[] ranges)
- : base(field, ranges, fastMatchFilter)
- {
- Count(valueSource, hits.GetMatchingDocs);
- }
-
- private void Count(ValueSource valueSource, IList<MatchingDocs> matchingDocs)
- {
-
- LongRange[] ranges = (LongRange[])this.Ranges;
-
- LongRangeCounter counter = new LongRangeCounter(ranges);
-
- int missingCount = 0;
- foreach (MatchingDocs hits in matchingDocs)
- {
- FunctionValues fv = valueSource.GetValues(new Dictionary<string, object>(), hits.context);
-
- TotCount += hits.totalHits;
- Bits bits;
- if (FastMatchFilter != null)
- {
- DocIdSet dis = FastMatchFilter.GetDocIdSet(hits.context, null);
- if (dis == null)
- {
- // No documents match
- continue;
- }
- bits = dis.GetBits();
- if (bits == null)
- {
- throw new System.ArgumentException("fastMatchFilter does not implement DocIdSet.bits");
- }
- }
- else
- {
- bits = null;
- }
-
- DocIdSetIterator docs = hits.bits.GetIterator();
- int doc;
- while ((doc = docs.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
- {
- if (bits != null && bits.Get(doc) == false)
- {
- doc++;
- continue;
- }
- // Skip missing docs:
- if (fv.Exists(doc))
- {
- counter.add(fv.LongVal(doc));
- }
- else
- {
- missingCount++;
- }
- }
- }
-
- int x = counter.fillCounts(Counts);
-
- missingCount += x;
-
- //System.out.println("totCount " + totCount + " missingCount " + counter.missingCount);
- TotCount -= missingCount;
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Range/Range.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Range/Range.cs b/Lucene.Net.Facet/Range/Range.cs
deleted file mode 100644
index 548b915..0000000
--- a/Lucene.Net.Facet/Range/Range.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using Lucene.Net.Facet;
-
-namespace Lucene.Net.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.
- */
-
- using ValueSource = Lucene.Net.Queries.Function.ValueSource;
- using Filter = Lucene.Net.Search.Filter;
- using FilteredQuery = Lucene.Net.Search.FilteredQuery; // javadocs
- using Lucene.Net.Search; // javadocs
-
- /// <summary>
- /// Base class for a single labeled range.
- ///
- /// @lucene.experimental
- /// </summary>
- public abstract class Range
- {
-
- /// <summary>
- /// Label that identifies this range. </summary>
- public readonly string Label;
-
- /// <summary>
- /// Sole constructor. </summary>
- protected internal Range(string label)
- {
- if (label == null)
- {
- throw new System.NullReferenceException("label cannot be null");
- }
- this.Label = label;
- }
-
- /// <summary>
- /// Returns a new <seealso cref="Filter"/> accepting only documents
- /// in this range. This filter is not general-purpose;
- /// you should either use it with <seealso cref="DrillSideways"/> by
- /// adding it to <seealso cref="DrillDownQuery#add"/>, or pass it to
- /// <seealso cref="FilteredQuery"/> using its {@link
- /// FilteredQuery#QUERY_FIRST_FILTER_STRATEGY}. If the
- /// <seealso cref="ValueSource"/> is static, e.g. an indexed numeric
- /// field, then it may be more efficient to use {@link
- /// NumericRangeFilter}. The provided fastMatchFilter,
- /// if non-null, will first be consulted, and only if
- /// that is set for each document will the range then be
- /// checked.
- /// </summary>
- public abstract Filter GetFilter(Filter fastMatchFilter, ValueSource valueSource);
-
- /// <summary>
- /// Returns a new <seealso cref="Filter"/> accepting only documents
- /// in this range. This filter is not general-purpose;
- /// you should either use it with <seealso cref="DrillSideways"/> by
- /// adding it to <seealso cref="DrillDownQuery#add"/>, or pass it to
- /// <seealso cref="FilteredQuery"/> using its {@link
- /// FilteredQuery#QUERY_FIRST_FILTER_STRATEGY}. If the
- /// <seealso cref="ValueSource"/> is static, e.g. an indexed numeric
- /// field, then it may be more efficient to use <seealso cref="NumericRangeFilter"/>.
- /// </summary>
- public virtual Filter GetFilter(ValueSource valueSource)
- {
- return GetFilter(null, valueSource);
- }
-
- /// <summary>
- /// Invoke this for a useless range. </summary>
- protected internal virtual void FailNoMatch()
- {
- throw new System.ArgumentException("range \"" + Label + "\" matches nothing");
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Range/RangeFacetCounts.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Range/RangeFacetCounts.cs b/Lucene.Net.Facet/Range/RangeFacetCounts.cs
deleted file mode 100644
index 29e02fb..0000000
--- a/Lucene.Net.Facet/Range/RangeFacetCounts.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System.Collections.Generic;
-using Lucene.Net.Facet;
-using Lucene.Net.Support;
-
-namespace Lucene.Net.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.
- */
-
-
- using Filter = Lucene.Net.Search.Filter;
-
- /// <summary>
- /// Base class for range faceting.
- ///
- /// @lucene.experimental
- /// </summary>
- public abstract class RangeFacetCounts : Facets
- {
- /// <summary>
- /// Ranges passed to constructor. </summary>
- protected internal readonly Range[] Ranges;
-
- /// <summary>
- /// Counts, initialized in by subclass. </summary>
- protected internal readonly int[] Counts;
-
- /// <summary>
- /// Optional: if specified, we first test this Filter to
- /// see whether the document should be checked for
- /// matching ranges. If this is null, all documents are
- /// checked.
- /// </summary>
- protected internal readonly Filter FastMatchFilter;
-
- /// <summary>
- /// Our field name. </summary>
- protected internal readonly string Field;
-
- /// <summary>
- /// Total number of hits. </summary>
- protected internal int TotCount;
-
- /// <summary>
- /// Create {@code RangeFacetCounts} </summary>
- protected internal RangeFacetCounts(string field, Range[] ranges, Filter fastMatchFilter)
- {
- this.Field = field;
- this.Ranges = ranges;
- this.FastMatchFilter = fastMatchFilter;
- Counts = new int[ranges.Length];
- }
-
- public override FacetResult GetTopChildren(int topN, string dim, params string[] path)
- {
- if (dim.Equals(Field) == false)
- {
- throw new System.ArgumentException("invalid dim \"" + dim + "\"; should be \"" + Field + "\"");
- }
- if (path.Length != 0)
- {
- throw new System.ArgumentException("path.length should be 0");
- }
- LabelAndValue[] labelValues = new LabelAndValue[Counts.Length];
- for (int i = 0; i < Counts.Length; i++)
- {
- labelValues[i] = new LabelAndValue(Ranges[i].Label, Counts[i]);
- }
- return new FacetResult(dim, path, TotCount, labelValues, labelValues.Length);
- }
-
- public override float GetSpecificValue(string dim, params string[] path)
- {
- // TODO: should we impl this?
- throw new System.NotSupportedException();
- }
-
- public override IList<FacetResult> GetAllDims(int topN)
- {
- return new[] { GetTopChildren(topN, null) };
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/SortedSet/DefaultSortedSetDocValuesReaderState.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/SortedSet/DefaultSortedSetDocValuesReaderState.cs b/Lucene.Net.Facet/SortedSet/DefaultSortedSetDocValuesReaderState.cs
deleted file mode 100644
index 8113d80..0000000
--- a/Lucene.Net.Facet/SortedSet/DefaultSortedSetDocValuesReaderState.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using System.Collections.Generic;
-using Lucene.Net.Index;
-using Lucene.Net.Support;
-using Lucene.Net.Util;
-using Lucene.Net.Facet;
-
-namespace Lucene.Net.Facet.SortedSet
-{
- /*
- * 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.
- */
-
- /// <summary>
- /// Default implementation of <seealso cref="SortedSetDocValuesFacetCounts"/>
- /// </summary>
- public class DefaultSortedSetDocValuesReaderState : SortedSetDocValuesReaderState
- {
-
- private readonly string field;
- private readonly AtomicReader topReader;
- private readonly int valueCount;
-
- /// <summary>
- /// <seealso cref="IndexReader"/> passed to the constructor. </summary>
- public readonly IndexReader origReader;
-
- private readonly IDictionary<string, OrdRange> prefixToOrdRange = new Dictionary<string, OrdRange>();
-
- /// <summary>
- /// Creates this, pulling doc values from the specified
- /// field.
- /// </summary>
- public DefaultSortedSetDocValuesReaderState(IndexReader reader, string field = FacetsConfig.DEFAULT_INDEX_FIELD_NAME)
- {
- this.field = field;
- this.origReader = reader;
-
- // We need this to create thread-safe MultiSortedSetDV
- // per collector:
- topReader = SlowCompositeReaderWrapper.Wrap(reader);
- SortedSetDocValues dv = topReader.GetSortedSetDocValues(field);
- if (dv == null)
- {
- throw new System.ArgumentException("field \"" + field + "\" was not indexed with SortedSetDocValues");
- }
- if (dv.ValueCount > int.MaxValue)
- {
- throw new System.ArgumentException("can only handle valueCount < Integer.MAX_VALUE; got " + dv.ValueCount);
- }
- valueCount = (int)dv.ValueCount;
-
- // TODO: we can make this more efficient if eg we can be
- // "involved" when OrdinalMap is being created? Ie see
- // each term/ord it's assigning as it goes...
- string lastDim = null;
- int startOrd = -1;
-
- // TODO: this approach can work for full hierarchy?;
- // TaxoReader can't do this since ords are not in
- // "sorted order" ... but we should generalize this to
- // support arbitrary hierarchy:
- for (int ord = 0; ord < valueCount; ord++)
- {
- BytesRef term = new BytesRef();
- dv.LookupOrd(ord, term);
- string[] components = FacetsConfig.StringToPath(term.Utf8ToString());
- if (components.Length != 2)
- {
- throw new System.ArgumentException("this class can only handle 2 level hierarchy (dim/value); got: " + Arrays.ToString(components) + " " + term.Utf8ToString());
- }
- if (!components[0].Equals(lastDim))
- {
- if (lastDim != null)
- {
- prefixToOrdRange[lastDim] = new OrdRange(startOrd, ord - 1);
- }
- startOrd = ord;
- lastDim = components[0];
- }
- }
-
- if (lastDim != null)
- {
- prefixToOrdRange[lastDim] = new OrdRange(startOrd, valueCount - 1);
- }
- }
-
- /// <summary>
- /// Return top-level doc values. </summary>
- public override SortedSetDocValues DocValues
- {
- get
- {
- return topReader.GetSortedSetDocValues(field);
- }
- }
-
- /// <summary>
- /// Returns mapping from prefix to <seealso cref="SortedSetDocValuesReaderState.OrdRange"/>. </summary>
- public override IDictionary<string, OrdRange> PrefixToOrdRange
- {
- get
- {
- return prefixToOrdRange;
- }
- }
-
- /// <summary>
- /// Returns the <seealso cref="SortedSetDocValuesReaderState.OrdRange"/> for this dimension. </summary>
- public override OrdRange GetOrdRange(string dim)
- {
- return prefixToOrdRange[dim];
- }
-
- /// <summary>
- /// Indexed field we are reading. </summary>
- public override string Field
- {
- get
- {
- return field;
- }
- }
-
- public override IndexReader OrigReader
- {
- get
- {
- return origReader;
- }
- }
-
- /// <summary>
- /// Number of unique labels. </summary>
- public override int Size
- {
- get
- {
- return valueCount;
- }
- }
-
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetCounts.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetCounts.cs b/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetCounts.cs
deleted file mode 100644
index 8ccb190..0000000
--- a/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetCounts.cs
+++ /dev/null
@@ -1,350 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using Lucene.Net.Facet;
-
-namespace Lucene.Net.Facet.SortedSet
-{
-
- /*
- * 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.
- */
-
-
- using MatchingDocs = FacetsCollector.MatchingDocs;
- using OrdRange = Lucene.Net.Facet.SortedSet.SortedSetDocValuesReaderState.OrdRange;
- using AtomicReader = Lucene.Net.Index.AtomicReader;
- using IndexReader = Lucene.Net.Index.IndexReader;
- using MultiDocValues = Lucene.Net.Index.MultiDocValues;
- using MultiSortedSetDocValues = Lucene.Net.Index.MultiDocValues.MultiSortedSetDocValues;
- using ReaderUtil = Lucene.Net.Index.ReaderUtil;
- using SortedSetDocValues = Lucene.Net.Index.SortedSetDocValues;
- using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
- using BytesRef = Lucene.Net.Util.BytesRef;
- using LongValues = Lucene.Net.Util.LongValues;
-
- /// <summary>
- /// Compute facets counts from previously
- /// indexed <seealso cref="SortedSetDocValuesFacetField"/>,
- /// without require a separate taxonomy index. Faceting is
- /// a bit slower (~25%), and there is added cost on every
- /// <seealso cref="IndexReader"/> open to create a new {@link
- /// SortedSetDocValuesReaderState}. Furthermore, this does
- /// not support hierarchical facets; only flat (dimension +
- /// label) facets, but it uses quite a bit less RAM to do
- /// so.
- ///
- /// <para><b>NOTE</b>: this class should be instantiated and
- /// then used from a single thread, because it holds a
- /// thread-private instance of <seealso cref="SortedSetDocValues"/>.
- ///
- /// </para>
- /// <para><b>NOTE:</b>: tie-break is by unicode sort order
- ///
- /// @lucene.experimental
- /// </para>
- /// </summary>
- public class SortedSetDocValuesFacetCounts : Facets
- {
-
- internal readonly SortedSetDocValuesReaderState state;
- internal readonly SortedSetDocValues dv;
- internal readonly string field;
- internal readonly int[] counts;
-
- /// <summary>
- /// Sparse faceting: returns any dimension that had any
- /// hits, topCount labels per dimension.
- /// </summary>
- public SortedSetDocValuesFacetCounts(SortedSetDocValuesReaderState state, FacetsCollector hits)
- {
- this.state = state;
- this.field = state.Field;
- dv = state.DocValues;
- counts = new int[state.Size];
- //System.out.println("field=" + field);
- Count(hits.GetMatchingDocs);
- }
-
- public override FacetResult GetTopChildren(int topN, string dim, params string[] path)
- {
- if (topN <= 0)
- {
- throw new System.ArgumentException("topN must be > 0 (got: " + topN + ")");
- }
- if (path.Length > 0)
- {
- throw new System.ArgumentException("path should be 0 length");
- }
- OrdRange ordRange = state.GetOrdRange(dim);
- if (ordRange == null)
- {
- throw new System.ArgumentException("dimension \"" + dim + "\" was not indexed");
- }
- return GetDim(dim, ordRange, topN);
- }
-
- private FacetResult GetDim(string dim, OrdRange ordRange, int topN)
- {
-
- TopOrdAndIntQueue q = null;
-
- int bottomCount = 0;
-
- int dimCount = 0;
- int childCount = 0;
-
- TopOrdAndIntQueue.OrdAndValue reuse = null;
- //System.out.println("getDim : " + ordRange.start + " - " + ordRange.end);
- for (int ord = ordRange.Start; ord <= ordRange.End; ord++)
- {
- //System.out.println(" ord=" + ord + " count=" + counts[ord]);
- if (counts[ord] > 0)
- {
- dimCount += counts[ord];
- childCount++;
- if (counts[ord] > bottomCount)
- {
- if (reuse == null)
- {
- reuse = new TopOrdAndIntQueue.OrdAndValue();
- }
- reuse.Ord = ord;
- reuse.Value = counts[ord];
- if (q == null)
- {
- // Lazy init, so we don't create this for the
- // sparse case unnecessarily
- q = new TopOrdAndIntQueue(topN);
- }
- reuse = q.InsertWithOverflow(reuse);
- if (q.Size() == topN)
- {
- bottomCount = q.Top().Value;
- }
- }
- }
- }
-
- if (q == null)
- {
- return null;
- }
-
- LabelAndValue[] labelValues = new LabelAndValue[q.Size()];
- for (int i = labelValues.Length - 1; i >= 0; i--)
- {
- TopOrdAndIntQueue.OrdAndValue ordAndValue = q.Pop();
- var term = new BytesRef();
- dv.LookupOrd(ordAndValue.Ord, term);
- string[] parts = FacetsConfig.StringToPath(term.Utf8ToString());
- labelValues[i] = new LabelAndValue(parts[1], ordAndValue.Value);
- }
-
- return new FacetResult(dim, new string[0], dimCount, labelValues, childCount);
- }
-
- /// <summary>
- /// Does all the "real work" of tallying up the counts. </summary>
- private void Count(IList<FacetsCollector.MatchingDocs> matchingDocs)
- {
- //System.out.println("ssdv count");
-
- MultiDocValues.OrdinalMap ordinalMap;
-
- // TODO: is this right? really, we need a way to
- // verify that this ordinalMap "matches" the leaves in
- // matchingDocs...
- if (dv is MultiDocValues.MultiSortedSetDocValues && matchingDocs.Count > 1)
- {
- ordinalMap = ((MultiDocValues.MultiSortedSetDocValues)dv).Mapping;
- }
- else
- {
- ordinalMap = null;
- }
-
- IndexReader origReader = state.OrigReader;
-
- foreach (FacetsCollector.MatchingDocs hits in matchingDocs)
- {
-
- var reader = hits.context.AtomicReader;
- //System.out.println(" reader=" + reader);
- // LUCENE-5090: make sure the provided reader context "matches"
- // the top-level reader passed to the
- // SortedSetDocValuesReaderState, else cryptic
- // AIOOBE can happen:
- if (!Equals(ReaderUtil.GetTopLevelContext(hits.context).Reader, origReader))
- {
- throw new ThreadStateException("the SortedSetDocValuesReaderState provided to this class does not match the reader being searched; you must create a new SortedSetDocValuesReaderState every time you open a new IndexReader");
- }
-
- SortedSetDocValues segValues = reader.GetSortedSetDocValues(field);
- if (segValues == null)
- {
- continue;
- }
-
- DocIdSetIterator docs = hits.bits.GetIterator();
-
- // TODO: yet another option is to count all segs
- // first, only in seg-ord space, and then do a
- // merge-sort-PQ in the end to only "resolve to
- // global" those seg ords that can compete, if we know
- // we just want top K? ie, this is the same algo
- // that'd be used for merging facets across shards
- // (distributed faceting). but this has much higher
- // temp ram req'ts (sum of number of ords across all
- // segs)
- if (ordinalMap != null)
- {
- int segOrd = hits.context.Ord;
-
- int numSegOrds = (int)segValues.ValueCount;
-
- if (hits.totalHits < numSegOrds / 10)
- {
- //System.out.println(" remap as-we-go");
- // Remap every ord to global ord as we iterate:
- int doc;
- while ((doc = docs.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
- {
- //System.out.println(" doc=" + doc);
- segValues.Document = doc;
- int term = (int)segValues.NextOrd();
- while (term != SortedSetDocValues.NO_MORE_ORDS)
- {
- //System.out.println(" segOrd=" + segOrd + " ord=" + term + " globalOrd=" + ordinalMap.getGlobalOrd(segOrd, term));
- counts[(int)ordinalMap.GetGlobalOrd(segOrd, term)]++;
- term = (int)segValues.NextOrd();
- }
- }
- }
- else
- {
- //System.out.println(" count in seg ord first");
-
- // First count in seg-ord space:
- int[] segCounts = new int[numSegOrds];
- int doc;
- while ((doc = docs.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
- {
- //System.out.println(" doc=" + doc);
- segValues.Document = doc;
- int term = (int)segValues.NextOrd();
- while (term != SortedSetDocValues.NO_MORE_ORDS)
- {
- //System.out.println(" ord=" + term);
- segCounts[term]++;
- term = (int)segValues.NextOrd();
- }
- }
-
- // Then, migrate to global ords:
- for (int ord = 0; ord < numSegOrds; ord++)
- {
- int count = segCounts[ord];
- if (count != 0)
- {
- //System.out.println(" migrate segOrd=" + segOrd + " ord=" + ord + " globalOrd=" + ordinalMap.getGlobalOrd(segOrd, ord));
- counts[(int)ordinalMap.GetGlobalOrd(segOrd, ord)] += count;
- }
- }
- }
- }
- else
- {
- // No ord mapping (e.g., single segment index):
- // just aggregate directly into counts:
- int doc;
- while ((doc = docs.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
- {
- segValues.Document = doc;
- int term = (int)segValues.NextOrd();
- while (term != SortedSetDocValues.NO_MORE_ORDS)
- {
- counts[term]++;
- term = (int)segValues.NextOrd();
- }
- }
- }
- }
- }
-
- public override float GetSpecificValue(string dim, params string[] path)
- {
- if (path.Length != 1)
- {
- throw new System.ArgumentException("path must be length=1");
- }
- int ord = (int)dv.LookupTerm(new BytesRef(FacetsConfig.PathToString(dim, path)));
- if (ord < 0)
- {
- return -1;
- }
-
- return counts[ord];
- }
-
- public override IList<FacetResult> GetAllDims(int topN)
- {
-
- IList<FacetResult> results = new List<FacetResult>();
- foreach (KeyValuePair<string, OrdRange> ent in state.PrefixToOrdRange)
- {
- FacetResult fr = GetDim(ent.Key, ent.Value, topN);
- if (fr != null)
- {
- results.Add(fr);
- }
- }
-
- var resultArray = results.ToArray();
- // Sort by highest count:
- Array.Sort(resultArray, new ComparatorAnonymousInnerClassHelper(this));
- return resultArray;
- }
-
- private class ComparatorAnonymousInnerClassHelper : IComparer<FacetResult>
- {
- private readonly SortedSetDocValuesFacetCounts outerInstance;
-
- public ComparatorAnonymousInnerClassHelper(SortedSetDocValuesFacetCounts outerInstance)
- {
- this.outerInstance = outerInstance;
- }
-
- public virtual int Compare(FacetResult a, FacetResult b)
- {
- if ((int)a.Value > (int)b.Value)
- {
- return -1;
- }
- else if ((int)b.Value > (int)a.Value)
- {
- return 1;
- }
- else
- {
- return a.Dim.CompareTo(b.Dim);
- }
- }
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetField.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetField.cs b/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetField.cs
deleted file mode 100644
index c860d56..0000000
--- a/Lucene.Net.Facet/SortedSet/SortedSetDocValuesFacetField.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-namespace Lucene.Net.Facet.SortedSet
-{
-
- /*
- * 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.
- */
-
- using Field = Lucene.Net.Documents.Field;
- using FieldType = Lucene.Net.Documents.FieldType;
-
- /// <summary>
- /// Add an instance of this to your Document for every facet
- /// label to be indexed via SortedSetDocValues.
- /// </summary>
- public class SortedSetDocValuesFacetField : Field
- {
-
- /// <summary>
- /// Indexed <seealso cref="FieldType"/>. </summary>
- public static readonly FieldType TYPE = new FieldType();
- static SortedSetDocValuesFacetField()
- {
- TYPE.Indexed = true;
- TYPE.Freeze();
- }
-
- /// <summary>
- /// Dimension. </summary>
- public readonly string Dim;
-
- /// <summary>
- /// Label. </summary>
- public readonly string Label;
-
- /// <summary>
- /// Sole constructor. </summary>
- public SortedSetDocValuesFacetField(string dim, string label)
- : base("dummy", TYPE)
- {
- FacetField.VerifyLabel(label);
- FacetField.VerifyLabel(dim);
- this.Dim = dim;
- this.Label = label;
- }
-
- public override string ToString()
- {
- return "SortedSetDocValuesFacetField(dim=" + Dim + " label=" + Label + ")";
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/SortedSet/SortedSetDocValuesReaderState.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/SortedSet/SortedSetDocValuesReaderState.cs b/Lucene.Net.Facet/SortedSet/SortedSetDocValuesReaderState.cs
deleted file mode 100644
index ef7e0fe..0000000
--- a/Lucene.Net.Facet/SortedSet/SortedSetDocValuesReaderState.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using System.Collections.Generic;
-
-namespace Lucene.Net.Facet.SortedSet
-{
-
- /*
- * 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.
- */
-
-
- using IndexReader = Lucene.Net.Index.IndexReader;
- using SortedSetDocValues = Lucene.Net.Index.SortedSetDocValues;
-
- /// <summary>
- /// Wraps a <seealso cref="IndexReader"/> and resolves ords
- /// using existing <seealso cref="SortedSetDocValues"/> APIs without a
- /// separate taxonomy index. This only supports flat facets
- /// (dimension + label), and it makes faceting a bit
- /// slower, adds some cost at reopen time, but avoids
- /// managing the separate taxonomy index. It also requires
- /// less RAM than the taxonomy index, as it manages the flat
- /// (2-level) hierarchy more efficiently. In addition, the
- /// tie-break during faceting is now meaningful (in label
- /// sorted order).
- ///
- /// <para><b>NOTE</b>: creating an instance of this class is
- /// somewhat costly, as it computes per-segment ordinal maps,
- /// so you should create it once and re-use that one instance
- /// for a given <seealso cref="IndexReader"/>.
- /// </para>
- /// </summary>
-
- public abstract class SortedSetDocValuesReaderState
- {
-
- /// <summary>
- /// Holds start/end range of ords, which maps to one
- /// dimension (someday we may generalize it to map to
- /// hierarchies within one dimension).
- /// </summary>
- public sealed class OrdRange
- {
- /// <summary>
- /// Start of range, inclusive: </summary>
- public readonly int Start;
- /// <summary>
- /// End of range, inclusive: </summary>
- public readonly int End;
-
- /// <summary>
- /// Start and end are inclusive. </summary>
- public OrdRange(int start, int end)
- {
- this.Start = start;
- this.End = end;
- }
- }
-
- /// <summary>
- /// Sole constructor. </summary>
- protected internal SortedSetDocValuesReaderState()
- {
- }
-
- /// <summary>
- /// Return top-level doc values. </summary>
- public abstract SortedSetDocValues DocValues { get; }
-
- /// <summary>
- /// Indexed field we are reading. </summary>
- public abstract string Field { get; }
-
- /// <summary>
- /// Returns the <seealso cref="OrdRange"/> for this dimension. </summary>
- public abstract OrdRange GetOrdRange(string dim);
-
- /// <summary>
- /// Returns mapping from prefix to <seealso cref="OrdRange"/>. </summary>
- public abstract IDictionary<string, OrdRange> PrefixToOrdRange { get; }
-
- /// <summary>
- /// Returns top-level index reader. </summary>
- public abstract IndexReader OrigReader { get; }
-
- /// <summary>
- /// Number of unique labels. </summary>
- public abstract int Size { get; }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Taxonomy/AssociationFacetField.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Taxonomy/AssociationFacetField.cs b/Lucene.Net.Facet/Taxonomy/AssociationFacetField.cs
deleted file mode 100644
index 6113eef..0000000
--- a/Lucene.Net.Facet/Taxonomy/AssociationFacetField.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using Lucene.Net.Facet;
-using Lucene.Net.Support;
-
-namespace Lucene.Net.Facet.Taxonomy
-{
-
- /*
- * 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.
- */
-
- using Document = Lucene.Net.Documents.Document; // javadocs
- using Field = Lucene.Net.Documents.Field;
- using FieldType = Lucene.Net.Documents.FieldType;
- using BytesRef = Lucene.Net.Util.BytesRef;
-
- /// <summary>
- /// Add an instance of this to your <seealso cref="Document"/> to add
- /// a facet label associated with an arbitrary byte[].
- /// This will require a custom <seealso cref="Facets"/>
- /// implementation at search time; see {@link
- /// IntAssociationFacetField} and {@link
- /// FloatAssociationFacetField} to use existing {@link
- /// Facets} implementations.
- ///
- /// @lucene.experimental
- /// </summary>
- public class AssociationFacetField : Field
- {
-
- /// <summary>
- /// Indexed <seealso cref="FieldType"/>. </summary>
- public static readonly FieldType TYPE = new FieldType();
- static AssociationFacetField()
- {
- TYPE.Indexed = true;
- TYPE.Freeze();
- }
-
- /// <summary>
- /// Dimension for this field. </summary>
- public readonly string dim;
-
- /// <summary>
- /// Facet path for this field. </summary>
- public readonly string[] path;
-
- /// <summary>
- /// Associated value. </summary>
- public readonly BytesRef assoc;
-
- /// <summary>
- /// Creates this from {@code dim} and {@code path} and an
- /// association
- /// </summary>
- public AssociationFacetField(BytesRef assoc, string dim, params string[] path)
- : base("dummy", TYPE)
- {
- FacetField.VerifyLabel(dim);
- foreach (string label in path)
- {
- FacetField.VerifyLabel(label);
- }
- this.dim = dim;
- this.assoc = assoc;
- if (path.Length == 0)
- {
- throw new System.ArgumentException("path must have at least one element");
- }
- this.path = path;
- }
-
- public override string ToString()
- {
- return "AssociationFacetField(dim=" + dim + " path=" + Arrays.ToString(path) + " bytes=" + assoc + ")";
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Taxonomy/CachedOrdinalsReader.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Taxonomy/CachedOrdinalsReader.cs b/Lucene.Net.Facet/Taxonomy/CachedOrdinalsReader.cs
deleted file mode 100644
index 0471f65..0000000
--- a/Lucene.Net.Facet/Taxonomy/CachedOrdinalsReader.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Lucene.Net.Support;
-
-namespace Lucene.Net.Facet.Taxonomy
-{
-
- /*
- * 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.
- */
-
-
- using DocValuesFormat = Lucene.Net.Codecs.DocValuesFormat;
- using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
- using BinaryDocValues = Lucene.Net.Index.BinaryDocValues;
- using Accountable = Lucene.Net.Util.Accountable;
- using ArrayUtil = Lucene.Net.Util.ArrayUtil;
- using IntsRef = Lucene.Net.Util.IntsRef;
- using RamUsageEstimator = Lucene.Net.Util.RamUsageEstimator;
-
- /// <summary>
- /// A per-segment cache of documents' facet ordinals. Every
- /// <seealso cref="CachedOrds"/> holds the ordinals in a raw {@code
- /// int[]}, and therefore consumes as much RAM as the total
- /// number of ordinals found in the segment, but saves the
- /// CPU cost of decoding ordinals during facet counting.
- ///
- /// <para>
- /// <b>NOTE:</b> every <seealso cref="CachedOrds"/> is limited to 2.1B
- /// total ordinals. If that is a limitation for you then
- /// consider limiting the segment size to fewer documents, or
- /// use an alternative cache which pages through the category
- /// ordinals.
- ///
- /// </para>
- /// <para>
- /// <b>NOTE:</b> when using this cache, it is advised to use
- /// a <seealso cref="DocValuesFormat"/> that does not cache the data in
- /// memory, at least for the category lists fields, or
- /// otherwise you'll be doing double-caching.
- ///
- /// </para>
- /// <para>
- /// <b>NOTE:</b> create one instance of this and re-use it
- /// for all facet implementations (the cache is per-instance,
- /// not static).
- /// </para>
- /// </summary>
- public class CachedOrdinalsReader : OrdinalsReader, Accountable
- {
-
- private readonly OrdinalsReader source;
-
- private readonly IDictionary<object, CachedOrds> ordsCache = new WeakDictionary<object, CachedOrds>();
-
- /// <summary>
- /// Sole constructor. </summary>
- public CachedOrdinalsReader(OrdinalsReader source)
- {
- this.source = source;
- }
-
- private CachedOrds GetCachedOrds(AtomicReaderContext context)
- {
- lock (this)
- {
- object cacheKey = context.Reader.CoreCacheKey;
- CachedOrds ords = ordsCache[cacheKey];
- if (ords == null)
- {
- ords = new CachedOrds(source.GetReader(context), context.Reader.MaxDoc);
- ordsCache[cacheKey] = ords;
- }
-
- return ords;
- }
- }
-
- public override string IndexFieldName
- {
- get
- {
- return source.IndexFieldName;
- }
- }
-
- public override OrdinalsSegmentReader GetReader(AtomicReaderContext context)
- {
- CachedOrds cachedOrds = GetCachedOrds(context);
- return new OrdinalsSegmentReaderAnonymousInnerClassHelper(this, cachedOrds);
- }
-
- private class OrdinalsSegmentReaderAnonymousInnerClassHelper : OrdinalsSegmentReader
- {
- private readonly CachedOrdinalsReader outerInstance;
-
- private Lucene.Net.Facet.Taxonomy.CachedOrdinalsReader.CachedOrds cachedOrds;
-
- public OrdinalsSegmentReaderAnonymousInnerClassHelper(CachedOrdinalsReader outerInstance, Lucene.Net.Facet.Taxonomy.CachedOrdinalsReader.CachedOrds cachedOrds)
- {
- this.outerInstance = outerInstance;
- this.cachedOrds = cachedOrds;
- }
-
- public override void Get(int docID, IntsRef ordinals)
- {
- ordinals.Ints = cachedOrds.ordinals;
- ordinals.Offset = cachedOrds.offsets[docID];
- ordinals.Length = cachedOrds.offsets[docID + 1] - ordinals.Offset;
- }
- }
-
- /// <summary>
- /// Holds the cached ordinals in two parallel {@code int[]} arrays. </summary>
- public sealed class CachedOrds : Accountable
- {
-
- /// <summary>
- /// Index into <seealso cref="#ordinals"/> for each document. </summary>
- public readonly int[] offsets;
-
- /// <summary>
- /// Holds ords for all docs. </summary>
- public readonly int[] ordinals;
-
- /// <summary>
- /// Creates a new <seealso cref="CachedOrds"/> from the <seealso cref="BinaryDocValues"/>.
- /// Assumes that the <seealso cref="BinaryDocValues"/> is not {@code null}.
- /// </summary>
- public CachedOrds(OrdinalsSegmentReader source, int maxDoc)
- {
- offsets = new int[maxDoc + 1];
- int[] ords = new int[maxDoc]; // let's assume one ordinal per-document as an initial size
-
- // this aggregator is limited to Integer.MAX_VALUE total ordinals.
- long totOrds = 0;
- IntsRef values = new IntsRef(32);
- for (int docID = 0; docID < maxDoc; docID++)
- {
- offsets[docID] = (int)totOrds;
- source.Get(docID, values);
- long nextLength = totOrds + values.Length;
- if (nextLength > ords.Length)
- {
- if (nextLength > ArrayUtil.MAX_ARRAY_LENGTH)
- {
- throw new ThreadStateException("too many ordinals (>= " + nextLength + ") to cache");
- }
- ords = ArrayUtil.Grow(ords, (int)nextLength);
- }
- Array.Copy(values.Ints, 0, ords, (int)totOrds, values.Length);
- totOrds = nextLength;
- }
- offsets[maxDoc] = (int)totOrds;
-
- // if ords array is bigger by more than 10% of what we really need, shrink it
- if ((double)totOrds / ords.Length < 0.9)
- {
- this.ordinals = new int[(int)totOrds];
- Array.Copy(ords, 0, this.ordinals, 0, (int)totOrds);
- }
- else
- {
- this.ordinals = ords;
- }
- }
-
- public long RamBytesUsed()
- {
- long mem = RamUsageEstimator.ShallowSizeOf(this) + RamUsageEstimator.SizeOf(offsets);
- if (offsets != ordinals)
- {
- mem += RamUsageEstimator.SizeOf(ordinals);
- }
- return mem;
- }
- }
-
- public virtual long RamBytesUsed()
- {
- lock (this)
- {
- long bytes = 0;
- foreach (CachedOrds ords in ordsCache.Values)
- {
- bytes += ords.RamBytesUsed();
- }
-
- return bytes;
- }
- }
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/eea269f3/Lucene.Net.Facet/Taxonomy/CategoryPath.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Facet/Taxonomy/CategoryPath.cs b/Lucene.Net.Facet/Taxonomy/CategoryPath.cs
deleted file mode 100644
index df69862..0000000
--- a/Lucene.Net.Facet/Taxonomy/CategoryPath.cs
+++ /dev/null
@@ -1,316 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Text;
-using Lucene.Net.Support;
-
-namespace Lucene.Net.Facet.Taxonomy
-{
-
- /*
- * 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.
- */
-
-
- /// <summary>
- /// Holds a sequence of string components, specifying the hierarchical name of a
- /// category.
- ///
- /// @lucene.experimental
- /// </summary>
- public class CategoryPath : IComparable<CategoryPath>
- {
-
- /// <summary>
- /// An empty <seealso cref="CategoryPath"/> </summary>
- public static readonly CategoryPath EMPTY = new CategoryPath();
-
- /// <summary>
- /// The components of this <seealso cref="CategoryPath"/>. Note that this array may be
- /// shared with other <seealso cref="CategoryPath"/> instances, e.g. as a result of
- /// <seealso cref="#subpath(int)"/>, therefore you should traverse the array up to
- /// <seealso cref="#length"/> for this path's components.
- /// </summary>
- public readonly string[] components;
-
- /// <summary>
- /// The number of components of this <seealso cref="CategoryPath"/>. </summary>
- public readonly int length;
-
- // Used by singleton EMPTY
- private CategoryPath()
- {
- components = null;
- length = 0;
- }
-
- // Used by subpath
- private CategoryPath(CategoryPath copyFrom, int prefixLen)
- {
- // while the code which calls this method is safe, at some point a test
- // tripped on AIOOBE in toString, but we failed to reproduce. adding the
- // assert as a safety check.
- Debug.Assert(prefixLen > 0 && prefixLen <= copyFrom.components.Length, "prefixLen cannot be negative nor larger than the given components' length: prefixLen=" + prefixLen + " components.length=" + copyFrom.components.Length);
- this.components = copyFrom.components;
- length = prefixLen;
- }
-
- /// <summary>
- /// Construct from the given path components. </summary>
- public CategoryPath(params string[] components)
- {
- Debug.Assert(components.Length > 0, "use CategoryPath.EMPTY to create an empty path");
- foreach (string comp in components)
- {
- if (string.IsNullOrEmpty(comp))
- {
- throw new System.ArgumentException("empty or null components not allowed: " + Arrays.ToString(components));
- }
- }
- this.components = components;
- length = components.Length;
- }
-
- /// <summary>
- /// Construct from a given path, separating path components with {@code delimiter}. </summary>
- public CategoryPath(string pathString, char delimiter)
- {
- string[] comps = pathString.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
- if (comps.Length == 1 && comps[0].Length == 0)
- {
- components = null;
- length = 0;
- }
- else
- {
- foreach (string comp in comps)
- {
- if (string.IsNullOrEmpty(comp))
- {
- throw new System.ArgumentException("empty or null components not allowed: " + Arrays.ToString(comps));
- }
- }
- components = comps;
- length = components.Length;
- }
- }
-
- /// <summary>
- /// Returns the number of characters needed to represent the path, including
- /// delimiter characters, for using with
- /// <seealso cref="#copyFullPath(char[], int, char)"/>.
- /// </summary>
- public virtual int FullPathLength()
- {
- if (length == 0)
- {
- return 0;
- }
-
- int charsNeeded = 0;
- for (int i = 0; i < length; i++)
- {
- charsNeeded += components[i].Length;
- }
- charsNeeded += length - 1; // num delimter chars
- return charsNeeded;
- }
-
- /// <summary>
- /// Compares this path with another <seealso cref="CategoryPath"/> for lexicographic
- /// order.
- /// </summary>
- public virtual int CompareTo(CategoryPath other)
- {
- int len = length < other.length ? length : other.length;
- for (int i = 0, j = 0; i < len; i++, j++)
- {
- int cmp = components[i].CompareTo(other.components[j]);
- if (cmp < 0) // this is 'before'
- {
- return -1;
- }
- if (cmp > 0) // this is 'after'
- {
- return 1;
- }
- }
-
- // one is a prefix of the other
- return length - other.length;
- }
-
- private void hasDelimiter(string offender, char delimiter)
- {
- throw new System.ArgumentException("delimiter character '" + delimiter + "' (U+" + delimiter.ToString() + ") appears in path component \"" + offender + "\"");
- }
-
- private void noDelimiter(char[] buf, int offset, int len, char delimiter)
- {
- for (int idx = 0; idx < len; idx++)
- {
- if (buf[offset + idx] == delimiter)
- {
- hasDelimiter(new string(buf, offset, len), delimiter);
- }
- }
- }
-
- /// <summary>
- /// Copies the path components to the given {@code char[]}, starting at index
- /// {@code start}. {@code delimiter} is copied between the path components.
- /// Returns the number of chars copied.
- ///
- /// <para>
- /// <b>NOTE:</b> this method relies on the array being large enough to hold the
- /// components and separators - the amount of needed space can be calculated
- /// with <seealso cref="#fullPathLength()"/>.
- /// </para>
- /// </summary>
- public virtual int CopyFullPath(char[] buf, int start, char delimiter)
- {
- if (length == 0)
- {
- return 0;
- }
-
- int idx = start;
- int upto = length - 1;
- for (int i = 0; i < upto; i++)
- {
- int len = components[i].Length;
- components[i].CopyTo(0, buf, idx, len - 0);
- noDelimiter(buf, idx, len, delimiter);
- idx += len;
- buf[idx++] = delimiter;
- }
- components[upto].CopyTo(0, buf, idx, components[upto].Length - 0);
- noDelimiter(buf, idx, components[upto].Length, delimiter);
-
- return idx + components[upto].Length - start;
- }
-
- public override bool Equals(object obj)
- {
- if (!(obj is CategoryPath))
- {
- return false;
- }
-
- CategoryPath other = (CategoryPath)obj;
- if (length != other.length)
- {
- return false; // not same length, cannot be equal
- }
-
- // CategoryPaths are more likely to differ at the last components, so start
- // from last-first
- for (int i = length - 1; i >= 0; i--)
- {
- if (!components[i].Equals(other.components[i]))
- {
- return false;
- }
- }
- return true;
- }
-
- public override int GetHashCode()
- {
- if (length == 0)
- {
- return 0;
- }
-
- int hash = length;
- for (int i = 0; i < length; i++)
- {
- hash = hash * 31 + components[i].GetHashCode();
- }
- return hash;
- }
-
- /// <summary>
- /// Calculate a 64-bit hash function for this path. </summary>
- public virtual long LongHashCode()
- {
- if (length == 0)
- {
- return 0;
- }
-
- long hash = length;
- for (int i = 0; i < length; i++)
- {
- hash = hash * 65599 + components[i].GetHashCode();
- }
- return hash;
- }
-
- /// <summary>
- /// Returns a sub-path of this path up to {@code length} components. </summary>
- public virtual CategoryPath Subpath(int length)
- {
- if (length >= this.length || length < 0)
- {
- return this;
- }
- else if (length == 0)
- {
- return EMPTY;
- }
- else
- {
- return new CategoryPath(this, length);
- }
- }
-
- /// <summary>
- /// Returns a string representation of the path, separating components with
- /// '/'.
- /// </summary>
- /// <seealso cref= #toString(char) </seealso>
- public override string ToString()
- {
- return ToString('/');
- }
-
- /// <summary>
- /// Returns a string representation of the path, separating components with the
- /// given delimiter.
- /// </summary>
- public virtual string ToString(char delimiter)
- {
- if (length == 0)
- {
- return "";
- }
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < length; i++)
- {
- if (components[i].IndexOf(delimiter) != -1)
- {
- hasDelimiter(components[i], delimiter);
- }
- sb.Append(components[i]).Append(delimiter);
- }
- sb.Length = sb.Length - 1; // remove last delimiter
- return sb.ToString();
- }
-
- }
-
-}
\ No newline at end of file