概要
やりたいことはある程度可能ですが、クエリはひどく醜く遅くなります。コレクション プロパティの代わりにノードとリレーションシップを使用することをお勧めします。これにより、クエリがより適切になり、フルテキスト インデックスを使用できるようになります。また、クエリをデータベースに送信する前に、探している「入力文字列」の部分を把握する必要があります。現状では、正規表現パターンと一致するはずのデータを混同しています。意図を正規表現として表現できたとしても、クエリを送信する前にアプリケーションを処理する方がはるかに優れています。
1)WHERE ... IN ...
正規表現を行いません
WHERE x IN y
x
は正規表現として扱われず、x
の値をそのまま取り、完全に一致するものを探します。WHERE ... IN ...
はこの意味で に類似しており、これには のようなコレクションWHERE ... = ...
の類似物が必要です。Cypher にはそのような構造はありません。=~
IN~
2)述語を使用してコレクションに対して正規表現を実行できますが、非効率的です
ANY
またはのような述語を使用して、文字列を正規表現として使用し、コレクションに対する一致をテストできますFILTER
。
CREATE (p:Person {collectionProperty:["Paulo","Jean-Paul"]})
と
WITH "(?i).*Paul" as param
MATCH (p:Person)
WHERE ANY(item IN p.collectionProperty WHERE item =~ param)
RETURN p
「Jean-Paul」で正規表現の一致が成功するため、ノードが返されます。
item
ただし、データベース内のすべてのすべてcollectionProperty
に対して正規表現を実行するため、これはひどいパフォーマンスになり:Person
ます。解決策はフルテキスト インデックスを使用することですが、彼のクエリでは次の 2 つの理由からインデックスを使用できません。
- クエリしている値は配列内にあります
- インデックス クエリを実行する代わりに、正規表現を使用して結果をフィルタリングしている
3)あなたの種類の入力では、コレクションに対して正規表現を行うことはできません
クエリの最大の問題は、正規表現シュガーを追加して"I saw Smith today"
変換しようとしていることです。"Smith"
それをどうするつもりですか?文字列を正規表現として使用する場合、これらの各文字は、一致するデータに含まれると予想されるリテラル文字です。.*
で使用すると、データ内の0 個以上の追加文字'Smith.*'
と一致するについて混乱しています。しかし、ゼロ個以上の文字がパターン内の何かに続く可能性があると言うためにそれを使用しようとします.'Smith'
コメントでクエリを実行します。
MATCH (p:Person)
WHERE '(?i).*I saw Smith today.*' IN p.alias
RETURN p
正規表現'(?i).*I saw Smith today.*'
が一致します
- リテラル文字列の大文字と小文字を区別しない – <code>'i SAW smith TOday' など。
- リテラル文字列の前後に 0 個以上の文字を使用 – <code>「はい、今日スミスに会いました。彼は幸せそうに見えました。」
しかし、追加して.*
もどういうわけか魔法のようにパターンが意味のあるものになるわけではありません'.*Smith.*'
。さらに、正規表現の砂糖を追加しても'I saw Smith today'
、サブセットとして表現することはほとんど不可能です。'Alex Smith'
代わりに、クエリを送信する前に、その文字列を処理し、正規表現で使用する部分を把握する必要があります。'Smith'
使用したい入力文字列の一部であることをデータベースはどのように認識しますか? ただし、クエリを送信する前にそれを知っている必要があり、その関連部分のみを含める必要があります。
余談: 機能しない追加された正規表現シュガーの例とその理由
?
パターン内の各文字の後に を挿入して、各文字をオプションにすることができます
RETURN "Smith" =~ "I? ?s?a?w? ?S?m?i?t?h? ?t?o?d?a?y?"
しかし、あなたのパターンはあまりにもルーシーなグージーであり、'I sat today'
and 'sam toy
' のような文字列に一致します。さらに、前に'Alex Smith
を付けない限り 'には一致しません.*
が、それはさらにルーシー グージーであり、どんな文字列にも一致します。
一緒に属する文字をグループに分割し、グループとそれらの間のスペースをオプションにすることができます。
RETURN "Smith" =~ "(I)? ?(saw)? ?(Smith)? ?(today)?"
しかし、これも範囲が広すぎて一致'Alex Smith'
しません.*
。
4) 悪い解決策
私が考えることができる唯一の「解決策」は、文字列を空白で分割し、正規表現シュガーを各単語に連結し、それを述語句の正規表現として比較する恐ろしいクエリです。それは本当に恐ろしいことであり、文字列全体ではなく文字列内の単語に一致させたいことがすでにわかっていることを前提としています。その場合、Cypher ではなく、クエリを送信する前にその処理を行う必要があります。この忌まわしさを見て泣きなさい
WITH "I saw Paul today" AS paramString
MATCH (p:Person)
WHERE ANY (param IN split(paramString, ' ')
WHERE ANY (item IN p.collectionProperty
WHERE item =~('(?i).*' + param)))
RETURN p
5。結論
結論は次のとおりです。
1) モデルを変更します。
このようにエイリアスごとにノードを保持します
CREATE (a:Alias)
SET a.aliasId = "Alex Smith"
これらのノードのフルテキスト インデックスを作成します。一般的なケースについてはブログとドキュメント、SDNについてはドキュメントを参照してください。
コレクション プロパティにエイリアスを持つようになったノードを、新しいエイリアス ノードに関連付けて接続します。
必要なエイリアス ノードを検索し、エイリアスを「持つ」ノードとの関係をたどります。ノードは引き続き多くのエイリアスを持つことができますが、それらをコレクション プロパティに格納する必要はなくなります。クエリ ロジックがより単純になり、全文 lucene インデックスのメリットが得られます。START n=node:indexName("query")
cypher を使用する場合にクエリを実行しfindAllByQuery()
、SDN で使用します。これは、クエリでフルテキスト インデックスを使用するために必要です。
クエリは最終的に次のようになります
START n=node:myIndex("aliasId:*smith")
MATCH n<-[:HAS_ALIAS]-smith
RETURN smith
2) データベースですべての作業を行わないでください。
プログラムが のような文字列を受け取り、'I saw Smith today'
のパターン マッチに基づいてノードを返すことになっている場合は、 andをデータベースに'Smith'
送信しないでください。アプリケーションで文字列の関連部分として識別したほうがよいでしょう。クエリを送信するときには、それが何を求めているかをすでに知っているはずです。'I saw'
'today'
'Smith'