├── .gitignore
├── .travis.yml
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── graphaware
│ └── module
│ └── noderank
│ ├── NodeRankApi.java
│ ├── NodeRankContext.java
│ ├── NodeRankController.java
│ ├── NodeRankModule.java
│ ├── NodeRankModuleBootstrapper.java
│ ├── NodeRankModuleConfiguration.java
│ ├── NodeRankProcedure.java
│ └── TopRankedNodes.java
└── test
├── java
└── com
│ └── graphaware
│ └── module
│ └── noderank
│ ├── EmbeddedDatabaseIntegration.java
│ ├── EmbeddedDatabaseIntegration2.java
│ ├── NodeRankControllerTest.java
│ ├── NodeRankModuleTest.java
│ ├── NodeRankProcedureTest.java
│ ├── NodeRankProcedureTestCausalCluster.java
│ ├── NodeRankProcedureTestHighAvailability.java
│ ├── PageRankIntegration.java
│ ├── TopRankedNodesTest.java
│ └── utils
│ ├── NetworkMatrix.java
│ ├── NetworkMatrixFactory.java
│ ├── NetworkMatrixFactoryTest.java
│ ├── PageRank.java
│ ├── Permutation.java
│ ├── PermutationTest.java
│ ├── RankNodePair.java
│ └── SimilarityComparison.java
├── python
├── README.md
└── neorank.py
└── resources
├── graph.cyp
├── int-test-neo4j.conf
├── neo4j-2.conf
├── schema.cyp
└── test-neo4j.conf
/.gitignore:
--------------------------------------------------------------------------------
1 | .buildpath
2 | .project
3 | .settings
4 | target/
5 | .idea/
6 | *.class
7 | *.iml
8 | *.iws
9 | *.ipr
10 | *.db
11 | .DS_Store
12 | .classpath
13 | neo4j-home/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | sudo: false
4 |
5 | jdk:
6 | - oraclejdk8
7 |
8 | after_success:
9 | - echo "ossrh\${env.OSSRH_USER}\${env.OSSRH_PASS}" > ~/settings.xml
10 | - mvn deploy --settings ~/settings.xml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | GraphAware Neo4j NodeRank - RETIRED
4 | =====================================
5 |
6 | NodeRank Has Been Retired
7 | ---------------------------
8 |
9 | As of October 17th 2017, this module is retiring. This means it will no longer be maintained and released together with
10 | new versions of the GraphAware Framework and Neo4j. The last compatible Neo4j version is 3.2.5.
11 |
12 | NodeRank is superseded by a much more active Neo4j Graph Algorithms project.
13 |
14 | This repository will remain public. Please get in touch if you've been using NodeRank and would like to migrate to
15 | Neo4j Graph Algorithms.
16 |
17 | =====================================
18 |
19 | [](https://travis-ci.org/graphaware/neo4j-noderank) | Downloads | Javadoc | Latest Release: 3.2.5.51.5
20 |
21 | GraphAware NodeRank is a [GraphAware](https://github.com/graphaware/neo4j-framework) Runtime Module that executes a configurable
22 | Page Rank-like algorithm on the Neo4j graph. It is a reference implementation of a [Timer-Driven GraphAware Runtime Module](https://github.com/graphaware/neo4j-framework/tree/master/runtime#building-a-timer-driven-graphaware-runtime-module).
23 |
24 | The module "crawls" the graph continuously behind the scenes, slowing down as the database gets busier and speeding up
25 | in quiet periods. It starts by selecting a node at random (obeying configured node inclusion policy). At each step of the
26 | algorithm, it follows a random relationship (obeying configured relationship inclusion policy) and increments a property
27 | (with configurable key) of the other node of the relationship. With a probability 1-p, where p is the configurable damping
28 | factor, it selects another random node rather than following a relationship. Also, if there are no suitable relationships
29 | to follow from a node, a jump to another suitable random node is performed.
30 |
31 | Over time, the node ranks written to the nodes in the graph converge to the results of Page Rank, had it been computed
32 | analytically. The amount of time it takes to converge greatly depends on the size of the graph, the load on the database,
33 | and the [Timer-Driven Module Scheduling Configuration](https://github.com/graphaware/neo4j-framework/tree/master/runtime#building-a-timer-driven-graphaware-runtime-module).
34 | With default settings and 100 nodes in the database, the top 10 nodes are identical for NodeRank and Page Rank within a
35 | few seconds of running the module.
36 |
37 | Getting the Software
38 | --------------------
39 |
40 | ### Server Mode
41 |
42 | When using Neo4j in the standalone server mode,
43 | you will need the GraphAware Neo4j Framework and GraphAware Neo4j NodeRank .jar files (both of which you can download here) dropped
44 | into the `plugins` directory of your Neo4j installation. After a change in neo4.properties (described later) and Neo4j restart, you will be able to use the REST APIs of the NodeRank
45 | and the computation will take place continuously.
46 |
47 | ### Embedded Mode / Java Development
48 |
49 | Java developers that use Neo4j in embedded mode
50 | and those developing Neo4j server plugins,
51 | unmanaged extensions,
52 | GraphAware Runtime Modules, or Spring MVC Controllers can include use the NodeRank as a dependency for their Java project.
53 |
54 | #### Releases
55 |
56 | Releases are synced to Maven Central repository. When using Maven for dependency management, include the following dependency in your pom.xml.
57 |
58 |
59 | ...
60 |
61 | com.graphaware.neo4j
62 | noderank
63 | 3.2.5.51.5
64 |
65 | ...
66 |
67 |
68 | #### Snapshots
69 |
70 | To use the latest development version, just clone this repository, run `mvn clean install` and change the version in the
71 | dependency above to 3.2.5.51.6-SNAPSHOT.
72 |
73 | #### Note on Versioning Scheme
74 |
75 | The version number has two parts. The first four numbers indicate compatibility with Neo4j GraphAware Framework.
76 | The last number is the version of the NodeRank library. For example, version 2.1.4.19.1 is version 1 of the NodeRank
77 | compatible with GraphAware Neo4j Framework 2.1.4.19.
78 |
79 | Setup and Configuration
80 | =======================
81 |
82 | ### Server Mode
83 |
84 | Edit `conf/neo4j.conf` to register the NodeRank module:
85 |
86 | ```
87 | com.graphaware.runtime.enabled=true
88 |
89 | #NR becomes the module ID:
90 | com.graphaware.module.NR.1=com.graphaware.module.noderank.NodeRankModuleBootstrapper
91 |
92 | #optional number of top ranked nodes to remember, the default is 10
93 | com.graphaware.module.NR.maxTopRankNodes=10
94 |
95 | #optional daming factor, which is a number p such that a random node will be selected at any step of the algorithm
96 | #with the probability 1-p (as opposed to following a random relationship). The default is 0.85
97 | com.graphaware.module.NR.dampingFactor=0.85
98 |
99 | #optional key of the property that gets written to the ranked nodes, default is "nodeRank"
100 | com.graphaware.module.NR.propertyKey=nodeRank
101 |
102 | #optionally specify nodes to rank using an expression-based node inclusion policy, default is all business (i.e. non-framework-internal) nodes
103 | com.graphaware.module.NR.node=hasLabel('Person')
104 |
105 | #optionally specify relationships to follow using an expression-based relationship inclusion policy, default is all business (i.e. non-framework-internal) relationships
106 | com.graphaware.module.NR.relationship=isType('FRIEND_OF')
107 | ```
108 |
109 | Note that "NR" becomes the module ID. It is possible to register the NodeRank module multiple times with different
110 | configurations, provided that their IDs are different. This ID is important for querying the top ranked nodes (read on).
111 |
112 | ### Embedded Mode / Java Development
113 |
114 | To use the NodeRank programmatically, register the module like this
115 |
116 | ```java
117 | GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(database); //where database is an instance of GraphDatabaseService
118 | NodeRankModule module = new NodeRankModule("NR");
119 | runtime.registerModule(module);
120 | runtime.start();
121 | ```
122 |
123 | Alternatively:
124 | ```java
125 | GraphDatabaseService database = new TestGraphDatabaseFactory().newEmbeddedDatabaseBuilder(pathToDb)
126 | .loadPropertiesFromFile(this.getClass().getClassLoader().getResource("neo4j.properties").getPath())
127 | .newGraphDatabase();
128 |
129 | RuntimeRegistry.getStartedRuntime(database);
130 | //make sure neo4j.properties contain the lines mentioned in previous section
131 | ```
132 |
133 | Using GraphAware NodeRank
134 | =========================
135 |
136 | ### Cypher
137 |
138 | The `ga.noderank.getTopRanked` procedure call can be used in Cypher to get a list of top ranked nodes, ordered by decreasing rank.
139 |
140 | ```
141 | CALL ga.noderank.getTopRanked("moduleId", 10) YIELD node RETURN node
142 | ```
143 |
144 | There are 2 arguments to pass to the call :
145 |
146 | * `moduleId`: a string representing the name of the module id you used to register the module in the configuration
147 | * `limit` : an integer used to determine the size of the returned list of nodes
148 |
149 | ### REST API
150 |
151 | In Server Mode, the NodeRank is accessible via the REST API.
152 |
153 | You can issue GET requests to `http://your-server-address:7474/graphaware/noderank/{moduleId}` to get a list of top ranked
154 | nodes, ordered by decreasing rank. The maximum size of the list is determined by the `maxTopRankNodes` parameter configured
155 | earlier. You can further limit the size of the results by provising a `limit` request parameter, e.g.
156 | `http://your-server-address:7474/graphaware/noderank/{moduleId}?limit=10`.
157 |
158 | The REST API returns a JSON array of nodes, e.g.
159 |
160 | ```json
161 | [
162 | {
163 | "id": 5,
164 | "labels": ["Person", "Male"],
165 | "properties":
166 | {
167 | "nodeRank": 3022,
168 | "name": "Brandon Morley"
169 | }
170 | },
171 | {
172 | "id": 9,
173 | "labels": ["Person", "Male"],
174 | "properties":
175 | {
176 | "nodeRank": 2656,
177 | "name": "Liam Rees"
178 | }
179 | },
180 | {
181 | "id": 8,
182 | "labels": ["Person", "Female"],
183 | "properties":
184 | {
185 | "nodeRank": 2280,
186 | "name": "Amelie Green"
187 | }
188 | }
189 | ]
190 | ```
191 |
192 | ### Java API
193 |
194 | To use the Java API, obtain the module and ask for top nodes. You can also just interrogate the node properties as usual,
195 | the property that NodeRank writes to the nodes is configured using the `propertyKey` parameter described above.
196 |
197 | ```
198 | NodeRankModule nodeRankModule = ProductionRuntime.getStartedRuntime(database).getModule("NR", NodeRankModule.class);
199 | List topNodes = nodeRankModule.getTopNodes().getTopNodes();
200 | ```
201 |
202 | Please refer to Javadoc for more detail.
203 |
204 | License
205 | -------
206 |
207 | Copyright (c) 2014-2017 GraphAware
208 |
209 | GraphAware is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
210 | as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
211 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
212 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
213 | You should have received a copy of the GNU General Public License along with this program.
214 | If not, see .
215 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | 4.0.0
19 |
20 | noderank
21 | 3.2.5.51.6-SNAPSHOT
22 |
23 |
24 | com.graphaware.neo4j
25 | module-parent
26 | 3.2.5.51
27 |
28 |
29 | GraphAware Neo4j Node Rank Module
30 | Neo4j Timer-Driven Module that computes a page-rank-like algorithm on a Neo4j graph
31 | http://graphaware.com
32 |
33 |
34 |
35 | GNU General Public License, version 3
36 | http://www.gnu.org/licenses/gpl-3.0.txt
37 | repo
38 |
39 |
40 |
41 |
42 | scm:git:git@github.com:graphaware/neo4j-noderank.git
43 | scm:git:git@github.com:graphaware/neo4j-noderank.git
44 | git@github.com:graphaware/neo4j-noderank.git
45 | HEAD
46 |
47 |
48 |
49 |
50 | bachmanm
51 | Michal Bachman
52 | neo4j-noderank@graphaware.com
53 |
54 |
55 | havlicek
56 | Vojtech Hlavicek
57 | vojta@graphaware.com
58 |
59 |
60 | atg
61 | Adam George
62 | adam@graphaware.com
63 |
64 |
65 |
66 |
67 | https://travis-ci.org/graphaware/neo4j-noderank
68 | Travis CI
69 |
70 |
71 | 2014
72 |
73 |
74 | GitHub
75 | https://github.com/graphaware/neo4j-noderank/issues
76 |
77 |
78 |
79 | Graph Aware Limited
80 | http://graphaware.com
81 |
82 |
83 |
84 |
85 |
86 | org.neo4j
87 | neo4j-kernel
88 | test-jar
89 |
90 |
91 |
92 | com.graphaware.neo4j
93 | api
94 |
95 |
96 |
97 | com.graphaware.neo4j
98 | runtime
99 |
100 |
101 |
102 | org.springframework
103 | spring-webmvc
104 |
105 |
106 |
107 | com.graphaware.neo4j
108 | server
109 |
110 |
111 |
112 |
113 | com.google.guava
114 | guava
115 | test
116 |
117 |
118 |
119 | org.la4j
120 | la4j
121 | 0.4.9
122 | test
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankApi.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.runtime.RuntimeRegistry;
20 | import org.neo4j.graphdb.GraphDatabaseService;
21 | import org.neo4j.graphdb.Node;
22 | import org.neo4j.graphdb.NotFoundException;
23 | import org.neo4j.graphdb.Transaction;
24 |
25 | import java.util.LinkedList;
26 | import java.util.List;
27 |
28 | public class NodeRankApi {
29 |
30 | private final GraphDatabaseService database;
31 |
32 | public NodeRankApi(GraphDatabaseService database) {
33 | this.database = database;
34 | }
35 |
36 | public List getTopRankedNodes(String moduleId, int limit) {
37 | List result = new LinkedList<>();
38 | NodeRankModule module = RuntimeRegistry.getStartedRuntime(database).getModule(moduleId, NodeRankModule.class);
39 |
40 | try (Transaction tx = database.beginTx()) {
41 | for (Node node : module.getTopNodes().getTopNodes()) {
42 | try {
43 | result.add(node);
44 |
45 | if (result.size() >= limit) {
46 | break;
47 | }
48 |
49 | } catch (NotFoundException e) {
50 | //oh well, deleted in the meantime
51 | }
52 | }
53 |
54 | tx.success();
55 | }
56 |
57 | return result;
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.runtime.metadata.NodeBasedContext;
20 | import org.neo4j.graphdb.Node;
21 |
22 | /**
23 | * Context for the {@link NodeRankModule} that extends {@link NodeBasedContext} and also remembers a
24 | * number of nodes with highest node ranks.
25 | */
26 | public class NodeRankContext extends NodeBasedContext {
27 |
28 | private Long[] topNodes;
29 |
30 | public NodeRankContext(long nodeId, Long[] topNodes) {
31 | super(nodeId);
32 | this.topNodes = topNodes;
33 | }
34 |
35 | public NodeRankContext(Node node, Long[] topNodes) {
36 | super(node);
37 | this.topNodes = topNodes;
38 | }
39 |
40 | public NodeRankContext(long nodeId, long earliestNextCall, Long[] topNodes) {
41 | super(nodeId, earliestNextCall);
42 | this.topNodes = topNodes;
43 | }
44 |
45 | public NodeRankContext(Node node, long earliestNextCall, Long[] topNodes) {
46 | super(node, earliestNextCall);
47 | this.topNodes = topNodes;
48 | }
49 |
50 | public Long[] getTopNodes() {
51 | return topNodes;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.api.json.JsonNode;
20 | import com.graphaware.api.json.LongIdJsonNode;
21 | import org.neo4j.graphdb.GraphDatabaseService;
22 | import org.neo4j.graphdb.NotFoundException;
23 | import org.neo4j.graphdb.Transaction;
24 | import org.springframework.beans.factory.annotation.Autowired;
25 | import org.springframework.http.HttpStatus;
26 | import org.springframework.stereotype.Controller;
27 | import org.springframework.web.bind.annotation.*;
28 |
29 | import java.util.LinkedList;
30 | import java.util.List;
31 |
32 | /**
33 | * REST API for {@link NodeRankModule}.
34 | */
35 | @Controller
36 | @RequestMapping("/noderank")
37 | public class NodeRankController {
38 |
39 | private final GraphDatabaseService database;
40 |
41 | private final NodeRankApi nodeRankApi;
42 |
43 | @Autowired
44 | public NodeRankController(GraphDatabaseService database) {
45 | this.database = database;
46 | this.nodeRankApi = new NodeRankApi(database);
47 | }
48 |
49 | @RequestMapping(value = "/{moduleId}", method = RequestMethod.GET)
50 | @ResponseBody
51 | public List topRankedNodes(@PathVariable String moduleId, @RequestParam(value = "limit", defaultValue = "10") int limit) {
52 | List result = new LinkedList<>();
53 | try (Transaction tx = database.beginTx()) {
54 | nodeRankApi.getTopRankedNodes(moduleId, limit).stream().forEach((node) -> {
55 | result.add(new LongIdJsonNode(node));
56 | });
57 | tx.success();
58 | }
59 |
60 | return result;
61 | }
62 |
63 | @ExceptionHandler(IllegalArgumentException.class)
64 | @ResponseStatus(HttpStatus.BAD_REQUEST)
65 | public void handleIllegalArguments() {
66 | }
67 |
68 | @ExceptionHandler(NotFoundException.class)
69 | @ResponseStatus(HttpStatus.NOT_FOUND)
70 | public void handleNotFound() {
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.common.log.LoggerFactory;
20 | import com.graphaware.runtime.config.TimerDrivenModuleConfiguration;
21 | import com.graphaware.runtime.config.util.InstanceRoleUtils;
22 | import com.graphaware.runtime.metadata.NodeBasedContext;
23 | import com.graphaware.runtime.module.BaseTimerDrivenModule;
24 | import com.graphaware.runtime.module.TimerDrivenModule;
25 | import com.graphaware.runtime.walk.NodeSelector;
26 | import com.graphaware.runtime.walk.RandomNodeSelector;
27 | import com.graphaware.runtime.walk.RandomRelationshipSelector;
28 | import com.graphaware.runtime.walk.RelationshipSelector;
29 | import org.neo4j.graphdb.*;
30 | import org.neo4j.logging.Log;
31 |
32 | import java.util.Random;
33 |
34 | /**
35 | * A {@link TimerDrivenModule} that perpetually walks the graph by randomly following relationships and increments
36 | * a configured node property called as it goes.
37 | *
38 | * Sooner or later, depending on the size and shape of the network, it will converge to values that would be computed
39 | * by PageRank algorithm (not normalised).
40 | */
41 | public class NodeRankModule extends BaseTimerDrivenModule implements TimerDrivenModule {
42 |
43 | private static final Log LOG = LoggerFactory.getLogger(NodeRankModule.class);
44 |
45 | private final NodeRankModuleConfiguration config;
46 | private final NodeSelector nodeSelector;
47 | private final RelationshipSelector relationshipSelector;
48 | private final TopRankedNodes topNodes = new TopRankedNodes();
49 | private final Random random = new Random();
50 |
51 | /**
52 | * Constructs a new {@link NodeRankModule} with the given ID using the default module configuration.
53 | *
54 | * @param moduleId The unique identifier for this module instance in the {@link com.graphaware.runtime.GraphAwareRuntime}.
55 | */
56 | public NodeRankModule(String moduleId) {
57 | this(moduleId, NodeRankModuleConfiguration.defaultConfiguration());
58 | }
59 |
60 | /**
61 | * Constructs a new {@link NodeRankModule} with the given ID and configuration settings.
62 | *
63 | * @param moduleId The unique identifier for this module instance in the {@link com.graphaware.runtime.GraphAwareRuntime}.
64 | * @param config The {@link NodeRankModuleConfiguration} to use.
65 | */
66 | public NodeRankModule(String moduleId, NodeRankModuleConfiguration config) {
67 | super(moduleId);
68 | this.config = config;
69 | this.nodeSelector = new RandomNodeSelector(config.getNodeInclusionPolicy());
70 | this.relationshipSelector = new RandomRelationshipSelector(config.getRelationshipInclusionPolicy());
71 | }
72 |
73 | /**
74 | * {@inheritDoc}
75 | */
76 | @Override
77 | public TimerDrivenModuleConfiguration getConfiguration() {
78 | return config;
79 | }
80 |
81 | /**
82 | * {@inheritDoc}
83 | */
84 | @Override
85 | public NodeRankContext createInitialContext(GraphDatabaseService database) {
86 | Node node;
87 |
88 | try (Transaction tx = database.beginTx()) {
89 | node = nodeSelector.selectNode(database);
90 | tx.success();
91 | }
92 |
93 | if (node == null) {
94 | LOG.debug("NodeRank did not find a node to start with. There are no nodes matching the configuration.");
95 | return null;
96 | }
97 |
98 | LOG.info("Starting node rank graph walker from random start node...");
99 | return new NodeRankContext(node.getId(), new Long[0]);
100 | }
101 |
102 | /**
103 | * {@inheritDoc}
104 | */
105 | @Override
106 | public NodeRankContext doSomeWork(NodeRankContext lastContext, GraphDatabaseService database) {
107 | topNodes.initializeIfNeeded(lastContext, database, config);
108 |
109 | Node lastNode = determineLastNode(lastContext, database);
110 | Node nextNode = determineNextNode(lastNode, database);
111 |
112 | if (nextNode == null) {
113 | LOG.debug("NodeRank did not find a node to continue with. There are no nodes matching the configuration.");
114 | return lastContext;
115 | }
116 |
117 | int rankValue = (int) nextNode.getProperty(config.getRankPropertyKey(), 0) + 1;
118 | nextNode.setProperty(config.getRankPropertyKey(), rankValue);
119 | topNodes.addNode(nextNode, rankValue);
120 |
121 | return new NodeRankContext(nextNode, topNodes.getTopNodeIds());
122 | }
123 |
124 | private Node determineLastNode(NodeBasedContext lastContext, GraphDatabaseService database) {
125 | if (lastContext == null) {
126 | LOG.debug("No context found. Will start from a random node.");
127 | return null;
128 | }
129 |
130 | try {
131 | return lastContext.find(database);
132 | } catch (NotFoundException e) {
133 | LOG.debug("Node referenced in last context with ID %s was not found in the database. Will start from a random node.", lastContext);
134 | return null;
135 | }
136 | }
137 |
138 | private Node determineNextNode(Node currentNode, GraphDatabaseService database) {
139 | if (currentNode == null) {
140 | return nodeSelector.selectNode(database);
141 | }
142 |
143 | //hyperjump
144 | if (random.nextDouble() > config.getDampingFactor()) {
145 | LOG.debug("Performing hyperjump");
146 | return nodeSelector.selectNode(database);
147 | }
148 |
149 | Relationship randomRelationship = relationshipSelector.selectRelationship(currentNode);
150 | if (randomRelationship == null) {
151 | LOG.debug("Dead end at %s, selecting a new random node", currentNode);
152 | return nodeSelector.selectNode(database);
153 | }
154 |
155 | Node result = randomRelationship.getOtherNode(currentNode);
156 |
157 | if (!config.getNodeInclusionPolicy().include(result)) {
158 | LOG.debug("Relationship Inclusion Policy allows for a relationship, which leads to a node that " +
159 | "is not included by the Node Inclusion Policy. This is likely a mis-configuration");
160 | }
161 |
162 | return result;
163 | }
164 |
165 | public TopRankedNodes getTopNodes() {
166 | return topNodes;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankModuleBootstrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import java.util.Map;
20 |
21 | import org.neo4j.graphdb.GraphDatabaseService;
22 | import org.neo4j.logging.Log;
23 |
24 | import com.graphaware.common.log.LoggerFactory;
25 | import com.graphaware.common.policy.inclusion.NodeInclusionPolicy;
26 | import com.graphaware.common.policy.inclusion.RelationshipInclusionPolicy;
27 | import com.graphaware.runtime.config.function.StringToNodeInclusionPolicy;
28 | import com.graphaware.runtime.config.function.StringToRelationshipInclusionPolicy;
29 | import com.graphaware.runtime.module.RuntimeModuleBootstrapper;
30 |
31 | /**
32 | * {@link RuntimeModuleBootstrapper} for {@link NodeRankModule}.
33 | */
34 | public class NodeRankModuleBootstrapper implements RuntimeModuleBootstrapper {
35 |
36 | private static final Log LOG = LoggerFactory.getLogger(NodeRankModuleBootstrapper.class);
37 |
38 | private static final String MAX_TOP_RANK_NODES = "maxTopRankNodes";
39 | private static final String DAMPING = "dampingFactor";
40 | private static final String PROPERTY_KEY = "propertyKey";
41 | private static final String NODE = "node";
42 | private static final String RELATIONSHIP = "relationship";
43 |
44 | /**
45 | * {@inheritDoc}
46 | */
47 | @Override
48 | public NodeRankModule bootstrapModule(String moduleId, Map config, GraphDatabaseService database) {
49 | LOG.info("Constructing new module with ID: %s", moduleId);
50 | LOG.debug("Configuration parameter map is: %s", config);
51 |
52 | NodeRankModuleConfiguration configuration = NodeRankModuleConfiguration.defaultConfiguration();
53 |
54 | if (config.get(PROPERTY_KEY) != null) {
55 | LOG.info("Property key set to %s", config.get(PROPERTY_KEY));
56 | configuration = configuration.withRankPropertyKey(config.get(PROPERTY_KEY));
57 | }
58 |
59 | if (config.get(MAX_TOP_RANK_NODES) != null) {
60 | LOG.info("Max top rank nodes set to %s", config.get(MAX_TOP_RANK_NODES));
61 | configuration = configuration.withMaxTopRankNodes(Integer.valueOf(config.get(MAX_TOP_RANK_NODES)));
62 | }
63 |
64 | if (config.get(DAMPING) != null) {
65 | LOG.info("Damping factor set to %s", config.get(DAMPING));
66 | configuration = configuration.withDampingFactor(Double.valueOf(config.get(DAMPING)));
67 | }
68 |
69 | if (config.get(NODE) != null) {
70 | NodeInclusionPolicy policy = StringToNodeInclusionPolicy.getInstance().apply(config.get(NODE));
71 | LOG.info("Node Inclusion Policy set to %s", policy);
72 | configuration = configuration.with(policy);
73 | }
74 |
75 | if (config.get(RELATIONSHIP) != null) {
76 | RelationshipInclusionPolicy policy = StringToRelationshipInclusionPolicy.getInstance().apply(config.get(RELATIONSHIP));
77 | LOG.info("Relationship Inclusion Policy set to %s", policy);
78 | configuration = configuration.with(policy);
79 | }
80 |
81 | return new NodeRankModule(moduleId, configuration);
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankModuleConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.common.policy.inclusion.NodeInclusionPolicy;
20 | import com.graphaware.common.policy.inclusion.RelationshipInclusionPolicy;
21 | import com.graphaware.common.policy.role.AnyRole;
22 | import com.graphaware.common.policy.role.InstanceRolePolicy;
23 | import com.graphaware.common.policy.role.WritableRole;
24 | import com.graphaware.runtime.config.BaseTimerDrivenModuleConfiguration;
25 | import com.graphaware.runtime.policy.all.IncludeAllBusinessNodes;
26 | import com.graphaware.runtime.policy.all.IncludeAllBusinessRelationships;
27 |
28 | /**
29 | * Configuration settings for the {@link NodeRankModule} with fluent interface.
30 | */
31 | public class NodeRankModuleConfiguration extends BaseTimerDrivenModuleConfiguration {
32 |
33 | private final String rankPropertyKey;
34 | private final NodeInclusionPolicy nodeInclusionPolicy;
35 | private final RelationshipInclusionPolicy relationshipInclusionPolicy;
36 | private final int maxTopRankNodes;
37 | private final double dampingFactor;
38 |
39 | /**
40 | * Retrieves the default {@link NodeRankModuleConfiguration}, which includes all (non-internal) nodes and relationships.
41 | *
42 | * @return The default {@link NodeRankModuleConfiguration}
43 | */
44 | public static NodeRankModuleConfiguration defaultConfiguration() {
45 | return new NodeRankModuleConfiguration(WritableRole.getInstance(), "nodeRank", IncludeAllBusinessNodes.getInstance(), IncludeAllBusinessRelationships.getInstance(), 10, 0.85);
46 | }
47 |
48 | /**
49 | * Construct a new configuration with the given rank property key.
50 | *
51 | * @param rankPropertyKey key of the property written to the ranked nodes.
52 | * @return new config.
53 | */
54 | public NodeRankModuleConfiguration withRankPropertyKey(String rankPropertyKey) {
55 | return new NodeRankModuleConfiguration(getInstanceRolePolicy(), rankPropertyKey, getNodeInclusionPolicy(), getRelationshipInclusionPolicy(), getMaxTopRankNodes(), getDampingFactor());
56 | }
57 |
58 | /**
59 | * Construct a new configuration with the given node inclusion policy.
60 | *
61 | * @param nodeInclusionPolicy The {@link NodeInclusionPolicy} to use for selecting nodes to include in the rank algorithm.
62 | * @return new config.
63 | */
64 | public NodeRankModuleConfiguration with(NodeInclusionPolicy nodeInclusionPolicy) {
65 | return new NodeRankModuleConfiguration(getInstanceRolePolicy(), getRankPropertyKey(), nodeInclusionPolicy, getRelationshipInclusionPolicy(), getMaxTopRankNodes(), getDampingFactor());
66 | }
67 |
68 | /**
69 | * Construct a new configuration with the given node inclusion policy.
70 | *
71 | * @param relationshipInclusionPolicy The {@link RelationshipInclusionPolicy} for selecting which relationships to follow when crawling the graph.
72 | * @return new config.
73 | */
74 | public NodeRankModuleConfiguration with(RelationshipInclusionPolicy relationshipInclusionPolicy) {
75 | return new NodeRankModuleConfiguration(getInstanceRolePolicy(), getRankPropertyKey(), getNodeInclusionPolicy(), relationshipInclusionPolicy, getMaxTopRankNodes(), getDampingFactor());
76 | }
77 |
78 | /**
79 | * Construct a new configuration with the given maximum number of top ranked nodes to remember.
80 | *
81 | * @param maxTopRankNodes to remember.
82 | * @return new config.
83 | */
84 | public NodeRankModuleConfiguration withMaxTopRankNodes(int maxTopRankNodes) {
85 | return new NodeRankModuleConfiguration(getInstanceRolePolicy(), getRankPropertyKey(), getNodeInclusionPolicy(), getRelationshipInclusionPolicy(), maxTopRankNodes, getDampingFactor());
86 | }
87 |
88 | /**
89 | * Construct a new configuration with the given damping factor.
90 | *
91 | * @param dampingFactor new damping factor.
92 | * @return new config.
93 | */
94 | public NodeRankModuleConfiguration withDampingFactor(double dampingFactor) {
95 | return new NodeRankModuleConfiguration(getInstanceRolePolicy(), getRankPropertyKey(), getNodeInclusionPolicy(), getRelationshipInclusionPolicy(), getMaxTopRankNodes(), dampingFactor);
96 | }
97 |
98 | /**
99 | * Constructs a new {@link NodeRankModuleConfiguration} based on the given configuration details.
100 | *
101 | * @param instanceRolePolicy specifies which role a machine must have in order to run the module with this configuration. Must not be null
.
102 | * @param rankPropertyKey name of the property written to the ranked nodes.
103 | * @param nodeInclusionPolicy The {@link NodeInclusionPolicy} to use for selecting nodes to include in the rank algorithm.
104 | * @param relationshipInclusionPolicy The {@link RelationshipInclusionPolicy} for selecting which relationships to follow when crawling the graph.
105 | * @param maxTopRankNodes maximum number of top ranked nodes to remember.
106 | */
107 | private NodeRankModuleConfiguration(InstanceRolePolicy instanceRolePolicy, String rankPropertyKey, NodeInclusionPolicy nodeInclusionPolicy, RelationshipInclusionPolicy relationshipInclusionPolicy, int maxTopRankNodes, double dampingFactor) {
108 | super(instanceRolePolicy);
109 |
110 | if (maxTopRankNodes < 0) {
111 | throw new IllegalArgumentException("Max top ranked nodes must be > 0");
112 | }
113 |
114 | if (dampingFactor < 0 || dampingFactor > 1.0) {
115 | throw new IllegalArgumentException("Damping factor must be between 0.0 and 1.0");
116 | }
117 |
118 | this.rankPropertyKey = rankPropertyKey;
119 | this.nodeInclusionPolicy = nodeInclusionPolicy;
120 | this.relationshipInclusionPolicy = relationshipInclusionPolicy;
121 | this.maxTopRankNodes = maxTopRankNodes;
122 | this.dampingFactor = dampingFactor;
123 | }
124 |
125 | /**
126 | * {@inheritDoc}
127 | */
128 | @Override
129 | protected NodeRankModuleConfiguration newInstance(InstanceRolePolicy instanceRolePolicy) {
130 | return new NodeRankModuleConfiguration(instanceRolePolicy, getRankPropertyKey(), getNodeInclusionPolicy(), getRelationshipInclusionPolicy(), getMaxTopRankNodes(), getDampingFactor());
131 | }
132 |
133 | public String getRankPropertyKey() {
134 | return rankPropertyKey;
135 | }
136 |
137 | public NodeInclusionPolicy getNodeInclusionPolicy() {
138 | return nodeInclusionPolicy;
139 | }
140 |
141 | public RelationshipInclusionPolicy getRelationshipInclusionPolicy() {
142 | return relationshipInclusionPolicy;
143 | }
144 |
145 | public int getMaxTopRankNodes() {
146 | return maxTopRankNodes;
147 | }
148 |
149 | public double getDampingFactor() {
150 | return dampingFactor;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/NodeRankProcedure.java:
--------------------------------------------------------------------------------
1 | package com.graphaware.module.noderank;
2 |
3 | import org.neo4j.graphdb.GraphDatabaseService;
4 | import org.neo4j.graphdb.Node;
5 | import org.neo4j.procedure.Context;
6 | import org.neo4j.procedure.Name;
7 | import org.neo4j.procedure.Procedure;
8 |
9 | import java.util.LinkedList;
10 | import java.util.List;
11 | import java.util.stream.Stream;
12 |
13 | public class NodeRankProcedure {
14 |
15 | @Context
16 | public GraphDatabaseService database;
17 |
18 | @Procedure("ga.noderank.getTopRanked")
19 | public Stream getTopRankedNodes(@Name("moduleId") String moduleId, @Name("limit") Number limit) {
20 | List result = new LinkedList<>();
21 | new NodeRankApi(database).getTopRankedNodes(moduleId, limit.intValue()).stream().forEach((node) -> {
22 | result.add(new NodeResult(node));
23 | });
24 |
25 | return result.stream();
26 | }
27 |
28 | public class NodeResult {
29 |
30 | public final Node node;
31 |
32 | public NodeResult(Node node) {
33 | this.node = node;
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/graphaware/module/noderank/TopRankedNodes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.common.log.LoggerFactory;
20 | import com.graphaware.common.util.BoundedSortedList;
21 | import org.neo4j.graphdb.GraphDatabaseService;
22 | import org.neo4j.graphdb.Node;
23 | import org.neo4j.logging.Log;
24 |
25 | import java.util.Collections;
26 | import java.util.LinkedList;
27 | import java.util.List;
28 |
29 | /**
30 | * A container for top ranked nodes.
31 | */
32 | public class TopRankedNodes {
33 |
34 | private static final Log LOG = LoggerFactory.getLogger(TopRankedNodes.class);
35 |
36 | private BoundedSortedList topNodes;
37 |
38 | public List getTopNodes() {
39 | if (topNodes == null) {
40 | return Collections.emptyList();
41 | }
42 |
43 | return topNodes.getItems();
44 | }
45 |
46 | public void addNode(Node node, int rank) {
47 | if (topNodes == null) {
48 | throw new IllegalStateException("Please initialize top ranked nodes first");
49 | }
50 |
51 | topNodes.add(node, rank);
52 | }
53 |
54 | public void initializeIfNeeded(NodeRankContext context, GraphDatabaseService database, NodeRankModuleConfiguration config) {
55 | if (topNodes != null) {
56 | return;
57 | }
58 |
59 | topNodes = new BoundedSortedList<>(config.getMaxTopRankNodes(), Collections.reverseOrder());
60 |
61 | if (context == null) {
62 | return;
63 | }
64 |
65 | for (long nodeId : context.getTopNodes()) {
66 | try {
67 | topNodes.add(database.getNodeById(nodeId), (int) database.getNodeById(nodeId).getProperty(config.getRankPropertyKey(), 0));
68 | } catch (Exception e) {
69 | LOG.warn("Exception while adding ranked node " + nodeId + " to the collection of top ranked nodes. Will ignore...", e);
70 | }
71 | }
72 | }
73 |
74 | public Long[] getTopNodeIds() {
75 | List result = new LinkedList<>();
76 |
77 | for (Node node : getTopNodes()) {
78 | result.add(node.getId());
79 | }
80 |
81 | return result.toArray(new Long[result.size()]);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/EmbeddedDatabaseIntegration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.runtime.RuntimeRegistry;
20 | import org.junit.Test;
21 | import org.neo4j.graphdb.GraphDatabaseService;
22 | import org.neo4j.graphdb.Result;
23 | import org.neo4j.test.TestGraphDatabaseFactory;
24 |
25 | import static org.junit.Assert.assertTrue;
26 |
27 | public class EmbeddedDatabaseIntegration {
28 |
29 | @Test
30 | public void shouldSuccessfullyInitialiseAndRunModuleWhenDatabaseIsStarted() throws InterruptedException {
31 | GraphDatabaseService database = new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder()
32 | .loadPropertiesFromFile("src/test/resources/test-neo4j.conf")
33 | .newGraphDatabase();
34 |
35 | RuntimeRegistry.getStartedRuntime(database);
36 |
37 | populateDatabase(database);
38 |
39 | Thread.sleep(2000);
40 |
41 | Result executionResult = database.execute("MATCH (p:Person) WHERE p.nodeRank > 0 RETURN p");
42 |
43 | assertTrue("The page rank module didn't run on startup", executionResult.hasNext());
44 |
45 | database.shutdown();
46 | }
47 |
48 | private void populateDatabase(GraphDatabaseService database) {
49 | database.execute( "CREATE " +
50 | " (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'}),"+
51 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'}),"+
52 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'}),"+
53 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'}),"+
54 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'}),"+
55 | " (vi)-[:FRIEND_OF]->(a),"+
56 | " (d)-[:FRIEND_OF]->(a),"+
57 | " (d)-[:FRIEND_OF]->(vi),"+
58 | " (v)-[:FRIEND_OF]->(a)");
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/EmbeddedDatabaseIntegration2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.runtime.GraphAwareRuntime;
20 | import com.graphaware.runtime.GraphAwareRuntimeFactory;
21 | import com.graphaware.runtime.RuntimeRegistry;
22 | import org.junit.Test;
23 | import org.neo4j.graphdb.GraphDatabaseService;
24 | import org.neo4j.graphdb.Node;
25 | import org.neo4j.graphdb.Result;
26 | import org.neo4j.test.TestGraphDatabaseFactory;
27 |
28 | import java.util.List;
29 |
30 | import static org.junit.Assert.assertTrue;
31 |
32 | public class EmbeddedDatabaseIntegration2 {
33 |
34 | @Test
35 | public void shouldSuccessfullyInitialiseAndRunModuleWhenDatabaseIsStarted() throws InterruptedException {
36 | GraphDatabaseService database = new TestGraphDatabaseFactory().newImpermanentDatabase();
37 |
38 | GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(database); //where database is an instance of GraphDatabaseService
39 | NodeRankModule module = new NodeRankModule("NR");
40 | runtime.registerModule(module);
41 | runtime.start();
42 |
43 | populateDatabase(database);
44 |
45 | Thread.sleep(2000);
46 |
47 | Result executionResult = database.execute("MATCH (p:Person) WHERE p.nodeRank > 0 RETURN p");
48 |
49 | assertTrue("The page rank module didn't run on startup", executionResult.hasNext());
50 |
51 | NodeRankModule nodeRankModule = RuntimeRegistry.getStartedRuntime(database).getModule("NR", NodeRankModule.class);
52 | List topNodes = nodeRankModule.getTopNodes().getTopNodes();
53 | assertTrue(topNodes.size() > 0);
54 |
55 | database.shutdown();
56 | }
57 |
58 | @Test
59 | public void shouldSuccessfullyStartWhenMoreThanOneModuleDefinitionIsConfigured() {
60 | GraphDatabaseService database = new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder()
61 | .loadPropertiesFromFile("src/test/resources/neo4j-2.conf")
62 | .newGraphDatabase();
63 |
64 | RuntimeRegistry.getStartedRuntime(database);
65 | assertTrue(true);
66 | database.shutdown();
67 | }
68 |
69 | private void populateDatabase(GraphDatabaseService database) {
70 | database.execute("CREATE " +
71 | " (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'})," +
72 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'})," +
73 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'})," +
74 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'})," +
75 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'})," +
76 | " (vi)-[:FRIEND_OF]->(a)," +
77 | " (d)-[:FRIEND_OF]->(a)," +
78 | " (d)-[:FRIEND_OF]->(vi)," +
79 | " (v)-[:FRIEND_OF]->(a)");
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/NodeRankControllerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.fasterxml.jackson.databind.ObjectMapper;
20 | import com.graphaware.test.integration.GraphAwareIntegrationTest;
21 | import org.junit.Test;
22 | import org.springframework.http.HttpStatus;
23 |
24 | import java.io.IOException;
25 | import java.util.List;
26 | import java.util.Map;
27 |
28 | import static org.junit.Assert.assertEquals;
29 |
30 |
31 | public class NodeRankControllerTest extends GraphAwareIntegrationTest {
32 |
33 | /**
34 | * {@inheritDoc}
35 | */
36 | @Override
37 | protected String configFile() {
38 | return "int-test-neo4j.conf";
39 | }
40 |
41 | @Test
42 | public void shouldRetrieveTopNodes() throws InterruptedException, IOException {
43 | httpClient.executeCypher(baseNeoUrl(), "CREATE (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'})," +
44 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'})," +
45 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'})," +
46 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'})," +
47 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'})," +
48 | " (vi)-[:FRIEND_OF]->(a)," +
49 | " (d)-[:FRIEND_OF]->(a)," +
50 | " (d)-[:FRIEND_OF]->(vi)," +
51 | " (v)-[:FRIEND_OF]->(a)");
52 |
53 | Thread.sleep(30000);
54 |
55 | String s = httpClient.get(baseUrl() + "/noderank/noderank/", HttpStatus.OK.value());
56 |
57 | Map first = (Map) new ObjectMapper().readValue(s, List.class).get(0);
58 |
59 | assertEquals(0, first.get("id"));
60 | assertEquals("Michal", ((Map) first.get("properties")).get("name"));
61 | }
62 |
63 | @Test
64 | public void requestToUnknownModuleShouldProduce404() throws InterruptedException {
65 | httpClient.get(baseUrl() + "/noderank/unknown/", HttpStatus.NOT_FOUND.value());
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/NodeRankModuleTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertNotNull;
21 |
22 | import java.util.Map;
23 |
24 | import org.junit.Test;
25 | import org.neo4j.graphdb.Label;
26 | import org.neo4j.graphdb.Node;
27 | import org.neo4j.graphdb.Relationship;
28 | import org.neo4j.graphdb.RelationshipType;
29 | import org.neo4j.graphdb.Result;
30 | import org.neo4j.graphdb.Transaction;
31 |
32 | import com.graphaware.common.policy.inclusion.BaseRelationshipInclusionPolicy;
33 | import com.graphaware.common.policy.inclusion.fluent.IncludeNodes;
34 | import com.graphaware.runtime.metadata.NodeBasedContext;
35 | import com.graphaware.test.integration.EmbeddedDatabaseIntegrationTest;
36 |
37 | public class NodeRankModuleTest extends EmbeddedDatabaseIntegrationTest {
38 |
39 | private NodeRankModule module;
40 |
41 | public void setUp() throws Exception {
42 | super.setUp();
43 | this.module = new NodeRankModule("TEST");
44 | }
45 |
46 | @Test
47 | public void shouldTolerateEmptyContextGivenIfNoPreviousStepsHaveBeenMade() {
48 | getDatabase().execute("CREATE (arbitraryNode)-[:RELATES_TO]->(otherNode);");
49 |
50 | try (Transaction tx = getDatabase().beginTx()) {
51 | module.doSomeWork(module.createInitialContext(getDatabase()), getDatabase());
52 | }
53 | }
54 |
55 | @Test
56 | public void shouldExecuteSingleStepTowardsConvergenceAndUpdateNodePropertiesAccordingly() {
57 | Result executionResult = getDatabase().execute(
58 | "CREATE (p:Person{name:'Gary'})-[:KNOWS]->(q:Person{name:'Sheila'}) RETURN p, q");
59 |
60 | Map insertionResults = executionResult.next();
61 |
62 | try (Transaction tx = getDatabase().beginTx()) {
63 | Node startNode = (Node) insertionResults.get("p");
64 | NodeRankContext lastContext = new NodeRankContext(startNode, new Long[0]);
65 |
66 | Node expectedNextNode = (Node) insertionResults.get("q");
67 |
68 | NodeBasedContext newContext = module.doSomeWork(lastContext, getDatabase());
69 | assertNotNull("The new context shouldn't be null", newContext);
70 | Node nextNode = newContext.find(getDatabase());
71 | assertNotNull("The next node in the new context shouldn't be null", nextNode);
72 | assertEquals("The next node wasn't selected as expected", expectedNextNode, nextNode);
73 | assertEquals("The expected page rank property wasn't updated", 1, nextNode.getProperty("nodeRank"));
74 | }
75 | }
76 |
77 | @Test
78 | public void shouldHonourInclusionStrategiesForNodesAndRelationships() {
79 |
80 | module = new NodeRankModule("TEST2", NodeRankModuleConfiguration
81 | .defaultConfiguration()
82 | .with(IncludeNodes.all().with("Car"))
83 | .with(new BaseRelationshipInclusionPolicy() {
84 | @Override
85 | public boolean include(Relationship relationship) {
86 | return relationship.isType(RelationshipType.withName("OWNS"));
87 | }
88 |
89 | @Override
90 | public boolean include(Relationship relationship, Node pointOfView) {
91 | return include(relationship) && relationship.getOtherNode(pointOfView).hasLabel(Label.label("Car"));
92 | }
93 | }));
94 |
95 | // set up test data and run test
96 | Result executionResult = getDatabase().execute(
97 | "CREATE (p:Person{name:'Sanjiv'})-[:KNOWS]->(:Person{name:'Lakshmipathy'}),"
98 | + " (p)-[:KNOWS]->(:Person{name:'Rajani'}), "
99 | + " (p)-[:OWNS]->(:Laptop{manufacturer:'Dell'}), "
100 | + " (p)-[:OWNS]->(:MobilePhone{manufacturer:'Nokia'}), "
101 | + " (p)-[:OWNS]->(:Car{manufacturer:'Vauxhall'}), "
102 | + " (p)-[:OWNS]->(:Tablet{manufacturer:'Samsung'}) "
103 | + "RETURN p");
104 |
105 | Map insertionResults = executionResult.next();
106 |
107 | try (Transaction tx = getDatabase().beginTx()) {
108 | Node person = (Node) insertionResults.get("p");
109 |
110 | NodeRankContext newContext = module.doSomeWork(new NodeRankContext(person, new Long[0]), getDatabase());
111 | assertNotNull("The new context shouldn't be null", newContext);
112 | Node nextNode = newContext.find(getDatabase());
113 | assertNotNull("The next node in the new context shouldn't be null", nextNode);
114 | assertEquals("The wrong next node was selected", "Car", nextNode.getLabels().iterator().next().name());
115 | }
116 | }
117 |
118 | @Test
119 | public void shouldChooseLegitimateRandomStartNodeInAccordanceWithInclusionStrategy() {
120 | module = new NodeRankModule("TEST3", NodeRankModuleConfiguration.defaultConfiguration().with(IncludeNodes.all().with("Vegan")));
121 |
122 | getDatabase().execute("CREATE (:Meat{name:'Chicken'}), (:Meat{name:'Mutton'}), (:Vegan{name:'Potato'}), "
123 | + "(:Vegetarian{name:'Milk'}), (:Vegetarian{name:'Cheese'}), (:Meat{name:'Pork'})");
124 |
125 | try (Transaction tx = getDatabase().beginTx()) {
126 | NodeBasedContext initialContext = module.createInitialContext(getDatabase());
127 | assertNotNull("The initial context shouldn't be null", initialContext);
128 | Node startNode = initialContext.find(getDatabase());
129 | assertEquals("The wrong start node was selected", "Potato", startNode.getProperty("name"));
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/NodeRankProcedureTest.java:
--------------------------------------------------------------------------------
1 | package com.graphaware.module.noderank;
2 |
3 | import com.graphaware.test.integration.GraphAwareIntegrationTest;
4 | import org.junit.Test;
5 | import org.neo4j.graphdb.Node;
6 | import org.neo4j.graphdb.Result;
7 | import org.neo4j.graphdb.Transaction;
8 | import org.neo4j.kernel.impl.factory.GraphDatabaseFacade;
9 | import org.neo4j.kernel.impl.proc.Procedures;
10 |
11 | import java.io.IOException;
12 | import java.util.LinkedList;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | import static org.junit.Assert.*;
17 |
18 | public class NodeRankProcedureTest extends GraphAwareIntegrationTest {
19 |
20 | /**
21 | * {@inheritDoc}
22 | */
23 | @Override
24 | protected String configFile() {
25 | return "int-test-neo4j.conf";
26 | }
27 |
28 | @Test
29 | public void testProcedureCall() throws InterruptedException, IOException {
30 | try (Transaction tx = getDatabase().beginTx()) {
31 | getDatabase().execute("CREATE (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'})," +
32 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'})," +
33 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'})," +
34 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'})," +
35 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'})," +
36 | " (vi)-[:FRIEND_OF]->(a)," +
37 | " (d)-[:FRIEND_OF]->(a)," +
38 | " (d)-[:FRIEND_OF]->(vi)," +
39 | " (v)-[:FRIEND_OF]->(a)");
40 |
41 | tx.success();
42 | }
43 |
44 | Thread.sleep(30000);
45 |
46 | try (Transaction tx = getDatabase().beginTx()) {
47 | Result result = getDatabase().execute("CALL ga.noderank.getTopRanked('noderank', 10) YIELD node RETURN node");
48 | List ranked = new LinkedList<>();
49 | while (result.hasNext()) {
50 | Map record = result.next();
51 | ranked.add((Node) record.get("node"));
52 | }
53 | assertEquals(0, ranked.get(0).getId());
54 | assertEquals("Michal", ranked.get(0).getProperty("name"));
55 | tx.success();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/NodeRankProcedureTestCausalCluster.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 | package com.graphaware.module.noderank;
17 |
18 | import static org.junit.Assert.assertEquals;
19 | import static org.junit.Assert.assertFalse;
20 |
21 | import java.io.IOException;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | import org.junit.Test;
28 | import org.neo4j.graphdb.GraphDatabaseService;
29 | import org.neo4j.graphdb.Node;
30 | import org.neo4j.graphdb.Result;
31 | import org.neo4j.graphdb.Transaction;
32 | import org.neo4j.kernel.impl.proc.Procedures;
33 |
34 | import com.graphaware.common.policy.role.WritableRole;
35 | import com.graphaware.runtime.GraphAwareRuntime;
36 | import com.graphaware.runtime.GraphAwareRuntimeFactory;
37 | import com.graphaware.test.data.DatabasePopulator;
38 | import com.graphaware.test.integration.cluster.CausalClusterDatabasesintegrationTest;
39 |
40 | /**
41 | *
42 | */
43 | public class NodeRankProcedureTestCausalCluster extends CausalClusterDatabasesintegrationTest {
44 |
45 | private static boolean initialized = false;
46 |
47 | @Override
48 | protected boolean shouldRegisterModules() {
49 | return true;
50 | }
51 |
52 | @Override
53 | protected boolean shouldRegisterProceduresAndFunctions() {
54 | return true;
55 | }
56 |
57 | @Override
58 | protected void registerModules(GraphDatabaseService db) throws Exception {
59 | GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(db);
60 | NodeRankModuleConfiguration config = NodeRankModuleConfiguration.defaultConfiguration()
61 | .with(WritableRole.getInstance())
62 | .withMaxTopRankNodes(3);
63 | runtime.registerModule(new NodeRankModule("noderank", config));
64 | runtime.start();
65 | }
66 |
67 | @Override
68 | protected DatabasePopulator databasePopulator() {
69 | return database -> {
70 | try (Transaction tx = database.beginTx()) {
71 | database.execute("CREATE (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'})," +
72 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'})," +
73 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'})," +
74 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'})," +
75 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'})," +
76 | " (vi)-[:FRIEND_OF]->(a)," +
77 | " (d)-[:FRIEND_OF]->(a)," +
78 | " (d)-[:FRIEND_OF]->(vi)," +
79 | " (v)-[:FRIEND_OF]->(a)");
80 | tx.success();
81 | }
82 |
83 | };
84 | }
85 |
86 | @Override
87 | public void setUp() throws Exception {
88 | super.setUp();
89 | if (!initialized) {
90 |
91 | GraphDatabaseService database = getOneReplicaDatabase();
92 | //wait for ranking synch to replica
93 | int count = 0;
94 | do {
95 | count = 0;
96 | try (Transaction tx = database.beginTx()) {
97 | Result result = database.execute("MATCH (node:Person) RETURN node");
98 | // System.out.println("======================");
99 | while (result.hasNext()) {
100 | Node node = (Node) result.next().get("node");
101 | if (node.hasProperty("nodeRank")) {
102 | Integer rank = (Integer) node.getProperty("nodeRank");
103 | // System.out.println(node.getProperty("name")+": "+node.getProperty("nodeRank"));
104 | if (rank > 2) {
105 | count++;
106 | }
107 | }
108 | }
109 | tx.failure();
110 | } catch (Exception e) {
111 | e.printStackTrace();
112 | }
113 |
114 | try {
115 | TimeUnit.SECONDS.sleep(1);
116 | } catch (InterruptedException e) {
117 | e.printStackTrace();
118 | }
119 | } while (count < 6);
120 |
121 | initialized = true;
122 | }
123 | }
124 |
125 | @Test
126 | public void testProcedureCall_LEADER() throws InterruptedException, IOException {
127 | GraphDatabaseService database = getLeaderDatabase();
128 | try (Transaction tx = database.beginTx()) {
129 | Result result = database.execute("CALL ga.noderank.getTopRanked('noderank', 10) YIELD node RETURN node");
130 | List ranked = new LinkedList<>();
131 | while (result.hasNext()) {
132 | Map record = result.next();
133 | ranked.add((Node) record.get("node"));
134 | }
135 | assertFalse("No ranked nodes found", ranked.isEmpty());
136 | assertEquals("Michal", ranked.get(0).getProperty("name"));
137 | tx.failure();
138 | }
139 | }
140 |
141 | @Test
142 | public void testProcedureCall_FOLLOWER() throws InterruptedException, IOException {
143 | GraphDatabaseService database = getOneFollowerDatabase();
144 | try (Transaction tx = database.beginTx()) {
145 | Result result = database.execute("CALL ga.noderank.getTopRanked('noderank', 10) YIELD node RETURN node");
146 | List ranked = new LinkedList<>();
147 | while (result.hasNext()) {
148 | Map record = result.next();
149 | ranked.add((Node) record.get("node"));
150 | }
151 | assertFalse("No ranked nodes found", ranked.isEmpty());
152 | assertEquals("Michal", ranked.get(0).getProperty("name"));
153 | tx.failure();
154 | }
155 | }
156 |
157 |
158 | @Test
159 | public void testProcedureCall_REPLICA() throws InterruptedException, IOException {
160 | GraphDatabaseService database = getOneReplicaDatabase();
161 | try (Transaction tx = database.beginTx()) {
162 | Result result = database.execute("CALL ga.noderank.getTopRanked('noderank', 10) YIELD node RETURN node");
163 | List ranked = new LinkedList<>();
164 | while (result.hasNext()) {
165 | Map record = result.next();
166 | ranked.add((Node) record.get("node"));
167 | }
168 | assertFalse("No ranked nodes found", ranked.isEmpty());
169 | assertEquals("Michal", ranked.get(0).getProperty("name"));
170 | tx.failure();
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/NodeRankProcedureTestHighAvailability.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 | package com.graphaware.module.noderank;
17 |
18 | import static org.junit.Assert.assertEquals;
19 | import static org.junit.Assert.assertFalse;
20 |
21 | import java.io.IOException;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 | import java.util.Map;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | import org.junit.Test;
28 | import org.neo4j.graphdb.GraphDatabaseService;
29 | import org.neo4j.graphdb.Node;
30 | import org.neo4j.graphdb.Result;
31 | import org.neo4j.graphdb.Transaction;
32 | import org.neo4j.kernel.impl.proc.Procedures;
33 |
34 | import com.graphaware.common.policy.role.MasterOnly;
35 | import com.graphaware.runtime.GraphAwareRuntime;
36 | import com.graphaware.runtime.GraphAwareRuntimeFactory;
37 | import com.graphaware.test.data.DatabasePopulator;
38 | import com.graphaware.test.integration.cluster.HighAvailabilityClusterDatabasesIntegrationTest;
39 |
40 | /**
41 | *
42 | */
43 | public class NodeRankProcedureTestHighAvailability extends HighAvailabilityClusterDatabasesIntegrationTest {
44 |
45 | @Override
46 | protected boolean shouldRegisterModules() {
47 | return true;
48 | }
49 |
50 | @Override
51 | protected boolean shouldRegisterProceduresAndFunctions() {
52 | return true;
53 | }
54 |
55 | @Override
56 | protected void registerModules(GraphDatabaseService db) throws Exception {
57 | GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(db);
58 | NodeRankModuleConfiguration config = NodeRankModuleConfiguration.defaultConfiguration()
59 | .with(MasterOnly.getInstance())
60 | .withMaxTopRankNodes(3);
61 | runtime.registerModule(new NodeRankModule("noderank",config));
62 | runtime.start();
63 | }
64 |
65 | @Override
66 | protected DatabasePopulator databasePopulator() {
67 | return new DatabasePopulator() {
68 |
69 | @Override
70 | public void populate(GraphDatabaseService database) {
71 | try(Transaction tx = database.beginTx();){
72 | database.execute("CREATE (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'})," +
73 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'})," +
74 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'})," +
75 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'})," +
76 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'})," +
77 | " (vi)-[:FRIEND_OF]->(a)," +
78 | " (d)-[:FRIEND_OF]->(a)," +
79 | " (d)-[:FRIEND_OF]->(vi)," +
80 | " (v)-[:FRIEND_OF]->(a)");
81 | tx.success();
82 | }
83 |
84 | //wait for ranking
85 | int count = 0;
86 | do{
87 | count = 0;
88 | try (Transaction tx = database.beginTx()) {
89 | Result result = database.execute("MATCH (node:Person) RETURN node");
90 | // System.out.println("======================");
91 | while(result.hasNext()){
92 | Node node = (Node) result.next().get("node");
93 | if(node.hasProperty("nodeRank")){
94 | Integer rank = (Integer) node.getProperty("nodeRank");
95 | // System.out.println(node.getProperty("name")+": "+node.getProperty("nodeRank"));
96 | if(rank > 10){
97 | count++;
98 | }
99 | }
100 | }
101 |
102 | }catch(Exception e){e.printStackTrace();}
103 |
104 | try {
105 | TimeUnit.SECONDS.sleep(1);
106 | } catch (InterruptedException e) {
107 | e.printStackTrace();
108 | }
109 | }while( count < 6 );
110 | }
111 | };
112 | }
113 |
114 | @Test
115 | public void testProcedureCall_MASTER() throws InterruptedException, IOException {
116 | GraphDatabaseService database = getMasterDatabase();
117 | try (Transaction tx = database.beginTx()) {
118 | Result result = database.execute("CALL ga.noderank.getTopRanked('noderank', 10) YIELD node RETURN node");
119 | List ranked = new LinkedList<>();
120 | while (result.hasNext()) {
121 | Map record = result.next();
122 | ranked.add((Node) record.get("node"));
123 | }
124 | assertFalse(ranked.isEmpty());
125 | assertEquals("Michal", ranked.get(0).getProperty("name"));
126 | tx.failure();
127 | }
128 | }
129 |
130 | @Test
131 | public void testProcedureCall_SLAVE() throws InterruptedException, IOException {
132 | GraphDatabaseService database = getOneSlaveDatabase();
133 | try (Transaction tx = database.beginTx()) {
134 | Result result = database.execute("CALL ga.noderank.getTopRanked('noderank', 10) YIELD node RETURN node");
135 | List ranked = new LinkedList<>();
136 | while (result.hasNext()) {
137 | Map record = result.next();
138 | ranked.add((Node) record.get("node"));
139 | }
140 | assertFalse("No ranked nodes found",ranked.isEmpty());
141 | assertEquals("Michal", ranked.get(0).getProperty("name"));
142 | tx.failure();
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/PageRankIntegration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.common.log.LoggerFactory;
20 | import com.graphaware.module.noderank.utils.*;
21 | import com.graphaware.runtime.GraphAwareRuntime;
22 | import com.graphaware.runtime.GraphAwareRuntimeFactory;
23 | import com.graphaware.runtime.config.FluentRuntimeConfiguration;
24 | import com.graphaware.runtime.policy.all.IncludeAllBusinessNodes;
25 | import com.graphaware.runtime.schedule.FixedDelayTimingStrategy;
26 | import com.graphaware.runtime.schedule.TimingStrategy;
27 | import com.graphaware.test.data.CypherFilesPopulator;
28 | import com.graphaware.test.data.DatabasePopulator;
29 | import com.graphaware.test.integration.EmbeddedDatabaseIntegrationTest;
30 | import org.junit.Test;
31 | import org.neo4j.graphdb.Node;
32 | import org.neo4j.graphdb.Transaction;
33 | import org.neo4j.logging.Log;
34 | import org.springframework.core.io.ClassPathResource;
35 |
36 | import java.io.IOException;
37 | import java.util.ArrayList;
38 | import java.util.List;
39 | import java.util.concurrent.TimeUnit;
40 |
41 | import static java.util.Collections.sort;
42 | import static org.junit.Assert.assertTrue;
43 |
44 | /**
45 | * Integration tests for page rank module.
46 | */
47 | public class PageRankIntegration extends EmbeddedDatabaseIntegrationTest {
48 |
49 | private static final Log LOG = LoggerFactory.getLogger(PageRankIntegration.class);
50 |
51 | private NodeRankModule nodeRankModule;
52 |
53 | public void setUp() throws Exception {
54 | super.setUp();
55 | this.nodeRankModule = new NodeRankModule("TEST");
56 | }
57 |
58 | @Override
59 | protected DatabasePopulator databasePopulator() {
60 | return new CypherFilesPopulator() {
61 | @Override
62 | protected String[] files() throws IOException {
63 | return new String[]{
64 | new ClassPathResource("schema.cyp").getFile().getAbsolutePath(),
65 | new ClassPathResource("graph.cyp").getFile().getAbsolutePath()};
66 | }
67 | };
68 | }
69 |
70 | @Test
71 | public void verifyRandomWalkerModuleGeneratesReasonablePageRank() {
72 | List pageRank = computePageRank(new NetworkMatrixFactory(getDatabase()));
73 |
74 | List nodeRank = computeNodeRank();
75 |
76 | analyseResults(pageRank, nodeRank);
77 | }
78 |
79 | private List computePageRank(NetworkMatrixFactory networkMatrixFactory) {
80 | LOG.info("Computing page rank based on adjacency matrix...");
81 |
82 | List pageRankResult;
83 | PageRank pageRank = new PageRank();
84 |
85 | try (Transaction tx = getDatabase().beginTx()) {
86 | NetworkMatrix transitionMatrix = networkMatrixFactory.getTransitionMatrix();
87 | pageRankResult = pageRank.getPageRankPairs(transitionMatrix, 0.85); // Sergei's & Larry's suggestion is to use .85 to become rich;)
88 | LOG.info("The highest PageRank in the network is: " + getDatabase().getNodeById(pageRankResult.get(0).node()).getProperty("name").toString());
89 |
90 | tx.success();
91 | }
92 |
93 | return pageRankResult;
94 | }
95 |
96 | private ArrayList computeNodeRank() {
97 | letCrawlerDoItsJob();
98 |
99 | ArrayList nodeRank = new ArrayList<>();
100 |
101 | try (Transaction tx = getDatabase().beginTx()) {
102 | for (Node node : getDatabase().getAllNodes()) {
103 | if (IncludeAllBusinessNodes.getInstance().include(node)) {
104 | nodeRank.add(new RankNodePair((int) node.getProperty("nodeRank", 0), node.getId()));
105 | }
106 | }
107 |
108 | sort(nodeRank);
109 | LOG.info("The highest NeoRank in the network is: " + getDatabase().getNodeById(nodeRank.get(0).node()).getProperty("name").toString());
110 |
111 | tx.success();
112 | }
113 |
114 | return nodeRank;
115 | }
116 |
117 | private void letCrawlerDoItsJob() {
118 | LOG.info("Applying random graph walker module to the graph");
119 |
120 | TimingStrategy timingStrategy = FixedDelayTimingStrategy.getInstance()
121 | .withInitialDelay(50)
122 | .withDelay(2);
123 |
124 | GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(getDatabase(),
125 | FluentRuntimeConfiguration
126 | .defaultConfiguration(getDatabase())
127 | .withTimingStrategy(timingStrategy));
128 | runtime.registerModule(nodeRankModule);
129 | runtime.start();
130 |
131 | LOG.info("Waiting for module walker to do its work");
132 |
133 | try {
134 | TimeUnit.SECONDS.sleep(30);
135 | } catch (InterruptedException e) {
136 | throw new RuntimeException(e);
137 | }
138 | }
139 |
140 | /**
141 | * Analyses and compares
142 | * the results of PageRank and NeoRank
143 | *
144 | * The input lists have to be
145 | * in descending order and have the same length
146 | */
147 | private void analyseResults(List pageRankPairs, List nodeRankPairs) {
148 | LOG.info("Analysing results:");
149 |
150 | List pageRank = RankNodePair.convertToRankedNodeList(pageRankPairs);
151 | List nodeRank = RankNodePair.convertToRankedNodeList(nodeRankPairs);
152 |
153 | SimilarityComparison similarityComparison = new SimilarityComparison();
154 | LOG.info("Similarity of all entries: " + similarityComparison.getHammingDistanceMeasure(pageRank, nodeRank));
155 |
156 | List pageRank20 = pageRank.subList(0, (int) (pageRank.size() * .2));
157 | List nodeRank20 = nodeRank.subList(0, (int) (nodeRank.size() * .2));
158 | LOG.info("Similarity of top 20% entries: " + similarityComparison.getHammingDistanceMeasure(pageRank20, nodeRank20));
159 |
160 | List pageRank5 = pageRank.subList(0, 5);
161 | List nodeRank5 = nodeRank.subList(0, 5);
162 | LOG.info("Unordered similarity of the top 5 entries: " + 100 * similarityComparison.unorderedComparisonOfEqualLengthLists(pageRank5, nodeRank5) + "%");
163 |
164 |
165 | /**
166 | * Measures the "Lehmer ratio" of the resulting list. The ratio is a percentage to which the new list is
167 | * completely permuted. The Lehmer code for nodeRank results, given pageRank results is calculated and
168 | * converted to decimal representation. This is the order-number of a permutation of nodeRank result,
169 | * given the pageRank result as a start. The Lehmer code is the normalised by maximum allowed LC (size!).
170 | *
171 | * 1.0 corresponds to a perfect match of the two algorithms.
172 | *
173 | * The log Lehmer ratio is a ratio of logarithms of the two numbers.
174 | */
175 | Permutation pageRankToNodeRankPermutation = new Permutation<>(pageRankPairs, nodeRankPairs);
176 | LOG.info("The un-normed Lehmer distance of pageRank to nodeRank is: " + pageRankToNodeRankPermutation.getPermutationIndex().toString());
177 | LOG.info("Lehmer distance ratio: %s ", pageRankToNodeRankPermutation.getNormedPermutationIndex());
178 | LOG.info("Lehmer log-distance ratio: %s ", pageRankToNodeRankPermutation.getLogNormedPermutationIndex());
179 |
180 | assertTrue(pageRankToNodeRankPermutation.getNormedPermutationIndex() * 100 > 90);
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/TopRankedNodesTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank;
18 |
19 | import com.graphaware.test.integration.DatabaseIntegrationTest;
20 | import com.graphaware.test.integration.EmbeddedDatabaseIntegrationTest;
21 | import org.junit.Test;
22 | import org.neo4j.graphdb.Node;
23 | import org.neo4j.graphdb.Transaction;
24 |
25 | import java.util.List;
26 |
27 | import static org.junit.Assert.*;
28 | import static org.mockito.Mockito.mock;
29 | import static org.mockito.Mockito.when;
30 |
31 | /**
32 | * Unit test for {@link NodeRankContext}.
33 | */
34 | public class TopRankedNodesTest extends EmbeddedDatabaseIntegrationTest {
35 |
36 | @Test
37 | public void emptyTopNodesShouldProduceEmptyList() {
38 | TopRankedNodes topNodes = new TopRankedNodes();
39 | topNodes.initializeIfNeeded(null, getDatabase(), NodeRankModuleConfiguration.defaultConfiguration().withMaxTopRankNodes(3));
40 | assertTrue(topNodes.getTopNodes().isEmpty());
41 | }
42 |
43 | @Test
44 | public void nodeRanksShouldBeCorrectlySorted() {
45 | Node node1 = mock(Node.class);
46 | Node node2 = mock(Node.class);
47 | Node node3 = mock(Node.class);
48 | Node node4 = mock(Node.class);
49 | Node node5 = mock(Node.class);
50 | when(node1.getId()).thenReturn(1L);
51 | when(node2.getId()).thenReturn(2L);
52 | when(node3.getId()).thenReturn(3L);
53 | when(node4.getId()).thenReturn(4L);
54 | when(node5.getId()).thenReturn(5L);
55 |
56 | TopRankedNodes topNodes = new TopRankedNodes();
57 | topNodes.initializeIfNeeded(null, getDatabase(), NodeRankModuleConfiguration.defaultConfiguration().withMaxTopRankNodes(3));
58 |
59 | topNodes.addNode(node1, 10);
60 | topNodes.addNode(node2, 1);
61 | topNodes.addNode(node3, 2);
62 | topNodes.addNode(node4, 4);
63 |
64 | List result = topNodes.getTopNodes();
65 | assertEquals(3, result.size());
66 |
67 | assertEquals(1L, result.get(0).getId());
68 | assertEquals(4L, result.get(1).getId());
69 | assertEquals(3L, result.get(2).getId());
70 | assertArrayEquals(new Long[]{1L, 4L, 3L}, topNodes.getTopNodeIds());
71 |
72 | topNodes.addNode(node5, 1);
73 | topNodes.addNode(node2, 3);
74 | topNodes.addNode(node3, 5);
75 | topNodes.addNode(node2, 6);
76 | topNodes.addNode(node2, 7);
77 |
78 | result = topNodes.getTopNodes();
79 | assertEquals(3, result.size());
80 |
81 | assertEquals(1L, result.get(0).getId());
82 | assertEquals(2L, result.get(1).getId());
83 | assertEquals(3L, result.get(2).getId());
84 | assertArrayEquals(new Long[]{1L, 2L, 3L}, topNodes.getTopNodeIds());
85 | }
86 |
87 | @Test
88 | public void nodeRanksShouldBeCorrectlyInitialized() {
89 | try (Transaction tx = getDatabase().beginTx()) {
90 | Node node1 = getDatabase().createNode();
91 | node1.setProperty("nodeRank", 10);
92 | Node node2 = getDatabase().createNode();
93 | node2.setProperty("nodeRank", 5);
94 | Node node3 = getDatabase().createNode();
95 | node3.setProperty("nodeRank", 3);
96 |
97 | tx.success();
98 | }
99 |
100 | TopRankedNodes topNodes = new TopRankedNodes();
101 | try (Transaction tx = getDatabase().beginTx()) {
102 | //10L doesn't exist and should be ignored:
103 | topNodes.initializeIfNeeded(new NodeRankContext(0L, new Long[]{0L, 10L, 1L, 2L}), getDatabase(), NodeRankModuleConfiguration.defaultConfiguration().withMaxTopRankNodes(3));
104 | tx.success();
105 | }
106 |
107 | List result = topNodes.getTopNodes();
108 | assertEquals(3, result.size());
109 |
110 | try (Transaction tx = getDatabase().beginTx()) {
111 | assertEquals(0L, result.get(0).getId());
112 | assertEquals(10, result.get(0).getProperty("nodeRank"));
113 | assertEquals(1L, result.get(1).getId());
114 | assertEquals(5, result.get(1).getProperty("nodeRank"));
115 | assertEquals(2L, result.get(2).getId());
116 | assertEquals(3, result.get(2).getProperty("nodeRank"));
117 | assertArrayEquals(new Long[]{0L, 1L, 2L}, topNodes.getTopNodeIds());
118 | tx.success();
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/NetworkMatrix.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 |
20 | import org.la4j.matrix.Matrix;
21 |
22 | import java.util.List;
23 |
24 | /**
25 | * An object returned by {@link NetworkMatrixFactory} matrix methods. Contains list of the node IDs and an adjacency matrix.
26 | *
27 | * The list of nodes is an ordered array of node IDs with indices corresponding to rows/columns in the adjacency matrix (and derivates).
28 | */
29 | public class NetworkMatrix {
30 |
31 | private final List nodeList;
32 | private final Matrix matrix;
33 |
34 | public NetworkMatrix(Matrix matrix, List nodeList) {
35 | this.matrix = matrix;
36 | this.nodeList = nodeList;
37 | }
38 |
39 | /**
40 | * Returns an ordered array of node IDs. Node indices correspond to rows/columns of the adjacency matrix.
41 | *
42 | * @return ordered list of node IDs.
43 | */
44 | public List getNodeList() {
45 | return nodeList;
46 | }
47 |
48 | /**
49 | * Returns the stored matrix.
50 | *
51 | * @return matrix corresponding to the network.
52 | */
53 | public Matrix getMatrix() {
54 | return matrix;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/NetworkMatrixFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import org.la4j.factory.CRSFactory;
20 | import org.la4j.factory.Factory;
21 | import org.la4j.matrix.Matrix;
22 | import org.la4j.matrix.sparse.CRSMatrix;
23 | import org.neo4j.graphdb.GraphDatabaseService;
24 | import org.neo4j.graphdb.Node;
25 | import org.neo4j.graphdb.Relationship;
26 |
27 | import java.util.LinkedHashMap;
28 | import java.util.LinkedList;
29 | import java.util.List;
30 | import java.util.Map;
31 |
32 | import static com.graphaware.common.util.IterableUtils.count;
33 |
34 | /**
35 | * Exports the entire graph as adjacency matrix.
36 | *
37 | * WARNING: This is for testing purposes only.
38 | */
39 | public class NetworkMatrixFactory {
40 |
41 | private final GraphDatabaseService database;
42 |
43 | public NetworkMatrixFactory(GraphDatabaseService database) {
44 | this.database = database;
45 | }
46 |
47 | /**
48 | * Returns an adjacency matrix in a raw List> form.
49 | *
50 | * @return la4j sparse matrix.
51 | */
52 | public NetworkMatrix getAdjacencyMatrix() {
53 | return getMatrix(new MatrixPopulator() {
54 | @Override
55 | public void populate(CRSMatrix matrix, Map indices, Node origin, Node target) {
56 | matrix.set(indices.get(origin.getId()), indices.get(target.getId()), 1);
57 | matrix.set(indices.get(target.getId()), indices.get(origin.getId()), 1);
58 | }
59 | });
60 | }
61 |
62 | /**
63 | * Returns a Markov transition matrix (all entries are weighted by their out degree).
64 | * The matrix is in format (i <- j), the sum of any column is 1.
65 | */
66 | public NetworkMatrix getTransitionMatrix() {
67 | return getMatrix(new MatrixPopulator() {
68 | @Override
69 | public void populate(CRSMatrix matrix, Map indices, Node origin, Node target) {
70 | matrix.set(indices.get(origin.getId()), indices.get(target.getId()), 1.0 / ((float) target.getDegree()));
71 | matrix.set(indices.get(target.getId()), indices.get(origin.getId()), 1.0 / ((float) origin.getDegree()));
72 | }
73 | });
74 | }
75 |
76 | /**
77 | * Produce matrix by looking at all relationships.
78 | *
79 | * @param populator that populates the matrix for each relationship.
80 | * @return matrix.
81 | */
82 | private NetworkMatrix getMatrix(MatrixPopulator populator) {
83 | int length = countNodes();
84 | CRSMatrix adjacency = new CRSMatrix(length, length);
85 |
86 | Map indices = matrixNodeIndices(database);
87 |
88 | for (Relationship r : database.getAllRelationships()) {
89 | populator.populate(adjacency, indices, r.getStartNode(), r.getEndNode());
90 | }
91 |
92 | return new NetworkMatrix(adjacency, new LinkedList<>(indices.keySet()));
93 | }
94 |
95 | /**
96 | * Get a mapping of node IDs to indices in the matrix.
97 | *
98 | * @param database in which to find mappings.
99 | * @return mapping.
100 | */
101 | private Map matrixNodeIndices(GraphDatabaseService database) {
102 | int count = 0;
103 | Map result = new LinkedHashMap<>();
104 | for (Node node : database.getAllNodes()) {
105 | result.put(node.getId(), count++);
106 | }
107 | return result;
108 | }
109 |
110 | interface MatrixPopulator {
111 | void populate(CRSMatrix matrix, Map indices, Node origin, Node target);
112 | }
113 |
114 | /**
115 | * Returns a google matrix given the specified damping constant.
116 | * The Google matrix is an iterative mtx for the pageRank algorithm.
117 | *
118 | * See.: The Anatomy of a Large-Scale Hypertextual Web Search Engine by Brin & Page
119 | *
120 | * @return Google matrix of the database, given the damping
121 | */
122 | public NetworkMatrix getGoogleMatrix(double damping) {
123 | Factory matrixFactory = new CRSFactory();
124 |
125 | NetworkMatrix transitionMatrixData = getTransitionMatrix();
126 | List nodeList = transitionMatrixData.getNodeList();
127 | Matrix transitionMatrix = getTransitionMatrix().getMatrix();
128 |
129 | int size = transitionMatrix.rows();
130 | Matrix identityMatrix = matrixFactory.createIdentityMatrix(size);
131 | Matrix googleMatrix = identityMatrix.multiply((1 - damping) / ((float) size)).add(transitionMatrix.multiply(damping));
132 |
133 | return new NetworkMatrix(googleMatrix, nodeList);
134 |
135 | }
136 |
137 | private int countNodes() {
138 | long length = count(database.getAllNodes());
139 |
140 | if (length > Integer.MAX_VALUE) {
141 | throw new IllegalStateException("Too many nodes in the database");
142 | }
143 |
144 | return Long.valueOf(length).intValue();
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/NetworkMatrixFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import com.graphaware.test.integration.DatabaseIntegrationTest;
20 | import com.graphaware.test.integration.EmbeddedDatabaseIntegrationTest;
21 | import org.junit.Test;
22 | import org.neo4j.graphdb.GraphDatabaseService;
23 | import org.neo4j.graphdb.Transaction;
24 |
25 | import static org.junit.Assert.assertEquals;
26 |
27 |
28 | public class NetworkMatrixFactoryTest extends EmbeddedDatabaseIntegrationTest {
29 |
30 | @Override
31 | public void populateDatabase(GraphDatabaseService database) {
32 | database.execute( "CREATE " +
33 | " (m:Person {name:'Michal'})-[:FRIEND_OF]->(d:Person {name:'Daniela'}),"+
34 | " (m)-[:FRIEND_OF]->(v:Person {name:'Vojta'}),"+
35 | " (m)-[:FRIEND_OF]->(a:Person {name:'Adam'}),"+
36 | " (m)-[:FRIEND_OF]->(vi:Person {name:'Vince'}),"+
37 | " (m)-[:FRIEND_OF]->(:Person {name:'Luanne'}),"+
38 | " (vi)-[:FRIEND_OF]->(a),"+
39 | " (d)-[:FRIEND_OF]->(a),"+
40 | " (d)-[:FRIEND_OF]->(vi),"+
41 | " (v)-[:FRIEND_OF]->(a)");
42 | }
43 |
44 | @Test
45 | public void shouldCalculateCorrectPageRank() {
46 | try (Transaction tx = getDatabase().beginTx()) {
47 | NetworkMatrixFactory networkMatrixFactory = new NetworkMatrixFactory(getDatabase());
48 | PageRank pageRank = new PageRank();
49 |
50 | NetworkMatrix adjacencyMatrix = networkMatrixFactory.getAdjacencyMatrix();
51 | NetworkMatrix transitionMatrix = networkMatrixFactory.getTransitionMatrix();
52 |
53 | System.out.println(adjacencyMatrix.getMatrix().toString());
54 | System.out.println(transitionMatrix.getMatrix().toString());
55 |
56 | System.out.println(pageRank.getPageRankVector(transitionMatrix, 0.85));
57 |
58 | Object name = getDatabase().getNodeById(pageRank.getPageRank(transitionMatrix, 0.85).get(0)).getProperty("name");
59 | System.out.println("The highest PageRank in the network is: " + name);
60 |
61 | assertEquals("Michal", name);
62 |
63 | tx.success();
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/PageRank.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import org.la4j.LinearAlgebra;
20 | import org.la4j.factory.Basic1DFactory;
21 | import org.la4j.factory.CRSFactory;
22 | import org.la4j.factory.Factory;
23 | import org.la4j.matrix.Matrix;
24 | import org.la4j.vector.Vector;
25 |
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | import static java.util.Collections.sort;
30 |
31 | /**
32 | * Testing implementation of PageRank. As PR is a global operation which requires a full adjacency matrix, this class
33 | * is meant to be used for TESTING PURPOSES only and mainly as a benchmark against the WEAKER NeoRank.
34 | */
35 | public class PageRank {
36 |
37 | /**
38 | * Returns PageRank of the nodes from the network.
39 | *
40 | * WARNING: The stored graph must be single-component (the
41 | * matrix must be irreducible for the algorithm to
42 | * succeed)
43 | *
44 | * @param transitionMatrix transition mtx of the system
45 | * @return pageRank vector
46 | */
47 | public Vector getPageRankVector(NetworkMatrix transitionMatrix, double damping) {
48 | validateArguments(transitionMatrix, damping);
49 |
50 | Factory vectorFactory = new Basic1DFactory();
51 | Factory matrixFactory = new CRSFactory();
52 |
53 | int size;
54 |
55 | // Calculates the pageRank. The convergence to PageRank is guaranteed
56 | // by picking the vector which converges to Perron Vector by
57 | // Perron-Frobenius theorem
58 |
59 | size = transitionMatrix.getMatrix().rows();
60 |
61 | Matrix identityMatrix = matrixFactory.createIdentityMatrix(size);
62 | Vector testVector = vectorFactory.createConstantVector(size, 1);
63 | Matrix inverse = identityMatrix.add(transitionMatrix.getMatrix().multiply(-damping)).withInverter(LinearAlgebra.InverterFactory.SMART).inverse();
64 | Matrix pageRankOperator = identityMatrix.multiply(1 - damping).multiply(inverse);
65 | Vector pageRank = pageRankOperator.multiply(testVector);
66 |
67 | return pageRank;
68 | }
69 |
70 | /**
71 | * Returns a pageRanked array list of nodes contained in the network.
72 | *
73 | * @return returns a list of nodes
74 | */
75 | public List getPageRank(NetworkMatrix transitionMatrix, double damping) {
76 | return RankNodePair.convertToRankedNodeList(getPageRankPairs(transitionMatrix, damping));
77 | }
78 |
79 | /**
80 | * Returns (rank, node) pairs sorted in descending order by pageRank.
81 | *
82 | * @param transitionMatrix tr. matrix
83 | * @param damping damping factor
84 | * @return rankNodePair list
85 | */
86 | public List getPageRankPairs(NetworkMatrix transitionMatrix, double damping) {
87 | Vector pageRankVector = getPageRankVector(transitionMatrix, damping);
88 | List nodeList = transitionMatrix.getNodeList();
89 | List rankNodePairs = new ArrayList<>(pageRankVector.length());
90 |
91 | for (int i = 0; i < pageRankVector.length(); ++i) {
92 | rankNodePairs.add(new RankNodePair(pageRankVector.get(i), nodeList.get(i)));
93 | }
94 |
95 | sort(rankNodePairs);
96 |
97 | return rankNodePairs;
98 | }
99 |
100 | /**
101 | * Throws an exception if the argument set is invalid.
102 | *
103 | * @param transitionMatrix tr. matrix
104 | * @param damping damping factor
105 | */
106 | private void validateArguments(NetworkMatrix transitionMatrix, double damping) {
107 | if (damping > 1.0 || damping < 0 || transitionMatrix == null) {
108 | throw new IllegalArgumentException("Wrong arguments passed on input");
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/Permutation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import com.google.common.math.BigIntegerMath;
20 |
21 | import java.math.BigDecimal;
22 | import java.math.BigInteger;
23 | import java.math.RoundingMode;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | /**
27 | * Generator of permutations in lexicographic ordering
28 | */
29 | public class Permutation> {
30 | private final List from;
31 | private final List to;
32 |
33 | public Permutation(List init) {
34 | this.to = init;
35 | this.from = init;
36 | }
37 |
38 | /**
39 | * Returns a new permutation from -> to
40 | * For example [1,2,3,4] -> [1,2,4,3]
41 | * is a permutation with Lehmer distance 1
42 | *
43 | * @param from initial (ordered) item list
44 | * @param to final (permuted) item list
45 | */
46 | public Permutation(List from, List to) {
47 | this.to = to;
48 | this.from = from;
49 |
50 | for (T first : from)
51 | if (!to.contains(first))
52 | throw new RuntimeException("Invalid arguments passed to permutation");
53 | }
54 |
55 | /**
56 | * Returns next permutation of the nodes
57 | *
58 | * @return nextPermutation in the sequence
59 | */
60 | public Permutation nextPermutation() {
61 | List shallowCopy = new ArrayList<>(to);
62 |
63 | if (isReverseOrdered(shallowCopy))
64 | return new Permutation<>(from, shallowCopy);
65 |
66 | int i, j;
67 | for (i = shallowCopy.size() - 1; i > 1; --i)
68 | if (shallowCopy.get(i).compareTo(shallowCopy.get(i - 1)) == 1) // stop at ordered seq. i-1 < i
69 | break;
70 |
71 |
72 | // i index of the largest descending pair is found
73 | for (j = shallowCopy.size() - 1; j > i; --j) {
74 | if (shallowCopy.get(j).compareTo(shallowCopy.get(i - 1)) == 1) // i-1 < j
75 | break;
76 | }
77 |
78 | // swap index is found
79 | T temp = shallowCopy.get(j);
80 | shallowCopy.set(j, shallowCopy.get(i - 1));
81 | shallowCopy.set(i - 1, temp);
82 |
83 | reverseSublist(shallowCopy, i, shallowCopy.size() - 1);
84 |
85 | return new Permutation<>(from, shallowCopy);
86 | }
87 |
88 | /**
89 | * Returns a Lehmer Code of the permutation
90 | *
91 | * @return integer lehmer code
92 | */
93 | public List getLehmerCode() {
94 | List shallowFrom = new ArrayList<>(from);
95 | List shallowTo = new ArrayList<>(to);
96 | List lehmerCode = new ArrayList<>();
97 |
98 | for (T decElem : shallowTo) {
99 | int factoradicDigit = shallowFrom.indexOf(decElem);
100 | shallowFrom.remove(factoradicDigit);
101 | lehmerCode.add(factoradicDigit);
102 | }
103 | return lehmerCode;
104 | }
105 |
106 | /**
107 | * Returns a natural log of a number
108 | * This particularly elegant solution is by leonbloy @ StackExchange
109 | * @param bigInteger bigInteger to be logarithmed
110 | * @return logarithm of the bigInteger
111 | */
112 | private double getBigIntegerLog(BigInteger bigInteger) {
113 | BigInteger val = bigInteger;
114 | int blex = val.bitLength() - 1022; // any value in 60..1023 works
115 | if (blex > 0)
116 | val = val.shiftRight(blex);
117 | double res = Math.log(val.doubleValue());
118 | return blex > 0 ? res + blex * Math.log(2.0) : res;
119 | }
120 |
121 | /**
122 | * Returns a lexicographic index corresponding to the permutation
123 | *
124 | * @return permutation index
125 | */
126 | public BigInteger getPermutationIndex() {
127 | BigInteger index = BigInteger.valueOf(0);
128 | List lehmerCode = getLehmerCode();
129 | int size = lehmerCode.size();
130 |
131 | for (int j = size - 1; j >= 0; j--) {
132 | index = index.add(BigInteger.valueOf(lehmerCode.get(j)).multiply(BigIntegerMath.factorial(size - j - 1)));
133 | }
134 |
135 | return index;
136 | }
137 |
138 |
139 | /**
140 | * Returns a percentage to which the permutation is reversely permuted
141 | * - i.e. how much is the ordering of the permutation distant from the
142 | * initial state.
143 | *
144 | * @return double in interal [0.0, 1.0], 1.0 corresponds to unpermuted list
145 | */
146 | public double getNormedPermutationIndex() {
147 | BigDecimal factorial = new BigDecimal(com.google.common.math.BigIntegerMath.factorial(size()).add(BigInteger.valueOf(-1)));
148 | BigDecimal numerator = new BigDecimal(getPermutationIndex());
149 |
150 | return 1.0 - numerator.divide(factorial, 8, RoundingMode.FLOOR).doubleValue();
151 | }
152 |
153 | /**
154 | * Returns a percentage from the correct solution in logarithmic distance
155 | * @return double in interal [0.0, 1.0], 1.0 corresponds to unpermuted list
156 | */
157 | public double getLogNormedPermutationIndex() {
158 | double factorial = getBigIntegerLog(com.google.common.math.BigIntegerMath.factorial(size()).add(BigInteger.ONE));
159 | double numerator = getBigIntegerLog(getPermutationIndex().add(BigInteger.ONE));
160 |
161 | return 1.0 - numerator/factorial;
162 | }
163 |
164 | /**
165 | * Reverses a sublist of a list
166 | *
167 | * @param list to reverse
168 | * @param from index
169 | * @param to index
170 | */
171 | public void reverseSublist(List list, int from, int to) {
172 |
173 | // reverse suffix starting at i ?
174 | int length = to - from;
175 | for (int k = 0; k <= length / 2; ++k) {
176 | T left = list.get(from + k);
177 | T right = list.get(to - k);
178 |
179 | list.set(from + k, right);
180 | list.set(to - k, left);
181 | }
182 | }
183 |
184 | /**
185 | * Checks if the list is reverse ordered.
186 | *
187 | * @param list list to check
188 | * @return true if reverse ordered
189 | */
190 | public boolean isReverseOrdered(List list) {
191 | for (int i = 0; i < list.size() - 1; ++i)
192 | if ((list.get(i).compareTo(list.get(i + 1))) == -1)
193 | return false;
194 |
195 |
196 | return true;
197 | }
198 |
199 |
200 | /**
201 | * Checks if the list is ordered
202 | *
203 | * @param list list to check
204 | * @return true if ordered
205 | */
206 | public boolean isOrdered(List list) {
207 | for (int i = 0; i < list.size() - 1; ++i)
208 | if (list.get(i).compareTo(list.get(i + 1)) == 1)
209 | return false;
210 |
211 |
212 | return true;
213 | }
214 |
215 | /**
216 | * Returns a string representation of the permutation
217 | * TODO: use disjoint set representation instead?
218 | */
219 | @Override
220 | public String toString() {
221 | String strFrom = from.toString();
222 | String strTo = to.toString();
223 |
224 | return strFrom + " -> " + strTo;
225 | }
226 |
227 | /**
228 | * Returns size of the permutation
229 | *
230 | * @return size of the permutation
231 | */
232 | public int size() {
233 | return to.size();
234 | }
235 |
236 | /**
237 | * Tests if the two permutations are equal
238 | * They are equal if the have the same
239 | * Lehmer code
240 | *
241 | * @param other object to be equal to the permutation
242 | * @return true if the two elements are equal
243 | */
244 | @Override
245 | public boolean equals(Object other) {
246 | if (other == null) return false;
247 | if (other == this) return true;
248 | if (!(other instanceof Permutation)) return false;
249 | Permutation otherPermutation = (Permutation) other;
250 |
251 | if (otherPermutation.size() != size())
252 | return false;
253 |
254 | return otherPermutation.hashCode() == hashCode();
255 |
256 | }
257 |
258 | /**
259 | * Returns a hashCode of the permutation
260 | * (Lehmer code based)
261 | *
262 | * @return lehmer code based hash code
263 | *
264 | * TODO: Is this a valid approach?
265 | */
266 | @Override
267 | public int hashCode() {
268 | return getPermutationIndex().hashCode();
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/PermutationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import org.junit.Test;
20 |
21 | import java.util.Arrays;
22 |
23 | import static org.junit.Assert.assertEquals;
24 | import static org.junit.Assert.assertNotEquals;
25 |
26 | public class PermutationTest {
27 |
28 | @Test
29 | public void testPermutationGeneration() {
30 | /**
31 | * Construct init object, log the permutation into the console
32 | */
33 | Permutation permutation = new Permutation<>(Arrays.asList(1, 2, 3, 4));
34 |
35 | /**
36 | * Check that the permutation index mapping is correct:
37 | */
38 | for (int i = 0; i < 24; ++i) {
39 | assertEquals(permutation.getPermutationIndex().intValue(), i);
40 | permutation = permutation.nextPermutation();
41 | }
42 |
43 | /**
44 | * Check equality between unequal-type permutations corresponding to the same permutation
45 | * mathematically.
46 | */
47 | Permutation permutationS = new Permutation<>(Arrays.asList('A', 'B', 'C', 'D'));
48 | permutationS = permutationS.nextPermutation();
49 | Permutation permutationT = new Permutation<>(Arrays.asList(1, 2, 3, 4), Arrays.asList(1, 2, 4, 3));
50 | assertEquals(permutationT, permutationS);
51 |
52 | /**
53 | * Check that different-sizes permutations are interpreted as unequal, although they have
54 | * the same permutation index.
55 | */
56 | Permutation permutationTN = new Permutation<>(Arrays.asList(1, 2, 3, 4, 5), Arrays.asList(1, 2, 3, 5, 4));
57 | assertNotEquals(permutationT, permutationTN);
58 |
59 |
60 | /**
61 | * Check that the range of normed permutation index is covered
62 | */
63 | Permutation completePermutation = new Permutation<>(Arrays.asList(1,2,3,4), Arrays.asList(4,3,2,1));
64 | System.out.format("Permutation index for complete permutation: %f\n", completePermutation.getNormedPermutationIndex());
65 | assertEquals(completePermutation.getNormedPermutationIndex(), 0, 10e-7);
66 |
67 | Permutation zeroPermutation = new Permutation<>(Arrays.asList('a','b','c'), Arrays.asList('a','b', 'c'));
68 | System.out.format("Permutation index for zero permutation: %f\n", zeroPermutation.getLogNormedPermutationIndex());
69 | assertEquals(zeroPermutation.getNormedPermutationIndex(), 1.0, 10e-7);
70 |
71 | }
72 | }
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/RankNodePair.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import com.google.common.base.Objects;
20 | import com.graphaware.common.util.Pair;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | /**
26 | * IndexNode pair, used for sorting the results of Page Rank algorithm
27 | *
28 | * The two RNP are equal if their names are equal. The comparable is
29 | * implemented with respect to the rank algorithm result value however!
30 | */
31 | public class RankNodePair extends Pair implements Comparable {
32 |
33 | /**
34 | * Construct a new pair.
35 | *
36 | * @param rank rank of the node
37 | * @param node node data
38 | */
39 | public RankNodePair(double rank, Long node) {
40 | super(rank, node);
41 | }
42 |
43 | /**
44 | * Return rank stored in the INP
45 | *
46 | * @return returns rank of the node
47 | */
48 | public double rank() {
49 | return first();
50 | }
51 |
52 | /**
53 | * Returns node stored in the INP
54 | */
55 | public Long node() {
56 | return second();
57 | }
58 |
59 | /**
60 | * Converts RankNodePairs to ArrayList of nodes
61 | *
62 | * @param rankNodePairs list
63 | * @return converted list of nodes
64 | */
65 | public static List convertToRankedNodeList(List rankNodePairs) {
66 | List toReturn = new ArrayList<>();
67 |
68 | // I am sure there is a plenty of room for improvement here ;)
69 | for (RankNodePair indexNodePair : rankNodePairs) {
70 | toReturn.add(indexNodePair.node());
71 | }
72 |
73 | return toReturn;
74 | }
75 |
76 | /**
77 | * Compares two ranks in descending order
78 | *
79 | * @param o RankNodePair to be compared to
80 | * @return -1 if this > o.rank(), 1 if < and 0 if =
81 | */
82 | @Override
83 | public int compareTo(RankNodePair o) {
84 | if (rank() > o.rank()) {
85 | return -1;
86 | }
87 | if (rank() < o.rank()) {
88 | return 1;
89 | }
90 | return 0;
91 | }
92 |
93 | /**
94 | * Two rank node pairs are equal iff their name is equal.
95 | */
96 | @Override
97 | public boolean equals(Object other) {
98 | if (other == null) return false;
99 | if (other == this) return true;
100 | if (!(other instanceof RankNodePair)) return false;
101 | RankNodePair otherRNP = (RankNodePair) other;
102 | return otherRNP.second().equals(second());
103 | }
104 |
105 |
106 | @Override
107 | public int hashCode() {
108 | return Objects.hashCode(first(), second());
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/test/java/com/graphaware/module/noderank/utils/SimilarityComparison.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2016 GraphAware
3 | *
4 | * This file is part of the GraphAware Framework.
5 | *
6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
7 | * the GNU General Public License as published by the Free Software Foundation, either
8 | * version 3 of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | * See the GNU General Public License for more details. You should have received a copy of
13 | * the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 |
17 | package com.graphaware.module.noderank.utils;
18 |
19 | import java.util.List;
20 |
21 | /**
22 | * Compares the result lists of two objects
23 | *
24 | */
25 | public class SimilarityComparison {
26 |
27 | /**
28 | * Returns the most basic similarity
29 | * measure of the two lists of THE SAME LENGTH!
30 | *
31 | * This is just a normalized Hamming distance of
32 | * the two strings.
33 | *
34 | * @param a first list
35 | * @param b second lisr
36 | * @return normalized hamming distance
37 | */
38 | public double getHammingDistanceMeasure(List a, List b) {
39 | if(a.size() != b.size())
40 | throw new RuntimeException("Two lists of unequal length were tested for similarity!");
41 |
42 | int length = a.size();
43 | int numerator = 0;
44 |
45 | for(int i = 0; i < length; ++i) {
46 | if(a.get(i).equals(b.get(i)))
47 | numerator ++;
48 | }
49 |
50 | return (double) numerator / (double) length;
51 | }
52 |
53 | /**
54 | * Returns an unordered similarity of
55 | * the two lists of THE SAME LENGTH!
56 | *
57 | * @param a first list
58 | * @param b second list
59 | * @return normalized number of equal elements
60 | */
61 | public double unorderedComparisonOfEqualLengthLists(List a, List b) {
62 | if(a.size() != b.size())
63 | throw new RuntimeException("Two lists of unequal length were tested for similarity!");
64 |
65 | int length = a.size();
66 | int numerator = 0;
67 |
68 | for(int i = 0; i < length; ++i) {
69 | if(a.contains(b.get(i)))
70 | numerator ++;
71 | }
72 |
73 | return (double) numerator / (double) length;
74 | }
75 |
76 | /**
77 | * TODO: weight success by lexicographic permutation distance?
78 | *
79 | * Weight results by their lexicographic distance
80 | */
81 | }
82 |
--------------------------------------------------------------------------------
/src/test/python/README.md:
--------------------------------------------------------------------------------
1 | Neorank test in Python
2 | ======================
3 |
4 | This is the most basic test comparing the classic
5 | pagerank with its variant using a discrete random
6 | walker on the network.
7 |
8 | Please make sure networkx and pylab are installed
9 | on your system to play around with the parameter
10 | set.
11 |
12 | To install, please use PIP (can be installed using
13 | homebrew on Mac or apt-get (Ubuntu) /yum (Fedora)
14 | or similar package manager on Linux)
15 |
16 |
17 | ## Notes on Running the Script
18 |
19 | The script is written for **Python 2** and is totally incompatible with Python 3. As stated above, you need to have networkx and pylab installed. If pip does the business for you then that's great, but, if not, then the following might be of use.
20 |
21 | ```
22 | sudo zypper install python-numpy
23 | sudo zypper install python-scipy python-matplotlib
24 | sudo zypper install python-matplotlib-tk
25 | ```
26 |
27 | Of course, `zypper` is for OpenSuSE so choose the package manager that is appropriate for your distribution.
--------------------------------------------------------------------------------
/src/test/python/neorank.py:
--------------------------------------------------------------------------------
1 | # Simple test of NeoRank Algorithm implemented in
2 | # Neo4j graph database
3 | import operator
4 | import networkx as nx
5 | import random as rnd
6 | import pylab
7 |
8 | # Allow for adaptive damping?
9 | # TODO: prepare a rigorous test of the algorithm
10 |
11 | # Picks an object from list at random
12 | # non-universal damping in pagerank?
13 | def pickRandom(lst):
14 | return rnd.choice(lst);
15 |
16 | N = 100
17 | hyperjump = 0.85
18 | ba = nx.barabasi_albert_graph(N, 2) # nx.erdos_renyi_graph(N, 0.8)# nx.fast_gnp_random_graph(N, p)
19 |
20 | steps = 1000
21 | nodes = ba.nodes() # List of nodes in the graph
22 |
23 | # Init neoranks for testing purposes
24 | for index in ba.nodes_iter():
25 | node = ba.node[index]
26 | node['neorank'] = 0
27 |
28 | current = pickRandom(nodes);
29 | normalization = 0;
30 |
31 |
32 | # Performs a step of a random walker on the graph
33 | def step():
34 | global current, normalization
35 |
36 | if rnd.random() < hyperjump:
37 | current = pickRandom(ba.neighbors(current))
38 | else:
39 | current = pickRandom(ba.nodes())
40 |
41 | # Increment the rank of the visited vertex
42 | node = ba.node[current]
43 | if 'neorank' in node:
44 | node['neorank'] += 1
45 | else :
46 | node['neorank'] = 0;
47 |
48 | # Increment normalization factor to present
49 | # the data consistently
50 | normalization += 1;
51 |
52 | for _ in range(steps):
53 | step()
54 |
55 |
56 | pagerank = list(reversed(sorted(nx.pagerank(ba, alpha = 1.0).iteritems(), key = operator.itemgetter(1))))
57 |
58 | print "highest pagerank: " + str( pagerank[0:3])
59 | # Perform analysis of NeoRank distribution
60 | neorank = list(reversed(sorted(ba.nodes(data = True), key = lambda (a, dct): dct['neorank'])))
61 |
62 | print "highest neoranks: " + str( neorank[0:3])
63 |
64 | labels = dict((n,str(n) + ", " + str(d['neorank'])) for n,d in ba.nodes(data = True))
65 | layout = nx.spring_layout(ba)
66 |
67 | # TODO: change node size according to its neoRank
68 | nx.draw(ba, layout, labels = labels, node_size = 1000)
69 |
70 | pylab.draw()
71 | pylab.show()
72 |
73 |
--------------------------------------------------------------------------------
/src/test/resources/int-test-neo4j.conf:
--------------------------------------------------------------------------------
1 | ################################################################
2 | # GraphAware configuration
3 | ################################################################
4 |
5 | dbms.unmanaged_extension_classes=com.graphaware.server=/graphaware
6 |
7 | com.graphaware.runtime.enabled=true
8 | com.graphaware.module.noderank.1=com.graphaware.module.noderank.NodeRankModuleBootstrapper
9 | com.graphaware.module.noderank.maxTopRankNodes=3
10 |
11 | #*****************************************************************
12 | # Neo4j configuration
13 | #*****************************************************************
14 |
15 | # The name of the database to mount
16 | #dbms.active_database=graph.db
17 |
18 | # Paths of directories in the installation.
19 | #dbms.directories.data=data
20 | #dbms.directories.plugins=plugins
21 | #dbms.directories.certificates=certificates
22 |
23 | # This setting constrains all `LOAD CSV` import files to be under the `import` directory. Remove or uncomment it to
24 | # allow files to be loaded from anywhere in filesystem; this introduces possible security problems. See the `LOAD CSV`
25 | # section of the manual for details.
26 | dbms.directories.import=import
27 |
28 | # Whether requests to Neo4j are authenticated.
29 | # To disable authentication, uncomment this line
30 | dbms.security.auth_enabled=false
31 |
32 | # Enable this to be able to upgrade a store from an older version.
33 | #dbms.allow_format_migration=true
34 |
35 | # The amount of memory to use for mapping the store files, in bytes (or
36 | # kilobytes with the 'k' suffix, megabytes with 'm' and gigabytes with 'g').
37 | # If Neo4j is running on a dedicated server, then it is generally recommended
38 | # to leave about 2-4 gigabytes for the operating system, give the JVM enough
39 | # heap to hold all your transaction state and query context, and then leave the
40 | # rest for the page cache.
41 | # The default page cache memory assumes the machine is dedicated to running
42 | # Neo4j, and is heuristically set to 50% of RAM minus the max Java heap size.
43 | #dbms.memory.pagecache.size=10g
44 |
45 | # Enable online backups to be taken from this database.
46 | dbms.backup.enabled=false
47 |
48 | # To allow remote backups, uncomment this line:
49 | #dbms.backup.address=0.0.0.0:6362
50 |
51 | #*****************************************************************
52 | # Network connector configuration
53 | #*****************************************************************
54 |
55 | # Bolt connector
56 | dbms.connector.bolt.type=BOLT
57 | dbms.connector.bolt.enabled=true
58 | dbms.connector.bolt.tls_level=OPTIONAL
59 | # To have Bolt accept non-local connections, uncomment this line
60 | # dbms.connector.bolt.address=0.0.0.0:7687
61 |
62 | # HTTP Connector
63 | dbms.connector.http.type=HTTP
64 | dbms.connector.http.enabled=true
65 | #dbms.connector.http.encryption=NONE
66 | # To have HTTP accept non-local connections, uncomment this line
67 | #dbms.connector.http.address=0.0.0.0:7474
68 |
69 | # HTTPS Connector
70 | dbms.connector.https.type=HTTP
71 | dbms.connector.https.enabled=true
72 | dbms.connector.https.encryption=TLS
73 | dbms.connector.https.address=localhost:7573
74 |
75 | # Number of Neo4j worker threads.
76 | #dbms.threads.worker_count=
77 |
78 | #*****************************************************************
79 | # Logging configuration
80 | #*****************************************************************
81 |
82 | # To enable HTTP logging, uncomment this line
83 | #dbms.logs.http.enabled=true
84 |
85 | # To enable GC Logging, uncomment this line
86 | #dbms.logs.gc.enabled=true
87 |
88 | # GC Logging Options
89 | # see http://docs.oracle.com/cd/E19957-01/819-0084-10/pt_tuningjava.html#wp57013 for more information.
90 | #dbms.logs.gc.options=-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintPromotionFailure -XX:+PrintTenuringDistribution
91 |
92 | # Number of GC logs to keep.
93 | #dbms.logs.gc.rotation.keep_number=5
94 |
95 | # Size of each GC log that is kept.
96 | #dbms.logs.gc.rotation.size=20m
97 |
98 | # Size threshold for rotation of the debug log. If set to zero then no rotation will occur. Accepts a binary suffix "k",
99 | # "m" or "g".
100 | #dbms.logs.debug.rotation.size=20m
101 |
102 | # Maximum number of history files for the internal log.
103 | #dbms.logs.debug.rotation.keep_number=7
104 |
105 | # Log executed queries that takes longer than the configured threshold. Enable by uncommenting this line.
106 | #dbms.logs.query.enabled=true
107 |
108 | # If the execution of query takes more time than this threshold, the query is logged. If set to zero then all queries
109 | # are logged.
110 | #dbms.logs.query.threshold=0
111 |
112 | # The file size in bytes at which the query log will auto-rotate. If set to zero then no rotation will occur. Accepts a
113 | # binary suffix "k", "m" or "g".
114 | #dbms.logs.query.rotation.size=20m
115 |
116 | # Maximum number of history files for the query log.
117 | #dbms.logs.query.rotation.keep_number=7
118 |
119 | #*****************************************************************
120 | # HA configuration
121 | #*****************************************************************
122 |
123 | # Uncomment and specify these lines for running Neo4j in High Availability mode.
124 | # See the High availability setup tutorial for more details on these settings
125 | # http://neo4j.com/docs/3.0.1/ha-setup-tutorial.html
126 |
127 | # Database mode
128 | # Allowed values:
129 | # HA - High Availability
130 | # SINGLE - Single mode, default.
131 | # To run in High Availability mode uncomment this line:
132 | #dbms.mode=HA
133 |
134 | # ha.server_id is the number of each instance in the HA cluster. It should be
135 | # an integer (e.g. 1), and should be unique for each cluster instance.
136 | #ha.server_id=
137 |
138 | # ha.initial_hosts is a comma-separated list (without spaces) of the host:port
139 | # where the ha.host.coordination of all instances will be listening. Typically
140 | # this will be the same for all cluster instances.
141 | #ha.initial_hosts=127.0.0.1:5001,127.0.0.1:5002,127.0.0.1:5003
142 |
143 | # IP and port for this instance to listen on, for communicating cluster status
144 | # information iwth other instances (also see ha.initial_hosts). The IP
145 | # must be the configured IP address for one of the local interfaces.
146 | #ha.host.coordination=127.0.0.1:5001
147 |
148 | # IP and port for this instance to listen on, for communicating transaction
149 | # data with other instances (also see ha.initial_hosts). The IP
150 | # must be the configured IP address for one of the local interfaces.
151 | #ha.host.data=127.0.0.1:6001
152 |
153 | # The interval at which slaves will pull updates from the master. Comment out
154 | # the option to disable periodic pulling of updates. Unit is seconds.
155 | ha.pull_interval=10
156 |
157 | # Amount of slaves the master will try to push a transaction to upon commit
158 | # (default is 1). The master will optimistically continue and not fail the
159 | # transaction even if it fails to reach the push factor. Setting this to 0 will
160 | # increase write performance when writing through master but could potentially
161 | # lead to branched data (or loss of transaction) if the master goes down.
162 | #ha.tx_push_factor=1
163 |
164 | # Strategy the master will use when pushing data to slaves (if the push factor
165 | # is greater than 0). There are three options available "fixed_ascending" (default),
166 | # "fixed_descending" or "round_robin". Fixed strategies will start by pushing to
167 | # slaves ordered by server id (accordingly with qualifier) and are useful when
168 | # planning for a stable fail-over based on ids.
169 | #ha.tx_push_strategy=fixed_ascending
170 |
171 | # Policy for how to handle branched data.
172 | #ha.branched_data_policy=keep_all
173 |
174 | # How often heartbeat messages should be sent. Defaults to ha.default_timeout.
175 | #ha.heartbeat_interval=5s
176 |
177 | # Timeout for heartbeats between cluster members. Should be at least twice that of ha.heartbeat_interval.
178 | #ha.heartbeat_timeout=11s
179 |
180 | # If you are using a load-balancer that doesn't support HTTP Auth, you may need to turn off authentication for the
181 | # HA HTTP status endpoint by uncommenting the following line.
182 | #dbms.security.ha_status_auth_enabled=false
183 |
184 | # Whether this instance should only participate as slave in cluster. If set to
185 | # true, it will never be elected as master.
186 | #ha.slave_only=false
187 |
188 | #*****************************************************************
189 | # Miscellaneous configuration
190 | #*****************************************************************
191 |
192 | # Enable this to specify a parser other than the default one.
193 | #cypher.default_language_version=3.0
194 |
195 | # Determines if Cypher will allow using file URLs when loading data using
196 | # `LOAD CSV`. Setting this value to `false` will cause Neo4j to fail `LOAD CSV`
197 | # clauses that load data from the file system.
198 | #dbms.security.allow_csv_import_from_file_urls=true
199 |
200 | # Retention policy for transaction logs needed to perform recovery and backups.
201 | #dbms.tx_log.rotation.retention_policy=7 days
202 |
203 | # Limit the number of IOs the background checkpoint process will consume per second.
204 | # This setting is advisory, is ignored in Neo4j Community Edition, and is followed to
205 | # best effort in Enterprise Edition.
206 | # An IO is in this case a 8 KiB (mostly sequential) write. Limiting the write IO in
207 | # this way will leave more bandwidth in the IO subsystem to service random-read IOs,
208 | # which is important for the response time of queries when the database cannot fit
209 | # entirely in memory. The only drawback of this setting is that longer checkpoint times
210 | # may lead to slightly longer recovery times in case of a database or system crash.
211 | # A lower number means lower IO pressure, and consequently longer checkpoint times.
212 | # The configuration can also be commented out to remove the limitation entirely, and
213 | # let the checkpointer flush data as fast as the hardware will go.
214 | # Set this to -1 to disable the IOPS limit.
215 | # dbms.checkpoint.iops.limit=1000
216 |
217 | # Enable a remote shell server which Neo4j Shell clients can log in to.
218 | #dbms.shell.enabled=true
219 | # The network interface IP the shell will listen on (use 0.0.0.0 for all interfaces).
220 | #dbms.shell.host=127.0.0.1
221 | # The port the shell will listen on, default is 1337.
222 | #dbms.shell.port=1337
223 |
224 | # Only allow read operations from this Neo4j instance. This mode still requires
225 | # write access to the directory for lock purposes.
226 | #dbms.read_only=false
227 |
228 | # Comma separated list of JAX-RS packages containing JAX-RS resources, one
229 | # package name for each mountpoint. The listed package names will be loaded
230 | # under the mountpoints specified. Uncomment this line to mount the
231 | # org.neo4j.examples.server.unmanaged.HelloWorldResource.java from
232 | # neo4j-server-examples under /examples/unmanaged, resulting in a final URL of
233 | # http://localhost:7474/examples/unmanaged/helloworld/{nodeId}
234 | #dbms.unmanaged_extension_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged
235 |
--------------------------------------------------------------------------------
/src/test/resources/neo4j-2.conf:
--------------------------------------------------------------------------------
1 | #*****************************************************************
2 | # Neo4j configuration
3 | #*****************************************************************
4 |
5 | # The name of the database to mount
6 | #dbms.active_database=graph.db
7 |
8 | # Paths of directories in the installation.
9 | #dbms.directories.data=data
10 | #dbms.directories.plugins=plugins
11 | #dbms.directories.certificates=certificates
12 |
13 | # This setting constrains all `LOAD CSV` import files to be under the `import` directory. Remove or uncomment it to
14 | # allow files to be loaded from anywhere in filesystem; this introduces possible security problems. See the `LOAD CSV`
15 | # section of the manual for details.
16 | dbms.directories.import=import
17 |
18 | # Whether requests to Neo4j are authenticated.
19 | # To disable authentication, uncomment this line
20 | dbms.security.auth_enabled=false
21 |
22 | # Enable this to be able to upgrade a store from an older version.
23 | #dbms.allow_format_migration=true
24 |
25 | # The amount of memory to use for mapping the store files, in bytes (or
26 | # kilobytes with the 'k' suffix, megabytes with 'm' and gigabytes with 'g').
27 | # If Neo4j is running on a dedicated server, then it is generally recommended
28 | # to leave about 2-4 gigabytes for the operating system, give the JVM enough
29 | # heap to hold all your transaction state and query context, and then leave the
30 | # rest for the page cache.
31 | # The default page cache memory assumes the machine is dedicated to running
32 | # Neo4j, and is heuristically set to 50% of RAM minus the max Java heap size.
33 | #dbms.memory.pagecache.size=10g
34 |
35 | # Enable online backups to be taken from this database.
36 | #dbms.backup.enabled=true
37 |
38 | # To allow remote backups, uncomment this line:
39 | #dbms.backup.address=0.0.0.0:6362
40 |
41 | #*****************************************************************
42 | # Network connector configuration
43 | #*****************************************************************
44 |
45 | # Bolt connector
46 | dbms.connector.bolt.type=BOLT
47 | dbms.connector.bolt.enabled=true
48 | dbms.connector.bolt.tls_level=OPTIONAL
49 | # To have Bolt accept non-local connections, uncomment this line
50 | # dbms.connector.bolt.address=0.0.0.0:7687
51 |
52 | # HTTP Connector
53 | dbms.connector.http.type=HTTP
54 | dbms.connector.http.enabled=true
55 | #dbms.connector.http.encryption=NONE
56 | # To have HTTP accept non-local connections, uncomment this line
57 | #dbms.connector.http.address=0.0.0.0:7474
58 |
59 | # HTTPS Connector
60 | dbms.connector.https.type=HTTP
61 | dbms.connector.https.enabled=true
62 | dbms.connector.https.encryption=TLS
63 | dbms.connector.https.address=localhost:7473
64 |
65 | # Number of Neo4j worker threads.
66 | #dbms.threads.worker_count=
67 |
68 | #*****************************************************************
69 | # Logging configuration
70 | #*****************************************************************
71 |
72 | # To enable HTTP logging, uncomment this line
73 | #dbms.logs.http.enabled=true
74 |
75 | # To enable GC Logging, uncomment this line
76 | #dbms.logs.gc.enabled=true
77 |
78 | # GC Logging Options
79 | # see http://docs.oracle.com/cd/E19957-01/819-0084-10/pt_tuningjava.html#wp57013 for more information.
80 | #dbms.logs.gc.options=-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintPromotionFailure -XX:+PrintTenuringDistribution
81 |
82 | # Number of GC logs to keep.
83 | #dbms.logs.gc.rotation.keep_number=5
84 |
85 | # Size of each GC log that is kept.
86 | #dbms.logs.gc.rotation.size=20m
87 |
88 | # Size threshold for rotation of the debug log. If set to zero then no rotation will occur. Accepts a binary suffix "k",
89 | # "m" or "g".
90 | #dbms.logs.debug.rotation.size=20m
91 |
92 | # Maximum number of history files for the internal log.
93 | #dbms.logs.debug.rotation.keep_number=7
94 |
95 | # Log executed queries that takes longer than the configured threshold. Enable by uncommenting this line.
96 | #dbms.logs.query.enabled=true
97 |
98 | # If the execution of query takes more time than this threshold, the query is logged. If set to zero then all queries
99 | # are logged.
100 | #dbms.logs.query.threshold=0
101 |
102 | # The file size in bytes at which the query log will auto-rotate. If set to zero then no rotation will occur. Accepts a
103 | # binary suffix "k", "m" or "g".
104 | #dbms.logs.query.rotation.size=20m
105 |
106 | # Maximum number of history files for the query log.
107 | #dbms.logs.query.rotation.keep_number=7
108 |
109 | #*****************************************************************
110 | # HA configuration
111 | #*****************************************************************
112 |
113 | # Uncomment and specify these lines for running Neo4j in High Availability mode.
114 | # See the High availability setup tutorial for more details on these settings
115 | # http://neo4j.com/docs/operations-manual/current/#tutorials
116 |
117 | # Database mode
118 | # Allowed values:
119 | # HA - High Availability
120 | # SINGLE - Single mode, default.
121 | # To run in High Availability mode uncomment this line:
122 | #dbms.mode=HA
123 |
124 | # ha.server_id is the number of each instance in the HA cluster. It should be
125 | # an integer (e.g. 1), and should be unique for each cluster instance.
126 | #ha.server_id=
127 |
128 | # ha.initial_hosts is a comma-separated list (without spaces) of the host:port
129 | # where the ha.host.coordination of all instances will be listening. Typically
130 | # this will be the same for all cluster instances.
131 | #ha.initial_hosts=127.0.0.1:5001,127.0.0.1:5002,127.0.0.1:5003
132 |
133 | # IP and port for this instance to listen on, for communicating cluster status
134 | # information iwth other instances (also see ha.initial_hosts). The IP
135 | # must be the configured IP address for one of the local interfaces.
136 | #ha.host.coordination=127.0.0.1:5001
137 |
138 | # IP and port for this instance to listen on, for communicating transaction
139 | # data with other instances (also see ha.initial_hosts). The IP
140 | # must be the configured IP address for one of the local interfaces.
141 | #ha.host.data=127.0.0.1:6001
142 |
143 | # The interval at which slaves will pull updates from the master. Comment out
144 | # the option to disable periodic pulling of updates. Unit is seconds.
145 | ha.pull_interval=10
146 |
147 | # Amount of slaves the master will try to push a transaction to upon commit
148 | # (default is 1). The master will optimistically continue and not fail the
149 | # transaction even if it fails to reach the push factor. Setting this to 0 will
150 | # increase write performance when writing through master but could potentially
151 | # lead to branched data (or loss of transaction) if the master goes down.
152 | #ha.tx_push_factor=1
153 |
154 | # Strategy the master will use when pushing data to slaves (if the push factor
155 | # is greater than 0). There are three options available "fixed_ascending" (default),
156 | # "fixed_descending" or "round_robin". Fixed strategies will start by pushing to
157 | # slaves ordered by server id (accordingly with qualifier) and are useful when
158 | # planning for a stable fail-over based on ids.
159 | #ha.tx_push_strategy=fixed_ascending
160 |
161 | # Policy for how to handle branched data.
162 | #ha.branched_data_policy=keep_all
163 |
164 | # How often heartbeat messages should be sent. Defaults to ha.default_timeout.
165 | #ha.heartbeat_interval=5s
166 |
167 | # Timeout for heartbeats between cluster members. Should be at least twice that of ha.heartbeat_interval.
168 | #ha.heartbeat_timeout=11s
169 |
170 | # If you are using a load-balancer that doesn't support HTTP Auth, you may need to turn off authentication for the
171 | # HA HTTP status endpoint by uncommenting the following line.
172 | #dbms.security.ha_status_auth_enabled=false
173 |
174 | # Whether this instance should only participate as slave in cluster. If set to
175 | # true, it will never be elected as master.
176 | #ha.slave_only=false
177 |
178 | #*****************************************************************
179 | # Miscellaneous configuration
180 | #*****************************************************************
181 |
182 | # Enable this to specify a parser other than the default one.
183 | #cypher.default_language_version=3.0
184 |
185 | # Determines if Cypher will allow using file URLs when loading data using
186 | # `LOAD CSV`. Setting this value to `false` will cause Neo4j to fail `LOAD CSV`
187 | # clauses that load data from the file system.
188 | #dbms.security.allow_csv_import_from_file_urls=true
189 |
190 | # Retention policy for transaction logs needed to perform recovery and backups.
191 | #dbms.tx_log.rotation.retention_policy=7 days
192 |
193 | # Limit the number of IOs the background checkpoint process will consume per second.
194 | # This setting is advisory, is ignored in Neo4j Community Edition, and is followed to
195 | # best effort in Enterprise Edition.
196 | # An IO is in this case a 8 KiB (mostly sequential) write. Limiting the write IO in
197 | # this way will leave more bandwidth in the IO subsystem to service random-read IOs,
198 | # which is important for the response time of queries when the database cannot fit
199 | # entirely in memory. The only drawback of this setting is that longer checkpoint times
200 | # may lead to slightly longer recovery times in case of a database or system crash.
201 | # A lower number means lower IO pressure, and consequently longer checkpoint times.
202 | # The configuration can also be commented out to remove the limitation entirely, and
203 | # let the checkpointer flush data as fast as the hardware will go.
204 | # Set this to -1 to disable the IOPS limit.
205 | # dbms.checkpoint.iops.limit=1000
206 |
207 | # Enable a remote shell server which Neo4j Shell clients can log in to.
208 | #dbms.shell.enabled=true
209 | # The network interface IP the shell will listen on (use 0.0.0.0 for all interfaces).
210 | #dbms.shell.host=127.0.0.1
211 | # The port the shell will listen on, default is 1337.
212 | #dbms.shell.port=1337
213 |
214 | # Only allow read operations from this Neo4j instance. This mode still requires
215 | # write access to the directory for lock purposes.
216 | #dbms.read_only=false
217 |
218 | # Comma separated list of JAX-RS packages containing JAX-RS resources, one
219 | # package name for each mountpoint. The listed package names will be loaded
220 | # under the mountpoints specified. Uncomment this line to mount the
221 | # org.neo4j.examples.server.unmanaged.HelloWorldResource.java from
222 | # neo4j-server-examples under /examples/unmanaged, resulting in a final URL of
223 | # http://localhost:7474/examples/unmanaged/helloworld/{nodeId}
224 | #dbms.unmanaged_extension_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged
225 |
226 | #For the framework to work at all, you need this
227 | dbms.unmanaged_extension_classes=com.graphaware.server=/graphaware
228 |
229 | # Runtime must be enabled like this
230 | com.graphaware.runtime.enabled=true
231 |
232 | #NR becomes the module ID:
233 | com.graphaware.module.NR.1=com.graphaware.module.noderank.NodeRankModuleBootstrapper
234 |
235 | #optional number of top ranked nodes to remember, the default is 10
236 | com.graphaware.module.NR.maxTopRankNodes=10
237 |
238 | #optional daming factor, which is a number p such that a random node will be selected at any step of the algorithm
239 | #with the probability 1-p (as opposed to following a random relationship). The default is 0.85
240 | com.graphaware.module.NR.dampingFactor=0.85
241 |
242 | #optional key of the property that gets written to the ranked nodes, default is "nodeRank"
243 | com.graphaware.module.NR.propertyKey=nodeRank
244 |
245 | #optionally specify nodes to rank using an expression-based node inclusion policy, default is all business (i.e. non-framework-internal) nodes
246 | com.graphaware.module.NR.node=hasLabel('Person')
247 |
248 | #optionally specify relationships to follow using an expression-based relationship inclusion policy, default is all business (i.e. non-framework-internal) relationships
249 | com.graphaware.module.NR.relationship=isType('FRIEND_OF')
250 |
251 |
252 | #NX becomes the module ID:
253 | com.graphaware.module.NX.2=com.graphaware.module.noderank.NodeRankModuleBootstrapper
254 |
255 | #optional number of top ranked nodes to remember, the default is 10
256 | com.graphaware.module.NX.maxTopRankNodes=10
257 |
258 | #optional daming factor, which is a number p such that a random node will be selected at any step of the algorithm
259 | #with the probability 1-p (as opposed to following a random relationship). The default is 0.85
260 | com.graphaware.module.NX.dampingFactor=0.85
261 |
262 | #optional key of the property that gets written to the ranked nodes, default is "nodeRank"
263 | com.graphaware.module.NX.propertyKey=nodeRank
264 |
265 | #optionally specify nodes to rank using an expression-based node inclusion policy, default is all business (i.e. non-framework-internal) nodes
266 | com.graphaware.module.NX.node=hasLabel('PersonX')
267 |
268 | #optionally specify relationships to follow using an expression-based relationship inclusion policy, default is all business (i.e. non-framework-internal) relationships
269 | com.graphaware.module.NX.relationship=isType('FRIEND_OF')
270 |
--------------------------------------------------------------------------------
/src/test/resources/schema.cyp:
--------------------------------------------------------------------------------
1 | CREATE INDEX ON :`PhoneNumber`(`id`);
2 | CREATE INDEX ON :`Email`(`subject`);
3 | CREATE CONSTRAINT ON (node:`User`) ASSERT node.`id` IS UNIQUE;
4 | CREATE CONSTRAINT ON (node:`Repository`) ASSERT node.`id` IS UNIQUE;
5 | CREATE CONSTRAINT ON (node:`File`) ASSERT node.`path` IS UNIQUE;
6 | CREATE CONSTRAINT ON (node:`PullRequest`) ASSERT node.`id` IS UNIQUE;
7 | CREATE CONSTRAINT ON (node:`EmailAddress`) ASSERT node.`_id` IS UNIQUE;
8 | CREATE CONSTRAINT ON (node:`UNIQUE IMPORT LABEL`) ASSERT node.`UNIQUE IMPORT ID` IS UNIQUE;
--------------------------------------------------------------------------------
/src/test/resources/test-neo4j.conf:
--------------------------------------------------------------------------------
1 | ################################################################
2 | # GraphAware configuration
3 | ################################################################
4 |
5 | dbms.unmanaged_extension_classes=com.graphaware.server=/graphaware
6 |
7 | com.graphaware.runtime.enabled=true
8 | com.graphaware.module.noderank.1=com.graphaware.module.noderank.NodeRankModuleBootstrapper
9 |
10 | #*****************************************************************
11 | # Neo4j configuration
12 | #*****************************************************************
13 |
14 | # The name of the database to mount
15 | #dbms.active_database=graph.db
16 |
17 | # Paths of directories in the installation.
18 | #dbms.directories.data=data
19 | #dbms.directories.plugins=plugins
20 | #dbms.directories.certificates=certificates
21 |
22 | # This setting constrains all `LOAD CSV` import files to be under the `import` directory. Remove or uncomment it to
23 | # allow files to be loaded from anywhere in filesystem; this introduces possible security problems. See the `LOAD CSV`
24 | # section of the manual for details.
25 | dbms.directories.import=import
26 |
27 | # Whether requests to Neo4j are authenticated.
28 | # To disable authentication, uncomment this line
29 | dbms.security.auth_enabled=false
30 |
31 | # Enable this to be able to upgrade a store from an older version.
32 | #dbms.allow_format_migration=true
33 |
34 | # The amount of memory to use for mapping the store files, in bytes (or
35 | # kilobytes with the 'k' suffix, megabytes with 'm' and gigabytes with 'g').
36 | # If Neo4j is running on a dedicated server, then it is generally recommended
37 | # to leave about 2-4 gigabytes for the operating system, give the JVM enough
38 | # heap to hold all your transaction state and query context, and then leave the
39 | # rest for the page cache.
40 | # The default page cache memory assumes the machine is dedicated to running
41 | # Neo4j, and is heuristically set to 50% of RAM minus the max Java heap size.
42 | #dbms.memory.pagecache.size=10g
43 |
44 | # Enable online backups to be taken from this database.
45 | dbms.backup.enabled=false
46 |
47 | # To allow remote backups, uncomment this line:
48 | #dbms.backup.address=0.0.0.0:6362
49 |
50 | #*****************************************************************
51 | # Network connector configuration
52 | #*****************************************************************
53 |
54 | # Bolt connector
55 | dbms.connector.bolt.type=BOLT
56 | dbms.connector.bolt.enabled=true
57 | dbms.connector.bolt.tls_level=OPTIONAL
58 | # To have Bolt accept non-local connections, uncomment this line
59 | # dbms.connector.bolt.address=0.0.0.0:7687
60 |
61 | # HTTP Connector
62 | dbms.connector.http.type=HTTP
63 | dbms.connector.http.enabled=true
64 | #dbms.connector.http.encryption=NONE
65 | # To have HTTP accept non-local connections, uncomment this line
66 | #dbms.connector.http.address=0.0.0.0:7474
67 |
68 | # HTTPS Connector
69 | dbms.connector.https.type=HTTP
70 | dbms.connector.https.enabled=true
71 | dbms.connector.https.encryption=TLS
72 | dbms.connector.https.address=localhost:7573
73 |
74 | # Number of Neo4j worker threads.
75 | #dbms.threads.worker_count=
76 |
77 | #*****************************************************************
78 | # Logging configuration
79 | #*****************************************************************
80 |
81 | # To enable HTTP logging, uncomment this line
82 | #dbms.logs.http.enabled=true
83 |
84 | # To enable GC Logging, uncomment this line
85 | #dbms.logs.gc.enabled=true
86 |
87 | # GC Logging Options
88 | # see http://docs.oracle.com/cd/E19957-01/819-0084-10/pt_tuningjava.html#wp57013 for more information.
89 | #dbms.logs.gc.options=-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintPromotionFailure -XX:+PrintTenuringDistribution
90 |
91 | # Number of GC logs to keep.
92 | #dbms.logs.gc.rotation.keep_number=5
93 |
94 | # Size of each GC log that is kept.
95 | #dbms.logs.gc.rotation.size=20m
96 |
97 | # Size threshold for rotation of the debug log. If set to zero then no rotation will occur. Accepts a binary suffix "k",
98 | # "m" or "g".
99 | #dbms.logs.debug.rotation.size=20m
100 |
101 | # Maximum number of history files for the internal log.
102 | #dbms.logs.debug.rotation.keep_number=7
103 |
104 | # Log executed queries that takes longer than the configured threshold. Enable by uncommenting this line.
105 | #dbms.logs.query.enabled=true
106 |
107 | # If the execution of query takes more time than this threshold, the query is logged. If set to zero then all queries
108 | # are logged.
109 | #dbms.logs.query.threshold=0
110 |
111 | # The file size in bytes at which the query log will auto-rotate. If set to zero then no rotation will occur. Accepts a
112 | # binary suffix "k", "m" or "g".
113 | #dbms.logs.query.rotation.size=20m
114 |
115 | # Maximum number of history files for the query log.
116 | #dbms.logs.query.rotation.keep_number=7
117 |
118 | #*****************************************************************
119 | # HA configuration
120 | #*****************************************************************
121 |
122 | # Uncomment and specify these lines for running Neo4j in High Availability mode.
123 | # See the High availability setup tutorial for more details on these settings
124 | # http://neo4j.com/docs/3.0.1/ha-setup-tutorial.html
125 |
126 | # Database mode
127 | # Allowed values:
128 | # HA - High Availability
129 | # SINGLE - Single mode, default.
130 | # To run in High Availability mode uncomment this line:
131 | #dbms.mode=HA
132 |
133 | # ha.server_id is the number of each instance in the HA cluster. It should be
134 | # an integer (e.g. 1), and should be unique for each cluster instance.
135 | #ha.server_id=
136 |
137 | # ha.initial_hosts is a comma-separated list (without spaces) of the host:port
138 | # where the ha.host.coordination of all instances will be listening. Typically
139 | # this will be the same for all cluster instances.
140 | #ha.initial_hosts=127.0.0.1:5001,127.0.0.1:5002,127.0.0.1:5003
141 |
142 | # IP and port for this instance to listen on, for communicating cluster status
143 | # information iwth other instances (also see ha.initial_hosts). The IP
144 | # must be the configured IP address for one of the local interfaces.
145 | #ha.host.coordination=127.0.0.1:5001
146 |
147 | # IP and port for this instance to listen on, for communicating transaction
148 | # data with other instances (also see ha.initial_hosts). The IP
149 | # must be the configured IP address for one of the local interfaces.
150 | #ha.host.data=127.0.0.1:6001
151 |
152 | # The interval at which slaves will pull updates from the master. Comment out
153 | # the option to disable periodic pulling of updates. Unit is seconds.
154 | ha.pull_interval=10
155 |
156 | # Amount of slaves the master will try to push a transaction to upon commit
157 | # (default is 1). The master will optimistically continue and not fail the
158 | # transaction even if it fails to reach the push factor. Setting this to 0 will
159 | # increase write performance when writing through master but could potentially
160 | # lead to branched data (or loss of transaction) if the master goes down.
161 | #ha.tx_push_factor=1
162 |
163 | # Strategy the master will use when pushing data to slaves (if the push factor
164 | # is greater than 0). There are three options available "fixed_ascending" (default),
165 | # "fixed_descending" or "round_robin". Fixed strategies will start by pushing to
166 | # slaves ordered by server id (accordingly with qualifier) and are useful when
167 | # planning for a stable fail-over based on ids.
168 | #ha.tx_push_strategy=fixed_ascending
169 |
170 | # Policy for how to handle branched data.
171 | #ha.branched_data_policy=keep_all
172 |
173 | # How often heartbeat messages should be sent. Defaults to ha.default_timeout.
174 | #ha.heartbeat_interval=5s
175 |
176 | # Timeout for heartbeats between cluster members. Should be at least twice that of ha.heartbeat_interval.
177 | #ha.heartbeat_timeout=11s
178 |
179 | # If you are using a load-balancer that doesn't support HTTP Auth, you may need to turn off authentication for the
180 | # HA HTTP status endpoint by uncommenting the following line.
181 | #dbms.security.ha_status_auth_enabled=false
182 |
183 | # Whether this instance should only participate as slave in cluster. If set to
184 | # true, it will never be elected as master.
185 | #ha.slave_only=false
186 |
187 | #*****************************************************************
188 | # Miscellaneous configuration
189 | #*****************************************************************
190 |
191 | # Enable this to specify a parser other than the default one.
192 | #cypher.default_language_version=3.0
193 |
194 | # Determines if Cypher will allow using file URLs when loading data using
195 | # `LOAD CSV`. Setting this value to `false` will cause Neo4j to fail `LOAD CSV`
196 | # clauses that load data from the file system.
197 | #dbms.security.allow_csv_import_from_file_urls=true
198 |
199 | # Retention policy for transaction logs needed to perform recovery and backups.
200 | #dbms.tx_log.rotation.retention_policy=7 days
201 |
202 | # Limit the number of IOs the background checkpoint process will consume per second.
203 | # This setting is advisory, is ignored in Neo4j Community Edition, and is followed to
204 | # best effort in Enterprise Edition.
205 | # An IO is in this case a 8 KiB (mostly sequential) write. Limiting the write IO in
206 | # this way will leave more bandwidth in the IO subsystem to service random-read IOs,
207 | # which is important for the response time of queries when the database cannot fit
208 | # entirely in memory. The only drawback of this setting is that longer checkpoint times
209 | # may lead to slightly longer recovery times in case of a database or system crash.
210 | # A lower number means lower IO pressure, and consequently longer checkpoint times.
211 | # The configuration can also be commented out to remove the limitation entirely, and
212 | # let the checkpointer flush data as fast as the hardware will go.
213 | # Set this to -1 to disable the IOPS limit.
214 | # dbms.checkpoint.iops.limit=1000
215 |
216 | # Enable a remote shell server which Neo4j Shell clients can log in to.
217 | #dbms.shell.enabled=true
218 | # The network interface IP the shell will listen on (use 0.0.0.0 for all interfaces).
219 | #dbms.shell.host=127.0.0.1
220 | # The port the shell will listen on, default is 1337.
221 | #dbms.shell.port=1337
222 |
223 | # Only allow read operations from this Neo4j instance. This mode still requires
224 | # write access to the directory for lock purposes.
225 | #dbms.read_only=false
226 |
227 | # Comma separated list of JAX-RS packages containing JAX-RS resources, one
228 | # package name for each mountpoint. The listed package names will be loaded
229 | # under the mountpoints specified. Uncomment this line to mount the
230 | # org.neo4j.examples.server.unmanaged.HelloWorldResource.java from
231 | # neo4j-server-examples under /examples/unmanaged, resulting in a final URL of
232 | # http://localhost:7474/examples/unmanaged/helloworld/{nodeId}
233 | #dbms.unmanaged_extension_classes=org.neo4j.examples.server.unmanaged=/examples/unmanaged
234 |
--------------------------------------------------------------------------------