Skip to content

Commit

Permalink
feat(index): support wildcard query for simplified regex-like search...
Browse files Browse the repository at this point in the history
...experience
  • Loading branch information
cmark committed May 13, 2024
1 parent 32e0db2 commit b904b47
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2024 B2i Healthcare, https://b2ihealthcare.com
*
* Licensed 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 com.b2international.index;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Collection;

import org.elasticsearch.core.List;
import org.junit.Before;
import org.junit.Test;

import com.b2international.index.Fixtures.Data;
import com.b2international.index.query.Expressions;
import com.b2international.index.query.Query;

/**
* @since 9.2
*/
public class WildcardQueryTest extends BaseIndexTest {

@Override
protected Collection<Class<?>> getTypes() {
return List.of(Data.class);
}

@Before
public void before() {
Data data1 = new Data(KEY1);
data1.setField1("fixture term one");
Data data2 = new Data(KEY2);
data2.setField1("fixture term two [x]");
indexDocuments(data1, data2);
}

@Test
public void wildcardWithAnyCharacter() throws Exception {
Hits<Data> matches = search(Query.select(Data.class).where(Expressions.wildcard("field1", "fix*one")).build());
assertThat(matches).extracting(Data::getId).containsOnly(KEY1);
}

@Test
public void wildcardWithQuestionMark() throws Exception {
Hits<Data> matches = search(Query.select(Data.class).where(Expressions.wildcard("field1", "*o?e*")).build());
assertThat(matches).extracting(Data::getId).containsOnly(KEY1);
}

@Test
public void wildcardWithAnyOtherRegexlikeCharacter() throws Exception {
Hits<Data> matches = search(Query.select(Data.class).where(Expressions.wildcard("field1", "*[*")).build());
assertThat(matches).extracting(Data::getId).containsOnly(KEY2);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ private void visit(Expression expression) {
visit((PrefixPredicate) expression);
} else if (expression instanceof RegexpPredicate) {
visit((RegexpPredicate) expression);
} else if (expression instanceof WildcardPredicate) {
visit((WildcardPredicate) expression);
} else if (expression instanceof StringSetPredicate) {
visit((StringSetPredicate) expression);
} else if (expression instanceof LongSetPredicate) {
Expand Down Expand Up @@ -471,6 +473,10 @@ private void visit(RegexpPredicate regexp) {
deque.push(QueryBuilders.regexp(r -> r.boost(this.boost).field(toFieldPath(regexp)).value(regexp.getArgument()).caseInsensitive(regexp.isCaseInsensitive()).flags(RegexpFlag.NONE.name())));
}

private void visit(WildcardPredicate wildcard) {
deque.push(QueryBuilders.wildcard(r -> r.boost(this.boost).field(toFieldPath(wildcard)).value(wildcard.getArgument()).caseInsensitive(wildcard.isCaseInsensitive())));
}

private void visit(RangePredicate<?> range) {
deque.push(QueryBuilders.range(r -> {
r.boost(this.boost);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ private void visit(Expression expression) {
visit((PrefixPredicate) expression);
} else if (expression instanceof RegexpPredicate) {
visit((RegexpPredicate) expression);
} else if (expression instanceof WildcardPredicate) {
visit((WildcardPredicate) expression);
} else if (expression instanceof StringSetPredicate) {
visit((StringSetPredicate) expression);
} else if (expression instanceof LongSetPredicate) {
Expand Down Expand Up @@ -424,6 +426,10 @@ private void visit(RegexpPredicate regexp) {
deque.push(QueryBuilders.regexpQuery(toFieldPath(regexp), regexp.getArgument()).caseInsensitive(regexp.isCaseInsensitive()).flags(RegexpFlag.NONE));
}

private void visit(WildcardPredicate wildcard) {
deque.push(QueryBuilders.wildcardQuery(toFieldPath(wildcard), wildcard.getArgument()).caseInsensitive(wildcard.isCaseInsensitive()));
}

private void visit(RangePredicate<?> range) {
final Object lower = range.lower() instanceof BigDecimal ? DecimalUtils.encode((BigDecimal) range.lower()) : range.lower();
final Object upper = range.upper() instanceof BigDecimal ? DecimalUtils.encode((BigDecimal) range.upper()) : range.upper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ public static RegexpPredicate regexp(String field, String regexp, boolean caseIn
return new RegexpPredicate(field, regexp, caseInsensitive);
}

public static WildcardPredicate wildcard(String field, String wildcard) {
return wildcard(field, wildcard, false);
}

public static WildcardPredicate wildcard(String field, String wildcard, boolean caseInsensitive) {
return new WildcardPredicate(field, wildcard, caseInsensitive);
}

public static Expression dismaxWithScoreCategories(Expression...disjuncts) {
return dismaxWithScoreCategories(List.of(disjuncts));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2024 B2i Healthcare, https://b2ihealthcare.com
*
* Licensed 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 com.b2international.index.query;

/**
* @since 9.2.0
*/
public final class WildcardPredicate extends SingleArgumentPredicate<String> {

private final boolean caseInsensitive;

public WildcardPredicate(String field, String value, boolean caseInsensitive) {
super(field, value);
this.caseInsensitive = caseInsensitive;
}

public boolean isCaseInsensitive() {
return caseInsensitive;
}

@Override
public String toString() {
return String.format("%s = wild(%s)%s", getField(), getArgument(), isCaseInsensitive() ? "[ci]" : "[cs]");
}

}

0 comments on commit b904b47

Please sign in to comment.