diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetField.java b/solr/core/src/java/org/apache/solr/search/facet/FacetField.java
index 728cd6ea9660..f5fdf3eaff37 100644
--- a/solr/core/src/java/org/apache/solr/search/facet/FacetField.java
+++ b/solr/core/src/java/org/apache/solr/search/facet/FacetField.java
@@ -47,6 +47,7 @@ public enum FacetMethod {
DV, // DocValues, collect into ordinal array
UIF, // UnInvertedField, collect into ordinal array
DVHASH, // DocValues, collect into hash
+ DVSTRING, // DocValues, collect into hash, for non-numeric single or multi-valued fields
ENUM, // TermsEnum then intersect DocSet (stream-able)
STREAM, // presently equivalent to ENUM
SMART,
@@ -58,6 +59,7 @@ public static FacetMethod fromString(String method) {
case "dv": return DV;
case "uif": return UIF;
case "dvhash": return DVHASH;
+ case "dvstring": return DVSTRING;
case "enum": return ENUM;
case "stream": return STREAM; // TODO replace with enum?
case "smart": return SMART;
@@ -116,6 +118,14 @@ public FacetProcessor createFacetProcessor(FacetContext fcontext) {
return new FacetFieldProcessorByEnumTermsStream(fcontext, this, sf);
}
+ if (method == FacetMethod.DVSTRING) {
+ if (ntype != null) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+ "Method " + method + " cannot support numeric-type field " + field);
+ }
+ return new FacetFieldProcessorByHashDVString(fcontext, this, sf);
+ }
+
// TODO if method=UIF and not single-valued numerics then simply choose that now? TODO add FieldType.getDocValuesType()
if (!multiToken) {
diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByHashDVString.java b/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByHashDVString.java
new file mode 100644
index 000000000000..1d9996fe252b
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByHashDVString.java
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ */
+package org.apache.solr.search.facet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.function.IntFunction;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.search.ScoreMode;
+import org.apache.lucene.search.SimpleCollector;
+import org.apache.lucene.util.BitUtil;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.DocSetUtil;
+import org.apache.solr.search.facet.SlotAcc.CountSlotAcc;
+import org.apache.solr.search.facet.SlotAcc.SlotContext;
+
+/**
+ * Facets DocValues into a HashMap using the BytesRef as key.
+ * Limitations:
+ *
+ *
Only for string type fields not numerics (use dvhash for those)
+ *
+ */
+class FacetFieldProcessorByHashDVString extends FacetFieldProcessor {
+
+ static class TermData {
+ int count;
+ int slotIndex;
+ }
+
+ // Using a regular HashMap hence slots get created dynamically as new keys are found from docvalues
+ private HashMap table;
+ private ArrayList slotList; // position in List is the slot number, value is key for table
+ private int capacity; // how many slots we will need for accs, gets resized later if needed
+
+ FacetFieldProcessorByHashDVString(FacetContext fcontext, FacetField freq, SchemaField sf) {
+ super(fcontext, freq, sf);
+ if (freq.mincount == 0) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ getClass()+" doesn't support mincount=0");
+ }
+ if (freq.prefix != null) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ getClass()+" doesn't support prefix"); // yet, but it could
+ }
+ FieldInfo fieldInfo = fcontext.searcher.getSlowAtomicReader().getFieldInfos().fieldInfo(sf.getName());
+ if (fieldInfo != null &&
+ fieldInfo.getDocValuesType() != DocValuesType.SORTED &&
+ fieldInfo.getDocValuesType() != DocValuesType.SORTED_SET) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ getClass()+" only supports string with docValues");
+ }
+ }
+
+ @Override
+ public void process() throws IOException {
+ super.process();
+ response = calcFacets();
+ table = null;//gc
+ slotList = null;
+ }
+
+ private SimpleOrderedMap