Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
([#909](https://github.com/aws/graph-explorer/pull/909))
- **Updated** the state management layer to use Jotai instead of Recoil
([#896](https://github.com/aws/graph-explorer/pull/896),
[#920](https://github.com/aws/graph-explorer/pull/920))
[#920](https://github.com/aws/graph-explorer/pull/920),
[#921](https://github.com/aws/graph-explorer/pull/921))
- **Updated** to use the React Compiler to improve performance and simplify code
([#916](https://github.com/aws/graph-explorer/pull/916))
- **Fixed** issue where a schema sync would not automatically run when a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import type { ConfigurationContextProps } from "./types";
import { atomFamily } from "jotai/utils";
import { atom, useAtomValue } from "jotai";
import { isEqual } from "lodash";

const assembledConfigSelector = atom(get => {
const configuration = get(mergedConfigurationSelector);
Expand Down Expand Up @@ -44,22 +45,25 @@ export const vertexTypeAttributesSelector = atomFamily(
);

return attributesByNameMap.values().toArray();
})
}),
isEqual
);

export const edgeTypeAttributesSelector = atomFamily((edgeTypes: string[]) =>
atom(get => {
const attributesByNameMap = new Map(
edgeTypes
.values()
.map(et => get(edgeTypeConfigSelector(et)))
.filter(et => et != null)
.flatMap(et => et.attributes)
.map(attr => [attr.name, attr])
);

return attributesByNameMap.values().toArray();
})
export const edgeTypeAttributesSelector = atomFamily(
(edgeTypes: string[]) =>
atom(get => {
const attributesByNameMap = new Map(
edgeTypes
.values()
.map(et => get(edgeTypeConfigSelector(et)))
.filter(et => et != null)
.flatMap(et => et.attributes)
.map(attr => [attr.name, attr])
);

return attributesByNameMap.values().toArray();
}),
isEqual
);

export const vertexTypeConfigSelector = atomFamily((vertexType: string) =>
Expand All @@ -75,16 +79,18 @@ export function useVertexTypeConfig(vertexType: string) {
return useAtomValue(vertexTypeConfigSelector(vertexType));
}

const vertexTypeConfigsSelector = atomFamily((vertexTypes?: string[]) =>
atom(get => {
const allConfigs = get(allVertexTypeConfigsSelector);
if (!vertexTypes) {
return allConfigs.values().toArray();
}
return vertexTypes.map(
type => allConfigs.get(type) ?? getDefaultVertexTypeConfig(type)
);
})
const vertexTypeConfigsSelector = atomFamily(
(vertexTypes?: string[]) =>
atom(get => {
const allConfigs = get(allVertexTypeConfigsSelector);
if (!vertexTypes) {
return allConfigs.values().toArray();
}
return vertexTypes.map(
type => allConfigs.get(type) ?? getDefaultVertexTypeConfig(type)
);
}),
isEqual
);

/** Gets the matching vertex type configs or the generated default values. */
Expand All @@ -105,16 +111,18 @@ export function useEdgeTypeConfig(edgeType: string) {
return useAtomValue(edgeTypeConfigSelector(edgeType));
}

const edgeTypeConfigsSelector = atomFamily((edgeTypes?: string[]) =>
atom(get => {
const allConfigs = get(allEdgeTypeConfigsSelector);
if (!edgeTypes) {
return allConfigs.values().toArray();
}
return edgeTypes.map(
type => allConfigs.get(type) ?? getDefaultEdgeTypeConfig(type)
);
})
const edgeTypeConfigsSelector = atomFamily(
(edgeTypes?: string[]) =>
atom(get => {
const allConfigs = get(allEdgeTypeConfigsSelector);
if (!edgeTypes) {
return allConfigs.values().toArray();
}
return edgeTypes.map(
type => allConfigs.get(type) ?? getDefaultEdgeTypeConfig(type)
);
}),
isEqual
);

/** Gets the matching edge type configs or the generated default values. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isEqual, uniq } from "lodash";
import { atom } from "jotai";
import { atom, useAtomValue } from "jotai";
import { sanitizeText } from "@/utils";
import DEFAULT_ICON_URL from "@/utils/defaultIconUrl";
import {
Expand Down Expand Up @@ -205,11 +205,17 @@ export const allVertexTypeConfigsSelector = atom(get => {
const configuration = get(mergedConfigurationSelector);
return new Map(configuration?.schema?.vertices.map(vt => [vt.type, vt]));
});
export function useAllVertexTypeConfigs() {
return useAtomValue(allVertexTypeConfigsSelector);
}

export const allEdgeTypeConfigsSelector = atom(get => {
const configuration = get(mergedConfigurationSelector);
return new Map(configuration?.schema?.edges.map(et => [et.type, et]));
});
export function useAllEdgeTypeConfigs() {
return useAtomValue(allEdgeTypeConfigsSelector);
}

export const vertexTypesSelector = atom(get => {
const configuration = get(mergedConfigurationSelector);
Expand Down
189 changes: 96 additions & 93 deletions packages/graph-explorer/src/core/StateProvider/displayEdge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "@/utils";
import { atom, useAtomValue } from "jotai";
import { atomFamily } from "jotai/utils";
import { isEqual } from "lodash";

/** Represents an edge's display information after all transformations have been applied. */
export type DisplayEdge = {
Expand Down Expand Up @@ -70,101 +71,103 @@ export function useDisplayEdgeFromEdge(edge: Edge) {
return useAtomValue(displayEdgeSelector(edge));
}

const displayEdgeSelector = atomFamily((edge: Edge) =>
atom(get => {
const textTransform = get(textTransformSelector);
const queryEngine = get(queryEngineSelector);
const isSparql = queryEngine === "sparql";

// One type config used for shape, color, icon, etc.
const typeConfig = get(displayEdgeTypeConfigSelector(edge.type));

// List all edge types for displaying
const edgeTypes = [edge.type];
const displayTypes = edgeTypes
.map(type => get(displayEdgeTypeConfigSelector(type)).displayLabel)
.join(", ");

// For SPARQL, display the edge type as the ID
const rawStringId = String(getRawId(edge.id));
const displayId = isSparql ? displayTypes : rawStringId;

const typeAttributes = get(edgeTypeAttributesSelector(edgeTypes));
const sortedAttributes = getSortedDisplayAttributes(
edge,
typeAttributes,
textTransform
);

const sourceRawStringId = String(getRawId(edge.source));
const targetRawStringId = String(getRawId(edge.target));
const sourceDisplayId = isSparql
? textTransform(sourceRawStringId)
: sourceRawStringId;
const targetDisplayId = isSparql
? textTransform(targetRawStringId)
: targetRawStringId;

const sourceDisplayTypes = edge.sourceTypes
.map(
type =>
get(vertexTypeConfigSelector(type))?.displayLabel ||
textTransform(type)
)
.join(", ");
const targetDisplayTypes = edge.targetTypes
.map(
type =>
get(vertexTypeConfigSelector(type))?.displayLabel ||
textTransform(type)
)
.join(", ");

// Get the display name and description for the edge
function getDisplayAttributeValueByName(name: string | undefined) {
if (name === RESERVED_ID_PROPERTY) {
return displayId;
} else if (name === RESERVED_TYPES_PROPERTY) {
return displayTypes;
} else if (name) {
return (
sortedAttributes.find(attr => attr.name === name)?.displayValue ??
MISSING_DISPLAY_VALUE
);
const displayEdgeSelector = atomFamily(
(edge: Edge) =>
atom(get => {
const textTransform = get(textTransformSelector);
const queryEngine = get(queryEngineSelector);
const isSparql = queryEngine === "sparql";

// One type config used for shape, color, icon, etc.
const typeConfig = get(displayEdgeTypeConfigSelector(edge.type));

// List all edge types for displaying
const edgeTypes = [edge.type];
const displayTypes = edgeTypes
.map(type => get(displayEdgeTypeConfigSelector(type)).displayLabel)
.join(", ");

// For SPARQL, display the edge type as the ID
const rawStringId = String(getRawId(edge.id));
const displayId = isSparql ? displayTypes : rawStringId;

const typeAttributes = get(edgeTypeAttributesSelector(edgeTypes));
const sortedAttributes = getSortedDisplayAttributes(
edge,
typeAttributes,
textTransform
);

const sourceRawStringId = String(getRawId(edge.source));
const targetRawStringId = String(getRawId(edge.target));
const sourceDisplayId = isSparql
? textTransform(sourceRawStringId)
: sourceRawStringId;
const targetDisplayId = isSparql
? textTransform(targetRawStringId)
: targetRawStringId;

const sourceDisplayTypes = edge.sourceTypes
.map(
type =>
get(vertexTypeConfigSelector(type))?.displayLabel ||
textTransform(type)
)
.join(", ");
const targetDisplayTypes = edge.targetTypes
.map(
type =>
get(vertexTypeConfigSelector(type))?.displayLabel ||
textTransform(type)
)
.join(", ");

// Get the display name and description for the edge
function getDisplayAttributeValueByName(name: string | undefined) {
if (name === RESERVED_ID_PROPERTY) {
return displayId;
} else if (name === RESERVED_TYPES_PROPERTY) {
return displayTypes;
} else if (name) {
return (
sortedAttributes.find(attr => attr.name === name)?.displayValue ??
MISSING_DISPLAY_VALUE
);
}

return MISSING_DISPLAY_VALUE;
}

return MISSING_DISPLAY_VALUE;
}

const displayName = getDisplayAttributeValueByName(
typeConfig.displayNameAttribute
);

const displayEdge: DisplayEdge = {
entityType: "edge",
id: edge.id,
displayId,
displayName,
displayTypes,
typeConfig,
source: {
id: edge.source,
displayId: sourceDisplayId,
displayTypes: sourceDisplayTypes,
types: edge.sourceTypes,
},
target: {
id: edge.target,
displayId: targetDisplayId,
displayTypes: targetDisplayTypes,
types: edge.targetTypes,
},
attributes: sortedAttributes,
// SPARQL does not have unique ID values for predicates, so the UI should hide them
hasUniqueId: isSparql === false,
};
return displayEdge;
})
const displayName = getDisplayAttributeValueByName(
typeConfig.displayNameAttribute
);

const displayEdge: DisplayEdge = {
entityType: "edge",
id: edge.id,
displayId,
displayName,
displayTypes,
typeConfig,
source: {
id: edge.source,
displayId: sourceDisplayId,
displayTypes: sourceDisplayTypes,
types: edge.sourceTypes,
},
target: {
id: edge.target,
displayId: targetDisplayId,
displayTypes: targetDisplayTypes,
types: edge.targetTypes,
},
attributes: sortedAttributes,
// SPARQL does not have unique ID values for predicates, so the UI should hide them
hasUniqueId: isSparql === false,
};
return displayEdge;
}),
isEqual
);

const displayEdgesInCanvasSelector = atom(get => {
Expand Down
Loading