You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

437 lines
13 KiB

import EventEmitter from '@antv/event-emitter';
import { GraphView } from './graphView';
import { Node, Edge, GraphChange, GraphChangedEvent, GraphOptions, ID, TreeData, PlainObject, GraphViewOptions } from './types';
export declare class Graph<N extends PlainObject, E extends PlainObject> extends EventEmitter {
private nodeMap;
private edgeMap;
private inEdgesMap;
private outEdgesMap;
private bothEdgesMap;
private treeIndices;
private changes;
private batchCount;
/**
* This function is called with a {@link GraphChangedEvent} each time a graph change happened.
*
* `event.changes` contains all the graph changes in order since last `onChanged`.
*/
onChanged: (event: GraphChangedEvent<N, E>) => void;
/**
* Create a new Graph instance.
* @param options - The options to initialize a graph. See {@link GraphOptions}.
*
* ```ts
* const graph = new Graph({
* // Optional, initial nodes.
* nodes: [
* // Each node has a unique ID.
* { id: 'A', foo: 1 },
* { id: 'B', foo: 1 },
* ],
* // Optional, initial edges.
* edges: [
* { id: 'C', source: 'B', target: 'B', weight: 1 },
* ],
* // Optional, called with a GraphChangedEvent.
* onChanged: (event) => {
* console.log(event);
* }
* });
* ```
*/
constructor(options?: GraphOptions<N, E>);
/**
* Batch several graph changes into one.
*
* Make several changes, but dispatch only one ChangedEvent at the end of batch:
* ```ts
* graph.batch(() => {
* graph.addNodes([]);
* graph.addEdges([]);
* });
* ```
*
* Batches can be nested. Only the outermost batch will dispatch a ChangedEvent:
* ```ts
* graph.batch(() => {
* graph.addNodes([]);
* graph.batch(() => {
* graph.addEdges([]);
* });
* });
* ```
*/
batch: (fn: () => void) => void;
/**
* Reset changes and dispatch a ChangedEvent.
*/
private commit;
/**
* Reduce the number of ordered graph changes by dropping or merging unnecessary changes.
*
* For example, if we update a node and remove it in a batch:
*
* ```ts
* graph.batch(() => {
* graph.updateNodeData('A', 'foo', 2);
* graph.removeNode('A');
* });
* ```
*
* We get 2 atomic graph changes like
*
* ```ts
* [
* { type: 'NodeDataUpdated', id: 'A', propertyName: 'foo', oldValue: 1, newValue: 2 },
* { type: 'NodeRemoved', value: { id: 'A', data: { foo: 2 } },
* ]
* ```
*
* Since node 'A' has been removed, we actually have no need to handle with NodeDataUpdated change.
*
* `reduceChanges()` here helps us remove such changes.
*/
reduceChanges(changes: GraphChange<N, E>[]): GraphChange<N, E>[];
private checkNodeExistence;
/**
* Check if a node exists in the graph.
* @group NodeMethods
*/
hasNode(id: ID): boolean;
/**
* Tell if two nodes are neighbors.
* @group NodeMethods
*/
areNeighbors(firstNodeId: ID, secondNodeId: ID): boolean;
/**
* Get the node data with given ID.
* @group NodeMethods
*/
getNode(id: ID): Node<N>;
/**
* Given a node ID, find all edges of the node.
* @param id - ID of the node
* @param direction - Edge direction, defaults to 'both'.
* @group NodeMethods
*/
getRelatedEdges(id: ID, direction?: 'in' | 'out' | 'both'): Edge<E>[];
/**
* Get the degree of the given node.
* @group NodeMethods
*/
getDegree(id: ID, direction?: 'in' | 'out' | 'both'): number;
/**
* Get all successors of the given node.
*/
getSuccessors(id: ID): Node<N>[];
/**
* Get all predecessors of the given node.
*/
getPredecessors(id: ID): Node<N>[];
/**
* Given a node ID, find its neighbors.
* @param id - ID of the node
* @group NodeMethods
*/
getNeighbors(id: ID): Node<N>[];
private doAddNode;
/**
* Add all nodes of the given array, or iterable, into the graph.
* @group NodeMethods
*/
addNodes(nodes: Iterable<Node<N>>): void;
/**
* Add a single node into the graph.
* @group NodeMethods
*/
addNode(node: Node<N>): void;
private doRemoveNode;
/**
* Remove nodes and their attached edges from the graph.
* @group NodeMethods
*/
removeNodes(idList: ID[]): void;
/**
* Remove a single node and its attached edges from the graph.
* @group NodeMethods
*/
removeNode(id: ID): void;
private updateNodeDataProperty;
/**
* Like Object.assign, merge all properties of `path` to the node data.
* @param id Node ID.
* @param patch A data object to merge.
*/
mergeNodeData(id: ID, patch: Partial<N>): void;
/**
* Update node data. This will replace the whole data object.
*
* ```ts
* updateNodeData(id, data); // Works like `node.data = data`
* ```
*
* @group NodeMethods
*/
updateNodeData(id: ID, data: N): void;
/**
* Update a single property on the node data.
*
* To update multiple properties, you could {@link Graph.batch batch} several updates or use {@link Graph.mergeNodeData mergeNodeData}.
*
* ```ts
* updateNodeData(id, key, value); // Works like `node.data[key] = value`
* ```
*
* @group NodeMethods
*/
updateNodeData<P extends keyof N>(id: ID, propertyName: P, value: N[P]): void;
/**
* Update node data by a update function.
*
* ```ts
* updateNodeData(id, oldData => newData);
* ```
* @group NodeMethods
*/
updateNodeData(id: ID, update: (data: N) => N): void;
private checkEdgeExistence;
/**
* Check if an edge exists in the graph.
* @group NodeMethods
*/
hasEdge(id: ID): boolean;
/**
* Get the edge data with given ID.
* @group EdgeMethods
*/
getEdge(id: ID): Edge<E>;
/**
* Get the edge, the source node, and the target node by an edge ID.
* @group EdgeMethods
*/
getEdgeDetail(id: ID): {
edge: Edge<E>;
source: Node<N>;
target: Node<N>;
};
private doAddEdge;
/**
* Add all edges of the given iterable(an array, a set, etc.) into the graph.
* @group EdgeMethods
*/
addEdges(edges: Iterable<Edge<E>>): void;
/**
* Add a single edge pointing from `source` to `target` into the graph.
*
* ```ts
* graph.addNode({ id: 'NodeA' });
* graph.addNode({ id: 'NodeB' });
* graph.addEdge({ id: 'EdgeA', source: 'NodeA', target: 'NodeB' });
* ```
*
* If `source` or `target` were not found in the current graph, it throws an Error.
* @group EdgeMethods
*/
addEdge(edge: Edge<E>): void;
private doRemoveEdge;
/**
* Remove edges whose id was included in the given id list.
* @group EdgeMethods
*/
removeEdges(idList: ID[]): void;
/**
* Remove a single edge of the given id.
* @group EdgeMethods
*/
removeEdge(id: ID): void;
/**
* Change the source of an edge. The source must be found in current graph.
* @group EdgeMethods
*/
updateEdgeSource(id: ID, source: ID): void;
/**
* Change the target of an edge. The target must be found in current graph.
* @group EdgeMethods
*/
updateEdgeTarget(id: ID, target: ID): void;
private updateEdgeDataProperty;
/**
* Update edge data. This will replace the whole data object.
*
* ```ts
* updateEdgeData(id, data); // Works like `edge.data = data`
* ```
*
* @group EdgeMethods
*/
updateEdgeData(id: ID, data: E): void;
/**
* Update a single property on the edge data.
*
* To update multiple properties, you could {@link Graph.batch batch} several updates or use {@link Graph.mergeEdgeData mergeNodeData}.
*
* ```ts
* updateEdgeData(id, key, value); // Works like `edge.data[key] = value`
* ```
*
* @group EdgeMethods
*/
updateEdgeData<P extends keyof E>(id: ID, propertyName: P, value: E[P]): void;
/**
* Update edge data by a update function.
*
* ```ts
* updateEdgeData(id, oldData => newData);
* ```
* @group EdgeMethods
*/
updateEdgeData(id: ID, update: (data: E) => E): void;
/**
* @group EdgeMethods
*/
mergeEdgeData(id: ID, patch: Partial<E>): void;
private checkTreeExistence;
hasTreeStructure(treeKey: string | undefined): boolean;
/**
* Attach a new tree structure representing the hierarchy of all nodes in the graph.
* @param treeKey A unique key of the tree structure. You can attach multiple tree structures with different keys.
*
* ```ts
* const graph = new Graph({
* nodes: [{ id: 1 }, { id: 2 }, { id: 3 }],
* });
* graph.attachTreeStructure('Inheritance');
* graph.setParent(2, 1, 'Inheritance');
* graph.setParent(3, 1, 'Inheritance');
* graph.getRoots('Inheritance'); // [1]
* graph.getChildren(1, 'Inheritance'); // [2,3]
* ```
* @group TreeMethods
*/
attachTreeStructure(treeKey?: string): void;
/**
* Detach the tree structure of the given tree key from the graph.
*
* ```ts
* graph.detachTreeStructure('Inheritance');
* graph.getRoots('Inheritance'); // Error!
* ```
* @group TreeMethods
*/
detachTreeStructure(treeKey?: string): void;
/**
* Traverse the given tree data, add each node into the graph, then attach the tree structure.
*
* ```ts
* graph.addTree({
* id: 1,
* children: [
* { id: 2 },
* { id: 3 },
* ],
* }, 'Inheritance');
* graph.getRoots('Inheritance'); // [1]
* graph.getChildren(1, 'Inheritance'); // [2, 3]
* graph.getAllNodes(); // [1, 2, 3]
* graph.getAllEdges(); // []
* ```
* @group TreeMethods
*/
addTree(tree: TreeData<N> | TreeData<N>[], treeKey?: string): void;
/**
* Get the root nodes of an attached tree structure.
*
* Consider a graph with the following tree structure attached:
* ```
* Tree structure:
* O 3
* / \ |
* 1 2 4
* ```
* `graph.getRoots()` takes all nodes without a parent, therefore [0, 3] was returned.
*
* Newly added nodes are also unparented. So they are counted as roots.
* ```ts
* graph.addNode({ id: 5 });
* graph.getRoots(); // [0, 3, 5]
* ```
*
* Here is how the tree structure looks like:
* ```
* Tree structure:
* O 3 5
* / \ |
* 1 2 4
* ```
*
* By setting a parent, a root node no more be a root.
* ```ts
* graph.setParent(5, 2);
* graph.getRoots(); // [0, 3]
* ```
*
* The tree structure now becomes:
* ```
* Tree structure:
* O 3
* / \ |
* 1 2 4
* |
* 5
* ```
*
* Removing a node forces its children to be unparented, or roots.
* ```ts
* graph.removeNode(0);
* graph.getRoots(); // [1, 2, 3]
* ```
*
* You might draw the the structure as follow:
* ```
* Tree structure:
* 1 2 3
* | |
* 5 4
* ```
* @group TreeMethods
*/
getRoots(treeKey?: string): Node<N>[];
/**
* Given a node ID and an optional tree key, get the children of the node in the specified tree structure.
* @group TreeMethods
*/
getChildren(id: ID, treeKey?: string): Node<N>[];
/**
* Given a node ID and an optional tree key, get the parent of the node in the specified tree structure.
* If the given node is one of the tree roots, this returns null.
* @group TreeMethods
*/
getParent(id: ID, treeKey?: string): Node<N> | null;
/**
* Returns an array of all the ancestor nodes, staring from the parent to the root.
*/
getAncestors(id: ID, treeKey?: string): Node<N>[];
/**
* Set node parent. If this operation causes a circle, it fails with an error.
* @param id - ID of the child node.
* @param parent - ID of the parent node. If it is undefined or null, means unset parent for node with id.
* @param treeKey - Which tree structure the relation is applied to.
* @group TreeMethods
*/
setParent(id: ID, parent?: ID | null, treeKey?: string): void;
dfsTree(id: ID, fn: (node: Node<N>) => boolean | void, treeKey?: string): boolean;
bfsTree(id: ID, fn: (node: Node<N>) => boolean | void, treeKey?: string): boolean;
/**
* Get all nodes in the graph as an array.
*/
getAllNodes(): Node<N>[];
/**
* Get all edges in the graph as an array.
*/
getAllEdges(): Edge<E>[];
bfs(id: ID, fn: (node: Node<N>) => boolean | void, direction?: 'in' | 'out' | 'both'): boolean;
dfs(id: ID, fn: (node: Node<N>) => boolean | void, direction?: 'in' | 'out' | 'both'): boolean;
clone(): Graph<N, E>;
toJSON(): string;
createView(options: Omit<GraphViewOptions<N, E>, 'graph'>): GraphView<N, E>;
}