From fc197efd74a07410f9ec334df53cce649fd88b75 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 14 Jul 2025 16:04:34 -0500 Subject: [PATCH] Add method buildCrossProduct to help build cross products --- .../model/data/QRecordWithJoinedRecords.java | 36 +++++++++++++++++++ .../data/QRecordWithJoinedRecordsTest.java | 32 +++++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecords.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecords.java index 70796b49..d9acb0e9 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecords.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecords.java @@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.data; import java.io.Serializable; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -257,4 +258,39 @@ public class QRecordWithJoinedRecords extends QRecord return (this); } + + + /*************************************************************************** + * Given an object of this type (`this`), add a list of join-records to it, + * producing a new list, which is `this` × joinRecordList. + * One may want to use this in a loop to build a larger cross product - more + * to come here in that spirit. + * @param joinTable name of the join table (e.g., to prefix the join fields) + * @param joinRecordList list of join records. may not be null. may be 0+ size. + * @return list of new QRecordWithJoinedRecords, based on `this` with each + * joinRecord added (e.g., output list is same size as joinRecordList). + * Note that does imply an 'inner' style join - where - if the joinRecordList + * is empty, you'll get back an empty list! + ***************************************************************************/ + public List buildCrossProduct(String joinTable, List joinRecordList) + { + List rs = new ArrayList<>(); + + for(QRecord joinRecord : joinRecordList) + { + ///////////////////////////////////////////////////////////// + // essentially clone the existing QRecordWithJoinedRecords // + ///////////////////////////////////////////////////////////// + QRecordWithJoinedRecords newRecord = new QRecordWithJoinedRecords(mainRecord); + components.forEach((k, v) -> newRecord.addJoinedRecordValues(k, v)); + + /////////////////////////////////////////// + // now add the new join record to it too // + /////////////////////////////////////////// + newRecord.addJoinedRecordValues(joinTable, joinRecord); + rs.add(newRecord); + } + + return (rs); + } } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecordsTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecordsTest.java index 2eb372d0..c6b3dcba 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecordsTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/data/QRecordWithJoinedRecordsTest.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.data; import java.time.LocalDate; import java.time.Month; +import java.util.List; import com.kingsrook.qqq.backend.core.BaseTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,8 +42,8 @@ class QRecordWithJoinedRecordsTest extends BaseTest @Test void test() { - QRecord order = new QRecord().withValue("id", 1).withValue("orderNo", "101").withValue("orderDate", LocalDate.of(2025, Month.JANUARY, 1)); - QRecord lineItem = new QRecord().withValue("id", 2).withValue("sku", "ABC").withValue("quantity", 47); + QRecord order = new QRecord().withValue("id", 1).withValue("orderNo", "101").withValue("orderDate", LocalDate.of(2025, Month.JANUARY, 1)); + QRecord lineItem = new QRecord().withValue("id", 2).withValue("sku", "ABC").withValue("quantity", 47); QRecord extrinsic = new QRecord().withValue("id", 3).withValue("key", "MyKey").withValue("value", "MyValue"); QRecordWithJoinedRecords joinedRecords = new QRecordWithJoinedRecords(order); @@ -73,4 +74,31 @@ class QRecordWithJoinedRecordsTest extends BaseTest assertEquals("St. Louis", order.getValue("shipToCity")); } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testBuildCrossProduct() + { + QRecord order = new QRecord().withValue("id", 1).withValue("orderNo", "101").withValue("orderDate", LocalDate.of(2025, Month.JANUARY, 1)); + List lineItems = List.of( + new QRecord().withValue("id", 2).withValue("sku", "ABC").withValue("quantity", 47), + new QRecord().withValue("id", 3).withValue("sku", "DEF").withValue("quantity", 42), + new QRecord().withValue("id", 4).withValue("sku", "XYZ").withValue("quantity", 1024) + ); + + QRecordWithJoinedRecords joinedRecords = new QRecordWithJoinedRecords(order); + List crossProduct = joinedRecords.buildCrossProduct("lineItem", lineItems); + assertEquals(3, crossProduct.size()); + assertEquals("101", crossProduct.get(0).getValue("orderNo")); + assertEquals("ABC", crossProduct.get(0).getValue("lineItem.sku")); + assertEquals("101", crossProduct.get(1).getValue("orderNo")); + assertEquals("DEF", crossProduct.get(1).getValue("lineItem.sku")); + assertEquals("101", crossProduct.get(2).getValue("orderNo")); + assertEquals("XYZ", crossProduct.get(2).getValue("lineItem.sku")); + + } + } \ No newline at end of file