Skip to content

Commit 7b81b0f

Browse files
committed
CQRS 4.5 - TransitAnalyzer - implementacja grafu
1 parent c9632e5 commit 7b81b0f

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<groupId>org.springframework.boot</groupId>
88
<artifactId>spring-boot-starter-parent</artifactId>
99
<version>2.4.4</version>
10+
1011
<relativePath/> <!-- lookup parent from repository -->
1112
</parent>
1213
<groupId>io.refactorers</groupId>
@@ -48,6 +49,31 @@
4849
<scope>test</scope>
4950
</dependency>
5051

52+
<dependency>
53+
<groupId>org.neo4j.driver</groupId>
54+
<artifactId>neo4j-java-driver</artifactId>
55+
<version>4.4.3</version>
56+
</dependency>
57+
58+
<dependency>
59+
<groupId>org.neo4j</groupId>
60+
<artifactId>neo4j-ogm-embedded-driver</artifactId>
61+
<version>3.2.28</version>
62+
</dependency>
63+
64+
<dependency>
65+
<groupId>org.neo4j</groupId>
66+
<artifactId>neo4j-ogm-test</artifactId>
67+
<version>3.1.22</version>
68+
</dependency>
69+
70+
<dependency>
71+
<groupId>org.awaitility</groupId>
72+
<artifactId>awaitility</artifactId>
73+
<version>3.0.0</version>
74+
<scope>test</scope>
75+
</dependency>
76+
5177
<dependency>
5278
<groupId>org.togglz</groupId>
5379
<artifactId>togglz-junit5</artifactId>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.legacyfighter.cabs.config;
2+
3+
import io.legacyfighter.cabs.transitanalyzer.GraphTransitAnalyzer;
4+
import org.neo4j.graphdb.GraphDatabaseService;
5+
import org.springframework.beans.factory.annotation.Value;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
10+
@Configuration
11+
class Neo4jConfig {
12+
13+
@Value("${neo4j.db.file:testPath}")
14+
private String dbPath;
15+
16+
@Bean(destroyMethod = "onClose")
17+
GraphTransitAnalyzer graphTransitAnalyzer() {
18+
return new GraphTransitAnalyzer(notConnectedOnProdYet(dbPath));
19+
}
20+
21+
@Bean
22+
GraphDatabaseService notConnectedOnProdYet(String dbPath) {
23+
return null;
24+
}
25+
26+
27+
}
28+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package io.legacyfighter.cabs.transitanalyzer;
2+
3+
4+
import org.neo4j.graphdb.*;
5+
6+
import java.time.Instant;
7+
import java.time.ZoneId;
8+
import java.time.format.DateTimeFormatter;
9+
import java.util.List;
10+
import java.util.stream.Collectors;
11+
12+
13+
public class GraphTransitAnalyzer {
14+
15+
private final GraphDatabaseService graphDb;
16+
17+
public GraphTransitAnalyzer(GraphDatabaseService graphDatabaseService) {
18+
this.graphDb = graphDatabaseService;
19+
}
20+
21+
public List<Long> analyze(Long clientId, Integer addressHash) {
22+
try (Transaction t = graphDb.beginTx()) {
23+
Result result = graphDb.execute("MATCH p=(a:Address)-[:Transit*]->(:Address) " +
24+
"WHERE a.hash = " + addressHash + " " +
25+
"AND (ALL(x IN range(1, length(p)-1) WHERE ((relationships(p)[x]).clientId = " + clientId + ") AND 0 <= duration.inSeconds( (relationships(p)[x-1]).completeAt, (relationships(p)[x]).started).minutes <= 15)) " +
26+
"AND length(p) >= 1 " +
27+
"RETURN [x in nodes(p) | x.hash] AS hashes ORDER BY length(p) DESC LIMIT 1");
28+
29+
t.success();
30+
31+
return ((List<Long>) result.next().get("hashes")).stream().collect(Collectors.toList());
32+
}
33+
}
34+
35+
public void addTransitBetweenAddresses(Long clientId, Long transitId, Integer addressFromHash, Integer addressToHash, Instant started, Instant completeAt) {
36+
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault());
37+
38+
try (Transaction t = graphDb.beginTx()) {
39+
graphDb.execute("MERGE (from:Address {hash: " + addressFromHash + "})");
40+
graphDb.execute("MERGE (to:Address {hash: " + addressToHash + "})");
41+
graphDb.execute("MATCH (from:Address {hash: " + addressFromHash + "}), (to:Address {hash: " + addressToHash + "}) " +
42+
"CREATE (from)-[:Transit {clientId: " + clientId + ", transitId: " + transitId + ", " +
43+
"started: datetime(\"" + formatter.format(started) + "\"), completeAt: datetime(\"" + formatter.format(completeAt) + "\") }]->(to)");
44+
45+
t.success();
46+
}
47+
}
48+
49+
public void onClose() {
50+
if (graphDb != null) {
51+
graphDb.shutdown();
52+
}
53+
}
54+
55+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.legacyfighter.cabs.common;
2+
3+
4+
import io.legacyfighter.cabs.CabsApplication;
5+
import io.legacyfighter.cabs.transitanalyzer.GraphTransitAnalyzer;
6+
import org.neo4j.graphdb.GraphDatabaseService;
7+
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.boot.test.context.SpringBootTest;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Primary;
12+
import org.springframework.util.FileSystemUtils;
13+
14+
import javax.annotation.PreDestroy;
15+
import java.io.File;
16+
17+
18+
@SpringBootTest(properties = "neo4j.db.file=${random.int}", classes = {CabsApplication.class, TestWithGraphDB.TestNeo4jConfig.class})
19+
public class TestWithGraphDB {
20+
21+
static class TestNeo4jConfig {
22+
23+
@Value("${neo4j.db.file}")
24+
private String dbPath;
25+
26+
GraphDatabaseService testGraphDatabaseService() {
27+
GraphDatabaseFactory graphDbFactory = new GraphDatabaseFactory();
28+
FileSystemUtils.deleteRecursively(new File("db"));
29+
File storeDir = new File("db/" + dbPath);
30+
return graphDbFactory.newEmbeddedDatabase(storeDir);
31+
}
32+
33+
@Bean(destroyMethod = "onClose")
34+
@Primary
35+
GraphTransitAnalyzer testGraphTransitAnalyzer() {
36+
return new GraphTransitAnalyzer(testGraphDatabaseService());
37+
}
38+
39+
@PreDestroy
40+
void cleanDbDir() {
41+
FileSystemUtils.deleteRecursively(new File("db"));
42+
}
43+
44+
}
45+
46+
}
47+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.legacyfighter.cabs.integration;
2+
3+
import io.legacyfighter.cabs.common.TestWithGraphDB;
4+
import io.legacyfighter.cabs.dto.AddressDTO;
5+
import io.legacyfighter.cabs.transitanalyzer.GraphTransitAnalyzer;
6+
import org.junit.jupiter.api.Test;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.boot.test.context.SpringBootTest;
9+
10+
import java.time.Instant;
11+
import java.util.List;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
class GraphTransitAnalyzerIntegrationTest extends TestWithGraphDB {
16+
17+
@Autowired
18+
GraphTransitAnalyzer analyzer;
19+
20+
@Test
21+
void canRecognizeNewAddress() {
22+
//given
23+
analyzer.addTransitBetweenAddresses(1L, 1L, 111, 222, Instant.now(), Instant.now());
24+
analyzer.addTransitBetweenAddresses(1L, 1L, 222, 333, Instant.now(), Instant.now());
25+
analyzer.addTransitBetweenAddresses(1L, 1L, 333, 444, Instant.now(), Instant.now());
26+
27+
//when
28+
List<Long> result = analyzer.analyze(1L, 111);
29+
30+
//then
31+
assertThat(result).containsExactly(111L, 222L, 333L, 444L);
32+
33+
}
34+
35+
}

0 commit comments

Comments
 (0)