Skip to content

Commit 8648cde

Browse files
neaorinSorin Peste
andauthored
limit number of entities in the graph (#44)
Co-authored-by: Sorin Peste <[email protected]>
1 parent 0a78b47 commit 8648cde

3 files changed

Lines changed: 82 additions & 18 deletions

File tree

src/app/components/GraphDataHandler.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const GraphDataHandler: React.FC = () => {
3030
const [includeTextUnits, setIncludeTextUnits] = useState(false);
3131
const [includeCommunities, setIncludeCommunities] = useState(false);
3232
const [includeCovariates, setIncludeCovariates] = useState(false);
33+
const [maxEntities, setMaxEntities] = useState(500);
3334

3435
const {
3536
entities,
@@ -54,7 +55,8 @@ const GraphDataHandler: React.FC = () => {
5455
includeDocuments,
5556
includeTextUnits,
5657
includeCommunities,
57-
includeCovariates
58+
includeCovariates,
59+
maxEntities
5860
);
5961

6062
const hasDocuments = documents.length > 0;
@@ -190,6 +192,9 @@ const GraphDataHandler: React.FC = () => {
190192
hasTextUnits={hasTextUnits}
191193
hasCommunities={hasCommunities}
192194
hasCovariates={hasCovariates}
195+
maxEntities={maxEntities}
196+
onMaxEntitiesChange={setMaxEntities}
197+
totalEntities={entities.length}
193198
/>
194199
</Box>
195200
)}

src/app/components/GraphViewer.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
FormControlLabel,
1414
FormGroup,
1515
IconButton,
16+
Slider,
1617
Switch,
1718
Tooltip,
1819
Typography,
@@ -60,6 +61,9 @@ interface GraphViewerProps {
6061
hasTextUnits: boolean;
6162
hasCommunities: boolean;
6263
hasCovariates: boolean;
64+
maxEntities: number;
65+
onMaxEntitiesChange: React.Dispatch<React.SetStateAction<number>>;
66+
totalEntities: number;
6367
}
6468

6569
const NODE_R = 8;
@@ -82,6 +86,9 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
8286
hasTextUnits,
8387
hasCommunities,
8488
hasCovariates,
89+
maxEntities,
90+
onMaxEntitiesChange,
91+
totalEntities,
8592
}) => {
8693
const theme = useTheme();
8794
const [highlightNodes, setHighlightNodes] = useState<Set<CustomNode>>(
@@ -716,6 +723,29 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
716723
label="Include Covariates"
717724
/>
718725
</FormGroup>
726+
727+
{totalEntities > 0 && (
728+
<Box sx={{ width: 200, mt: 2 }}>
729+
<Typography variant="body2" gutterBottom>
730+
Max Entities: {maxEntities === 0 ? "All" : maxEntities}
731+
{totalEntities > 0 && ` / ${totalEntities}`}
732+
</Typography>
733+
<Slider
734+
value={maxEntities}
735+
onChange={(_, value) => onMaxEntitiesChange(value as number)}
736+
min={0}
737+
max={Math.max(totalEntities, 1000)}
738+
step={50}
739+
marks={[
740+
{ value: 0, label: "All" },
741+
{ value: 500, label: "500" },
742+
]}
743+
valueLabelDisplay="auto"
744+
valueLabelFormat={(value) => (value === 0 ? "All" : value)}
745+
disabled={apiSearchResults !== null}
746+
/>
747+
</Box>
748+
)}
719749
</Box>
720750

721751
<APISearchDrawer

src/app/hooks/useGraphData.ts

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,32 @@ const useGraphData = (
1919
includeDocuments: boolean,
2020
includeTextUnits: boolean,
2121
includeCommunities: boolean,
22-
includeCovariates: boolean
22+
includeCovariates: boolean,
23+
maxEntities: number = 0 // 0 means no limit
2324
) => {
2425

2526
const [graphData, setGraphData] = useState<CustomGraphData>({ nodes: [], links: [] });
2627
useEffect(() => {
27-
const nodes: CustomNode[] = entities.map((entity) => ({
28+
// Calculate relationship count for each entity to determine top N
29+
const entityRelationshipCount: { [key: string]: number } = {};
30+
relationships.forEach((rel) => {
31+
entityRelationshipCount[rel.source] = (entityRelationshipCount[rel.source] || 0) + 1;
32+
entityRelationshipCount[rel.target] = (entityRelationshipCount[rel.target] || 0) + 1;
33+
});
34+
35+
// Sort entities by relationship count and limit to maxEntities if specified
36+
let filteredEntities = entities;
37+
if (maxEntities > 0 && entities.length > maxEntities) {
38+
filteredEntities = [...entities]
39+
.sort((a, b) => {
40+
const countA = entityRelationshipCount[a.title] || 0;
41+
const countB = entityRelationshipCount[b.title] || 0;
42+
return countB - countA; // Sort descending
43+
})
44+
.slice(0, maxEntities);
45+
}
46+
47+
const nodes: CustomNode[] = filteredEntities.map((entity) => ({
2848
uuid: entity.id,
2949
id: entity.title, // use title as id because relationships use title as source/target
3050
name: entity.title, // legacy field for old GraphRAG 0.2.x - 0.3.x
@@ -111,13 +131,17 @@ const useGraphData = (
111131
const textUnitEntityLinks = textunits
112132
.filter((textunit) => (textunit.entity_ids ?? []).length > 0)
113133
.flatMap((textunit) =>
114-
textunit.entity_ids.map((entityId) => ({
115-
source: textunit.id,
116-
target: nodes.find((e) => e.uuid === entityId)?.name || "",
117-
type: "HAS_ENTITY",
118-
id: `${textunit.id}-${entityId}`,
119-
}))
120-
);
134+
textunit.entity_ids.map((entityId) => {
135+
const targetName = nodes.find((e) => e.uuid === entityId)?.name;
136+
return targetName ? {
137+
source: textunit.id,
138+
target: targetName,
139+
type: "HAS_ENTITY",
140+
id: `${textunit.id}-${entityId}`,
141+
} : null;
142+
})
143+
)
144+
.filter((link): link is NonNullable<typeof link> => link !== null);
121145

122146
links.push(...textUnitEntityLinks);
123147
}
@@ -160,7 +184,8 @@ const useGraphData = (
160184

161185
const newLinks = [];
162186

163-
if (!uniqueLinks.has(sourceLinkId)) {
187+
// Only add link if source node exists
188+
if (!uniqueLinks.has(sourceLinkId) && nodesMap[relationship.source]) {
164189
uniqueLinks.add(sourceLinkId);
165190
newLinks.push({
166191
source: relationship.source,
@@ -170,7 +195,8 @@ const useGraphData = (
170195
});
171196
}
172197

173-
if (!uniqueLinks.has(targetLinkId)) {
198+
// Only add link if target node exists
199+
if (!uniqueLinks.has(targetLinkId) && nodesMap[relationship.target]) {
174200
uniqueLinks.add(targetLinkId);
175201
newLinks.push({
176202
source: relationship.target,
@@ -242,12 +268,14 @@ const useGraphData = (
242268
covariateNodes.forEach(node => nodesMap[node.id] = node);
243269
nodes.push(...covariateNodes);
244270

245-
const covariateTextUnitLinks = covariates.map((covariate) => ({
246-
source: covariate.text_unit_id,
247-
target: covariate.id,
248-
type: "HAS_COVARIATE",
249-
id: `${covariate.text_unit_id}-${covariate.id}`,
250-
}));
271+
const covariateTextUnitLinks = covariates
272+
.filter((covariate) => nodesMap[covariate.text_unit_id] && nodesMap[covariate.id])
273+
.map((covariate) => ({
274+
source: covariate.text_unit_id,
275+
target: covariate.id,
276+
type: "HAS_COVARIATE",
277+
id: `${covariate.text_unit_id}-${covariate.id}`,
278+
}));
251279

252280
links.push(...covariateTextUnitLinks);
253281
}
@@ -283,6 +311,7 @@ const useGraphData = (
283311
includeTextUnits,
284312
includeCommunities,
285313
includeCovariates,
314+
maxEntities,
286315
]);
287316

288317
return graphData;

0 commit comments

Comments
 (0)