【问题标题】:Array intersection in MongoDBMongoDB中的数组交集
【发布时间】:2013-04-04 20:14:36
【问题描述】:

好的,这里发生了一些事情..我有两个集合:test 和 test1。两个集合中的文档都有一个包含一些标签的数组字段(分别为 tagstags1)。我需要找到这些标签的交集 如果即使单个标签匹配,也从集合 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 标签1。位置运算符是否只返回第一个匹配项?此外,使用$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 文档,而在测试中将有很少 (

感谢您的帮助!

【问题讨论】:

  • 要评论您的第一个问题,“db.test1.find({tags1:{$in:doc.tags}})”将返回一个游标。要打印到屏幕上,您应该在第二个查找中附加如下内容: .forEach(function(doc){print(tojson(doc));})
  • 谢谢@JamesWahlin!我知道这很愚蠢:P 您的快速评论让我至少可以进一步寻找解决方案。

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


【解决方案1】:

如果你想实时拥有这个,你应该考虑放弃只用一个线程运行并且应该很慢(单线程)的服务器端 Javascript(这对于 v2.4 不再适用, http://docs.mongodb.org/manual/core/server-side-javascript/)。

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

我不知道您是否需要笛卡尔积来进行搜索,但我会考虑将您的几个 test one 文档标签合并为一个,然后在 test1 上进行一些 $in 搜索,返回所有匹配的文档。在您的本地机器上,您可以有多个线程为您的文档生成交集。

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

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

【讨论】:

  • 为什么与服务器端 JS 相关?他在尝试中没有做任何服务器端 JS - shell 在客户端机器上本地运行 js。
  • 是的,你是对的,JavaScript 是在本地执行的。我在这里走错了路。
【解决方案2】:

Mongo 没有任何固有的检索数组交集的能力。如果您确实需要使用临时查询,请在客户端获取交集。

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-16
      • 2014-08-21
      • 2019-12-06
      • 1970-01-01
      相关资源
      最近更新 更多