4

オブジェクトのコレクションがあり、それぞれに 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 は問題外だと思いますか?

ありがとう

4

1 に答える 1

5

集計フレームワークで必要なことを行うのは、実際には非常に簡単です。必要なことを正確に行うために、次のことを改善できると確信しています。

db.objects.aggregate([
    {$unwind : "$fingerprint" },
    {$match  : {fingerprint : {$in: [hashes] } } },
    {$group  : {_id:"$title", numMatches: {$sum:1} } },
    {$match  : {numMatches : {$gt: X} } }
])
于 2013-01-07T18:25:40.047 に答える