图 DB 与 Prolog(或 miniKanren)

A graph DB vs a Prolog (or miniKanren)

最近我一直在研究 Neo4j 等图形数据库以及 Prolog 和 miniKanren 中的逻辑编程。从我目前所学的情况来看,两者都允许指定事实和它们之间的关系,还可以查询生成的系统以进行某些选择。所以,实际上我看不出它们之间有什么区别,因为它们都可以用来构建图形并查询它,但是使用不同的语法。然而,它们呈现为完全不同类型的软件。

除了数据库可能提出更 space 时间有效的存储技术的技术性,以及像 miniKanren 这样的微型逻辑核心更简单和可嵌入之外,图形数据库和逻辑编程语言之间的实际区别是什么, 如果它们都只是一个图形数据库 + 查询 API?

不,那些东西体现的逻辑编程和neo4j完全不同。

在一个层面上,你是对的,它们在概念上都相当于图形存储和图形查询。但是对于逻辑编程来说,它只是概念上的图查询,不能保证它实际上是这样存储的(对于neo4j,它是)。

其次,通过逻辑编程,您通常会尝试建立 horn clauses 以允许您通过大量数据进行推理。您可以将 horn 子句视为一个简单的规则,例如 "If a person is male, and is the direct ancestor of a biological child, that implies that person is a father"。在带有 neo4j 的 cypher 中,您将描述一个您希望匹配的图形模式,从而产生数据,例如:

 MATCH (p:Person)-[:father*]->(maleAncestor:Person)
 RETURN maleAncestor

这告诉我们通过 father 关系和 return 男性祖先来遍历图表。在逻辑编程语言中,您不会这样做。您可以指定 ab 的父亲意味着 a 是男性,而 ab 的祖先。对于所有有效的 a/b 配对,这将隐含地和传递地说明这一点。然后你会问一个问题,"who are the male ancestors"?然后编程环境将通过利用您的规则来回答这个问题。这将产生通过与我上面指定的密码非常相似的数据构建遍历的效果,但是你理解数据和构建遍历的方式完全不同。

逻辑编程语言通常通过 predicate resolution 工作。像 cypher 这样的图形查询语言通过模式匹配和显式路径指定的组合来工作。他们很不一样。

图形数据库和逻辑编程之间有一些非常酷的相似之处。你把两者联系起来很聪明。

然而,虽然它们能够抽象地描述相同的数据集,但 prolog 通常在小型数据集上运行并执行扫描 in-memory。它不是数据库,当然不适合使用数据库 运行 跨越的 real-time 约束中的 many/most 进行扩展——即大量数据库写入。

像 Datomic 这样的语言使用 prolog (Datalog) 的一个子集作为它的查询语言,并且可能会稍微更符合你的想法,但即使那样也与 Labeled 属性 Graph (LPG) 这样的语言相去甚远neo4j。一个很大的区别是,具有属性的节点之间的 "labeled edges" 不是(据我所知)除了 LPG 之外的任何 first-order 概念。尽管您可以使用例如描述节点之间的这些边缘或关系。 join-table 来创建 many-to-many 关系,它们在像 neo4j 这样的东西中更加流畅。

从计算的角度来看,两种模型之间存在很大差异。 Prolog是图灵完备,这意味着原则上任何其他语言的程序都可以翻译成Prolog。

但是,Neo4j 查询语言 *Cypher 以及大多数数据库查询语言不是 图灵完备的,因此不适合表示任何通用程序。这有利也有弊。主要缺点是您通常需要将 Neo4j 的强大功能与 Python 或任何其他语言的外部程序结合起来才能生成有用的应用程序。主要优点是 Cypher 中的所有查询都是 terminating(尽管它们可能需要很长时间才能完成)对于数据库查询来说非常好 属性;当您查询数据库时,您总是希望得到答案。

Prolog 中不会发生这种情况。像

这样的简单程序
 p(X):-p(X).

并且像 p(a) 这样的目标会导致不终止计算。这是拥有图灵完备语言的所有功能所必须付出的代价。

如果你想看看另一个相关的范例,它介于 Prolog 和 Neo4j 之间,看看 演绎数据库,比如 Datalog. Datalog 的语法类似于 prolog(实际上它是一个子集),但是类似于数据库查询语言,Datalog 中的 goals/queries 总是终止的。

比如之前Datalog中的程序

  p(X):-p(X).

以相同的目标 p(a),轻松生成答案的空集 {},而不是像 Prolog 中那样无限循环。