diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
index d242ccc1a067dff0b74a3374a6c5e920fc1776fa..0e321f471415a313f279c80cb814b60ede24f2ec 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
@@ -233,11 +233,8 @@ export const PaohVis = ({ data, graphMetadata, schema, configuration: conf, upda
       const index = className.split('circle-')[1];
       if (currentPageRows) {
         const indexNumber = parseInt(index);
-        const { pageRow } = currentPageRows;
-        if (typeof pageRow === 'number') {
-          const rowSelector = `.row-${indexNumber - (pageRow - 1) * configPaohvis.rowsMaxPerPage}`;
-          select(rowSelector).selectAll('text').attr('fill', configStyle.colorText);
-        }
+        const rowSelector = `.row-${indexNumber - currentPageRows.startIndexRow}`;
+        select(rowSelector).selectAll('text').attr('fill', configStyle.colorText);
       }
     });
   };
diff --git a/libs/shared/lib/vis/visualizations/paohvis/types.ts b/libs/shared/lib/vis/visualizations/paohvis/types.ts
index 80692ced94a5da62379bca8d66e10ff6b0d433a5..0d28f205c1ca7528b3600b4f2a887ea8c4b53f76 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/types.ts
+++ b/libs/shared/lib/vis/visualizations/paohvis/types.ts
@@ -49,6 +49,7 @@ export type HyperEdgeRange2 = {
   rangeText: any;
   hyperEdges: HyperEdgeI2;
   _id: string;
+  reduced_ids: string[];
   degree: number;
 };
 
diff --git a/libs/shared/lib/vis/visualizations/paohvis/utils/ToPaohvisDataParserUsecase.tsx b/libs/shared/lib/vis/visualizations/paohvis/utils/ToPaohvisDataParserUsecase.tsx
index ce9c2ecbfd09fbd37661c0646802986aff6e5f99..908360622329e646eb0636a7ffb5578a197cce74 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/utils/ToPaohvisDataParserUsecase.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/utils/ToPaohvisDataParserUsecase.tsx
@@ -6,239 +6,10 @@
 
 import { getGroupName } from './ResultNodeLinkParserUseCase';
 import { GraphQueryResult, Node } from '@graphpolaris/shared/lib/data-access';
-import { MultiGraph } from 'graphology';
 
-import {
-  PaohvisAxisInfo,
-  PaohvisData,
-  PaohvisFilters,
-  PaohvisNodeInfo,
-  PaohvisNodeOrder,
-  Relation,
-  AugmentedNodeAttributesGraph,
-  HyperEdgeRange2,
-  HyperEdgeI2,
-} from '../types';
+import { PaohvisAxisInfo, PaohvisData, PaohvisFilters, PaohvisNodeInfo, HyperEdgeRange2, HyperEdgeI2 } from '../types';
 import AttributeFilterUsecase, { getIds } from './AttributesFilterUseCase';
-//import SortUseCase from './SortUseCase';
-import {
-  sortObjectsByDegree,
-  countRepetition,
-  getTransformIndices,
-  sortObjectsByName2Show,
-  sortByIndicesLength2,
-  sortByRangeText2,
-} from './utils';
+import { countRepetition } from './utils';
 
 //type Index = number;
 //type Index = number;
-
-/**
- * This parser is used to parse the incoming query result to the format that's needed to make the Paohvis table.
- */
-export class ToPaohvisDataParserUseCase {
-  private queryResult: GraphQueryResult;
-  private xAxisNodeGroup: string;
-  private yAxisNodeGroup: string;
-  private paohvisFilters: PaohvisFilters;
-
-  public constructor(queryResult: GraphQueryResult) {
-    this.queryResult = queryResult;
-
-    this.xAxisNodeGroup = '';
-    this.yAxisNodeGroup = '';
-    this.paohvisFilters = { nodeFilters: [], edgeFilters: [] };
-  }
-  /**
-   * Parses query results to the format that's needed to make a Paohvis table.
-   * @param axisInfo is the information that's needed to parse everything to the correct axis.
-   * @param nodeOrder is the order in which the nodes should be parsed.
-   * @returns the information that's needed to make a Paohvis table.
-   */
-  public parseQueryResult(axisInfo: PaohvisAxisInfo, entityHorizontal: string, entityVertical: string): PaohvisData {
-    console.log('this.paohvisFilters ', this.paohvisFilters);
-
-    this.setAxesNodeGroups(axisInfo, entityHorizontal, entityVertical);
-
-    const augmentedNodes: AugmentedNodeAttributesGraph[] = this.queryResult.nodes.map((node: Node) => ({
-      id: node._id,
-      attributes: node.attributes,
-      label: node._id,
-    }));
-
-    //const nodeA: AugmentedNodeAttributesGraph[] = data.nodes.filter((obj) => obj.id.includes(configuration.rowNode));
-
-    console.log('augmentedNodes ', this.queryResult.nodes);
-
-    const nodesRow: AugmentedNodeAttributesGraph[] = augmentedNodes.filter((node) => {
-      return node.label == this.yAxisNodeGroup;
-    });
-    const nodesColumn: AugmentedNodeAttributesGraph[] = augmentedNodes.filter((node) => {
-      return node.label == this.xAxisNodeGroup;
-    });
-
-    const nodeIdIndex: { [id: string]: number } = nodesRow.reduce((acc, node, index) => ({ ...acc, [node._id]: index }), {});
-
-    const filteredData = AttributeFilterUsecase.applyFilters(this.queryResult, this.paohvisFilters);
-
-    //parse nodes
-    const rowInfo: PaohvisNodeInfo = ToPaohvisDataParserUseCase.parseNodes(nodesRow, this.yAxisNodeGroup, this.xAxisNodeGroup);
-
-    let nodeListAttr: any[] = [];
-    nodeListAttr = nodesRow.map((node) => node._id);
-    nodeListAttr = nodesRow.map((node) => node._id);
-
-    const [resultHyperEdgeRanges, rowsDegree] = ToPaohvisDataParserUseCase.parseHyperEdgeRanges(
-      nodesColumn,
-      nodesRow,
-      filteredData,
-      axisInfo,
-      rowInfo,
-      nodeIdIndex,
-      this.xAxisNodeGroup,
-    );
-
-    return {
-      rowLabels: nodeListAttr,
-      hyperEdgeRanges: resultHyperEdgeRanges,
-      rowDegrees: rowsDegree,
-    };
-  }
-
-  /**
-   * Sets the x-axis and y-axis node groups from the given PaohvisAxisInfo in the parser.
-   * @param axisInfo is the new PaohvisAxisInfo that will be used.
-   */
-  private setAxesNodeGroups(axisInfo: PaohvisAxisInfo, entityHorizontal: string, entityVertical: string): void {
-    this.xAxisNodeGroup = entityHorizontal;
-    this.yAxisNodeGroup = entityVertical;
-  }
-
-  /**
-   * This parses the nodes to get the information that's needed to parse the hyperedges.
-   * @param nodes are the nodes from the query result.
-   * @param hyperEdgeDegree is the dictionary where you can find how many edges connected from the node.
-   * @param nodeOrder is the order in which the nodes should be sorted.
-   * @param yAxisNodeType is the type of nodes that should be on the y-axis.
-   * @param xAxisNodeType is the type of nodes that should be on the x-axis.
-   * @returns the information that's needed to parse the hyperedges.
-   */
-  private static parseNodes(nodes: Node[], yAxisNodeType: string, xAxisNodeType: string): PaohvisNodeInfo {
-    //const rowNodes = filterRowNodes(nodes, hyperEdgeDegree, yAxisNodeType);
-    const rowNodes = nodes; // to list all nodes available, even if they do not have connections
-
-    const rowLabels = getIds(rowNodes);
-
-    //make dictionary for finding the index of a row
-    const yNodesIndexDict: Record<string, number> = {};
-    let yNodeIndexCounter = 0;
-    for (let i = 0; i < rowNodes.length; i++) {
-      yNodesIndexDict[rowNodes[i]._id] = yNodeIndexCounter;
-      yNodeIndexCounter++;
-    }
-
-    const xNodesAttributesDict: Record<string, any> = getXNodesAttributesDict(yAxisNodeType, xAxisNodeType, nodes);
-
-    return {
-      rowLabels: rowLabels,
-      xNodesAttributesDict: xNodesAttributesDict,
-      yNodesIndexDict: yNodesIndexDict,
-    };
-  }
-
-  /**
-   * Parses the edges to make hyperedge ranges for the Paohvis table.
-   * @param nodes the unused nodes should already be filtered out.
-   * @param edges the unused edges should already be filtered out.
-   * @param axisInfo is the information that's needed to parse the edges to their respective hyperedge range.
-   * @param rowInfo is the information about the nodes that's needed to parse the edges to their respective hyperedge range.
-   * @returns the hyperedge ranges that will be used by the Paohvis table.
-   */
-
-  private static parseHyperEdgeRanges(
-    nodesColumn: AugmentedNodeAttributesGraph[],
-    nodesRow: AugmentedNodeAttributesGraph[],
-    filteredData: GraphQueryResult,
-    axisInfo: PaohvisAxisInfo,
-    rowInfo: PaohvisNodeInfo,
-    nodeIdIndex: { [id: string]: number },
-    xAxisNodeGroup: string,
-  ): [HyperEdgeRange2[], { [key: string]: number }] {
-    if (nodesColumn.length == 0 || nodesRow.length == 0) return [[], {}];
-
-    let resultHyperEdgeRanges: HyperEdgeRange2[] = nodesColumn.map((element) => {
-      const rangeText: any = element.attributes[axisInfo.selectedAttribute.name];
-      const id = element._id;
-      const degree = 0;
-      const hyperEdges: HyperEdgeI2 = {
-        indices: [],
-      };
-
-      return {
-        rangeText,
-        hyperEdges,
-        id,
-        degree,
-      };
-    });
-
-    const toOrFrom = axisInfo.relation.to == xAxisNodeGroup ? 'to' : 'from';
-    const toOrFromOpposite = toOrFrom == 'to' ? 'from' : 'to';
-
-    // loop through all edges - and creates hyperedge structure (coluumn order/x)
-    for (let i = 0; i < filteredData.edges.length; i++) {
-      const edge = filteredData.edges[i];
-      const hyperEdgeRange = resultHyperEdgeRanges.find((range) => range._id === edge[toOrFrom])?.hyperEdges;
-      //console.log(i, edge.from, edge.to, nodeIdIndex[edge[toOrFromOpposite]]);
-
-      hyperEdgeRange?.indices.push(nodeIdIndex[edge[toOrFromOpposite]]);
-    }
-
-    // Sequential order in indices and count degree ( remove undefined = nodes without connection )
-    resultHyperEdgeRanges.forEach((element) => {
-      element.degree = element.hyperEdges.indices.filter((num) => {
-        //console.log(num, typeof num);
-        if (typeof num !== 'number') {
-          //console.log('Filtered out:', num);
-        }
-        return typeof num === 'number';
-      }).length;
-    });
-
-    resultHyperEdgeRanges.forEach((element) => {
-      element.hyperEdges.indices.sort((a, b) => a - b);
-    });
-
-    // Count nodes degree from structure
-    const repetitionCounts: { [key: number]: number } = {};
-
-    for (let indexX = 0; indexX < resultHyperEdgeRanges.length; indexX++) {
-      countRepetition(resultHyperEdgeRanges[indexX].hyperEdges.indices, repetitionCounts);
-    }
-    const repetitionCountLabel: { [key: string]: number } = {};
-    // get nodes ids
-    rowInfo.rowLabels.forEach((arrayElement, index) => {
-      repetitionCountLabel[arrayElement] = repetitionCounts[index] === undefined ? 0 : repetitionCounts[index];
-    });
-
-    return [resultHyperEdgeRanges, repetitionCountLabel];
-  }
-
-  /** Sets new PaohvisFilters. */
-  public setPaohvisFilters(filters: PaohvisFilters): void {
-    this.paohvisFilters = filters;
-  }
-}
-
-/** Gets a dictionary where you can find the attributes that belong to the nodes on teh x-axis.  */
-function getXNodesAttributesDict(yAxisNodeType: string, xAxisNodeType: string, nodes: Node[]) {
-  const resultXNodesAttributesDict: Record<string, any> = {};
-  // it goes to case1:
-  if (yAxisNodeType == xAxisNodeType) nodes.forEach((node) => (resultXNodesAttributesDict[node!._id] = node.attributes));
-  //console.log('cas2 ', yAxisNodeType == xAxisNodeType);
-  else
-    nodes.forEach((node) => {
-      if (getGroupName(node) == xAxisNodeType) resultXNodesAttributesDict[node._id] = node.attributes;
-    });
-  return resultXNodesAttributesDict;
-}
diff --git a/libs/shared/lib/vis/visualizations/paohvis/utils/dataProcessing.tsx b/libs/shared/lib/vis/visualizations/paohvis/utils/dataProcessing.tsx
index c803008e20d84bff6d255807d18e32ea59c75bde..f8cd33af7a5401004f28d7a989913896ebdb7eda 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/utils/dataProcessing.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/utils/dataProcessing.tsx
@@ -1,10 +1,13 @@
-import { cloneDeep } from 'lodash-es';
 import { PaohVisProps } from '../paohvis';
-import { PaohvisData, AugmentedNodeAttributesGraph, HyperEdgeRange2, HyperEdgeI2 } from '../types';
+import { PaohvisData, HyperEdgeRange2, HyperEdgeI2 } from '../types';
 import AttributeFilterUsecase, { getIds } from './AttributesFilterUseCase';
 import { countRepetition } from './utils';
 import { Edge, GraphQueryResult, Node } from '@graphpolaris/shared/lib/data-access';
 
+type AugmentedNode = Node & {
+  reduced_ids: string[];
+};
+
 function parseNodes(nodes: Node[], yAxisNodeType: string, xAxisNodeType: string): string[] {
   //const rowNodes = filterRowNodes(nodes, hyperEdgeDegree, yAxisNodeType);
   const rowNodes = nodes;
@@ -22,8 +25,8 @@ function parseNodes(nodes: Node[], yAxisNodeType: string, xAxisNodeType: string)
 }
 
 function parseHyperEdgeRanges(
-  nodesColumn: AugmentedNodeAttributesGraph[],
-  nodesRow: AugmentedNodeAttributesGraph[],
+  nodesColumn: AugmentedNode[],
+  nodesRow: AugmentedNode[],
   filteredData: { nodes: Node[]; edges: Edge[] },
   rowInfo: string[],
   nodeIdIndex: { [id: string]: number },
@@ -44,6 +47,7 @@ function parseHyperEdgeRanges(
       rangeText,
       hyperEdges,
       _id,
+      reduced_ids: element.reduced_ids,
       degree,
     };
   });
@@ -56,15 +60,13 @@ function parseHyperEdgeRanges(
 
   //console.log(relationTo, columnNode, relationTo == columnNode);
 
-  // loop through all edges - and creates hyperedge structure (coluumn order/x)
+  // loop through all edges - and creates hyperedge structure (column order/x)
   for (let i = 0; i < filteredData.edges.length; i++) {
     const edge = filteredData.edges[i];
 
     const pairString = `${edge.from}-${edge.to}`;
     if (!processedPairs.has(pairString)) {
       const hyperEdgeRange = resultHyperEdgeRanges.find((range) => range._id === edge[toOrFrom])?.hyperEdges;
-      //console.log(i, edge.from, edge.to, nodeIdIndex[edge[toOrFromOpposite]], edge[toOrFrom]);
-
       hyperEdgeRange?.indices.push(nodeIdIndex[edge[toOrFromOpposite]]);
       processedPairs.add(pairString);
     }
@@ -112,9 +114,10 @@ export function parseQueryResult(
   const skipReduceRow = configuration.attributeRowShow.includes('_id');
   const skipReduceColumn = configuration.attributeColumnShow.includes('_id');
   const replaceNodeIds: Record<string, string> = {};
+  const reducedReplaceNodeIds: Record<string, string[]> = {};
   const hashAttributesToIdMap: Record<string, string[]> = {};
 
-  let nodesRow = queryResult.nodes
+  let nodesRow: AugmentedNode[] = queryResult.nodes
     .filter((node) => node.label == configuration.rowNode)
     .map((node) => {
       const attributes = skipReduceRow
@@ -131,10 +134,10 @@ export function parseQueryResult(
       } else {
         hashAttributesToIdMap[hash] = [node._id];
       }
-      return { ...node, attributes: attributes };
+      return { ...node, attributes: attributes, reduced_ids: [node._id] };
     });
 
-  let nodesColumn = queryResult.nodes
+  let nodesColumn: AugmentedNode[] = queryResult.nodes
     .filter((node) => node.label == configuration.columnNode)
     .map((node) => {
       const attributes = skipReduceColumn
@@ -150,20 +153,28 @@ export function parseQueryResult(
       } else {
         hashAttributesToIdMap[hash] = [node._id];
       }
-      return { ...node, attributes: attributes };
+      return { ...node, attributes: attributes, reduced_ids: [node._id] };
     });
 
   // Make id->id replace map
   Object.entries(hashAttributesToIdMap).forEach(([k, v]) => {
     if (v.length <= 1) return;
+
     v.slice(1).forEach((id) => {
       replaceNodeIds[id] = v[0];
+      if (!(id in reducedReplaceNodeIds)) reducedReplaceNodeIds[id] = [v[0]];
+      else reducedReplaceNodeIds[id].push(v[0]);
     });
   });
+
   const toRemoveIds = new Set(Object.keys(replaceNodeIds));
   if (mergeData) {
-    nodesRow = nodesRow.filter((node) => !toRemoveIds.has(node._id));
-    nodesColumn = nodesColumn.filter((node) => !toRemoveIds.has(node._id));
+    nodesRow = nodesRow
+      .filter((node) => !toRemoveIds.has(node._id))
+      .map((node) => ({ ...node, reduced_ids: [node._id, ...(reducedReplaceNodeIds?.[node._id] || [])] }));
+    nodesColumn = nodesColumn
+      .filter((node) => !toRemoveIds.has(node._id))
+      .map((node) => ({ ...node, reduced_ids: [node._id, ...(reducedReplaceNodeIds?.[node._id] || [])] }));
   }
 
   const augmentedNodes = [...nodesRow, ...nodesColumn];
diff --git a/libs/shared/lib/vis/visualizations/paohvis/utils/utils.tsx b/libs/shared/lib/vis/visualizations/paohvis/utils/utils.tsx
index 0f1f1e1ced21fecc540f1212a35c1bc872bf96ca..2726a5e5b1cbb8fb50ab9b7ea074cfbd74ae1e14 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/utils/utils.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/utils/utils.tsx
@@ -6,12 +6,12 @@ import {
   connectionFromTo,
   idConnections,
   GraphData,
-  AugmentedNodeAttributesGraph,
   RowInformation,
   Data2RenderI,
 } from '../types';
 import { MultiGraph } from 'graphology';
-import * as d3 from 'd3';
+import { Node } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
+import { group } from 'd3';
 
 /**
  * Takes a schema result and calculates all the entity names, and relation names and attribute names per entity.
@@ -167,7 +167,7 @@ export function findConnectionsNodes(
   originIDs: string[],
   graphStructure: MultiGraph,
   labelNode: string,
-  edges: AugmentedNodeAttributesGraph[],
+  edges: Node[],
 ): connectionFromTo[] {
   const neighborMap: idConnections = {};
   const neighborMapNo: idConnections = {};
@@ -336,7 +336,7 @@ export function wrapperForEdge(data: idConnections, attributes: any): connection
 export const buildGraphology = (data: GraphData): MultiGraph => {
   const graph = new MultiGraph();
 
-  const nodeMap = new Map<string, AugmentedNodeAttributesGraph>();
+  const nodeMap = new Map<string, Node>();
   data.nodes.forEach((node) => {
     nodeMap.set(node._id, node);
   });
@@ -632,7 +632,7 @@ export const processDataColumn = (dataColumn: string, firstRowData: any, data: a
     newData2Render.typeData === 'datetime' ||
     newData2Render.typeData === 'time'
   ) {
-    const groupedData = d3.group(data, (d) => d.attribute[dataColumn]);
+    const groupedData = group(data, (d) => d.attribute[dataColumn]);
     categoryCounts = Array.from(groupedData, ([category, items]) => ({
       category: category as string,
       count: items.length,
@@ -641,7 +641,7 @@ export const processDataColumn = (dataColumn: string, firstRowData: any, data: a
     newData2Render.numUniqueElements = categoryCounts.length;
     newData2Render.data = categoryCounts;
   } else if (newData2Render.typeData === 'bool') {
-    const groupedData = d3.group(data, (d) => d.attribute[dataColumn]);
+    const groupedData = group(data, (d) => d.attribute[dataColumn]);
 
     categoryCounts = Array.from(groupedData, ([category, items]) => ({
       category: category as string,
@@ -660,7 +660,7 @@ export const processDataColumn = (dataColumn: string, firstRowData: any, data: a
     newData2Render.data = categoryCounts;
   } else {
     // there is also array type, when considering labels
-    const groupedData = d3.group(data, (d) => (d.attribute[dataColumn] as any)?.[0]);
+    const groupedData = group(data, (d) => (d.attribute[dataColumn] as any)?.[0]);
 
     categoryCounts = Array.from(groupedData, ([category, items]) => ({
       category: category as string,