MongoDB中的数组交集

时间:2013-04-12 10:30:58

标签: mongodb mongodb-.net-driver mongodb-query

好的,这里有几件事情......我有两个集合:test和test1。两个集合中的文档都有一个数组字段(分别为标记 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 tags1。位置操作员只返回第一场比赛吗?另外,使用$in并不会给我一个交叉点。我怎样才能得到一个交叉点(应该返回“a”和“b”)而不管哪个数组与另一个数组进行比较。
< BR />

现在说有一个运营商可以做到这一点..

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

我的要求是,我需要整个tags1数组PLUS这个交集,在同一个查询中如下:

> 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个文档,在测试中将获得极少数(<10)。而这个查询是实时的,所以我想避免mapreduce:)

感谢您的帮助!

3 个答案:

答案 0 :(得分:0)

如果你想实时拥有它,你应该考虑放弃仅使用一个线程运行的Serverside Javascript,并且应该非常慢(单线程)(v2.4不再适用) http://docs.mongodb.org/manual/core/server-side-javascript/)。

位置运算符仅返回第一个匹配/当前值。在不了解内部实现的情况下,从性能的角度来看,如果文档已被评估为匹配,则查找进一步的匹配条件甚至没有意义。所以我怀疑你能不能这样做。

我不知道你的搜索是否需要笛卡尔积,但我会考虑将你的几个测试文档标签加入一个,然后在test1上搜索一些$,返回所有匹配的文档。在本地计算机上,您可以使用多个线程生成文档的交集。

根据您的test1和测试集合更改的频率,您执行此查询可能会预先计算此信息。这将允许轻松地对包含交叉点信息的字段进行查询。

该文档无效,因为您有两个字段名称 tags1

答案 1 :(得分:0)

Mongo没有任何固有的能力来检索阵列交叉点。如果你真的需要使用ad-hoc查询来获取客户端的交集。

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

答案 2 :(得分:0)

在较新的版本中,您可以使用aggregation完成此操作。

db.test.aggregate(
    {
        $match: {
            tags1: {
                $in: doc.tags
            }
        }
    },
    {
        $project: {
            tags1: 1,
            intersection: {
                $setIntersection: [doc.tags, "$tags1"]
            }
        }
    }
);

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