neo4j の「データ操作プログラミング」から 「ビジュアライズ」まで
TRANSCRIPT
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 | May 1, 2023 |
Neo4j の利用
サイト流入、回遊状況の集計 一時的に Neo4j に格納し、集計を簡単にしている
サイト回遊状況の可視化 まだ、実験段階
なぜ、 Neo4j を利用したのか? 遷移数が「不定」なので、 RDB では難易度が高いと思った
4 | May 1, 2023 |
Rest API / 組み込み (Embedded) API
5 | May 1, 2023 |
アーキテクチャ図
6 | May 1, 2023 |
組み込み API REST API
JAVA( もしく
は、 Scala 、 Groovy などJAVA API が利用できる言語 )
Neo4j サーバー
アプリケーション
プログラミング言語
REST APIクライアント
データファイル
Neo4j
REST API と組み込み APIREST API
Neo4j サーバを起動すれば、即利用可能 REST API なので、クライアンの言語、 FW は問わない 他サーバから呼べる HTTP 通信、 REST 解析が行わるので、若干遅い 基本は、こちらを利用する
組み込み API ( EmbeddedAPI ) JAVA API ( JVM で動く言語から利用可) 同じサーバからのみ Neo4j をデータストアとして利用し、大量、高速に処理したい ServerAPI を作成したい
7 | May 1, 2023 |
集計できるまで
データ量 1 日分 120 万件くらい(計測したい部分のみ) Mac Book Pro で開発
最初 Cypher で、 CSV インポート 丸一日でも終わらない 1 ページを 1 ノードとして作成したので、特定のノード間で、数万エッジと偏りが生じ
る 終端ノードを取得する検索で、偏っていたエッジを全探索していた
ReTry ( 2 回目) 1PV を 1 ノードに変更 13 時間くらい 終端ノードの検索が遅い
8 | May 1, 2023 |
集計できるまで
ReTry ( 3 回目) 組み込み API を利用 5 時間くらい やはり、終端ノードを検索するクエリが遅い
ReTry ( 4 回目) 投入データの前処理で、ユーザ毎に時系列で並べ替える 終端ノードの検索はしないで、追加した終端ノードを保持するように変更 30 分くらい 更に並列処理化して、 4,5 分程度に収める 最大で 2 億ノードくらいある状態でも実行したが処理速度は、それほど変わらなかった
9 | May 1, 2023 |
Neo4j REST APIOGM(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 | May 1, 2023 |
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 | May 1, 2023 |
OGM のサンプルソース
12 | May 1, 2023 |
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 | May 1, 2023 |
Neo4jOGMSample.java
14 | May 1, 2023 |
/** * 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 | May 1, 2023 |
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 | May 1, 2023 |
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 | May 1, 2023 |
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 | May 1, 2023 |
/* * アノテーションで 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 | May 1, 2023 |
/* * リレーションシップのエンティティでは、開始、終了ノードもアノテーションで指定できます。 */@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 | May 1, 2023 |
Embedded API
21 | May 1, 2023 |
/** * 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 | May 1, 2023 |
/* * 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 | May 1, 2023 |
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 | May 1, 2023 |
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 Neo4 J サーバを起動すれば、即利用可能 REST API なので、クライアンの言語、 FW は問わない 他サーバから呼べる HTTP 通信、 REST 解析が行わるので、若干遅い 基本は、こちらを利用する
組み込み API ( EmbeddedAPI ) JAVA API ( JVM で動く言語から利用可) 同じサーバからのみ Neo4j をデータストアとして利用し、大量、高速に処理したい ServerAPI を作成したい
25 | May 1, 2023 |
26 | May 1, 2023 |
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 | May 1, 2023 |
アーキテクチャ図
28 | May 1, 2023 |
Neo4j サーバ
RESTクライア
ント
REST サーバ(JAX-RS)
Neo4j の REST API
UnmanagedExtension
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.ServerPluginjp.co.inte.Sample
jar を作成して、 ${NEO4J_HOME}/plugin に配置する
29 | May 1, 2023 |
30 | May 1, 2023 |
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 | May 1, 2023 |
$ 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 | May 1, 2023 |
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 | May 1, 2023 |
もう一度 まとめ
標準の 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 | May 1, 2023 |
ビジュアライゼーション
35 | May 1, 2023 |
ビジュアライゼーション
linkurious.jshttps://github.com/Linkurious/linkurious.js
フランスのベンチャー linkurious(https://linkurio.us/) が提供しているSigma.jsベースの JS ライブラリ。
REST API 経由で、 Cypher を実行してビジュアライズする JavaScript が標準で用意
36 | May 1, 2023 |
ビジュアライゼーション
Gephihttps://gephi.wordpress.com/ ネットワーク分析 GUIツール
Neo4j Shell-tools を利用すれば、 Gephi で読み込めるデータ形式で出力可能https://github.com/jexp/neo4j-shell-tools
37 | May 1, 2023 |
ビジュアライゼーション
Popoto.jshttp://www.popotojs.com/
d3.jsベースの JavaScript ライブラリ インタラクティブなデータ探索が可能
38 | May 1, 2023 |
まとめ
まずは、 REST API+ REST フレームワークで検討しよう。パフォーマンスがでなければ、 Server Plugin を検討しよう。Embeddedモードは最後の手段
Neo4j をバッチ処理のみに使用するなら、 Embeddedモード
ブラウザでビジュアライズしたければ、 Linkrious.js が良いネットワーク分析がしたければ、 Gephi
39 | May 1, 2023 |
お願い
知見、試してみた事の共有を是非!! GraphAware 、 DataNucleus の使い方
ネットワーク分析 Gephi の使い方
Spark GraphX と Neo4j の関係性– GraphX のデータストアに Neo4j が利用できる?
複雑ネットワーク分析を Neo4j で。– ネットワークの特徴量、類似性、伝播モデルなどなど
40 | May 1, 2023 |
41 | May 1, 2023 |