Skip to content

Commit

Permalink
ISPN-6588 Query DSL 'like' queries should only accept % and _ as the …
Browse files Browse the repository at this point in the history
…wildcards
  • Loading branch information
anistor authored and tristantarrant committed May 11, 2016
1 parent 0eeea50 commit 7cc0f88
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 9 deletions.
Expand Up @@ -3093,4 +3093,36 @@ public void testTwoPhaseGroupingAndAggregationOnSameField() {
assertEquals(1L, list.get(2)[0]);
assertNull(list.get(2)[1]);
}

/**
* Test that 'like' accepts only % and _ as wildcards.
*/
@Test
public void testLuceneWildcardsAreEscaped() {
QueryFactory qf = getQueryFactory();

// use a true wildcard
Query q1 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("J%n").toBuilder()
.build();
assertEquals(1, q1.list().size());

// use an improper wildcard
Query q2 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("J*n").toBuilder()
.build();
assertEquals(0, q2.list().size());

// use a true wildcard
Query q3 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("Jo_n").toBuilder()
.build();
assertEquals(1, q3.list().size());

// use an improper wildcard
Query q4 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("Jo?n").toBuilder()
.build();
assertEquals(0, q4.list().size());
}
}
Expand Up @@ -93,6 +93,8 @@ private String jpaWildcardToRegex(String jpaPattern) {
break;

// regexp special characters need to be escaped
case '+':
case '*':
case '(':
case ')':
case '[':
Expand Down
@@ -0,0 +1,75 @@
package org.infinispan.objectfilter.impl.predicateindex;

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
* @author anistor@redhat.com
* @since 9.0
*/
public class RegexConditionTest {

@Test
public void testSingleWildcard() throws Exception {
RegexCondition regexCondition = new RegexCondition("a_b");
assertTrue(regexCondition.match("aXb"));
assertTrue(regexCondition.match("a_b"));
assertTrue(regexCondition.match("a%b"));

assertFalse(regexCondition.match("ab"));
assertFalse(regexCondition.match("aXXb"));
}

@Test
public void testMultipleWildcard() throws Exception {
RegexCondition regexCondition = new RegexCondition("a%b");
assertTrue(regexCondition.match("ab"));
assertTrue(regexCondition.match("aXb"));
assertTrue(regexCondition.match("aXYb"));
assertTrue(regexCondition.match("a_b"));
assertTrue(regexCondition.match("a%b"));

assertFalse(regexCondition.match("a"));
assertFalse(regexCondition.match("aX"));
assertFalse(regexCondition.match("a%"));
}

@Test
public void testPlusEscaping() throws Exception {
RegexCondition regexCondition = new RegexCondition("a%aZ+");
assertTrue(regexCondition.match("aaaZ+"));

assertFalse(regexCondition.match("aaa"));
assertFalse(regexCondition.match("aaaZ"));
assertFalse(regexCondition.match("aaaZZ"));
assertFalse(regexCondition.match("aaaZZZ"));
}

@Test
public void testAsteriskEscaping() throws Exception {
RegexCondition regexCondition = new RegexCondition("a%aZ*");
assertTrue(regexCondition.match("aaaZ*"));

assertFalse(regexCondition.match("aaa"));
assertFalse(regexCondition.match("aaaZ"));
assertFalse(regexCondition.match("aaaZZ"));
assertFalse(regexCondition.match("aaaZZZ"));
}

@Test
public void testGeneralMetacharEscaping() {
assertTrue(new RegexCondition("a%(b").match("aaa(b"));
assertTrue(new RegexCondition("a%)b").match("aaa)b"));
assertTrue(new RegexCondition("a%[b").match("aaa[b"));
assertTrue(new RegexCondition("a%]b").match("aaa]b"));
assertTrue(new RegexCondition("a%{b").match("aaa{b"));
assertTrue(new RegexCondition("a%}b").match("aaa}b"));
assertTrue(new RegexCondition("a%$b").match("aaa$b"));
assertTrue(new RegexCondition("a%^b").match("aaa^b"));
assertTrue(new RegexCondition("a%.b").match("aaa.b"));
assertTrue(new RegexCondition("a%|b").match("aaa|b"));
assertTrue(new RegexCondition("a%\\b").match("aaa\\b"));
}
}
Expand Up @@ -30,7 +30,6 @@
import org.jboss.logging.Logger;

import java.util.Map;
import java.util.regex.Pattern;

/**
* @author anistor@redhat.com
Expand All @@ -40,11 +39,8 @@ public final class LuceneQueryMaker implements Visitor<Query, Query> {

private static final Log log = Logger.getMessageLogger(Log.class, LuceneQueryMaker.class.getName());

private static final String LUCENE_SINGLE_CHARACTER_WILDCARD = "?";
private static final String LUCENE_MULTIPLE_CHARACTERS_WILDCARD = "*";

private static final Pattern MULTIPLE_CHARACTERS_WILDCARD_PATTERN = Pattern.compile("%");
private static final Pattern SINGLE_CHARACTER_WILDCARD_PATTERN = Pattern.compile("_");
private static final char LUCENE_SINGLE_CHARACTER_WILDCARD = '?';
private static final char LUCENE_MULTIPLE_CHARACTERS_WILDCARD = '*';

private final QueryContextBuilder queryContextBuilder;
private final HibernateSearchPropertyHelper propertyHelper;
Expand Down Expand Up @@ -217,10 +213,24 @@ public Query visit(LikeExpr likeExpr) {
PropertyValueExpr propertyValueExpr = (PropertyValueExpr) likeExpr.getChild();
String[] propertyPath = propertyValueExpr.getPropertyPath();
ensureNotAnalyzed(propertyPath);
String patternValue = MULTIPLE_CHARACTERS_WILDCARD_PATTERN.matcher(likeExpr.getPattern()).replaceAll(LUCENE_MULTIPLE_CHARACTERS_WILDCARD);
patternValue = SINGLE_CHARACTER_WILDCARD_PATTERN.matcher(patternValue).replaceAll(LUCENE_SINGLE_CHARACTER_WILDCARD);
StringBuilder lucenePattern = new StringBuilder(likeExpr.getPattern());
for (int i = 0; i < lucenePattern.length(); i++) {
switch (lucenePattern.charAt(i)) {
case LUCENE_MULTIPLE_CHARACTERS_WILDCARD:
case LUCENE_SINGLE_CHARACTER_WILDCARD:
lucenePattern.insert(i, '\\');
i++;
break;
case '%':
lucenePattern.setCharAt(i, LUCENE_MULTIPLE_CHARACTERS_WILDCARD);
break;
case '_':
lucenePattern.setCharAt(i, LUCENE_SINGLE_CHARACTER_WILDCARD);
break;
}
}
return applyFieldBridge(propertyPath, queryBuilder.keyword().wildcard().onField(StringHelper.join(propertyPath)))
.matching(patternValue).createQuery();
.matching(lucenePattern.toString()).createQuery();
}

@Override
Expand Down
Expand Up @@ -2901,4 +2901,35 @@ public void testTwoPhaseGroupingAndAggregationOnSameField() {
assertEquals(1L, list.get(2)[0]);
assertNull(list.get(2)[1]);
}

/**
* Test that 'like' accepts only % and _ as wildcards.
*/
public void testLuceneWildcardsAreEscaped() {
QueryFactory qf = getQueryFactory();

// use a true wildcard
Query q1 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("J%n").toBuilder()
.build();
assertEquals(1, q1.list().size());

// use an improper wildcard
Query q2 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("J*n").toBuilder()
.build();
assertEquals(0, q2.list().size());

// use a true wildcard
Query q3 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("Jo_n").toBuilder()
.build();
assertEquals(1, q3.list().size());

// use an improper wildcard
Query q4 = qf.from(getModelFactory().getUserImplClass())
.having("name").like("Jo?n").toBuilder()
.build();
assertEquals(0, q4.list().size());
}
}

0 comments on commit 7cc0f88

Please sign in to comment.