查找所有记录,其中数组中没有项目具有值

时间:2019-10-02 23:52:39

标签: mongodb mongodb-query

我想找到所有记录,其中EITHER数组中的任何项目(things1或事物2)都没有“颜色”值。 (因此,所有颜色均为”或null或不存在)

这是数据结构-

{
    _id: objectId(),
    things1: [
        {
            height:
            weight:
            color:
        }
    ],
    things2: [
        {
            size:
            flavor:
            color:
        }
    ]
}

我已经尝试过了:

.find({
    $and: [
        {
            things1:{ 
                $elemMatch: { 
                    $or: [
                        {
                            color: ''
                        },
                        {
                            color: null
                        },
                        {
                            color: { $exists: false }
                        }
                    ]
                } 
            } 
        },
        {
            things2:{ 
                $elemMatch: { 
                    $or: [
                        {
                            color: ''
                        },
                        {
                            color: null
                        },
                        {
                            color: { $exists: false }
                        }
                    ]
                } 
            } 
        }
    ]
})

这将拉回ANY记录,其中任何项目具有空白,空值或不存在的“颜色” ... 意思是如果“ things1”中有您的东西,并且三个中的两个具有颜色,而第三个是一个空字符串,并且“ things2”中的所有项目都具有颜色-上面的查询将返回此文档

我只想在其中记录所有“颜色”字段为空白,空或不存在的地方。

任何帮助表示赞赏 谢谢

2 个答案:

答案 0 :(得分:3)

我们可以想到一条相反的路线,即必须为null,不存在或为空的字符串基本上是没有字符的字符串。我们可以计算 NOR(填充颜色存在于Things1数组中,填充颜色存在于Things2数组中。)

例如:

db.collection.find({
    $nor:[
        {
            "things1.color":/.+/
        },
        {
            "things2.color":/.+/
        }
    ]
}).pretty()

答案 1 :(得分:1)

我将简化您的数据集,只是为了使示例易于读者理解,但逻辑仍然相同

{ "a" : [ { "sample" : 1 } ], "b" : [ { "sample" : 1 } ] }       // No color
{ "a" : [ { "color" : "b" } ], "b" : [ { "color" : "b" } ] }     // YES - Valid color
{ "b" : [ { "color" : "" }, { "color" : "a" } ] }                // Empty color 
{ "b" : [ { "color" : null }, { "color" : "a" } ] }              // Null color
{ "b" : [ { "sample" : 1 } ] }                                   // No color
{ "b" : [ { "color" : "b" } ] }                                  // YES - Valid Color
{ "a" : [ { "color" : "" }, { "color" : "a" } ] }                // Empty color
{ "a" : [ { "color" : null }, { "color" : "a" } ] }              // Null Color
{ "a" : [ { "sample" : 1 } ] }                                   // No color
{ "a" : [ { "color" : "b" } ] }                                  // YES - Valid color

基本上具有您要查找的所有相同组合,因为在两个属性中,一个或两个都包含数组中的对象,而您希望该数组中的所有项都具有color属性:< / p>

  • 现有
  • 不为空
  • 不为空

简而言之,上述文件中只有三份符合条件。所以这是获取它们的方法:

db.collection.find({
  "$or": [
    {
      "a.color": { "$exists": true },
      "a": { "$not": { "$elemMatch": { "color": { "$in": [null, ""] } } } }
    },
    {
      "b.color": { "$exists": true },
      "b": { "$not": { "$elemMatch": { "color": { "$in": [null, ""] } } } }
    }
  ]
})

$or下,我们有两个成对条件。 分别(非常重要),以测试命名路径的存在,然后查找数组中任何对象与排除它们的条件(null或empty)和<使用$not表达式来strong>拒绝这些文档。

$or仅用于表示每个包含数组的离散字段的

结果当然是:

{ "a" : [ { "color" : "b" } ], "b" : [ { "color" : "b" } ] }
{ "b" : [ { "color" : "b" } ] }
{ "a" : [ { "color" : "b" } ] }

仅作为那些文档,其中所提供的外键中的一个或两个包含包含所有元素具有color属性且值不为null的数组。


实际上,您的意思是things1things2之类的字段可以动态方式在文档之间变化,并且可能具有things3,并且您想要所有属性及其包含数组的条件都可以匹配条件,那么对于标准查询而言,您基本上是不走运的,因此需要退回到aggregate()

在这里的示例中,如果我们添加了像这样的文档:

{ "a": [{ "color": "b" }, "c": [{ "color": "" }] }

然后,上面显示的基本查询表单仍将返回此文档,因此您将使用aggregate()

db.collection.aggregate([
  { "$addFields": {
    "comb": {
      "$reduce": {
        "input": {
          "$map": {
            "input": {
              "$filter": {
                "input": { "$objectToArray": "$$ROOT" },
                "cond": { "$in": [ "$$this.k", [ "a", "b", "c" ] ] }
              }
            },
            "as": "el",
            "in": {
              "$map": {
                "input": "$$el.v",
                "in": {
                  "$mergeObjects": [
                    { "type": "$$el.k" },
                    "$$this"
                  ]
                }
              }
            }
          }
        },
        "initialValue": [],
        "in": { "$concatArrays": [ "$$value", "$$this" ] }
      }
    }
  }},
  { "$match": {
    "comb.color": { "$exists": true },
    "comb": { "$not": { "$elemMatch": { "color": { "$in": [null, ""] } } } }
  }},
  { "$addFields": {
    "comb": "$$REMOVE"
  }} 
])

但这确实是不可取的。请注意,为了动态地遍历键,您需要$objectToArray才能有效地将文档中的所有键本身转换为单个数组条目。然后当然使用$filter作为 expected 键,或者替代地,将逻辑倒置为排除 _id和其他不适用的值。

然后这会将这些数组合并,同时将key的名称​​重新映射作为数组内的属性。主要注意到这里的真正要点不是"a.color""b.color",我们现在有一个"comb"单个路径,表示组合映射。

这将产生预期的结果,但是真正的解决方案应该在管道的已实现逻辑中看到,即代替带有数组的多个文档属性,更好的方法是单个数组仅使用thing1thing2(或在这种情况下为"a""b""c")中的>数组。

因此,这种存储数据的方式效率更高:

{ "data" : [ { "type" : "a", "color" : "" }, { "type" : "a", "color" : "a" }, { "type" : "b", "color" : "" }, { "type" : "b", "color" : "a" } ] }
{ "data" : [ { "type" : "a", "color" : null }, { "type" : "a", "color" : "a" }, { "type" : "b", "color" : null }, { "type" : "b", "color" : "a" } ] }
{ "data" : [ { "type" : "a", "sample" : 1 }, { "type" : "b", "sample" : 1 } ] }
{ "data" : [ { "type" : "a", "color" : "b" }, { "type" : "b", "color" : "b" } ] }
{ "data" : [ { "type" : "b", "color" : "" }, { "type" : "b", "color" : "a" } ] }
{ "data" : [ { "type" : "b", "color" : null }, { "type" : "b", "color" : "a" } ] }
{ "data" : [ { "type" : "b", "sample" : 1 } ] }
{ "data" : [ { "type" : "b", "color" : "b" } ] }
{ "data" : [ { "type" : "a", "color" : "" }, { "type" : "a", "color" : "a" } ] }
{ "data" : [ { "type" : "a", "color" : null }, { "type" : "a", "color" : "a" } ] }
{ "data" : [ { "type" : "a", "sample" : 1 } ] }
{ "data" : [ { "type" : "a", "color" : "b" } ] }
{ "data" : [ { "type" : "a", "color" : "b" }, { "type" : "c", "color" : "" } ] }

如果这是您的集合结构,那么 ALL 数组元素查询将变得非常简单,基本上就是aggregate()的最后一个阶段,该阶段实际上将您现有的结构转换为这种形式:

db.collection.find({
  "data.color": { "$exists": true },
  "data": { "$not": { "$elemMatch": { "color": { "$in": [null, ""] } } } }
})

然后本质上仍然符合条件的三个文档:

{ "data" : [ { "type" : "a", "color" : "b" }, { "type" : "b", "color" : "b" } ] }
{ "data" : [ { "type" : "b", "color" : "b" } ] }
{ "data" : [ { "type" : "a", "color" : "b" } ] }

在此注意,像"data.type"这样的一致路径名具有许多优点,这也使该名称对查询选项很有用。如果只想查找等于thing1的“类型”,就可以将其干净地添加到过滤条件中,这也使更新文档变得更加容易。

值得考虑,因为从长远来看,不依赖aggregate()语句进行操作的基本查询表单的效果要好得多。