Skip to content

Commit 0c7981a

Browse files
authored
Merge pull request #837 from Iterable/bugfix/MOB-9672-Nested-JSON-with-Array-Criteria-Matching-did-not-work
Add support for Nested JSON with Array Criteria Match
2 parents 445c893 + ac62b9e commit 0c7981a

File tree

2 files changed

+225
-7
lines changed

2 files changed

+225
-7
lines changed

swift-sdk/Internal/AnonymousUserManager+Functions.swift

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -377,23 +377,49 @@ struct CriteriaCompletionChecker {
377377
} else {
378378
doesKeyExist = filteredLocalDataKeys.filter {$0 as! String == field }.count > 0
379379
}
380-
380+
381381
if field.contains(".") {
382-
if let firstKey = field.components(separatedBy: ".").first, let dataArray = eventData[firstKey] as? [Any], let eventType = query[JsonKey.eventType] as? String {
383-
return dataArray.contains { item in
384-
let dataItem: [String: Any] = [ firstKey: item,
385-
JsonKey.eventType: eventType ]
386-
return evaluateFieldLogic(searchQueries: searchQueries, eventData: dataItem)
382+
var fields = field.split(separator: ".").map { String($0) }
383+
if let type = eventData[JsonKey.eventType] as? String, let name = eventData[JsonKey.eventName] as? String, type == EventType.customEvent, name == fields.first {
384+
fields = Array(fields.dropFirst())
385+
}
386+
387+
var fieldValue: Any = eventData
388+
var isSubFieldArray = false
389+
var isSubMatch = false
390+
391+
for subField in fields {
392+
if let subFieldValue = (fieldValue as? [String: Any])?[subField] {
393+
if let arrayValue = subFieldValue as? [[String: Any]] {
394+
isSubFieldArray = true
395+
isSubMatch = arrayValue.contains { item in
396+
let data = fields.reversed().reduce([String: Any]()) { acc, key in
397+
if key == subField {
398+
return [key: item]
399+
}
400+
return [key: acc]
401+
}
402+
return evaluateFieldLogic(searchQueries: searchQueries, eventData: eventData.merging(data) { $1 })
403+
}
404+
} else {
405+
fieldValue = subFieldValue
406+
}
387407
}
388408
}
389-
if let valueFromObj = getFieldValue(data: eventData, field: field), let comparatorType = query[JsonKey.CriteriaItem.comparatorType] as? String {
409+
410+
if isSubFieldArray {
411+
return isSubMatch
412+
}
413+
414+
if let valueFromObj = getFieldValue(data: eventData, field: field), let comparatorType = query[JsonKey.CriteriaItem.comparatorType] as? String {
390415
return evaluateComparison(comparatorType: comparatorType, matchObj: valueFromObj, valueToCompare: query[JsonKey.CriteriaItem.value] ?? query[JsonKey.CriteriaItem.values])
391416
}
392417
} else if doesKeyExist {
393418
if let comparatorType = query[JsonKey.CriteriaItem.comparatorType] as? String, (evaluateComparison(comparatorType: comparatorType, matchObj: eventData[field] ?? "", valueToCompare: query[JsonKey.CriteriaItem.value] ?? query[JsonKey.CriteriaItem.values])) {
394419
return true
395420
}
396421
}
422+
397423
return false
398424
}
399425
return matchResult

tests/unit-tests/NestedFieldSupportForArrayData.swift

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,196 @@ final class NestedFieldSupportForArrayData: XCTestCase {
131131
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockData)!, anonymousEvents: eventItems).getMatchedCriteria()
132132
XCTAssertEqual(matchedCriteriaId, nil)
133133
}
134+
135+
private let mokeDataForUserArray = """
136+
{
137+
"count": 1,
138+
"criteriaSets": [
139+
{
140+
"criteriaId": "436",
141+
"name": "Criteria 2.1 - 09252024 Bug Bash",
142+
"createdAt": 1727286807360,
143+
"updatedAt": 1727950464167,
144+
"searchQuery": {
145+
"combinator": "And",
146+
"searchQueries": [
147+
{
148+
"combinator": "And",
149+
"searchQueries": [
150+
{
151+
"dataType": "user",
152+
"searchCombo": {
153+
"combinator": "And",
154+
"searchQueries": [
155+
{
156+
"dataType": "user",
157+
"field": "furniture.material.type",
158+
"comparatorType": "Contains",
159+
"value": "table",
160+
"fieldType": "string"
161+
},
162+
{
163+
"dataType": "user",
164+
"field": "furniture.material.color",
165+
"comparatorType": "Equals",
166+
"values": [
167+
"black"
168+
]
169+
}
170+
]
171+
}
172+
}
173+
]
174+
}
175+
]
176+
}
177+
}
178+
]
179+
}
180+
"""
181+
func testNestedFieldArrayValueUserSuccess() {
182+
183+
let eventItems: [[AnyHashable: Any]] = [
184+
[
185+
"dataType": "user",
186+
"dataFields": [
187+
"furniture": [
188+
"material": [
189+
[
190+
"type": "table",
191+
"color": "black",
192+
"lengthInches": 40,
193+
"widthInches": 60
194+
],
195+
[
196+
"type": "Sofa",
197+
"color": "Gray",
198+
"lengthInches": 20,
199+
"widthInches": 30
200+
]
201+
]
202+
]
203+
]
204+
]
205+
]
206+
207+
208+
let expectedCriteriaId = "436"
209+
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mokeDataForUserArray)!, anonymousEvents: eventItems).getMatchedCriteria()
210+
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
211+
}
212+
213+
func testNestedFieldArrayUserValueFail() {
214+
215+
let eventItems: [[AnyHashable: Any]] = [
216+
[
217+
"dataType": "user",
218+
"dataFields": [
219+
"furniture": [
220+
"material": [
221+
[
222+
"type": "Chair",
223+
"color": "black",
224+
"lengthInches": 40,
225+
"widthInches": 60
226+
],
227+
[
228+
"type": "Sofa",
229+
"color": "black",
230+
"lengthInches": 20,
231+
"widthInches": 30
232+
]
233+
]
234+
]
235+
]
236+
]
237+
]
238+
239+
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mokeDataForUserArray)!, anonymousEvents: eventItems).getMatchedCriteria()
240+
XCTAssertEqual(matchedCriteriaId, nil)
241+
}
242+
243+
private let mokeDataForEventArray = """
244+
{
245+
"count": 1,
246+
"criteriaSets": [
247+
{
248+
"criteriaId": "459",
249+
"name": "event a.h.b=d && a.h.c=g",
250+
"createdAt": 1727717997842,
251+
"updatedAt": 1728024187962,
252+
"searchQuery": {
253+
"combinator": "And",
254+
"searchQueries": [
255+
{
256+
"combinator": "And",
257+
"searchQueries": [
258+
{
259+
"dataType": "customEvent",
260+
"searchCombo": {
261+
"combinator": "And",
262+
"searchQueries": [
263+
{
264+
"dataType": "customEvent",
265+
"field": "TopLevelArrayObject.a.h.b",
266+
"comparatorType": "Equals",
267+
"value": "d",
268+
"fieldType": "string"
269+
},
270+
{
271+
"dataType": "customEvent",
272+
"field": "TopLevelArrayObject.a.h.c",
273+
"comparatorType": "Equals",
274+
"value": "g",
275+
"fieldType": "string"
276+
}
277+
]
278+
}
279+
}
280+
]
281+
}
282+
]
283+
}
284+
}
285+
]
286+
}
287+
"""
288+
func testNestedFieldArrayValueEventSuccess() {
289+
let eventItems: [[AnyHashable: Any]] = [
290+
[
291+
"dataType": "customEvent",
292+
"eventName": "TopLevelArrayObject",
293+
"dataFields": [
294+
"a": ["h": [["b": "e",
295+
"c": "h"],
296+
["b": "d",
297+
"c": "g"]]]
298+
]
299+
]
300+
]
301+
302+
303+
let expectedCriteriaId = "459"
304+
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mokeDataForEventArray)!, anonymousEvents: eventItems).getMatchedCriteria()
305+
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
306+
}
307+
308+
func testNestedFieldArrayEventValueFail() {
309+
310+
let eventItems: [[AnyHashable: Any]] = [
311+
[
312+
"dataType": "customEvent",
313+
"eventName": "TopLevelArrayObject",
314+
"dataFields": [
315+
"a": ["h": [["b": "d",
316+
"c": "h"],
317+
["b": "e",
318+
"c": "g"]]]
319+
]
320+
]
321+
]
322+
323+
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mokeDataForEventArray)!, anonymousEvents: eventItems).getMatchedCriteria()
324+
XCTAssertEqual(matchedCriteriaId, nil)
325+
}
134326
}

0 commit comments

Comments
 (0)