解决聚会费用问题
引言
这个周六下午的“沙发工作坊”的想法源于一个常见的情况,即一群人为一个聚会各自带来了食物补给。
然后就到了每个人必须为他/她所欠的钱互相支付的时候(A欠B €€€,B欠C €€€,等等)。
参与的人越多,就越有挑战性,有时会导致一些令人困惑的情况。我过去常常使用电子表格来解决这个问题,然后就出现了图数据库和Neo4j。
设置
// 1. Initializing the Database
CREATE
(gaelle:Person {name: "Gaëlle"}),
(gregory:Person {name: "Gregory"}),
(mathilde:Person {name: "Mathilde"}),
(michel:Person {name: "Michel"}),
(yann:Person {name: "Yann"}),
(gaelle)-[:SPENT {amount: 100}]->(gaelle),
(gregory)-[:SPENT {amount: 35}]->(gregory),
(mathilde)-[:SPENT {amount: 67}]->(mathilde),
(michel)-[:SPENT {amount: 0}]->(michel),
(yann)-[:SPENT {amount: 85}]->(yann);
// 2. Adding the Debt Relationship
MATCH (p:Person)
WITH toFloat(count(p)) AS GuestsCount
MATCH (s:Person)
MATCH (t:Person)
MATCH (s)-[r:SPENT]-(s)
WHERE s <> t
AND r.amount <> 0
CREATE (t)-[:OWES_TO {amount: round(r.amount / GuestsCount * 100) / 100}]->(s);
// 3. Simplifying Mutual Debts
MATCH (s)-[r1:OWES_TO]->(t)
MATCH (t)-[r2:OWES_TO]->(s)
WHERE r1.amount - r2.amount > 0
// Create a new merged transaction...
CREATE (s)-[:OWES_TO {amount: round((r1.amount - r2.amount) * 100) / 100}]->(t)
// ...Then delete the previous ones
DELETE r1
DELETE r2;
问题
假设我们共有五个人来参加我们的聚会。他们的费用是
MATCH (n)-[r:SPENT]-(n)
RETURN n.name as Person, r.amount AS Expense
ORDER BY Person;
初始化数据库
以下Cypher查询将创建人员(节点)及其费用(使用自关系)
CREATE
(gaelle:Person {name: "Gaëlle"}),
(gregory:Person {name: "Gregory"}),
(mathilde:Person {name: "Mathilde"}),
(michel:Person {name: "Michel"}),
(yann:Person {name: "Yann"}),
(gaelle)-[:SPENT {amount: 100}]->(gaelle),
(gregory)-[:SPENT {amount: 35}]->(gregory),
(mathilde)-[:SPENT {amount: 67}]->(mathilde),
(michel)-[:SPENT {amount: 0}]->(michel),
(yann)-[:SPENT {amount: 85}]->(yann);
注意:费用可以存储为人的一项属性。之所以更倾向于使用关系,是因为费用表示一个动作(用动词描述),而不是人的一个特征。

MATCH (n)-[r:SPENT]-(m) RETURN *;
添加债务关系
以下Cypher查询将在两个人之间添加一个关系,以指示第一个人欠第二个人的金额。
MATCH (p:Person)
WITH toFloat(count(p)) AS GuestsCount
MATCH (s:Person)
MATCH (t:Person)
MATCH (s)-[r:SPENT]-(s)
WHERE s <> t
AND r.amount <> 0
CREATE (t)-[:OWES_TO {amount: round(r.amount / GuestsCount * 100) / 100}]->(s);

简化相互债务
就像在现实生活中一样,如果两个人互相欠钱,我们将这两笔交易合并为一笔。
这可以通过以下Cypher查询完成
MATCH (s)-[r1:OWES_TO]->(t)
MATCH (t)-[r2:OWES_TO]->(s)
WHERE r1.amount - r2.amount > 0
// Create a new merged transaction...
CREATE (s)-[:OWES_TO {amount: round((r1.amount - r2.amount) * 100) / 100}]->(t)
// ...Then delete the previous ones
DELETE r1
DELETE r2;
输出报告
Set 6 properties, deleted 12 relationships, created 6 relationships.

MATCH (n)-[r]-(m) RETURN *;
获取账单
我们最后的Cypher查询总结了每个人的债务
MATCH ()-[r:OWES_TO]->()
WITH
startNode(r).name AS Debitor,
endNode(r).name AS Creditor,
r.amount AS Amount
RETURN Debitor, Amount, Creditor
ORDER BY Debitor, Amount;
此页面有帮助吗?