Skip to content

Commit 1401b72

Browse files
committed
fix(filter): remove dummy "exists" property for propertyless nodes/edges
Instead of injecting a fake property, featureless elements now bypass the filter/query system entirely and are always visible. Replaces per-element warnings with a single consolidated info log.
1 parent 82d5b83 commit 1401b72

4 files changed

Lines changed: 343 additions & 31 deletions

File tree

src/graph/core.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,11 @@ class GraphCoreManager {
10271027
this.cache.qm.decodeQueryAndBuildAST();
10281028

10291029
for (const node of this.cache.nodeRef.values()) {
1030+
// Nodes with no features are always visible (nothing to filter on)
1031+
if (node.features.size === 0) {
1032+
this.cache.nodeIDsToBeShown.add(node.id);
1033+
continue;
1034+
}
10301035
if (this.cache.query.ast.testNode(node)) {
10311036
this.cache.nodeIDsToBeShown.add(node.id);
10321037
node.featureIsWithinThreshold.forEach((v, k) => {
@@ -1038,14 +1043,22 @@ class GraphCoreManager {
10381043
}
10391044
});
10401045
}
1041-
// else ui.debug(`Node ${node.id} did not fulfill filter!`);
10421046
}
10431047

10441048
for (const edge of this.cache.edgeRef.values()) {
10451049
const endsOk =
10461050
this.cache.nodeIDsToBeShown.has(edge.source) &&
10471051
this.cache.nodeIDsToBeShown.has(edge.target);
10481052

1053+
// Edges with no features are always visible (nothing to filter on),
1054+
// but still require both endpoints to be visible
1055+
if (edge.features.size === 0) {
1056+
if (endsOk) {
1057+
this.cache.edgeIDsToBeShown.add(edge.id);
1058+
}
1059+
continue;
1060+
}
1061+
10491062
if (endsOk && this.cache.query.ast.testEdge(edge)) {
10501063
this.cache.edgeIDsToBeShown.add(edge.id);
10511064
edge.featureIsWithinThreshold.forEach((v, k) => {
@@ -1057,7 +1070,6 @@ class GraphCoreManager {
10571070
}
10581071
});
10591072
}
1060-
// else ui.debug(`Edge ${edge.id} did not fulfill filter!`);
10611073
}
10621074

10631075
const nodeIDsToBeHidden = [...this.cache.nodeRef.keys()].filter(

src/graph/filter.js

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ class GraphFilterManager {
55

66
async resetFilters(section, subSection = undefined) {
77
const idPrefix = section + (subSection ? `::${subSection}` : "");
8-
const affectedPropIDs = Array.from(this.cache.propIDs).filter(id => id.startsWith(idPrefix));
8+
const affectedPropIDs = Array.from(this.cache.propIDs).filter((id) =>
9+
id.startsWith(idPrefix),
10+
);
911

1012
for (const propID of affectedPropIDs) {
1113
this.cache.ui.checkCheckbox(propID, true);
@@ -16,21 +18,34 @@ class GraphFilterManager {
1618
if (dropdown) await dropdown.selectAllCategories(true);
1719
}
1820

19-
await this.handleFilterEvent("Filtering", `Resetting filters for ${idPrefix} ..`);
21+
await this.handleFilterEvent(
22+
"Filtering",
23+
`Resetting filters for ${idPrefix} ..`,
24+
);
2025
}
2126

22-
async handleFilterEvent(header, text, propID = null, shouldResetQuery = true) {
27+
async handleFilterEvent(
28+
header,
29+
text,
30+
propID = null,
31+
shouldResetQuery = true,
32+
) {
2333
if (shouldResetQuery) {
2434
this.cache.qm.resetQuery();
2535
}
2636

2737
// skip rendering if property is not active
28-
if (propID !== null && !this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).active) {
38+
if (
39+
propID !== null &&
40+
!this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(
41+
propID,
42+
).active
43+
) {
2944
return;
3045
}
3146

3247
await this.cache.ui.showLoading(header, text);
33-
await new Promise(resolve => requestAnimationFrame(resolve));
48+
await new Promise((resolve) => requestAnimationFrame(resolve));
3449

3550
// Clean up manual bubble groups (remove filtered-out nodes)
3651
this.cache.bs.cleanupManualGroupMembers();
@@ -64,11 +79,18 @@ class GraphFilterManager {
6479
getPropertiesNotWithinThresholds(nodeID = null, edgeID = null) {
6580
const keysWithFalse = [];
6681
const isNode = nodeID !== null;
67-
const element = isNode ? this.cache.nodeRef.get(nodeID) : this.cache.edgeRef.get(edgeID);
82+
const element = isNode
83+
? this.cache.nodeRef.get(nodeID)
84+
: this.cache.edgeRef.get(edgeID);
85+
86+
// Elements with no features are not subject to property threshold checks
87+
if (element.features.size === 0) return [];
6888

6989
// we only check properties that belong to this element type (specific props for nodes and edges)
7090
const availableProps = new Set([
71-
...(isNode ? this.cache.nodeExclusiveProps : this.cache.edgeExclusiveProps),
91+
...(isNode
92+
? this.cache.nodeExclusiveProps
93+
: this.cache.edgeExclusiveProps),
7294
...this.cache.mixedProps,
7395
]);
7496

@@ -103,14 +125,19 @@ class GraphFilterManager {
103125

104126
async updateElementVisibility(idsToShow, idsToHide) {
105127
this.cache.visibleElementsChanged = false;
106-
const {nodes, edges} = await this.cache.graph.getData();
107-
const {visible, hidden} = [...nodes, ...edges].reduce((acc, item) => {
108-
acc[item.style.visibility === "visible" ? 'visible' : 'hidden'].push(item.id);
109-
return acc;
110-
}, {visible: [], hidden: []});
111-
112-
const showElementsDiff = idsToShow.filter(id => hidden.includes(id));
113-
const hideElementsDiff = idsToHide.filter(id => visible.includes(id));
128+
const { nodes, edges } = await this.cache.graph.getData();
129+
const { visible, hidden } = [...nodes, ...edges].reduce(
130+
(acc, item) => {
131+
acc[item.style.visibility === "visible" ? "visible" : "hidden"].push(
132+
item.id,
133+
);
134+
return acc;
135+
},
136+
{ visible: [], hidden: [] },
137+
);
138+
139+
const showElementsDiff = idsToShow.filter((id) => hidden.includes(id));
140+
const hideElementsDiff = idsToHide.filter((id) => visible.includes(id));
114141

115142
if (showElementsDiff.length > 0) {
116143
await this.cache.graph.showElement(showElementsDiff);
@@ -123,8 +150,7 @@ class GraphFilterManager {
123150
if (this.cache.visibleElementsChanged) {
124151
this.cache.metrics.invalidateMetricValues();
125152
}
126-
127153
}
128154
}
129155

130-
export {GraphFilterManager};
156+
export { GraphFilterManager };

src/managers/io.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,16 +1806,7 @@ class IOManager {
18061806
propsAdded++;
18071807
}
18081808

1809-
if (propsAdded === 0) {
1810-
this.cache.ui
1811-
.warning(`${descriptor} in row ${row.__rowNum__} (${nodeOrEdge.id}) has no properties.
1812-
Added property 'exists' to enable display.`);
1813-
nodeOrEdge.D4Data[header][
1814-
this.cache.CFG.EXCEL_UNCATEGORIZED_SUBHEADER
1815-
] = {
1816-
exists: true,
1817-
};
1818-
}
1809+
return propsAdded;
18191810
};
18201811

18211812
const nodeIDs = new Set();
@@ -1845,18 +1836,29 @@ class IOManager {
18451836
nodeIDs.add(nodeID);
18461837

18471838
addNodeOrEdgeStyle(node, row, EXCEL_NODE_PROPERTIES, descriptor);
1848-
addNodeOrEdgeUserData(
1839+
const propsAdded = addNodeOrEdgeUserData(
18491840
node,
18501841
row,
18511842
EXCEL_NODE_PROPERTIES,
18521843
this.cache.CFG.EXCEL_NODE_HEADER,
18531844
descriptor,
18541845
);
1846+
node._propsAdded = propsAdded;
18551847

18561848
return node;
18571849
})
18581850
.filter((node) => node !== null);
18591851

1852+
const nodesWithoutProps = parsedNodes.filter((n) => n._propsAdded === 0);
1853+
if (nodesWithoutProps.length > 0) {
1854+
const ids = nodesWithoutProps.map((n) => n.id);
1855+
this.cache.ui.info(
1856+
`${nodesWithoutProps.length} node(s) have no user-defined properties and will always be visible:\n` +
1857+
ids.map((id) => ` - ${id}`).join("\n"),
1858+
);
1859+
}
1860+
parsedNodes.forEach((n) => delete n._propsAdded);
1861+
18601862
const parsedEdges = edgesData
18611863
.map((row) => {
18621864
const edge = {};
@@ -1898,18 +1900,29 @@ class IOManager {
18981900
edge.target = targetID;
18991901

19001902
addNodeOrEdgeStyle(edge, row, EXCEL_EDGE_PROPERTIES, descriptor);
1901-
addNodeOrEdgeUserData(
1903+
const propsAdded = addNodeOrEdgeUserData(
19021904
edge,
19031905
row,
19041906
EXCEL_EDGE_PROPERTIES,
19051907
this.cache.CFG.EXCEL_EDGE_HEADER,
19061908
descriptor,
19071909
);
1910+
edge._propsAdded = propsAdded;
19081911

19091912
return edge;
19101913
})
19111914
.filter((edge) => edge !== null);
19121915

1916+
const edgesWithoutProps = parsedEdges.filter((e) => e._propsAdded === 0);
1917+
if (edgesWithoutProps.length > 0) {
1918+
const ids = edgesWithoutProps.map((e) => e.id);
1919+
this.cache.ui.info(
1920+
`${edgesWithoutProps.length} edge(s) have no user-defined properties and will always be visible:\n` +
1921+
ids.map((id) => ` - ${id}`).join("\n"),
1922+
);
1923+
}
1924+
parsedEdges.forEach((e) => delete e._propsAdded);
1925+
19131926
return {
19141927
nodes: parsedNodes,
19151928
edges: parsedEdges,

0 commit comments

Comments
 (0)