From e5586c972344a1d10fb84b9014bbeb2715d49925 Mon Sep 17 00:00:00 2001
From: Leonardo <leomilho@gmail.com>
Date: Fri, 10 Jan 2025 16:43:03 +0100
Subject: [PATCH] chore: test, lint and prettier executed

---
 src/frontend/statistics/graphStatistics.ts    |   9 +-
 src/frontend/statistics/index.ts              |  34 +-
 ...teStats.spec.ts => attributeStats.test.ts} |   4 +-
 ...eType.spec.ts => getAttributeType.test.ts} |   2 +-
 ...istics.spec.ts => graphStatistics.test.ts} |  14 +-
 .../statistics/utils/attributeStats/array.ts  |   1 -
 .../utils/attributeStats/boolean.ts           |   1 -
 .../utils/attributeStats/categorical.ts       |   3 +-
 .../utils/attributeStats/numerical.ts         |   1 -
 .../statistics/utils/attributeStats/object.ts |   1 -
 .../utils/attributeStats/temporal.ts          |   1 -
 .../statistics/utils/updateStatistics.ts      |   1 -
 src/index.ts                                  |  34 +-
 src/logger.ts                                 |   7 +-
 src/readers/diffCheck.ts                      |  49 +-
 src/readers/insightProcessor.ts               |  64 +-
 src/readers/queryService.ts                   | 249 ++---
 src/readers/statCheck.ts                      | 110 +-
 src/utils/cypher/converter/export.ts          |  31 +-
 src/utils/cypher/converter/filter.ts          |  94 +-
 src/utils/cypher/converter/index.ts           |   2 +-
 src/utils/cypher/converter/logic.ts           | 142 +--
 src/utils/cypher/converter/model.ts           |  13 +-
 src/utils/cypher/converter/node.ts            |  62 +-
 .../cypher/converter/queryConverter.test.ts   | 954 +++++++++---------
 src/utils/cypher/converter/queryConverter.ts  | 120 +--
 src/utils/cypher/converter/relation.ts        |  84 +-
 src/utils/cypher/queryParser.ts               | 265 ++---
 src/utils/queryPublisher.ts                   |   2 +-
 src/utils/reactflow/query2backend.ts          |  66 +-
 src/variables.ts                              |  79 +-
 31 files changed, 1267 insertions(+), 1232 deletions(-)
 rename src/frontend/statistics/tests/{attributeStats.spec.ts => attributeStats.test.ts} (96%)
 rename src/frontend/statistics/tests/{getAttributeType.spec.ts => getAttributeType.test.ts} (97%)
 rename src/frontend/statistics/tests/{graphStatistics.spec.ts => graphStatistics.test.ts} (87%)

diff --git a/src/frontend/statistics/graphStatistics.ts b/src/frontend/statistics/graphStatistics.ts
index 93ed780..904223d 100644
--- a/src/frontend/statistics/graphStatistics.ts
+++ b/src/frontend/statistics/graphStatistics.ts
@@ -45,12 +45,12 @@ export const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphSta
         const attributeType = getAttributeType(attributeValue);
         nodeTypeAttributes[attributeId] = {
           attributeType,
-          statistics: initializeStatistics(attributeType)
+          statistics: initializeStatistics(attributeType),
         };
       }
       updateStatistics(nodeTypeAttributes[attributeId], attributeValue);
     }
-  };
+  }
 
   // Process edges
   // Pre-initialize sets and maps for faster lookups
@@ -81,13 +81,12 @@ export const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphSta
         const attributeType = getAttributeType(attributeValue);
         edgeTypeAttributes[attributeId] = {
           attributeType,
-          statistics: initializeStatistics(attributeType)
+          statistics: initializeStatistics(attributeType),
         };
       }
       updateStatistics(edgeTypeAttributes[attributeId], attributeValue);
     }
-  };
+  }
 
   return metaData;
 };
-
diff --git a/src/frontend/statistics/index.ts b/src/frontend/statistics/index.ts
index 7d22fed..2d2a10f 100644
--- a/src/frontend/statistics/index.ts
+++ b/src/frontend/statistics/index.ts
@@ -4,24 +4,24 @@ import { getGraphStatistics } from './graphStatistics';
 export * from './graphStatistics';
 
 export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBackend): GraphQueryResultMetaFromBackend => {
-    const nodeIDs = new Set();
-    const edgeIDs = new Set();
+  const nodeIDs = new Set();
+  const edgeIDs = new Set();
 
-    // Assign ids to a hidden attribute _id to better identify them
-    for (let i = 0; i < payload.nodes.length; i++) {
-        payload.nodes[i]._id = payload.nodes[i]._id || (payload.nodes[i] as any).id;
-        nodeIDs.add(payload.nodes[i]._id);
-    }
-    for (let i = 0; i < payload.edges.length; i++) {
-        payload.edges[i]._id = payload.edges[i]._id || (payload.edges[i] as any).id;
-        edgeIDs.add(payload.edges[i]._id);
-    }
+  // Assign ids to a hidden attribute _id to better identify them
+  for (let i = 0; i < payload.nodes.length; i++) {
+    payload.nodes[i]._id = payload.nodes[i]._id || (payload.nodes[i] as any).id;
+    nodeIDs.add(payload.nodes[i]._id);
+  }
+  for (let i = 0; i < payload.edges.length; i++) {
+    payload.edges[i]._id = payload.edges[i]._id || (payload.edges[i] as any).id;
+    edgeIDs.add(payload.edges[i]._id);
+  }
 
-    // Only keep one node and one edge per id. This is also done in the backend, but we do it here as well to be sure.
-    let nodes = [...nodeIDs].map((nodeID) => payload.nodes.find((node) => node._id === nodeID) as unknown as NodeQueryResult);
-    let edges = [...edgeIDs].map((edgeID) => payload.edges.find((edge) => edge._id === edgeID) as unknown as EdgeQueryResult);
+  // Only keep one node and one edge per id. This is also done in the backend, but we do it here as well to be sure.
+  const nodes = [...nodeIDs].map(nodeID => payload.nodes.find(node => node._id === nodeID) as unknown as NodeQueryResult);
+  const edges = [...edgeIDs].map(edgeID => payload.edges.find(edge => edge._id === edgeID) as unknown as EdgeQueryResult);
 
-    const metaData = getGraphStatistics(payload);
+  const metaData = getGraphStatistics(payload);
 
-    return { metaData, nodes, edges };
-};
\ No newline at end of file
+  return { metaData, nodes, edges };
+};
diff --git a/src/frontend/statistics/tests/attributeStats.spec.ts b/src/frontend/statistics/tests/attributeStats.test.ts
similarity index 96%
rename from src/frontend/statistics/tests/attributeStats.spec.ts
rename to src/frontend/statistics/tests/attributeStats.test.ts
index 1c794fb..17f3093 100644
--- a/src/frontend/statistics/tests/attributeStats.spec.ts
+++ b/src/frontend/statistics/tests/attributeStats.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest';
+import { describe, it, expect } from 'bun:test';
 import {
   updateArrayStats,
   updateBooleanStats,
@@ -8,7 +8,7 @@ import {
   updateObjectStats,
   initializeStatistics,
 } from '../utils/attributeStats';
-import type { ArrayStats, BooleanStats, CategoricalStats, NumericalStats, TemporalStats, ObjectStats } from 'ts-common/src/model/query/statistics';
+import type { ArrayStats, BooleanStats, CategoricalStats, NumericalStats, TemporalStats, ObjectStats } from 'ts-common';
 
 describe('updateArrayStats', () => {
   it('should update the length of the array', () => {
diff --git a/src/frontend/statistics/tests/getAttributeType.spec.ts b/src/frontend/statistics/tests/getAttributeType.test.ts
similarity index 97%
rename from src/frontend/statistics/tests/getAttributeType.spec.ts
rename to src/frontend/statistics/tests/getAttributeType.test.ts
index 5625845..6b8923c 100644
--- a/src/frontend/statistics/tests/getAttributeType.spec.ts
+++ b/src/frontend/statistics/tests/getAttributeType.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest';
+import { describe, it, expect } from 'bun:test';
 import { getAttributeType } from '../utils/getAttributeType';
 
 // Sample values for testing
diff --git a/src/frontend/statistics/tests/graphStatistics.spec.ts b/src/frontend/statistics/tests/graphStatistics.test.ts
similarity index 87%
rename from src/frontend/statistics/tests/graphStatistics.spec.ts
rename to src/frontend/statistics/tests/graphStatistics.test.ts
index 9d59f29..f2b912e 100644
--- a/src/frontend/statistics/tests/graphStatistics.spec.ts
+++ b/src/frontend/statistics/tests/graphStatistics.test.ts
@@ -1,6 +1,6 @@
-import { describe, it, expect } from 'vitest';
-import { GraphQueryResultFromBackend } from '../../data-access/store/graphQueryResultSlice';
+import { describe, it, expect } from 'bun:test';
 import { getGraphStatistics } from '../graphStatistics';
+import type { GraphQueryResultFromBackend } from 'ts-common';
 
 describe('getGraphStatistics', () => {
   it('should return correct statistics for a graph with no nodes and edges', () => {
@@ -93,9 +93,9 @@ describe('getGraphStatistics', () => {
 
   it('should correctly count self-loops', () => {
     const graph: GraphQueryResultFromBackend = {
-      nodes: [{ _id: '1', attributes: {} }],
+      nodes: [{ _id: '1', attributes: {}, label: 'Person' }],
       edges: [
-        { _id: 'e1', attributes: {}, from: '1', to: '1' }, // self-loop
+        { _id: 'e1', attributes: {}, from: '1', to: '1', label: 'TO_PERSON' }, // self-loop
       ],
     };
 
@@ -107,10 +107,10 @@ describe('getGraphStatistics', () => {
   it('should correctly compute density for a graph with nodes and edges', () => {
     const graph: GraphQueryResultFromBackend = {
       nodes: [
-        { _id: '1', attributes: {} },
-        { _id: '2', attributes: {} },
+        { _id: '1', attributes: {}, label: 'Person' },
+        { _id: '2', attributes: {}, label: 'Person' },
       ],
-      edges: [{ _id: 'e1', attributes: {}, from: '1', to: '2' }],
+      edges: [{ _id: 'e1', attributes: {}, from: '1', to: '2', label: 'TO_PERSON' }],
     };
 
     const stats = getGraphStatistics(graph);
diff --git a/src/frontend/statistics/utils/attributeStats/array.ts b/src/frontend/statistics/utils/attributeStats/array.ts
index 25e217c..1845238 100644
--- a/src/frontend/statistics/utils/attributeStats/array.ts
+++ b/src/frontend/statistics/utils/attributeStats/array.ts
@@ -4,4 +4,3 @@ export const updateArrayStats = (stats: ArrayStats, value: any[]) => {
   stats.length = value.length;
   stats.count++;
 };
-
diff --git a/src/frontend/statistics/utils/attributeStats/boolean.ts b/src/frontend/statistics/utils/attributeStats/boolean.ts
index 5439ad7..a737e97 100644
--- a/src/frontend/statistics/utils/attributeStats/boolean.ts
+++ b/src/frontend/statistics/utils/attributeStats/boolean.ts
@@ -7,4 +7,3 @@ export const updateBooleanStats = (stats: BooleanStats, value: boolean) => {
     stats.false += 1;
   }
 };
-
diff --git a/src/frontend/statistics/utils/attributeStats/categorical.ts b/src/frontend/statistics/utils/attributeStats/categorical.ts
index 69a7d4c..860cda7 100644
--- a/src/frontend/statistics/utils/attributeStats/categorical.ts
+++ b/src/frontend/statistics/utils/attributeStats/categorical.ts
@@ -7,10 +7,9 @@ export const updateCategoricalStats = (stats: CategoricalStats, value: string |
   stats.uniqueItems = new Set(stats.values).size;
 
   const frequencyMap: { [key: string]: number } = {};
-  stats.values.forEach((val) => {
+  stats.values.forEach(val => {
     frequencyMap[val] = (frequencyMap[val] || 0) + 1;
   });
   stats.mode = Object.keys(frequencyMap).reduce((a, b) => (frequencyMap[a] > frequencyMap[b] ? a : b));
   stats.count++;
 };
-
diff --git a/src/frontend/statistics/utils/attributeStats/numerical.ts b/src/frontend/statistics/utils/attributeStats/numerical.ts
index e3a8ddb..d0a68b5 100644
--- a/src/frontend/statistics/utils/attributeStats/numerical.ts
+++ b/src/frontend/statistics/utils/attributeStats/numerical.ts
@@ -7,4 +7,3 @@ export const updateNumericalStats = (stats: NumericalStats, value: number) => {
   stats.count++;
   stats.average = (stats.average * (stats.count - 1) + value) / stats.count;
 };
-
diff --git a/src/frontend/statistics/utils/attributeStats/object.ts b/src/frontend/statistics/utils/attributeStats/object.ts
index 6f38156..46e60f2 100644
--- a/src/frontend/statistics/utils/attributeStats/object.ts
+++ b/src/frontend/statistics/utils/attributeStats/object.ts
@@ -3,4 +3,3 @@ import type { ObjectStats } from 'ts-common';
 export const updateObjectStats = (stats: ObjectStats, value: object) => {
   stats.length = Object.keys(value).length;
 };
-
diff --git a/src/frontend/statistics/utils/attributeStats/temporal.ts b/src/frontend/statistics/utils/attributeStats/temporal.ts
index 70468b2..49e1b3e 100644
--- a/src/frontend/statistics/utils/attributeStats/temporal.ts
+++ b/src/frontend/statistics/utils/attributeStats/temporal.ts
@@ -8,4 +8,3 @@ export const updateTemporalStats = (stats: TemporalStats, value: string | Date)
 
   stats.range = stats.max - stats.min;
 };
-
diff --git a/src/frontend/statistics/utils/updateStatistics.ts b/src/frontend/statistics/utils/updateStatistics.ts
index 694d2aa..03a49de 100644
--- a/src/frontend/statistics/utils/updateStatistics.ts
+++ b/src/frontend/statistics/utils/updateStatistics.ts
@@ -43,4 +43,3 @@ export const updateStatistics = (attribute: AttributeStats<AttributeType>, value
       break;
   }
 };
-
diff --git a/src/index.ts b/src/index.ts
index 8dff35c..c94c87a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,25 +4,31 @@ import { log } from './logger';
 import { queryServiceReader } from './readers/queryService';
 import { insightProcessor } from './readers/insightProcessor';
 
-
 async function main() {
-    log.info('Starting query-service...');
+  log.info('Starting query-service...');
 
-    log.info('Connecting to RabbitMQ...');
-    const frontendPublisher = await new RabbitMqBroker(rabbitMq, 'ui-direct-exchange').connect();
-    const mlPublisher = await new RabbitMqBroker(rabbitMq, 'ml-direct-exchange', "query-service", undefined, { autoDelete: false }, { autoDelete: false }).connect();
-    log.info('Connected to RabbitMQ!');
+  log.info('Connecting to RabbitMQ...');
+  const frontendPublisher = await new RabbitMqBroker(rabbitMq, 'ui-direct-exchange').connect();
+  const mlPublisher = await new RabbitMqBroker(
+    rabbitMq,
+    'ml-direct-exchange',
+    'query-service',
+    undefined,
+    { autoDelete: false },
+    { autoDelete: false },
+  ).connect();
+  log.info('Connected to RabbitMQ!');
 
-    log.info('Connecting to Redis...');
-    const redis = new RedisConnector(REDIS_PASSWORD, REDIS_HOST, REDIS_PORT);
-    await redis.connect();
-    log.info('Connected to Redis!');
+  log.info('Connecting to Redis...');
+  const redis = new RedisConnector(REDIS_PASSWORD, REDIS_HOST, REDIS_PORT);
+  await redis.connect();
+  log.info('Connected to Redis!');
 
-    queryServiceReader(frontendPublisher, mlPublisher, redis, 'neo4j');
-    insightProcessor();
-    // TODO: other query services for other databases
+  queryServiceReader(frontendPublisher, mlPublisher, redis, 'neo4j');
+  insightProcessor();
+  // TODO: other query services for other databases
 
-    log.info('Connected to RabbitMQ!');
+  log.info('Connected to RabbitMQ!');
 }
 
 main();
diff --git a/src/logger.ts b/src/logger.ts
index 1afbe97..7c49b4a 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -1,5 +1,4 @@
-import { Logger } from "ts-common";
-import { LOG_LEVEL } from "./variables";
+import { Logger } from 'ts-common';
+import { LOG_LEVEL } from './variables';
 
-
-export const log = new Logger(LOG_LEVEL as any, "query-service");
\ No newline at end of file
+export const log = new Logger(LOG_LEVEL as any, 'query-service');
diff --git a/src/readers/diffCheck.ts b/src/readers/diffCheck.ts
index 4fb4e2f..ba3dd09 100644
--- a/src/readers/diffCheck.ts
+++ b/src/readers/diffCheck.ts
@@ -1,30 +1,33 @@
-import type { SaveState } from "ts-common";
-import { log } from "../logger";
-import { hashDictionary, hashIsEqual } from "../utils/hashing";
-import type { GraphQueryResultMetaFromBackend } from "ts-common/src/model/webSocket/graphResult";
-import { ums } from "../variables";
-import type { InsightModel } from "ts-common";
+import type { SaveState } from 'ts-common';
+import { log } from '../logger';
+import { hashDictionary, hashIsEqual } from '../utils/hashing';
+import type { GraphQueryResultMetaFromBackend } from 'ts-common/src/model/webSocket/graphResult';
+import { ums } from '../variables';
+import type { InsightModel } from 'ts-common';
 
+export const diffCheck = async (
+  insight: InsightModel,
+  ss: SaveState,
+  graphResult: GraphQueryResultMetaFromBackend,
+): Promise<InsightModel> => {
+  const previousQueryResult = await ums.getInsightHash(insight.id);
 
-export const diffCheck = async (insight: InsightModel, ss: SaveState, graphResult: GraphQueryResultMetaFromBackend): Promise<InsightModel> => {
-    const previousQueryResult = await ums.getInsightHash(insight.id);
+  log.debug('Received query request:', ss);
 
-    log.debug("Received query request:", ss);
+  const queryResultHash = hashDictionary({
+    nodes: graphResult.nodes.map(node => node._id),
+    edges: graphResult.edges.map(edge => edge._id),
+  });
 
-    const queryResultHash = hashDictionary({
-        nodes: graphResult.nodes.map((node) => node._id),
-        edges: graphResult.edges.map((edge) => edge._id),
-    });
+  log.debug('Comparing hash values from current and previous query');
+  const changed = !previousQueryResult || !hashIsEqual(queryResultHash, previousQueryResult);
+  insight.status ||= changed;
+  log.debug('Updated node and edge ids in SaveState');
 
-    log.debug("Comparing hash values from current and previous query");
-    const changed = !previousQueryResult || !hashIsEqual(queryResultHash, previousQueryResult);
-    insight.status ||= changed;
-    log.debug("Updated node and edge ids in SaveState");
+  if (changed) {
+    await ums.setInsightHash(insight.id, queryResultHash);
+    log.debug('Saved new hash value to the database');
+  }
 
-    if (changed) {
-        await ums.setInsightHash(insight.id, queryResultHash);
-        log.debug("Saved new hash value to the database");
-    }
-
-    return insight;
+  return insight;
 };
diff --git a/src/readers/insightProcessor.ts b/src/readers/insightProcessor.ts
index 77b6fec..e0fef37 100644
--- a/src/readers/insightProcessor.ts
+++ b/src/readers/insightProcessor.ts
@@ -1,16 +1,16 @@
-import { rabbitMq, ums, mail, SMTP_USER, DEBUG_EMAIL } from "../variables";
-import { log } from "../logger";
-import { RabbitMqBroker, type InsightModel } from "ts-common";
-import { createHeadlessEditor } from "@lexical/headless";
-import { $generateHtmlFromNodes } from "@lexical/html";
-import { JSDOM } from "jsdom";
-import { Query2BackendQuery } from "../utils/reactflow/query2backend";
-import { query2Cypher } from "../utils/cypher/converter";
-import { queryService } from "./queryService";
-import { statCheck } from "./statCheck";
-import { diffCheck } from "./diffCheck";
-import { VariableNode } from "../utils/lexical";
-import { populateTemplate } from "../utils/insights";
+import { rabbitMq, ums, mail, SMTP_USER, DEBUG_EMAIL } from '../variables';
+import { log } from '../logger';
+import { RabbitMqBroker, type InsightModel } from 'ts-common';
+import { createHeadlessEditor } from '@lexical/headless';
+import { $generateHtmlFromNodes } from '@lexical/html';
+import { JSDOM } from 'jsdom';
+import { Query2BackendQuery } from '../utils/reactflow/query2backend';
+import { query2Cypher } from '../utils/cypher/converter';
+import { queryService } from './queryService';
+import { statCheck } from './statCheck';
+import { diffCheck } from './diffCheck';
+import { VariableNode } from '../utils/lexical';
+import { populateTemplate } from '../utils/insights';
 
 const dom = new JSDOM();
 function setUpDom() {
@@ -19,14 +19,13 @@ function setUpDom() {
   const _documentFragment = global.DocumentFragment;
   const _navigator = global.navigator;
 
-  // @ts-ignore
+  // @ts-expect-error - global.window is readonly
   global.window = dom.window;
   global.document = dom.window.document;
   global.DocumentFragment = dom.window.DocumentFragment;
   global.navigator = dom.window.navigator;
 
   return () => {
-    // @ts-ignore
     global.window = _window;
     global.document = _document;
     global.DocumentFragment = _documentFragment;
@@ -37,36 +36,41 @@ function setUpDom() {
 export const insightProcessor = async () => {
   if (mail == null) {
     log.warn('Mail is not configured. Insight processor will be disabled');
-    return
+    return;
   }
 
   log.info('Starting insight processor');
 
-  const insightProcessorConsumer = await new RabbitMqBroker(rabbitMq, 'insight-processor', `insight-processor`, `insight-processor`).connect();
+  const insightProcessorConsumer = await new RabbitMqBroker(
+    rabbitMq,
+    'insight-processor',
+    `insight-processor`,
+    `insight-processor`,
+  ).connect();
   log.info('Connected to RabbitMQ ST!');
 
-  await insightProcessorConsumer.startConsuming<{ insight: InsightModel, force: boolean }>("query-service", async (message, headers) => {
+  await insightProcessorConsumer.startConsuming<{ insight: InsightModel; force: boolean }>('query-service', async (message, headers) => {
     let insight = message.insight;
     if (insight == null || insight.template == null || insight.userId == null || insight.saveStateId == null) {
-      log.error("Invalid Insight received in insightProcessorConsumer:", insight);
+      log.error('Invalid Insight received in insightProcessorConsumer:', insight);
       return;
     }
 
-    if (insight.alarmMode === "disabled" && !message.force) {
-      log.debug("Alarm mode is disabled", insight.id);
+    if (insight.alarmMode === 'disabled' && !message.force) {
+      log.debug('Alarm mode is disabled', insight.id);
       return;
     }
 
     if (insight.recipients == null || insight.recipients.length === 0) {
-      log.debug("No recipients found in the insight, skipping");
+      log.debug('No recipients found in the insight, skipping');
       return;
     }
 
-    log.info("Received insight to be processed", insight);
+    log.info('Received insight to be processed', insight);
 
     const editor = createHeadlessEditor({
       nodes: [VariableNode],
-      onError: (error) => {
+      onError: error => {
         log.error(error);
       },
     });
@@ -89,11 +93,11 @@ export const insightProcessor = async () => {
 
     insight.status = false;
 
-    if (insight.alarmMode === "always") {
+    if (insight.alarmMode === 'always') {
       insight.status = true;
-    } else if (insight.alarmMode === "diff") {
+    } else if (insight.alarmMode === 'diff') {
       insight = await diffCheck(insight, ss, result);
-    } else if (insight.alarmMode === "conditional" && insight.conditionsCheck && insight.conditionsCheck.length > 0) {
+    } else if (insight.alarmMode === 'conditional' && insight.conditionsCheck && insight.conditionsCheck.length > 0) {
       insight = statCheck(insight, result);
     }
 
@@ -114,7 +118,7 @@ export const insightProcessor = async () => {
         for (const recipient of insight.recipients) {
           if (mail == null) {
             log.warn('Mail is not configured. Insight processor will be disabled');
-            return
+            return;
           }
 
           if (DEBUG_EMAIL) {
@@ -127,8 +131,8 @@ export const insightProcessor = async () => {
             to: recipient,
             from: SMTP_USER,
             subject: `GraphPolaris report: ${insight.name}`,
-            html: html
-          })
+            html: html,
+          });
           log.info('Mail sent to ', recipient);
         }
       });
diff --git a/src/readers/queryService.ts b/src/readers/queryService.ts
index ac90c9a..3263a09 100644
--- a/src/readers/queryService.ts
+++ b/src/readers/queryService.ts
@@ -1,131 +1,132 @@
-import { type QueryRequest } from "ts-common";
-import {
-    Neo4jConnection,
-    type DbConnection,
-    RabbitMqBroker,
-    RedisConnector
-} from "ts-common";
-import { rabbitMq, ums, type QueryExecutionTypes } from "../variables";
-import { log } from "../logger";
-import { QueryPublisher } from "../utils/queryPublisher";
-import { query2Cypher } from "../utils/cypher/converter";
-import { parseCypherQuery } from "../utils/cypher/queryParser";
-import { formatTimeDifference } from "ts-common/src/logger/logger";
-import { graphQueryBackend2graphQuery } from "../frontend/statistics";
-import { Query2BackendQuery } from "../utils/reactflow/query2backend";
-import type { GraphQueryResultMetaFromBackend } from "ts-common/src/model/webSocket/graphResult";
-
+import { type QueryRequest } from 'ts-common';
+import { Neo4jConnection, type DbConnection, RabbitMqBroker, RedisConnector } from 'ts-common';
+import { rabbitMq, ums, type QueryExecutionTypes } from '../variables';
+import { log } from '../logger';
+import { QueryPublisher } from '../utils/queryPublisher';
+import { query2Cypher } from '../utils/cypher/converter';
+import { parseCypherQuery } from '../utils/cypher/queryParser';
+import { formatTimeDifference } from 'ts-common/src/logger/logger';
+import { graphQueryBackend2graphQuery } from '../frontend/statistics';
+import { Query2BackendQuery } from '../utils/reactflow/query2backend';
+import type { GraphQueryResultMetaFromBackend } from 'ts-common/src/model/webSocket/graphResult';
 
 export const queryService = async (db: DbConnection, query: string): Promise<GraphQueryResultMetaFromBackend> => {
-    // TODO: only neo4j is supported for now
-    const connection = new Neo4jConnection(db);
-    try {
-        const [neo4jResult] = await connection.run([query]);
-        const graph = parseCypherQuery(neo4jResult.records);
-
-        // calculate metadata
-        const result = graphQueryBackend2graphQuery(graph);
-
-        return result
-    } catch (error) {
-        log.error('Error parsing query result:', query, error);
-        throw new Error('Error parsing query result');
-    } finally {
-        connection.close();
+  // TODO: only neo4j is supported for now
+  const connection = new Neo4jConnection(db);
+  try {
+    const [neo4jResult] = await connection.run([query]);
+    const graph = parseCypherQuery(neo4jResult.records);
+
+    // calculate metadata
+    const result = graphQueryBackend2graphQuery(graph);
+
+    return result;
+  } catch (error) {
+    log.error('Error parsing query result:', query, error);
+    throw new Error('Error parsing query result');
+  } finally {
+    connection.close();
+  }
+};
+
+export const queryServiceReader = async (
+  frontendPublisher: RabbitMqBroker,
+  mlPublisher: RabbitMqBroker,
+  redis: RedisConnector,
+  type: QueryExecutionTypes,
+) => {
+  if (type == null) {
+    log.error('Unsupported query execution type:', type);
+    throw new Error('Unsupported query execution type');
+  }
+  log.info('Starting query reader for', type);
+
+  const publisher = new QueryPublisher(frontendPublisher, mlPublisher);
+
+  const queryServiceConsumer = await new RabbitMqBroker(
+    rabbitMq,
+    'requests-exchange',
+    `${type}-query-queue`,
+    `${type}-query-request`,
+  ).connect();
+
+  log.info('Connected to RabbitMQ ST!');
+
+  await queryServiceConsumer.startConsuming<QueryRequest>('query-service', async (message, headers) => {
+    const startTime = Date.now();
+    const ss = await ums.getUserSaveState(headers.message.sessionData.userID, message.saveStateID);
+
+    if (!ss) {
+      log.error('Invalid SaveState received in queryServiceConsumer:', ss);
+      publisher.publishErrorToFrontend('Invalid SaveState');
+      return;
+    }
+
+    log.debug('Received query request:', message, headers, ss);
+    log.debug('Received routing key:', headers.routingKey);
+
+    if (!headers.callID) {
+      log.error('QueryID not set in headers:', headers);
+      return;
     }
-}
 
+    publisher.withHeaders(headers).withRoutingKey(headers.routingKey).withQueryID(headers.callID);
+    publisher.publishStatusToFrontend('Received');
+
+    if (ss == null || ss.dbConnections == null || ss.dbConnections[0] == null || ss.dbConnections.length === 0) {
+      log.error('Invalid SaveState received in queryServiceConsumer:', ss);
+      publisher.publishErrorToFrontend('Invalid SaveState');
+      return;
+    }
 
-export const queryServiceReader = async (frontendPublisher: RabbitMqBroker, mlPublisher: RabbitMqBroker, redis: RedisConnector, type: QueryExecutionTypes) => {
-    if (type == null) {
-        log.error('Unsupported query execution type:', type);
-        throw new Error('Unsupported query execution type');
+    const visualQuery = ss.queries[0].graph;
+    log.debug('Received query request:', message, headers, visualQuery);
+    if (visualQuery.nodes.length === 0) {
+      log.info('Empty query received');
+      publisher.publishResultToFrontend({ nodes: [], edges: [] });
+      return;
     }
-    log.info('Starting query reader for', type);
-
-    const publisher = new QueryPublisher(frontendPublisher, mlPublisher);
-
-    const queryServiceConsumer = await new RabbitMqBroker(rabbitMq, 'requests-exchange', `${type}-query-queue`, `${type}-query-request`).connect();
-
-    log.info('Connected to RabbitMQ ST!');
-
-    await queryServiceConsumer.startConsuming<QueryRequest>("query-service", async (message, headers) => {
-        const startTime = Date.now();
-        const ss = await ums.getUserSaveState(headers.message.sessionData.userID, message.saveStateID);
-
-        if (!ss) {
-            log.error('Invalid SaveState received in queryServiceConsumer:', ss);
-            publisher.publishErrorToFrontend('Invalid SaveState');
-            return;
-        }
-
-        log.debug('Received query request:', message, headers, ss);
-        log.debug('Received routing key:', headers.routingKey);
-
-        if (!headers.callID) {
-            log.error('QueryID not set in headers:', headers);
-            return;
-        }
-
-
-        publisher.withHeaders(headers).withRoutingKey(headers.routingKey).withQueryID(headers.callID);
-        publisher.publishStatusToFrontend('Received');
-
-        if (ss == null || ss.dbConnections == null || ss.dbConnections[0] == null || ss.dbConnections.length === 0) {
-            log.error('Invalid SaveState received in queryServiceConsumer:', ss);
-            publisher.publishErrorToFrontend('Invalid SaveState');
-            return;
-        }
-
-        const visualQuery = ss.queries[0].graph;
-        log.debug('Received query request:', message, headers, visualQuery);
-        if (visualQuery.nodes.length === 0) {
-            log.info('Empty query received');
-            publisher.publishResultToFrontend({ nodes: [], edges: [] });
-            return;
-        }
-
-        const queryBuilderSettings = ss.queries[0].settings;
-        const ml = message.ml;
-        const convertedQuery = Query2BackendQuery(ss.id, visualQuery, queryBuilderSettings, ml);
-
-        log.debug('translating query:', convertedQuery);
-        publisher.publishStatusToFrontend('Translating');
-
-        const query = query2Cypher(convertedQuery);
-        if (query == null) {
-            log.error('Error translating query:', convertedQuery);
-            publisher.publishErrorToFrontend('Error translating query');
-            return;
-        }
-
-        log.info('Translated query:', query);
-        publisher.publishTranslationResultToFrontend(query);
-
-        for (let i = 0; i < ss.dbConnections.length; i++) {
-            queryService(ss.dbConnections[i], query).then((result) => {
-                publisher.publishResultToFrontend(result);
-                log.debug('Query result!');
-                log.info(`Query executed in ${formatTimeDifference(Date.now() - startTime)}`);
-
-                if (convertedQuery.machineLearning && convertedQuery.machineLearning.length > 0) {
-                    for (let i = 0; i < convertedQuery.machineLearning.length; i++) {
-                        try {
-                            publisher.publishMachineLearningRequest(result, convertedQuery.machineLearning[i], headers);
-                            log.debug('Published machine learning request', convertedQuery.machineLearning[i]);
-                        } catch (error) {
-                            log.error('Error publishing machine learning request', error);
-                            publisher.publishErrorToFrontend('Error publishing machine learning request');
-                        }
-                    }
-                }
-
-            }).catch((error) => {
-                log.error('Error querying database', error);
-                publisher.publishErrorToFrontend('Error querying database');
-            });
-        }
-
-    });
-}
 
+    const queryBuilderSettings = ss.queries[0].settings;
+    const ml = message.ml;
+    const convertedQuery = Query2BackendQuery(ss.id, visualQuery, queryBuilderSettings, ml);
+
+    log.debug('translating query:', convertedQuery);
+    publisher.publishStatusToFrontend('Translating');
+
+    const query = query2Cypher(convertedQuery);
+    if (query == null) {
+      log.error('Error translating query:', convertedQuery);
+      publisher.publishErrorToFrontend('Error translating query');
+      return;
+    }
+
+    log.info('Translated query:', query);
+    publisher.publishTranslationResultToFrontend(query);
+
+    for (let i = 0; i < ss.dbConnections.length; i++) {
+      queryService(ss.dbConnections[i], query)
+        .then(result => {
+          publisher.publishResultToFrontend(result);
+          log.debug('Query result!');
+          log.info(`Query executed in ${formatTimeDifference(Date.now() - startTime)}`);
+
+          if (convertedQuery.machineLearning && convertedQuery.machineLearning.length > 0) {
+            for (let i = 0; i < convertedQuery.machineLearning.length; i++) {
+              try {
+                publisher.publishMachineLearningRequest(result, convertedQuery.machineLearning[i], headers);
+                log.debug('Published machine learning request', convertedQuery.machineLearning[i]);
+              } catch (error) {
+                log.error('Error publishing machine learning request', error);
+                publisher.publishErrorToFrontend('Error publishing machine learning request');
+              }
+            }
+          }
+        })
+        .catch(error => {
+          log.error('Error querying database', error);
+          publisher.publishErrorToFrontend('Error querying database');
+        });
+    }
+  });
+};
diff --git a/src/readers/statCheck.ts b/src/readers/statCheck.ts
index c130de6..96ea655 100644
--- a/src/readers/statCheck.ts
+++ b/src/readers/statCheck.ts
@@ -1,69 +1,67 @@
-import type { GraphQueryResultMetaFromBackend, InsightModel } from "ts-common";
-import { log } from "../logger";
+import type { GraphQueryResultMetaFromBackend, InsightModel } from 'ts-common';
+import { log } from '../logger';
 
 function processAlarmStats(alarmStat: InsightModel, resultQuery: GraphQueryResultMetaFromBackend): boolean {
-    for (const condition of alarmStat.conditionsCheck) {
-        const ssInsightNode = condition.nodeLabel;
-        const ssInsightStatistic = condition.statistic;
-        const ssInsightOperator = condition.operator;
-        const ssInsightValue = condition.value;
-        // TODO: eventually we need to support multiple conditions with and/or
+  for (const condition of alarmStat.conditionsCheck) {
+    const ssInsightNode = condition.nodeLabel;
+    const ssInsightStatistic = condition.statistic;
+    const ssInsightOperator = condition.operator;
+    const ssInsightValue = condition.value;
+    // TODO: eventually we need to support multiple conditions with and/or
 
-        log.debug(`Checking condition: ${ssInsightNode} ${ssInsightStatistic} ${ssInsightOperator} ${ssInsightValue}`);
+    log.debug(`Checking condition: ${ssInsightNode} ${ssInsightStatistic} ${ssInsightOperator} ${ssInsightValue}`);
 
-        if (resultQuery.metaData.nodes.labels.includes(ssInsightNode)) {
-            const nodeCount = resultQuery.metaData.nodes.count;
-            let conditionMet = false;
-            switch (ssInsightOperator) {
-                case ">":
-                    conditionMet = nodeCount > ssInsightValue;
-                    break;
-                case ">=":
-                    conditionMet = nodeCount >= ssInsightValue;
-                    break;
-                case "==":
-                    conditionMet = nodeCount === ssInsightValue;
-                    break;
-                case "!=":
-                    conditionMet = nodeCount !== ssInsightValue;
-                    break;
-                case "<":
-                    conditionMet = nodeCount < ssInsightValue;
-                    break;
-                case "<=":
-                    conditionMet = nodeCount <= ssInsightValue;
-                    break;
-                default:
-                    log.error(`Unsupported operator: ${ssInsightOperator}`);
-                    throw new Error(`Unsupported operator: ${ssInsightOperator}`);
-            }
+    if (resultQuery.metaData.nodes.labels.includes(ssInsightNode)) {
+      const nodeCount = resultQuery.metaData.nodes.count;
+      let conditionMet = false;
+      switch (ssInsightOperator) {
+        case '>':
+          conditionMet = nodeCount > ssInsightValue;
+          break;
+        case '>=':
+          conditionMet = nodeCount >= ssInsightValue;
+          break;
+        case '==':
+          conditionMet = nodeCount === ssInsightValue;
+          break;
+        case '!=':
+          conditionMet = nodeCount !== ssInsightValue;
+          break;
+        case '<':
+          conditionMet = nodeCount < ssInsightValue;
+          break;
+        case '<=':
+          conditionMet = nodeCount <= ssInsightValue;
+          break;
+        default:
+          log.error(`Unsupported operator: ${ssInsightOperator}`);
+          throw new Error(`Unsupported operator: ${ssInsightOperator}`);
+      }
 
-            if (conditionMet) {
-                log.info("Condition met ");
-                return true;
-            }
-
-            else {
-                log.info("Condition not met");
-                return false;
-            }
-        }
-
-        break; // TODO: only one condition is supported for now
+      if (conditionMet) {
+        log.info('Condition met ');
+        return true;
+      } else {
+        log.info('Condition not met');
+        return false;
+      }
     }
 
-    return false;
+    break; // TODO: only one condition is supported for now
+  }
+
+  return false;
 }
 
 export const statCheck = (insight: InsightModel, graphResult: GraphQueryResultMetaFromBackend): InsightModel => {
-    if (insight.type === "report") {
-        // report is not an alarm, just log the stats
-        log.debug("report!", insight);
-        return insight;
-    }
+  if (insight.type === 'report') {
+    // report is not an alarm, just log the stats
+    log.debug('report!', insight);
+    return insight;
+  }
 
-    insight.status ||= processAlarmStats(insight, graphResult); //statusResult;
-    log.debug("Alarm status", insight.status);
+  insight.status ||= processAlarmStats(insight, graphResult); //statusResult;
+  log.debug('Alarm status', insight.status);
 
-    return insight;
+  return insight;
 };
diff --git a/src/utils/cypher/converter/export.ts b/src/utils/cypher/converter/export.ts
index 6c37069..fa39213 100644
--- a/src/utils/cypher/converter/export.ts
+++ b/src/utils/cypher/converter/export.ts
@@ -1,24 +1,23 @@
-import type { ExportNodeStruct, NodeStruct } from "ts-common";
-
+import type { ExportNodeStruct, NodeStruct } from 'ts-common';
 
 export function createExportCypher(JSONQuery: ExportNodeStruct, id: string): string {
-    return `${id}.${JSONQuery.attribute} as ${JSONQuery.id}`;
+  return `${id}.${JSONQuery.attribute} as ${JSONQuery.id}`;
 }
 
 export function extractExportCypher(JSONQuery: NodeStruct): string[] {
-    const exports: string[] = [];
-    if (JSONQuery.export) {
-        for (const exportItem of JSONQuery.export) {
-            if (exportItem && JSONQuery.id) {
-                const exportCypher = createExportCypher(exportItem, JSONQuery.id);
-                exports.push(exportCypher);
-            }
-        }
+  const exports: string[] = [];
+  if (JSONQuery.export) {
+    for (const exportItem of JSONQuery.export) {
+      if (exportItem && JSONQuery.id) {
+        const exportCypher = createExportCypher(exportItem, JSONQuery.id);
+        exports.push(exportCypher);
+      }
     }
+  }
 
-    if (JSONQuery.relation && JSONQuery.relation.node) {
-        const relationCypher = extractExportCypher(JSONQuery.relation.node);
-        exports.push(...relationCypher);
-    }
-    return exports;
+  if (JSONQuery.relation && JSONQuery.relation.node) {
+    const relationCypher = extractExportCypher(JSONQuery.relation.node);
+    exports.push(...relationCypher);
+  }
+  return exports;
 }
diff --git a/src/utils/cypher/converter/filter.ts b/src/utils/cypher/converter/filter.ts
index dbf1225..a4bb370 100644
--- a/src/utils/cypher/converter/filter.ts
+++ b/src/utils/cypher/converter/filter.ts
@@ -1,49 +1,49 @@
-import type { FilterStruct } from "ts-common";
+import type { FilterStruct } from 'ts-common';
 
 export function createFilterCypher(JSONQuery: FilterStruct, ID: string): string {
-    let cypher = `${ID}.${JSONQuery.attribute}`;
-    switch (JSONQuery.operation) {
-        case "EQ":
-            cypher += ` = ${JSONQuery.value}`;
-            break;
-        case "NEQ":
-            cypher += ` <> ${JSONQuery.value}`;
-            break;
-        case "GT":
-            cypher += ` > ${JSONQuery.value}`;
-            break;
-        case "GTE":
-            cypher += ` >= ${JSONQuery.value}`;
-            break;
-        case "LT":
-            cypher += ` < ${JSONQuery.value}`;
-            break;
-        case "LTE":
-            cypher += ` <= ${JSONQuery.value}`;
-            break;
-        case "CONTAINS":
-            cypher += ` CONTAINS ${JSONQuery.value}`;
-            break;
-        case "STARTS_WITH":
-            cypher += ` STARTS WITH ${JSONQuery.value}`;
-            break;
-        case "ENDS_WITH":
-            cypher += ` ENDS WITH ${JSONQuery.value}`;
-            break;
-        case "IN":
-            cypher += ` IN ${JSONQuery.value}`;
-            break;
-        case "NOT_IN":
-            cypher += ` NOT IN ${JSONQuery.value}`;
-            break;
-        case "IS_NULL":
-            cypher += ` IS NULL`;
-            break;
-        case "IS_NOT_NULL":
-            cypher += ` IS NOT NULL`;
-            break;
-        default:
-            throw new Error("operator not supported");
-    }
-    return cypher;
-}
\ No newline at end of file
+  let cypher = `${ID}.${JSONQuery.attribute}`;
+  switch (JSONQuery.operation) {
+    case 'EQ':
+      cypher += ` = ${JSONQuery.value}`;
+      break;
+    case 'NEQ':
+      cypher += ` <> ${JSONQuery.value}`;
+      break;
+    case 'GT':
+      cypher += ` > ${JSONQuery.value}`;
+      break;
+    case 'GTE':
+      cypher += ` >= ${JSONQuery.value}`;
+      break;
+    case 'LT':
+      cypher += ` < ${JSONQuery.value}`;
+      break;
+    case 'LTE':
+      cypher += ` <= ${JSONQuery.value}`;
+      break;
+    case 'CONTAINS':
+      cypher += ` CONTAINS ${JSONQuery.value}`;
+      break;
+    case 'STARTS_WITH':
+      cypher += ` STARTS WITH ${JSONQuery.value}`;
+      break;
+    case 'ENDS_WITH':
+      cypher += ` ENDS WITH ${JSONQuery.value}`;
+      break;
+    case 'IN':
+      cypher += ` IN ${JSONQuery.value}`;
+      break;
+    case 'NOT_IN':
+      cypher += ` NOT IN ${JSONQuery.value}`;
+      break;
+    case 'IS_NULL':
+      cypher += ` IS NULL`;
+      break;
+    case 'IS_NOT_NULL':
+      cypher += ` IS NOT NULL`;
+      break;
+    default:
+      throw new Error('operator not supported');
+  }
+  return cypher;
+}
diff --git a/src/utils/cypher/converter/index.ts b/src/utils/cypher/converter/index.ts
index b2283fc..2411846 100644
--- a/src/utils/cypher/converter/index.ts
+++ b/src/utils/cypher/converter/index.ts
@@ -1 +1 @@
-export { query2Cypher } from './queryConverter';
\ No newline at end of file
+export { query2Cypher } from './queryConverter';
diff --git a/src/utils/cypher/converter/logic.ts b/src/utils/cypher/converter/logic.ts
index ac4ddce..0fec27a 100644
--- a/src/utils/cypher/converter/logic.ts
+++ b/src/utils/cypher/converter/logic.ts
@@ -1,84 +1,86 @@
-import type { AnyStatement } from "ts-common/src/model/query/logic/general";
-import type { QueryCacheData } from "./model";
+import type { AnyStatement } from 'ts-common/src/model/query/logic/general';
+import type { QueryCacheData } from './model';
 
 export function createWhereLogic(op: string, left: string, whereLogic: string, cacheData: QueryCacheData): [string, string] {
-    const newWhereLogic = `${left.replace(".", "_")}_${op}`;
-    if (whereLogic) {
-        whereLogic += ", ";
-    }
-    const remainingNodes: string[] = [];
+  const newWhereLogic = `${left.replace('.', '_')}_${op}`;
+  if (whereLogic) {
+    whereLogic += ', ';
+  }
+  const remainingNodes: string[] = [];
 
-    for (const entity of cacheData.entities) {
-        if (entity.id !== left) {
-            remainingNodes.push(entity.id);
-        }
+  for (const entity of cacheData.entities) {
+    if (entity.id !== left) {
+      remainingNodes.push(entity.id);
     }
+  }
 
-    // TODO: Relation temporarily ignored due to unnecessary added complexity in the query
-    // for (const relation of cacheData.relations) {
-    //     if (relation.id !== left) {
-    //         remainingNodes.push(relation.id);
-    //     }
-    // }
+  // TODO: Relation temporarily ignored due to unnecessary added complexity in the query
+  // for (const relation of cacheData.relations) {
+  //     if (relation.id !== left) {
+  //         remainingNodes.push(relation.id);
+  //     }
+  // }
 
-    const remainingNodesStr = remainingNodes.length > 0 ? `, ${remainingNodes.join(", ")}` : "";
-    const newWithLogic = `${whereLogic}${op}(${left}) AS ${newWhereLogic}${remainingNodesStr}`;
+  const remainingNodesStr = remainingNodes.length > 0 ? `, ${remainingNodes.join(', ')}` : '';
+  const newWithLogic = `${whereLogic}${op}(${left}) AS ${newWhereLogic}${remainingNodesStr}`;
 
-    return [newWhereLogic, newWithLogic];
+  return [newWhereLogic, newWithLogic];
 }
 
 export function extractLogicCypher(logicQuery: AnyStatement, cacheData: QueryCacheData): [string, string] {
-    switch (typeof logicQuery) {
-        case "object":
-            if (Array.isArray(logicQuery)) {
-                let op = logicQuery[0].replace("_", "").toLowerCase();
-                let [left, whereLogic] = extractLogicCypher(logicQuery[1], cacheData);
+  switch (typeof logicQuery) {
+    case 'object':
+      if (Array.isArray(logicQuery)) {
+        let op = logicQuery[0].replace('_', '').toLowerCase();
+        const [left, whereLogic] = extractLogicCypher(logicQuery[1], cacheData);
+        let whereLogicMutable = whereLogic;
 
-                switch (op) {
-                    case "!=":
-                        op = "<>";
-                        break;
-                    case "==":
-                        op = "=";
-                        break;
-                    case "like":
-                        op = "=~";
-                        break;
-                    case "isempty":
-                        return [`(${left} IS NULL OR ${left} = "")`, whereLogic];
-                    case "lower":
-                        return [`toLower(${left})`, whereLogic];
-                    case "upper":
-                        return [`toUpper(${left})`, whereLogic];
-                    case "avg":
-                    case "count":
-                    case "max":
-                    case "min":
-                    case "sum":
-                        return createWhereLogic(op, left, whereLogic, cacheData);
-                }
-                if (logicQuery.length > 2 && logicQuery[2]) {
-                    let [right, whereLogicRight] = extractLogicCypher(logicQuery[2], cacheData);
+        switch (op) {
+          case '!=':
+            op = '<>';
+            break;
+          case '==':
+            op = '=';
+            break;
+          case 'like':
+            op = '=~';
+            break;
+          case 'isempty':
+            return [`(${left} IS NULL OR ${left} = "")`, whereLogicMutable];
+          case 'lower':
+            return [`toLower(${left})`, whereLogicMutable];
+          case 'upper':
+            return [`toUpper(${left})`, whereLogicMutable];
+          case 'avg':
+          case 'count':
+          case 'max':
+          case 'min':
+          case 'sum':
+            return createWhereLogic(op, left, whereLogicMutable, cacheData);
+        }
+        if (logicQuery.length > 2 && logicQuery[2]) {
+          const [right, whereLogicRight] = extractLogicCypher(logicQuery[2], cacheData);
+          let rightMutable = right;
 
-                    if (whereLogicRight) {
-                        if (whereLogic) {
-                            whereLogic += ", ";
-                        }
-                        whereLogic += whereLogicRight;
-                    }
-                    if (op === "=~") {
-                        right = `(".*" + ${right} + ".*")`;
-                    }
-                    return [`(${left} ${op} ${right})`, whereLogic];
-                }
-                return [`(${op} ${left})`, whereLogic];
+          if (whereLogicRight) {
+            if (whereLogicMutable) {
+              whereLogicMutable += ', ';
             }
-            return [logicQuery, ""];
-        case "string":
-            return [logicQuery.replace("@", ""), ""];
-        case "number":
-            return [logicQuery.toString(), ""];
-        default:
-            return [logicQuery as any, ""];
-    }
+            whereLogicMutable += whereLogicRight;
+          }
+          if (op === '=~') {
+            rightMutable = `(".*" + ${rightMutable} + ".*")`;
+          }
+          return [`(${left} ${op} ${rightMutable})`, whereLogicMutable];
+        }
+        return [`(${op} ${left})`, whereLogicMutable];
+      }
+      return [logicQuery, ''];
+    case 'string':
+      return [logicQuery.replace('@', ''), ''];
+    case 'number':
+      return [logicQuery.toString(), ''];
+    default:
+      return [logicQuery as any, ''];
+  }
 }
diff --git a/src/utils/cypher/converter/model.ts b/src/utils/cypher/converter/model.ts
index 492fde7..e4460ce 100644
--- a/src/utils/cypher/converter/model.ts
+++ b/src/utils/cypher/converter/model.ts
@@ -1,15 +1,14 @@
-
 export interface RelationCacheData {
-    id: string;
-    queryId: string;
+  id: string;
+  queryId: string;
 }
 
 export interface EntityCacheData {
-    id: string;
-    queryId: string;
+  id: string;
+  queryId: string;
 }
 
 export interface QueryCacheData {
-    entities: EntityCacheData[];
-    relations: RelationCacheData[];
+  entities: EntityCacheData[];
+  relations: RelationCacheData[];
 }
diff --git a/src/utils/cypher/converter/node.ts b/src/utils/cypher/converter/node.ts
index cb6b0bf..0837633 100644
--- a/src/utils/cypher/converter/node.ts
+++ b/src/utils/cypher/converter/node.ts
@@ -1,39 +1,39 @@
-import type { NodeStruct } from "ts-common";
-import type { QueryCacheData } from "./model";
-import { getRelationCypher } from "./relation";
+import type { NodeStruct } from 'ts-common';
+import type { QueryCacheData } from './model';
+import { getRelationCypher } from './relation';
 
 export function getNodeCypher(JSONQuery: NodeStruct): [string, QueryCacheData] {
-    let label = "";
-    if (JSONQuery.label) {
-        label = `:${JSONQuery.label}`;
-    }
-    let id = "";
-    if (JSONQuery.id) {
-        id = JSONQuery.id;
-    }
+  let label = '';
+  if (JSONQuery.label) {
+    label = `:${JSONQuery.label}`;
+  }
+  let id = '';
+  if (JSONQuery.id) {
+    id = JSONQuery.id;
+  }
 
-    const cacheData: QueryCacheData = { entities: [], relations: [] };
+  const cacheData: QueryCacheData = { entities: [], relations: [] };
 
-    if (id) {
-        cacheData.entities.push({ id, queryId: "" });
-    }
+  if (id) {
+    cacheData.entities.push({ id, queryId: '' });
+  }
 
-    let cypher = `(${id}${label})`;
+  let cypher = `(${id}${label})`;
 
-    if (JSONQuery.relation) {
-        const [relationCypher, subCache] = getRelationCypher(JSONQuery.relation);
+  if (JSONQuery.relation) {
+    const [relationCypher, subCache] = getRelationCypher(JSONQuery.relation);
 
-        if (relationCypher) {
-            cacheData.entities.push(...subCache.entities);
-            cacheData.relations.push(...subCache.relations);
-            if (JSONQuery.relation.direction === "FROM") {
-                cypher += `<-${relationCypher}`;
-            } else if (JSONQuery.relation.direction === "TO") {
-                cypher += `-${relationCypher}`;
-            } else {
-                cypher += `-${relationCypher}`;
-            }
-        }
+    if (relationCypher) {
+      cacheData.entities.push(...subCache.entities);
+      cacheData.relations.push(...subCache.relations);
+      if (JSONQuery.relation.direction === 'FROM') {
+        cypher += `<-${relationCypher}`;
+      } else if (JSONQuery.relation.direction === 'TO') {
+        cypher += `-${relationCypher}`;
+      } else {
+        cypher += `-${relationCypher}`;
+      }
     }
-    return [cypher, cacheData];
-}
\ No newline at end of file
+  }
+  return [cypher, cacheData];
+}
diff --git a/src/utils/cypher/converter/queryConverter.test.ts b/src/utils/cypher/converter/queryConverter.test.ts
index becdd4c..bb3cd7e 100644
--- a/src/utils/cypher/converter/queryConverter.test.ts
+++ b/src/utils/cypher/converter/queryConverter.test.ts
@@ -1,318 +1,318 @@
-import { query2Cypher } from "./queryConverter";
-import type { BackendQueryFormat } from "ts-common";
-import { expect, test, describe, it } from "bun:test";
+import { query2Cypher } from './queryConverter';
+import type { BackendQueryFormat } from 'ts-common';
+import { expect, test, describe, it } from 'bun:test';
 
 function fixCypherSpaces(cypher?: string | null): string {
-    if (!cypher) {
-        return "";
-    }
-
-    let trimmedCypher = cypher.replace(/\n/g, " ");
-    trimmedCypher = trimmedCypher.replaceAll(/ {2,50}/g, " ");
-    trimmedCypher = trimmedCypher.replace(/\t+/g, "");
-    return trimmedCypher.trim();
+  if (!cypher) {
+    return '';
+  }
+
+  let trimmedCypher = cypher.replace(/\n/g, ' ');
+  trimmedCypher = trimmedCypher.replaceAll(/ {2,50}/g, ' ');
+  trimmedCypher = trimmedCypher.replace(/\t+/g, '');
+  return trimmedCypher.trim();
 }
 
-describe("query2Cypher", () => {
-    it("should return correctly on a simple query with multiple paths", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            return: ["*"],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            label: "DIRECTED",
-                            direction: "TO",
-                            depth: { min: 1, max: 1 },
-                            node: {
-                                label: "Movie",
-                                id: "m1"
-                            }
-                        }
-                    }
-                },
-                {
-                    id: "path2",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            label: "IN_GENRE",
-                            direction: "TO",
-                            depth: { min: 1, max: 1 },
-                            node: {
-                                label: "Genre",
-                                id: "g1"
-                            }
-                        }
-                    }
-                }
-            ],
-            limit: 5000
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
+describe('query2Cypher', () => {
+  it('should return correctly on a simple query with multiple paths', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      return: ['*'],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              label: 'DIRECTED',
+              direction: 'TO',
+              depth: { min: 1, max: 1 },
+              node: {
+                label: 'Movie',
+                id: 'm1',
+              },
+            },
+          },
+        },
+        {
+          id: 'path2',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              label: 'IN_GENRE',
+              direction: 'TO',
+              depth: { min: 1, max: 1 },
+              node: {
+                label: 'Genre',
+                id: 'g1',
+              },
+            },
+          },
+        },
+      ],
+      limit: 5000,
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
         MATCH path2 = ((p1:Person)-[:IN_GENRE*1..1]->(g1:Genre))
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a complex query with logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            return: ["*"],
-            logic: ["!=", "@p1.name", "\"Raymond Campbell\""],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            label: "DIRECTED",
-                            direction: "TO",
-                            depth: { min: 1, max: 1 },
-                            node: {
-                                label: "Movie",
-                                id: "m1"
-                            }
-                        }
-                    }
-                },
-                {
-                    id: "path2",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            label: "IN_GENRE",
-                            direction: "TO",
-                            depth: { min: 1, max: 1 },
-                            node: {
-                                label: "Genre",
-                                id: "g1"
-                            }
-                        }
-                    }
-                }
-            ],
-            limit: 5000
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a complex query with logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      return: ['*'],
+      logic: ['!=', '@p1.name', '"Raymond Campbell"'],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              label: 'DIRECTED',
+              direction: 'TO',
+              depth: { min: 1, max: 1 },
+              node: {
+                label: 'Movie',
+                id: 'm1',
+              },
+            },
+          },
+        },
+        {
+          id: 'path2',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              label: 'IN_GENRE',
+              direction: 'TO',
+              depth: { min: 1, max: 1 },
+              node: {
+                label: 'Genre',
+                id: 'g1',
+              },
+            },
+          },
+        },
+      ],
+      limit: 5000,
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
         MATCH path2 = ((p1:Person)-[:IN_GENRE*1..1]->(g1:Genre)) 
         WHERE (p1.name <> "Raymond Campbell")
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with group by logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 5000,
-            logic: ["And", ["<", "@movie.imdbRating", 7.5], ["==", "p2.age", "p1.age"]],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            id: "acted",
-                            label: "ACTED_IN",
-                            depth: { min: 1, max: 1 },
-                            direction: "TO",
-                            node: {
-                                label: "Movie",
-                                id: "movie"
-                            }
-                        }
-                    }
-                },
-                {
-                    id: "path2",
-                    node: {
-                        label: "Person",
-                        id: "p2"
-                    }
-                }
-            ],
-            return: ["@path2"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with group by logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 5000,
+      logic: ['And', ['<', '@movie.imdbRating', 7.5], ['==', 'p2.age', 'p1.age']],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              id: 'acted',
+              label: 'ACTED_IN',
+              depth: { min: 1, max: 1 },
+              direction: 'TO',
+              node: {
+                label: 'Movie',
+                id: 'movie',
+              },
+            },
+          },
+        },
+        {
+          id: 'path2',
+          node: {
+            label: 'Person',
+            id: 'p2',
+          },
+        },
+      ],
+      return: ['@path2'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
         MATCH path2 = ((p2:Person)) 
         WHERE ((movie.imdbRating < 7.5) and (p2.age = p1.age))
         RETURN path2 LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with no label", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 5000,
-            logic: ["<", ["-", "@movie.year", "p1.year"], 10],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        id: "p1",
-                        filter: [],
-                        relation: {
-                            id: "acted",
-                            depth: { min: 1, max: 1 },
-                            direction: "TO",
-                            node: {
-                                label: "Movie",
-                                id: "movie"
-                            }
-                        }
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1)-[acted*1..1]->(movie:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with no label', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 5000,
+      logic: ['<', ['-', '@movie.year', 'p1.year'], 10],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            id: 'p1',
+            filter: [],
+            relation: {
+              id: 'acted',
+              depth: { min: 1, max: 1 },
+              direction: 'TO',
+              node: {
+                label: 'Movie',
+                id: 'movie',
+              },
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1)-[acted*1..1]->(movie:Movie))
         WHERE ((movie.year - p1.year) < 10)
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with no depth", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 5000,
-            logic: ["And", ["<", "@movie.imdbRating", 7.5], ["==", "p2.age", "p1.age"]],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        id: "p1",
-                        relation: {
-                            id: "acted",
-                            direction: "TO",
-                            node: {
-                                label: "Movie",
-                                id: "movie"
-                            }
-                        }
-                    }
-                },
-                {
-                    id: "path2",
-                    node: {
-                        id: "p2"
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1)-[acted]->(movie:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with no depth', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 5000,
+      logic: ['And', ['<', '@movie.imdbRating', 7.5], ['==', 'p2.age', 'p1.age']],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            id: 'p1',
+            relation: {
+              id: 'acted',
+              direction: 'TO',
+              node: {
+                label: 'Movie',
+                id: 'movie',
+              },
+            },
+          },
+        },
+        {
+          id: 'path2',
+          node: {
+            id: 'p2',
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1)-[acted]->(movie:Movie))
         MATCH path2 = ((p2)) 
         WHERE ((movie.imdbRating < 7.5) and (p2.age = p1.age))
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with average calculation", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 5000,
-            logic: ["<", "@p1.age", ["Avg", "@p1.age"]],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            id: "acted",
-                            label: "ACTED_IN",
-                            depth: { min: 1, max: 1 },
-                            direction: "TO",
-                            node: {
-                                label: "Movie",
-                                id: "movie"
-                            }
-                        }
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with average calculation', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 5000,
+      logic: ['<', '@p1.age', ['Avg', '@p1.age']],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              id: 'acted',
+              label: 'ACTED_IN',
+              depth: { min: 1, max: 1 },
+              direction: 'TO',
+              node: {
+                label: 'Movie',
+                id: 'movie',
+              },
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
         WITH avg(p1.age) AS p1_age_avg, p1, movie
         MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
         WHERE (p1.age <  p1_age_avg)
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with average calculation and multiple paths", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 5000,
-            logic: ["<", "@p1.age", ["Avg", "@p1.age"]],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            id: "acted",
-                            label: "ACTED_IN",
-                            depth: { min: 1, max: 1 },
-                            direction: "TO",
-                            node: {
-                                label: "Movie",
-                                id: "movie"
-                            }
-                        }
-                    }
-                },
-                {
-                    id: "path2",
-                    node: {
-                        label: "Person",
-                        id: "p2",
-                        relation: {
-                            id: "acted",
-                            label: "ACTED_IN",
-                            depth: { min: 1, max: 1 },
-                            direction: "TO",
-                            node: {
-                                label: "Movie",
-                                id: "movie"
-                            }
-                        }
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with average calculation and multiple paths', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 5000,
+      logic: ['<', '@p1.age', ['Avg', '@p1.age']],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              id: 'acted',
+              label: 'ACTED_IN',
+              depth: { min: 1, max: 1 },
+              direction: 'TO',
+              node: {
+                label: 'Movie',
+                id: 'movie',
+              },
+            },
+          },
+        },
+        {
+          id: 'path2',
+          node: {
+            label: 'Person',
+            id: 'p2',
+            relation: {
+              id: 'acted',
+              label: 'ACTED_IN',
+              depth: { min: 1, max: 1 },
+              direction: 'TO',
+              node: {
+                label: 'Movie',
+                id: 'movie',
+              },
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
         MATCH path2 = ((p2:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
         WITH avg(p1.age) AS p1_age_avg, p2, movie
         MATCH path1 = ((p1:Person)-[acted:ACTED_IN*1..1]->(movie:Movie))
@@ -320,216 +320,216 @@ describe("query2Cypher", () => {
         WHERE (p1.age <  p1_age_avg)
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a single entity query with lower like logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 5000,
-            logic: ["Like", ["Lower", "@p1.name"], "\"john\""],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1"
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a single entity query with lower like logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 5000,
+      logic: ['Like', ['Lower', '@p1.name'], '"john"'],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person))
         WHERE (toLower(p1.name) =~ (".*" + "john" + ".*"))
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with like logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 500,
-            logic: ["Like", "@id_1691576718400.title", "\"ale\""],
-            query: [
-                {
-                    id: "path_0",
-                    node: {
-                        id: "id_1691576718400",
-                        label: "Employee",
-                        relation: {
-                            id: "id_1691576720177",
-                            label: "REPORTS_TO",
-                            direction: "TO",
-                            node: {}
-                        }
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path_0 = ((id_1691576718400:Employee)-[id_1691576720177:REPORTS_TO]->()) 
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with like logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 500,
+      logic: ['Like', '@id_1691576718400.title', '"ale"'],
+      query: [
+        {
+          id: 'path_0',
+          node: {
+            id: 'id_1691576718400',
+            label: 'Employee',
+            relation: {
+              id: 'id_1691576720177',
+              label: 'REPORTS_TO',
+              direction: 'TO',
+              node: {},
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path_0 = ((id_1691576718400:Employee)-[id_1691576720177:REPORTS_TO]->()) 
         WHERE (id_1691576718400.title =~ (".*" + "ale" + ".*")) 
         RETURN * LIMIT 500`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with both direction relation", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 500,
-            logic: ["Like", "@id_1691576718400.title", "\"ale\""],
-            query: [
-                {
-                    id: "path_0",
-                    node: {
-                        id: "id_1691576718400",
-                        label: "Employee",
-                        relation: {
-                            id: "id_1691576720177",
-                            label: "REPORTS_TO",
-                            direction: "BOTH",
-                            node: {}
-                        }
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path_0 = ((id_1691576718400:Employee)-[id_1691576720177:REPORTS_TO]-()) 
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with both direction relation', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 500,
+      logic: ['Like', '@id_1691576718400.title', '"ale"'],
+      query: [
+        {
+          id: 'path_0',
+          node: {
+            id: 'id_1691576718400',
+            label: 'Employee',
+            relation: {
+              id: 'id_1691576720177',
+              label: 'REPORTS_TO',
+              direction: 'BOTH',
+              node: {},
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path_0 = ((id_1691576718400:Employee)-[id_1691576720177:REPORTS_TO]-()) 
         WHERE (id_1691576718400.title =~ (".*" + "ale" + ".*")) 
         RETURN * LIMIT 500`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with relation logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 500,
-            logic: ["<", "@id_1698231933579.unitPrice", "10"],
-            query: [
-                {
-                    id: "path_0",
-                    node: {
-                        relation: {
-                            id: "id_1698231933579",
-                            label: "CONTAINS",
-                            depth: { min: 0, max: 1 },
-                            direction: "TO",
-                            node: {}
-                        }
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path_0 = (()-[id_1698231933579:CONTAINS*0..1]->())
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with relation logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 500,
+      logic: ['<', '@id_1698231933579.unitPrice', '10'],
+      query: [
+        {
+          id: 'path_0',
+          node: {
+            relation: {
+              id: 'id_1698231933579',
+              label: 'CONTAINS',
+              depth: { min: 0, max: 1 },
+              direction: 'TO',
+              node: {},
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path_0 = (()-[id_1698231933579:CONTAINS*0..1]->())
         WHERE ALL(path_0_rel_id_1698231933579 in id_1698231933579 WHERE (path_0_rel_id_1698231933579.unitPrice < 10))
         RETURN * LIMIT 500`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with count logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            return: ["*"],
-            logic: [">", ["Count", "@p1"], "1"],
-            query: [
-                {
-                    id: "path1",
-                    node: {
-                        label: "Person",
-                        id: "p1",
-                        relation: {
-                            label: "DIRECTED",
-                            direction: "TO",
-                            depth: { min: 1, max: 1 },
-                            node: {
-                                label: "Movie",
-                                id: "m1"
-                            }
-                        }
-                    }
-                }
-            ],
-            limit: 5000
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with count logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      return: ['*'],
+      logic: ['>', ['Count', '@p1'], '1'],
+      query: [
+        {
+          id: 'path1',
+          node: {
+            label: 'Person',
+            id: 'p1',
+            relation: {
+              label: 'DIRECTED',
+              direction: 'TO',
+              depth: { min: 1, max: 1 },
+              node: {
+                label: 'Movie',
+                id: 'm1',
+              },
+            },
+          },
+        },
+      ],
+      limit: 5000,
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
         WITH count(p1) AS p1_count, m1
         MATCH path1 = ((p1:Person)-[:DIRECTED*1..1]->(m1:Movie))
         WHERE (p1_count > 1)
         RETURN * LIMIT 5000`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with empty relation", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 500,
-            query: [
-                {
-                    "id": "path_0",
-                    "node": {
-                        "label": "Movie",
-                        "id": "id_1730483610947",
-                        "relation": {
-                            "label": "",
-                            "id": "",
-                            "depth": {
-                                "min": 0,
-                                "max": 0
-                            },
-                        },
-                    }
-                }
-            ],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path_0 = ((id_1730483610947:Movie))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with empty relation', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 500,
+      query: [
+        {
+          id: 'path_0',
+          node: {
+            label: 'Movie',
+            id: 'id_1730483610947',
+            relation: {
+              label: '',
+              id: '',
+              depth: {
+                min: 0,
+                max: 0,
+              },
+            },
+          },
+        },
+      ],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path_0 = ((id_1730483610947:Movie))
         RETURN * LIMIT 500`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
-
-    it("should return correctly on a query with upper case logic", () => {
-        const query: BackendQueryFormat = {
-            saveStateID: "test",
-            limit: 500,
-            query: [
-                {
-                    id: "path_0",
-                    node: {
-                        id: "id_1731428699410",
-                        label: "Character"
-                    }
-                }
-            ],
-            logic: ["Upper", "@id_1731428699410.name"],
-            return: ["*"]
-        };
-
-        const cypher = query2Cypher(query);
-        const expectedCypher = `MATCH path_0 = ((id_1731428699410:Character))
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
+
+  it('should return correctly on a query with upper case logic', () => {
+    const query: BackendQueryFormat = {
+      saveStateID: 'test',
+      limit: 500,
+      query: [
+        {
+          id: 'path_0',
+          node: {
+            id: 'id_1731428699410',
+            label: 'Character',
+          },
+        },
+      ],
+      logic: ['Upper', '@id_1731428699410.name'],
+      return: ['*'],
+    };
+
+    const cypher = query2Cypher(query);
+    const expectedCypher = `MATCH path_0 = ((id_1731428699410:Character))
         WHERE toUpper(id_1731428699410.name)
         RETURN *
         LIMIT 500`;
 
-        expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
-    });
+    expect(fixCypherSpaces(cypher)).toBe(fixCypherSpaces(expectedCypher));
+  });
 });
diff --git a/src/utils/cypher/converter/queryConverter.ts b/src/utils/cypher/converter/queryConverter.ts
index 5eeacc7..240da9b 100644
--- a/src/utils/cypher/converter/queryConverter.ts
+++ b/src/utils/cypher/converter/queryConverter.ts
@@ -3,79 +3,81 @@
  © Copyright Utrecht University(Department of Information and Computing Sciences)
 */
 
-import type { BackendQueryFormat } from "ts-common";
-import { extractLogicCypher } from "./logic";
-import { extractExportCypher } from "./export";
-import type { QueryCacheData } from "./model";
-import { getNodeCypher } from "./node";
-
+import type { BackendQueryFormat } from 'ts-common';
+import { extractLogicCypher } from './logic';
+import { extractExportCypher } from './export';
+import type { QueryCacheData } from './model';
+import { getNodeCypher } from './node';
 
 // formQuery uses the hierarchy to create cypher for each part of the query in the right order
 export function query2Cypher(JSONQuery: BackendQueryFormat): string | null {
-    let totalQuery = "";
-    let matchQuery = "";
-    let cacheData: QueryCacheData = { entities: [], relations: [] };
-
-    // MATCH block
-    for (const query of JSONQuery.query) {
-        let match = "MATCH ";
-        const [nodeCypher, _cacheData] = getNodeCypher(query.node);
+  let totalQuery = '';
+  let matchQuery = '';
+  let cacheData: QueryCacheData = { entities: [], relations: [] };
 
-        cacheData = _cacheData;
-        for (let i = 0; i < cacheData.entities.length; i++) {
-            cacheData.entities[i].queryId = query.id;
-        }
-        for (let i = 0; i < cacheData.relations.length; i++) {
-            cacheData.relations[i].queryId = query.id;
-        }
+  // MATCH block
+  for (const query of JSONQuery.query) {
+    let match = 'MATCH ';
+    const [nodeCypher, _cacheData] = getNodeCypher(query.node);
 
-        // Generate cypher query for path
-        if (query.id) {
-            match += `${query.id} = (${nodeCypher})`;
-        } else {
-            match += nodeCypher;
-        }
+    cacheData = _cacheData;
+    for (let i = 0; i < cacheData.entities.length; i++) {
+      cacheData.entities[i].queryId = query.id;
+    }
+    for (let i = 0; i < cacheData.relations.length; i++) {
+      cacheData.relations[i].queryId = query.id;
+    }
 
-        // Exports (not used right now)
-        const exports = extractExportCypher(query.node);
+    // Generate cypher query for path
+    if (query.id) {
+      match += `${query.id} = (${nodeCypher})`;
+    } else {
+      match += nodeCypher;
+    }
 
-        if (exports.length > 0 && exports[0]) {
-            match += " WITH " + exports.join(", ");
-        }
+    // Exports (not used right now)
+    const exports = extractExportCypher(query.node);
 
-        matchQuery = match.replace(/@/g, "").replace(/\\"/g, "\"") + "\n";
-        totalQuery += matchQuery;
+    if (exports.length > 0 && exports[0]) {
+      match += ' WITH ' + exports.join(', ');
     }
 
-    // logic calculation for WHERE block
-    if (JSONQuery.logic) {
-        const [_logic, whereLogic] = extractLogicCypher(JSONQuery.logic, cacheData);
+    matchQuery = match.replace(/@/g, '').replace(/\\"/g, '"') + '\n';
+    totalQuery += matchQuery;
+  }
 
-        let logic = _logic as string;
+  // logic calculation for WHERE block
+  if (JSONQuery.logic) {
+    const [_logic, whereLogic] = extractLogicCypher(JSONQuery.logic, cacheData);
 
-        for (const relation of cacheData.relations) {
-            // if we need to do logic on relations, this with is required
-            if (relation.queryId && logic.includes(relation.id)) {
-                logic = `ALL(${relation.queryId}_rel_${relation.id} in ${relation.id} WHERE ${logic.replace(relation.id, `${relation.queryId}_rel_${relation.id}`)})`;
-            }
-        }
-        let totalQueryWithLogic = totalQuery;
-        if (whereLogic) {
-            totalQueryWithLogic += `WITH ${whereLogic}\n`;
-            totalQuery = totalQueryWithLogic + totalQuery;
-        }
-        totalQuery += `WHERE ${logic}\n`;
-    }
+    let logic = _logic as string;
 
-    // RETURN block
-    if (JSONQuery.return.length === 0 || JSONQuery.return[0] === "*") {
-        totalQuery += "RETURN *";
-    } else {
-        totalQuery += "RETURN " + JSONQuery.return.join(", ").replace(/@/g, "");
+    for (const relation of cacheData.relations) {
+      // if we need to do logic on relations, this with is required
+      if (relation.queryId && logic.includes(relation.id)) {
+        logic = `ALL(${relation.queryId}_rel_${relation.id} in ${relation.id} WHERE ${logic.replace(
+          relation.id,
+          `${relation.queryId}_rel_${relation.id}`,
+        )})`;
+      }
     }
+    let totalQueryWithLogic = totalQuery;
+    if (whereLogic) {
+      totalQueryWithLogic += `WITH ${whereLogic}\n`;
+      totalQuery = totalQueryWithLogic + totalQuery;
+    }
+    totalQuery += `WHERE ${logic}\n`;
+  }
+
+  // RETURN block
+  if (JSONQuery.return.length === 0 || JSONQuery.return[0] === '*') {
+    totalQuery += 'RETURN *';
+  } else {
+    totalQuery += 'RETURN ' + JSONQuery.return.join(', ').replace(/@/g, '');
+  }
 
-    // LIMIT block
-    totalQuery += `\nLIMIT ${JSONQuery.limit}`;
+  // LIMIT block
+  totalQuery += `\nLIMIT ${JSONQuery.limit}`;
 
-    return totalQuery;
+  return totalQuery;
 }
diff --git a/src/utils/cypher/converter/relation.ts b/src/utils/cypher/converter/relation.ts
index 8a96316..9689a60 100644
--- a/src/utils/cypher/converter/relation.ts
+++ b/src/utils/cypher/converter/relation.ts
@@ -1,45 +1,45 @@
-import type { RelationStruct } from "ts-common";
-import { getNodeCypher } from "./node";
-import type { QueryCacheData } from "./model";
+import type { RelationStruct } from 'ts-common';
+import { getNodeCypher } from './node';
+import type { QueryCacheData } from './model';
 
-export const NoNodeError = new Error("relation must have a node");
+export const NoNodeError = new Error('relation must have a node');
 
 export function getRelationCypher(JSONQuery: RelationStruct): [string | undefined, QueryCacheData] {
-    let label = "";
-    if (JSONQuery.label) {
-        label = `:${JSONQuery.label}`;
-    }
-    let id = "";
-    if (JSONQuery.id) {
-        id = JSONQuery.id;
-    }
-    let depth = "";
-    if (JSONQuery.depth) {
-        depth = `*${JSONQuery.depth.min}..${JSONQuery.depth.max}`;
-    }
-
-    const cacheData: QueryCacheData = { entities: [], relations: [] };
-
-    if (id) {
-        cacheData.relations.push({ id, queryId: "" });
-    }
-
-    let cypher = `[${id}${label}${depth}]`;
-
-    if (!JSONQuery.node) {
-        return [undefined, cacheData];
-    }
-
-    const [nodeCypher, subCache] = getNodeCypher(JSONQuery.node);
-
-    cacheData.entities.push(...subCache.entities);
-    cacheData.relations.push(...subCache.relations);
-    if (JSONQuery.direction === "TO") {
-        cypher += `->${nodeCypher}`;
-    } else if (JSONQuery.direction === "FROM") {
-        cypher += `-${nodeCypher}`;
-    } else {
-        cypher += `-${nodeCypher}`;
-    }
-    return [cypher, cacheData];
-}
\ No newline at end of file
+  let label = '';
+  if (JSONQuery.label) {
+    label = `:${JSONQuery.label}`;
+  }
+  let id = '';
+  if (JSONQuery.id) {
+    id = JSONQuery.id;
+  }
+  let depth = '';
+  if (JSONQuery.depth) {
+    depth = `*${JSONQuery.depth.min}..${JSONQuery.depth.max}`;
+  }
+
+  const cacheData: QueryCacheData = { entities: [], relations: [] };
+
+  if (id) {
+    cacheData.relations.push({ id, queryId: '' });
+  }
+
+  let cypher = `[${id}${label}${depth}]`;
+
+  if (!JSONQuery.node) {
+    return [undefined, cacheData];
+  }
+
+  const [nodeCypher, subCache] = getNodeCypher(JSONQuery.node);
+
+  cacheData.entities.push(...subCache.entities);
+  cacheData.relations.push(...subCache.relations);
+  if (JSONQuery.direction === 'TO') {
+    cypher += `->${nodeCypher}`;
+  } else if (JSONQuery.direction === 'FROM') {
+    cypher += `-${nodeCypher}`;
+  } else {
+    cypher += `-${nodeCypher}`;
+  }
+  return [cypher, cacheData];
+}
diff --git a/src/utils/cypher/queryParser.ts b/src/utils/cypher/queryParser.ts
index d6c21ea..9b61108 100644
--- a/src/utils/cypher/queryParser.ts
+++ b/src/utils/cypher/queryParser.ts
@@ -1,150 +1,169 @@
-import { Record, Node, Relationship, Path, PathSegment, isRelationship, isNode, isPath, isPathSegment, Integer, isUnboundRelationship, type RecordShape } from 'neo4j-driver';
+import {
+  Record,
+  Node,
+  Relationship,
+  Path,
+  PathSegment,
+  isRelationship,
+  isNode,
+  isPath,
+  isPathSegment,
+  Integer,
+  isUnboundRelationship,
+  type RecordShape,
+} from 'neo4j-driver';
 import { log } from '../../logger';
 import type { EdgeQueryResult, NodeQueryResult } from 'ts-common';
 import type { GraphQueryResultFromBackend } from 'ts-common';
 
-export function parseCypherQuery(result: RecordShape[], returnType: "nodelink" | "table" = "nodelink"): GraphQueryResultFromBackend {
+export function parseCypherQuery(result: RecordShape[], returnType: 'nodelink' | 'table' = 'nodelink'): GraphQueryResultFromBackend {
+  try {
     try {
-        try {
-            switch (returnType) {
-                case "nodelink":
-                    return parseNodeLinkQuery(result);
-                case "table":
-                    // TODO: add table support later
-                    // return parseGroupByQuery(result.records as any);
-                    log.error(`Table format not supported yet`);
-                    throw new Error("Table format not supported yet");
-                default:
-                    log.error(`Error Unknown query Format`);
-                    throw new Error("Unknown query Format");
-            }
-        } catch (err) {
-            log.error(`Parsing failed ${err}`);
-            throw err;
-        }
-
+      switch (returnType) {
+        case 'nodelink':
+          return parseNodeLinkQuery(result);
+        case 'table':
+          // TODO: add table support later
+          // return parseGroupByQuery(result.records as any);
+          log.error(`Table format not supported yet`);
+          throw new Error('Table format not supported yet');
+        default:
+          log.error(`Error Unknown query Format`);
+          throw new Error('Unknown query Format');
+      }
     } catch (err) {
-        log.error(`Error executing query`, err);
-        throw err;
+      log.error(`Parsing failed ${err}`);
+      throw err;
     }
+  } catch (err) {
+    log.error(`Error executing query`, err);
+    throw err;
+  }
 }
 function parseNodeLinkQuery(results: RecordShape[]): GraphQueryResultFromBackend {
-    let nodes: NodeQueryResult[] = [];
-    let edges: EdgeQueryResult[] = [];
-    let nodeIds: Set<string> = new Set();
-    let edgeIds: Set<string> = new Set();
-    const result: GraphQueryResultFromBackend = { nodes, edges };
-
-    for (let i = 0; i < results.length; i++) {
-        const resList = results[i];
-        for (let j = 0; j < resList.length; j++) {
-            const res = resList.get(j);
-            parseNodeLinkEntity(res, nodes, edges, nodeIds, edgeIds);
-        }
+  const nodes: NodeQueryResult[] = [];
+  const edges: EdgeQueryResult[] = [];
+  const nodeIds: Set<string> = new Set();
+  const edgeIds: Set<string> = new Set();
+  const result: GraphQueryResultFromBackend = { nodes, edges };
+
+  for (let i = 0; i < results.length; i++) {
+    const resList = results[i];
+    for (let j = 0; j < resList.length; j++) {
+      const res = resList.get(j);
+      parseNodeLinkEntity(res, nodes, edges, nodeIds, edgeIds);
     }
+  }
 
-    return result;
+  return result;
 }
 
-function parseNodeLinkEntity(res: Node | Relationship | Path | PathSegment | any, nodes: NodeQueryResult[], edges: EdgeQueryResult[], nodeIds: Set<string>, edgeIds: Set<string>): void {
-    if (isRelationship(res)) {
-        const neoEdge = parseEdge(res);
-        if (!edgeIds.has(neoEdge._id)) {
-            edgeIds.add(neoEdge._id);
-            edges.push(neoEdge);
-        }
-    } else if (isNode(res)) {
-        const neoNode = parseNode(res);
-        if (!nodeIds.has(neoNode._id)) {
-            nodeIds.add(neoNode._id);
-            nodes.push(neoNode);
-        }
-    } else if (isPath(res)) {
-        parseNodeLinkEntity(res.start, nodes, edges, nodeIds, edgeIds);
-        for (const segment of res.segments) {
-            parseNodeLinkEntity(segment.relationship, nodes, edges, nodeIds, edgeIds);
-            parseNodeLinkEntity(segment.end, nodes, edges, nodeIds, edgeIds);
-        }
-        parseNodeLinkEntity(res.end, nodes, edges, nodeIds, edgeIds);
-    } else if (isPathSegment(res)) {
-        parseNodeLinkEntity(res.start, nodes, edges, nodeIds, edgeIds);
-        parseNodeLinkEntity(res.relationship, nodes, edges, nodeIds, edgeIds);
-        parseNodeLinkEntity(res.end, nodes, edges, nodeIds, edgeIds);
-    } else if (res?.constructor?.name === "Array") {
-        for (const r of res as any) {
-            parseNodeLinkEntity(r, nodes, edges, nodeIds, edgeIds);
-        }
-    } else {
-        log.warn(`Ignoring Unknown type ${res} ${res?.constructor?.name}`);
+function parseNodeLinkEntity(
+  res: Node | Relationship | Path | PathSegment | any,
+  nodes: NodeQueryResult[],
+  edges: EdgeQueryResult[],
+  nodeIds: Set<string>,
+  edgeIds: Set<string>,
+): void {
+  if (isRelationship(res)) {
+    const neoEdge = parseEdge(res);
+    if (!edgeIds.has(neoEdge._id)) {
+      edgeIds.add(neoEdge._id);
+      edges.push(neoEdge);
+    }
+  } else if (isNode(res)) {
+    const neoNode = parseNode(res);
+    if (!nodeIds.has(neoNode._id)) {
+      nodeIds.add(neoNode._id);
+      nodes.push(neoNode);
+    }
+  } else if (isPath(res)) {
+    parseNodeLinkEntity(res.start, nodes, edges, nodeIds, edgeIds);
+    for (const segment of res.segments) {
+      parseNodeLinkEntity(segment.relationship, nodes, edges, nodeIds, edgeIds);
+      parseNodeLinkEntity(segment.end, nodes, edges, nodeIds, edgeIds);
+    }
+    parseNodeLinkEntity(res.end, nodes, edges, nodeIds, edgeIds);
+  } else if (isPathSegment(res)) {
+    parseNodeLinkEntity(res.start, nodes, edges, nodeIds, edgeIds);
+    parseNodeLinkEntity(res.relationship, nodes, edges, nodeIds, edgeIds);
+    parseNodeLinkEntity(res.end, nodes, edges, nodeIds, edgeIds);
+  } else if (res?.constructor?.name === 'Array') {
+    for (const r of res as any) {
+      parseNodeLinkEntity(r, nodes, edges, nodeIds, edgeIds);
     }
+  } else {
+    log.warn(`Ignoring Unknown type ${res} ${res?.constructor?.name}`);
+  }
 }
 
 function parseNode(node: Node): NodeQueryResult {
-    return {
-        _id: node.identity.toString(),
-        label: node.labels[0], // TODO: only using first label
-        attributes: Object.fromEntries(Object.entries(node.properties)?.map(([k, v]) => {
-            if (Integer.isInteger(v)) {
-                return [k, v.toNumber()];
-            } else if (typeof v === 'string') {
-                return [k, v];
-            } else if (v instanceof Object) {
-                return [k, v.toString()];
-            }
+  return {
+    _id: node.identity.toString(),
+    label: node.labels[0], // TODO: only using first label
+    attributes: Object.fromEntries(
+      Object.entries(node.properties)?.map(([k, v]) => {
+        if (Integer.isInteger(v)) {
+          return [k, v.toNumber()];
+        } else if (typeof v === 'string') {
+          return [k, v];
+        } else if (v instanceof Object) {
+          return [k, v.toString()];
+        }
 
-            return [k, v];
-        })),
-    };
+        return [k, v];
+      }),
+    ),
+  };
 }
 
 function parseEdge(edge: Relationship): EdgeQueryResult {
-    return {
-        _id: edge.identity.toString(),
-        label: edge.type,
-        from: edge.start.toString(),
-        to: edge.end.toString(),
-        attributes: { ...edge.properties, type: edge.type },
-    };
+  return {
+    _id: edge.identity.toString(),
+    label: edge.type,
+    from: edge.start.toString(),
+    to: edge.end.toString(),
+    attributes: { ...edge.properties, type: edge.type },
+  };
 }
 
-
 function parseGroupByQuery(results: Record<any, string>[]): any {
-    // TODO: not tested, since we don't use this yet
-
-    let groupKey: string;
-    let byKey: string;
-    const group: string[] = [];
-    const by: string[] = [];
-    const result: { [key: string]: any } = {};
-
-    if (results[0].keys.length !== 2) {
-        throw new Error("invalid result format");
-    }
-
-    // Checks if the first letter of the first key is either an e or an r, since that would make it the By-key
-    // By-keys start with e0_attr or r0_attr and group keys with an aggregation funcion such as AVG_attr or MIN_attr
-    // This will work when an aggregation function starts with an E or R, since e != E
-    if (results[0].keys[0][0] === 'e' || results[0].keys[0][0] === 'r') {
-        byKey = results[0].keys[0];
-        groupKey = results[0].keys[1];
-    } else {
-        byKey = results[0].keys[1];
-        groupKey = results[0].keys[0];
+  // TODO: not tested, since we don't use this yet
+
+  let groupKey: string;
+  let byKey: string;
+  const group: string[] = [];
+  const by: string[] = [];
+  const result: { [key: string]: any } = {};
+
+  if (results[0].keys.length !== 2) {
+    throw new Error('invalid result format');
+  }
+
+  // Checks if the first letter of the first key is either an e or an r, since that would make it the By-key
+  // By-keys start with e0_attr or r0_attr and group keys with an aggregation funcion such as AVG_attr or MIN_attr
+  // This will work when an aggregation function starts with an E or R, since e != E
+  if (results[0].keys[0][0] === 'e' || results[0].keys[0][0] === 'r') {
+    byKey = results[0].keys[0];
+    groupKey = results[0].keys[1];
+  } else {
+    byKey = results[0].keys[1];
+    groupKey = results[0].keys[0];
+  }
+
+  // Form proper result structure
+  for (const res of results) {
+    const g = res.get(groupKey);
+    const b = res.get(byKey);
+
+    if (g !== null && b !== null) {
+      group.push(g.toString());
+      by.push(b.toString());
     }
+  }
 
-    // Form proper result structure
-    for (const res of results) {
-        const g = res.get(groupKey);
-        const b = res.get(byKey);
+  result['group'] = { name: groupKey, values: group };
+  result['by'] = { name: byKey, values: by };
 
-        if (g !== null && b !== null) {
-            group.push(g.toString());
-            by.push(b.toString());
-        }
-    }
-
-    result["group"] = { name: groupKey, values: group };
-    result["by"] = { name: byKey, values: by };
-
-    return result;
-}
\ No newline at end of file
+  return result;
+}
diff --git a/src/utils/queryPublisher.ts b/src/utils/queryPublisher.ts
index feb0751..8922fa0 100644
--- a/src/utils/queryPublisher.ts
+++ b/src/utils/queryPublisher.ts
@@ -111,7 +111,7 @@ export class QueryPublisher {
             mlAttributes: mlAttributes.parameters
         }
 
-        let queueName = mlType2Queue[mlAttributes.type];
+        const queueName = mlType2Queue[mlAttributes.type];
         if (!queueName) {
             log.error('Invalid ML type:', mlAttributes, mlAttributes.type);
             throw new Error('Invalid ML type');
diff --git a/src/utils/reactflow/query2backend.ts b/src/utils/reactflow/query2backend.ts
index 3091656..6965baf 100644
--- a/src/utils/reactflow/query2backend.ts
+++ b/src/utils/reactflow/query2backend.ts
@@ -5,7 +5,13 @@ import { allSimplePaths } from 'graphology-simple-path';
 import type { BackendQueryFormat, NodeStruct, QueryStruct, RelationStruct } from 'ts-common';
 import type { MachineLearning } from 'ts-common/src/model/query/queryRequestModel';
 
-import type { QueryMultiGraph, EntityNodeAttributes, LogicNodeAttributes, QueryGraphNodes, RelationNodeAttributes } from 'ts-common/src/model/graphology';
+import type {
+  QueryMultiGraph,
+  EntityNodeAttributes,
+  LogicNodeAttributes,
+  QueryGraphNodes,
+  RelationNodeAttributes,
+} from 'ts-common/src/model/graphology';
 import type { AllLogicStatement } from 'ts-common/src/model/query/logic/general';
 import { QueryElementTypes } from 'ts-common/src/model/reactflow';
 import { type QueryBuilderSettings, QueryUnionType } from 'ts-common/src/model/query/queryBuilderModel';
@@ -49,11 +55,11 @@ const traverseEntityRelationPaths = (
   paths[currentIdx].push(node.attributes);
 
   const edges = graph.edges.filter(
-    (n) =>
+    n =>
       n?.attributes?.sourceHandleData.nodeType !== QueryElementTypes.Logic &&
       n?.attributes?.targetHandleData.nodeType !== QueryElementTypes.Logic,
   );
-  let connections = edges.filter((e) => e.source === node.key);
+  const 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, attributes: [] });
@@ -70,14 +76,14 @@ const traverseEntityRelationPaths = (
         throw Error('Malformed Graph! One or more edges of a relation node do not exist');
       const rightNode =
         rightNodeHandleData.nodeType === QueryElementTypes.Entity
-          ? entities.find((r) => r.key === c.target)
-          : relations.find((r) => r.key === c.target);
+          ? entities.find(r => r.key === c.target)
+          : relations.find(r => r.key === c.target);
       return rightNode;
     })
-    .filter((n) => n !== undefined) as SerializedNode<QueryGraphNodes>[];
+    .filter(n => n !== undefined) as SerializedNode<QueryGraphNodes>[];
 
   let chunkOffset = 0;
-  let pathBeforeTraversing = [...paths[currentIdx]];
+  const pathBeforeTraversing = [...paths[currentIdx]];
   nodesToRight.forEach((rightNode, i) => {
     if (i > 0) {
       paths.push([...pathBeforeTraversing]); // clone previous path in case of branching
@@ -94,9 +100,9 @@ function calculateQueryLogic(
   logics: SerializedNode<LogicNodeAttributes>[],
 ): AllLogicStatement {
   if (!node?.attributes) throw Error('Malformed Graph! Node has no attributes');
-  let connectionsToLeft = graph.edges.filter((e) => e.target === node.key);
+  const connectionsToLeft = graph.edges.filter(e => e.target === node.key);
 
-  let ret = [...node.attributes.logic.logic].map((l) => {
+  const ret = [...node.attributes.logic.logic].map(l => {
     if (typeof l !== 'string') throw Error('Malformed Graph! Logic node has no logic');
     if (!node.attributes) throw Error('Malformed Graph! Logic node has no attributes');
 
@@ -111,21 +117,21 @@ function calculateQueryLogic(
         throw Error('Malformed Graph! Logic node has incorrect input reference');
       }
 
-      const connectionToInputRef = connectionsToLeft.find((c) => c?.attributes?.targetHandleData.attributeName === inputRef.name);
+      const connectionToInputRef = connectionsToLeft.find(c => c?.attributes?.targetHandleData.attributeName === inputRef.name);
       if (!connectionToInputRef) {
         // Not connected, search for set or default value
         let val = node.attributes.inputs?.[inputRef.name] || inputRef.default;
         if (inputRef.type === 'string') {
           if (val) {
-            val = `\"${val}\"`;
+            val = `"${val}"`;
           } else {
-            val = `\".*\"`; // Empty means allow anything
+            val = `".*"`; // Empty means allow anything
           }
         }
         return val;
       } else if (connectionToInputRef.attributes?.sourceHandleData.nodeType === QueryElementTypes.Logic) {
         // Is connected to another logic node
-        const leftLogic = logics.find((r) => r.key === connectionToInputRef.attributes?.sourceHandleData.nodeId);
+        const leftLogic = logics.find(r => r.key === connectionToInputRef.attributes?.sourceHandleData.nodeId);
         if (!leftLogic) throw Error('Malformed Graph! Logic node is connected but has no logic data');
         return calculateQueryLogic(leftLogic, graph, logics);
       } else {
@@ -151,13 +157,13 @@ function queryLogicUnion(
   logics: SerializedNode<LogicNodeAttributes>[],
   unionTypes: { [node_id: string]: QueryUnionType },
 ): AllLogicStatement | undefined {
-  let graphLogicChunks = nodes.map((node) => calculateQueryLogic(node, graph, logics));
+  const graphLogicChunks = nodes.map(node => calculateQueryLogic(node, graph, logics));
 
   if (graphLogicChunks.length === 0) return undefined;
   if (graphLogicChunks.length === 1) return graphLogicChunks[0];
 
   const constraintNodeId = nodes[0].key;
-  const entityNodeId = graph.edges.filter((x) => x.target == constraintNodeId)[0].source;
+  const entityNodeId = graph.edges.filter(x => x.target == constraintNodeId)[0].source;
   const unionType = unionTypes[entityNodeId] || QueryUnionType.AND;
 
   return [unionType, graphLogicChunks[0], queryLogicUnion(nodes.slice(1), graph, logics, unionTypes) || '0'];
@@ -171,9 +177,9 @@ export function Query2BackendQuery(
   saveStateID: string,
   graph: QueryMultiGraph,
   settings: QueryBuilderSettings,
-  ml: MachineLearning[]
+  ml: MachineLearning[],
 ): BackendQueryFormat {
-  let query: BackendQueryFormat = {
+  const query: BackendQueryFormat = {
     saveStateID: saveStateID,
     query: [],
     machineLearning: ml,
@@ -182,13 +188,13 @@ export function Query2BackendQuery(
     cached: false,
   };
 
-  let entities = graph.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Entity) as SerializedNode<EntityNodeAttributes>[];
-  let relations = graph.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Relation) as SerializedNode<RelationNodeAttributes>[];
+  const entities = graph.nodes.filter(n => n?.attributes?.type === QueryElementTypes.Entity) as SerializedNode<EntityNodeAttributes>[];
+  const relations = graph.nodes.filter(n => n?.attributes?.type === QueryElementTypes.Relation) as SerializedNode<RelationNodeAttributes>[];
 
   const graphologyQuery = Graph.from(graph);
   graphologyQuery
     .filterNodes((n, att) => att?.type == QueryElementTypes.Logic)
-    .forEach((n) => {
+    .forEach(n => {
       graphologyQuery.dropNode(n);
     }); // Remove all logic nodes from the graph for cycle test
   if (hasCycle(graphologyQuery)) {
@@ -216,13 +222,13 @@ export function Query2BackendQuery(
     return Query2BackendQuery(saveStateID, graphologyQuery.export(), settings, ml);
   }
   // Chunk extraction: traverse graph to find all paths of logic between relations and entities
-  let graphSequenceChunks: QueryGraphNodes[][] = [];
-  let graphSequenceLogicChunks: QueryGraphNodes[][] = [];
-  let graphSequenceChunksIdMap: Record<string, [number, number]> = {};
+  const graphSequenceChunks: QueryGraphNodes[][] = [];
+  const graphSequenceLogicChunks: QueryGraphNodes[][] = [];
+  const graphSequenceChunksIdMap: Record<string, [number, number]> = {};
   let chunkOffset = 0;
 
-  let entitiesEmptyLeftHandle = entities.filter((n) => !graph.edges.some((e) => e.target === n.key));
-  let relationsEmptyLeftHandle = relations.filter((n) => !graph.edges.some((e) => e.target === n.key));
+  const entitiesEmptyLeftHandle = entities.filter(n => !graph.edges.some(e => e.target === n.key));
+  const relationsEmptyLeftHandle = relations.filter(n => !graph.edges.some(e => e.target === n.key));
   // let entitiesEmptyRightHandle = entities.filter((n) => !n?.attributes?.rightRelationHandleId);
   entitiesEmptyLeftHandle.map((entity, i) => {
     // start with all entities that have no left handle, which means it "starts" a logic
@@ -241,12 +247,12 @@ export function Query2BackendQuery(
 
   // Logic pathways extraction: now traverse the graph again though the logic components to construct the logic chunks
   // The traversal is done in reverse order, starting from the logic pill's right handle connected to an entity or relation, and going backwards
-  let logics = graph.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Logic) as SerializedNode<LogicNodeAttributes>[];
-  let logicsRightHandleConnectedOutside = logics.filter((n) => {
-    return graph.edges.some((e) => e.source === n.key && e.attributes?.targetHandleData.nodeType === QueryElementTypes.Entity);
+  const logics = graph.nodes.filter(n => n?.attributes?.type === QueryElementTypes.Logic) as SerializedNode<LogicNodeAttributes>[];
+  const logicsRightHandleConnectedOutside = logics.filter(n => {
+    return graph.edges.some(e => e.source === n.key && e.attributes?.targetHandleData.nodeType === QueryElementTypes.Entity);
   });
-  let logicsRightHandleFinal = logics.filter((n) => {
-    return !graph.edges.some((e) => e.source === n.key);
+  const logicsRightHandleFinal = logics.filter(n => {
+    return !graph.edges.some(e => e.source === n.key);
   });
   query.logic = queryLogicUnion(logicsRightHandleFinal, graph, logics, settings.unionTypes);
 
diff --git a/src/variables.ts b/src/variables.ts
index 108de10..ad788c3 100644
--- a/src/variables.ts
+++ b/src/variables.ts
@@ -1,51 +1,56 @@
-import { RabbitMqConnection, UMSApi } from "ts-common";
-// @ts-ignore
-import nodemailer from "nodemailer";
+import { RabbitMqConnection, UMSApi } from 'ts-common';
+import nodemailer from 'nodemailer';
 
 export type QueryExecutionTypes = 'neo4j';
 
-export const CACHE_KEY_PREFIX = Bun.env.CACHE_KEY_PREFIX || "cached-schemas";
+export const CACHE_KEY_PREFIX = Bun.env.CACHE_KEY_PREFIX || 'cached-schemas';
 export const CACHE_DURATION = Bun.env.CACHE_DURATION ? parseInt(Bun.env.CACHE_DURATION) : 5 * 60 * 1000;
-export const USER_MANAGEMENT_SERVICE_API = Bun.env.USER_MANAGEMENT_SERVICE_API || "http://localhost:8000"
+export const USER_MANAGEMENT_SERVICE_API = Bun.env.USER_MANAGEMENT_SERVICE_API || 'http://localhost:8000';
 export const DEV = Bun.env.DEV || false;
-export const ENV = Bun.env.ENV || "develop";
-export const RABBIT_USER = Bun.env.RABBIT_USER || "rabbitmq";
-export const RABBIT_PASSWORD = Bun.env.RABBIT_PASSWORD || "DevOnlyPass";
-export const RABBIT_HOST = Bun.env.RABBIT_HOST || "localhost";
-export const RABBIT_PORT = parseInt(Bun.env.RABBIT_PORT || "5672");
+export const ENV = Bun.env.ENV || 'develop';
+export const RABBIT_USER = Bun.env.RABBIT_USER || 'rabbitmq';
+export const RABBIT_PASSWORD = Bun.env.RABBIT_PASSWORD || 'DevOnlyPass';
+export const RABBIT_HOST = Bun.env.RABBIT_HOST || 'localhost';
+export const RABBIT_PORT = parseInt(Bun.env.RABBIT_PORT || '5672');
 export const LOG_MESSAGES = Bun.env.LOG_MESSAGES || false;
 export const LOG_LEVEL = parseInt(Bun.env.LOG_LEVEL || '-1');
-export const REDIS_HOST = Bun.env.REDIS_HOST || "localhost";
-export const REDIS_PORT = parseInt(Bun.env.REDIS_PORT || "6379");
-export const REDIS_PASSWORD = Bun.env.REDIS_PASSWORD || "DevOnlyPass";
-export const REDIS_SCHEMA_CACHE_DURATION = Bun.env.REDIS_SCHEMA_CACHE_DURATION || "60m";
+export const REDIS_HOST = Bun.env.REDIS_HOST || 'localhost';
+export const REDIS_PORT = parseInt(Bun.env.REDIS_PORT || '6379');
+export const REDIS_PASSWORD = Bun.env.REDIS_PASSWORD || 'DevOnlyPass';
+export const REDIS_SCHEMA_CACHE_DURATION = Bun.env.REDIS_SCHEMA_CACHE_DURATION || '60m';
 
 export const ums = new UMSApi(USER_MANAGEMENT_SERVICE_API);
 export const rabbitMq = new RabbitMqConnection({
-    protocol: 'amqp',
-    hostname: RABBIT_HOST,
-    port: RABBIT_PORT,
-    username: RABBIT_USER,
-    password: RABBIT_PASSWORD,
+  protocol: 'amqp',
+  hostname: RABBIT_HOST,
+  port: RABBIT_PORT,
+  username: RABBIT_USER,
+  password: RABBIT_PASSWORD,
 });
 
-export const SMTP_HOST = Bun.env.SMTP_HOST || "";
+export const SMTP_HOST = Bun.env.SMTP_HOST || '';
 export const SMTP_PORT = Bun.env.SMTP_PORT || 587;
-export const SMTP_USER = Bun.env.SMTP_USER || "";
-export const SMTP_PASSWORD = Bun.env.SMTP_PASSWORD || "";
-export const DEBUG_EMAIL = (Bun.env.DEBUG_EMAIL !== "false") || false;
+export const SMTP_USER = Bun.env.SMTP_USER || '';
+export const SMTP_PASSWORD = Bun.env.SMTP_PASSWORD || '';
+export const DEBUG_EMAIL = Bun.env.DEBUG_EMAIL !== 'false' || false;
 
-export const mail = SMTP_HOST === '' || SMTP_USER === '' || SMTP_PASSWORD === '' ? undefined : nodemailer.createTransport({
-    // @ts-ignore
-    host: SMTP_HOST,
-    port: SMTP_PORT,
-    secure: false,
-    tls: {
-        ciphers: "SSLv3",
-        rejectUnauthorized: false,
-    },
-    auth: {
-        user: SMTP_USER,
-        pass: SMTP_PASSWORD,
-    }
-}, { debug: true, logger: true });
\ No newline at end of file
+export const mail =
+  SMTP_HOST === '' || SMTP_USER === '' || SMTP_PASSWORD === ''
+    ? undefined
+    : nodemailer.createTransport(
+        {
+          // @ts-expect-error - ciphers is not in the type
+          host: SMTP_HOST,
+          port: SMTP_PORT,
+          secure: false,
+          tls: {
+            ciphers: 'SSLv3',
+            rejectUnauthorized: false,
+          },
+          auth: {
+            user: SMTP_USER,
+            pass: SMTP_PASSWORD,
+          },
+        },
+        { debug: true, logger: true },
+      );
-- 
GitLab