This is an automated email from the ASF dual-hosted git repository.

jsweeney pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 3a69654b9eb SOLR-17022: Support for glob patterns for fields in Export 
handler, Stream handler and with SelectStream streaming expression (#1996)
3a69654b9eb is described below

commit 3a69654b9eb2382c550d90b62c253588cb95f029
Author: Justin Sweeney <[email protected]>
AuthorDate: Fri Dec 22 05:29:38 2023 -0700

    SOLR-17022: Support for glob patterns for fields in Export handler, Stream 
handler and with SelectStream streaming expression (#1996)
    
    * Adding support for Glob patterns in Export handler and Select stream 
handler using the same logic to match glob patterns to fields as is used in 
select requests
---
 .../apache/solr/handler/export/ExportWriter.java   | 77 ++++++++++++----------
 .../org/apache/solr/search/SolrReturnFields.java   |  5 +-
 .../solr/handler/export/TestExportWriter.java      | 37 +++++++++++
 .../query-guide/pages/exporting-result-sets.adoc   |  5 +-
 .../pages/stream-decorator-reference.adoc          |  2 +-
 .../solr/client/solrj/io/stream/SelectStream.java  | 30 ++++++++-
 .../io/stream/StreamExpressionToExpessionTest.java |  3 +-
 .../apache/solr/common/util/GlobPatternUtil.java   | 37 +++++++++++
 .../solr/common/util/TestGlobPatternUtil.java      | 33 ++++++++++
 9 files changed, 187 insertions(+), 42 deletions(-)

diff --git 
a/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java 
b/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java
index 51ba5551b69..e5e40e6d07e 100644
--- a/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java
+++ b/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java
@@ -27,6 +27,7 @@ import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.lang.invoke.MethodHandles;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
@@ -76,6 +77,7 @@ import org.apache.solr.schema.SortableTextField;
 import org.apache.solr.schema.StrField;
 import org.apache.solr.search.DocValuesIteratorCache;
 import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.SolrReturnFields;
 import org.apache.solr.search.SortSpec;
 import org.apache.solr.search.SyntaxError;
 import org.slf4j.Logger;
@@ -121,7 +123,7 @@ public class ExportWriter implements SolrCore.RawWriter, 
Closeable {
   private int priorityQueueSize;
   StreamExpression streamExpression;
   StreamContext streamContext;
-  FieldWriter[] fieldWriters;
+  List<FieldWriter> fieldWriters;
   int totalHits = 0;
   FixedBitSet[] sets = null;
   PushWriter writer;
@@ -293,7 +295,7 @@ public class ExportWriter implements SolrCore.RawWriter, 
Closeable {
     }
 
     try {
-      fieldWriters = getFieldWriters(fields, req.getSearcher());
+      fieldWriters = getFieldWriters(fields, req);
     } catch (Exception e) {
       writeException(e, writer, true);
       return;
@@ -473,7 +475,7 @@ public class ExportWriter implements SolrCore.RawWriter, 
Closeable {
   }
 
   void writeDoc(
-      SortDoc sortDoc, List<LeafReaderContext> leaves, EntryWriter ew, 
FieldWriter[] writers)
+      SortDoc sortDoc, List<LeafReaderContext> leaves, EntryWriter ew, 
List<FieldWriter> writers)
       throws IOException {
     int ord = sortDoc.ord;
     LeafReaderContext context = leaves.get(ord);
@@ -485,82 +487,89 @@ public class ExportWriter implements SolrCore.RawWriter, 
Closeable {
     }
   }
 
-  public FieldWriter[] getFieldWriters(String[] fields, SolrIndexSearcher 
searcher)
+  public List<FieldWriter> getFieldWriters(String[] fields, SolrQueryRequest 
req)
       throws IOException {
-    IndexSchema schema = searcher.getSchema();
-    FieldWriter[] writers = new FieldWriter[fields.length];
-    DocValuesIteratorCache dvIterCache = new DocValuesIteratorCache(searcher, 
false);
-    for (int i = 0; i < fields.length; i++) {
-      String field = fields[i];
-      SchemaField schemaField = null;
+    DocValuesIteratorCache dvIterCache = new 
DocValuesIteratorCache(req.getSearcher(), false);
 
-      try {
-        schemaField = schema.getField(field);
-      } catch (Exception e) {
-        throw new IOException(e);
-      }
+    SolrReturnFields solrReturnFields = new SolrReturnFields(fields, req);
 
+    List<FieldWriter> writers = new ArrayList<>();
+    for (String field : req.getSearcher().getFieldNames()) {
+      if (!solrReturnFields.wantsField(field)) {
+        continue;
+      }
+      SchemaField schemaField = req.getSchema().getField(field);
       if (!schemaField.hasDocValues()) {
         throw new IOException(schemaField + " must have DocValues to use this 
feature.");
       }
       boolean multiValued = schemaField.multiValued();
       FieldType fieldType = schemaField.getType();
-
-      if (fieldType instanceof SortableTextField && 
schemaField.useDocValuesAsStored() == false) {
-        throw new IOException(
-            schemaField + " Must have useDocValuesAsStored='true' to be used 
with export writer");
+      FieldWriter writer;
+
+      if (fieldType instanceof SortableTextField && 
!schemaField.useDocValuesAsStored()) {
+        if (solrReturnFields.getRequestedFieldNames() != null
+            && solrReturnFields.getRequestedFieldNames().contains(field)) {
+          // Explicitly requested field cannot be used due to not having 
useDocValuesAsStored=true,
+          // throw exception
+          throw new IOException(
+              schemaField + " Must have useDocValuesAsStored='true' to be used 
with export writer");
+        } else {
+          // Glob pattern matched field cannot be used due to not having 
useDocValuesAsStored=true
+          continue;
+        }
       }
 
       DocValuesIteratorCache.FieldDocValuesSupplier docValuesCache = 
dvIterCache.getSupplier(field);
 
       if (docValuesCache == null) {
-        writers[i] = EMPTY_FIELD_WRITER;
+        writer = EMPTY_FIELD_WRITER;
       } else if (fieldType instanceof IntValueFieldType) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
true, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, true, 
docValuesCache);
         } else {
-          writers[i] = new IntFieldWriter(field, docValuesCache);
+          writer = new IntFieldWriter(field, docValuesCache);
         }
       } else if (fieldType instanceof LongValueFieldType) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
true, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, true, 
docValuesCache);
         } else {
-          writers[i] = new LongFieldWriter(field, docValuesCache);
+          writer = new LongFieldWriter(field, docValuesCache);
         }
       } else if (fieldType instanceof FloatValueFieldType) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
true, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, true, 
docValuesCache);
         } else {
-          writers[i] = new FloatFieldWriter(field, docValuesCache);
+          writer = new FloatFieldWriter(field, docValuesCache);
         }
       } else if (fieldType instanceof DoubleValueFieldType) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
true, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, true, 
docValuesCache);
         } else {
-          writers[i] = new DoubleFieldWriter(field, docValuesCache);
+          writer = new DoubleFieldWriter(field, docValuesCache);
         }
       } else if (fieldType instanceof StrField || fieldType instanceof 
SortableTextField) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
false, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, false, 
docValuesCache);
         } else {
-          writers[i] = new StringFieldWriter(field, fieldType, docValuesCache);
+          writer = new StringFieldWriter(field, fieldType, docValuesCache);
         }
       } else if (fieldType instanceof DateValueFieldType) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
false, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, false, 
docValuesCache);
         } else {
-          writers[i] = new DateFieldWriter(field, docValuesCache);
+          writer = new DateFieldWriter(field, docValuesCache);
         }
       } else if (fieldType instanceof BoolField) {
         if (multiValued) {
-          writers[i] = new MultiFieldWriter(field, fieldType, schemaField, 
true, docValuesCache);
+          writer = new MultiFieldWriter(field, fieldType, schemaField, true, 
docValuesCache);
         } else {
-          writers[i] = new BoolFieldWriter(field, fieldType, docValuesCache);
+          writer = new BoolFieldWriter(field, fieldType, docValuesCache);
         }
       } else {
         throw new IOException(
             "Export fields must be one of the following types: 
int,float,long,double,string,date,boolean,SortableText");
       }
+      writers.add(writer);
     }
     return writers;
   }
diff --git a/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java 
b/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java
index 7d0583ce63a..af35245af15 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java
@@ -28,7 +28,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Supplier;
-import org.apache.commons.io.FilenameUtils;
 import org.apache.lucene.queries.function.FunctionQuery;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.QueryValueSource;
@@ -37,6 +36,7 @@ import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.GlobPatternUtil;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.transform.DocTransformer;
 import org.apache.solr.response.transform.DocTransformers;
@@ -577,8 +577,7 @@ public class SolrReturnFields extends ReturnFields {
       return true;
     }
     for (String s : globs) {
-      // TODO something better?
-      if (FilenameUtils.wildcardMatch(name, s)) {
+      if (GlobPatternUtil.matches(s, name)) {
         okFieldNames.add(name); // Don't calculate it again
         return true;
       }
diff --git 
a/solr/core/src/test/org/apache/solr/handler/export/TestExportWriter.java 
b/solr/core/src/test/org/apache/solr/handler/export/TestExportWriter.java
index 8337609faf9..e37f26efc94 100644
--- a/solr/core/src/test/org/apache/solr/handler/export/TestExportWriter.java
+++ b/solr/core/src/test/org/apache/solr/handler/export/TestExportWriter.java
@@ -1298,6 +1298,43 @@ public class TestExportWriter extends SolrTestCaseJ4 {
             .contains("Must have useDocValuesAsStored='true'"));
   }
 
+  @Test
+  public void testGlobFields() throws Exception {
+    assertU(delQ("*:*"));
+    assertU(commit());
+    createLargeIndex();
+    SolrQueryRequest req =
+        req("q", "*:*", "qt", "/export", "fl", "id,*_udvas,*_i_p", "sort", "id 
asc");
+    assertJQ(
+        req,
+        "response/numFound==100000",
+        "response/docs/[0]/id=='0'",
+        "response/docs/[1]/id=='1'",
+        "response/docs/[0]/sortabledv_udvas=='0'",
+        "response/docs/[1]/sortabledv_udvas=='1'",
+        "response/docs/[0]/small_i_p==0",
+        "response/docs/[1]/small_i_p==1");
+
+    assertU(delQ("*:*"));
+    assertU(commit());
+    createLargeIndex();
+    req = req("q", "*:*", "qt", "/export", "fl", "*", "sort", "id asc");
+    assertJQ(
+        req,
+        "response/numFound==100000",
+        "response/docs/[0]/id=='0'",
+        "response/docs/[1]/id=='1'",
+        "response/docs/[0]/sortabledv_udvas=='0'",
+        "response/docs/[1]/sortabledv_udvas=='1'",
+        "response/docs/[0]/small_i_p==0",
+        "response/docs/[1]/small_i_p==1");
+
+    String jq = JQ(req);
+    assertFalse(
+        "Fields without docvalues and useDocValuesAsStored should not be 
returned",
+        jq.contains("\"sortabledv\""));
+  }
+
   @SuppressWarnings("rawtypes")
   private void validateSort(int numDocs) throws Exception {
     // 10 fields
diff --git 
a/solr/solr-ref-guide/modules/query-guide/pages/exporting-result-sets.adoc 
b/solr/solr-ref-guide/modules/query-guide/pages/exporting-result-sets.adoc
index 28c395daa46..bbd31c7b358 100644
--- a/solr/solr-ref-guide/modules/query-guide/pages/exporting-result-sets.adoc
+++ b/solr/solr-ref-guide/modules/query-guide/pages/exporting-result-sets.adoc
@@ -70,7 +70,10 @@ It can get worse otherwise.
 The `fl` property defines the fields that will be exported with the result set.
 Any of the field types that can be sorted (i.e., int, long, float, double, 
string, date, boolean) can be used in the field list.
 The fields can be single or multi-valued.
-However, returning scores and wildcards are not supported at this time.
+
+Wildcard patterns can be used for the field list (e.g. `fl=*_i`) and will be 
expanded to the list of fields that match the pattern and are able to be 
exported, see <<Field Requirements>>.
+
+Returning scores is not supported at this time.
 
 === Specifying the Local Streaming Expression
 
diff --git 
a/solr/solr-ref-guide/modules/query-guide/pages/stream-decorator-reference.adoc 
b/solr/solr-ref-guide/modules/query-guide/pages/stream-decorator-reference.adoc
index d5e25ba98fa..28a570ffae4 100644
--- 
a/solr/solr-ref-guide/modules/query-guide/pages/stream-decorator-reference.adoc
+++ 
b/solr/solr-ref-guide/modules/query-guide/pages/stream-decorator-reference.adoc
@@ -1376,7 +1376,7 @@ One can provide a list of operations and evaluators to 
perform on any fields, su
 === select Parameters
 
 * `StreamExpression`
-* `fieldName`: name of field to include in the output tuple (can include 
multiple of these), such as `outputTuple[fieldName] = inputTuple[fieldName]`
+* `fieldName`: name of field to include in the output tuple (can include 
multiple of these), such as `outputTuple[fieldName] = inputTuple[fieldName]`. 
The `fieldName` can be a wildcard pattern, e.g. `a_*` to select all fields that 
start with `a_`.
 * `fieldName as aliasFieldName`: aliased field name to include in the output 
tuple (can include multiple of these), such as `outputTuple[aliasFieldName] = 
incomingTuple[fieldName]`
 * `replace(fieldName, value, withValue=replacementValue)`: if 
`incomingTuple[fieldName] == value` then `outgoingTuple[fieldName]` will be set 
to `replacementValue`.
 `value` can be the string "null" to replace a null value with some other value.
diff --git 
a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/stream/SelectStream.java
 
b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/stream/SelectStream.java
index 80219e797bb..647a1c59d4c 100644
--- 
a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/stream/SelectStream.java
+++ 
b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/stream/SelectStream.java
@@ -38,6 +38,7 @@ import 
org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParser;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionValue;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
+import org.apache.solr.common.util.GlobPatternUtil;
 
 /**
  * Selects fields from the incoming stream and applies optional field 
renaming. Does not reorder the
@@ -52,14 +53,21 @@ public class SelectStream extends TupleStream implements 
Expressible {
   private TupleStream stream;
   private StreamContext streamContext;
   private Map<String, String> selectedFields;
+  private List<String> selectedFieldGlobPatterns;
   private Map<StreamEvaluator, String> selectedEvaluators;
   private List<StreamOperation> operations;
 
   public SelectStream(TupleStream stream, List<String> selectedFields) throws 
IOException {
     this.stream = stream;
     this.selectedFields = new HashMap<>();
+    this.selectedFieldGlobPatterns = new ArrayList<>();
     for (String selectedField : selectedFields) {
-      this.selectedFields.put(selectedField, selectedField);
+      if (selectedField.contains("*")) {
+        // selected field is a glob pattern
+        this.selectedFieldGlobPatterns.add(selectedField);
+      } else {
+        this.selectedFields.put(selectedField, selectedField);
+      }
     }
     operations = new ArrayList<>();
     selectedEvaluators = new LinkedHashMap<>();
@@ -68,6 +76,7 @@ public class SelectStream extends TupleStream implements 
Expressible {
   public SelectStream(TupleStream stream, Map<String, String> selectedFields) 
throws IOException {
     this.stream = stream;
     this.selectedFields = selectedFields;
+    selectedFieldGlobPatterns = new ArrayList<>();
     operations = new ArrayList<>();
     selectedEvaluators = new LinkedHashMap<>();
   }
@@ -123,6 +132,7 @@ public class SelectStream extends TupleStream implements 
Expressible {
     stream = factory.constructStream(streamExpressions.get(0));
 
     selectedFields = new HashMap<>();
+    selectedFieldGlobPatterns = new ArrayList<>();
     selectedEvaluators = new LinkedHashMap<>();
     for (StreamExpressionParameter parameter : selectAsFieldsExpressions) {
       StreamExpressionValue selectField = (StreamExpressionValue) parameter;
@@ -175,7 +185,11 @@ public class SelectStream extends TupleStream implements 
Expressible {
           selectedFields.put(asValue, asName);
         }
       } else {
-        selectedFields.put(value, value);
+        if (value.contains("*")) {
+          selectedFieldGlobPatterns.add(value);
+        } else {
+          selectedFields.put(value, value);
+        }
       }
     }
 
@@ -217,6 +231,11 @@ public class SelectStream extends TupleStream implements 
Expressible {
       }
     }
 
+    // selected glob patterns
+    for (String selectFieldGlobPattern : selectedFieldGlobPatterns) {
+      expression.addParameter(selectFieldGlobPattern);
+    }
+
     // selected evaluators
     for (Map.Entry<StreamEvaluator, String> selectedEvaluator : 
selectedEvaluators.entrySet()) {
       expression.addParameter(
@@ -308,6 +327,13 @@ public class SelectStream extends TupleStream implements 
Expressible {
       workingForEvaluators.put(fieldName, original.get(fieldName));
       if (selectedFields.containsKey(fieldName)) {
         workingToReturn.put(selectedFields.get(fieldName), 
original.get(fieldName));
+      } else {
+        for (String globPattern : selectedFieldGlobPatterns) {
+          if (GlobPatternUtil.matches(globPattern, fieldName)) {
+            workingToReturn.put(fieldName, original.get(fieldName));
+            break;
+          }
+        }
       }
     }
 
diff --git 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionToExpessionTest.java
 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionToExpessionTest.java
index 4069b671b32..2c941f142d7 100644
--- 
a/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionToExpessionTest.java
+++ 
b/solr/solrj-streaming/src/test/org/apache/solr/client/solrj/io/stream/StreamExpressionToExpessionTest.java
@@ -105,7 +105,7 @@ public class StreamExpressionToExpessionTest extends 
SolrTestCase {
     try (SelectStream stream =
         new SelectStream(
             StreamExpressionParser.parse(
-                "select(\"a_s as fieldA\", search(collection1, q=*:*, 
fl=\"id,a_s,a_i,a_f\", sort=\"a_f asc, a_i asc\"))"),
+                "select(\"a_s as fieldA\", a_*, search(collection1, q=*:*, 
fl=\"id,a_s,a_i,a_f\", sort=\"a_f asc, a_i asc\"))"),
             factory)) {
       expressionString = stream.toExpression(factory).toString();
       assertTrue(expressionString.contains("select(search(collection1,"));
@@ -113,6 +113,7 @@ public class StreamExpressionToExpessionTest extends 
SolrTestCase {
       assertTrue(expressionString.contains("fl=\"id,a_s,a_i,a_f\""));
       assertTrue(expressionString.contains("sort=\"a_f asc, a_i asc\""));
       assertTrue(expressionString.contains("a_s as fieldA"));
+      assertTrue(expressionString.contains("a_*"));
     }
   }
 
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/util/GlobPatternUtil.java 
b/solr/solrj/src/java/org/apache/solr/common/util/GlobPatternUtil.java
new file mode 100644
index 00000000000..8b26ab5a355
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/common/util/GlobPatternUtil.java
@@ -0,0 +1,37 @@
+/*
+ * 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.common.util;
+
+import java.nio.file.FileSystems;
+import java.nio.file.Paths;
+
+/** Provides methods for matching glob patterns against input strings. */
+public class GlobPatternUtil {
+
+  /**
+   * Matches an input string against a provided glob patterns. This uses Java 
NIO FileSystems
+   * PathMatcher to match glob patterns in the same way to how glob patterns 
are matches for file
+   * paths, rather than implementing our own glob pattern matching.
+   *
+   * @param pattern the glob pattern to match against
+   * @param input the input string to match against a glob pattern
+   * @return true if the input string matches the glob pattern, false otherwise
+   */
+  public static boolean matches(String pattern, String input) {
+    return FileSystems.getDefault().getPathMatcher("glob:" + 
pattern).matches(Paths.get(input));
+  }
+}
diff --git 
a/solr/solrj/src/test/org/apache/solr/common/util/TestGlobPatternUtil.java 
b/solr/solrj/src/test/org/apache/solr/common/util/TestGlobPatternUtil.java
new file mode 100644
index 00000000000..a5bdcad92fa
--- /dev/null
+++ b/solr/solrj/src/test/org/apache/solr/common/util/TestGlobPatternUtil.java
@@ -0,0 +1,33 @@
+/*
+ * 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.common.util;
+
+import org.apache.solr.SolrTestCase;
+
+public class TestGlobPatternUtil extends SolrTestCase {
+
+  public void testMatches() {
+    assertTrue(GlobPatternUtil.matches("*_str", "user_str"));
+    assertFalse(GlobPatternUtil.matches("*_str", "str_user"));
+    assertTrue(GlobPatternUtil.matches("str_*", "str_user"));
+    assertFalse(GlobPatternUtil.matches("str_*", "user_str"));
+    assertTrue(GlobPatternUtil.matches("str?", "str1"));
+    assertFalse(GlobPatternUtil.matches("str?", "str_user"));
+    assertTrue(GlobPatternUtil.matches("user_*_str", "user_type_str"));
+    assertFalse(GlobPatternUtil.matches("user_*_str", "user_str"));
+  }
+}

Reply via email to