4

我目前正在评估 Datomic 用于存储和查询构成本体的已解析符号的用例。数据库中总共有 225122 个符号(实体)(所以它是一个相当大的本体,但对于数据库来说应该没什么大不了的)。

结构很标准,符号有

  1. 包含它们的父符号(如子符号等)
  2. 超级符号(它们继承的符号)

为了更好地访问符号,我们name为每个符号设置了一个唯一的。这增加了以下 Datomic 模式:

[{:db/ident :ml/name,
  :db/valueType :db.type/string,
  :db/cardinality :db.cardinality/one,
  :db/unique :db.unique/identity}
 {:db/ident :ml/parent,
  :db/valueType :db.type/ref,
  :db/index true,
  :db/cardinality :db.cardinality/one}
 {:db/ident :ml/superclass,
  :db/valueType :db.type/ref,
  :db/index true,
  :db/cardinality :db.cardinality/one}]

现在我有了最基本的递归查询“给我所有(传递地)包含在符号中的符号p”。在原子术语中:

(def rules
  '[
    [(ubersymbol ?c ?p) (?c :ml/parent ?p)]
    [(ubersymbol ?c ?p) (?c :ml/parent ?c1) (ubersymbol ?c1 ?p) ]
    ])
(q '[:find ?c ?n :in $ % :where
     (ubersymbol ?c ?d) [?d :ml/name "name of a root symbol"] [?c :ml/name ?n]]
   current-db rules)

查询本身(因此是一个中等大小的符号)需要55.5秒并返回 80 个命中。不是毫秒,而是真正的秒。这只是我想询问的关于数据集的最基本查询(它旨在从网络工具中使用,以帮助建模者理解本体的结构)。

我正在运行datomic-pro-0.9.5554,使用内存数据库并使用对等库(我按照“入门”指南中的说明启动了服务器。

非常感谢帮助为 Datomic 提供案例。

马库斯

4

1 回答 1

6

编辑

正如fricke他自己发现的那样,这一个子句排序的问题,但在查询中,而不是在规则集中。更有效的版本是:

[:find ?c ?n :in $ % :where
   [?d :ml/name "name of a root symbol"]
   (ubersymbol ?c ?d) 
   [?c :ml/name ?n]]

可以通过以下方式进一步改进上述查询:

  • 使用查询参数而不是在查询正文中使用动态参数
  • 使用查找引用来解析输入实体:ml/name

产生:

(d/q
  '[:find ?c ?n :in % $ ?d :where
    (ubersymbol ?c ?d)
    [?c :ml/name ?n]]
  rules current-db [:ml/name "name of a root symbol"])

我的理论是,您的规则不是以 Datalog 可以针对这种读取模式进行优化的方式编写的 - 可能导致遍历所有实体。我建议将它们重写如下:

[[(ubersymbol ?c ?p) 
  (?c :ml/parent ?p)]
 [(ubersymbol ?c ?p) 
  ;; we bind a child of the ancestor, instead of a parent of the descendant
  (?c1 :ml/parent ?p)
  (ubersymbol ?c ?c1)]]

这种编写规则集的方式经过优化,可以找到某个节点的后代。您最初编写它的方式已针对查找某个节点的祖先进行了优化。

在我的机器上使用 Datomic 0.9.5385 在 50000 个实体的平衡二叉树上进行的快速基准测试表明,使用第二种方法确实可以获得所需的性能。

于 2017-02-26T14:58:28.650 回答