Mais conteúdo relacionado
Semelhante a Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで (20)
Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
- 6. アーキテクチャ図
6 | 25-Jan-16 |
組み込みAPI REST API
JAVA
(もしくは、Scala、Groovy
などJAVA APIが利用できる
言語)
Neo4jサーバー
アプリケーション
プログラミング言語
REST API
クライアント
データファイル
Neo4j
- 7. REST APIと組み込みAPI
REST API
Neo4jサーバを起動すれば、即利用可能
REST APIなので、クライアンの言語、FWは問わない
他サーバから呼べる
HTTP通信、REST解析が行わるので、若干遅い
基本は、こちらを利用する
組み込みAPI(EmbeddedAPI)
JAVA API(JVMで動く言語から利用可)
同じサーバからのみ
Neo4jをデータストアとして利用し、大量、高速に処理したい
ServerAPIを作成したい
7 | 25-Jan-16 |
- 8. 集計できるまで
データ量
1日分 120万件くらい(計測したい部分のみ)
Mac Book Proで開発
最初
Cypherで、CSVインポート
丸一日でも終わらない
1ページを1ノードとして作成したので、特定のノード間で、数万エッジと偏りが生じる
終端ノードを取得する検索で、偏っていたエッジを全探索していた
ReTry(2回目)
1PVを1ノードに変更
13時間くらい
終端ノードの検索が遅い
8 | 25-Jan-16 |
- 9. 集計できるまで
ReTry(3回目)
組み込みAPIを利用
5時間くらい
やはり、終端ノードを検索するクエリが遅い
ReTry(4回目)
投入データの前処理で、ユーザ毎に時系列で並べ替える
終端ノードの検索はしないで、追加した終端ノードを保持するように変更
30分くらい
更に並列処理化して、4,5分程度に収める
最大で2億ノードくらいある状態でも実行したが処理速度は、それほど変わらなかった
9 | 25-Jan-16 |
- 10. Neo4j REST API
OGM(Object Graph Mapper)
SDN4(Spring Data Neo4j)
http://projects.spring.io/spring-data-neo4j/
JavaのアプリケーションFWでは、一般的なSpringFrameworkのコンポーネント
OGMベース。GraphAwareの社員がフルコミットで開発している。
この社員はOGMのコミッターなので、Javaでの開発であれば、SDN4を選択すべき
GraphAware
Neo4jに関することを主力とした英国のベンチャー
Neo4j関連のOSSを多数公開している
http://graphaware.com/products/
GraphAware Framework
https://github.com/graphaware/neo4j-framework/
10 | 25-Jan-16 |
- 13. OGM サンプル
src
└ main
├── java
│ └── jp
│ └── co
│ └── inte
│ ├── Neo4JOGMSample.java
│ ├── Neo4jSessionFactory.java
│ ├── domain
│ │ ├── nodes
│ │ │ ├── Movie.java
│ │ │ └── Person.java
│ │ └── relationships
│ │ ├── Acted.java
│ │ └── Directed.java
│ └── service
│ ├── GenericService.java
│ ├── PersonService.java
│ ├── Service.java
│ └── impl
│ └── PersonServiceImpl.java
└── resources
13 | 25-Jan-16 |
- 14. Neo4jOGMSample.java
14 | 25-Jan-16 |
/**
* 1956年生まれの俳優/女優の名前と、出演した映画のタイトルを出演します。
*/
public class Neo4JOGMSample {
public static void main(String... args) {
PersonService service = new PersonServiceImpl();
Iterable p = service.findByBorn(1956);
p.forEach(person -> {
System.out.print(person.getName());
// Personノードから「ACTED_IN」でリレーションシップされているMovieノードを取得
List acteds = person.getActed();
acteds.forEach( acted -> {
System.out.print(" : " + acted.getMovie().getTitle());
});
System.out.println();
});
}
}
- 15. Neo4jSessionFactory.java
15 | 25-Jan-16 |
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
public class Neo4jSessionFactory {
// Entityクラスがあるパッケージ名を渡す
private final static SessionFactory sessionFactory =
new SessionFactory("jp.co.inte.domain.nodes", "jp.co.inte.domain.relationships");
private static Neo4jSessionFactory factory = new Neo4jSessionFactory();
public static Neo4jSessionFactory getInstance() {
return factory;
}
private Neo4jSessionFactory() {
}
public Session getNeo4jSession() {
return sessionFactory.openSession("http://localhost:7474", "neo4j", "neo");
}
}
- 16. GenericService.java
16 | 25-Jan-16 |
public abstract class GenericService<T> implements Service<T> {
private static final int DEPTH_LIST = 0;
private static final int DEPTH_ENTITY = 2;
private Session session = Neo4jSessionFactory.getInstance().getNeo4jSession();
@Override
public Iterable<T> findAll() {
return session.loadAll(getEntityType(), DEPTH_LIST);
}
@Override
public T find(Long id) {
return session.load(getEntityType(), id, DEPTH_ENTITY);
}
public abstract Class<T> getEntityType();
}
- 17. PersonServiceImpl.java
17 | 25-Jan-16 |
public class PersonServiceImpl extends GenericService<Person> implements PersonService {
@Override
public Iterable<Person> findByBorn(int born) {
/**
* リレーションシップも返さないとNodeEntityクラスのRelationshipに値が入らないので注意
*/
String query = "MATCH (p:Person)-[a:ACTED_IN|DIRECTED]->() where p.born = {born} return p,a";
Session session = Neo4jSessionFactory.getInstance().getNeo4jSession();
Map<String, Integer> params = new HashMap<>();
params.put("born", born);
return session.query(getEntityType(), query, params);
}
@Override
public Class<Person> getEntityType() {
return Person.class;
}
}
- 18. ノードのドメイン
18 | 25-Jan-16 |
/*
* アノテーションでLABEL、ノードのプロパティ、リレーションシップタイプなどを指定します。
*/
@NodeEntity(label = "Movie")
public class Movie {
@GraphId
private Long id;
@Property(name = "title")
private String title;
@Property(name = "tagline")
private String tagline;
@Property(name = "released")
private Integer released;
@Relationship(type = "ACTED_IN", direction = "INCOMING")
private Person person;
/**
Getter、Setterは省略
**/
}
- 19. リレーションシップのドメイン
19 | 25-Jan-16 |
/*
* リレーションシップのエンティティでは、開始、終了ノードもアノテーションで指定できます。
*/
@RelationshipEntity(type = "ACTED_IN")
public class Acted {
@GraphId
private Long id;
@Property(name = "roles")
private List<String> roles;
@StartNode private Person person;
@EndNode private Movie movie;
/**
略
*/
}
- 21. Embedded API
21 | 25-Jan-16 |
/**
* Neo4J起動
*/
GraphDatabaseService graphDatabaseService = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder(Paths.get("").toFile())
.newGraphDatabase();
/**
* 実行中にNeo4j Shellで接続可能
*/
GraphDatabaseService graphDatabaseService = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder(Paths.get("").toFile())
.setConfig(ShellSettings.remote_shell_enabled, "true")
.setConfig(ShellSettings.remote_shell_port, "5555")
.setConfig(ShellSettings.remote_shell_read_only, "true")
.newGraphDatabase();
// 終了
graphDatabaseService.shutdown();
- 22. 22 | 25-Jan-16 |
/*
* Ctrl-Cなどの停止が発生した場合の処理
* Neo4Jへの接続を停止する。
*/
private static void registerShutdownHook(final GraphDatabaseService graphDatabaseService) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
graphDatabaseService.shutdown();
}
});
}
// 起動したNeo4jのインスタンスを渡す
registerShutdownHook(graphDatabaseService);
- 23. 23 | 25-Jan-16 |
try (Transaction transaction = graphDatabaseService.beginTx()) {
// ノードの作成
Node startNode = graphDatabaseService.createNode(DynamicLabel.label("LABEL"));
Node endNode = graphDatabaseService.createNode(DynamicLabel.label("LABEL"));
// リレーションシップの作成
Relationship rel = startNode.createRelationshipTo(endNode, DynamicRelationshipType.withName("TYPE"));
// successs設定しないとプロパティを更新できない
transaction.success();
// プロパティの設定
startNode.setProperty("property", "value");
rel.setProperty("property", "value");
transaction.success();
}
- 24. 24 | 25-Jan-16 |
for (Path path : traversal.traverse(node)) {
pages.add((String) path.endNode().getProperty("url"));
}
// トラバーサル
TraversalDescription traversal = graphDatabaseService.traversalDescription()
.relationships(DynamicRelationshipType.withName("TYPE"), Direction.OUTGOING)
.evaluator(path -> {
Node node = path.endNode();
// 特定のラベルを持っていなければ含まない
if (!node.hasLabel(DynamicLabel.label("LABEL"))) {
return Evaluation.EXCLUDE_AND_CONTINUE;
}
// 目的のプロパティを持っていたら、以降は枝切り
String url = (String) node.getProperty("url");
if (url.matches(".*HOGE.*")) {
return Evaluation.INCLUDE_AND_PRUNE;
}
return Evaluation.INCLUDE_AND_CONTINUE;
});
- 25. REST API
Neo4Jサーバを起動すれば、即利用可能
REST APIなので、クライアンの言語、FWは問わない
他サーバから呼べる
HTTP通信、REST解析が行わるので、若干遅い
基本は、こちらを利用する
組み込みAPI(EmbeddedAPI)
JAVA API(JVMで動く言語から利用可)
同じサーバからのみ
Neo4jをデータストアとして利用し、大量、高速に処理したい
ServerAPIを作成したい
25 | 25-Jan-16 |
- 28. アーキテクチャ図
28 | 25-Jan-16 |
Neo4j サーバ
REST
クライア
ント
REST サーバ
(JAX-RS)
Neo4jのREST API
Unmanaged
Extension
Server Plugin
Embedded API データ
ファイル
HTTP
HTTP
- 30. 30 | 25-Jan-16 |
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.neo4j.server.plugins.Name;
import org.neo4j.server.plugins.PluginTarget;
import org.neo4j.server.plugins.ServerPlugin;
import org.neo4j.server.plugins.Source;
public class Sample extends ServerPlugin {
@Name("plugin_sample")
@PluginTarget(GraphDatabaseService.class)
public Long count(@Source GraphDatabaseService graphDb) {
Long count = 0L;
try(Transaction tx = graphDb.beginTx()) {
Result result = graphDb.execute("MATCH (n) return count(n) as cnt");
count = result.next();
count = (Long)ret.get("cnt");
}
return count;
}
}
- 31. 31 | 25-Jan-16 |
$ curl http://localhost:7474/db/data/
{
"extensions" : {
"Sample" : {
"plugin_sample" : "http://localhost:7474/db/data/ext/Sample/graphdb/plugin_sample"
}
},
"node" : "http://localhost:7474/db/data/node", "node_index" :
"http://localhost:7474/db/data/index/node", "relationship_index" :
"http://localhost:7474/db/data/index/relationship", "extensions_info" :
"http://localhost:7474/db/data/ext", "relationship_types" :
"http://localhost:7474/db/data/relationship/types", "batch" :
"http://localhost:7474/db/data/batch", "cypher" : "http://localhost:7474/db/data/cypher",
"indexes" : "http://localhost:7474/db/data/schema/index", "constraints" :
"http://localhost:7474/db/data/schema/constraint", "transaction" :
"http://localhost:7474/db/data/transaction", "node_labels" :
"http://localhost:7474/db/data/labels", "neo4j_version" : "2.3.0"
}
- 32. Unmanaged Extensionの作成
32 | 25-Jan-16 |
package jp.co.inte;
import org.neo4j.graphdb.GraphDatabaseService;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.nio.charset.Charset;
@Path("/helloworld")
public class UnmanagedPluginSample {
private final GraphDatabaseService graphDatabaseService;
public UnmanagedPluginSample(@Context GraphDatabaseService graphDatabaseService) {
this.graphDatabaseService = graphDatabaseService;
}
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/{nodeId}")
public Response hello(@PathParam("nodeId") long nodeId) {
return Response.status(Response.Status.OK).entity(
("Hello World, nodeId=" + nodeId).getBytes( Charset.forName("UTF-8") )
).build();
}
}