From 88e47ef9ca44a5c92512ed07faadd11a7f03fd3d Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 14 Apr 2023 10:04:56 -0500 Subject: [PATCH] WIP on getting joins to frontend --- .../core/actions/metadata/JoinGraph.java | 233 ++++++++++++++++++ .../core/actions/metadata/MetaDataAction.java | 152 ++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/JoinGraph.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/JoinGraph.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/JoinGraph.java new file mode 100644 index 00000000..decb9ee4 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/JoinGraph.java @@ -0,0 +1,233 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.actions.metadata; + + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class JoinGraph +{ + private record Node(String tableName) + { + } + + + + private record Edge(String joinName, String leftTable, String rightTable) + { + } + + + + private static class CanonicalJoin + { + private String tableA; + private String tableB; + private String joinFieldA; + private String joinFieldB; + + + + /******************************************************************************* + ** + *******************************************************************************/ + public CanonicalJoin(QJoinMetaData joinMetaData) + { + boolean needFlip = false; + int tableCompare = joinMetaData.getLeftTable().compareTo(joinMetaData.getRightTable()); + if(tableCompare < 0) + { + needFlip = true; + } + else if(tableCompare == 0) + { + int fieldCompare = joinMetaData.getJoinOns().get(0).getLeftField().compareTo(joinMetaData.getJoinOns().get(0).getRightField()); + if(fieldCompare < 0) + { + needFlip = true; + } + } + + if(needFlip) + { + joinMetaData = joinMetaData.flip(); + } + + tableA = joinMetaData.getLeftTable(); + tableB = joinMetaData.getRightTable(); + joinFieldA = joinMetaData.getJoinOns().get(0).getLeftField(); + joinFieldB = joinMetaData.getJoinOns().get(0).getRightField(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + CanonicalJoin that = (CanonicalJoin) o; + return Objects.equals(tableA, that.tableA) && Objects.equals(tableB, that.tableB) && Objects.equals(joinFieldA, that.joinFieldA) && Objects.equals(joinFieldB, that.joinFieldB); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public int hashCode() + { + return Objects.hash(tableA, tableB, joinFieldA, joinFieldB); + } + } + + + + private Set nodes = new HashSet<>(); + private Set edges = new HashSet<>(); + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public JoinGraph(QInstance qInstance) + { + Set usedJoins = new HashSet<>(); + for(QJoinMetaData join : qInstance.getJoins().values()) + { + CanonicalJoin canonicalJoin = new CanonicalJoin(join); + if(usedJoins.contains(canonicalJoin)) + { + continue; + } + + usedJoins.add(canonicalJoin); + nodes.add(new Node(join.getLeftTable())); + nodes.add(new Node(join.getRightTable())); + edges.add(new Edge(join.getName(), join.getLeftTable(), join.getRightTable())); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public Set getJoins(String tableName) + { + Set rs = new HashSet<>(); + Set tables = new HashSet<>(); + tables.add(tableName); + + boolean keepGoing = true; + while(keepGoing) + { + keepGoing = false; + for(Edge edge : edges) + { + if(tables.contains(edge.leftTable) || tables.contains(edge.rightTable)) + { + if(!rs.contains(edge.joinName)) + { + tables.add(edge.leftTable); + tables.add(edge.rightTable); + rs.add(edge.joinName); + keepGoing = true; + } + } + } + } + + return (rs); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public record Something(String joinTable, List joinPath) + { + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public Set getJoinsBetter(String tableName) + { + Set rs = new HashSet<>(); + Set usedEdges = new HashSet<>(); + Set tables = new HashSet<>(); + tables.add(tableName); + doGetJoinsBetter(rs, tables, new ArrayList<>(), usedEdges); + + return (rs); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void doGetJoinsBetter(Set rs, Set tables, List joinPath, Set usedEdges) + { + for(Edge edge : edges) + { + if(usedEdges.contains(edge.joinName)) + { + continue; + } + + if(tables.contains(edge.leftTable) || tables.contains(edge.rightTable)) + { + usedEdges.add(edge.joinName); + // todo - clone list here, then recurisiv call + rs.add(new Something(tables.contains(edge.leftTable) ? edge.rightTable : edge.leftTable, joinPath)); + } + } + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/MetaDataAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/MetaDataAction.java index 04dbc905..6d2d70eb 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/MetaDataAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/metadata/MetaDataAction.java @@ -23,16 +23,21 @@ package com.kingsrook.qqq.backend.core.actions.metadata; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult; import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper; +import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface; import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode; import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData; @@ -40,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMe import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendReportMetaData; import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendWidgetMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules; @@ -88,6 +94,9 @@ public class MetaDataAction } metaDataOutput.setTables(tables); + // addJoinsToTables(tables); + // addJoinedTablesToTables(tables); + //////////////////////////////////////// // map processes to frontend metadata // //////////////////////////////////////// @@ -212,6 +221,149 @@ public class MetaDataAction return metaDataOutput; } + ////////////////////////////////////// start v1 ////////////////////////////////////// + + + + private record JoinedTable(String joinedTableName, List joinPath) + { + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void addJoinedTablesToTables(Map tables) + { + for(QFrontendTableMetaData table : tables.values()) + { + List joinedTables = new ArrayList<>(); + addJoinedTablesToTable(tables, table, joinedTables, new ArrayList<>()); + + if(joinedTables.size() > 0) + { + System.out.println("For [" + table.getName() + "] we have:\n " + joinedTables.stream().map(String::valueOf).collect(Collectors.joining("\n ")) + "\n"); + } + else + { + System.out.println("No joins for [" + table.getName() + "]\n"); + } + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void addJoinedTablesToTable(Map tables, QFrontendTableMetaData table, List joinedTables, List joinPath) + { + QInstance qInstance = QContext.getQInstance(); + for(QJoinMetaData join : qInstance.getJoins().values()) + { + if(join.getLeftTable().equals(table.getName())) + { + String joinName = join.getName(); + JoinedTable joinedTable = new JoinedTable(join.getRightTable(), joinPath); + System.out.println("Adding to [" + table.getName() + "]: " + joinedTable); + joinedTables.add(joinedTable); + + ArrayList subJoinPath = new ArrayList<>(joinPath); + subJoinPath.add(joinName); + addJoinedTablesToTable(tables, tables.get(join.getRightTable()), joinedTables, subJoinPath); + } + if(join.getRightTable().equals(table.getName())) + { + String joinName = join.getName() + ".flipped"; + JoinedTable joinedTable = new JoinedTable(join.getLeftTable(), joinPath); + System.out.println("Adding to [" + table.getName() + "]: " + joinedTable); + joinedTables.add(joinedTable); + + ArrayList subJoinPath = new ArrayList<>(joinPath); + subJoinPath.add(joinName); + addJoinedTablesToTable(tables, tables.get(join.getLeftTable()), joinedTables, subJoinPath); + } + } + } + + ////////////////////////////////////// end v1 ////////////////////////////////////// + + ////////////////////////////////////// start v0 ////////////////////////////////////// + + + + private record Something(String joinName, List joinPath) + { + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void addJoinsToTables(Map tables) + { + for(QFrontendTableMetaData table : tables.values()) + { + List something = new ArrayList<>(); + addJoinsToTable(tables, table, something, new ArrayList<>(), new HashSet<>()); + if(something.size() > 0) + { + System.out.println("For [" + table.getName() + "] we have:\n " + something.stream().map(String::valueOf).collect(Collectors.joining("\n ")) + "\n"); + } + else + { + System.out.println("No joins for [" + table.getName() + "]\n"); + } + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void addJoinsToTable(Map tables, QFrontendTableMetaData table, List something, List joinPath, Set usedJoins) + { + QInstance qInstance = QContext.getQInstance(); + for(QJoinMetaData join : qInstance.getJoins().values()) + { + if(join.getLeftTable().equals(table.getName())) + { + String joinName = join.getName(); + if(!usedJoins.contains(joinName)) + { + usedJoins.add(joinName); + something.add(new Something(joinName, joinPath)); + + ArrayList subJoinPath = new ArrayList<>(joinPath); + subJoinPath.add(joinName); + + QFrontendTableMetaData rightTable = tables.get(join.getRightTable()); + addJoinsToTable(tables, rightTable, something, subJoinPath, usedJoins); + } + } + else if(join.getRightTable().equals(table.getName())) + { + String joinName = join.getName() + ".flipped"; + if(!usedJoins.contains(joinName)) + { + usedJoins.add(joinName); + something.add(new Something(joinName, joinPath)); + + ArrayList subJoinPath = new ArrayList<>(joinPath); + subJoinPath.add(joinName); + + QFrontendTableMetaData leftTable = tables.get(join.getLeftTable()); + addJoinsToTable(tables, leftTable, something, subJoinPath, usedJoins); + } + } + } + } + + ////////////////////////////////////// end v0 ////////////////////////////////////// + /*******************************************************************************