mongodb · 27 10 月, 2022 0

mongodb基础操作(一) ——插入和查询

1. 插入操作(insert)

插入操作是插入一个新的文档(Document)到指定的集合(Collection)中, 如果插入的数据中没有指定_id属性,mongodb自动创建_id属性的值,值为ObjectId(). 在插入操作的时候,主要有以下两个操作

  • 判断当前集合(Collection)是否存在, 如果不存在,则创建Collection
  • 判断插入数据是否包含了_id属性,不过不包含,则新增_id属性,默认值为ObjectId()。在mongodb中,_id作为主键是必须存在的,因此mongodb设置了默认值

1.1 插入单个文档(insertOne)

db.inventory.insertOne(
   { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)

返回值为

{
  acknowledged: true,
  insertedId: ObjectId("6358981177ef9e18649ae28f")
}

当插入数据时,我们并没有指定_id属性,因此mongodb为我们默认指定了_id属性值。

  • acknoledged: 该属性值为true表明插入数据成功
  • insertedId: 该值返回了插入记录数据的_id值,该值唯一

1.2 批量插入(insertMany)

db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])

返回值为:

{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("635898ee77ef9e18649ae290"),
    '1': ObjectId("635898ee77ef9e18649ae291"),
    '2': ObjectId("635898ee77ef9e18649ae292")
  }
}

当我们批量插入多条数据的时候,会返回多条数据记录的_id值,顺序是相对应的。

我们可以通过db.inventory.find({})语句,查询出我们查询的数据信息。

db.inventory.find({})

返回结果为

[
  {
    _id: ObjectId("6358981177ef9e18649ae28f"),
    item: 'canvas',
    qty: 100,
    tags: [ 'cotton' ],
    size: { h: 28, w: 35.5, uom: 'cm' }
  },
  {
    _id: 123,
    item: 'canvas',
    qty: 100,
    tags: [ 'cotton' ],
    size: { h: 28, w: 35.5, uom: 'cm' }
  },
  {
    _id: ObjectId("635898ee77ef9e18649ae290"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    size: { h: 14, w: 21, uom: 'cm' }
  },
  {
    _id: ObjectId("635898ee77ef9e18649ae291"),
    item: 'mat',
    qty: 85,
    tags: [ 'gray' ],
    size: { h: 27.9, w: 35.5, uom: 'cm' }
  },
  {
    _id: ObjectId("635898ee77ef9e18649ae292"),
    item: 'mousepad',
    qty: 25,
    tags: [ 'gel', 'blue' ],
    size: { h: 19, w: 22.85, uom: 'cm' }
  }
]

当我们插入一条_id已经存在的记录的时候,就会抛出异常,例如:

db.inventory.insertOne( { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" }, _id:123 } )
db.inventory.insertOne( { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" }, _id:123 } )

返回结果为:

MongoError: E11000 duplicate key error collection: test.inventory index: _id_ dup key: { _id: 123 }

因此这里就会返回duplicate key的提示信息。

1.3 插入行为

mongodb插入行为主要包含以下几点。

1.3.1 创建集合

执行时,会判断集合是否存在,如果不存在,则创建集合

1.3.2 设置_id属性

当执行插入数据时,会判断是否包含了_id属性,如果没有包含,则设置_id默认值为ObjectId

1.3.3 原子性

mongodb在单个文档插入上具备原子性。

这句话表明了,在执行批量操作的时候,如果某条语句执行失败,不会讲已经插入的数据全部回滚,这可能会导致批量操作时,部分数据插入成功,部分数据插入失败。

例如以下示例中,_id=123已经存在,我们执行批量插入数据操作,

db.inventory.insertMany([
   { item: "journal1", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat3", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { _id: 123, item: "mousepad3", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])

返回结果为:

MongoBulkWriteError: E11000 duplicate key error collection: test.inventory index: _id_ dup key: { _id: 123 }

我们查询操作数据列表:

db.inventory.find({item:"mat3"})

返回结果为:

[
  {
    _id: ObjectId("63589f6277ef9e18649ae298"),
    item: 'mat3',
    qty: 85,
    tags: [ 'gray' ],
    size: { h: 27.9, w: 35.5, uom: 'cm' }
  }
]

表明数据插入成功。其他insert操作可以参考官网。

2. 查询操作(Query)

mongodb提供了基础的查询功能,在执行之前,还是使用上面的inventory的集合来做查询,这里和官网保持一致,还是插入一些测试数据。

db.inventory.insertMany([
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);

2.1 查询所有数据

在mongodb中查询主要通过find()来实现,在find()方法中包含了两个主要的参数:

  • query: 设置数据过滤条件,mongodb主要根据查询条件返回结果集
  • projection: 该参数用于指定返回的属性列表,当不指定时,返回所有的属性信息。
db.inventory.find({})

当传入的query为空体或者不传入的时候,则默认查询所有的数据。这个就相当于sql中的

select * from inventory;

2.2 等值查询

等值查询语法如下:

{field: value, ...}
  • field为查询的字段名称
  • value为需要查询的具体值。

例如我们需要查询status=D的数据,语法如下:

db.inventory.find({status: "D"})

返回的结果值为:

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29c"),
    item: 'paper',
    qty: 100,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'D'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29d"),
    item: 'planner',
    qty: 75,
    size: { h: 22.85, w: 30, uom: 'cm' },
    status: 'D'
  }
]

2.3 操作符查询(operator)

操作符查询语法如下:

{field: {$operator: value}}

在以上示例中,比如我们需要查询status in A, D时,可以写为:

db.inventory.find( { status: { $in: [ "A", "D" ] } } )

这就相当于SQL中的语句为:

select * from inventory where status in ('A', 'D')

返回结果为

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29a"),
    item: 'journal',
    qty: 25,
    size: { h: 14, w: 21, uom: 'cm' },
    status: 'A'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29b"),
    item: 'notebook',
    qty: 50,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'A'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29c"),
    item: 'paper',
    qty: 100,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'D'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29d"),
    item: 'planner',
    qty: 75,
    size: { h: 22.85, w: 30, uom: 'cm' },
    status: 'D'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29e"),
    item: 'postcard',
    qty: 45,
    size: { h: 10, w: 15.25, uom: 'cm' },
    status: 'A'
  }
]

支持操作符列表

比较操作符
操作符 简写 说明
$eq = 按照等值查询数据
$gt > 大于某个值
$gte >= 大于或等于指定值
$in in 查找某个属性等于给定值列表的数据
$lt < 小于指定值
$lte <= 小于或等于指定值
$ne != 查找所有数据属性不等于给定值的数据
$nin not in 查找所有数据属性不在给定值列表内的数据
逻辑操作符
操作符 简写 说明
$and and 查询同时满足多个条件
$not ! 对表达式执行取反操作,查出的数据不满足表达式
$nor nor 对所有表达式执行NOR操作,查询出来的所有数据都不满足查询条件
$or or 对所有表达式执行OR操作,查询出来的数据满足其中一个表达式即可
元素判断(Element)
操作符 简写 说明
$exists exists 获取包含有指定属性的文档数据
$type type 获取属性类型相同的文档数据
计算表达式
操作符 简写 说明
$expr 允许在查询语言中使用归集表达式
$jsonSchema 根据指定的JSON规范验证文档
$mod % 对字段的值执行模运算,并选择具有指定结果的文档。
$regex 选择属性值符合正则表达式的文档数据
$text 执行文本搜索
$where 使用javascript表达式过滤数据
地址坐标(Geospatial)
Name Description
$geoIntersects Selects geometries that intersect with a GeoJSON geometry. The 2dsphere index supports $geoIntersects
$geoWithin Selects geometries within a bounding GeoJSON geometry. The 2dsphere and 2d indexes support $geoWithin
$near Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support $near
$nearSphere Returns geospatial objects in proximity to a point on a sphere. Requires a geospatial index. The 2dsphere and 2d indexes support $nearSphere
Array
Name Description
$all 查询数组中能够满足所有指定元素的文档数据
$elemMatch 查询数组中所有元素满足查询条件的文档数据
$size 查询数据size长度等于查询size的文档数据
Bitwise
Name Description
$bitsAllClear Matches numeric or binary values in which a set of bit positions all have a value of 0.
$bitsAllSet Matches numeric or binary values in which a set of bit positions all have a value of 1.
$bitsAnyClear Matches numeric or binary values in which any bit from a set of bit positions has a value of 0.
$bitsAnySet Matches numeric or binary values in which any bit from a set of bit positions has a value of 1.
Projection Operators
Name Description
$ Projects the first element in an array that matches the query condition.
$elemMatch Projects the first element in an array that matches the specified $elemMatch condition.
$meta Projects the document’s score assigned during $text operation.
$slice Limits the number of elements projected from an array. Supports skip and limit slices.
混杂运算符(Miscellaneous Operators)
Name Description
$comment 为一个查询条件添加一个备注信息
$rand 生成一个0-1的随机数

2.4 AND查询条件

在查询数据的时候,往往根据多个属性过滤数据,mongdb实现AND是将查询体中的所有属性查询按照AND链接并执行查询。

例如查询status=A and qty < 30的数据,我们可以使用以下查询方式:

db.inventory.find( { status: "A", qty: { $lt: 30 } } )

这就相当于SQL

select * from inventory where status - 'A' and qty < 30

执行结果输出为:

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29a"),
    item: 'journal',
    qty: 25,
    size: { h: 14, w: 21, uom: 'cm' },
    status: 'A'
  }
]

2.5 OR查询条件

or查询条件主要为了查询文档数据属性能够满足其中一个条件即可,具体需要用到操作符$or来对条件做拼接。

例如我们需要查询status = A OR qty < 30的文档数据,这对应查询为:

db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )

这就相当于SQL

select * from inventory where status = 'A' or qty < 30

2.6 OR和AND合并查询

OR和AND也可以作为一个条件查询,例如,我们需要查询status=A AND (qty < 30 or item like 'q%'), 这里主要查询status=A并且 qty < 30 或者item以p开头的数据,这对应的查询为:

db.inventory.find( {
     status: "A",
     $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )

这就相当于SQL

select * from inventory where status = 'A' and (qty < 30 or item like 'p%')

对应的结果输出为:

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29a"),
    item: 'journal',
    qty: 25,
    size: { h: 14, w: 21, uom: 'cm' },
    status: 'A'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29e"),
    item: 'postcard',
    qty: 45,
    size: { h: 10, w: 15.25, uom: 'cm' },
    status: 'A'
  }
]

2.7 查询嵌套文档

查询嵌套文档时,也可以使用find()对数据进行过滤,但是嵌套文档本身会存在一些不同,主要包括:

  • 等值匹配对于嵌套文档会有点特殊,不仅要保证值匹配,同时也需要保证查询的顺序一致。
  • 等值匹配时,必须属性数量也需要保持一致,当少一个属性时,此时会导致匹配不到结果,可能达不到预期

例如以下示例:

db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )

查询结果为:

[
  {
    _id: ObjectId("635898ee77ef9e18649ae290"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    size: { h: 14, w: 21, uom: 'cm' }
  },
  {
    _id: '123',
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    size: { h: 14, w: 21, uom: 'cm' }
  },
  {
    _id: ObjectId("63589f6277ef9e18649ae297"),
    item: 'journal1',
    qty: 25,
    tags: [ 'blank', 'red' ],
    size: { h: 14, w: 21, uom: 'cm' }
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29a"),
    item: 'journal',
    qty: 25,
    size: { h: 14, w: 21, uom: 'cm' },
    status: 'A'
  }
]

当我们调整减少一个属性值查询:

db.inventory.find( { size: { h: 14, w: 21} } )

会导致查询结果为空,这里则需要特别注意。

2.7.1 查询嵌套属性(Nested Field)

2.7.1.1 等值查询

嵌套文档运行我们通过嵌套属性方式查询数据,这种方式和我们正常查询保持基本一致,比如我们查询size的属性uom等于in的数据,则查询为:

db.inventory.find( { "size.uom": "in" } )

则对应输出结果为:

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29b"),
    item: 'notebook',
    qty: 50,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'A'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29c"),
    item: 'paper',
    qty: 100,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'D'
  }
]
2.7.1.2 使用查询操作符

嵌套属性查询也可以使用查询操作符,例如查询size中h属性的值小于10的数据,则对应的查询为:

db.inventory.find( { "size.h": { $lt: 10} } )

对应查询结果为;

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29b"),
    item: 'notebook',
    qty: 50,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'A'
  },
  {
    _id: ObjectId("6358adad77ef9e18649ae29c"),
    item: 'paper',
    qty: 100,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'D'
  }
]
2.7.2.3 使用AND多条件查询

嵌套查询也可以和基本属性查询一起使用,例如查询size的h属性小于15并且size的uom值为IN并且status等于D的文档数据,则对应的查询为:

db.inventory.find( { "size.h": { $lt: 15 }, "size.uom": "in", status: "D" } )

则对应的查询结果为:

[
  {
    _id: ObjectId("6358adad77ef9e18649ae29c"),
    item: 'paper',
    qty: 100,
    size: { h: 8.5, w: 11, uom: 'in' },
    status: 'D'
  }
]

2.7.2 查询嵌套文档(Nested Document)

在上面的嵌套属性的查询时,主要查询的数组具体值,这里主要是当数组中存储的是文档数据时,一些查询操作。这里还是以官方例子作为熟悉数据。

db.inventory.insertMany( [
   { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
   { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
   { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
   { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
2.7.2.1 等值查询数组

和嵌套属性方式一样,嵌套文档也可以等值查询文档数据,这里的等值查询也有一下限制:

  • 属性顺序和文档数据属性保持一致
  • 查询值类型必须要和文档数据值类型一致
db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )

对应的返回结果为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  }
]

当我们交换查询属性的顺序时,就会导致查询结果为空:

db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )
2.7.2.2 针对嵌套数组中某个属性查询

mongodb可以在查询的时候指定数组中文档单个或者多个属性过滤文档数据。

2.7.2.2.1 单个属性查询

当需要真多单个属性过滤文档时,需要用到.的语法,具体如下:

db.inventory.find( { 'instock.qty': { $lte: 20 } } )

这里查询instock中qty值小于等于20的文档数据

具体返回结果为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a5"),
    item: 'notebook',
    instock: [ { warehouse: 'C', qty: 5 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a6"),
    item: 'paper',
    instock: [ { warehouse: 'A', qty: 60 }, { warehouse: 'B', qty: 15 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a7"),
    item: 'planner',
    instock: [ { warehouse: 'A', qty: 40 }, { warehouse: 'B', qty: 5 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a8"),
    item: 'postcard',
    instock: [ { warehouse: 'B', qty: 15 }, { warehouse: 'C', qty: 35 } ]
  }
]
2.7.2.2.2 按照数组索引查询

比如查询数组中第一个文档数据的属性值,则对应查询为:

db.inventory.find( { 'instock.0.qty': { $lte: 20 } } )

则返回数据为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a5"),
    item: 'notebook',
    instock: [ { warehouse: 'C', qty: 5 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a8"),
    item: 'postcard',
    instock: [ { warehouse: 'B', qty: 15 }, { warehouse: 'C', qty: 35 } ]
  }
]

当我们使用嵌套属性或者索引的方式过滤数据时,必须将对应的key用引号包裹起来

2.7.2.3 多条件查询

在查询的时候不仅能够指定单个条件,也可以指定多条件过滤文档数据。

2.7.2.3.1 单个嵌套文档满足多条件查询

在多条件查询的时候,可以使用$elemMatch操作符链接多个查询条件,则对应语义为至少有一个嵌套文档满足条件接口作为数据返回。

例如查询instock中qty等于5并且warehouse等于A的文档数据,则查询为:

db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } )

则返回结果为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  }
]

同时在查询条件时,也是可以指定多个条件查询,例如在以上基础上修改为instock中qty大于5小于等于20并且warehouse等于A的文档,则对应的查询条件为:

db.inventory.find( { "instock": { $elemMatch: { qty: { $gte: 5, $lte: 20 }, warehouse: "A" } } } )

则返回数据为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  }
]
2.7.2.3.2 组合查询条件

上面演示中为单个属性的查询写法,我们也可以不适用$elemMatch操作符来组合多个过滤条件,当不使用$elemMatch操作符时,对应的语义稍微发生了一点变化

例如我们查询instock中qty大于10小于等于20等文档数据,则对应的查询为;

db.inventory.find( { "instock.qty": { $gt: 10,  $lte: 20 } } )

则对应的响应数据为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a6"),
    item: 'paper',
    instock: [ { warehouse: 'A', qty: 60 }, { warehouse: 'B', qty: 15 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a7"),
    item: 'planner',
    instock: [ { warehouse: 'A', qty: 40 }, { warehouse: 'B', qty: 5 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a8"),
    item: 'postcard',
    instock: [ { warehouse: 'B', qty: 15 }, { warehouse: 'C', qty: 35 } ]
  }
]

只需要在instock嵌套文档中有任何一个嵌套文档满足条件就可以作为返回数据。

同时我们也可以在查询体中写入多个查询嵌套属性过滤文档数,例如:

db.inventory.find( { "instock.qty": 5, "instock.warehouse": "A" } )

则对应返回结果为:

[
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a4"),
    item: 'journal',
    instock: [ { warehouse: 'A', qty: 5 }, { warehouse: 'C', qty: 15 } ]
  },
  {
    _id: ObjectId("6359e8a377ef9e18649ae2a7"),
    item: 'planner',
    instock: [ { warehouse: 'A', qty: 40 }, { warehouse: 'B', qty: 5 } ]
  }
]

通过以上返回结果我们得出以上结论:

  • 第一个条件表达了至少有一个文档满足qty=5, 当满足第一个条件后,则就可以作为返回数据
  • 第二个条件并不是必须的,也就是说第二个条件是可以不用满足。这两者不是AND的关系

2.8 数组查询

当文档数据中包含了数组时,这是我们希望通过数组中的元素的值过滤文档数据,这时数组查询的支持就比较重要,这里我们还是以官方给的实例做演示。

在开始前,我们先插入测试数据,便于在后面操作。

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 ] }
]);

2.8.1 精确匹配数组

精确匹配数组是在执行查询时候,过滤值为数组,当没有使用操作符的时候,mongodb过滤数据主要包含两点:

  • 对应属性值数据顺序与查询数组值一致
  • 对应属性值与查询数据值一一相等

例如查询tags的值为blank 和red的文档数据,则对应的查询为:

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

则对应的输出结果为:

 {
    _id: ObjectId("6358edea77ef9e18649ae2a2"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }

此时,如果在匹配的时候,没有顺序的严格要求,这是我们可以使用$all操作符实现:

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

这时的查询出的结果会稍有些变化:

{
    _id: ObjectId("6358edea77ef9e18649ae2a0"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a1"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a2"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
  • 查询出的结果没有严格的查询顺序
  • 也查询出了超过两个tags的值的数据

因此这里的$all操作符的使用,可以定义为只要tags包含了blank, red两个属性值,都应当作为结果返回.

2.8.2 数组单个元素匹配

数组中也能够根据当个值匹配,具体语义为:只要数组中某一个元素满足条件,就作为结果返回。

例如,查询tags中包含red的文档数据,则对应查询为:

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

则对应的返回结果为:

{
    _id: ObjectId("6358edea77ef9e18649ae2a0"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a1"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a2"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }

同时单个元素的匹配也能够使用操作符实现,例如查询dim_cm的值大于25的文档数据,则对应的查询为:

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

则对应的查询结果为:

[
  {
    _id: ObjectId("6358edea77ef9e18649ae2a2"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]

2.8.3 数组元素多条件查询

数组元素多条件查询可以在过滤是指定多个条件,当数组元素满足的文档数据作为返回。

2.8.3.1 组合过滤数组元素

例如查询dim_cm大于15小于20的文档数据,则对应查询为:

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

返回结果为:

[
  {
    _id: ObjectId("6358edea77ef9e18649ae29f"),
    item: 'journal',
    qty: 25,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a0"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a1"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  },
  {
    _id: ObjectId("6358edea77ef9e18649ae2a3"),
    item: 'postcard',
    qty: 45,
    tags: [ 'blue' ],
    dim_cm: [ 10, 15.25 ]
  }
]

通过返回结果可以看出,上面的查询条件其实是一个或的关系,只要有其中一个元素满足其中一个条件即可。

2.8.3.2 多条件过滤数组元素

多条件过滤数组元素,可以使用操作符限制元素至少有一个满足所有条件的文档数据就返回,这里可以使用$elemMatch实现多条件的逻辑。

例如查询dim_cm满足大于15并且小于20的文档数据,这查询为:

db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

则返回结果为:

[
  {
    _id: ObjectId("6358edea77ef9e18649ae2a2"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]
2.8.3.3 索引过滤数组元素

在mongodb中可以根据索引号来过滤指定文档数据,例如查询dim_cm索引为1的值大于25的数据,则对应查询为:

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

则查询结果为

[
  {
    _id: ObjectId("6358edea77ef9e18649ae2a2"),
    item: 'planner',
    qty: 75,
    tags: [ 'blank', 'red' ],
    dim_cm: [ 22.85, 30 ]
  }
]
2.8.3.4 数组长度过滤

通过$size操作符过滤指定长度的文档数组,例如查询tags长度为3的文档数据

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

则返回结果数据为:

[
  {
    _id: ObjectId("6358edea77ef9e18649ae2a1"),
    item: 'paper',
    qty: 100,
    tags: [ 'red', 'blank', 'plain' ],
    dim_cm: [ 14, 21 ]
  }
]

2.9 查询指定返回字段属性

在默认情况下,mongodb查询都是返回所有的字段,为了限制返回的文档数据,可以通过projection限制返回的属性列表。

这章节还是以官网给定的例子学习projection的基础用法。一下为实验的数据:

db.inventory.insertMany( [
  { item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
  { item: "notebook", status: "A",  size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
  { item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
  { item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
  { item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);

2.9.1 返回所有字段

返回所有字段属性的用法就是不指定projection参数,例如查询status=A的所有文档的所有属性,则查询为:

db.inventory.find( { status: "A" } )

这就相当于sql

select * from inventory where status = 'A'

2.9.2 查询指定属性和_id属性

在查询结果中可以指定查询属性列表,在projection配置中,只需要将对应的属性配置为1即可,具体语法为: {field: 1}.

例如我需要返回status=A的item和status属性, 则对应的查询为:

db.inventory.find( { status: "A" }, { item: 1, status: 1 } )

这个查询就相当于SQL

select _id, item, status from inventory where status = 'A'

当没有默认配置不返回_id属性时,默认会将_id属性一起返回,我们可以通过配置的方式控制不返回_id属性。

不返回_id属性
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id:0 } )

除了_id属性外,不能将排他属性和返回属性混合使用。

2.9.3 排除指定属性

除了指定返回属性之外,也可以通过排除的方式指定那些属性不返回。例如:不返回status和instock属性信息。

db.inventory.find( { status: "A" }, { status: 0, instock: 0, item:1 } )

除了_id属性外,不能将排他属性和返回属性混合使用。

2.9.4 嵌套文档返回指定属性

对于嵌套文档,我么也可以指定返回嵌套文档中的指定属性,这里我们也需要用到.的语法来指向嵌套文档属性。

在下面例子中,主要返回了以下属性:

  • _id属性,该属性默认返回
  • item属性
  • status属性
  • 嵌套文档size中的uom属性

则对应具体查询为:

db.inventory.find(
   { status: "A" },
   { item: 1, status: 1, "size.uom": 1 }
)

从4.4版本开始,嵌套查询也可以使用如下格式:

db.inventory.find(
   { status: "A" },
   { item: 1, status: 1, size: {uom: 1} })

这个查询语句和上面查询语句具有同样的效果。

2.9.5 嵌套文档限制返回属性

跟限制属性一样,嵌套文档属性限制返回,只需要将对应的属性设置为0即可。

例如在查询结果中不返回size下的uom属性, 则对应的查询为:

db.inventory.find(
   { status: "A" },
   { "size.uom": 0 }
)

从4.4版本开始,上面的语法可以写为:

db.inventory.find(
   { status: "A" },
   { size: {uom: 0} }
)

2.9.6 嵌套文档数组返回指定属性

当嵌套文档为数组列表时,也可以指定数组中的文档返回指定属性信息。

例如返回instock中qty属性,则对应的查询为:

db.inventory.find( { status: "A" }, { item: 1, status: 1, "instock.qty": 1 } )

在上面的查询中,主要返回了一下字段信息:

  • _id属性,默认返回
  • item属性
  • status属性
  • instock数组中的qty属性

2.9.7 返回数组指定元素

当嵌套文档为数组元素时,此时我们可以通过$slice操作符获取指定元素的数据。

$slice有几种用法:

  • 当指定单个数字时:
    • 如果指定数字n为正数时,则返回数组中最开始的n个文档
    • 当指定数字n为负数时,则返回数组中最后n个文档
  • 当指定两个数字m,n时,此时意义为跳过m个文档,返回n个文档。

例如查询instock中最后一个文档,则对应的查询为:

db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } )

2.10 查询值为Null或者缺失属性

Mongodb存储数据的格式使用的json, 当后存储json多了属性时,并不会影响之前已经存储的json数据,这是我们需要查询缺失或者值为null的数据时,就会显得很有用处。

2.10.1 判断属性值是否为null

在mongodb中判断属性值是否为null有两种判断方式,第一种就是通过等值判断,另外一种就是判断类型是否为10

在mongodb中类型的存储主要使用BSON type来 表示,10就表示了Null

2.10.1.1 等值判断

等值判断和简单查询基本保持一致, 对应的查询为:

db.inventory.find( { item: null } )
2.10.1.2 类型判断

类型判断则是判断对应的属性是否为指定类型,对应的查询为:

db.inventory.find( { item : { $type: 10 } } )
2.10.2 判断属性是否存在

判断属性是否存在主要可以通过$exists操作符来做判断,具体查询如下:

db.inventory.find( { item : { $exists: false } } )