バックエンドにdateというinst属性を持つ 100 万の articleエンティティ、またはpointsというint属性を持つ 100 万のプレーヤーエンティティがあるとします。最新の 10 の記事またはトップ スコアのプレーヤーを選択する良い方法は何ですか?
何百万もの全体をピアにフェッチしてから、並べ替えてドロップする必要がありますか?
バックエンドにdateというinst属性を持つ 100 万の articleエンティティ、またはpointsというint属性を持つ 100 万のプレーヤーエンティティがあるとします。最新の 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 件の記事をインデックスから検索できます (すべての参照がインデックス化されます)。
;; "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
次に、低レベルの呼び出しを使用しても、「トップ 10」にアクセスできます。
(take-last 10 (d/datoms (db conn) :avet :ocelot/number))
明らかに、さらにフィルタリングを行う必要がある場合 (「このクラブのトップ 10 のスコアラーは誰ですか?」)、このアプローチは機能しませんが、その時点で手元にあるデータの量ははるかに少なく、そうすべきではありません。インデックス作成について心配する必要があります。
私は Datalog から利用可能な集計関数を広範囲に調べましたが、それらを理解するのに苦労しています。たとえばmax
、データの完全なスキャンではなく、このインデックスを使用するかどうかはわかりません。同様に、(index-range ...)
関数はほぼ確実にこのインデックスを使用しますが、開始値および/または終了値を知る必要があります。