From ff10fbca359bd84dfbe705ea24de04d400ae1e34 Mon Sep 17 00:00:00 2001
From: Leonardo <leomilho@gmail.com>
Date: Mon, 1 Jul 2024 13:57:25 +0200
Subject: [PATCH] fix(qb): attributes in node array should be a list

---
 .../DatabaseManagement/forms/settings.tsx     | 60 ++++++-----
 .../querybuilder/model/graphology/model.ts    |  9 +-
 .../querybuilder/model/graphology/utils.ts    | 16 ++-
 .../lib/querybuilder/panel/QueryBuilder.tsx   |  6 ++
 .../queryBuilderLogicPillsPanel.tsx           |  4 +
 .../queryBuilderRelatedNodesPanel.tsx         |  2 +
 .../pills/pilldropdown/PillDropdown.tsx       | 11 ++-
 .../pills/pilldropdown/PillDropdownItem.tsx   | 10 +-
 .../query-utils/query2backend.spec.ts         | 99 +++++++++++++------
 .../querybuilder/query-utils/query2backend.ts |  8 +-
 10 files changed, 154 insertions(+), 71 deletions(-)

diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
index 03f75e7f9..fb0fcb6a9 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
@@ -166,29 +166,43 @@ export const SettingsForm = (props: { onClose(): void; open: 'add' | 'update'; s
             </div>
           )}
 
-        <div
-          className={`grid md:grid-cols-2 gap-3 card-actions w-full justify-stretch items-center ${sampleDataPanel === true && 'hidden'}`}
-        >
-          <Button
-            type="primary"
-            label={connection.updating ? (formTitle === 'Add' ? formTitle + 'ing...' : formTitle.slice(0, -1) + 'ing...') : formTitle}
-            onClick={(event) => {
-              event.preventDefault();
-              handleSubmit();
-            }}
-            disabled={connection.updating || hasError}
-          />
-          <Button
-            variant="outline"
-            label="Cancel"
-            disabled={props.disableCancel}
-            onClick={(event) => {
-              event.preventDefault();
-              closeDialog();
-            }}
-          />
-        </div>
-      </>
+          <div
+            className={`pt-4 flex flex-row gap-3 card-actions w-full justify-stretch items-center ${sampleDataPanel === true && 'hidden'}`}
+          >
+            <Button
+              variantType="primary"
+              className="flex-grow"
+              label={connection.updating ? (formTitle === 'Add' ? formTitle + 'ing...' : formTitle.slice(0, -1) + 'ing...') : formTitle}
+              onClick={(event) => {
+                event.preventDefault();
+                handleSubmit();
+              }}
+              disabled={connection.updating || hasError}
+            />
+            {props.open === 'update' && (
+              <Button
+                variantType="secondary"
+                className="flex-grow"
+                label={'Clone'}
+                onClick={(event) => {
+                  handleSubmit({ ...formData, name: formData.name + ' (copy)', id: nilUUID }, true);
+                }}
+                disabled={connection.updating || hasError}
+              />
+            )}
+            <Button
+              variant="outline"
+              className="flex-grow"
+              label="Cancel"
+              disabled={props.disableCancel}
+              onClick={(event) => {
+                event.preventDefault();
+                closeDialog();
+              }}
+            />
+          </div>
+        </>
+      </DialogContent>
     </Dialog>
   );
 };
diff --git a/libs/shared/lib/querybuilder/model/graphology/model.ts b/libs/shared/lib/querybuilder/model/graphology/model.ts
index 4e4cc935d..b47deeb6e 100644
--- a/libs/shared/lib/querybuilder/model/graphology/model.ts
+++ b/libs/shared/lib/querybuilder/model/graphology/model.ts
@@ -33,6 +33,7 @@ export interface EntityData {
   leftRelationHandleId?: QueryGraphEdgeHandle;
   rightRelationHandleId?: QueryGraphEdgeHandle;
   selected?: boolean;
+  type: QueryElementTypes.Entity;
 }
 
 /** Interface for the data in an relation node. */
@@ -44,6 +45,7 @@ export interface RelationData {
   rightEntityHandleId?: QueryGraphEdgeHandle;
   direction?: 'left' | 'right' | 'both';
   selected?: boolean;
+  type: QueryElementTypes.Relation;
 }
 
 export interface LogicData {
@@ -53,11 +55,12 @@ export interface LogicData {
   // key: string;
   logic: GeneralDescription<AllLogicTypes>;
   inputs: Record<string, InputNodeTypeTypes>; // name from InputNode -> InputNodeTypeTypes
+  type: QueryElementTypes.Logic;
 }
 
-export type EntityNodeAttributes = XYPosition & EntityData & NodeDefaults;
-export type RelationNodeAttributes = XYPosition & RelationData & NodeDefaults;
-export type LogicNodeAttributes = XYPosition & LogicData & NodeDefaults;
+export type EntityNodeAttributes = XYPosition & NodeDefaults & EntityData;
+export type RelationNodeAttributes = XYPosition & NodeDefaults & RelationData;
+export type LogicNodeAttributes = XYPosition & NodeDefaults & LogicData;
 
 export type QueryGraphNodes = EntityNodeAttributes | RelationNodeAttributes | LogicNodeAttributes;
 
diff --git a/libs/shared/lib/querybuilder/model/graphology/utils.ts b/libs/shared/lib/querybuilder/model/graphology/utils.ts
index c2d3ae399..f08865788 100644
--- a/libs/shared/lib/querybuilder/model/graphology/utils.ts
+++ b/libs/shared/lib/querybuilder/model/graphology/utils.ts
@@ -54,8 +54,6 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
 
     if (!attributes.id) attributes.id = 'id_' + (Date.now() + Math.floor(Math.random() * 1000)).toString();
 
-    attributes.attributes = attributes.attributes || [];
-
     // Add to the beginning the meta attributes, such as (# Connection)
     attributes.attributes = [...checkForMetaAttributes(attributes).map((a) => ({ handleData: a })), ...attributes.attributes];
 
@@ -118,19 +116,17 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
     return attributes;
   }
 
-  public addLogicPill2Graphology(attributes: QueryGraphNodes, inputValues: Record<string, InputNodeTypeTypes> = {}): QueryGraphNodes {
-    attributes = this.configureDefaults(attributes);
-    if (!attributes.type) attributes.type = QueryElementTypes.Logic;
+  public addLogicPill2Graphology(attributes: LogicNodeAttributes, inputValues: Record<string, InputNodeTypeTypes> = {}): QueryGraphNodes {
+    attributes = this.configureDefaults(attributes) as LogicNodeAttributes;
     if (!attributes.name || !attributes.id) throw Error('type or name is not defined');
 
     // add default inputs, but only if not there yet
     if (attributes.type === QueryElementTypes.Logic) {
-      if ((attributes as LogicNodeAttributes).inputs === undefined) {
-        attributes = attributes as LogicNodeAttributes;
-        (attributes as LogicNodeAttributes).logic.inputs.forEach((input, i) => {
+      if (attributes.inputs === undefined || Object.keys(attributes.inputs).length === 0) {
+        attributes.logic.inputs.forEach((input, i) => {
           // Setup default non-linked inputs as regular values matching the input expected type
-          if (!(attributes as LogicNodeAttributes).inputs) (attributes as LogicNodeAttributes).inputs = {};
-          (attributes as LogicNodeAttributes).inputs[input.name] = inputValues?.[input.name] || input.default;
+          if (!attributes.inputs) attributes.inputs = {};
+          attributes.inputs[input.name] = inputValues?.[input.name] || input.default;
         });
         // (attributes as LogicNodeAttributes).leftEntityHandleId = getHandleId(attributes.id, name, type, Handles.RelationLeft, '');
         // (attributes as LogicNodeAttributes).rightEntityHandleId = getHandleId(attributes.id, name, type, Handles.RelationRight, '');
diff --git a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx
index 26da1edfb..a54e5e213 100644
--- a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx
+++ b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx
@@ -195,6 +195,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
             y: position.y - mouse_y,
             name: dragData.name,
             schemaKey: dragData.name,
+            attributes: [],
           },
           schema.getNodeAttribute(dragData.name, 'attributes'),
         );
@@ -212,6 +213,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
             name: dragData.collection,
             schemaKey: dragData.label,
             collection: dragData.collection,
+            attributes: [],
           },
           schema.getEdgeAttribute(dragData.label, 'attributes'),
         );
@@ -238,6 +240,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
                 y: position.y,
                 name: fromNodeID,
                 schemaKey: fromNodeID,
+                attributes: [],
               },
               schema.getNodeAttribute(fromNodeID, 'attributes'),
             );
@@ -249,6 +252,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
                 y: position.y,
                 name: toNodeID,
                 schemaKey: toNodeID,
+                attributes: [],
               },
               schema.getNodeAttribute(toNodeID, 'attributes'),
             );
@@ -280,6 +284,8 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
           x: position.x,
           y: position.y,
           logic: logic,
+          attributes: [],
+          inputs: {},
         });
 
         dispatch(setQuerybuilderGraphology(graphologyGraph));
diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx
index 041acb8df..0b9f7cff8 100644
--- a/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx
+++ b/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderLogicPillsPanel.tsx
@@ -86,6 +86,8 @@ export const QueryBuilderLogicPillsPanel = (props: {
         x: bounds.width / 2,
         y: bounds.height / 2,
         logic: logic,
+        attributes: [],
+        inputs: {},
       });
     } else {
       const params = props.connection.params;
@@ -97,6 +99,8 @@ export const QueryBuilderLogicPillsPanel = (props: {
         x: position.x,
         y: position.y,
         logic: logic,
+        attributes: [],
+        inputs: {},
       });
 
       if (!logicNode?.id) throw new Error('Logic node has no id');
diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderRelatedNodesPanel.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderRelatedNodesPanel.tsx
index 3b57002c3..5b398a8d5 100644
--- a/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderRelatedNodesPanel.tsx
+++ b/libs/shared/lib/querybuilder/panel/querysidepanel/queryBuilderRelatedNodesPanel.tsx
@@ -91,6 +91,7 @@ export const QueryBuilderRelatedNodesPanel = (props: {
         y: position.y,
         name: entity.name,
         schemaKey: entity.name,
+        attributes: [],
       },
       schemaGraph.getNodeAttribute(entity.name, 'attributes'),
     );
@@ -124,6 +125,7 @@ export const QueryBuilderRelatedNodesPanel = (props: {
         depth: { min: queryBuilderSettings.depth.min, max: queryBuilderSettings.depth.max },
         name: relation.collection,
         collection: relation.collection,
+        attributes: [],
       },
       schemaGraph.getEdgeAttribute(relation.label, 'attributes'),
     );
diff --git a/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx b/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx
index d26c952c0..086858584 100644
--- a/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx
+++ b/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx
@@ -1,5 +1,12 @@
 import { useMemo, ReactElement, useState, useContext } from 'react';
-import { Handles, NodeAttribute, QueryElementTypes, QueryGraphEdges, SchemaReactflowEntityNode } from '../../model';
+import {
+  Handles,
+  NodeAttribute,
+  QueryElementTypes,
+  QueryGraphEdges,
+  SchemaReactflowEntityNode,
+  SchemaReactflowRelationNode,
+} from '../../model';
 import { Abc, CalendarToday, Map, Numbers, Place, QuestionMarkOutlined } from '@mui/icons-material';
 import { Button, TextInput, useAppDispatch, useQuerybuilderAttributesShown } from '../../..';
 import { attributeShownToggle } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
@@ -8,7 +15,7 @@ import { QueryBuilderDispatcherContext } from '../../panel/QueryBuilderDispatche
 import { PillDropdownItem } from './PillDropdownItem';
 
 type PillDropdownProps = {
-  node: SchemaReactflowEntityNode;
+  node: SchemaReactflowEntityNode | SchemaReactflowRelationNode;
   attributes: NodeAttribute[];
   attributeEdges: (QueryGraphEdges | undefined)[];
   open: boolean;
diff --git a/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdownItem.tsx b/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdownItem.tsx
index 161a42ab6..6304229d1 100644
--- a/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdownItem.tsx
+++ b/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdownItem.tsx
@@ -1,5 +1,11 @@
 import { ReactElement, useContext } from 'react';
-import { NodeAttribute, SchemaReactflowEntityNode, handleDataFromReactflowToDataId, toHandleId } from '../../model';
+import {
+  NodeAttribute,
+  SchemaReactflowEntityNode,
+  SchemaReactflowRelationNode,
+  handleDataFromReactflowToDataId,
+  toHandleId,
+} from '../../model';
 import { Handle, Position } from 'reactflow';
 import { PillHandle } from '@graphpolaris/shared/lib/components/pills/PillHandle';
 import { pillDropdownPadding } from '@graphpolaris/shared/lib/components/pills/pill.const';
@@ -8,7 +14,7 @@ import { QueryBuilderDispatcherContext } from '../../panel/QueryBuilderDispatche
 
 type PillDropdownItemProps = {
   attribute: NodeAttribute;
-  node: SchemaReactflowEntityNode;
+  node: SchemaReactflowEntityNode | SchemaReactflowRelationNode;
   className?: string;
   mr?: number;
   icon: ReactElement;
diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
index a2ed912b9..8b35dba94 100644
--- a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
+++ b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
@@ -14,6 +14,7 @@ const defaultQuery = {
   query: [],
   limit: 500,
   return: ['*'],
+  machineLearning: [],
 };
 
 const defaultSettings: QueryBuilderSettings = {
@@ -38,6 +39,7 @@ describe('QueryUtils Entity and Relations', () => {
       x: 100,
       y: 100,
       name: 'Airport 1',
+      attributes: [],
     });
     const entity2 = graph.addPill2Graphology({
       id: '10',
@@ -45,6 +47,7 @@ describe('QueryUtils Entity and Relations', () => {
       x: 200,
       y: 200,
       name: 'Airport 2',
+      attributes: [],
     });
 
     const relation1 = graph.addPill2Graphology({
@@ -55,6 +58,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(entity1, relation1);
@@ -90,8 +94,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run multiple path query translation', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' });
+    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -101,13 +105,14 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1, { type: 'connection' });
     graph.addEdge2Graphology(r1, e2, { type: 'connection' });
 
-    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12' });
-    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22' });
+    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] });
+    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] });
 
     const r12 = graph.addPill2Graphology({
       id: 'r12',
@@ -117,6 +122,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports 2',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e12, r12);
@@ -169,10 +175,10 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run one relation multiple entity query translation', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' });
-    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12' });
-    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });
+    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] });
+    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -182,6 +188,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1);
@@ -235,10 +242,10 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run lone entities query translation', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' });
-    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12' });
-    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22' });
+    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });
+    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] });
+    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -248,6 +255,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1);
@@ -276,8 +284,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run lone relations query translation', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' });
+    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -287,6 +295,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
     const r2 = graph.addPill2Graphology({
       id: 'r2',
@@ -296,6 +305,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports 2',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1);
@@ -326,7 +336,7 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run relation only left side connected query translation', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -336,6 +346,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1);
@@ -361,7 +372,7 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run relation only right side connected query translation', () => {
     const graph = new QueryMultiGraphology();
 
-    const e2 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2' });
+    const e2 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -371,6 +382,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(r1, e2);
@@ -394,8 +406,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run entity and relations multi connection', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -405,6 +417,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     const r2 = graph.addPill2Graphology({
@@ -415,6 +428,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports 2',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1);
@@ -451,7 +465,7 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run in case of loops', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -461,6 +475,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1, { type: 'connection' });
@@ -487,8 +502,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run in case of loops and regular', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -498,6 +513,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1, { type: 'connection' });
@@ -538,8 +554,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run in case of loops and regular left', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     const r1 = graph.addPill2Graphology({
       id: 'r1',
@@ -549,6 +565,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(e1, r1, { type: 'connection' });
@@ -584,8 +601,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run in case of direct entity connection', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     graph.addEdge2Graphology(e1, e2, { type: 'connection' });
 
@@ -618,6 +635,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     const r2 = graph.addPill2Graphology({
@@ -628,6 +646,7 @@ describe('QueryUtils Entity and Relations', () => {
       name: 'Flight between airports 2',
       collection: 'Relation Pill',
       depth: { min: 0, max: 1 },
+      attributes: [],
     });
 
     graph.addEdge2Graphology(r1, r2, { type: 'connection' });
@@ -656,8 +675,8 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run in case of entity only loop', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
-    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
+    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     graph.addEdge2Graphology(e1, e2, { type: 'connection' });
     graph.addEdge2Graphology(e2, e1, { type: 'connection' });
@@ -691,7 +710,7 @@ describe('QueryUtils Entity and Relations', () => {
   it('should run in case of entity only self loop', () => {
     const graph = new QueryMultiGraphology();
 
-    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' });
+    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
 
     graph.addEdge2Graphology(e1, e1, { type: 'connection' });
 
@@ -721,6 +740,7 @@ describe('QueryUtils calculateQueryLogic', () => {
         x: 100,
         y: 100,
         name: 'Airport 1',
+        attributes: [],
       },
       [{ name: 'age', type: 'string' }],
     );
@@ -732,6 +752,8 @@ describe('QueryUtils calculateQueryLogic', () => {
       y: 100,
       name: 'Logic 1',
       logic: MathFilters[NumberFilterTypes.EQUAL],
+      attributes: [],
+      inputs: {},
     });
 
     graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' });
@@ -754,6 +776,7 @@ describe('QueryUtils with Logic', () => {
         x: 100,
         y: 100,
         name: 'Airport 1',
+        attributes: [],
       },
       [{ name: 'age', type: 'string' }],
     );
@@ -765,6 +788,8 @@ describe('QueryUtils with Logic', () => {
       y: 100,
       name: 'Logic 1',
       logic: MathFilters[NumberFilterTypes.EQUAL],
+      attributes: [],
+      inputs: {},
     });
 
     graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });
@@ -800,6 +825,7 @@ describe('QueryUtils with Logic', () => {
         x: 100,
         y: 100,
         name: 'Airport 1',
+        attributes: [],
       },
       [{ name: 'age', type: 'string' }],
     );
@@ -811,6 +837,7 @@ describe('QueryUtils with Logic', () => {
         x: 100,
         y: 100,
         name: 'Airport 2',
+        attributes: [],
       },
       [{ name: 'age', type: 'string' }],
     );
@@ -822,6 +849,8 @@ describe('QueryUtils with Logic', () => {
       y: 100,
       name: 'Filter EQ',
       logic: MathFilters[NumberFilterTypes.EQUAL],
+      attributes: [],
+      inputs: {},
     });
 
     const l2 = graph.addLogicPill2Graphology({
@@ -831,6 +860,8 @@ describe('QueryUtils with Logic', () => {
       y: 100,
       name: 'Logic ADD',
       logic: NumberFunctions[NumberFunctionTypes.ADD],
+      attributes: [],
+      inputs: {},
     });
 
     graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });
@@ -876,6 +907,7 @@ describe('QueryUtils with Logic', () => {
         x: 100,
         y: 100,
         name: 'Airport 1',
+        attributes: [],
       },
       [{ name: 'age', type: 'string' }],
     );
@@ -887,6 +919,8 @@ describe('QueryUtils with Logic', () => {
       y: 100,
       name: 'Logic LT',
       logic: MathFilters[NumberFilterTypes.LESS_THAN],
+      attributes: [],
+      inputs: {},
     });
 
     const l2 = graph.addLogicPill2Graphology({
@@ -896,6 +930,8 @@ describe('QueryUtils with Logic', () => {
       y: 100,
       name: 'Logic average',
       logic: MathAggregations[NumberAggregationTypes.AVG],
+      attributes: [],
+      inputs: {},
     });
 
     graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });
@@ -932,6 +968,7 @@ describe('QueryUtils with Logic', () => {
         x: 100,
         y: 100,
         name: 'Airport 1',
+        attributes: [],
       },
       [{ name: 'age', type: 'string' }],
     );
@@ -944,6 +981,8 @@ describe('QueryUtils with Logic', () => {
         y: 100,
         name: 'Logic LT',
         logic: MathFilters[NumberFilterTypes.LESS_THAN],
+        attributes: [],
+        inputs: {},
       },
       { '1': 5 },
     );
@@ -982,6 +1021,7 @@ it('should no connections between entities and relations', () => {
       x: 100,
       y: 100,
       name: 'Airport 1',
+      attributes: [],
     },
     [{ name: 'age', type: 'string' }],
   );
@@ -993,6 +1033,7 @@ it('should no connections between entities and relations', () => {
       x: 100,
       y: 100,
       name: 'Airport 2',
+      attributes: [],
     },
     [{ name: 'age', type: 'string' }],
   );
@@ -1004,6 +1045,8 @@ it('should no connections between entities and relations', () => {
       y: 100,
       name: 'Relation 1',
       depth: { min: 0, max: 1 },
+      attributes: [],
+      collection: 'r',
     },
     [{ name: 'age', type: 'string' }],
   );
diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.ts
index fc1aa9942..f04b9f1cf 100644
--- a/libs/shared/lib/querybuilder/query-utils/query2backend.ts
+++ b/libs/shared/lib/querybuilder/query-utils/query2backend.ts
@@ -31,7 +31,7 @@ const traverseEntityRelationPaths = (
     // console.log('new path');
     paths.push([]);
     if (node.attributes.type === QueryElementTypes.Relation) {
-      paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.y });
+      paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.y, attributes: [] });
     }
   } else if (paths[currentIdx].length > 0) {
     const lastNode = paths[currentIdx][paths[currentIdx].length - 1];
@@ -39,13 +39,15 @@ const traverseEntityRelationPaths = (
       if (lastNode.type === QueryElementTypes.Entity) {
         paths[currentIdx].push({
           type: QueryElementTypes.Relation,
+          collection: node.key,
           x: node.attributes.x,
           y: node.attributes.x,
           depth: { min: settings.depth.min, max: settings.depth.max },
           direction: 'both',
+          attributes: [],
         });
       } else {
-        paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.x });
+        paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.x, attributes: [] });
       }
     }
   }
@@ -59,7 +61,7 @@ const traverseEntityRelationPaths = (
   let connections = edges.filter((e) => e.source === node.key);
   if (connections.length === 0) {
     if (node.attributes.type === QueryElementTypes.Relation) {
-      paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.x });
+      paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.x, attributes: [] });
     }
     return 0;
   }
-- 
GitLab