MongoDB匹配$ type的数组?

Ali*_*nat 12 mongodb mongodb-query

我有一个包含284.116条推文的MongoDb集合.问题是某些对象中的"author"字段是对象类型,但在其他对象中 - 这个"author"字段是数组类型.所以问题是我想过滤哪些是Array,哪些是Object.

例如:作者字段的类型是对象.

{
    "_id" : ObjectId("55edfbd11a87d41d987a6dc1"),
    "tweet" : "Back in my dorm, yay!",
    "uri" : "https://twitter.com/natalylug0/status/640994018529181696",
    "date" : "2015-09-08 00:04:17",
    "country" : "U.S.A.",
    "city" : "Texas",
    "state" : "Dallas",
    "author" : {
        "username" : "Nataly",
        "uri" : "https://twitter.com/natalylug0",
        "screenname" : "natalylug0"
    }
}
Run Code Online (Sandbox Code Playgroud)

而另一个:作者字段的类型是数组.

{
    "_id" : ObjectId("55ee3a00e11fbb1030d659fe"),
    "author" : [ 
        {
            "username" : "Relapsed Shini",
            "uri" : "https://twitter.com/iPictoraL",
            "screenname" : "iPictoraL"
        }
    ],
    "tweet" : "@zumbiezuza  ily zoeeeeeeee",
    "uri" : "https://twitter.com/iPictoraL/status/641060812140900352",
    "date" : "2015-09-08 01:29:42",
    "country" : "U.S.A.",
    "city" : "Texas",
    "state" : "Dallas"
}
Run Code Online (Sandbox Code Playgroud)

所以我执行了这样的查询:

db.getCollection('tweets').find({ author: { $type: 4} })
Run Code Online (Sandbox Code Playgroud)

而我得到的是

Fetched 0 record(s) 
Run Code Online (Sandbox Code Playgroud)

但是如果执行$ type:3,我得到284.116个值,这个值与此集合的大小相同.

所以我的问题是,如何过滤"作者"字段包含数组的对象.

Bla*_*ven 29

实际上,文档中列出了一个$type特别关于数组的"问题" :

应用于数组时,$ type匹配任何指定类型的内部元素.如果没有投影,这意味着如果任何元素具有正确的类型,整个数组将匹配.通过投影,结果将仅包括所请求类型的那些元素.

这意味着不是检测"元素本身"是否在数组中,而是实际测试的是数组的"内部元素",以查看它是什么类型.

现在,文档本身建议使用以下JavaScript测试$where:

.find({ "$where": "return Array.isArray(this.author)" })
Run Code Online (Sandbox Code Playgroud)

但我认为这是非常可怕的,因为有更好的方法.

诀窍是"点表示法",你要求0数组的索引元素$exists

.find({ "author.0": { "$exists": true } })
Run Code Online (Sandbox Code Playgroud)

这只是基本情况,如果存在"0th"元素,则该字段存在,因此数据是数组.

一旦你理解了这个逻辑前提,那么它就是非常简单的测试.唯一无法匹配的是"真正空"的数组,在这种情况下,如果需要,您可以回退到JavaScript替代方案.但这实际上可以使用索引,因此最好使用后一种形式.

  • 如果您的代码有错误,并且正在插入看起来像数组的对象,则建议的““ author.0”:{“ $ exists”:true}`将不起作用。我说的是`{0:“ bla”}` (2认同)

Cia*_*ros 6

这是做您最初要求的更好的方法。这实际上是检查某个字段是否持有数组类型值:

.find({ "author": { "$gte": [] } })
Run Code Online (Sandbox Code Playgroud)

MongoDB的数组$ type功能虽然有充分的文档证明,但IMO与所有其他$ type检查不一致,并且显然不适用于此用例,但是从2.6开始,您可以使用上述查询来检查值是否为数组(是否为空)。

我说这比当前选择的答案“更好”,因为不建议通过$ where执行代码,除非标准查询构造确实无法完成工作。

详细地说,由于缺乏执行代码中的索引的性能,因此不建议使用$ where。更多详细信息:https : //docs.mongodb.com/manual/reference/operator/query/where/#considerations

另外,如果您要专门检查非空数组,请使用以下命令:

.find({ "author": { "$gt": [] } })
Run Code Online (Sandbox Code Playgroud)

从技术上讲,此字段也比当前答案的相应$ exists解决方案好,因为该字段可能具有一个非数组对象,该对象的字段名为“ 0”,并且会匹配为“非空数组”,这是错误的在这种情况下。


nim*_*rok 3

从 mongoDB 版本 3.2 开始,我们有了$isArray一个聚合管道,它允许执行以下操作:

db.tweets.aggregate([
   {$set: {isArray: {$cond: [{ $isArray: "$author" }, 1, 0]}}},
   {$match: {isArray: 1}}
])
Run Code Online (Sandbox Code Playgroud)

游乐场示例 A

甚至:

db.tweets.aggregate([
  {$match: {$expr: {$isArray: "$author"}}}
])
Run Code Online (Sandbox Code Playgroud)

游乐场示例 B