From 760e377b1f03381845f9c4a210ae6c764d141515 Mon Sep 17 00:00:00 2001 From: Forest Vey <36905077+forestmvey@users.noreply.github.com> Date: Thu, 19 May 2022 12:42:44 -0700 Subject: [PATCH] Match Query Unit Tests (#614) Signed-off-by: forestmvey --- .../filter/lucene/relevance/MatchQuery.java | 3 + .../script/filter/lucene/MatchQueryTest.java | 157 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 opensearch/src/test/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/MatchQueryTest.java diff --git a/opensearch/src/main/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/relevance/MatchQuery.java b/opensearch/src/main/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/relevance/MatchQuery.java index bcdcc9f296..a1ed67cd9b 100644 --- a/opensearch/src/main/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/relevance/MatchQuery.java +++ b/opensearch/src/main/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/relevance/MatchQuery.java @@ -63,6 +63,9 @@ public class MatchQuery extends LuceneQuery { @Override public QueryBuilder build(FunctionExpression func) { + if (func.getArguments().size() < 2) { + throw new SemanticCheckException("match must have at least two arguments"); + } Iterator iterator = func.getArguments().iterator(); NamedArgumentExpression field = (NamedArgumentExpression) iterator.next(); NamedArgumentExpression query = (NamedArgumentExpression) iterator.next(); diff --git a/opensearch/src/test/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/MatchQueryTest.java b/opensearch/src/test/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/MatchQueryTest.java new file mode 100644 index 0000000000..7e3e5e0862 --- /dev/null +++ b/opensearch/src/test/java/org/opensearch/sql/opensearch/storage/script/filter/lucene/MatchQueryTest.java @@ -0,0 +1,157 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.opensearch.storage.script.filter.lucene; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.data.type.ExprType; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.DSL; +import org.opensearch.sql.expression.Expression; +import org.opensearch.sql.expression.FunctionExpression; +import org.opensearch.sql.expression.NamedArgumentExpression; +import org.opensearch.sql.expression.config.ExpressionConfig; +import org.opensearch.sql.expression.env.Environment; +import org.opensearch.sql.expression.function.FunctionName; +import org.opensearch.sql.opensearch.storage.script.filter.lucene.relevance.MatchQuery; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +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"); + + static Stream> generateValidData() { + final DSL dsl = new ExpressionConfig().dsl(new ExpressionConfig().functionRepository()); + return Stream.of( + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("analyzer", DSL.literal("standard")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("auto_generate_synonyms_phrase_query", DSL.literal("true")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("fuzziness", DSL.literal("AUTO")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("max_expansions", DSL.literal("50")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("prefix_length", DSL.literal("0")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("fuzzy_transpositions", DSL.literal("true")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("fuzzy_rewrite", DSL.literal("constant_score")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("lenient", DSL.literal("false")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("operator", DSL.literal("OR")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("minimum_should_match", DSL.literal("3")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("zero_terms_query", DSL.literal("NONE")) + ), + List.of( + dsl.namedArgument("field", DSL.literal("field_value")), + dsl.namedArgument("query", DSL.literal("query_value")), + dsl.namedArgument("boost", DSL.literal("1")) + ) + ); + } + + @ParameterizedTest + @MethodSource("generateValidData") + public void test_valid_parameters(List validArgs) { + Assertions.assertNotNull(matchQuery.build(new MatchExpression(validArgs))); + } + + @Test + public void test_SemanticCheckException_when_no_arguments() { + List arguments = List.of(); + assertThrows(SemanticCheckException.class, + () -> matchQuery.build(new MatchExpression(arguments))); + } + + @Test + public void test_SemanticCheckException_when_one_argument() { + List arguments = List.of(namedArgument("field", "field_value")); + assertThrows(SemanticCheckException.class, + () -> matchQuery.build(new MatchExpression(arguments))); + } + + @Test + public void test_SemanticCheckException_when_invalid_parameter() { + List arguments = List.of( + namedArgument("field", "field_value"), + namedArgument("query", "query_value"), + namedArgument("unsupported", "unsupported_value")); + Assertions.assertThrows(SemanticCheckException.class, + () -> matchQuery.build(new MatchExpression(arguments))); + } + + private NamedArgumentExpression namedArgument(String name, String value) { + return dsl.namedArgument(name, DSL.literal(value)); + } + + private class MatchExpression extends FunctionExpression { + public MatchExpression(List arguments) { + super(MatchQueryTest.this.match, arguments); + } + + @Override + public ExprValue valueOf(Environment valueEnv) { + throw new UnsupportedOperationException("Invalid function call, " + + "valueOf function need implementation only to support Expression interface"); + } + + @Override + public ExprType type() { + throw new UnsupportedOperationException("Invalid function call, " + + "type function need implementation only to support Expression interface"); + } + } +}