mongodb是一个文档数据库。
mongo操作
多个修改操作,但每个修改携带的数据包较小,可操作考虑批量操作.bulkWrite()
改善性能。
MongoCollection是线程安全的。
db.coll.find()(shell、java api)之后接.limit .skip .sort不论编码调用顺序如何,执行时都是.sort -> .skip -> .limit。
如果要定义limit, sort, skip的顺序,应该使用.aggregate管道流。
db.createCollection()在coll已存在情况下会出错(java api则抛异常),db.getCollection后接CRUD则会自动创建coll,仅仅db.getCollection(String)数据库不会自动创建。
skip()
不是O(1)时间,是O(n)时间,不建议使用。
cursor
batchSize:
insert
insertOne()若在唯一索引字段上有重复值时插入抛出异常。
query
查询一个字段的值
mongo-shell: find({},{'field':1,'_id':0}),指定返回字段field,_id字段默认返回,若不需要该字段故需显式关闭。
不等于查询$ne
:find({field:{$ne: val}})
区间查询 $gt
, $gte
, $lt
, $lte
db.collection.find({"myNum":{$gt:5, $lt:20}})
查询数组中满足条件的元素且只返回匹配的元素:$elemMatch
匹配元素、".$
"返回仅匹配的元素
.find({'myArray': {$elemMatch: {$gt: 50}}}) //查询数组中元素大于50的元素
.projection({'myArray.$': 1}) //仅返回数组中匹配上的元素
查询文档数组字段中满足条件的最后一个元素:
mongo-shell pretty output(beautiful output) 配置
查询数组字段非空的文档:
有以下数种方式,假设目标字段名为"array"。
$elemMatch
和$ne
:
db.Collection.find({array:{$elemMatch:{$ne:null}}})
$where
+js数组的.length
db.Collection.find({$where:"this.array.length>0"})
$not
和$size
db.Collection.find({array: {$not: {$size: 0}}})
$exists
和'.
'路径试图访问首元素
db.Collection.find({{'array.0': {$exists: 1}}})
$exists
和$ne
db.Collection.find({ array: { $exists: true, $ne: [] } })
$gt
db.Collection.find({ array: { $gt: [] } })
TODO
查询object类型字段的键数量:
匹配object类型字段非空:
update
db.coll.updateOne(filter, update[, options])
db.coll.updateMany(filter, update[, options])
db.coll.replaceOne(filter, update, options)
update时不能在同一字段上有多个operator。即不允许这样的update:
.update({},{$pull:{"entity":'a'}, $addToSet:{entities:'b'}})
.updateOne查找一个数组字段中某个元素更新时,参数提供 {$set:{'arrayField.$':newValue},将会替换原来的值,需注意的是,当元素是json时(提供的newValue新值也是json),操作会直接替换掉原来的json,不是在原文档上添加字段或修改字段值。
涉及数组字段中更新查询到的元素时,在提供了占位符'$'的更新时,不存在upsert:true,因为在
更新/删除字段:
findAndUpdate(query, {"$set", {field1:'val1'...}, "$unset": {"fieldx":"值无所谓"}})
尽管insert方法没返回值,但驱动会自动生成_id(如果没有提供),并put到被插入的Document中。_id会根据时间戳、主机等信息生成。
upsert(search, doc) -> update or insert,这里的search相当于where条件,这比在客户端先search是否存在,然后决定insert还是update高效。
键名(key)可以是任意Unicode字符,但不允许包含点号.和美元符$,可以通过转义的方式存,常见的转义方式是将这两个ascii字符转到Unicode:. -> '\u2024':[․],\uff0e':[.], $ -> '\ufe69':[﹩],'\uff04':[$]。
db.collection.distinct("field") <- select distinct field
update operators:
$pull
{$pull: { : <value | condition> } } 从array字段删除符合条件的元素。
$pullAll
{$pullAll: { : [ value1, val2, ...] }}
从array字段删除操作中提供的多个元素(不是匹配条件),值为数组,不能是单值。
$push
插入(结合$position)或追加(无$position)元素到数组字段,如果追加多个元素(一次性插入多个)需结合$each。
{$push: {:}} {$push: {field: {$each: [,] } }}}
$addToSet
{$addToSet:{: },如果参数值是数组,则会被视为单一元素插入到目标字段(成为内嵌数组);如果想对一组元素都进行$addToSet操作,应将这组元素封装到$each操作:
{
$addToSet: {
"field":{$each:['v1','v2'...]}
}
}
$set
$setOnInsert
set when insert in upsert mode
delete/remove doc
.deleteOne(filter)
.deleteMany(filter)
aggregate
.aggregate([pipe1, pipe2])
$match, $project
count object keys: 利用$objectToArray将对象转为数组,然后利用$size。
.aggregate({$project: {out: {$size: {$objectToArray: "$valueeOfSomeKey"}}}})
数组上的aggregate: $size(aggregate)
例:获取值类型为数组的字段"entities"上数组的长度 .aggregate({$project:{ asNewFieldName: {$size: "$entities"}}),注意,引用字段entities时需要字段名前加$,否则被认为是string常量,导致$size操作失败。
update: { "$update-opterator": {}, "$update-operator":{} }
对于.updateOne(filter, update, opts),当filter是eq(全匹配)且upsert为true时,如果update的文档不存在,则在插入时会自动把filter上的字段-值插入。例: getCollection.updateOne(Filters.eq("nerword", "xxxxxx"), new bson.Document("$addToSet", new bson.Document("entities", "zzz")),new UpdateOptions().upsert(true)),如果不存在该nerword的文档,则插入时会加入nerword,即有新文档{nerword:'xxxxxx', entities:['zzz']}。对于replaceOne
with upsert: ture 在插入时不会添加filter的字段。
update
和replace
在filter指定的文档不存在且upsert:false时无任何操作。
update operator:
$rename
$set
$unset
backup/restore export(dump)/import
导出远程数据库集合
mongoexport -h <host> --port <port> -u <user> -p <pass> -d <db> -c <collection> -o <outputfile.json>
mongoimport -h .. --port .. -u .. -p .. -d .. -c .. --file <data.json>
# import csv
mongoimport -d <db> -c <coll> --type csv --headerline (first line is headerline, as fields) --ignoreBlanks (ignore blank values) --file <FILE>
数据库database
show dbs # 列举db
use <db> # 切换到db,如果不存在则在需要时创建
db # show current db
#use之后内置变量db指向当前的database。
use <db> # create <db>
db.dropDatabase() # drop database
用户user
db.getUsers()
db.createUser(
{
user:'name'
pwd:'password'
roles:[{role:'what-role',db:'which-db'}]
})
db role
show roels
// create role
// drop role
//增加授权,不会先清除之前的授权
db.grantRolesToUser('username',[
{role:'role1',db:'db1'}, {role:'role2',db:'db2'}])
// 收回授权
db.revokeRolesFromUser('username',[{role:'role1',db:'db1'},{role:'role2',db:'db2'}])
index 索引
db.coll.createIndex(keys, options)
//如果不存在则创建
db.collection.createIndex(
{<field>: 1/-1, <anotherField>: 1/-1},
<optional>{unique: true, name:"name for index", sparse: true|false}
)
字段有重复时创建唯一索引(unique index)会失败,{unique:true, dropDups:true}在 MOngoDB 3.0后不可取,dropDups选项无效。
数据状态异常
查询集合中文档数量 db..count()(包括.stats())的结果和通过find()结果遍历次数不一致,这是因为mongodb关于此集合的数据状态有异常,需要执行db..invalidate(true)来矫正数据,参考mongodb问题 count() incorrect。数据有误时可考虑用db..count({_id:{$exists:true}})代替count()。
mongo java driver
结论一般针对3.4.2版本
类型为Array[?]的字段值不能被解码,要为Collection[?]才行。例如,String[] x,则new Document("key",x)时,x存储会识别,因无法找到[Ljava.lang.String的codec,而存储转为List的x(x.asList)则能成功。
insertMany之前先判断插入的集合是否为空,因为插入空集合会引发异常(╮(╯▽╰)╭)
mongo-java-drive 3+
collection.updateOne(Filter.eq("id-or-other-field","xxxxxx"), new Document("$set", Document.parse(json: {timstamps: new Date, xx:{...})
updateOne vs. findOneAndUpdate 后者会返回更新前的值,前者不会。
插入整数时一定注意区分是int还是long型,分别与mongodb的Int32、Int64对应,如果是后者,用java api Document.getInteger会出异常,而往往直接用该方法取整数型数据。
无需auth信息的serve不能new 带credential的MongoClient构造器(MongoClient(ServerAddress, List[MongoCredential])。
【添加codec】
MongoClientOptions.builder().codecRegistry(CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new LocalDateTimeCodec()), MongoClient.getDefaultCodecRegistry()))
new Document(Map<String,Object>)(如从一个json构建)并不会将深层Map<String,Object>转为Document,因此使得插入时转码失败(code错误)。从json到document的一种正确方式是Document.parse(String json)。
mongodb python driver
package dependency: pymongo
from pymongo import MongoClient
cursor = MongoClient(host,port)['db']['coll'].find({key:val, k2:val2}, projection=[(key:1),(key2:1)])
try:
while True:
cursor.next()
except StopIteration:
# reach end of stream
finally:
cursor.close()
mongodb scala driver
dependency: org.mongodb.scala:mongo-scala-driver_2.11 or _2.12
SHIT!!!!! HOLY SHIT !!!!!!!! also mongodb casbah !!!!! SHIT !!!!!!!! 2018.3.22
mongodb spark connector
org.mongodb.spark:mongo-spark-connector_2.11:2.2.1
val sc = new SparkContext(new SparkConf().setAppName("myapp")
.set("spark.mongodb.input.uri", "mongodb://user:pass@host:port/db.collection"))
val rdd = MongoSpark.load(sc)
// 官网建议使用withPipeline(而非RDD.filter),不但能使数据在返回到rdd前过滤,还能在没有数据匹配时不引起异常,否则RDD.filter会导致NPE。
val aggregatedRdd = rdd.withPipeline(Seq(Document.parse("""{$match:{}""")))
如果不希望通过uri传入用户名密码认证信息,需要自定义MongoClientFactory,作为MongoConnector的构造参数,然后将MongoConnector作为MongoSpark.Builder的connector创建MongoSpark,之后调用toRdd获得MongoRDD。(注意这是版本spark-connector_2.11:2.2.1。
FAQ
sort排序失败:
Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit。
解决:在文档量过大时排序字段需建索引。org.mongodb:mongodb-driver-core:3.6.3与org.mongodb.spark:mongo-spark-connector_2.11:2.2.1存在兼容问题,可能导致NPE。
安装mongodb server、client
centos:
vim /etc/yum.repo.d/mongo.repo
填入内容:
name=MongoDB Repository
baseurl=http://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.0/x86_64/
gpgcheck=0
enabled=1
默认配置文件为/etc/mongod.conf,服务器绑定的网络接口配置在bindIp:上,默认为0.0.0.0,如果只想为本机服务,则配置为127.0.0.1,如果配置为局域网ip(如10.36.124.131)则只能在lan接口上服务,本机(127.0.0.1)没有服务,如果要在本机及lan接口上服务则用逗号连接两个ip,逗号后不得有空格(bindIp: 127.0.0.1,10.36.124.131)。
手动安装:
下载对系统版本的server安装包,
Windows系统下将mongodb安装为服务的命令:
mongod --config "C:\Program Files\MongoDB\Server\3.4\conf\mongo.conf" --serviceName MongoDB --install
其中mongo.conf由自己创建,window版无默认配置文件,配置文件中必须包含dbpath和logpath,dbpath文件夹需提前创建,否则mongod无法成功启动。
mongodb由MongoDB,Inc开发,公司曾用名“10gen”。
mongodb doc离线版下载,原提供pdf格式,现提供epub格式。
(待丰富)