diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 6aaa76411a2..6b64f1f3d06 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -34,7 +34,11 @@ Bug Fixes * SOLR-9310: PeerSync fails on a node restart due to IndexFingerPrint mismatch (Pushkar Raste, noble) +Other Changes +---------------------- +* SOLR-9412: Add failOnMissingParams option to MacroExpander, add TestMacroExpander class. + (Jon Dorando, Christine Poerschke) ================== 6.2.0 ================== diff --git a/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java b/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java index ff8a19b18eb..7db4cb02e2f 100644 --- a/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java +++ b/solr/core/src/java/org/apache/solr/request/macro/MacroExpander.java @@ -34,10 +34,16 @@ public class MacroExpander { private String macroStart = MACRO_START; private char escape = '\\'; private int level; + private final boolean failOnMissingParams; public MacroExpander(Map orig) { + this(orig, false); + } + + public MacroExpander(Map orig, boolean failOnMissingParams) { this.orig = orig; + this.failOnMissingParams = failOnMissingParams; } public static Map expand(Map params) { @@ -163,8 +169,14 @@ public class MacroExpander { String replacement = replacementList!=null ? replacementList[0] : defVal; if (replacement != null) { String expandedReplacement = expand(replacement); + if (failOnMissingParams && expandedReplacement == null) { + return null; + } sb.append(expandedReplacement); } + else if (failOnMissingParams) { + return null; + } } catch (SyntaxError syntaxError) { // append the part we would have skipped diff --git a/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java b/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java new file mode 100644 index 00000000000..5b16a11e849 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/request/macro/TestMacroExpander.java @@ -0,0 +1,116 @@ +/* + * 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.request.macro; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.lucene.util.LuceneTestCase; +import org.junit.Test; + +/* + * Tests for the MacroExpander + */ +public class TestMacroExpander extends LuceneTestCase { + + @Test + public void testExamples() { + final Map testParams = new HashMap(); + final MacroExpander me; + // example behavior unaffected by absence or value of failOnMissingParams + if (random().nextBoolean()) { + me = new MacroExpander(testParams); + } else { + final boolean failOnMissingParams = random().nextBoolean(); + me = new MacroExpander(testParams, failOnMissingParams); + } + + //default examples: https://cwiki.apache.org/confluence/display/solr/Parameter+Substitution + // and http://yonik.com/solr-query-parameter-substitution/ + + //using params + String[] lowParams = {"50"}; + testParams.put("low",lowParams); + String[] highParams = {"100"}; + testParams.put("high",highParams); + + String testQuery = "q=popularity:[ ${low} TO ${high} ]"; + + assertEquals("q=popularity:[ 50 TO 100 ]", me.expand(testQuery)); + + //using default values + testQuery = "q=popularity:[ ${low:10} TO ${high:20} ]"; + assertEquals("q=popularity:[ 50 TO 100 ]", me.expand(testQuery)); + + testParams.clear(); + assertEquals("q=popularity:[ 10 TO 20 ]", me.expand(testQuery)); + + //multiple levels of substitutions + testQuery = "q=${pop_query}"; + String[] popQueryParams = {"${pop_field}:[ ${low} TO ${high} ] AND inStock:true"}; + String[] popFieldParams = {"popularity"}; + testParams.put("low",lowParams); + testParams.put("high",highParams); + testParams.put("pop_query",popQueryParams); + testParams.put("pop_field",popFieldParams); + + assertEquals("q=popularity:[ 50 TO 100 ] AND inStock:true", me.expand(testQuery)); + + //end default examples + } + + @Test + public void testOnMissingParams() { + final Map testParams = new HashMap(); + final MacroExpander meSkipOnMissingParams = new MacroExpander(testParams); + final MacroExpander meFailOnMissingParams = new MacroExpander(testParams, true); + + final String low = "50"; + final String high = "100"; + testParams.put("low", new String[]{ low }); + testParams.put("high", new String[]{ high }); + + final String testQuery = "q=popularity:[ ${low} TO ${high} ]"; + + //when params all present the expansion results match + final String expandedQuery = "q=popularity:[ "+low+" TO "+high+" ]"; + assertEquals(expandedQuery, meSkipOnMissingParams.expand(testQuery)); + assertEquals(expandedQuery, meFailOnMissingParams.expand(testQuery)); + + //when param(s) missing and have no default the expansion results differ + final String expandedLow; + final String expandedHigh; + if (random().nextBoolean()) { // keep low + expandedLow = low; + } else { + expandedLow = ""; + testParams.remove("low"); + } + if (random().nextBoolean()) { // keep high + expandedHigh = high; + } else { + expandedHigh = ""; + testParams.remove("high"); + } + assertEquals("q=popularity:[ "+expandedLow+" TO "+expandedHigh+" ]", + meSkipOnMissingParams.expand(testQuery)); + if (testParams.size() < 2) { // at least one of the two parameters missing + assertEquals(null, meFailOnMissingParams.expand(testQuery)); + } + } + +}