4

我有一个对象集合,每个对象都有一个名为指纹的字段,其中包含 20 个哈希:

{
    title: 'The Chronicles of Narnia',
    authors: ['C.S. Lewis'],
    fingerprint: ['50e...', 'ae2...', ...]
}

然后我有另外 20 个哈希的查询指纹。我想做的是找到所有共享至少 X 哈希的条目。换句话说,两个数组的交集必须是一定的大小。

我有一个使用 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 和一个来自指纹的单个螺纹。每个文档将有 20 个条目Fingerprint

据我了解,此查询的作用是在每次螺纹匹配时复制文档,然后按唯一文档进行分组。这似乎有点浪费,但它有效。

我正在尝试在 MongoDB 中重新实现该系统,但运气不佳。我可以获得共享至少一个或所有螺纹的所有条目的列表:

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 查询所做的事情:为每个匹配项发出一个文档,然后计算文档数。当然,无论有多少匹配项,我们都只会发出一次文档。

然后我想到$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 回答