@@ -5,6 +5,7 @@ import streams.events.*
55import streams.service.StreamsSinkEntity
66import streams.utils.StreamsUtils
77import kotlin.test.assertEquals
8+ import kotlin.test.assertTrue
89
910class SchemaIngestionStrategyTest {
1011
@@ -225,6 +226,160 @@ class SchemaIngestionStrategyTest {
225226 assertEquals(expectedRelEvents, eventsRelList)
226227 }
227228
229+ @Test
230+ fun `should create the Schema Query Strategy for relationships with multiple unique constraints` () {
231+ // the Schema Query Strategy leverage the first constraint with lowest properties
232+ // with the same size, we take the first sorted properties list alphabetically
233+
234+ // given
235+ // we shuffle the constraints to ensure that the result doesn't depend from the ordering
236+ val constraintsList = listOf (
237+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" address" )),
238+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" country" )),
239+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" name" , " surname" )),
240+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" profession" , " another_one" )),
241+ Constraint (label = " Product Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" code" )),
242+ Constraint (label = " Product Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" name" ))
243+ ).shuffled()
244+
245+ val relSchema = Schema (properties = mapOf (" since" to " Long" ), constraints = constraintsList)
246+ val idsStart = mapOf (" name" to " Sherlock" ,
247+ " surname" to " Holmes" ,
248+ " country" to " UK" ,
249+ " profession" to " detective" ,
250+ " another_one" to " foo" ,
251+ " address" to " Baker Street" )
252+ val idsEnd = mapOf (" name" to " My Awesome Product" , " code" to 17294 )
253+
254+ val cdcDataRelationship = StreamsTransactionEvent (
255+ meta = Meta (timestamp = System .currentTimeMillis(),
256+ username = " user" ,
257+ txId = 1 ,
258+ txEventId = 2 ,
259+ txEventsCount = 3 ,
260+ operation = OperationType .updated
261+ ),
262+ payload = RelationshipPayload (
263+ id = " 2" ,
264+ start = RelationshipNodeChange (id = " 1" , labels = listOf (" User Ext" , " NewLabel" ), ids = idsStart),
265+ end = RelationshipNodeChange (id = " 2" , labels = listOf (" Product Ext" , " NewLabelA" ), ids = idsEnd),
266+ after = RelationshipChange (properties = mapOf (" since" to 2014 )),
267+ before = null ,
268+ label = " HAS BOUGHT"
269+ ),
270+ schema = relSchema
271+ )
272+ val cdcQueryStrategy = SchemaIngestionStrategy ()
273+ val txEvents = listOf (StreamsSinkEntity (cdcDataRelationship, cdcDataRelationship))
274+
275+ // when
276+ val relationshipEvents = cdcQueryStrategy.mergeRelationshipEvents(txEvents)
277+ val relationshipDeleteEvents = cdcQueryStrategy.deleteRelationshipEvents(txEvents)
278+
279+ // then
280+ assertEquals(0 , relationshipDeleteEvents.size)
281+ assertEquals(1 , relationshipEvents.size)
282+ val relQuery = relationshipEvents[0 ].query
283+ val expectedRelQuery = """
284+ |${StreamsUtils .UNWIND }
285+ |MERGE (start:`User Ext`{address: event.start.address})
286+ |MERGE (end:`Product Ext`{code: event.end.code})
287+ |MERGE (start)-[r:`HAS BOUGHT`]->(end)
288+ |SET r = event.properties
289+ """ .trimMargin()
290+ assertEquals(expectedRelQuery, relQuery.trimIndent())
291+ val eventsRelList = relationshipEvents[0 ].events
292+ assertEquals(1 , eventsRelList.size)
293+ val expectedRelEvents = listOf (
294+ mapOf (" start" to mapOf (" address" to " Baker Street" ),
295+ " end" to mapOf (" code" to 17294 ),
296+ " properties" to mapOf (" since" to 2014 ))
297+ )
298+ assertEquals(expectedRelEvents, eventsRelList)
299+ }
300+
301+ @Test
302+ fun `should create the Schema Query Strategy for relationships with multiple unique constraints and labels` () {
303+ // the Schema Query Strategy leverage the first constraint with lowest properties
304+ // with the same size, we take the first label in alphabetical order
305+ // finally, with same label name, we take the first sorted properties list alphabetically
306+
307+ // given
308+ // we shuffle the constraints to ensure that the result doesn't depend from the ordering
309+ val constraintsList = listOf (
310+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" address" )),
311+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" country" )),
312+ Constraint (label = " User AAA" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" another_two" )),
313+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" name" , " surname" )),
314+ Constraint (label = " User Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" profession" , " another_one" )),
315+ Constraint (label = " Product Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" code" )),
316+ Constraint (label = " Product Ext" , type = StreamsConstraintType .UNIQUE , properties = linkedSetOf(" name" ))
317+ ).shuffled()
318+
319+ val relSchema = Schema (properties = mapOf (" since" to " Long" ), constraints = constraintsList)
320+ val idsStart = mapOf (" name" to " Sherlock" ,
321+ " surname" to " Holmes" ,
322+ " country" to " UK" ,
323+ " profession" to " detective" ,
324+ " another_one" to " foo" ,
325+ " address" to " Baker Street" ,
326+ " another_two" to " Dunno" )
327+ val idsEnd = mapOf (" name" to " My Awesome Product" , " code" to 17294 )
328+
329+ val cdcDataRelationship = StreamsTransactionEvent (
330+ meta = Meta (timestamp = System .currentTimeMillis(),
331+ username = " user" ,
332+ txId = 1 ,
333+ txEventId = 2 ,
334+ txEventsCount = 3 ,
335+ operation = OperationType .updated
336+ ),
337+ payload = RelationshipPayload (
338+ id = " 2" ,
339+ start = RelationshipNodeChange (id = " 1" , labels = listOf (" User Ext" , " User AAA" , " NewLabel" ), ids = idsStart),
340+ end = RelationshipNodeChange (id = " 2" , labels = listOf (" Product Ext" , " NewLabelA" ), ids = idsEnd),
341+ after = RelationshipChange (properties = mapOf (" since" to 2014 )),
342+ before = null ,
343+ label = " HAS BOUGHT"
344+ ),
345+ schema = relSchema
346+ )
347+ val cdcQueryStrategy = SchemaIngestionStrategy ()
348+ val txEvents = listOf (StreamsSinkEntity (cdcDataRelationship, cdcDataRelationship))
349+
350+ // when
351+ val relationshipEvents = cdcQueryStrategy.mergeRelationshipEvents(txEvents)
352+ val relationshipDeleteEvents = cdcQueryStrategy.deleteRelationshipEvents(txEvents)
353+
354+ // then
355+ assertEquals(0 , relationshipDeleteEvents.size)
356+ assertEquals(1 , relationshipEvents.size)
357+ val relQuery = relationshipEvents[0 ].query
358+ val expectedRelQueryOne = """
359+ |${StreamsUtils .UNWIND }
360+ |MERGE (start:`User AAA`:`User Ext`{another_two: event.start.another_two})
361+ |MERGE (end:`Product Ext`{code: event.end.code})
362+ |MERGE (start)-[r:`HAS BOUGHT`]->(end)
363+ |SET r = event.properties
364+ """ .trimMargin()
365+ val expectedRelQueryTwo = """
366+ |${StreamsUtils .UNWIND }
367+ |MERGE (start:`User Ext`:`User AAA`{another_two: event.start.another_two})
368+ |MERGE (end:`Product Ext`{code: event.end.code})
369+ |MERGE (start)-[r:`HAS BOUGHT`]->(end)
370+ |SET r = event.properties
371+ """ .trimMargin()
372+ assertTrue { listOf (expectedRelQueryOne, expectedRelQueryTwo).contains(relQuery.trimIndent()) }
373+ val eventsRelList = relationshipEvents[0 ].events
374+ assertEquals(1 , eventsRelList.size)
375+ val expectedRelEvents = listOf (
376+ mapOf (" start" to mapOf (" another_two" to " Dunno" ),
377+ " end" to mapOf (" code" to 17294 ),
378+ " properties" to mapOf (" since" to 2014 ))
379+ )
380+ assertEquals(expectedRelEvents, eventsRelList)
381+ }
382+
228383 @Test
229384 fun `should create the Schema Query Strategy for node deletes` () {
230385 // given
0 commit comments