Neo4j + Rubyでグラフデータベース探索(Neo4j v3.1.2 + Ruby)
久しぶりにグラフデータベースをいじったのでメモ。
Neo4j・・・NoSQLに分類されるグラフデータベース。
公式ページ:Neo4j, the world's leading graph database - Neo4j Graph Database
少し前になりますが、パナマ文書の解析にも使用されたと紹介されていました。
こちらの日本語記事がとても興味深いです:「パナマ文書」解析の技術的側面 – Keiichiro Ono – Medium
グラフデータベースなので、人物の相関図(Twitterのフォロー・フォロワー関係のような)や、路線図を格納・検索するのには適していますが、テーブル構造のデータには当然リレーショナルデータベースを使うべきなので、「これはNeo4jを使おう!」という場面は私のお仕事の範囲ではまだそれほど多くありません。また、データ自体がグラフ構造でも検索目的によってはRDBが適している、という場合もあります。
ということで、久しぶりになってしまったので、最低限のデータロードと探索方法のメモです。
Neo4jの起動
Neo4jはメジャーバージョンが3になっていました。
個人で非商用に使うぶんには無料ですので、https://neo4j.com/download/ からダウンロードしてインストールします。
バージョン2系のときはコマンドラインから起動していたのですが、3からは簡単なGUIアプリケーションが付属しています。
これを起動してサーバをスタートします。
サーバが起動していれば、ブラウザから http://localhost:7474 にアクセスするとコンソールが表示されます。
パスワードを聞かれますが、初期設定は「neo4j」です。ログインするとまず「パスワード」を変更するようにうながされますので、変更します。
(そのまま「neo4j」にするとエラーになるので、何か別のものにする必要があります。)
Rubyから操作する
RubyからNeo4jを操作するためのドライバは、Neo4jの公式ページで紹介されているものでは、
- neography
- neo4j-core
- neo4j (これはRubyのドライバとしての名前で、データベース本体の「Neo4j」とは別物)
の3つがあります。
neographyは何年も前からあり、私も以前使用していたのですが、GitHubを見ると最近はリリース・開発ともにあまり活発ではないようです。(ある程度完成している、という面もあるのかもしれませんが。)
neo4jも以前からありますが、こちらはRubyOnRailsやActiveRecordと合わせての利用向けという印象で、「スクリプトを書いて探索したいだけ」の用途にはオーバースペックに思えて使用したことがありませんでした。
そういう人が他にもいたのかどうか分かりませんが、neo4jからローレベルAPIだけを取り出したneo4j-coreというものができていましたので、今回はこちらを使ってみます(もしかしたらだいぶ以前から存在していたのかもしれませんが・・・)。
GitHub:https://github.com/neo4jrb/neo4j-core
Gem:https://rubygems.org/gems/neo4j-core/
ドライバ本体は、
gem install neo4j-core
でインストールできます。
ここから先のサンプルコードは全部まとめてGitHubに置きました。
GitHub - yuw27b/neo4j_ruby_sample: Load and search data on Neo4j with Ruby driver
サンプルデータ:都道府県の隣接関係
サンプル用にこんな感じのデータを用意してみました(*)。くっついている都道府県のリストです。接続方法が橋かどうか、という情報を持っています。
#pref1 | #pref2 | #bridge |
---|---|---|
北海道 | 青森県 | 1 |
岩手県 | 青森県 | |
秋田県 | 青森県 | |
宮城県 | 岩手県 | |
つづく・・・ |
グラフのロード
Neo4j本体にはCSVロード用のコマンドラインツールも付属していますが、わざわざインポート用CSVの成形をするくらいなら、Rubyでデータをパースしながらneo4j-core経由でロードしていくほうがやりやすく感じます。
(手元のCSVファイルがneo4jのツールでそのまま使えるフォーマットであれば別ですが、私の経験上そういうケースはあまりないので。)
ロード用スクリプト:
https://github.com/yuw27b/neo4j_ruby_sample/blob/master/load.rb
ロードしたらコンソール(http://localhost:7474)で確認してみます。
※コンソールの左側メニューにあるボタンは、25ノードor25エッジまで表示します。あまり大きなネットワークをプレビューしようとすると、ブラウザが重くなってくるので、自分でCypher queryを実行する場合も100くらいでLIMITをかけておいたほうが無難です。
グラフの探索
Cypher Queryの一例です。
新潟県のお隣のお隣
MATCH (a{name: '新潟県'})-[*2]->(b) RETURN DISTINCT b
橋でつながっているところ
MATCH (a)-[:Adjoin{connection: 'bridge'}]->(b) RETURN a, b
新潟県から神奈川県までの経路10個まで。ただし、経由する都道府県は9まで。
MATCH p = (a{name: '新潟県'})-[*..10]->(b{name: '神奈川県'}) RETURN p LIMIT 10
※グラフなので、経路探索の場合は容易に組み合わせ爆発が起こります。経路の長さやマッチさせる数に制限をかけないといつまで経ってもレスポンスが帰ってきません。
新潟県から神奈川県までを最短経路で
この場合は、距離を無視しているので、経由する都道府県が最も少ない、という検索になります。また、同様に組み合わせ爆発が起きる可能性があるので、経由数の上限もつけてあります。
MATCH p=shortestPath((a:Pref {name: '新潟県'})-[*0..10]-(b:Pref {name: '神奈川県'})) RETURN p
2つ以上ある場合に全部取るなら、
MATCH p=allShortestPaths((a:Pref {name: '新潟県'})-[*0..10]-(b:Pref {name: '神奈川県'})) RETURN p
ドライバ経由の探索
ドライバ(neo4j-core)経由の場合は、
そのままCypher Queryを投げるやり方
Neo4j::Session.query("MATCH (a{name: '新潟県'})-[*2]->(b) RETURN DISTINCT b.name")
と、
Cypher DSL APIを利用して、もう少し読みやすい(?)感じで書くやり方
Neo4j::Session.query.match('(a)-[*2]->(b)').where(a: {name: '新潟県'}).pluck('DISTINCT b.name')
があります。
個人的には別にDSLじゃなくてもいいかなあ、と思っていますが・・・。
簡単なコード例はこちらにあります:
GitHub - yuw27b/neo4j_ruby_sample: Load and search data on Neo4j with Ruby driver
以下、公式サイトなどのリンクです。
こういうのはできるのかな?どうやるのかな?というときはこのあたりを調べるとだいたいあるような気がします。
公式ページ https://neo4j.com/
Cypher Queryのドキュメント https://neo4j.com/developer/cypher/
Cypher Queryチートシート https://neo4j.com/docs/cypher-refcard/current/
また、Rubyのドライバ(neo4j-core)に関してはGitHubリポジトリにドキュメントがあります。
Home · neo4jrb/neo4j-core Wiki · GitHub
※データはこちらを参考にさせてもらいました:http://uub.jp/prf/rinsetsu.html
NDS第51回勉強会に行ってきたよ
第51回勉強会(2017/03/25) - 長岡 IT開発者 勉強会(NDS) に行ってきました。
50回は行けなかったので、半年ぶりのNDS、半年ぶりの長岡でした。
NDSは名称に「IT開発者」とあるとおり、幅広い分野の開発者さんが参加しているところが好きです。
私はWeb周りをやっているので、普段SIerさんとお話しする機会はなかなかないですが、NDSでそういった方の発表を聞くと勉強になります。
また、今回は「データに関わるなにか」がテーマということで、グラフ構造データについて発表させてもらいました。
スライドはこれです:
yuw27b.github.io
という、それぞれ独立したものを詰め合わせたのですが、「独立したもの」ってちゃんと伝わったかどうかがちょっと不安です(もうちょっと強調してしゃべるべきだったかな、と。)
そもそもは、Neo4jやSPARQLを使ったことある人はあんまりいないんじゃないかな、と思って題材に選びました。
質問していただけたり、知らなかったよ、と言ってもらったりしたので、その点は良かったです。
今すぐ業務に使う、ということはあまりないかもしれませんが、こんなものもあるんだな、と思っていただけてたらうれしいな。
以下、みなさんの発表を聞きながらのメモ・感想など、備忘録:
Vue.jsから始めるDOMにデータを持たせないアプリケーション開発への第1歩 nemuzukaさん
サーバからJSON返してもらうような業務用Webアプリケーションを作っているとのこと。
jQueryやテンプレートエンジンでは辛くなってきたが、業務アプリでReactとかAngularを覚えるのもちょっと、ということでVue.jsを採用したそう。
HTMLのクラスがJS用かCSS用かわからなくなるなど、DOMにデータを持たせるのは限界だった、とおっしゃってましたが、非常に共感できました。
ちょうどVue.jsやってみたいな、というところだったので、タイムリーな発表でした!
RDBMS のトランザクション分離レベルやレプリケーションとか。あと苦労話。 bell-miyaさん
なんとなくサーバサイドもやっている(けどフロントメイン)な私が一番分かってない部分な気がしていて、楽しみにしていた発表でした。で、やっぱり私分かってないな、と。
分離レベルやSQL標準など、まだ理解しきれませんでしたが、勉強していきたいです。
入門系の本を一冊も読まずにデータサイエンスの世界に足を突っ込んでみる sakapunさん
チートシートがわかりやすかったです!やはりこの分野はPythonなんですね。
職場に機械学習をやっている人たちがいて、彼らはChainerを使っているようでしたが、今はいっぱいあるんだな、と。
あと「そろそろ本を読む」とのことでした。
PostgreSQL データ型 civicさん
「数値・文字列・日付ぐらいしか使ってない」って私のとこですね・・・。
PostgreSQLはデータ型が多いと思ったが、現在はそれほど差がなかった、というのが興味深かったです。
Python SQL Alchemyが便利とか。
排他制約とか、そもそも適切な型を指定するとか、もっとちゃんとやるべきだな、と思いました。
JavaScriptでクリップボードにコピー(Chrome/Firefox/Safari10〜/IE9〜)
※2022/4/19追記:ここで使っている仕様は古くなり、非推奨になっているため、新しい記事を書きました。そちらを参照してください。
「クリップボードにコピー」はJavaScriptのみでは不可能だと、昔に思い込んだままだったのですが、HTMLの各種APIとJavaScriptを組み合わせることで、ほとんどのブラウザで実現できるようになってきたようです。
HTMLとJavaScriptのコード
<p class="copyTarget">コピーしてもらいたいテキスト</p> <button class="copyBtn">クリップボードにコピーする</button>
function copyText () { var $target = document.querySelector('.copyTarget'); if (!$target) { return false; } var range = document.createRange(); range.selectNode($target); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); document.execCommand('copy'); return false; } document.querySelector('.copyBtn').addEventListener('click', copyText, false);
使用するAPIとブラウザ対応
以下のAPIを使っています。
API | 仕様 | 対応ブラウザ |
---|---|---|
Range API | DOM Level2 + Living standard | IE9〜、Edge、Chrome、Firefox、Safari |
Selection API > getSelection() | W3C 草案 | IE、Edge、Chrome、Firefox、Safari、 Android browser4.3〜 *ただし、window.getSelection()のサポートに限って確認。 Selection APIの全てをサポートしていないブラウザもあります。 |
Editing API > execCommand('copy') | W3C 勧告改定案 | IE9〜、Edge、Chrome、Firefox、Safari10〜 これも仕様の全てをサポートしているブラウザは少ないですが、「copy」の実行についてのみ確認。 |
Safariがバージョン10以降なのがちょっとネックではありますが(2017年2月現在)、限られたユーザー層向けのWebサイトでは実際に使用したケースでは特に不具合の報告はありません。
ブラウザによっては、一旦「クリップボードにアクセスしようとしています」というような警告ダイアログが表示されるので、導入の際にはそのあたりも検討が必要になりそうです。
CSSの擬似要素を使ったあれこれ
この記事は、今年お世話になったCSS Advent Calendar 2016の20日目です。
擬似要素自体は特に今年どうこうなった仕様ではありませんが、大変お世話になりました。
擬似要素をよく使う場面
自分的頻出パターンとしてはこんな感じです:
「:」なの?「::」なの?
擬似要素は、CSS 2.1の仕様ではコロン1つの「:before」「:after」でしたが、CSS3ではコロン2つになりました。
CSS3はモダンブラウザとIE9以上が対応しているので、IE8のサポートが切れた今年(2016年)からは「::before」「::after」を使用すれば良いと思います。
とはいえ、IE8でも・・・という案件がなぜかまだあったり、各ブラウザも後方互換のため「:before」でもきちんと解釈してくれるし、という状況で、結局コロン1つで書くことも多かった1年でした。
Safariのバグに出会った
擬似要素とcurrentColorを組み合わせたときに発生します。Safari9で修正されたようです。
親要素のcurrentColorがうまく継承されない、というものです。
css - currentColor seems to get "stuck" in Safari - Stack Overflow
Bug 133420 – `currentColor` computes to the same colour on all elements, even if 'color' is inherited differently
ターゲットブラウザにSafari8が含まれそうなら、擬似要素内でのcurrentColorの使用は避けたほうが良いと思います。
来年の抱負は、Flexible Boxにお世話になりたい!です。
未だにIE9〜対応の案件もあるので難しそうですが、スマートフォン専用サイトならそろそろ使えそうですね。
SVGをReact.js/JSXで使う
この記事はSVG Advent Calendar 2016の19日目です。
去年に引き続きグラフを描いての参加です。
React × SVG
React.jsなプロジェクトでSVGを描画したときのメモです。
React.jsでは、JSXという記法でJavaScriptコード内にHTMLタグを記述していきますが、SVGタグも記述できます。
React.jsの特徴は仮想DOMですが、SVGもSVG DOMというインターフェースを持っているので、HTML同様にサポートされているのですね。
Attributeはキャメルケースを基本に
まずタグ内の属性名については、基本的にハイフンがキャメルケースになります。ネームスペースを表すコロンも同様です。
通常のSVG
<svg version="1.1" width="200" height="200" xmlns="http://www.w3.org/2000/svg"> <rect width="100" height="100" x="10" y="10" fill="#900" stroke="#666" stroke-width="5" /> <a xlink:href="http://blog.hatena.ne.jp/yuw27b/"> <text x="20" y="40" fill="#FFF" text-anchor="start" font-size="20">Text</text> </a> </svg>
JSX内のSVG
<svg version="1.1" width="200" height="200" xmlns="http://www.w3.org/2000/svg"> <rect width="100" height="100" x="10" y="10" fill="#900" stroke="#666" strokeWidth="5" /> <a xlinkHref="http://blog.hatena.ne.jp/yuw27b/"> <text x="20" y="40" fill="#FFF" textAnchor="start" fontSize="20">Text</text> </a> </svg>
JSXはあくまでもJavaScriptの中なので、ハイフンやコロンが使えないからだと思われます。
サポートされている属性名のリストは以下の公式ページの最下部にあります。
https://facebook.github.io/react/docs/dom-elements.html
上記のリストには含まれていないのですが(HTMLと同じだから?)、「class」も「className」になります。
ブラウザにReactのDeveloper Toolsを入れておけば、使用できない属性名についてはコンソールに警告が出るので、まずはそのままSVGを書いてしまって、エラーが出たら都度修正、でもいいと思います。
または、自動的に変換してくれるnpmパッケージと、それを元にしたオンラインサービスもあります。
svg-to-jsx
SVG to JSX Online Converter
ただし、若干バグがあるような感じなので、全部お任せはちょっと難しいかもしれません。