Skip to content

Commit

Permalink
fix: add 'CONTAIN' support in query builder
Browse files Browse the repository at this point in the history
This fix to address the issue #114
  • Loading branch information
yangyangv2 committed Jul 27, 2021
1 parent 77df5d8 commit 200ebfe
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public static Map<String, String> getRequestMap(@Nullable Filter requestParams)
* <p>If the condition between a field and value is not the same as EQUAL, a Range query is constructed. This
* condition does not support multiple values for the same field.
*
* <p>When CONTAIN, START_WITH and END_WITH conditions are used, the underlying logic is using wildcard query which is
* not performant according to ES. For details, please refer to:
* https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-wildcard-query.html#wildcard-query-field-params
*
* @param criterion {@link Criterion} single criterion which contains field, value and a comparison operator
*/
@Nonnull
Expand All @@ -78,6 +82,15 @@ public static QueryBuilder getQueryBuilderFromCriterion(@Nonnull Criterion crite
return QueryBuilders.rangeQuery(criterion.getField()).lt(criterion.getValue().trim());
} else if (condition == Condition.LESS_THAN_OR_EQUAL_TO) {
return QueryBuilders.rangeQuery(criterion.getField()).lte(criterion.getValue().trim());
} else if (condition == Condition.CONTAIN) {
return QueryBuilders.wildcardQuery(criterion.getField(),
"*" + ESUtils.escapeReservedCharacters(criterion.getValue().trim()) + "*");
} else if (condition == Condition.START_WITH) {
return QueryBuilders.wildcardQuery(criterion.getField(),
ESUtils.escapeReservedCharacters(criterion.getValue().trim()) + "*");
} else if (condition == Condition.END_WITH) {
return QueryBuilders.wildcardQuery(criterion.getField(),
"*" + ESUtils.escapeReservedCharacters(criterion.getValue().trim()));
}

throw new UnsupportedOperationException("Unsupported condition: " + condition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public void testFilteredQueryUnsupportedCondition() {
int from = 0;
int size = 10;
final Filter filter2 = new Filter().setCriteria(new CriterionArray(Arrays.asList(
new Criterion().setField("field_contain").setValue("value_contain").setCondition(Condition.CONTAIN))));
new Criterion().setField("field_contain").setValue("value_contain").setCondition(Condition.IN))));
SortCriterion sortCriterion = new SortCriterion().setOrder(SortOrder.ASCENDING).setField("urn");
assertThrows(UnsupportedOperationException.class,
() -> _searchDAO.getFilteredSearchQuery(filter2, sortCriterion, from, size));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.WildcardQueryBuilder;
import org.testng.annotations.Test;

import static com.linkedin.metadata.dao.utils.SearchUtils.*;
Expand Down Expand Up @@ -37,4 +39,62 @@ public void testGetRequestMap() {
));
assertThrows(UnsupportedOperationException.class, () -> getRequestMap(filter3));
}


@Test
public void testGetQueryBuilderFromContainCriterion() {

// Given: a 'contain' criterion
Criterion containCriterion = new Criterion();
containCriterion.setValue("match * text");
containCriterion.setCondition(Condition.CONTAIN);
containCriterion.setField("text");

// Expect 'contain' criterion creates a MatchQueryBuilder
QueryBuilder queryBuilder = SearchUtils.getQueryBuilderFromCriterion(containCriterion);
assertNotNull(queryBuilder);
assertTrue(queryBuilder instanceof WildcardQueryBuilder);

// Expect 'field name' and search terms
assertEquals(((WildcardQueryBuilder) queryBuilder).fieldName(), "text");
assertEquals(((WildcardQueryBuilder) queryBuilder).value(), "*match \\* text*");
}

@Test
public void testGetQueryBuilderFromStartWithCriterion() {

// Given: a 'start_with' criterion
Criterion containCriterion = new Criterion();
containCriterion.setValue("match * text");
containCriterion.setCondition(Condition.START_WITH);
containCriterion.setField("text");

// Expect 'start_with' criterion creates a WildcardQueryBuilder
QueryBuilder queryBuilder = SearchUtils.getQueryBuilderFromCriterion(containCriterion);
assertNotNull(queryBuilder);
assertTrue(queryBuilder instanceof WildcardQueryBuilder);

// Expect 'field name' and search terms
assertEquals(((WildcardQueryBuilder) queryBuilder).fieldName(), "text");
assertEquals(((WildcardQueryBuilder) queryBuilder).value(), "match \\* text*");
}

@Test
public void testGetQueryBuilderFromEndWithCriterion() {

// Given: a 'end_with' criterion
Criterion containCriterion = new Criterion();
containCriterion.setValue("match * text");
containCriterion.setCondition(Condition.END_WITH);
containCriterion.setField("text");

// Expect 'end_with' criterion creates a MatchQueryBuilder
QueryBuilder queryBuilder = SearchUtils.getQueryBuilderFromCriterion(containCriterion);
assertNotNull(queryBuilder);
assertTrue(queryBuilder instanceof WildcardQueryBuilder);

// Expect 'field name' and search terms
assertEquals(((WildcardQueryBuilder) queryBuilder).fieldName(), "text");
assertEquals(((WildcardQueryBuilder) queryBuilder).value(), "*match \\* text");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ static boolean isUrn(@Nonnull String value) {
* <p>If the condition between a field and value is not the same as EQUAL, a Range query is constructed. This
* condition does not support multiple values for the same field.
*
* <p>When CONTAIN, START_WITH and END_WITH conditions are used, the underlying logic is using wildcard query which is
* not performant according to ES. For details, please refer to:
* https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-wildcard-query.html#wildcard-query-field-params
*
* @param criterion {@link Criterion} single criterion which contains field, value and a comparison operator
*/
@Nonnull
Expand All @@ -88,8 +92,16 @@ public static QueryBuilder getQueryBuilderFromCriterion(@Nonnull Criterion crite
return QueryBuilders.rangeQuery(criterion.getField()).lt(criterion.getValue().trim());
} else if (condition == Condition.LESS_THAN_OR_EQUAL_TO) {
return QueryBuilders.rangeQuery(criterion.getField()).lte(criterion.getValue().trim());
} else if (condition == Condition.CONTAIN) {
return QueryBuilders.wildcardQuery(criterion.getField(),
"*" + ESUtils.escapeReservedCharacters(criterion.getValue().trim()) + "*");
} else if (condition == Condition.START_WITH) {
return QueryBuilders.wildcardQuery(criterion.getField(),
ESUtils.escapeReservedCharacters(criterion.getValue().trim()) + "*");
} else if (condition == Condition.END_WITH) {
return QueryBuilders.wildcardQuery(criterion.getField(),
"*" + ESUtils.escapeReservedCharacters(criterion.getValue().trim()));
}

throw new UnsupportedOperationException("Unsupported condition: " + condition);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public void testFilteredQueryUnsupportedCondition() {
int from = 0;
int size = 10;
final Filter filter2 = new Filter().setCriteria(new CriterionArray(Arrays.asList(
new Criterion().setField("field_contain").setValue("value_contain").setCondition(Condition.CONTAIN))));
new Criterion().setField("field_contain").setValue("value_contain").setCondition(Condition.IN))));
SortCriterion sortCriterion = new SortCriterion().setOrder(SortOrder.ASCENDING).setField("urn");
assertThrows(UnsupportedOperationException.class,
() -> _searchDAO.getFilteredSearchQuery(filter2, sortCriterion, from, size));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.WildcardQueryBuilder;
import org.testng.annotations.Test;

import static com.linkedin.metadata.dao.utils.SearchUtils.*;
Expand Down Expand Up @@ -37,4 +39,61 @@ public void testGetRequestMap() {
));
assertThrows(UnsupportedOperationException.class, () -> getRequestMap(filter3));
}

@Test
public void testGetQueryBuilderFromContainCriterion() {

// Given: a 'contain' criterion
Criterion containCriterion = new Criterion();
containCriterion.setValue("match * text");
containCriterion.setCondition(Condition.CONTAIN);
containCriterion.setField("text");

// Expect 'contain' criterion creates a MatchQueryBuilder
QueryBuilder queryBuilder = SearchUtils.getQueryBuilderFromCriterion(containCriterion);
assertNotNull(queryBuilder);
assertTrue(queryBuilder instanceof WildcardQueryBuilder);

// Expect 'field name' and search terms
assertEquals(((WildcardQueryBuilder) queryBuilder).fieldName(), "text");
assertEquals(((WildcardQueryBuilder) queryBuilder).value(), "*match \\* text*");
}

@Test
public void testGetQueryBuilderFromStartWithCriterion() {

// Given: a 'start_with' criterion
Criterion containCriterion = new Criterion();
containCriterion.setValue("match * text");
containCriterion.setCondition(Condition.START_WITH);
containCriterion.setField("text");

// Expect 'start_with' criterion creates a WildcardQueryBuilder
QueryBuilder queryBuilder = SearchUtils.getQueryBuilderFromCriterion(containCriterion);
assertNotNull(queryBuilder);
assertTrue(queryBuilder instanceof WildcardQueryBuilder);

// Expect 'field name' and search terms
assertEquals(((WildcardQueryBuilder) queryBuilder).fieldName(), "text");
assertEquals(((WildcardQueryBuilder) queryBuilder).value(), "match \\* text*");
}

@Test
public void testGetQueryBuilderFromEndWithCriterion() {

// Given: a 'end_with' criterion
Criterion containCriterion = new Criterion();
containCriterion.setValue("match * text");
containCriterion.setCondition(Condition.END_WITH);
containCriterion.setField("text");

// Expect 'end_with' criterion creates a MatchQueryBuilder
QueryBuilder queryBuilder = SearchUtils.getQueryBuilderFromCriterion(containCriterion);
assertNotNull(queryBuilder);
assertTrue(queryBuilder instanceof WildcardQueryBuilder);

// Expect 'field name' and search terms
assertEquals(((WildcardQueryBuilder) queryBuilder).fieldName(), "text");
assertEquals(((WildcardQueryBuilder) queryBuilder).value(), "*match \\* text");
}
}

0 comments on commit 200ebfe

Please sign in to comment.