【问题标题】:SQL to mongodb conversionSQL到MongoDB的转换
【发布时间】:2014-06-11 21:40:35
【问题描述】:

我在 mongodb 中有两个字段,A 和 B

我想在mongo中执行下面的sql查询

SELECT DISTINCT A FROM table WHERE B LIKE 'asdf'

编辑澄清

foo ={
    bar: [{
            baz:[
                ‘one’,
                ‘two'
            ]
        },{...}
    ]
}

我想选择 bar.baz 包含“一”的不同 foo 对象。 查询:

db.runCommand({
    "distinct": "foo",
    "query": {
        “bar.baz": “one"
    },
    "key": “bar.baz"
});

这个查询,奇怪的是,返回 bar.baz /doesnt/ 包含“一个”的 foo 对象。

【问题讨论】:

  • 在您的 SQL 中,“A”是表名,而不是字段。您的意思是输入其他内容吗?
  • 已修复。感谢您指出这个错误
  • 我发布的问题是一个简化。请查看更详细的问题

标签: sql mongodb postgresql mongodb-query aggregation-framework


【解决方案1】:

这里似乎对 MongoDB distinct 命令的工作方式或任何查询如何与数组一起工作存在误解。

我将考虑您实际上拥有的文档看起来像这样:

{

    "_id" : ObjectId("5398f8bf0b5d1b43d3e26816"),
    "bar" : [
        {
            "baz" : [
                "one",
                "two"
            ]
        },
        {
            "baz" : [
                "three"
            ]
        },
        {
            "baz" : [
                "one",
                "four"
            ]
        }
    ]
}

所以你运行的查询,这两种形式是等价的:

db.runCommand({ 
    "distinct": "foo", 
    "query": { "bar.baz": "one" }, 
    "key": "bar.baz" 
})

db.foo.distinct("bar.baz", { "bar.baz": "one" })

基本上返回这个:

[ "four", "one", "three", "two" ]

为什么?好吧,因为你要求它。让我们考虑一种描述您实际调用的内容的声明方式。

你的“查询”本质上是说'找到所有“bar.baz”等于“one”的“文档”'然后你问'然后把所有的都还给我“bar.baz”的“不同”值

因此,您的语句的“查询”部分正是这样做的,并且匹配了“文档”,而不是与您指定的值匹配的数组成员。在上面的示例中,您随后要求“bar.baz”的“不同”值,这正是您得到的,只有“一个”的值从“bar.baz”的所有值返回一次”。

所以“查询”语句不会“过滤”数组内容,它们只是“匹配”条件存在的地方。上述文档符合条件,“bar.baz”的值为“one”,甚至两次。所以选择不同的“foo”或者基本上是文档真的是:

db.foo.find({ "bar.baz": "one" })

匹配所有满足条件的文档。这就是嵌入的工作方式,但也许您想要过滤结果之类的东西。因此,如果只返回那些“baz”值为“one”的“bar”项目,您会这样做:

db.collection.aggregate([
    // Matches documents
    { "$match": { "bar.baz": "one" } },

    // Unwind to de-normalize arrays as documents
    { "$unwind": "$bar" },

    // Match to "filter" documents without "bar.baz" matching "one"
    { "$match": { "bar.baz": "one" } },

    // Maybe group back to document with the array
    { "$group": {
        "_id": "$_id",
        "bar": { "$push": "$bar" }
    }}

])

这个.aggregate() 语句的结果是没有“bar”成员且“baz”下不包含“one”的文档:

{

    "_id" : ObjectId("5398f8bf0b5d1b43d3e26816"),
    "bar" : [
        {
            "baz" : [
                "one",
                "two"
            ]
        },
        {
            "baz" : [
                "one",
                "four"
            ]
        }
    ]
}

但是假设您实际上只希望元素“bar.baz”等于“one”以及这些出现在整个集合中的总数,那么您会想要这样做:

db.collection.aggregate([
    // Matches documents
    { "$match": { "bar.baz": "one" } },

    // Unwind to de-normalize arrays as documents
    { "$unwind": "$bar" },

    // And the inner array as well
    { "$unwind": "$bar.baz" },

    // Then just match and filter out everything but the matching items
    { "$match": { "bar.baz": "one" } },

    // Group to get the count
    { "$group": {
        "_id": "$bar.baz",
        "count": { "$sum": 1 }
    }}

])

您可以从我们的单一文档收集示例中获得:

{ "_id": "one", "count": 2 }

因为该匹配值出现了两次。


至于您的问题开头的 SQL,这确实不适用于此类数据。更实际的例子是这样的数据:

{ "A": "A", "B": "BASDFJJ" }
{ "A": "A", "B": "ASDFTT" }
{ "A": "B", "B": "CASDF" }
{ "A": "B", "B": "DKITB" }

所以“A”的“不同”值,其中“B”类似于“ASDF”,再次使用聚合并注意您在任何一方都没有通配符:

db.foo.aggregate([
    { "$match": { "B": "ASDF" } },
    { "$group": { "_id": "$A" } }
])

本质上产生:

{ "_id": "A" }

或者在 "%ASDF%" 两边使用通配符,这是要匹配的 $regex 查询:

db.foo.aggregate([
    { "$match": { "B": { "$regex": "ASDF" } } },
    { "$group": { "_id": "$A" } }
])

所以只有两个结果:

{ "_id": "A" }
{ "_id": "B" }

如果您“计算”不同的匹配项,那么根据匹配的文档,您会看到 2 和 1 分别作为计数。

进一步查看文档中包含的SQL Mapping Chart 和SQL to Aggregation Mapping Chart。它应该可以帮助您了解常见操作的实际转化方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多