反抗军金融系统
介绍
我在这里讲一个故事来展示一个日常问题的解决方案。有多少次你与其他反抗军一起进行超空间旅行,与几个绝地武士执行任务,或者一般来说与其他人一起做一些事情,并且需要共同分摊费用?
通常在这种情况下,会有人欠其他人的钱,而另一个人又欠第一个人的钱。
在这里,我展示了共和国如何使用 Neo4j 以优雅的方式解决这个问题的故事。
故事
很久以前,在一个遥远的星系,有四个人一起旅行,以拯救共和国。
他们很勇敢,但超空间的通行费和汽油可不是免费的;他们不富有,也没有一个集中的金融系统,而是一个由他们的储蓄组成的分布式系统。
幸运的是,除了原力之外,他们还有一个 Neo4j 数据库来管理这个分布式金融。
所以有勇敢的绝地女孩蕾伊,悔过的芬恩,历史学家汉·索罗,以及毛茸茸的外星人丘巴卡。
他们的任务是:从 Neo4j 星球获取管理共和国财务的技术,并找到正确使用它的方法。
第一部分很容易,他们用一个 USB 闪存盘到达了那个星球并获得了数据库,但他们必须找到一种正确使用它的方法来解决奥加纳将军的问题。他们决定用他们的返程进行一个小实验。
出发前,他们将自己添加到了数据库中,作为节点,以他们的名字作为属性,并标记为“朋友”。
CREATE (f1:Friend {name:'Rey'})
CREATE (f2:Friend {name:'Finn'})
CREATE (f3:Friend {name:'Han'})
CREATE (f4:Friend {name:'Chewbacca'})
RETURN f1, f2, f3, f4
然后他们需要加满油箱,所以汉·索罗往千年隼号里加了 1000 共和国信用点(从现在开始我们称之为英镑)的汽油,这样其他人每人欠他 250 英镑(1000 / 4 = 250)。
他们决定将 250 英镑表示为“费用”节点,船员与汉·索罗之间的关系是“信用”节点,包含欠债权人(在本例中为汉·索罗)的总金额。每个“信用”节点通过“:CREDIT”关系连接到债权人,并通过“:DEBIT”关系连接到债务人。在 Cypher 中,以汉·索罗为债权人,蕾伊为债务人为例:
MATCH (creditor:Friend {name:'Han'}), (debitor:Friend {name:'Rey'})
MERGE (creditor)-[:CREDIT]->(c:Credit {Creditor: creditor.name, Debitor: debitor.name})-[:DEBIT]->(debitor)
MERGE (c)<-[:EXPENSE]-(exp:Expense {Creditor: creditor.name, Debitor: debitor.name, Amount: 250, Description: 'Fuel Millenium Falcon'})
WITH c
MATCH (c)<-[:EXPENSE]-(exp)
WITH c, SUM(exp.Amount) AS total
SET c.Amount = total;
MATCH (creditor:Friend {name:'Han'}), (debitor:Friend)
WHERE debitor.name IN ['Finn', 'Chewbacca']
MERGE (creditor)-[:CREDIT]->(c:Credit {Creditor: creditor.name, Debitor: debitor.name})-[:DEBIT]->(debitor)
MERGE (c)<-[:EXPENSE]-(exp:Expense {Creditor: creditor.name, Debitor: debitor.name, Amount: 250, Description: 'Fuel Millenium Falcon'})
WITH c
MATCH (c)<-[:EXPENSE]-(exp)
WITH c, SUM(exp.Amount) AS total
SET c.Amount = total;
这对于整个团队来说变成了:
MATCH (n)
RETURN n
加完油后,他们开始了旅程,但过了一段时间,到了午餐时间,所以他们在服务区停了下来,丘巴卡在那里花了 80 英镑买了些夸伦食物。
同样地,他们将费用添加到他们的 Neo4j 数据库中,所以他们每人欠丘巴卡 20 英镑:
在这一点上有一个有趣的点:丘巴卡欠汉·索罗 250 英镑,而汉·索罗欠丘巴卡 20 英镑。这意味着他们的信用是关联的。
MATCH p=(c:Friend {name:'Chewbacca'})-[:CREDIT|DEBIT*2]-(h:Friend {name:'Han'})
RETURN p
他们想描述这种关联,所以他们在同一个人的两个信用之间定义了一个“:CHAIN”关系,这表明在他的收入和支出信用之间存在资金流动。
在本例中,汉·索罗的借方和贷方之间存在“:CHAIN”关系,丘巴卡也是如此:
MATCH (credit:Credit)<-[:CREDIT]-(creditor:Friend {name:'Han'})<-[:DEBIT]-(debit :Credit)
MERGE (credit)<-[chain:CHAIN]-(debit);
MATCH (credit:Credit)<-[:CREDIT]-(creditor:Friend {name:'Chewbacca'})<-[:DEBIT]-(debit :Credit)
MERGE (credit)<-[chain:CHAIN]-(debit);
(这里有一个指向模型清晰图片的链接,不幸的是,我无法链接到 Dropbox 图片 :()
这里开始变得神奇,因为他们发现可以要求 Neo4j 查找信用之间是否存在抵消并进行结算。
MATCH (creditor:Friend {name:'Chewbacca'})-[:CREDIT]->(credit:Credit),
p = (credit)-[:CHAIN*1..]->(credit)
WITH TAIL(NODES(p)) AS ns, credit
WITH ns,
SIZE(ns) AS Chain_length,
[x IN ns | x.Creditor] AS People_involved,
REDUCE(comp = credit.Amount, n in ns | CASE WHEN comp < n.Amount THEN comp ELSE n.Amount END) AS Compensation
WHERE Compensation > 0
FOREACH (credit in ns| SET credit.Amount = credit.Amount - Compensation)
RETURN Chain_length, People_involved, Compensation;
在本例中,丘巴卡和汉·索罗之间存在 20 英镑的抵消,沿着长度为 2 的链条。MATCH
查询从丘巴卡开始,因为他的最后一次信用更新可能引发了一个链条。
到目前为止,这很简单,但让我们继续讲故事,看看这个工具的强大之处。
在他们继续旅程后,他们遇到了流星雨,损坏了护盾。
所以他们去购买了一个机械部件来替换损坏的部件,蕾伊支付了 1200 英镑(每人 300 英镑)。
这使得网络变得更加复杂,因为他们获得了一些额外的抵消。
MATCH (creditor:Friend)-[:CREDIT]->(credit:Credit),
p = (credit)-[:CHAIN*1..]->(credit)
WITH TAIL(NODES(p)) AS ns, credit
WITH ns,
SIZE(ns) AS Chain_length,
[x IN ns | x.Creditor] AS People_involved,
REDUCE(comp = credit.Amount, n in ns | CASE WHEN comp < n.Amount THEN comp ELSE n.Amount END) AS Compensation
WHERE Compensation > 0 AND
ALL(n in People_involved where
1=size([m in People_involved where m=n]))
RETURN Chain_length, People_involved, Compensation
ORDER BY Compensation DESC, Chain_length DESC
现在是棘手的地方:我们得到了具有不同抵消值的不同的链条,但有一些信用节点是共有的。这使得我们无法像对丘巴卡和汉·索罗那样抵消所有内容。我们必须谨慎地一次抵消一条链条。最好的解决方案是抵消最多的,并且具有最长链条长度的解决方案(当抵消值相同时,最好满足最多的人)。
MATCH (creditor:Friend)-[:CREDIT]->(credit:Credit),
p = (credit)-[:CHAIN*1..]->(credit)
WITH TAIL(NODES(p)) AS ns, credit
WITH ns,
SIZE(ns) AS Chain_length,
[x IN ns | x.Creditor] AS People_involved,
REDUCE(comp = credit.Amount, n in ns | CASE WHEN comp < n.Amount THEN comp ELSE n.Amount END) AS Compensation
WHERE Compensation > 0 AND
ALL(n in People_involved where
1=size([m in People_involved where m=n]))
WITH Chain_length, People_involved, Compensation, ns
ORDER BY Compensation DESC, Chain_length DESC
LIMIT 1
FOREACH (credit in ns| SET credit.Amount = credit.Amount - Compensation)
RETURN Chain_length, People_involved, Compensation
继续故事,一旦他们获得了护盾部件,他们就去找了一名机械师,芬恩支付了 600 英镑的修理费用(每人 150 英镑),他们将其添加到系统中。
与此同时,丘巴卡和汉·索罗决定去酒馆喝班莎牛奶鸡尾酒和绝地啤酒,丘巴卡支付了 50 英镑(每人 25 英镑)。
数据库再次发挥作用,找到了一些抵消来平衡信用。
MATCH (creditor:Friend)-[:CREDIT]->(credit:Credit),
p = (credit)-[:CHAIN*1..]->(credit)
WITH TAIL(NODES(p)) AS ns, credit
WITH ns,
SIZE(ns) AS Chain_length,
[x IN ns | x.Creditor] AS People_involved,
REDUCE(comp = credit.Amount, n in ns | CASE WHEN comp < n.Amount THEN comp ELSE n.Amount END) AS Compensation
WHERE Compensation > 0 AND
ALL(n in People_involved where
1=size([m in People_involved where m=n]))
WITH Chain_length, People_involved, Compensation, ns
ORDER BY Compensation DESC, Chain_length DESC
LIMIT 1
FOREACH (credit in ns| SET credit.Amount = credit.Amount - Compensation)
RETURN Chain_length, People_involved, Compensation
多次运行查询后,他们到达了一个所有内容都被抵消的点。
MATCH (creditor:Friend)-[:CREDIT]->(credit:Credit),
p = (credit)-[:CHAIN*1..]->(credit)
WITH TAIL(NODES(p)) AS ns, credit
WITH ns,
SIZE(ns) AS Chain_length,
[x IN ns | x.Creditor] AS People_involved,
REDUCE(comp = credit.Amount, n in ns | CASE WHEN comp < n.Amount THEN comp ELSE n.Amount END) AS Compensation
WHERE Compensation > 0 AND
ALL(n in People_involved where
1=size[m in People_involved where m=n]))
WITH Chain_length, People_involved, Compensation, ns
ORDER BY Compensation DESC, Chain_length DESC
LIMIT 1
FOREACH (credit in ns| SET credit.Amount = credit.Amount - Compensation)
RETURN Chain_length, People_involved, Compensation
MATCH (creditor:Friend)-[:CREDIT]->(credit:Credit),
p = (credit)-[:CHAIN*1..]->(credit)
WITH TAIL(NODES(p)) AS ns, credit
WITH ns,
SIZE(ns) AS Chain_length,
[x IN ns | x.Creditor] AS People_involved,
REDUCE(comp = credit.Amount, n in ns | CASE WHEN comp < n.Amount THEN comp ELSE n.Amount END) AS Compensation
WHERE Compensation > 0 AND
ALL(n in People_involved where
1=size([m in People_involved where m=n]))
WITH Chain_length, People_involved, Compensation, ns
ORDER BY Compensation DESC, Chain_length DESC
LIMIT 1
FOREACH (credit in ns| SET credit.Amount = credit.Amount - Compensation)
RETURN Chain_length, People_involved, Compensation
他们知道这项技术是新革命的开始。这确实可以简化所有反抗军的金融系统,节省大量资金和时间。
他们飞回自己的星球,向奥加纳将军展示了他们的发现。
故事的其余部分众所周知,但直到这一刻,还没有人解释过这些贫穷的反抗军是如何在一个如此稀疏的共和国中管理他们的金钱的。
未来的发展
在我的工作中,我解释了一种补偿链的方法,这种方法有点机械化,但我很有信心有更好的方法。首先,描述一个查询来补偿所有不连通的子网络将非常有趣,此外,找到一种方法来一次性补偿所有内容也很不错。
结论
这是一个有趣的案例,用来解释图数据库真正发挥其优势的用例,它可以描述这样一个内在互连的网络并在数据上执行非常描述性的查询。我的工作只是找到共享信用的子网络问题的一种方法,并且可以作为开发分布式经济系统的一个想法。我真的很感谢 Neo4j,因为它对连接数据描述的方法打开了思维方式,并让我们对信息的真正价值有了更好的见解。
此页面是否有帮助?