オブジェクトのコレクションがあり、それぞれに 20 個のハッシュを含む指紋と呼ばれるフィールドがあります。
{
title: 'The Chronicles of Narnia',
authors: ['C.S. Lewis'],
fingerprint: ['50e...', 'ae2...', ...]
}
次に、別の 20 個のハッシュのクエリ フィンガープリントがあります。私がやりたいことは、少なくとも X ハッシュを共有するすべてのエントリを見つけることです。つまり、2 つの配列の交点は特定のサイズでなければなりません。
MySQL を使用する同様のシステムの古い実装があります。クエリは次のようになります。
SELECT *
FROM Document d
INNER JOIN Fingerprint f
ON d.id = f.document_id
WHERE f.whorl IN (:hashes)
GROUP BY d.id
HAVING COUNT(d.id) >= X
テーブル内の各エントリにFingerprint
は、ドキュメント ID とフィンガープリントからの単一の渦巻きが含まれています。Fingerprint
ドキュメントごとに 20 のエントリがあります。
私が理解しているように、このクエリが行っていることは、渦巻きが一致するたびにドキュメントを複製し、一意のドキュメントでグループ化することです。これは少し無駄に思えますが、うまくいきます。
このシステムを MongoDB に再実装しようとしていますが、うまくいきませんでした。少なくとも 1 つまたはすべての渦巻きを共有するすべてのエントリのリストを取得できます。
at least one: db.objects.find({ fingerprint: {$in: [hashes]})
all: db.objects.find({ fingerprint: {$all: [hashes]})
そして、アプリケーション層でこのリストをスキャンして、探している一致を見つけることができることを理解しています. 何百万ものオブジェクト (現在約 150 万) を予想する場合、これは悪い考えのように思えます。
私はaggregate()
機能を見てきましたが、私がすでに持っているものを改善することはできません:
db.objects.aggregate({$match: {fingerprint: {$in: [hashes]}}})
ここから、グループ化してフィルタリングできると思いました:
db.objects.aggregate({$match: {fingerprint: {$in: [hashes]}}},
{$group: {_id: "$_id", matches: {$sum: 1}}})
ここで、MySQL クエリが行ったことを再現しようとしました。一致するたびにドキュメントを発行し、ドキュメントをカウントします。もちろん、ここでは、一致するものがいくつあっても、ドキュメントは 1 回だけ発行します。
次に、一致したリストを考えまし$unwind
たが、毎回 20 個のドキュメントが生成されます。
理想的には、次の$some
ように使用できる演算子があるでしょう。
db.objects.find(fingerprint: {$some: {from: [hashes], count: X}})
このようなことは可能で効率的ですか?ユーザーの検索に応じてこれらのクエリを実行できるようにしたいので、MapReduce は問題外だと思いますか?
ありがとう