假设我的后端有一百万篇文章实体,其inst属性称为date,或者有一百万个玩家实体,其int属性称为points。挑选10篇最新文章或得分最高的球员有什么好方法?
我是否需要将全部数百万美元提取给对等方,然后从他们那里分类并丢弃?
假设我的后端有一百万篇文章实体,其inst属性称为date,或者有一百万个玩家实体,其int属性称为points。挑选10篇最新文章或得分最高的球员有什么好方法?
我是否需要将全部数百万美元提取给对等方,然后从他们那里分类并丢弃?
在获取反向索引成为 Datomic 特性之前,您可以手动定义一个。
例如,对于 :db.type/instant,创建一个类型为 :db.type/long 的附加属性,您将使用该属性填充
(- (Long/MAX_VALUE) (.getTime date))
并且可以获取最新的 10 篇文章
(take 10 (d/index-range db reverse-attr nil nil))
是的,您需要获取所有数据,因为这里没有可以帮助您的索引。
我会创建自己的“索引”并将这些数据标准化。您可以拥有一组单独的 N 个实体,您可以在其中保留任意数量的实体。您可以从 10 开始,或者考虑存储 100 以换取一些(可能可以忽略不计的)速度以获得更大的灵活性。此索引可以存储在您添加为架构的一部分的单独“单例”实体上。
;; The attribute that stores the index
{:db/id #db/id[:db.part/db]
:db/ident :indexed-articles
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db.install/_attribute :db.part/db}
;; The named index entity.
{:db/id #db/id[:db.part/db]
:db/ident :articles-index}
您可以拥有一个执行此操作的数据库函数。每次插入要“索引”的新实体时,调用此函数。
[[:db/add tempid :article/title "Foo]
[:db/add tempid :article/date ....]
[:index-article tempid 10]]
index-article 的实现可能如下所示:
{:db/id #db/id[:db.part/user]
:db/ident :index-article
:db/fn #db/fn {:lang "clojure"
:params [db article-id idx-size]
:code (concat
(map
(fn [article]
[:db/retract
(d/entid db :articles-index)
:indexed-articles
(:db/id article)])
(->> (datomic.api/entity db :articles-index)
(sort-by (fn [] ... implement me ... ))
(drop (dec idx-size))))
[[:db/add (d/entid db :articles-index) :indexed-articles article-id]])}}
免责声明:我还没有实际测试过这个函数,所以它可能包含错误:) 一般的想法是我们从集合中删除任何“溢出”实体,并添加新实体。当 idx-size 为 10 时,我们要确保集合中只有 9 个项目,然后将新项目添加到其中。
现在您有了一个可以从索引中查找的实体:articles-index,并且可以从索引中查找最近的 10 篇文章(所有 ref 都被索引),而不会导致完整的数据库读取。
;; "indexed" set of articles.
(d/entity db :articles-index)
我一直在研究这个,并认为我有一个更优雅的答案。
将您的属性声明为索引:db/index true
{:db/id #db/id[:db.part/db -1]
:db/ident :ocelot/number
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/doc "An ocelot number"
:db/index true
:db.install/_attribute :db.part/db}
这可确保属性包含在 AVET 索引中。
然后以下内容让您可以访问“前十名”,尽管使用的是低级datoms
调用。
(take-last 10 (d/datoms (db conn) :avet :ocelot/number))
显然,如果您需要进行任何进一步的过滤(“谁是这个俱乐部的前十名得分手?”),那么这种方法将不起作用,但此时您手中的数据量要少得多,不应该需要担心索引。
我确实广泛研究了 Datalog 提供的聚合函数,但我无法理解它们 - 并且不确定例如max
是否会使用此索引而不是完整扫描数据。同样,该(index-range ...)
函数几乎肯定会使用此索引,但需要您知道开始和/或结束值。