diff --git a/src/core/pagerankGraph.js b/src/core/pagerankGraph.js index cdea6ee..6ab8720 100644 --- a/src/core/pagerankGraph.js +++ b/src/core/pagerankGraph.js @@ -186,8 +186,8 @@ export class PagerankGraph { return this._syntheticLoopWeight; } - *_nodesIterator(): Iterator { - for (const node of this._graph.nodes()) { + *_nodesIterator(options?: {|+prefix: NodeAddressT|}): Iterator { + for (const node of this._graph.nodes(options)) { const score = NullUtil.get(this._scores.get(node)); yield {node, score}; } @@ -196,11 +196,13 @@ export class PagerankGraph { /** * Provides node and score for every node in the underlying graph. * - * TODO(#1020): Allow optional filtering, as in Graph.nodes. + * Optionally, provide a node prefix to return an iterator containing + * only node/score objects whose nodes match the provided node prefix. + * See Graph.nodes and Address.hasPrefix for details. */ - nodes(): Iterator { + nodes(options?: {|+prefix: NodeAddressT|}): Iterator { this._verifyGraphNotModified(); - return this._nodesIterator(); + return this._nodesIterator(options); } /** diff --git a/src/core/pagerankGraph.test.js b/src/core/pagerankGraph.test.js index 2ab5c62..5385388 100644 --- a/src/core/pagerankGraph.test.js +++ b/src/core/pagerankGraph.test.js @@ -1,7 +1,13 @@ // @flow import sortBy from "lodash.sortby"; -import {Graph, NodeAddress, EdgeAddress, type Edge} from "./graph"; +import { + Graph, + NodeAddress, + EdgeAddress, + type NodeAddressT, + type Edge, +} from "./graph"; import {PagerankGraph} from "./pagerankGraph"; import {advancedGraph} from "./graphTestUtil"; import * as NullUtil from "../util/null"; @@ -63,6 +69,57 @@ describe("core/pagerankGraph", () => { }); }); + describe("node prefix filter matches graph filter", () => { + const n1 = NodeAddress.empty; + const n2 = NodeAddress.fromParts(["foo"]); + const n3 = NodeAddress.fromParts(["foo", "bar"]); + const n4 = NodeAddress.fromParts(["zod", "bar"]); + const g = () => + new Graph() + .addNode(n1) + .addNode(n2) + .addNode(n3) + .addNode(n4); + const pg = () => new PagerankGraph(g(), defaultEvaluator); + + function expectPagerankGraphToEqualGraph( + options: {|+prefix: NodeAddressT|} | void + ) { + const pagerankGraphNodes = Array.from(pg().nodes(options)).sort(); + const graphNodes = Array.from(g().nodes(options)).sort(); + + pagerankGraphNodes.forEach( + (pgNode, i) => + expect(pgNode.node).toEqual(graphNodes[i]) && + expect(pgNode.score).toBe(0.25) + ); + } + + it("with no options object", () => { + expectPagerankGraphToEqualGraph(undefined); + }); + + it("with prefix filter", () => { + expectPagerankGraphToEqualGraph({prefix: n2}); + }); + + it("with empty prefix", () => { + expectPagerankGraphToEqualGraph({prefix: NodeAddress.empty}); + }); + + it("with prefix that matches nothing", () => { + expectPagerankGraphToEqualGraph({prefix: NodeAddress.fromParts(["2"])}); + }); + }); + + describe("node prefix filter", () => { + it("requires a prefix when options are specified", () => { + const pg = new PagerankGraph(nonEmptyGraph(), defaultEvaluator); + // $ExpectFlowError + expect(() => Array.from(pg.nodes({}))).toThrow("prefix"); + }); + }); + describe("edge/edges", () => { it("edges returns the same edges as are in the graph", () => { const g = advancedGraph().graph1();