美文网首页
MongoDB 查询技巧(3) - 数组操作

MongoDB 查询技巧(3) - 数组操作

作者: Yellowtail | 来源:发表于2021-08-31 09:09 被阅读0次

    概述

    最近用到了 MongoDB 数组的一些操作符,如 $all $elemMatch 所以研究一下,分享在这里

    准备数据

    数据如下

    { "_id" : { "$oid" : "6128e4c50990c143c05a8d29" }, "array" : [ 1, 2, 3 ], "name" : "hello" }
    { "_id" : { "$oid" : "6128e4cf0990c143c05a8d2a" }, "array" : [ 2, 3, 7 ], "name" : "world" }
    { "_id" : { "$oid" : "6128e4d80990c143c05a8d2b" }, "array" : [ 7, 15, 30 ], "name" : "java" }
    { "_id" : { "$oid" : "6128e4ef0990c143c05a8d2c" }, "array" : [ 25, 30, 49 ], "name" : "python" }
    { "_id" : { "$oid" : "6128e5000990c143c05a8d2d" }, "array" : [ 34, 48, 49 ], "name" : "c" }
    { "_id" : { "$oid" : "612a1ed50990c143c05a8d6b" }, "name" : "i-am-null" }
    { "_id" : { "$oid" : "612a1edf0990c143c05a8d6c" }, "name" : "i-am-empty", "array" : [  ] }
    
    
    data

    1. 『任意元素 eq』找到含有3的

    没什么好说的,大家都会

    db.getCollection("test-sytax").find({
        array: 3
    })
    
    one

    2. 『任意元素 匹配区间』找到含有 14 < x < 33 元素的文档

    我们的需求是数组含有 15、16。。。30、31,太多了,用表达式 $gt :14, $lt: 33
    我们很快写出来了

    db.getCollection("test-sytax").find({
        array: {
            $gt: 14,
            $lt: 33
        }
    })
    
    two

    范围调整一下,试试查不到的情况,那就 11 < x < 15 吧,看下 数组含有 12、13、14,应该是查不到的

    db.getCollection("test-sytax").find({
        array: {
            $gt: 11,
            $lt: 15
        }
    })
    
    what?

    什么情况?应该是匹配不到的呀
    查阅官方文档,Query an Array
    find里对数组字段写多个条件,这些条件之间是 in 的关系,大家理解为 or 也没问题
    那此时该怎么办呢? $elemMatch 就排上用场了, 官方文档为 elemMatch

    The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.

    翻译过来就是: $elemMatch 操作符可以用来匹配指定数组字段,包含有 至少一个 可以满足所有查询条件的元素
    我们试一下

    db.getCollection("test-sytax").find({
        array: {
            $elemMatch: {
                $gt: 11,
                $lt: 15
            }
        }
    })
    
    empty

    OK,此时返回为空,正常了

    对比一下普通字段
    对于数据

    { "_id" : { "$oid" : "612a32400990c143c05a8dc7" }, "age" : 1 }
    { "_id" : { "$oid" : "612a32430990c143c05a8dc8" }, "age" : 2 }
    { "_id" : { "$oid" : "612a32470990c143c05a8dc9" }, "age" : 3 }
    
    and

    可以看出:

    • 对于普通字段,多个条件之间是 and 关系
    • 对于数组字段,多个条件之间是 or 关系,如果想要达到 and 关系,需要使用 $elemMatch

    3. 『数组 eq 指定数组』想要找的数组元素,一个不多,一个不少

    对于数据


    image.png

    我知道有个文档的数组字段值就是 [ 1, 2, 3 ] ,现在把它找出来
    现在写语句

    db.getCollection("test-sytax").find({
        array: {$in: [1,2,3]}
    })
    
    wrong

    又不对了~
    我们都知道还有个 $all 操作,现在来试试

    db.getCollection("test-sytax").find({
        array: {
            $all: [1,2,3]
        }
    })
    
    all

    查阅文档 $all 得知

    The $all operator selects the documents where the value of a field is an array that contains all the specified elements

    翻译一下就是 $all 用在数组字段上,这个数字字段需要包含所有的指定元素

    所以 $all 的参数其实是一个 子集,作用就是找出包含这个子集的数组字段的文档

    那么这里改怎么实现呢?在网上看到了一些回答,思路都是 数组字段不包含 1 2 3之外的元素, 也就是 双重否定
    来试一下

    db.getCollection("test-sytax").find({
        array: {
            $not: {
                $elemMatch: {
                    $nin: [1,2,3]
                }
            }        
        }
    })
    
    
    image.png

    发现一些空的,还有其它类型的也都冒出来了,那么就再过滤一下

    db.getCollection("test-sytax").find({
        array: {
            $not: {
                $elemMatch: {
                    $nin: [1,2,3]
                }
            },
            $type: 16       
        }
    })
    
    
    image.png

    4. 『数组 all match』想要找的数组元素,所有元素都满足指定条件

    对于数据

    image.png
    我们现在来找出 所有元素大于24的数组文档, 也就是图中红框的两个文档

    按照普通思路,上查询语句

    db.getCollection("test-sytax").find({
        array: {
            $gt: 24
        }
    })
    
    
    image.png

    发现不对,还是按照 上面的 双重否定 思路来实现

    db.getCollection("test-sytax").find({
        array: {
            $not: {
                $elemMatch: {
                    $lte: 24
                }
            }
        }
    })
    
    image.png

    还是有一些干扰项,排除之

    db.getCollection("test-sytax").find({
        array: {
            $not: {
                $elemMatch: {
                    $lte: 24
                }
            },
            $type: 16
        }
    })
    
    
    image.png

    总结下来:
    allMatch 思路就是 双重否定 加 排除干扰项

    总结

    1. find有多个条件时,如果字段是普通字段就是and的关系;
    2. find有多个条件时,如果是数组字段就是 or 的关系;想要数组字段达到and的效果,需要使用$elemMatch
    3. 数组字段的操作符 $all, 参数是指定子集,作用是找出 所有包含这个子集的数组字段的文档
    4. MongoDB 本身没有提供 allMatch, 需要自行实现,思路是 双重否定 配合 排除干扰项

    参考

    https://stackoverflow.com/questions/23595023/check-if-every-element-in-array-matches-condition
    https://stackoverflow.com/questions/19574849/check-if-all-elements-in-mongodb-array-match-a-given-query
    https://dba.stackexchange.com/questions/203042/mongodb-operator-to-match-arrays-whose-elements-are-all-contained-in-a-specifie
    http://www.askasya.com/post/matchallarrayelements/

    相关文章

      网友评论

          本文标题:MongoDB 查询技巧(3) - 数组操作

          本文链接:https://www.haomeiwen.com/subject/zzciiltx.html