Skip to content

Commit 33d7ee8

Browse files
Merge pull request #163 from Bit-Quill/dev-add-legacy-syntax-for-match-function-alternate
Add Match_query And Matchquery As Alternate Syntax for Match Function
2 parents 03f30e3 + 77fcacd commit 33d7ee8

File tree

7 files changed

+280
-10
lines changed

7 files changed

+280
-10
lines changed

core/src/main/java/org/opensearch/sql/expression/function/OpenSearchFunctions.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ public class OpenSearchFunctions {
2727
*/
2828
public void register(BuiltinFunctionRepository repository) {
2929
repository.register(match_bool_prefix());
30-
repository.register(match());
30+
repository.register(match(BuiltinFunctionName.MATCH));
31+
repository.register(match(BuiltinFunctionName.MATCHQUERY));
32+
repository.register(match(BuiltinFunctionName.MATCH_QUERY));
3133
repository.register(multi_match());
3234
repository.register(simple_query_string());
3335
repository.register(query());
@@ -44,8 +46,8 @@ private static FunctionResolver match_bool_prefix() {
4446
return new RelevanceFunctionResolver(name, STRING);
4547
}
4648

47-
private static FunctionResolver match() {
48-
FunctionName funcName = BuiltinFunctionName.MATCH.getName();
49+
private static FunctionResolver match(BuiltinFunctionName match) {
50+
FunctionName funcName = match.getName();
4951
return new RelevanceFunctionResolver(funcName, STRING);
5052
}
5153

docs/user/dql/functions.rst

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2730,6 +2730,99 @@ Another example to show how to set custom values for the optional parameters::
27302730
+------------+
27312731

27322732

2733+
MATCHQUERY
2734+
-----
2735+
2736+
Description
2737+
>>>>>>>>>>>
2738+
2739+
``matchquery(field_expression, query_expression[, option=<option_value>]*)``
2740+
2741+
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:
2742+
2743+
- analyzer
2744+
- auto_generate_synonyms_phrase
2745+
- fuzziness
2746+
- max_expansions
2747+
- prefix_length
2748+
- fuzzy_transpositions
2749+
- fuzzy_rewrite
2750+
- lenient
2751+
- operator
2752+
- minimum_should_match
2753+
- zero_terms_query
2754+
- boost
2755+
2756+
For backwards compatibility, matchquery is supported and mapped to the match query.
2757+
2758+
Example with only ``field`` and ``query`` expressions, and all other parameters are set default values::
2759+
2760+
os> SELECT lastname, address FROM accounts WHERE matchquery(address, 'Street');
2761+
fetched rows / total rows = 2/2
2762+
+------------+--------------------+
2763+
| lastname | address |
2764+
|------------+--------------------|
2765+
| Bond | 671 Bristol Street |
2766+
| Bates | 789 Madison Street |
2767+
+------------+--------------------+
2768+
2769+
Another example to show how to set custom values for the optional parameters::
2770+
2771+
os> SELECT lastname FROM accounts WHERE matchquery(firstname, 'Hattie', operator='AND', boost=2.0);
2772+
fetched rows / total rows = 1/1
2773+
+------------+
2774+
| lastname |
2775+
|------------|
2776+
| Bond |
2777+
+------------+
2778+
2779+
MATCH_QUERY
2780+
-----
2781+
2782+
Description
2783+
>>>>>>>>>>>
2784+
2785+
``match_query(field_expression, query_expression[, option=<option_value>]*)``
2786+
2787+
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:
2788+
2789+
- analyzer
2790+
- auto_generate_synonyms_phrase
2791+
- fuzziness
2792+
- max_expansions
2793+
- prefix_length
2794+
- fuzzy_transpositions
2795+
- fuzzy_rewrite
2796+
- lenient
2797+
- operator
2798+
- minimum_should_match
2799+
- zero_terms_query
2800+
- boost
2801+
2802+
For backwards compatibility, match_query is supported and mapped to the match query.
2803+
2804+
Example with only ``field`` and ``query`` expressions, and all other parameters are set default values::
2805+
2806+
os> SELECT lastname, address FROM accounts WHERE match_query(address, 'Street');
2807+
fetched rows / total rows = 2/2
2808+
+------------+--------------------+
2809+
| lastname | address |
2810+
|------------+--------------------|
2811+
| Bond | 671 Bristol Street |
2812+
| Bates | 789 Madison Street |
2813+
+------------+--------------------+
2814+
2815+
Another example to show how to set custom values for the optional parameters::
2816+
2817+
os> SELECT lastname FROM accounts WHERE match_query(firstname, 'Hattie', operator='AND', boost=2.0);
2818+
fetched rows / total rows = 1/1
2819+
+------------+
2820+
| lastname |
2821+
|------------|
2822+
| Bond |
2823+
+------------+
2824+
2825+
27332826
MATCH_PHRASE
27342827
------------
27352828

integ-test/src/test/java/org/opensearch/sql/sql/MatchIT.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,48 @@ public void match_in_having() throws IOException {
3535
verifySchema(result, schema("lastname", "text"));
3636
verifyDataRows(result, rows("Bates"));
3737
}
38+
39+
@Test
40+
public void matchquery_in_where() throws IOException {
41+
JSONObject result = executeJdbcRequest("SELECT firstname FROM " + TEST_INDEX_ACCOUNT + " WHERE matchquery(lastname, 'Bates')");
42+
verifySchema(result, schema("firstname", "text"));
43+
verifyDataRows(result, rows("Nanette"));
44+
}
45+
46+
@Test
47+
public void matchquery_in_having() throws IOException {
48+
JSONObject result = executeJdbcRequest("SELECT lastname FROM " + TEST_INDEX_ACCOUNT + " HAVING matchquery(firstname, 'Nanette')");
49+
verifySchema(result, schema("lastname", "text"));
50+
verifyDataRows(result, rows("Bates"));
51+
}
52+
53+
@Test
54+
public void match_query_in_where() throws IOException {
55+
JSONObject result = executeJdbcRequest("SELECT firstname FROM " + TEST_INDEX_ACCOUNT + " WHERE match_query(lastname, 'Bates')");
56+
verifySchema(result, schema("firstname", "text"));
57+
verifyDataRows(result, rows("Nanette"));
58+
}
59+
60+
@Test
61+
public void match_query_in_having() throws IOException {
62+
JSONObject result = executeJdbcRequest(
63+
"SELECT lastname FROM " + TEST_INDEX_ACCOUNT + " HAVING match_query(firstname, 'Nanette')");
64+
verifySchema(result, schema("lastname", "text"));
65+
verifyDataRows(result, rows("Bates"));
66+
}
67+
68+
@Test
69+
public void alternate_syntaxes_return_the_same_results() throws IOException {
70+
String query1 = "SELECT lastname FROM "
71+
+ TEST_INDEX_ACCOUNT + " HAVING match(firstname, 'Nanette')";
72+
JSONObject result1 = executeJdbcRequest(query1);
73+
String query2 = "SELECT lastname FROM "
74+
+ TEST_INDEX_ACCOUNT + " HAVING matchquery(firstname, 'Nanette')";
75+
JSONObject result2 = executeJdbcRequest(query2);
76+
String query3 = "SELECT lastname FROM "
77+
+ TEST_INDEX_ACCOUNT + " HAVING match_query(firstname, 'Nanette')";
78+
JSONObject result3 = executeJdbcRequest(query3);
79+
assertEquals(result1.getInt("total"), result2.getInt("total"));
80+
assertEquals(result1.getInt("total"), result3.getInt("total"));
81+
}
3882
}

opensearch/src/test/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/MatchQueryTest.java

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@
3232
public class MatchQueryTest {
3333
private final DSL dsl = new ExpressionConfig().dsl(new ExpressionConfig().functionRepository());
3434
private final MatchQuery matchQuery = new MatchQuery();
35-
private final FunctionName match = FunctionName.of("match");
35+
private final FunctionName matchName = FunctionName.of("match");
36+
private final FunctionName matchQueryName = FunctionName.of("matchquery");
37+
private final FunctionName matchQueryWithUnderscoreName = FunctionName.of("match_query");
38+
private final FunctionName[] functionNames =
39+
{matchName,matchQueryName, matchQueryWithUnderscoreName};
3640

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

146+
@ParameterizedTest
147+
@MethodSource("generateValidData")
148+
public void test_valid_parameters_matchquery_syntax(List<Expression> validArgs) {
149+
Assertions.assertNotNull(matchQuery.build(
150+
new MatchExpression(validArgs, MatchQueryTest.this.matchQueryName)));
151+
}
152+
153+
@Test
154+
public void test_SyntaxCheckException_when_no_arguments_matchquery_syntax() {
155+
List<Expression> arguments = List.of();
156+
assertThrows(SyntaxCheckException.class,
157+
() -> matchQuery.build(
158+
new MatchExpression(arguments, MatchQueryTest.this.matchQueryName)));
159+
}
160+
161+
@Test
162+
public void test_SyntaxCheckException_when_one_argument_matchquery_syntax() {
163+
List<Expression> arguments = List.of(namedArgument("field", "field_value"));
164+
assertThrows(SyntaxCheckException.class,
165+
() -> matchQuery.build(
166+
new MatchExpression(arguments, MatchQueryTest.this.matchQueryName)));
167+
}
168+
169+
@Test
170+
public void test_SemanticCheckException_when_invalid_parameter_matchquery_syntax() {
171+
List<Expression> arguments = List.of(
172+
namedArgument("field", "field_value"),
173+
namedArgument("query", "query_value"),
174+
namedArgument("unsupported", "unsupported_value"));
175+
Assertions.assertThrows(SemanticCheckException.class,
176+
() -> matchQuery.build(
177+
new MatchExpression(arguments, MatchQueryTest.this.matchQueryName)));
178+
}
179+
180+
@ParameterizedTest
181+
@MethodSource("generateValidData")
182+
public void test_valid_parameters_match_query_syntax(List<Expression> validArgs) {
183+
Assertions.assertNotNull(matchQuery.build(
184+
new MatchExpression(validArgs, MatchQueryTest.this.matchQueryWithUnderscoreName)));
185+
}
186+
187+
@Test
188+
public void test_SyntaxCheckException_when_no_arguments_match_query_syntax() {
189+
List<Expression> arguments = List.of();
190+
assertThrows(SyntaxCheckException.class,
191+
() -> matchQuery.build(
192+
new MatchExpression(arguments, MatchQueryTest.this.matchQueryWithUnderscoreName)));
193+
}
194+
195+
@Test
196+
public void test_SyntaxCheckException_when_one_argument_match_query_syntax() {
197+
List<Expression> arguments = List.of(namedArgument("field", "field_value"));
198+
assertThrows(SyntaxCheckException.class,
199+
() -> matchQuery.build(
200+
new MatchExpression(arguments, MatchQueryTest.this.matchQueryWithUnderscoreName)));
201+
}
202+
203+
@Test
204+
public void test_SemanticCheckException_when_invalid_parameter_match_query_syntax() {
205+
List<Expression> arguments = List.of(
206+
namedArgument("field", "field_value"),
207+
namedArgument("query", "query_value"),
208+
namedArgument("unsupported", "unsupported_value"));
209+
Assertions.assertThrows(SemanticCheckException.class,
210+
() -> matchQuery.build(
211+
new MatchExpression(arguments, MatchQueryTest.this.matchQueryWithUnderscoreName)));
212+
}
213+
214+
142215
private NamedArgumentExpression namedArgument(String name, String value) {
143216
return dsl.namedArgument(name, DSL.literal(value));
144217
}
145218

146219
private class MatchExpression extends FunctionExpression {
220+
147221
public MatchExpression(List<Expression> arguments) {
148-
super(MatchQueryTest.this.match, arguments);
222+
super(MatchQueryTest.this.matchName, arguments);
223+
}
224+
225+
public MatchExpression(List<Expression> arguments, FunctionName funcName) {
226+
super(funcName, arguments);
149227
}
150228

151229
@Override

sql/src/main/antlr/OpenSearchSQLParser.g4

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ systemFunctionName
425425
;
426426

427427
singleFieldRelevanceFunctionName
428-
: MATCH | MATCH_PHRASE | MATCHPHRASE
428+
: MATCH | MATCHQUERY | MATCH_QUERY
429+
| MATCH_PHRASE | MATCHPHRASE
429430
| MATCH_BOOL_PREFIX | MATCH_PHRASE_PREFIX
430431
;
431432

@@ -435,10 +436,6 @@ multiFieldRelevanceFunctionName
435436
| QUERY_STRING
436437
;
437438

438-
legacyRelevanceFunctionName
439-
: QUERY | MATCH_QUERY | MATCHQUERY
440-
;
441-
442439
functionArgs
443440
: (functionArg (COMMA functionArg)*)?
444441
;

sql/src/test/java/org/opensearch/sql/sql/antlr/SQLSyntaxParserTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,31 @@ public void can_parse_match_relevance_function() {
375375
assertNotNull(parser.parse("SELECT * FROM test WHERE match(`column`, \"this is a test\")"));
376376
assertNotNull(parser.parse("SELECT * FROM test WHERE match(`column`, 'this is a test')"));
377377
assertNotNull(parser.parse("SELECT * FROM test WHERE match(column, 100500)"));
378+
}
379+
380+
@Test
381+
public void can_parse_matchquery_relevance_function() {
382+
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(column, \"this is a test\")"));
383+
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(column, 'this is a test')"));
384+
assertNotNull(parser.parse(
385+
"SELECT * FROM test WHERE matchquery(`column`, \"this is a test\")"));
386+
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(`column`, 'this is a test')"));
387+
assertNotNull(parser.parse("SELECT * FROM test WHERE matchquery(column, 100500)"));
388+
}
378389

390+
@Test
391+
public void can_parse_match_query_relevance_function() {
392+
assertNotNull(parser.parse(
393+
"SELECT * FROM test WHERE match_query(column, \"this is a test\")"));
394+
assertNotNull(parser.parse("SELECT * FROM test WHERE match_query(column, 'this is a test')"));
395+
assertNotNull(parser.parse(
396+
"SELECT * FROM test WHERE match_query(`column`, \"this is a test\")"));
397+
assertNotNull(parser.parse("SELECT * FROM test WHERE match_query(`column`, 'this is a test')"));
398+
assertNotNull(parser.parse("SELECT * FROM test WHERE match_query(column, 100500)"));
399+
}
400+
401+
@Test
402+
public void can_parse_match_phrase_relevance_function() {
379403
assertNotNull(
380404
parser.parse("SELECT * FROM test WHERE match_phrase(column, \"this is a test\")"));
381405
assertNotNull(parser.parse("SELECT * FROM test WHERE match_phrase(column, 'this is a test')"));

sql/src/test/java/org/opensearch/sql/sql/parser/AstExpressionBuilderTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,38 @@ public void relevanceMatch() {
485485
buildExprAst("match('message', 'search query', analyzer='keyword', operator='AND')"));
486486
}
487487

488+
@Test
489+
public void relevanceMatchQuery() {
490+
assertEquals(AstDSL.function("matchquery",
491+
unresolvedArg("field", stringLiteral("message")),
492+
unresolvedArg("query", stringLiteral("search query"))),
493+
buildExprAst("matchquery('message', 'search query')")
494+
);
495+
496+
assertEquals(AstDSL.function("matchquery",
497+
unresolvedArg("field", stringLiteral("message")),
498+
unresolvedArg("query", stringLiteral("search query")),
499+
unresolvedArg("analyzer", stringLiteral("keyword")),
500+
unresolvedArg("operator", stringLiteral("AND"))),
501+
buildExprAst("matchquery('message', 'search query', analyzer='keyword', operator='AND')"));
502+
}
503+
504+
@Test
505+
public void relevanceMatch_Query() {
506+
assertEquals(AstDSL.function("match_query",
507+
unresolvedArg("field", stringLiteral("message")),
508+
unresolvedArg("query", stringLiteral("search query"))),
509+
buildExprAst("match_query('message', 'search query')")
510+
);
511+
512+
assertEquals(AstDSL.function("match_query",
513+
unresolvedArg("field", stringLiteral("message")),
514+
unresolvedArg("query", stringLiteral("search query")),
515+
unresolvedArg("analyzer", stringLiteral("keyword")),
516+
unresolvedArg("operator", stringLiteral("AND"))),
517+
buildExprAst("match_query('message', 'search query', analyzer='keyword', operator='AND')"));
518+
}
519+
488520
@Test
489521
public void relevanceMulti_match() {
490522
assertEquals(AstDSL.function("multi_match",

0 commit comments

Comments
 (0)