From f14f3b994372331beecd963dd7d40e7c666a414f Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:28:03 -0800 Subject: [PATCH] add queryFieldNames field in Doc Level Queries (#582) (#597) * add queryFieldNames field in Doc Level Queries * add tests to verify queryFieldNames field in DocLevelQuery --------- (cherry picked from commit 75925dcdbc98e29c07e007676ea6c68ee7468dec) Signed-off-by: Surya Sashank Nistala Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] --- .../commons/alerting/model/DocLevelQuery.kt | 44 ++++++++++++++++--- .../commons/alerting/model/WriteableTests.kt | 25 +++++++++++ .../commons/alerting/model/XContentTests.kt | 24 ++++++++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelQuery.kt b/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelQuery.kt index 5d3749dd..dd361f24 100644 --- a/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelQuery.kt +++ b/src/main/kotlin/org/opensearch/commons/alerting/model/DocLevelQuery.kt @@ -8,14 +8,14 @@ import org.opensearch.core.xcontent.ToXContent import org.opensearch.core.xcontent.XContentBuilder import org.opensearch.core.xcontent.XContentParser import java.io.IOException -import java.lang.IllegalArgumentException import java.util.UUID data class DocLevelQuery( val id: String = UUID.randomUUID().toString(), val name: String, val query: String, - val tags: List = mutableListOf() + val tags: List = mutableListOf(), + val queryFieldNames: List = mutableListOf(), ) : BaseModel { init { @@ -31,7 +31,8 @@ data class DocLevelQuery( sin.readString(), // id sin.readString(), // name sin.readString(), // query - sin.readStringList() // tags + sin.readStringList(), // tags, + sin.readStringList() // fieldsBeingQueried ) fun asTemplateArg(): Map { @@ -39,7 +40,8 @@ data class DocLevelQuery( QUERY_ID_FIELD to id, NAME_FIELD to name, QUERY_FIELD to query, - TAGS_FIELD to tags + TAGS_FIELD to tags, + QUERY_FIELD_NAMES_FIELD to queryFieldNames ) } @@ -49,6 +51,7 @@ data class DocLevelQuery( out.writeString(name) out.writeString(query) out.writeStringCollection(tags) + out.writeStringCollection(queryFieldNames) } override fun toXContent(builder: XContentBuilder, params: ToXContent.Params): XContentBuilder { @@ -57,6 +60,7 @@ data class DocLevelQuery( .field(NAME_FIELD, name) .field(QUERY_FIELD, query) .field(TAGS_FIELD, tags.toTypedArray()) + .field(QUERY_FIELD_NAMES_FIELD, queryFieldNames.toTypedArray()) .endObject() return builder } @@ -66,6 +70,7 @@ data class DocLevelQuery( const val NAME_FIELD = "name" const val QUERY_FIELD = "query" const val TAGS_FIELD = "tags" + const val QUERY_FIELD_NAMES_FIELD = "query_field_names" const val NO_ID = "" val INVALID_CHARACTERS: List = listOf(" ", "[", "]", "{", "}", "(", ")") @@ -76,6 +81,7 @@ data class DocLevelQuery( lateinit var query: String lateinit var name: String val tags: MutableList = mutableListOf() + val queryFieldNames: MutableList = mutableListOf() XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, xcp.currentToken(), xcp) while (xcp.nextToken() != XContentParser.Token.END_OBJECT) { @@ -88,6 +94,7 @@ data class DocLevelQuery( name = xcp.text() validateQuery(name) } + QUERY_FIELD -> query = xcp.text() TAGS_FIELD -> { XContentParserUtils.ensureExpectedToken( @@ -101,6 +108,18 @@ data class DocLevelQuery( tags.add(tag) } } + + QUERY_FIELD_NAMES_FIELD -> { + XContentParserUtils.ensureExpectedToken( + XContentParser.Token.START_ARRAY, + xcp.currentToken(), + xcp + ) + while (xcp.nextToken() != XContentParser.Token.END_ARRAY) { + val field = xcp.text() + queryFieldNames.add(field) + } + } } } @@ -108,7 +127,8 @@ data class DocLevelQuery( id = id, name = name, query = query, - tags = tags + tags = tags, + queryFieldNames = queryFieldNames ) } @@ -129,4 +149,18 @@ data class DocLevelQuery( } } } + + // constructor for java plugins' convenience to optionally avoid passing empty list for 'fieldsBeingQueried' field + constructor( + id: String, + name: String, + query: String, + tags: MutableList, + ) : this( + id = id, + name = name, + query = query, + tags = tags, + queryFieldNames = emptyList() + ) } diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/WriteableTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/WriteableTests.kt index a3e83026..084aa3ca 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/model/WriteableTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/WriteableTests.kt @@ -11,6 +11,7 @@ import org.opensearch.commons.alerting.randomAction import org.opensearch.commons.alerting.randomActionExecutionPolicy import org.opensearch.commons.alerting.randomBucketLevelTrigger import org.opensearch.commons.alerting.randomChainedAlertTrigger +import org.opensearch.commons.alerting.randomDocLevelQuery import org.opensearch.commons.alerting.randomDocumentLevelTrigger import org.opensearch.commons.alerting.randomQueryLevelMonitor import org.opensearch.commons.alerting.randomQueryLevelTrigger @@ -19,6 +20,7 @@ import org.opensearch.commons.alerting.randomUser import org.opensearch.commons.alerting.randomUserEmpty import org.opensearch.commons.authuser.User import org.opensearch.search.builder.SearchSourceBuilder +import kotlin.test.assertTrue class WriteableTests { @@ -112,6 +114,29 @@ class WriteableTests { Assertions.assertEquals(trigger, newTrigger, "Round tripping DocumentLevelTrigger doesn't work") } + @Test + fun `test doc-level query as stream`() { + val dlq = randomDocLevelQuery() + val out = BytesStreamOutput() + dlq.writeTo(out) + val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) + val newDlq = DocLevelQuery.readFrom(sin) + Assertions.assertEquals(dlq, newDlq, "Round tripping DocLevelQuery doesn't work") + assertTrue(newDlq.queryFieldNames.isEmpty()) + } + + @Test + fun `test doc-level query with query Field Names as stream`() { + val dlq = randomDocLevelQuery().copy(queryFieldNames = listOf("f1", "f2")) + val out = BytesStreamOutput() + dlq.writeTo(out) + val sin = StreamInput.wrap(out.bytes().toBytesRef().bytes) + val newDlq = DocLevelQuery.readFrom(sin) + assertTrue(newDlq.queryFieldNames.contains(dlq.queryFieldNames[0])) + assertTrue(newDlq.queryFieldNames.contains(dlq.queryFieldNames[1])) + Assertions.assertEquals(dlq, newDlq, "Round tripping DocLevelQuery doesn't work") + } + @Test fun `test chained alert trigger as stream`() { val trigger = randomChainedAlertTrigger() diff --git a/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt b/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt index 67e16908..1c04d196 100644 --- a/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt +++ b/src/test/kotlin/org/opensearch/commons/alerting/model/XContentTests.kt @@ -393,6 +393,30 @@ class XContentTests { ) } + @Test + fun `test doc level query toXcontent`() { + val dlq = DocLevelQuery("id", "name", "query", listOf("t1", "t2")) + val dlqString = dlq.toXContent(builder(), ToXContent.EMPTY_PARAMS).string() + val parsedDlq = DocLevelQuery.parse(parser(dlqString)) + Assertions.assertEquals( + dlq, + parsedDlq, + "Round tripping Doc level query doesn't work" + ) + } + + @Test + fun `test doc level query toXcontent with query field names`() { + val dlq = DocLevelQuery("id", "name", "query", listOf("t1", "t2"), listOf("f1", "f2")) + val dlqString = dlq.toXContent(builder(), ToXContent.EMPTY_PARAMS).string() + val parsedDlq = DocLevelQuery.parse(parser(dlqString)) + Assertions.assertEquals( + dlq, + parsedDlq, + "Round tripping Doc level query doesn't work" + ) + } + @Test fun `test alert parsing`() { val alert = randomAlert()