Skip to content

Commit

Permalink
Merge pull request #163 from Bit-Quill/dev-add-legacy-syntax-for-matc…
Browse files Browse the repository at this point in the history
…h-function-alternate

Add Match_query And Matchquery As Alternate Syntax for Match Function
  • Loading branch information
GabeFernandez310 authored Nov 18, 2022
2 parents 03f30e3 + 77fcacd commit 33d7ee8
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ public class OpenSearchFunctions {
*/
public void register(BuiltinFunctionRepository repository) {
repository.register(match_bool_prefix());
repository.register(match());
repository.register(match(BuiltinFunctionName.MATCH));
repository.register(match(BuiltinFunctionName.MATCHQUERY));
repository.register(match(BuiltinFunctionName.MATCH_QUERY));
repository.register(multi_match());
repository.register(simple_query_string());
repository.register(query());
Expand All @@ -44,8 +46,8 @@ private static FunctionResolver match_bool_prefix() {
return new RelevanceFunctionResolver(name, STRING);
}

private static FunctionResolver match() {
FunctionName funcName = BuiltinFunctionName.MATCH.getName();
private static FunctionResolver match(BuiltinFunctionName match) {
FunctionName funcName = match.getName();
return new RelevanceFunctionResolver(funcName, STRING);
}

Expand Down
93 changes: 93 additions & 0 deletions docs/user/dql/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2730,6 +2730,99 @@ Another example to show how to set custom values for the optional parameters::
+------------+


MATCHQUERY
-----

Description
>>>>>>>>>>>

``matchquery(field_expression, query_expression[, option=<option_value>]*)``

The matchquery function maps to the match query used in search engine, to return the documents that match a provided text, number, date or boolean value with a given field. This is alternate syntax for the `match`_ function. Available parameters include:

- analyzer
- auto_generate_synonyms_phrase
- fuzziness
- max_expansions
- prefix_length
- fuzzy_transpositions
- fuzzy_rewrite
- lenient
- operator
- minimum_should_match
- zero_terms_query
- boost

For backwards compatibility, matchquery is supported and mapped to the match query.

Example with only ``field`` and ``query`` expressions, and all other parameters are set default values::

os> SELECT lastname, address FROM accounts WHERE matchquery(address, 'Street');
fetched rows / total rows = 2/2
+------------+--------------------+
| lastname | address |
|------------+--------------------|
| Bond | 671 Bristol Street |
| Bates | 789 Madison Street |
+------------+--------------------+

Another example to show how to set custom values for the optional parameters::

os> SELECT lastname FROM accounts WHERE matchquery(firstname, 'Hattie', operator='AND', boost=2.0);
fetched rows / total rows = 1/1
+------------+
| lastname |
|------------|
| Bond |
+------------+

MATCH_QUERY
-----

Description
>>>>>>>>>>>

``match_query(field_expression, query_expression[, option=<option_value>]*)``

The match_query function maps to the match query used in search engine, to return the documents that match_query a provided text, number, date or boolean value with a given field. This is alternate syntax for the `match`_ function. Available parameters include:

- analyzer
- auto_generate_synonyms_phrase
- fuzziness
- max_expansions
- prefix_length
- fuzzy_transpositions
- fuzzy_rewrite
- lenient
- operator
- minimum_should_match
- zero_terms_query
- boost

For backwards compatibility, match_query is supported and mapped to the match query.

Example with only ``field`` and ``query`` expressions, and all other parameters are set default values::

os> SELECT lastname, address FROM accounts WHERE match_query(address, 'Street');
fetched rows / total rows = 2/2
+------------+--------------------+
| lastname | address |
|------------+--------------------|
| Bond | 671 Bristol Street |
| Bates | 789 Madison Street |
+------------+--------------------+

Another example to show how to set custom values for the optional parameters::

os> SELECT lastname FROM accounts WHERE match_query(firstname, 'Hattie', operator='AND', boost=2.0);
fetched rows / total rows = 1/1
+------------+
| lastname |
|------------|
| Bond |
+------------+


MATCH_PHRASE
------------

Expand Down
44 changes: 44 additions & 0 deletions integ-test/src/test/java/org/opensearch/sql/sql/MatchIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,48 @@ public void match_in_having() throws IOException {
verifySchema(result, schema("lastname", "text"));
verifyDataRows(result, rows("Bates"));
}

@Test
public void matchquery_in_where() throws IOException {
JSONObject result = executeJdbcRequest("SELECT firstname FROM " + TEST_INDEX_ACCOUNT + " WHERE matchquery(lastname, 'Bates')");
verifySchema(result, schema("firstname", "text"));
verifyDataRows(result, rows("Nanette"));
}

@Test
public void matchquery_in_having() throws IOException {
JSONObject result = executeJdbcRequest("SELECT lastname FROM " + TEST_INDEX_ACCOUNT + " HAVING matchquery(firstname, 'Nanette')");
verifySchema(result, schema("lastname", "text"));
verifyDataRows(result, rows("Bates"));
}

@Test
public void match_query_in_where() throws IOException {
JSONObject result = executeJdbcRequest("SELECT firstname FROM " + TEST_INDEX_ACCOUNT + " WHERE match_query(lastname, 'Bates')");
verifySchema(result, schema("firstname", "text"));
verifyDataRows(result, rows("Nanette"));
}

@Test
public void match_query_in_having() throws IOException {
JSONObject result = executeJdbcRequest(
"SELECT lastname FROM " + TEST_INDEX_ACCOUNT + " HAVING match_query(firstname, 'Nanette')");
verifySchema(result, schema("lastname", "text"));
verifyDataRows(result, rows("Bates"));
}

@Test
public void alternate_syntaxes_return_the_same_results() throws IOException {
String query1 = "SELECT lastname FROM "
+ TEST_INDEX_ACCOUNT + " HAVING match(firstname, 'Nanette')";
JSONObject result1 = executeJdbcRequest(query1);
String query2 = "SELECT lastname FROM "
+ TEST_INDEX_ACCOUNT + " HAVING matchquery(firstname, 'Nanette')";
JSONObject result2 = executeJdbcRequest(query2);
String query3 = "SELECT lastname FROM "
+ TEST_INDEX_ACCOUNT + " HAVING match_query(firstname, 'Nanette')";
JSONObject result3 = executeJdbcRequest(query3);
assertEquals(result1.getInt("total"), result2.getInt("total"));
assertEquals(result1.getInt("total"), result3.getInt("total"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@
public class MatchQueryTest {
private final DSL dsl = new ExpressionConfig().dsl(new ExpressionConfig().functionRepository());
private final MatchQuery matchQuery = new MatchQuery();
private final FunctionName match = FunctionName.of("match");
private final FunctionName matchName = FunctionName.of("match");
private final FunctionName matchQueryName = FunctionName.of("matchquery");
private final FunctionName matchQueryWithUnderscoreName = FunctionName.of("match_query");
private final FunctionName[] functionNames =
{matchName,matchQueryName, matchQueryWithUnderscoreName};

static Stream<List<Expression>> generateValidData() {
final DSL dsl = new ExpressionConfig().dsl(new ExpressionConfig().functionRepository());
Expand Down Expand Up @@ -139,13 +143,87 @@ public void test_SemanticCheckException_when_invalid_parameter() {
() -> matchQuery.build(new MatchExpression(arguments)));
}

@ParameterizedTest
@MethodSource("generateValidData")
public void test_valid_parameters_matchquery_syntax(List<Expression> validArgs) {
Assertions.assertNotNull(matchQuery.build(
new MatchExpression(validArgs, MatchQueryTest.this.matchQueryName)));
}

@Test
public void test_SyntaxCheckException_when_no_arguments_matchquery_syntax() {
List<Expression> arguments = List.of();
assertThrows(SyntaxCheckException.class,
() -> matchQuery.build(
new MatchExpression(arguments, MatchQueryTest.this.matchQueryName)));
}

@Test
public void test_SyntaxCheckException_when_one_argument_matchquery_syntax() {
List<Expression> arguments = List.of(namedArgument("field", "field_value"));
assertThrows(SyntaxCheckException.class,
() -> matchQuery.build(
new MatchExpression(arguments, MatchQueryTest.this.matchQueryName)));
}

@Test
public void test_SemanticCheckException_when_invalid_parameter_matchquery_syntax() {
List<Expression> arguments = List.of(
namedArgument("field", "field_value"),
namedArgument("query", "query_value"),
namedArgument("unsupported", "unsupported_value"));
Assertions.assertThrows(SemanticCheckException.class,
() -> matchQuery.build(
new MatchExpression(arguments, MatchQueryTest.this.matchQueryName)));
}

@ParameterizedTest
@MethodSource("generateValidData")
public void test_valid_parameters_match_query_syntax(List<Expression> validArgs) {
Assertions.assertNotNull(matchQuery.build(
new MatchExpression(validArgs, MatchQueryTest.this.matchQueryWithUnderscoreName)));
}

@Test
public void test_SyntaxCheckException_when_no_arguments_match_query_syntax() {
List<Expression> arguments = List.of();
assertThrows(SyntaxCheckException.class,
() -> matchQuery.build(
new MatchExpression(arguments, MatchQueryTest.this.matchQueryWithUnderscoreName)));
}

@Test
public void test_SyntaxCheckException_when_one_argument_match_query_syntax() {
List<Expression> arguments = List.of(namedArgument("field", "field_value"));
assertThrows(SyntaxCheckException.class,
() -> matchQuery.build(
new MatchExpression(arguments, MatchQueryTest.this.matchQueryWithUnderscoreName)));
}

@Test
public void test_SemanticCheckException_when_invalid_parameter_match_query_syntax() {
List<Expression> arguments = List.of(
namedArgument("field", "field_value"),
namedArgument("query", "query_value"),
namedArgument("unsupported", "unsupported_value"));
Assertions.assertThrows(SemanticCheckException.class,
() -> matchQuery.build(
new MatchExpression(arguments, MatchQueryTest.this.matchQueryWithUnderscoreName)));
}


private NamedArgumentExpression namedArgument(String name, String value) {
return dsl.namedArgument(name, DSL.literal(value));
}

private class MatchExpression extends FunctionExpression {

public MatchExpression(List<Expression> arguments) {
super(MatchQueryTest.this.match, arguments);
super(MatchQueryTest.this.matchName, arguments);
}

public MatchExpression(List<Expression> arguments, FunctionName funcName) {
super(funcName, arguments);
}

@Override
Expand Down
7 changes: 2 additions & 5 deletions sql/src/main/antlr/OpenSearchSQLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,8 @@ systemFunctionName
;

singleFieldRelevanceFunctionName
: MATCH | MATCH_PHRASE | MATCHPHRASE
: MATCH | MATCHQUERY | MATCH_QUERY
| MATCH_PHRASE | MATCHPHRASE
| MATCH_BOOL_PREFIX | MATCH_PHRASE_PREFIX
;

Expand All @@ -435,10 +436,6 @@ multiFieldRelevanceFunctionName
| QUERY_STRING
;

legacyRelevanceFunctionName
: QUERY | MATCH_QUERY | MATCHQUERY
;

functionArgs
: (functionArg (COMMA functionArg)*)?
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,31 @@ public void can_parse_match_relevance_function() {
assertNotNull(parser.parse("SELECT * FROM test WHERE match(`column`, \"this is a test\")"));
assertNotNull(parser.parse("SELECT * FROM test WHERE match(`column`, 'this is a test')"));
assertNotNull(parser.parse("SELECT * FROM test WHERE match(column, 100500)"));
}

@Test
public void can_parse_matchquery_relevance_function() {
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(column, \"this is a test\")"));
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(column, 'this is a test')"));
assertNotNull(parser.parse(
"SELECT * FROM test WHERE matchquery(`column`, \"this is a test\")"));
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(`column`, 'this is a test')"));
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(column, 100500)"));
}

@Test
public void can_parse_match_query_relevance_function() {
assertNotNull(parser.parse(
"SELECT * FROM test WHERE match_query(column, \"this is a test\")"));
assertNotNull(parser.parse("SELECT * FROM test WHERE match_query(column, 'this is a test')"));
assertNotNull(parser.parse(
"SELECT * FROM test WHERE match_query(`column`, \"this is a test\")"));
assertNotNull(parser.parse("SELECT * FROM test WHERE match_query(`column`, 'this is a test')"));
assertNotNull(parser.parse("SELECT * FROM test WHERE match_query(column, 100500)"));
}

@Test
public void can_parse_match_phrase_relevance_function() {
assertNotNull(
parser.parse("SELECT * FROM test WHERE match_phrase(column, \"this is a test\")"));
assertNotNull(parser.parse("SELECT * FROM test WHERE match_phrase(column, 'this is a test')"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,38 @@ public void relevanceMatch() {
buildExprAst("match('message', 'search query', analyzer='keyword', operator='AND')"));
}

@Test
public void relevanceMatchQuery() {
assertEquals(AstDSL.function("matchquery",
unresolvedArg("field", stringLiteral("message")),
unresolvedArg("query", stringLiteral("search query"))),
buildExprAst("matchquery('message', 'search query')")
);

assertEquals(AstDSL.function("matchquery",
unresolvedArg("field", stringLiteral("message")),
unresolvedArg("query", stringLiteral("search query")),
unresolvedArg("analyzer", stringLiteral("keyword")),
unresolvedArg("operator", stringLiteral("AND"))),
buildExprAst("matchquery('message', 'search query', analyzer='keyword', operator='AND')"));
}

@Test
public void relevanceMatch_Query() {
assertEquals(AstDSL.function("match_query",
unresolvedArg("field", stringLiteral("message")),
unresolvedArg("query", stringLiteral("search query"))),
buildExprAst("match_query('message', 'search query')")
);

assertEquals(AstDSL.function("match_query",
unresolvedArg("field", stringLiteral("message")),
unresolvedArg("query", stringLiteral("search query")),
unresolvedArg("analyzer", stringLiteral("keyword")),
unresolvedArg("operator", stringLiteral("AND"))),
buildExprAst("match_query('message', 'search query', analyzer='keyword', operator='AND')"));
}

@Test
public void relevanceMulti_match() {
assertEquals(AstDSL.function("multi_match",
Expand Down

0 comments on commit 33d7ee8

Please sign in to comment.