[kintone] 関連レコードを絞り込み検索対象にしたら混乱した話
こんにちは、キン担ラボの本橋です。
ちょっとハマった関連レコードの罠についてメモを残しておきたいと思います。僕自身、お客さんからご相談いただいて「?」となりました。なんだか直感と合わない結果になるのです。
「絞り込む」は関連レコードも条件にできる
kintoneの絞り込み検索では関連レコードも条件に追加できます。そこで関連レコードを対象とした『≠(等しくない)』条件を追加したときに「?」が発生しました。
下の図でいうと、顧客名簿的なアプリ(左アプリ)のレコード一覧を見ているときに、石原さんの関連レコードである支払い記録(左アプリ)を対象とした絞り込みが可能です。
その挙動が直感に反していたことが理由でした。ここからはこのkintoneアプリ模式図を使って説明していきます。
アプリ模式図では、顧客名簿的なアプリの棒人間アイコンひとつで1レコード、支払い記録的なアプリでも書類アイコンひとつが1レコードを表しています。♢を頭に持った線を引いたものは関連レコード関係(今回は顧客IDで関連させています)です。
結論から言うと、関連レコードを対象に絞り込むと、条件は「全ての関連レコードに対する判定の論理和(OR)」として判定されます。
細かく見ていきたいと思います。
関連レコードを対象とした絞り込み
絞り込みダイアログで関連レコードを対象としたときに選べる条件は以下の5つです。
このとき、2つのグループに分けて考えることができます。
「=等しい」と「次のキーワードを含む」
「≠(等しくない)」と「次のキーワードを含まない」
前者は肯定形で、=や∈で表現されます。後者は「ない」という否定を含んだ条件です。後者の否定で事件は起きます。
順番に見ていきます。まずは素直に飲み込める肯定形から。
関連レコードの「=等しい」と「次のキーワードを含む」
kintoneの絞り込み条件で関連レコードに「キーワードを含む」条件を指定すると、関連レコードのいずれかに一つでも含まれていれば親レコードはヒットします。下の図でいうと、中村さんだけが該当します。
ベン図にしてみる
「=等しい」と「次のキーワードを含む」は直感的です。試しに関連レコードの支払方法フィールドに「paypay」を含む名簿を検索すると、下の図でAに該当する名簿を検索することができました。
直感的ですね。続いて否定形を見ていきます。
関連レコードの「≠(等しくない)」と「次のキーワードを含まない」
一方で「含まない」条件を指定した場合、含まないレコードが1件でも関連レコードにあればその親レコードがヒットします。すべての関連レコードに対して「含まない」判定をした結果をORで結合した形です。
下の図でいうと赤い書類アイコンを探して、石原さん、山田さん、中村さんが三人とも該当します。
こうして図示してみるとよく分かりますね。
関連レコードを対象とした条件は、「=(等しい)」「含む」の真逆になるような集合を期待して「≠(等しくない)」「含まない」を使ってしまうと、期待と外れた動作をしてしまうことになります。
ベン図にしてみる
一方で「≠(等しくない)」と「次のキーワードを含まない」がクセモノでした。
「次のキーワードを含まない」という条件は、てっきり「次のキーワードを含む」レコードを全て除去した補集合になるとばかり思っていました。下の図でいうとBの緑の範囲を期待していました。
関連レコードではなく、通常の絞り込みでは「≠(等しくない)」と「キーワードを含まない」この図と同様の挙動になります。
ところが関連レコードを対象とした場合は「関連レコードのいずれかにpaypayを含まないレコードがあるレコード」の集合でした。
グループDとEが増えています。ややこしいですね。
「paypayを含まないレコード」が関連レコードのどこかに存在していれば、そのレコードは絞り込み条件に合致したことになります。
検証してみます
ここまで説明してきた挙動を検証してみたいと思います。みなさんのお手元ではわざわざダミーデータ作ったりしなくとも、手元の環境にそれっぽい修正を加えたら検証できるかと思います。よろしければお付き合いください。
ダミーデータを作る
まず支払記録的なアプリを作って空の状態でcsvをエクスポート。ChatGPTさんにダミーデータを作っていただきます。
ダミーの作り方は過去記事で詳しく紹介しています。
1件だけ書き換える
ダミーデータをインポートしたら、その中から1件だけ支払方法をpaypayに書き換えてこんな集計ができるようにします。
1件だけ発生する仕分けがポイントです。
親側アプリを作って関連レコードを設定する
つぎに、支払いアプリを関連レコードで表示する顧客名簿的なアプリを作ります。
顧客名簿的なアプリには、関連レコードとして支払記録を表示できるようにしておきます。
ここまでで準備完了です。
ではいよいよ検索を実行してみたいと思います。
親側アプリを検索してみる
顧客名簿的なアプリの一覧画面で、絞り込み条件を開いて関連レコードを条件に「=(等しい)」に「paypay」を指定します。
すると、paypayの支払履歴を持つ中村俊介さんがヒットしました。最初のダミーデータ作成手順のときに書き換えた支払記録レコードですね。
次に「≠(等しくない)」に「paypay」を指定します。
ここで中村俊介さん以外の顧客が出てきて欲しいところですが、中村さんを含めた10人全員がヒットします。
これは「paypayと等しくないレコード」が関連レコードとして1件でも該当している顧客は全員ヒットしてしまうためです。もちろん中村俊介さんもヒットしてます。
ここまで説明してきた通りの動作が検証できました。
さきほどの図をもう一度表示します。Bに絞り込みたいのに、B, C, Eがヒットしている形です。
大分長々と説明してしまいました。
「~ない」という否定形の条件を指定した場合は、もしかしたら直感と異なる動きをするかもな、くらいの情報を片隅で覚えておいていただけたらと思います。
顧客が本当に欲しかった条件
顧客が本当に欲しかった条件はこちらです。(「≠(等しくない)」条件です)
import pandas as pd
df_payments = pd.read_csv('支払記録的なアプリのレコード.csv', encoding='ShiftJIS', dtype=str, na_filter=False)
df_customers = pd.read_csv('顧客名簿的なアプリのレコード.csv', encoding='ShiftJIS', dtype=str, na_filter=False)
df_paypay = df_payments[df_payments['支払種別'] == 'paypay']
# df_paypay = df_payments[df_payments['支払種別'].str.contains('paypay')] # キーワードを含む場合はこちら
result = df_customers[df_customers['顧客ID'].isin(df_paypay['顧客ID'])]
# 結果をCSVとして出力
result.to_csv(output_path, encoding='UTF-8', index=False) # type: ignore
print(f'output file: {output_path}')
すみません、kintoneだけでは無理でした…
えーっと、キン担ラボではpythonやTypeScriptを使った複雑な絞り込みのご依頼もお待ちしています!
タイトル画像について
有名なミーム『顧客が本当に欲しかったもの』にベン図を重ねた絵です。