Mongodb中老司机也会漏掉的点——数组查询

本文基于mongodb官方文档,整理了字段值为数组类型的文档查询方法。以往涉猎mongodb数组查询较少,而且一直认为与对其他类型字段查询无异,直到深入阅读文档,才发现自己对数组查询认知错误。确实通过认真阅读文档,学习到了知识,补上漏洞。刚刚也与使用mongodb多年的朋友也讨论了本文中的查询方法,数组查询的这个知识漏洞,确实很容易忽略。

构建测试数据

首先,构建本次查询测试的数据。创建inventory集合,集合中包含字符串数组tags,数值型数组dim_cm.

db.inventory.insertMany([
    { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
    { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
    { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
    { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
    { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
])

围绕着两个数组字段进行查询

使用数组作为查询条件

在查询过滤器中,将数组指定到查询字段,即查询出该字段符合数组的文档。如下面语句中,查询出字段tags是["red", "blank"]的文档数据。其中,文档中tags要严格等于["red", "blank"]数组的文档,才会被查询出来。顺序不符合["red", "blank"]的文档,也不会被查询出来。

db.inventory.find({tags: ["red", "blank"]})

如果查询所有tags字段包含"red", "blank"两个字符串的元素,使用$all操作符。该查询操作,不限数组元素的排列顺序,只要包含两个这两个元素就可以。

db.inventory.find({tags: {$all: ["red", "blank"]}})

使用数组元素作为查询条件

等值查询

查询出数组中包含指定字符串的文档。当tags字段为字符串时,查询出字段值是"red"的文档。当tags字段为数组时,查询出包含"red"元素的数组所在文档。

db.inventory.find({tags: "red"})

使用查询操作符构建复杂查询

查询出包含大于25的数组元素的文档

db.inventory.find({dim_cm: { $gt: 25}})

指定多个条件查询数组元素

指定多个查询条件时,用户可以限制单一数组元素符合限制条件,也可以查询出多个元素满足查询条件的文档。

下面的查询语句中,查询出部分数组元素满足条件的文档。查询dim_cm数组中,包含大于15的元素,同时包含小于20元素的文档。

db.inventory.find({dim_cm: { $gt: 15, $lt: 20}})

在测试数据中,插入了5条测试文档

 { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
 { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
 { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
 { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
 { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }

{item: "journal"}的文档dim_cm字段中,包含14,21两个元素。其中元素20大于15, 元素14小于20,符合条件;

{item: "notebook"}的文档dim_cm字段中,包含14,21两个元素。其中元素20大于15, 元素14小于20,符合条件;

{item: "paper"}的文档dim_cm字段中,包含14,21两个元素。其中元素20大于15, 元素14小于20,符合条件;

{item: "planner"}的文档dim_cm字段中,包含22.85,30两个元素。其中元素22.85和30大于15, 但没有小于20的元素,不符合条件;

{item: "postcard"}的文档dim_cm字段中,包含10,15.25两个元素。其中元素15.25大于15, 10和15.25小于20的元素,符合条件;

这个查询语句并不是对单一数组元素进行限定,只要数组中包含一条或多条符合查询条件的元素,就会被查找出来。

限定单一元素满足查询条件,使用$elemMatch操作符。下面的查询语句中,要找出数组中同时满足大于15小于20的元素。有这样元素的数组,才会被查询出来。

db.inventory.find({dim_cm: {$elemMatch: { $gt: 15, $lt: 20}}})

回到插入的这5条数据。只有{item: "postcard"}的文档中元素15.25符合查询条件,因此只返回一条数据。

这两个查询语句限定的范围不同,使用上也很容易混淆,而且发现问题解决起来很困难。也是平时工作学习中容易忽略的地方。需要引起注意。

使用数组索引位置查询

利用点操作符,用户可以针对数组中某个位置的元素构建查询条件。索引位置是从0开始的数组元素索引。当使用点操作符时,字段名和内嵌字段名都必须在引号当中。

如下面的查询语句,查询出数组dim_cm中第二个元素大于25的数据

db.inventory.find( { "dim_cm.1": { $gt: 25}})

按照数组长度查询

除了可以构建基于数组元素的查询语句外,mongodb支持数组长度查询。

使用$size操作符,查询指定长度的数组。

查询tags字段数组长度为3的文档

db.inventory.find({"tags": {$size: 3}})