狠狠撸

狠狠撸Share a Scribd company logo
INTELLIGENCE, Ltd
?2012 INTELLIGENCE, Ltd. All Rights Reserved.
INTELLIGENCE, Ltd
?2012 INTELLIGENCE, Ltd. All Rights Reserved.
Neo4jの
「データ操作プログラミング」から
「ビジュアライズ」まで
2016.01.25
清田 馨一郎
自己紹介
【名前】
清田 馨一郎 (@seikei1874)
【所属】
キャリアDiv.
マーケティング企画統括部
データアナリティクス部
マーケティングテクノロジーグループ
【経歴】
2002年 受託メインの会社に入社
PGから叩き上げでPMまで経験
大手企業の基幹システムからソーシャルゲーム開発まで幅広く経験
2014年4月からインテリジェンスへJOIN
部門内のKPI集計、見える化を進めるシステムの開発、運用を担当
石抱き
これから話す内容は、個人の見解であり、
所属する組織、団体の見解ではありません。
「At your own risk」でお願いします。
3 | 25-Jan-16 |
Neo4jの利用
?サイト流入、回遊状況の集計
? 一時的にNeo4jに格納し、集計を簡単にしている
?サイト回遊状況の可視化
? まだ、実験段階
?なぜ、Neo4jを利用したのか?
? 遷移数が「不定」なので、RDBでは難易度が高いと思った
4 | 25-Jan-16 |
Rest API /
組み込み(Embedded) API
5 | 25-Jan-16 |
アーキテクチャ図
6 | 25-Jan-16 |
組み込みAPI REST API
JAVA
(もしくは、Scala、Groovy
などJAVA APIが利用できる
言語)
Neo4jサーバー
アプリケーション
プログラミング言語
REST API
クライアント
データファイル
Neo4j
REST APIと組み込みAPI
?REST API
? Neo4jサーバを起動すれば、即利用可能
? REST APIなので、クライアンの言語、FWは問わない
? 他サーバから呼べる
? HTTP通信、REST解析が行わるので、若干遅い
? 基本は、こちらを利用する
?組み込みAPI(EmbeddedAPI)
? JAVA API(JVMで動く言語から利用可)
? 同じサーバからのみ
? Neo4jをデータストアとして利用し、大量、高速に処理したい
? ServerAPIを作成したい
7 | 25-Jan-16 |
集計できるまで
?データ量
? 1日分 120万件くらい(計測したい部分のみ)
? Mac Book Proで開発
?最初
? Cypherで、CSVインポート
? 丸一日でも終わらない
? 1ページを1ノードとして作成したので、特定のノード間で、数万エッジと偏りが生じる
? 終端ノードを取得する検索で、偏っていたエッジを全探索していた
?ReTry(2回目)
? 1PVを1ノードに変更
? 13時間くらい
? 終端ノードの検索が遅い
8 | 25-Jan-16 |
集計できるまで
?ReTry(3回目)
? 組み込みAPIを利用
? 5時間くらい
? やはり、終端ノードを検索するクエリが遅い
?ReTry(4回目)
? 投入データの前処理で、ユーザ毎に時系列で並べ替える
? 終端ノードの検索はしないで、追加した終端ノードを保持するように変更
? 30分くらい
? 更に並列処理化して、4,5分程度に収める
? 最大で2億ノードくらいある状態でも実行したが処理速度は、それほど変わらなかった
9 | 25-Jan-16 |
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 |
?Neo4j JDBC
? https://github.com/neo4j-contrib/neo4j-jdbc
JDBCドライバ経由で、REST APIと通信できる
アプリケーションでの利用は現実的で無く、
DBのビジュアルツールのドライバとして使用すべき
?DataNucleus
? 様々なDBのORMライブラリ群らしい
?Py2Neo、neo4jPHPなど、様々な言語のFWがある
?詳しくは、Neo4jデベロッパーサイト
? http://neo4j.com/developer/language-guides/
11 | 25-Jan-16 |
OGMのサンプルソース
12 | 25-Jan-16 |
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 |
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();
});
}
}
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");
}
}
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();
}
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 | 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 | 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;
/**
略
*/
}
Embedded API
サンプルソース
20 | 25-Jan-16 |
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 | 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 | 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 | 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;
});
?REST API
? Neo4Jサーバを起動すれば、即利用可能
? REST APIなので、クライアンの言語、FWは問わない
? 他サーバから呼べる
? HTTP通信、REST解析が行わるので、若干遅い
? 基本は、こちらを利用する
?組み込みAPI(EmbeddedAPI)
? JAVA API(JVMで動く言語から利用可)
? 同じサーバからのみ
? Neo4jをデータストアとして利用し、大量、高速に処理したい
? ServerAPIを作成したい
25 | 25-Jan-16 |
26 | 25-Jan-16 |
Server Plugin/
Unmanaged Extension
最初に まとめ
?標準のREST APIとCypherの組み合わせで十分なことが多いが
パフォーマンスや、トラーバサルを細かく定義したい場合がある
?Embedded APIを利用すればパフォーマンスの問題は解決できるが
Serverモードとの併用ができない
?REST API経由でEmbedded APIが利用できるServer APIを自作する
?Server APIは、「Server Plugin」「Unmanaged Extension」
?「Unmanaged Extension」は、Neo4jの管轄外となる
? どのような影響があるが把握していないが、オフィシャルでは利用を推奨していない
?「Unmanaged Extension」では、JSON以外で返せる
27 | 25-Jan-16 |
アーキテクチャ図
28 | 25-Jan-16 |
Neo4j サーバ
REST
クライア
ント
REST サーバ
(JAX-RS)
Neo4jのREST API
Unmanaged
Extension
Server Plugin
Embedded API データ
ファイル
HTTP
HTTP
Server Pluginの作成
?Java。「org.neo4j.server.plugins.ServerPlugin」を継承する
?継承先から渡される「GraphDatabaseService」を利用して処理する
?ServerPluginから返せる値
? Node型
? Relationship型
? Path型
? Java プリミティブ型
? String型
? org.neo4j.server.rest.repr.Representation
?ServiceLoaderなので、META-INF/services配下に
「org.neo4j.server.plugins.ServerPlugin」ファイルを作成し、
そのファイルにpluginにしたいクラスを記述する
$ cat src/main/resources/META-INF/services/org.neo4j.server.plugins.ServerPlugin
jp.co.inte.Sample
?jarを作成して、${NEO4J_HOME}/pluginに配置する
29 | 25-Jan-16 |
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 | 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"
}
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();
}
}
Unmanaged Extensionの作成
?「neo4j-server.properties」に呼び出したいExtensionを記述する
org.neo4j.server.thirdparty_jaxrs_classes=jp.co.inte=/sample/unmanage
?この例では、パッケージ「jp.co.inte」にあるクラス群を
「/sample/unmanage」で呼ぶ定義。
?サンプルコードのパスは「/sample/unmanage/helloworld」となる。
33 | 25-Jan-16 |
もう一度 まとめ
?標準のREST APIとCypherの組み合わせで十分なことが多いが
パフォーマンスや、トラーバサルを細かく定義したい場合がある
?Embedded APIを利用すればパフォーマンスの問題は解決できるが
Serverモードとの併用ができない
?REST API経由でEmbedded APIが利用できるServer APIを自作する
?Server APIは、「Server Plugin」「Unmanaged Extension」
?「Unmanaged Extension」は、Neo4jの管轄外となる
? どのような影響があるが把握していないが、オフィシャルでは利用を推奨していない
?「Unmanaged Extension」では、JSON以外で返せる
34 | 25-Jan-16 |
ビジュアライゼーション
35 | 25-Jan-16 |
ビジュアライゼーション
?linkurious.js
https://github.com/Linkurious/linkurious.js
? フランスのベンチャーlinkurious(https://linkurio.us/)が提供している
Sigma.jsベースのJSライブラリ。
? REST API経由で、Cypherを実行してビジュアライズするJavaScriptが標準で用意
36 | 25-Jan-16 |
ビジュアライゼーション
?Gephi
https://gephi.wordpress.com/
? ネットワーク分析 GUIツール
? Neo4j Shell-toolsを利用すれば、Gephiで読み込めるデータ形式で出力可能
https://github.com/jexp/neo4j-shell-tools
37 | 25-Jan-16 |
ビジュアライゼーション
?Popoto.js
http://www.popotojs.com/
? d3.jsベースのJavaScriptライブラリ
? インタラクティブなデータ探索が可能
38 | 25-Jan-16 |
まとめ
?まずは、REST API+RESTフレームワークで検討しよう。
?パフォーマンスがでなければ、Server Pluginを検討しよう。
?Embeddedモードは最後の手段
? Neo4jをバッチ処理のみに使用するなら、Embeddedモード
?ブラウザでビジュアライズしたければ、Linkrious.jsが良い
?ネットワーク分析がしたければ、Gephi
39 | 25-Jan-16 |
お願い
?知見、試してみた事の共有を是非!!
? GraphAware、DataNucleusの使い方
? ネットワーク分析
? Gephiの使い方
? Spark GraphXとNeo4jの関係性
– GraphXのデータストアにNeo4jが利用できる?
? 複雑ネットワーク分析をNeo4jで。
– ネットワークの特徴量、類似性、伝播モデルなどなど
40 | 25-Jan-16 |
41 | 25-Jan-16 |

More Related Content

Neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで

  • 1. INTELLIGENCE, Ltd ?2012 INTELLIGENCE, Ltd. All Rights Reserved. INTELLIGENCE, Ltd ?2012 INTELLIGENCE, Ltd. All Rights Reserved. Neo4jの 「データ操作プログラミング」から 「ビジュアライズ」まで 2016.01.25 清田 馨一郎
  • 2. 自己紹介 【名前】 清田 馨一郎 (@seikei1874) 【所属】 キャリアDiv. マーケティング企画統括部 データアナリティクス部 マーケティングテクノロジーグループ 【経歴】 2002年 受託メインの会社に入社 PGから叩き上げでPMまで経験 大手企業の基幹システムからソーシャルゲーム開発まで幅広く経験 2014年4月からインテリジェンスへJOIN 部門内のKPI集計、見える化を進めるシステムの開発、運用を担当 石抱き
  • 5. Rest API / 組み込み(Embedded) API 5 | 25-Jan-16 |
  • 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 |
  • 11. ?Neo4j JDBC ? https://github.com/neo4j-contrib/neo4j-jdbc JDBCドライバ経由で、REST APIと通信できる アプリケーションでの利用は現実的で無く、 DBのビジュアルツールのドライバとして使用すべき ?DataNucleus ? 様々なDBのORMライブラリ群らしい ?Py2Neo、neo4jPHPなど、様々な言語のFWがある ?詳しくは、Neo4jデベロッパーサイト ? http://neo4j.com/developer/language-guides/ 11 | 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 |
  • 26. 26 | 25-Jan-16 | Server Plugin/ Unmanaged Extension
  • 27. 最初に まとめ ?標準のREST APIとCypherの組み合わせで十分なことが多いが パフォーマンスや、トラーバサルを細かく定義したい場合がある ?Embedded APIを利用すればパフォーマンスの問題は解決できるが Serverモードとの併用ができない ?REST API経由でEmbedded APIが利用できるServer APIを自作する ?Server APIは、「Server Plugin」「Unmanaged Extension」 ?「Unmanaged Extension」は、Neo4jの管轄外となる ? どのような影響があるが把握していないが、オフィシャルでは利用を推奨していない ?「Unmanaged Extension」では、JSON以外で返せる 27 | 25-Jan-16 |
  • 28. アーキテクチャ図 28 | 25-Jan-16 | Neo4j サーバ REST クライア ント REST サーバ (JAX-RS) Neo4jのREST API Unmanaged Extension Server Plugin Embedded API データ ファイル HTTP HTTP
  • 29. Server Pluginの作成 ?Java。「org.neo4j.server.plugins.ServerPlugin」を継承する ?継承先から渡される「GraphDatabaseService」を利用して処理する ?ServerPluginから返せる値 ? Node型 ? Relationship型 ? Path型 ? Java プリミティブ型 ? String型 ? org.neo4j.server.rest.repr.Representation ?ServiceLoaderなので、META-INF/services配下に 「org.neo4j.server.plugins.ServerPlugin」ファイルを作成し、 そのファイルにpluginにしたいクラスを記述する $ cat src/main/resources/META-INF/services/org.neo4j.server.plugins.ServerPlugin jp.co.inte.Sample ?jarを作成して、${NEO4J_HOME}/pluginに配置する 29 | 25-Jan-16 |
  • 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(); } }
  • 34. もう一度 まとめ ?標準のREST APIとCypherの組み合わせで十分なことが多いが パフォーマンスや、トラーバサルを細かく定義したい場合がある ?Embedded APIを利用すればパフォーマンスの問題は解決できるが Serverモードとの併用ができない ?REST API経由でEmbedded APIが利用できるServer APIを自作する ?Server APIは、「Server Plugin」「Unmanaged Extension」 ?「Unmanaged Extension」は、Neo4jの管轄外となる ? どのような影響があるが把握していないが、オフィシャルでは利用を推奨していない ?「Unmanaged Extension」では、JSON以外で返せる 34 | 25-Jan-16 |
  • 37. ビジュアライゼーション ?Gephi https://gephi.wordpress.com/ ? ネットワーク分析 GUIツール ? Neo4j Shell-toolsを利用すれば、Gephiで読み込めるデータ形式で出力可能 https://github.com/jexp/neo4j-shell-tools 37 | 25-Jan-16 |
  • 39. まとめ ?まずは、REST API+RESTフレームワークで検討しよう。 ?パフォーマンスがでなければ、Server Pluginを検討しよう。 ?Embeddedモードは最後の手段 ? Neo4jをバッチ処理のみに使用するなら、Embeddedモード ?ブラウザでビジュアライズしたければ、Linkrious.jsが良い ?ネットワーク分析がしたければ、Gephi 39 | 25-Jan-16 |
  • 40. お願い ?知見、試してみた事の共有を是非!! ? GraphAware、DataNucleusの使い方 ? ネットワーク分析 ? Gephiの使い方 ? Spark GraphXとNeo4jの関係性 – GraphXのデータストアにNeo4jが利用できる? ? 複雑ネットワーク分析をNeo4jで。 – ネットワークの特徴量、類似性、伝播モデルなどなど 40 | 25-Jan-16 |