假设有一份商品结构的表
{
"id":"r24EIHYBZCX0L6qWIrDf",
"name":"product-1",
"price":123,
"detail":"<html><body>hello world</body></html>",
"sku":[
{
"id":"r14EIHYBZCX0L6qWIrDf",
"inventory":123
}
],
"tag":[
"red",
"black"
]
}
1.1 插入数据
db.collection.insertOne()插入单条数据,其中collection是变量,代表你希望往哪个集合插入数据。
db.product.insertOne(
{
"id":"r24EIHYBZCX0L6qWIrDf",
"name":"product-1",
"price":123,
"detail":"<html><body>hello world</body></html>",
"sku":[
{
"id":"r14EIHYBZCX0L6qWIrDf",
"inventory":123
}
],
"tag":[
"red",
"black"
]
})
//返回
{
"acknowledged" : true,
"insertedId" : ObjectId("5ffa68feda5b1dccc637c4a9")
}
db.collection.insertMany(),插入多条数据,需要传插入文档的数组
db.product.insertMany([
{
"id":"r24EIHYBZCX016qWIrDf",
"name":"product-2",
...//省略
},{
"id":"r24EIHYBZC40L6qWIrDf",
"name":"product-3",
...
}])
//返回
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5ffa6bc39bb4925656ccad86"),
ObjectId("5ffa6bc39bb4925656ccad87")
]
}
1.2 查找数据
db.collection.findOne(query, projection),其中query与projection都是json文档类型
1.2.1简单查询
简单的数据匹配,只需要query按{ <field1>: <value1>, ... }格式去写就可以了,多个field是and条件
//查询一条字段“name”为"product-1"的数据
db.product.findOne({
"name":"product-1"
})
//查询一条字段“name”为"product-1"且"price"为123的数据
db.product.findOne({
"name":"product-1",
"price":123
})
1.2.2 或查询
$in
操作符,格式:{ field: { $in: [<value1>, <value2>, ... <valueN> ] } },可以查找同一个字段的多个值条件,当数组中只有一个值时,相当于=
//查找price为100,200,300的数据
db.product.find({
"price":{
"$in":[100,200,300]
}
})
$or
操作符,格式:{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] },expression为查询表达式
//查询要么price为100或者name为“product-1"的数据
db.product.find({
"$or":[
{"price":100},
{"name":"product-1"}
]
})
$in
,$or
区别主要有:
1,$in
作用于具体的一个字段,$or
作用于多个expression
2,$in
只需要执行一次搜索,$or
根据expression的数量执行多个搜索,然后合并,会更耗性能
3,$in
实际上是一种范围比较查询
1.2.3 数组查询
1.2.3.1 $all
查询
mongo中数组查询相对比较特殊,假设表中有3条数据,第一条与第二条tag值一样,但顺序不一样
db.product.insertMany([
{
...
"tag":[
"red",
"black"
],
dim_cm: [ 14, 21 ]
},{
...
"tag":[
"black",
"red"
],
dim_cm: [ 22.85, 30 ]
},{
...
"tag":[
"yellow",
"red"
],
dim_cm: [ 10, 15.25 ]
}])
如果我们按前面说的查询方式查找下数据
db.product.find( { tags: ["red", "blank"] } )
这样只会返回第一条记录,第二条记录不会返回。这种方式的查询需要数据里面的值与顺序都匹配才认为是符合查询条件的,那如果我只需要查找数组里面的值匹配,不关心存放顺序,那么就要按下面的这种方式查询
db.product.find( { tags: { $all: ["red", "blank"] } } )
1.2.3.2 数组范围查询
如果我们想要查找dim_cm大于15小于20的数据,想当然会这样子写:
db.product.find( { dim_cm: { $gt: 15, $lt: 20 } } )
你会发现查回来的数据包含了dim_cm: [ 10, 15.25 ]
这条数据,实际上这样搜索,查找时mongo用15.25
满足$gt: 15
而10
满足$lt: 20
条件,得到了错误的查询结果,正确的查询方式需要用到$elemMatch
操作符
db.inventory.find( { dim_cm: { $elemMatch: { $gt: 15, $lt: 20 } } } )
至于为什么会这样子,估计是mongo把数组里面的元素当做一个整体看待,只要整体满足条件即可,不需要每个元素满足所有条件
1.2.4 projection
很多时候我们并不想要把文档中的所有字段返回回来,例如商品表中的detail字段,一般都比较大,多个商品查询也返回时会对网络io及服务器内存分配都造成不必要的压力,这时候我们就需要用到projection
例如我们不返回商品detail,以及mongo自带的_id
db.product.find({},{"_id":0,"detail":0})
1.3 更新数据
常见的更新方法:
db.collection.updateOne()
db.collection.updateMany()
db.collection.replaceOne()
个人感觉db.collection.replaceOne是覆盖原有文档,实际上update的操作也能做到,但是replaceOne只允许覆盖操作,不能带有update operators expressions,另外从功能语义上update不要做覆盖操作
例如我想把name为"product-1"的数据的price改成200,需要用到$set
操作符
db.product.updateOne({"name":"product-1"},{"$set":{"price":200}})
如果我们没有使用$set
操作符,相当于覆盖
/*
会将
{
"id":***,
"name":"product-1",
....
}文档变成
{
"price":200
}
*/
db.product.updateOne({"name":"product-1"},{"price":200})
$set
更新或创建文档field,语法:{ $set: { <field1>: <value1>, ... } }
$unset
删除文档field,语法:{ $unset: { <field1>: "", ... } }
1.4 删除数据
常见的更新方法:
db.collection.deleteOne()
db.collection.deleteMany()
db.collection.remove()
remove与delete最大的区别在于delete会返回acknowledged字段
db.collection.deleteMany({})
会删除所有数据,时间要根据数据量,更快的删除考虑用db.collection.drop()
,会删掉整个集合数据包括集合本身信息如index
更多方式删除
1.5 特殊场景操作
1.5.1 update参数upsert作用
update的完整参数如下:
db.collection.updateOne(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
upsert的含义,数据表中能查到就更新,没有就插入,例如下面这个场景:
假如有个页面统计次数的功能
// 检查这个页面是否有一个文档
blog = db.analytics.findOne({url : "/blog"}) // 如果有,就将视图数加/并保存
if (blog) {
blog.pageviews++; db.analytics.save(blog);
}
// 否则为这个页面创建一个新文档
else {
db.analytics.save({url : "/blog", pageviews : 1})
}
这种写法有2个问题:1,复杂,2,多线程会存在竞态条件
采用upsert可以解决这2个问题
db.analytics.update({"url" : "/blog"}, {"$inc" : {"pageviews" : 1}}, true)
1.5.2 findAndModify
db.collection.findAndModify
这个方法的作用是修改符合条件的文档,并将文档修改前的数据返回回来,这对于操作队列以及执行其他需要进行原子性取 值和赋值的操作来说,十分方便。
例如存在队列结构
{ "_id" : ObjectId(), "status" : state, "priority" : N }
/*
将任务按优先级排列,并将排在第一个的状态修改为RUNNING,
返回的数据可以认为被当前线程独占了,后续操作不会发生竞争关系了
*/
ps = db.runCommand({
"findAndModify" : "processes",
"query" : {"status" : "READY"},
"sort" : {"priority" : -1},
"update" : {"$set" : {"status" : "RUNNING"}}}).value
do_something(ps)
db.process.update({"_id" : ps._id}, {"$set" : {"status" : "DONE"}})
网友评论