Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

RedisGraph 2.8正式リリース!マルチラベルノードや全文検索強化など新機能を徹底解説

本記事では、グラフデータベース「RedisGraph」の最新バージョン2.8が正式リリース(GA:General Availability)されたことをお知らせします。この記事では、新しく利用可能になった主要な新機能について詳しく解説していきます。

RedisGraphとは

RedisGraphは、Redis向けに設計された高性能なメモリファースト型のグラフデータ構造です。グラフのマルチテナンシー(複数のグラフを同時に保持できる)に対応しており、複数のクライアントが同時にグラフへアクセスすることも可能です。現在では、Redis Stackの一部としても提供されています。

RedisGraph 2.8の主な新機能

  • より豊かなグラフモデル
    • マルチラベルノード
  • クエリ機能の強化
    • 全文検索の強化
    • Cypherの構文・関数・演算子のサポート拡充
  • パフォーマンスの向上
    • リレーションシッププロパティへのインデックス対応
    • デルタ行列(Delta Matrices)
    • ノード作成バッファの制御
    • ベンチマーク結果

より豊かなグラフモデル

マルチラベルノード

ラベル付きプロパティグラフ(LPG)データモデルの多くの定義(例:The Property Graph Database Model – Angles, 2018 や ISO/IEC JTC 1/SC 32 – GQLドラフト)では、1つのノードが複数のラベルを持てることが規定されています。しかしv2.8までのRedisGraphは単一ラベルのみしかサポートしていませんでした。今回のリリースにより、パフォーマンスの低下やメモリ使用量の大幅な増加なしに、各ノードへ複数のラベルを付与できるようになりました。

複数ラベルを持つノードを作成するには、コロン区切りでラベルを列挙するだけです。

GRAPH.QUERY g "CREATE (e:Employee:BoardMember {Name: 'Vincent Chan', Title: 'Web marketing lead'}) return e"

また、複数ラベルを持つノードをマッチさせる場合(AND条件)も、同じくコロンの記法を使用します。

GRAPH.QUERY g "MATCH (e:Employee:BoardMember) return e"

クエリ機能の強化

全文検索の強化

RedisGraphにはRediSearchが組み込まれており、セカンダリインデックスとして活用されるだけでなく、高度なインデックス作成や検索にも利用できます。たとえば、地球上の特定地点からの地理的な近接度に基づいてノードを検索したり、関連性の高い項目を上位にスコアリングしたりすることが可能です。

バージョン2.8では、「language」と「stopwords」という設定オプションが追加されました。「language」は、テキストのステミング(語の原形をインデックスに登録する処理)に使用する言語を定義します。これにより、たとえば「going」でクエリを実行した際に、「go」や「gone」の結果も返せるようになります。「stopwords」は「is」「the」「an」「and」のような、検索にほとんど情報を与えない一方でインデックス内のスペースを大きく消費するごく一般的な単語です。これらの単語はインデックスされず、検索時にも無視されます。たとえば「in Paris」というクエリ語句は「Paris」のみとして扱われます。

次の例は、Movieラベルを持つすべてのノードのtitleプロパティに対して、ドイツ語とカスタムストップワードを指定して全文インデックスを作成するクエリです。

GRAPH.QUERY DEMO_GRAPH "CALL db.idx.fulltext.createNodeIndex({ label: 'Movie', language: 'German', stopwords: ['a', 'ab'] }, 'title')"

さらにRediSearchでは、以下の3つのフィールド設定オプションも提供されています。

  • weight – フィールド内テキストの重要度
  • nostem – テキストのインデックス時にステミングをスキップ
  • phonetic – テキストに対する音声検索を有効化

次の例は、Movieラベルを持つすべてのノードのtitleプロパティに対して、音声検索を有効にした全文インデックスを作成するクエリです。

GRAPH.QUERY DEMO_GRAPH "CALL db.idx.fulltext.createNodeIndex('Movie', {field: 'title', phonetic: 'dm:en'})"

Cypherの構文・関数・演算子のサポート拡充

RedisGraph 2.8では、Cypherのサポート範囲が大幅に拡張されました。

  • パターン内包表記(Pattern comprehensions)
  • allShortestPaths 関数のサポート
  • Cypher関数:keysreducereplacenonesingle
  • SET 句でのノード属性セットのコピー
  • WHERE 句でのノードラベルによるフィルタリング
  • Cypher演算子:XOR および ^(べき乗)

パターン内包表記

パターン内包表記はCypherで利用できる構文です。リスト内包表記が既存のリストから新しいリストを作成するのに対し、パターン内包表記はパターンのマッチング結果を使ってリストを生成する手法です。標準の MATCH 句と同様に指定パターンをマッチさせ、WHERE 句と同様に述語を適用しながら、指定した射影を出力します。

たとえば、次のクエリは男性従業員が受け取った助成金のうち、金額が1,000ドルを超えるものすべてのタイプを含むリストを返します。

GRAPH.QUERY g "CREATE (e:Employee {gender:'Male'})-[:granted]->(g:Grant {type: 'Research', amount: 2000})"
GRAPH.QUERY g "MATCH (e:Employee {gender:'Male'}) RETURN [(e)-[:granted]->(g:Grant) WHERE g.amount > 1000 | g.type] AS grantTypes"

allShortestPaths 関数のサポート

allShortestPaths 関数は、すべての条件に合致する2つのエンティティ間の最短パスをすべて返します。両方のエンティティは、直前の WITH で区切られたスコープ内でバインドされている必要があります。

GRAPH.QUERY DEMO_GRAPH "MATCH (c:Actor {name: 'Charlie Sheen'}), (k:Actor {name: 'Kevin Bacon'}) WITH c, k MATCH p = allShortestPaths((c)-[:PLAYED_WITH*]->(k)) RETURN nodes(p) as actors"

このクエリは、Charlie Sheenを表すアクターノードからKevin Baconを表すノードにつながる最小長のパスをすべて出力します。2人の間には複数の2ホップのパスが存在し、そのすべてが返されます。それ以上の長さのパスには興味がないため、パスの計算はそこで終了します。

検索の最小長(1以上である必要があります)と最大長(最低1以上である必要があります)を指定することもできます。リレーションシップタイプは0個以上指定可能です(例:[:R|Q*1..3])。ただし、パターン内にプロパティフィルタを入れることはできません。

keys 関数のサポート

keys 関数は、ノード・リレーションシップ・マップを入力として受け取り、その入力が持つすべてのキーを配列で返します。

MATCH (a) RETURN keys(a)
MATCH ()-[e]->() RETURN keys(e)
RETURN keys({a:1, b:2})

reduce 関数のサポート

reduce 関数は、初期値とリストを受け取ります。そしてリストの各要素に対して式を評価しながら値を更新していきます。

GRAPH.QUERY g "RETURN reduce(sum = 0, n IN range(1,10) | sum + n)"

この関数の出力は55、つまり1から10までの整数の合計となります。

GRAPH.QUERY g "RETURN reduce(arr = [], n IN range(1,10) | arr + [n*n])"

こちらの例では、1から10までの整数の平方を格納した配列が出力されます。

文字列置換 replace 関数のサポート

replace 関数は、指定した部分文字列のすべての出現箇所を別の文字列に置き換えます。引数は3つで、元の文字列、置換対象の出現箇所、置換後の文字列です。

GRAPH.QUERY g "RETURN replace('abc*efg', '*', 'd')"

戻り値は「abcdefg」になります。この関数は、空文字列('')に置き換えることで部分文字列を削除することもできます。

none および single 関数のサポート

リストが与えられたとき、none はどの要素でも述語が成立しない場合にtrueを返し、single は述語がちょうど1つの要素でのみ成立する場合にtrueを返します。

GRAPH.QUERY g "RETURN none(x IN range(1,10) WHERE x>10)"
GRAPH.QUERY g "RETURN single(x IN range(1,10) WHERE x>9)"

これらの関数は、既存の allany 関数と似た働きをします。

考えられるユースケースのひとつは、パスのフィルタリングです。

graph.query DEMO_GRAPH "MATCH p = (a {name:'Johnny Depp'})-[*2..5]->(b {name:'Kevin Bacon'}) WHERE none(n IN nodes(p) WHERE n.year > 1970) RETURN p"

このクエリは、Johnny DeppからKevin Baconへの長さ2〜5のパスのうち、1970年以降生まれのアクターを含まないパスをすべて返します。

SET 句でのノード属性セットのコピー

SET 句を使用すると、あるノードのすべてのプロパティ値を、別のノードのプロパティ値で置き換えたり追記したりできます。

次のクエリは2つのエンティティをマッチさせ、aの全プロパティをbのプロパティで置き換えます。

GRAPH.QUERY g "MATCH (a {v: 1}), (b {v: 2}) SET a = b"

次のクエリは2つのエンティティをマッチさせ、aのプロパティにbのプロパティを追記(または値を上書き)します。

GRAPH.QUERY g "MATCH (a {v: 1}), (b {v: 2}) SET a += b"

さらに、プロパティを変更せずにリレーションシップのタイプだけを変更することも可能です。

GRAPH.QUERY g "MATCH (a)-[b]->(c) WHERE ID(b)=0 CREATE (a)-[d:bar]->(c) SET d=b DELETE b RETURN d"

WHERE 句でのノードラベルによるフィルタリング

WHERE句内でも、ノードラベルやリレーションシップタイプによるフィルタリングが可能になりました。

GRAPH.QUERY g "MATCH (a) WHERE a:L RETURN a"
GRAPH.QUERY g "MATCH (a)-[b]-(c) WHERE b:L RETURN b"

Cypher演算子 XOR と ^ のサポート

GRAPH.QUERY g "RETURN true XOR true"
GRAPH.QUERY g "RETURN 2 ^ 3"

結果はそれぞれ false8 となります。

パフォーマンスの向上

リレーションシッププロパティへのインデックス

ノードについては、以下のコマンドでインデックスを作成できます。

GRAPH.QUERY g "CREATE INDEX FOR (n:GRANTS) ON (n.GrantedBy)"

そして今回のバージョンから、リレーションシップに対してもインデックスを作成できるようになりました。

GRAPH.QUERY g "CREATE INDEX FOR ()-[r:R]-() ON (r.prop)"

次のクエリを例に考えてみましょう。

GRAPH.QUERY g "MATCH (a)-[r:R {prop:5}]-(b) return *"

まず、インデックス作成前の実行プランを見てみます。

redis:6379> GRAPH.EXPLAIN g "MATCH (a)-[r:R {prop:5}]-(b) return *"
1) "Results"
2) " Project"
3) " Filter"
4) " Conditional Traverse | (a)-[r:R]->(b)"
5) " All Node Scan | (a)"

続いて、同じクエリのインデックス作成後の実行プランです。

redis:6379> GRAPH.EXPLAIN g "MATCH (a)-[r:R {prop:5}]-(b) return *"
1) "Results"
2) " Project"
3) " Edge By Index Scan | [r:R]"

フルスキャンからインデックススキャンへ変わっていることが分かります。

デルタ行列(Delta Matrices)

バージョン2.8以降、グラフのノードやリレーションシップの追加・削除が大幅に高速化されました。変更はまず小さなデルタ行列に反映され、その後メインの行列へまとめて一括更新されます。

RedisGraphでは、グラフは隣接行列で表現されます。グラフ内のすべてのノードラベルとすべてのリレーションシップタイプが、それぞれ固有の行列を持ちます。従来は新しいノードがグラフに追加されるたびにすべての行列をリサイズする必要があり、データベースが大きくなるほど時間がかかっていました。

v2.8以降は、新しいノードやリレーションシップの挿入にかかる時間が大幅に短縮され、グラフのサイズに依存しなくなりました。この最適化は、グラフ内の各行列に対して2つのデルタ行列(ノード追加用のD+とノード削除用のD−)を導入することで実現されました。ノードの追加・削除は対応するデルタ行列に反映され、デルタ行列が10,000ノードの閾値に達すると(DELTA_MAX_PENDING_CHANGES 設定パラメータで変更可能)、メイン行列と一回の一括操作で同期され、デルタ行列は空になって同じサイクルが再開されます。

ノード作成バッファの制御

ロード時の新しい設定パラメータ「NODE_CREATION_BUFFER」により、将来のノード作成のために行列内に確保しておくメモリ量を制御できます。たとえば16,384に設定すると、行列の作成時に16,384ノード分の余剰スペースが確保されます。余剰スペースを使い切ると、行列のサイズは16,384ずつ増加します。

この値を小さくするとメモリ消費量は減りますが、行列の再割り当て頻度が高まるためパフォーマンスが低下します。逆に値を大きくすると、書き込み負荷の高いワークロードでパフォーマンスが向上する可能性がある一方、メモリ消費量は増加します。

なお、渡された引数が2のべき乗でなかった場合は、メモリアライメントを改善するために次に大きい2のべき乗の値に丸められます。

ベンチマーク

デルタ行列以外にも、多くのパフォーマンス改善が加えられています。以下ではLDBC SNBベンチマークを使用してこれらの改善を実証します。
LDBC SNB(Linked Data Benchmark Council – Social Network Benchmarks)は、グラフデータベースの実世界の読み取り・書き込みワークロードを比較するための業界標準ベンチマークです。

全体的なデータロード速度は、RedisGraph 2.8で大幅に高速化されています。

  • LDBCスケールファクター1:
    RedisGraph 2.8はRedisGraph 2.4の1.92倍高速
  • LDBCスケールファクター10:
    RedisGraph 2.8はRedisGraph 2.4の2.00倍高速

LDBCクエリ(読み取り・書き込みの両方)も、RedisGraph 2.8では大幅に高速に実行されます。

  • 読み取りクエリ:
    RedisGraph 2.8はRedisGraph 2.4の2.32倍高速
  • 書き込みクエリ:
    RedisGraph 2.8はRedisGraph 2.4の1.09倍高速

さらに、データの復元や同期(RDBおよびAOF)も大幅に高速化されており、状況によっては数桁速くなることもあります。

RedisGraphはRedis Stackの一部に

RedisGraphは現在、Redis Stackの一部として提供されています。macOS、Ubuntu、Redhat向けの最新のRedis Stack Serverバイナリをダウンロードできるほか、Docker、Homebrew、Linux経由でもインストールできます。

RedisInsightでRedisGraphを体験しよう

RedisInsightは開発者向けのビジュアルツールで、RedisまたはRedis Stackを使用した開発中にデータを探索する優れた手段を提供します。

グラフクエリを実行し、その結果をGUI上で直接確認することができます。RedisInsightはRedisGraphのクエリ結果を視覚化できるようになりました。

さらに、RedisInsightにはRedisGraphをインタラクティブに学べるクイックガイドやチュートリアルも収録されています。

RedisGraphの詳細については、redis.ioおよびdeveloper.redis.comをご覧ください。

  1. Pythonでグラフを描く方法!matplotlibによるグラフ作成の基本と応用テクニック

    Pythonでは、matplotlibライブラリを使用することで、簡単にグラフを作成できます。matplotlibには多数のパッケージと関数が用意されており、さまざまな種類のグラフやプロットを生成できます。また、使い方も非常にシンプルです。NumPyなどのPython組み込み関数と組み合わせることで、データ可視化の目的を効率的に達成できます。この記事では、matplotlibで描画できる代表的なグラフの種類とその実装方法を、サンプルコード付きで紹介します。シンプルなグラフの描き方まずは基本的なグラフの描画方法です。ここでは数学関数を使ってX座標とY座標を生成し、その関数をmatplotlibで

  2. RedisTimeSeries 1.6正式リリース!新機能と性能改善を徹底解説

    本日、RedisTimeSeries 1.6の一般提供(GA)開始をお知らせできることを嬉しく思います。この記事では、新たに利用可能になった主要な新機能について詳しく紹介します。 RedisTimeSeriesとは RedisTimeSeriesは、Redis向けの高性能なメモリファースト型の時系列データ構造です。マルチテナンシー(多数の時系列を同時に保持できる)に対応しており、複数のクライアントがこれらの時系列へ同時にアクセスすることも可能です。現在では、Redis Stackの一部としても提供されています。 RedisTimeSeries 1.6の主な新機能 クエリ機能の強化