Skip to content

Commit 7998830

Browse files
Mappings parsing fix (#851) (#871)
* fixed mappings parsing when field name named "properties" exists in mappings Signed-off-by: Petar Dzepina <[email protected]> * message typo fix Signed-off-by: Petar Dzepina <[email protected]> --------- Signed-off-by: Petar Dzepina <[email protected]> (cherry picked from commit 6a5c041) Co-authored-by: Petar Dzepina <[email protected]>
1 parent 29eaff9 commit 7998830

File tree

2 files changed

+169
-41
lines changed

2 files changed

+169
-41
lines changed

alerting/src/main/kotlin/org/opensearch/alerting/util/DocLevelMonitorQueries.kt

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -160,38 +160,34 @@ class DocLevelMonitorQueries(private val client: Client, private val clusterServ
160160
) {
161161
// If node contains "properties" property then it is internal(non-leaf) node
162162
log.debug("Node in traverse: $node")
163-
if (node.containsKey(PROPERTIES)) {
164-
return traverseMappingsAndUpdate(node.get(PROPERTIES) as MutableMap<String, Any>, currentPath, processLeafFn, flattenPaths)
165-
} else {
166-
// newNodes will hold list of updated leaf properties
167-
var newNodes = ArrayList<Triple<String, String, Any>>(node.size)
168-
node.entries.forEach {
169-
// Compute full path relative to root
170-
val fullPath = if (currentPath.isEmpty()) it.key
171-
else "$currentPath.${it.key}"
172-
val nodeProps = it.value as MutableMap<String, Any>
173-
// If it has type property and type is not "nested" then this is a leaf
174-
if (nodeProps.containsKey(TYPE) && nodeProps[TYPE] != NESTED) {
175-
// At this point we know full path of node, so we add it to output array
176-
flattenPaths.add(fullPath)
177-
// Calls processLeafFn and gets old node name, new node name and new properties of node.
178-
// This is all information we need to update this node
179-
val (oldName, newName, props) = processLeafFn(it.key, it.value as MutableMap<String, Any>)
180-
newNodes.add(Triple(oldName, newName, props))
181-
} else {
182-
// Internal(non-leaf) node - visit children
183-
traverseMappingsAndUpdate(nodeProps[PROPERTIES] as MutableMap<String, Any>, fullPath, processLeafFn, flattenPaths)
184-
}
163+
// newNodes will hold list of updated leaf properties
164+
var newNodes = ArrayList<Triple<String, String, Any>>(node.size)
165+
node.entries.forEach {
166+
// Compute full path relative to root
167+
val fullPath = if (currentPath.isEmpty()) it.key
168+
else "$currentPath.${it.key}"
169+
val nodeProps = it.value as MutableMap<String, Any>
170+
// If it has type property and type is not "nested" then this is a leaf
171+
if (nodeProps.containsKey(TYPE) && nodeProps[TYPE] != NESTED) {
172+
// At this point we know full path of node, so we add it to output array
173+
flattenPaths.add(fullPath)
174+
// Calls processLeafFn and gets old node name, new node name and new properties of node.
175+
// This is all information we need to update this node
176+
val (oldName, newName, props) = processLeafFn(it.key, it.value as MutableMap<String, Any>)
177+
newNodes.add(Triple(oldName, newName, props))
178+
} else {
179+
// Internal(non-leaf) node - visit children
180+
traverseMappingsAndUpdate(nodeProps[PROPERTIES] as MutableMap<String, Any>, fullPath, processLeafFn, flattenPaths)
185181
}
186-
// Here we can update all processed leaves in tree
187-
newNodes.forEach {
188-
// If we renamed leaf, we have to remove it first
189-
if (it.first != it.second) {
190-
node.remove(it.first)
191-
}
192-
// Put new properties of leaf
193-
node.put(it.second, it.third)
182+
}
183+
// Here we can update all processed leaves in tree
184+
newNodes.forEach {
185+
// If we renamed leaf, we have to remove it first
186+
if (it.first != it.second) {
187+
node.remove(it.first)
194188
}
189+
// Put new properties of leaf
190+
node.put(it.second, it.third)
195191
}
196192
}
197193

alerting/src/test/kotlin/org/opensearch/alerting/MonitorDataSourcesIT.kt

Lines changed: 143 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,149 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
141141
Assert.assertEquals(acknowledgeAlertResponse.acknowledged.size, 1)
142142
}
143143

144+
fun `test mappings parsing`() {
145+
146+
val index1 = "index_123"
147+
val index2 = "index_456"
148+
val index3 = "index_789"
149+
val index4 = "index_012"
150+
val q1 = DocLevelQuery(query = "properties:\"abcd\"", name = "1")
151+
val q2 = DocLevelQuery(query = "type.properties:\"abcd\"", name = "2")
152+
val q3 = DocLevelQuery(query = "type.something.properties:\"abcd\"", name = "3")
153+
val q4 = DocLevelQuery(query = "type.something.properties.lastone:\"abcd\"", name = "4")
154+
155+
createIndex(index1, Settings.EMPTY)
156+
createIndex(index2, Settings.EMPTY)
157+
createIndex(index3, Settings.EMPTY)
158+
createIndex(index4, Settings.EMPTY)
159+
160+
val m1 = """{
161+
"properties": {
162+
"properties": {
163+
"type": "keyword"
164+
}
165+
}
166+
}
167+
""".trimIndent()
168+
client().admin().indices().putMapping(PutMappingRequest(index1).source(m1, XContentType.JSON)).get()
169+
170+
val m2 = """{
171+
"properties": {
172+
"type": {
173+
"properties": {
174+
"properties": { "type": "keyword" }
175+
}
176+
}
177+
}
178+
}
179+
""".trimIndent()
180+
client().admin().indices().putMapping(PutMappingRequest(index2).source(m2, XContentType.JSON)).get()
181+
182+
val m3 = """{
183+
"properties": {
184+
"type": {
185+
"properties": {
186+
"something": {
187+
"properties" : {
188+
"properties": { "type": "keyword" }
189+
}
190+
}
191+
}
192+
}
193+
}
194+
}
195+
""".trimIndent()
196+
client().admin().indices().putMapping(PutMappingRequest(index3).source(m3, XContentType.JSON)).get()
197+
198+
val m4 = """{
199+
"properties": {
200+
"type": {
201+
"properties": {
202+
"something": {
203+
"properties" : {
204+
"properties": {
205+
"properties": {
206+
"lastone": { "type": "keyword" }
207+
}
208+
}
209+
}
210+
}
211+
}
212+
}
213+
}
214+
}
215+
""".trimIndent()
216+
client().admin().indices().putMapping(PutMappingRequest(index4).source(m4, XContentType.JSON)).get()
217+
218+
val docLevelInput = DocLevelMonitorInput(
219+
"description",
220+
listOf(index1, index2, index3, index4),
221+
listOf(q1, q2, q3, q4)
222+
)
223+
val trigger = randomDocumentLevelTrigger(condition = ALWAYS_RUN)
224+
val customFindingsIndex = "custom_findings_index"
225+
val customFindingsIndexPattern = "custom_findings_index-1"
226+
val customQueryIndex = "custom_alerts_index"
227+
var monitor = randomDocumentLevelMonitor(
228+
inputs = listOf(docLevelInput),
229+
triggers = listOf(trigger),
230+
dataSources = DataSources(
231+
queryIndex = customQueryIndex,
232+
findingsIndex = customFindingsIndex,
233+
findingsIndexPattern = customFindingsIndexPattern
234+
)
235+
)
236+
val monitorResponse = createMonitor(monitor)
237+
238+
val testDoc1 = """{
239+
"properties": "abcd"
240+
}"""
241+
indexDoc(index1, "1", testDoc1)
242+
val testDoc2 = """{
243+
"type.properties": "abcd"
244+
}"""
245+
indexDoc(index2, "1", testDoc2)
246+
val testDoc3 = """{
247+
"type.something.properties": "abcd"
248+
}"""
249+
indexDoc(index3, "1", testDoc3)
250+
val testDoc4 = """{
251+
"type.something.properties.lastone": "abcd"
252+
}"""
253+
indexDoc(index4, "1", testDoc4)
254+
255+
assertFalse(monitorResponse?.id.isNullOrEmpty())
256+
monitor = monitorResponse!!.monitor
257+
val id = monitorResponse.id
258+
val executeMonitorResponse = executeMonitor(monitor, id, false)
259+
Assert.assertEquals(executeMonitorResponse!!.monitorRunResult.monitorName, monitor.name)
260+
Assert.assertEquals(executeMonitorResponse.monitorRunResult.triggerResults.size, 1)
261+
searchAlerts(id)
262+
val table = Table("asc", "id", null, 1, 0, "")
263+
var getAlertsResponse = client()
264+
.execute(AlertingActions.GET_ALERTS_ACTION_TYPE, GetAlertsRequest(table, "ALL", "ALL", null, null))
265+
.get()
266+
Assert.assertTrue(getAlertsResponse != null)
267+
Assert.assertTrue(getAlertsResponse.alerts.size == 1)
268+
val findings = searchFindings(id, customFindingsIndex)
269+
assertEquals("Findings saved for test monitor", 4, findings.size)
270+
}
271+
144272
fun `test execute monitor with custom query index`() {
145-
val docQuery1 = DocLevelQuery(query = "source.ip.v6.v1:12345", name = "3")
146-
val docQuery2 = DocLevelQuery(query = "source.ip.v6.v2:16645", name = "4")
147-
val docQuery3 = DocLevelQuery(query = "source.ip.v4.v0:120", name = "5")
148-
val docQuery4 = DocLevelQuery(query = "alias.some.fff:\"us-west-2\"", name = "6")
149-
val docQuery5 = DocLevelQuery(query = "message:\"This is an error from IAD region\"", name = "7")
150-
val docQuery6 = DocLevelQuery(query = "f1.type.f4:\"hello\"", name = "8")
151-
val docQuery7 = DocLevelQuery(query = "f1.type.f2.f3:\"world\"", name = "9")
152-
val docQuery8 = DocLevelQuery(query = "type:\"some type\"", name = "10")
273+
val q1 = DocLevelQuery(query = "source.ip.v6.v1:12345", name = "3")
274+
val q2 = DocLevelQuery(query = "source.ip.v6.v2:16645", name = "4")
275+
val q3 = DocLevelQuery(query = "source.ip.v4.v0:120", name = "5")
276+
val q4 = DocLevelQuery(query = "alias.some.fff:\"us-west-2\"", name = "6")
277+
val q5 = DocLevelQuery(query = "message:\"This is an error from IAD region\"", name = "7")
278+
val q6 = DocLevelQuery(query = "f1.type.f4:\"hello\"", name = "8")
279+
val q7 = DocLevelQuery(query = "f1.type.f2.f3:\"world\"", name = "9")
280+
val q8 = DocLevelQuery(query = "type:\"some type\"", name = "10")
281+
val q9 = DocLevelQuery(query = "properties:123", name = "11")
153282

154283
val docLevelInput = DocLevelMonitorInput(
155-
"description", listOf(index), listOf(docQuery1, docQuery2, docQuery3, docQuery4, docQuery5, docQuery6, docQuery7, docQuery8)
284+
"description",
285+
listOf(index),
286+
listOf(q1, q2, q3, q4, q5, q6, q7, q8, q9)
156287
)
157288
val trigger = randomDocumentLevelTrigger(condition = ALWAYS_RUN)
158289
val customFindingsIndex = "custom_findings_index"
@@ -180,7 +311,8 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
180311
"test_field.some_other_field" : "us-west-2",
181312
"f1.type.f2.f3" : "world",
182313
"f1.type.f4" : "hello",
183-
"type" : "some type"
314+
"type" : "some type",
315+
"properties": 123
184316
}"""
185317
indexDoc(index, "1", testDoc)
186318
client().admin().indices().putMapping(
@@ -207,7 +339,7 @@ class MonitorDataSourcesIT : AlertingSingleNodeTestCase() {
207339
val findings = searchFindings(id, customFindingsIndex)
208340
assertEquals("Findings saved for test monitor", 1, findings.size)
209341
assertTrue("Findings saved for test monitor", findings[0].relatedDocIds.contains("1"))
210-
assertEquals("Didn't match all 8 queries", 8, findings[0].docLevelQueries.size)
342+
assertEquals("Didn't match all 9 queries", 9, findings[0].docLevelQueries.size)
211343
}
212344

213345
fun `test execute monitor with non-flattened json doc as source`() {

0 commit comments

Comments
 (0)