MongoDB中的数组交集

4

这里有几件事情需要处理。我有两个集合:test和test1。这两个集合中的文档都有一个数组字段(分别为tags和tags1),其中包含一些标签。我需要找出这些标签的交集,并且如果存在单个标签匹配,就从test1集合中获取整个文档。

> db.test.find();
{
    "_id" : ObjectId("5166c19b32d001b79b32c72a"),
    "tags" : [
            "a",
            "b",
            "c"
    ]
}          
> db.test1.find();
{
    "_id" : ObjectId("5166c1c532d001b79b32c72b"),
    "tags1" : [
            "a",
            "b",
            "x",
            "y"
    ]
}
> db.test.find().forEach(function(doc){db.test1.find({tags1:{$in:doc.tags}})});

令人惊讶的是,这并没有返回任何内容。然而当我尝试使用单个文档时,它可以工作:

> var doc = db.test.findOne();
> db.test1.find({tags1:{$in:doc.tags}});
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1" : [ "a", "b", "x", "y" ] }

但这只是我需要的一部分。我还需要交集。所以我尝试了这个:
> db.test1.find({tags1:{$in:doc.tags}},{"tags1.$":1});
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1" : [ "a" ] }

但是它只返回了"a",而"a"和"b"都在标签中。位置操作符只返回第一个匹配项吗?此外,使用$in无法精确给出交集。我应该如何获得交集(应该返回"a"和"b"),而不考虑哪个数组与另一个进行比较。现在假设有一个可以做到这一点的操作符..
> db.test1.find({tags1:{$intersection:doc.tags}},{"tags1.$":1});
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1" : [ "a", "b" ] }

我的要求是,在同一查询中获取整个tags1数组及此交集,如下所示:

> db.test1.find({tags1:{$intersection:doc.tags}},{"tags1":1, "tags1.$":1});
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1": [ "a", "b", "x", "y" ],
"tags1" : [ "a", "b" ] }

但是这是一个无效的 JSON。是否可以重命名键名,或者只能通过聚合框架实现(跨不同集合)?我尝试了上面的查询和$in。但它的行为就像完全忽略了 "tags:1" 投影一样。
PS:我将在test1中有至少10k个文档,在test中只有很少的文档(<10)。而且这个查询是实时的,所以我想避免使用MapReduce :)
谢谢任何帮助!

2
针对你的第一个问题,"db.test1.find({tags1:{$in:doc.tags}})" 会返回一个游标。如果要将结果打印到屏幕上,你需要在第二个find后附加类似以下的内容:".forEach(function(doc){print(tojson(doc));})"。 - James Wahlin
谢谢@JamesWahlin!我知道这是一些愚蠢的事情:P你快速的评论让我至少能够继续寻找解决方案。 - Aafreen Sheikh
3个回答

1
在较新的版本中,您可以使用 聚合 来实现这一点。
db.test.aggregate(
    {
        $match: {
            tags1: {
                $in: doc.tags
            }
        }
    },
    {
        $project: {
            tags1: 1,
            intersection: {
                $setIntersection: [doc.tags, "$tags1"]
            }
        }
    }
);

正如你所看到的,match 部分与您最初的 find() 查询完全相同。 project 部分生成结果字段。 在这种情况下,它从匹配的文档中选择 tags1,并从输入和匹配的文档中创建 intersection

-1
如果您想实时运行此程序,应考虑远离仅使用一个线程且可能非常缓慢(单线程)的服务器端JavaScript(对于v2.4不再适用http://docs.mongodb.org/manual/core/server-side-javascript/)。
位置操作符仅返回第一个匹配/当前值。不知道内部实现情况,从性能角度来看,如果已将文档评估为匹配,则查找其他匹配条件甚至没有意义。 因此,我怀疑您能否继续执行此操作。
我不知道您是否需要笛卡尔积进行搜索,但我建议将少量测试标记连接到一个文档标记中,然后在test1上进行一些$in搜索,返回所有匹配的文档。 在本地计算机上,您可以有多个线程为文档生成交集。
根据test1和test collection更改频率以及您执行此查询的次数,您可以预先计算此信息。 这将允许轻松查询包含交集信息的字段。
该文档无效,因为您有两个名为tags1的字段。

为什么服务器端JS很重要?他在尝试中没有进行任何服务器端JS操作 - shell在客户端机器上本地运行JS。 - Asya Kamsky
是的,你说得对,JavaScript 是在本地执行的。我在这里走错了路。 - philnate

-1

MongoDB没有内置的能力来检索数组交集。如果您确实需要使用特定查询,请在客户端上获取交集。

另一方面,考虑使用Map-Reduce并将其输出存储为集合。您可以在finalize部分中增强返回的对象以添加交叉标签。使用Cron MR每隔几秒运行。您将获得永久集合的好处,可以从客户端查询。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接