Add `Graph.edges` filtering by prefixing (#391)
Similar to #390, we now allow filtering the results from `Graph.edges` by address prefixes. It's a little more complicated than #390, as we allow filtering by src, dst, or address. Test plan: Unit tests added. `yarn travis` passes. Paired with @wchargin
This commit is contained in:
parent
1a08a48c03
commit
7199586262
|
@ -46,6 +46,12 @@ export type NeighborsOptions = {|
|
|||
+edgePrefix: EdgeAddressT,
|
||||
|};
|
||||
|
||||
export type EdgesOptions = {|
|
||||
+addressPrefix: EdgeAddressT,
|
||||
+srcPrefix: NodeAddressT,
|
||||
+dstPrefix: NodeAddressT,
|
||||
|};
|
||||
|
||||
type AddressJSON = string[]; // Result of calling {Node,Edge}Address.toParts
|
||||
type Integer = number;
|
||||
type IndexedEdgeJSON = {|
|
||||
|
@ -409,17 +415,44 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
edges(): Iterator<Edge> {
|
||||
const result = this._edgesIterator(this._modificationCount);
|
||||
edges(options?: EdgesOptions): Iterator<Edge> {
|
||||
if (options == null) {
|
||||
options = {
|
||||
addressPrefix: EdgeAddress.fromParts([]),
|
||||
srcPrefix: NodeAddress.fromParts([]),
|
||||
dstPrefix: NodeAddress.fromParts([]),
|
||||
};
|
||||
}
|
||||
if (options.addressPrefix == null) {
|
||||
throw new Error(
|
||||
`Invalid address prefix: ${String(options.addressPrefix)}`
|
||||
);
|
||||
}
|
||||
if (options.srcPrefix == null) {
|
||||
throw new Error(`Invalid src prefix: ${String(options.srcPrefix)}`);
|
||||
}
|
||||
if (options.dstPrefix == null) {
|
||||
throw new Error(`Invalid dst prefix: ${String(options.dstPrefix)}`);
|
||||
}
|
||||
const result = this._edgesIterator(this._modificationCount, options);
|
||||
this._maybeCheckInvariants();
|
||||
return result;
|
||||
}
|
||||
|
||||
*_edgesIterator(initialModificationCount: ModificationCount): Iterator<Edge> {
|
||||
*_edgesIterator(
|
||||
initialModificationCount: ModificationCount,
|
||||
options: EdgesOptions
|
||||
): Iterator<Edge> {
|
||||
for (const edge of this._edges.values()) {
|
||||
this._checkForComodification(initialModificationCount);
|
||||
this._maybeCheckInvariants();
|
||||
yield edge;
|
||||
if (
|
||||
EdgeAddress.hasPrefix(edge.address, options.addressPrefix) &&
|
||||
NodeAddress.hasPrefix(edge.src, options.srcPrefix) &&
|
||||
NodeAddress.hasPrefix(edge.dst, options.dstPrefix)
|
||||
) {
|
||||
this._checkForComodification(initialModificationCount);
|
||||
this._maybeCheckInvariants();
|
||||
yield edge;
|
||||
}
|
||||
}
|
||||
this._checkForComodification(initialModificationCount);
|
||||
this._maybeCheckInvariants();
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
import sortBy from "lodash.sortby";
|
||||
|
||||
import {
|
||||
type Edge,
|
||||
type EdgeAddressT,
|
||||
type EdgesOptions,
|
||||
type Neighbor,
|
||||
type NeighborsOptions,
|
||||
type NodeAddressT,
|
||||
|
@ -585,6 +587,131 @@ describe("core/graph", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("edges filtering", () => {
|
||||
const src1 = NodeAddress.fromParts(["src", "1"]);
|
||||
const src2 = NodeAddress.fromParts(["src", "2"]);
|
||||
const dst1 = NodeAddress.fromParts(["dst", "1"]);
|
||||
const dst2 = NodeAddress.fromParts(["dst", "2"]);
|
||||
const e11 = {
|
||||
src: src1,
|
||||
dst: dst1,
|
||||
address: EdgeAddress.fromParts(["e", "1", "1"]),
|
||||
};
|
||||
const e12 = {
|
||||
src: src1,
|
||||
dst: dst2,
|
||||
address: EdgeAddress.fromParts(["e", "1", "2"]),
|
||||
};
|
||||
const e21 = {
|
||||
src: src2,
|
||||
dst: dst1,
|
||||
address: EdgeAddress.fromParts(["e", "2", "1"]),
|
||||
};
|
||||
const e22 = {
|
||||
src: src2,
|
||||
dst: dst2,
|
||||
address: EdgeAddress.fromParts(["e", "2", "2"]),
|
||||
};
|
||||
const graph = () =>
|
||||
[e11, e12, e21, e22].reduce(
|
||||
(g, e) => g.addEdge(e),
|
||||
[src1, src2, dst1, dst2].reduce((g, n) => g.addNode(n), new Graph())
|
||||
);
|
||||
function expectEdges(
|
||||
options: EdgesOptions | void,
|
||||
expected: $ReadOnlyArray<Edge>
|
||||
) {
|
||||
const sort = (es) => sortBy(es, (e) => e.address);
|
||||
expect(sort(Array.from(graph().edges(options)))).toEqual(
|
||||
sort(expected.slice())
|
||||
);
|
||||
}
|
||||
it("finds all edges when no options are specified", () => {
|
||||
expectEdges(undefined, [e11, e12, e21, e22]);
|
||||
});
|
||||
it("finds all edges when universal filters are specified", () => {
|
||||
expectEdges(
|
||||
{
|
||||
addressPrefix: EdgeAddress.fromParts(["e"]),
|
||||
srcPrefix: NodeAddress.fromParts(["src"]),
|
||||
dstPrefix: NodeAddress.fromParts(["dst"]),
|
||||
},
|
||||
[e11, e12, e21, e22]
|
||||
);
|
||||
});
|
||||
it("requires `addressPrefix` to be present in provided options", () => {
|
||||
expect(() => {
|
||||
graph()
|
||||
// $ExpectFlowError
|
||||
.edges({srcPrefix: src1, dstPrefix: dst1});
|
||||
}).toThrow("Invalid address prefix: undefined");
|
||||
});
|
||||
it("requires `srcPrefix` to be present in provided options", () => {
|
||||
expect(() => {
|
||||
graph()
|
||||
// $ExpectFlowError
|
||||
.edges({addressPrefix: e11, dstPrefix: dst1});
|
||||
}).toThrow("Invalid src prefix: undefined");
|
||||
});
|
||||
it("requires `dstPrefix` to be present in provided options", () => {
|
||||
expect(() => {
|
||||
graph()
|
||||
// $ExpectFlowError
|
||||
.edges({addressPrefix: e11, srcPrefix: dst1});
|
||||
}).toThrow("Invalid dst prefix: undefined");
|
||||
});
|
||||
it("finds edges by address prefix", () => {
|
||||
expectEdges(
|
||||
{
|
||||
addressPrefix: EdgeAddress.fromParts(["e", "1"]),
|
||||
srcPrefix: NodeAddress.fromParts([]),
|
||||
dstPrefix: NodeAddress.fromParts([]),
|
||||
},
|
||||
[e11, e12]
|
||||
);
|
||||
});
|
||||
it("finds edges by src prefix", () => {
|
||||
expectEdges(
|
||||
{
|
||||
addressPrefix: EdgeAddress.fromParts([]),
|
||||
srcPrefix: NodeAddress.fromParts(["src", "1"]),
|
||||
dstPrefix: NodeAddress.fromParts([]),
|
||||
},
|
||||
[e11, e12]
|
||||
);
|
||||
});
|
||||
it("finds edges by dst prefix", () => {
|
||||
expectEdges(
|
||||
{
|
||||
addressPrefix: EdgeAddress.fromParts([]),
|
||||
srcPrefix: NodeAddress.fromParts([]),
|
||||
dstPrefix: NodeAddress.fromParts(["dst", "1"]),
|
||||
},
|
||||
[e11, e21]
|
||||
);
|
||||
});
|
||||
it("yields nothing for disjoint filters", () => {
|
||||
expectEdges(
|
||||
{
|
||||
addressPrefix: EdgeAddress.fromParts(["e", "1"]),
|
||||
srcPrefix: NodeAddress.fromParts(["src", "2"]),
|
||||
dstPrefix: NodeAddress.fromParts([]),
|
||||
},
|
||||
[]
|
||||
);
|
||||
});
|
||||
it("yields appropriate filter intersection", () => {
|
||||
expectEdges(
|
||||
{
|
||||
addressPrefix: EdgeAddress.fromParts([]),
|
||||
srcPrefix: NodeAddress.fromParts(["src", "1"]),
|
||||
dstPrefix: NodeAddress.fromParts(["dst", "2"]),
|
||||
},
|
||||
[e12]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("on a graph", () => {
|
||||
const src = NodeAddress.fromParts(["foo"]);
|
||||
const dst = NodeAddress.fromParts(["bar"]);
|
||||
|
|
Loading…
Reference in New Issue