fbpx

婚活支援プロジェクト(3/4): どのように改善したらいいのか #neo4j

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

ここまで2回に渡って「婚活支援」というトライアルプロジェクトを進めながら記事を書いてきましたが、その過程でいくつかの問題点を挙げています。今回は、その問題点をどのように改善したらいいのかを考え、少なくともデータモデルにまでは落として行きたいと思います。

第1回目:構想からデータモデル設計まで
第2回目:データ処理と問題点
第3回目:どのように改善したらいいのか
第4回目:改善したデータモデルによるデータ処理

ここまでの問題点のおさらいとデータモデル改善の方向性

ここでは、前回までのトライアルプロジェクトを進めながら出てきた問題点をおさらいし、改善の方向性を出していきたいと思います。

問題1

婚活をする男女にオリジナルプロファイルとリクエストプロファイルがありますが、リクエストプロファイルはデータモデルに反映されていません。

問題1の改善案

リクエストプロファイルをデータモデルに取り込む

問題2

プロファイル内のいくつかの要素は必須条件になることがあります。例えば身長や収入、住んでる場所などを限定した場合です。

問題2の改善案

この問題もデータモデルに取り込んで解決する方法を探ってみることにしました。

問題3

ある要素は重み付けをして優先度を高くしたいというようなケースがあります。例えば年収の高い相手は優先度を高くする、などのケースが想定できます。

問題3の改善案

この問題には取り組まないことにします。理由は以下のとおりです。
この問題は、基本的に優先度が高い要素を持った候補者の表示ランクを上げたいということが背景にあります。既に優先度が高い要素は必須条件としているような状態なので、今回、さらに定量的な重み付けをすることによる有意差は出ないと考えました。

問題4

人の様々なパーソナリティを表す言葉に重複や曖昧な表現が存在すると、表示ランクに影響があったり、意味が伝わりにくくなります。例えば、「その他」、「どちらでもよい」、「どちらでもない」のような言葉です。

問題4の改善案

この問題は、データ処理の方法とは直接関係しないということで、属性値(要素)の変更までは踏み切らないことにしました。現実の世界であれば、プロファイル作成のための調査資料にまでさかのぼる必要があります。

問題5

ある種類のサブドメインの要素数が非常に多い場合、グループを表す言葉を検討すべきかもしれません。例えば、スポーツの場合は、全種目の名称を並べるより、種目自体(例えば球技など)を要素にしたほうが良いかもしれません。

問題5の改善案

この問題は、データ処理の方法とは直接関係しないということで、属性値(要素)の変更までは踏み切らないことにしました。
ただし、現実のケースでは上記で述べたような「サブドメインのグループ化」は頻繁に行われると思います。実際、サブドメインのグループ化(ネスト化)はグラフデータの利点でもあります。ただし、今回のプロジェクトはあくまで理想の相手を発見するというプロセスに集中するため、付随的な要素は除外するようにしました。

問題6

パターンマッチの結果値のカラム名の特定が困難。

問題6の改善案

この問題は、データ処理とは直接関係なく、処理結果を出力するときの可視化の問題なので、データモデルには反映せず、処理方法を考えてみたいと思います。

前回のデータモデルをシステム化した場合

前回の記事では、リクエストプロファイルや必須条件の問題を直接Cypherクエリーの調整で対応しました。もし、そのままの状態でシステムを開発すると、Neo4jの外部にリクエストプロファイルや必須条件を保存しておいて、クエリーを発行する時にアプリケーションで情報を検索し、Cypherクエリーを生成して実行することになります。
そのようにするためには、外部のデータベースに候補者の個人情報を含めてNeo4jのリクエストや必須条件の要素を格納して置くことが考えられます。この案は、既に外部のデータベースを運用しているようなケースでは、とても身近な選択肢の一つなのかも知れません。但し、Neo4jでシンプルに実現できることに対して安易な選択をすれば、それはシステムを複雑にするなど非効率(コスト増)に繋がりかねません。

今回のトライアルプロジェクトは、基本方針だけを決めて進めています。開始段階で情報収集や大まかな分析のシナリオを作成し、データモデルを検討はしていますが、この段階で問題点として挙がっていることに対する具体的な解決策までは考えていませんでした。いわゆる走りながら考えているという状態です。私は、「Cypherクエリーの論理構成能力は、SQLよりはるかに高いです」と言い続けて来ましたが、今回は、それを立証しなければいけない状況になりました(汗)。以下は私の苦闘の記録と言ってもいいかもしれませんね。

M04さんのリクエストの解決策を考えてみる

問題解決のためのトライ&エラーフロー

Neo4jは、このようなタイプの問題解決のために「トライ&エラー」で最適な解を求めていくことに適しています。なぜかというと、問題をグラフにして、それをNeo4jにロードし、Cypherクエリーで狙い通りのデータが取得できるかどうか試してみるまでのサイクルが、とても短い時間で簡単に実行できるからです。
今回は、M04さんのリクエストに対して1つずつ解決策を考えてみることにしました。ここで試してみたことは、以下のような「問題解決のためのトライ&エラーフロー」です。簡単に言うと、最終的に狙い通りの結果とパフォーマンス、スマートなCypherクエリーという3つのステップがクリアできればパーフェクトな解決策と言えるでしょう。

solution-flow

[問題解決ためのトライ&エラーフロー]

M04さんのケース

ここでM04さんのプロファイルを思い出してみましょう。以下の表です。

カテゴリ オリジナル リクエスト 必須
Alcohol 飲む 飲む
Born 1986 1981-1989
Education 大学 短大以上
Fortunetelling 信じない 信じない
Healthcare 意識して運動する 意識して運動する
Height 190 140-160
Holiday 土日祝日 土日祝日
Income 700 気にしない
Indoor TV感想 映画感想,ゲーム
Job 会社員 無職,学生,家事手伝は駄目
Karaoke 好き 好き
Location 埼玉県 埼玉県,千葉県,東京都,神奈川県
Maritalhistory 無し 無し
Meeting 積極的 積極的
Outdoor 公園 海,川
Personality 外向的 気にしない
Pet 猫派 猫派
Smoking 吸わない 吸わない
Sport バスケットボール 気にしない
Talk 聞くタイプ 話すタイプ

 

これから、M04さんのリクエストプロファイル及び必須条件をもとに、「問題解決フロー」に従って実現可能性を検討してみます。

必須条件ではないリクエスト

アウトドア(Outdoor):海、川
このようなリクエストは、単純にすべての要素をサブグラフにすることにしました。

indoor

性格(Personality):気にしない
「気にしない」というリクエストは「どれでもいい」という意味で捉え、"性格"に含まれるすべての属性(ここでは”内向的”と”外向的”)をサブグラフにします。

personality

必須条件が存在するリクエスト

教育(Education):短大・高専以上(必須)
このようなリクエストは、まずリクエストとして挙がっている要素のみをサブグラフにします。このようなサブグラフが存在すると、M04さんが相手に求める教育水準に対するリクエストは、["短大・高専", "大学", "大学院"]のようなパターンとして簡単に把握することができます。

education1

ここで、M04さんの場合、教育を必須条件として範囲を決めているので、対象外の学歴を持つ候補者は、除外する必要があります。そのフィルター条件をサブグラフにすると、以下のようになります。これは後に、["義務教育", "高等学校", "各種専門学校"]のようなパターンとして利用することができます。

education2

なぜ、上記のような2つのパターンが必要なのでしょうか。実際にシミュレーションしてみます。例えば、M04さんのリクエスト[(...略,)"野球","サッカー","バレーボール","バスケットボール","テーブルテニス","ヨガ","散歩","ダンス","その他", "短大・高専", "大学", "大学院"]に対して◎◎さんのオリジナルプロファイル[(...略,)"水泳", "義務教育"]をフィルターすると、教育の要素はマッチしなくても、他にマッチする要素が存在するためにマッチする要素と共に「◎◎さん」も残ることになります。これは望んでいない結果です。

・・・
filter ( x IN ["水泳", "短大・高専", "大学", "大学院"] WHERE x IN ["水泳", "義務教育"]) AS result
RETURN woman.name, result
◎◎さん["水泳"]

今回の処理で望んでいる結果は、M04さんの教育に対するリクエストと一致する要素が存在しない場合、「◎◎さん」を候補者から除外することです。
このような処理は、以下のようにフィルター条件を付けると、「◎◎さん」のオリジナルプロファイル(list)のなかから、必須要素のフィルター条件が1個でも含まれている場合、「◎◎さん」が除外されます。

・・・
WHERE NONE ( x IN list WHERE x IN ["義務教育", "高等学校", "各種専門学校"] )
・・・

次のCypherクエリーは、ここまでの説明をフルに反映した例です。

WITH ["飲む",1981,1982,1983,1984,1985,1986,1987,1989,"短大・高専","大学","大学院","信じない","意識して運動する",140,150,160,"土日祝日",100,200,300,400,500,600,700,800,900,"映画感想","ゲーム","公務員","会社員","自営業","企業経営・役員","教師","医師","医療関係","保育士","農林・漁業","パート・アルバイト","学生","家事手伝","好き","埼玉県","千葉県","東京都","神奈川県","無し","積極的","海","川","外向的","内向的","猫派","吸わない","野球","サッカー","バレーボール","バスケットボール","テーブルテニス","ヨガ","散歩","ダンス","話すタイプ"] AS list
MATCH (woman:Person:Woman)-[*]->(things)
WHERE NOT woman-[:DISAGREE|SUCCESS]-()
WITH woman, COLLECT(things.title) AS wlist, list
WITH woman, filter( x IN list WHERE x IN wlist) AS shared, [1995,1994,1993,1992,1991,1990,1980,1979,1978,1977,1976,1975,1974,1973,1972,1971,1970,170,180,190,200,"dummy"] AS list2
WHERE NONE ( x IN shared WHERE x IN list2)
RETURN woman.name AS 女性, shared AS 共有, 1.0 * length(shared) / 20 AS 類似性
ORDER BY 類似性 DESC
LIMIT 10

 

私が書いた、『Cypherクエリー言語の事例で学ぶ グラフデータベースNeo4j』には、上記のようなパターン処理に対する詳細な解説を入れています。ご興味がありましたらあわせてこちらもご参照ください。
出版社URL: http://www.impressrd.jp/news/151029/NP

結果値からカラム名特定が困難な問題

前項の「ここまでの問題点のおさらいとデータモデル改善の方向性」を参照して頂きたいのですが、「パターンマッチの結果値のカラム名の特定が困難」という問題点がありました。
以下のリストを見て下さい。結果値の要素の数が多い場合や曖昧な言葉が存在する場合、意味が正しく伝わらない可能性があります。次のリストの「とちらでも良い」という要素は、「会話のタイプ」であり、「信じる」という要素は「占い」に関することです。

W24 [1982, 大学, 意識して運動する, 140, 600, ゲーム, 自営業, 好き, 埼玉県, 無し, 積極的, 海, 外向的, 猫派, 吸わない, バレーボール] 0.8

W24 [平日, とちらでも良い, 飲まない, 信じる] 4

ここでは、2つのことを考えてみました。

案1
以下のように要素の内容でタイトルが要らないようにすることです。しかし、今回の場合は、身長や収入などの数字が含まれているので、混乱を完全に避けることは難しいように見えます。例のように140という数字が登場した場合、身長なのか収入なのか混乱を招く可能性が高いです。

[休日が平日, 会話は聞くタイプ話タイプのとちらでも良い,酒は 飲まない,占いを信じる, 140]

案2
もう一つは、タイトル用のサブドメインをデータモデルに反映することです。タイトル出力専用のサブドメインが存在し、すべての要素とタイトルがノードの属性として存在している場合、Cypherクエリーを利用してタイトルを逆引きすることが可能です。以下のようなCypherクエリーを使います。リストの要素をパターンマッチの値(title: x)として渡すことができます。

WITH ["義務教育","高等学校","各種専門学校","短大・高専","大学","大学院"] AS list UNWIND list AS x
MATCH (a:Education { title: x })
RETURN labels(a), a.title
[Education] 義務教育
[Education] 高等学校
[Education] 各種専門学校
[Education] 短大・高専
[Education] 大学
[Education] 大学院

その他の解決策

必須要素のフィルター条件を人のノードの属性に持たせる方法も考えられます。まず、簡単な例で考え方を説明します。
以下のように、前項の["義務教育", "高等学校", "各種専門学校"]のような必須要素のフィルター条件を人の属性として格納しておいて、Cypherクエリーの条件として使う方法です。
次のように必須要素のフィルター条件を属性として格納します。

MERGE ( man:Man { name: "M04"})
ON MATCH SET man.filter_education="義務教育|高等学校|各種専門学校"

この要素は、以下のようにリストに変換し、WITHやWHERE、RETURNで利用することができます。

MATCH ( man:Man { name: "M04"})
RETURN split (man.filter_education, "|") AS list1
[義務教育, 高等学校, 各種専門学校]

このリストは、必須要素のフィルター条件として利用することができます。
・・・
WHERE NONE ( x IN list WHERE x IN list1 )
・・・

しかし、この方式には落とし穴があります。あらかじめ、どのサブドメインが必須なのかを知っている必要があります。事前に知らない場合、どの属性が対象になるか分からないためにCypherクエリーで無条件に20種の属性を並べて処理することになります。

MATCH ( man:Man { name: "M04"})
WITH split(man.filter_education, "|") AS list1, split(man.filter_born, "|") AS list2・・・
・・・

さらに、20種のすべてのリストをWHEREに並べてフィルターを行うことになります。これはやりたくないですね。

・・・
WHERE NONE ( x IN shared WHERE x IN list1 ) OR NONE ( x IN shared WHERE x IN list2 )・・・
・・・

但し、居住地域のようにあらかじめ必須条件やフィルター条件がかならず発生すると事前に分かっているサブドメインについて適用するのはどうでしょうか。
都道府県のようなフィルター条件をグラフにした場合、圧倒的にフィルター条件のパターンのほうが多くなります。
次は、今回のM04さんのリクエストで居住地域の必須要素のサブグラフです。

location1

次は、必須要素のフィルター条件のグラフです。

location2

今回は、特別に居住地域のフィルター条件だけは、人の属性として持たせてみることにします。

データモデルの変更

前項の「M04さんのリクエストの解決策を考えてみる」でデータモデルを改善するための様々な方策を検討しました。ここでは、検討した内容に基づいてデータモデルの変更に入ります。

コンセプトデータモデル

今回のデータモデルの変更は、既存データモデルにリクエストプロファイルのために20種(Category2)のサブドメインを追加し、さらに必須要素のフィルター条件のために19種(Category3)のサブドメインを追加することにしました。そして居住地域のみは、人の属性として持たせています。

re-datamodel1

MY:Original RE:Request FI:Filter

ディテールデータモデル

以下は、上記のコンセプトデータモデルに基づいてそれぞれのサブドメインを並べたディテールデータモデルです。最初から、こちらを書いても大差がないのではないかと思う方がいらっしゃるかも知れません。しかし、今回の場合は、実際に進めてみると、コンセプトデータモデルでサブドメインをどのようにするか仮説を立て、簡単なプロトタイプを作って実現の可能性を検討してから、ディテールデータモデルを確定することが出来ました。

re-datamodel2

以下は、データモデルに基づいたドメインモデルの詳細です。これで、今回のグラフのラベル名、タイプ、関係性などをすべて確定しました。

Woman-[:RE_SPORT]->Sport2
Woman-[:RE_HOLIDAY]->holiday2
Woman-[:RE_TALK]->Talk2
Woman-[:RE_HEIGHT]->Height2
Woman-[:RE_PERSONALITY]->Personality2
Woman-[:RE_INDOOR]->Indoor2
Woman-[:RE_SMOKING]->Smoking2
Woman-[:RE_ALCOHOL]->Alcohol2
Woman-[:RE_KARAOKE]->Karaoke2
Woman-[:RE_FORTUNETELLING]->Fortunetelling2
Woman-[:RE_OUTDOOR]->Outdoor2
Woman-[:RE_INCOME]->Income2
Woman-[:RE_BORN]->Born2
Woman-[:RE_LOCATION]->Location2
Woman-[:RE_PET]->Pet2
Woman-[:RE_EDUCATION]->Education2
Woman-[:RE_JOB]->Job2
Woman-[:RE_MEET]->Meet2
Woman-[:RE_MARRIAGE]->Marriage2
Woman-[:RE_HEALTHCARE]->Healthcare2
Man-[:RE_SPORT]->Sport2
Man-[:RE_HOLIDAY]->Holiday2
Man-[:RE_TALK]->Talk2
Man-[:RE_HEIGHT]->Height2
Man-[:RE_PERSONALITY]->Personality2
Man-[:RE_INDOOR]->Indoor2
Man-[:RE_SMOKING]->Smoking2
Man-[:RE_ALCOHOL]->Slcohol2
Man-[:RE_KARAOKE]->Karaoke2
Man-[:RE_FORTUNETELLING]->Fortunetelling2
Man-[:RE_OUTDOOR]->outdoor2
Man-[:RE_INCOME]->Income2
Man-[:RE_BORN]->Born2
Man-[:RE_LOCATION]->Location2
Man-[:RE_PET]->Pet2
Man-[:RE_EDUCATION]->Education2
Man-[:RE_JOB]->Job2
Man-[:RE_MEET]->Meet2
Man-[:RE_MARRIAGE]->Marriage2
Man-[:RE_HEALTHCARE]->Healthcare2
Woman-[:FI_SPORT]->Sport3
Woman-[:FI_HOLIDAY]->holiday3
Woman-[:FI_TALK]->Talk3
Woman-[:FI_HEIGHT]->Height3
Woman-[:FI_PERSONALITY]->Personality3
Woman-[:FI_INDOOR]->Indoor3
Woman-[:FI_SMOKING]->Smoking3
Woman-[:FI_ALCOHOL]->Alcohol3
Woman-[:FI_KARAOKE]->Karaoke3
Woman-[:FI_FORTUNETELLING]->Fortunetelling3
Woman-[:FI_OUTDOOR]->Outdoor3
Woman-[:FI_INCOME]->Income3
Woman-[:FI_BORN]->Born3
Woman-[:FI_PET]->Pet3
Woman-[:FI_EDUCATION]->Education3
Woman-[:FI_JOB]->Job3
Woman-[:FI_MEET]->Meet3
Woman-[:FI_MARRIAGE]->Marriage3
Woman-[:FI_HEALTHCARE]->Healthcare3
Man-[:FI_SPORT]->Sport3
Man-[:FI_HOLIDAY]->Holiday3
Man-[:FI_TALK]->Talk3
Man-[:FI_HEIGHT]->Height3
Man-[:FI_PERSONALITY]->Personality3
Man-[:FI_INDOOR]->Indoor3
Man-[:FI_SMOKING]->Smoking3
Man-[:FI_ALCOHOL]->Slcohol3
Man-[:FI_KARAOKE]->Karaoke3
Man-[:FI_FORTUNETELLING]->Fortunetelling3
Man-[:FI_OUTDOOR]->outdoor3
Man-[:FI_INCOME]->Income3
Man-[:FI_BORN]->Born3
Man-[:FI_PET]->Pet3
Man-[:FI_EDUCATION]->Education3
Man-[:FI_JOB]->Job3
Man-[:FI_MEET]->Meet3
Man-[:FI_MARRIAGE]->Marriage3
Man-[:FI_HEALTHCARE]->Healthcare3

 

まとめ

ここまでで、婚活という特殊な状況での複数の種類のプロファイルや必須要素、必須要素のフィルター条件のグラフ化などの問題点をすべて盛り込んだデータモデル設計を完了しました。次回では、改善したデータモデルで再度M04さんの婚活候補者を探してみたいと思います。次回最終回です。お楽しみに!

Author

モダンアーキテクチャー基盤のソリューションアーキテクトとして活動しています。

[著書]
・Amazon Cloudテクニカルガイド―EC2/S3からVPCまで徹底解析
・Amazon Elastic MapReduceテクニカルガイド ―クラウド型Hadoopで実現する大規模分散処理
・Cypherクエリー言語の事例で学ぶグラフデータベースNeo4j
・Neo4jを使うグラフ型データベース入門(共著)
・RDB技術者のためのNoSQLガイド(共著)

leeの記事一覧

新規CTA