mongodb是开源世界一颗冉冉升起的明星,是一个文档型的数据库,允许数据嵌套存储。它的设计目标是:高可扩展性、高性能、便捷的数据访问。
这里不打算对安装做陈述,具体可参看官网。
mongo book(命令行):连接book数据库,但这时book数据库还不存在,直到我们向次数据库中写入第一条数据。
use book(mongo shell):切换到book数据库
插入数据:
- > db.towns.insert({
- name: "New York",
- population: 22200000,
- last_census: ISODate("2009-07-31"), famous_for: [ "statue of liberty", "food" ],
- mayor : {
- name : "Michael Bloomberg",
- party : "I" }
- })
显示当前数据库中的所有collection:
- > show collections
- system.indexes
- towns
查询collection中所有记录:
- > db.towns.find()
- {
- "_id" : ObjectId("4d0ad975bb30773266f39fe3"),
- "name" : "New York",
- "population": 22200000,
- "last_census": "Fri Jul 31 2009 00:00:00 GMT-0700 (PDT)",
- "famous_for" : [ "statue of liberty", "food" ],
- "mayor" : { "name" : "Michael Bloomberg", "party" : "I" }
- }
_id这个域是系统为我们自动生成的,有点类似于postresql中的serial increment |
查看数据类型:
- > typeof db
- object
- > typeof db.towns
- object
- > typeof db.towns.insert
- function
执行命令时不加()可以查看函数的源码:
- db.towns.insert
- function (obj, _allow_dot) {
- if (!obj) {
- throw "no object passed to insert!";
- }
- if (!_allow_dot) {
- this._validateForStorage(obj);
- }
- if (typeof obj._id == "undefined") { var tmp = obj;
- obj = {_id:new ObjectId}; for (var key in tmp) {
- obj[key] = tmp[key];
- }
- }
- this._mongo.insert(this._fullName, obj);
- this._lastID = obj._id;
- }
我们可以自定义类javascript式的函数,
- mongo/insert_city.js
- function insertCity(
- name, population, last_census, famous_for, mayor_info
- ){ db.towns.insert({
- name:name,
- population:population,
- last_census: ISODate(last_census),
- famous_for:famous_for,
- mayor : mayor_info
- });
- }
然后使用其操作:
- insertCity("Punxsutawney", 6200, '2008-31-01',
- ["phil the groundhog"], { name : "Jim Wehrle" }
- )
- insertCity("Portland", 582000, '2007-20-09',
- ["beer", "food"], { name : "Sam Adams", party : "D" }
- )
根据某个字段查询:
- db.towns.find({ "_id" : ObjectId("4d0ada1fbb30773266f39fe4") })
- {
- "_id" : ObjectId("4d0ada1fbb30773266f39fe4"),
- "name" : "Punxsutawney",
- "population" : 6200,
- "last_census" : "Thu Jan 31 2008 00:00:00 GMT-0800 (PST)",
- "famous_for" : [ "phil the groundhog" ],
- "mayor" : { "name" : "Jim Wehrle" }
- }
只查询出特定字段:
- db.towns.find({ _id : ObjectId("4d0ada1fbb30773266f39fe4") }, { name : 1 })
- {
- "_id" : ObjectId("4d0ada1fbb30773266f39fe4"),
- "name" : "Punxsutawney"
- }
查询出除了name以外的其它字段:
- db.towns.find({ _id : ObjectId("4d0ada1fbb30773266f39fe4") }, { name : 0 })
- {
- "_id" : ObjectId("4d0ada1fbb30773266f39fe4"),
- "population" : 6200,
- "last_census" : "Thu Jan 31 2008 00:00:00 GMT-0800 (PST)",
- "famous_for" : [ "phil the groundhog" ]
- }
使用正则表达式匹配查询、范围运算符查询:
- db.towns.find(
- { name : /^P/, population : { $lt : 10000 } },
- { name : 1, population : 1 }
- )
- { "name" : "Punxsutawney", "population" : 6200 }
查询条件操作符的格式如下:
- field : { $op : value }
使用javascript使得我们可以像操作对象一样来构造查询:
- var population_range = {}
- population_range['$lt'] = 1000000
- population_range['$gt'] = 10000
- db.towns.find(
- { name : /^P/, population : population_range },
- { name: 1 } )
- { "_id" : ObjectId("4d0ada87bb30773266f39fe5"), "name" : "Portland" }
再看一个使用运算符的例子:
- db.towns.find(
- { last_census : { $lte : ISODate('2008-31-01') } },
- { _id : 0, name: 1 }
- )
- { "name" : "Punxsutawney" }
- { "name" : "Portland" }
嵌套的子文档的查询(使用.):
- db.towns.find(
- { 'mayor.party' : 'I' },
- { _id : 0, name : 1, mayor : 1 }
- )
- {
- "name" : "New York",
- "mayor" : {
- "name" : "Michael Bloomberg",
- "party" : "I"
- }
- }
再插入一些数据:
- db.countries.insert({
- _id : "us",
- name : "United States", exports : {
- foods : [
- { name : "bacon", tasty : true }, { name : "burgers" }
- ] }
- })
- db.countries.insert({
- _id : "ca",
- name : "Canada", exports : {
- "name" : "New York", "mayor" : {
- "name" : "Michael Bloomberg",
- "party" : "I" }
- foods : [
- { name : "bacon", tasty : false },
- { name : "syrup", tasty : true } ]
- }
- })
- db.countries.insert({
- _id : "mx",
- name : "Mexico", exports : {
- foods : [{
- name : "salsa", tasty : true, condiment : true
- }] }
- })
统计有多少条数据:
- > print( db.countries.count() )
- 3
现在我们想查找出这样一个国家:出口bacon、又是出口tasty的bacon,先看下面的例子:
- db.countries.find(
- { 'exports.foods.name' : 'bacon', 'exports.foods.tasty' : true },
- { _id : 0, name : 1 }
- )
- { "name" : "United States" }
- { "name" : "Canada" }
但是这不是我们想要的,我们想要的是同一个document,这时可以使用$elemMatch运算符:
- db.countries.find(
- {
- 'exports.foods' : {
- $elemMatch : {
- name : 'bacon',
- tasty : true }
- }
- },
- { _id : 0, name : 1 }
- )
- { "name" : "United States" }
逻辑运算:
- db.countries.find(
- {
- $or : [
- { _id : "mx" },
- { name : "United States" }
- ]
- },
- { _id:1 }
- )
- { "_id" : "us" } { "_id" : "mx" }
注:逻辑运算符的value是个数组
其它运算符:
- Command Description
- $regex Match by any PCRE-compliant regular expression string (or just use the // delimiters as shown earlier)
- $ne Not equal to
- $lt Less than
- $lte Less than or equal to
- $gt Greater than
- $gte Greater than or equal to
- $exists Check for the existence of a field
- $all Match all elements in an array
- $in Match any elements in an array
- $nin Does not match any elements in an array
- $elemMatch Match all fields in an array of nested documents
- $or or
- $nor Not or
- $size Match array of given size
- $mod Modulus
- $type Match if field is a given datatype
- $not Negate the given operator check
更新一条document:
- db.towns.update(
- { _id : ObjectId("4d0ada87bb30773266f39fe5") },
- { $set : { "state" : "OR" } }
- );
这会向此_id的文档中添加列state并将其值置为"OR",
update(criteria, operation):前一个参数为查询条件,后一个为向查询出的记录所做之操作
请注意不要向下面这样用:
- db.towns.update(
- { _id : ObjectId("4d0ada87bb30773266f39fe5") }, { state : "OR" }
- );
这会将匹配记录的整条文档替换成{state : "OR"},而不是添加一个field,这通常不是我们想要看到的
再看一个更新的例子:
- db.towns.update(
- { _id : ObjectId("4d0ada87bb30773266f39fe5") }, { $inc : { population : 1000} }
- )
更新时可用的操作符:
- Command Description
- $set Sets the given field with the given value
- $unset Removes the field
- $inc Adds the given field by the given number
- $pop Removes the last (or first) element from an array
- $push Adds the value to an array
- $pushAll Adds all values to an array
- $addToSet Similar to push, but won’t duplicate values
- $pull Removes matching value from an array
- $pullAll Removes all matching values from an array
引用:
由于mongo的设计目的,join注定在mongo中是性能非常低下的,但有时你可能确实需要它。
这时你可以使用这样的结构:{$ref : "collection_name", $id : "reference_id"}
- db.towns.update(
- { _id : ObjectId("4d0ada87bb30773266f39fe5") },
- { $set : { country: { $ref: "countries", $id: "us" } } }
- )
现在我们可从towns中查询出对应的国家:
- var portland = db.towns.findOne({ _id : ObjectId("4d0ada87bb30773266f39fe5") })
- db.countries.findOne({ _id: portland.country.$id })
删除:与find类似,只要将find()函数替换成remove()函数即可,有一点需要注意:删除时会删除掉所有匹配的文档包括子文档。
- var bad_bacon = { 'exports.foods' : {
- $elemMatch : { name : 'bacon', tasty : false
- } }
- }
- db.countries.find( bad_bacon )
- {
- "_id" : ObjectId("4d0b7b84bb30773266f39fef"), "name" : "Canada",
- "exports" : {
- "foods" : [ {
- "name" : "bacon",
- "tasty" : false
- },
- {
- "name" : "syrup", "tasty" : true
- } ]
- } }
- db.countries.remove( bad_bacon )
- db.countries.count()
- 2
再看几个关于查询的例子(尽量少用这种自定义的查询,性能较差):
- db.towns.find( function() {
- return this.population > 6000 && this.population < 600000;
- })
- db.towns.find("this.population > 6000 && this.population < 600000")
- db.towns.find( {
- $where : "this.population > 6000 && this.population < 600000", famous_for : /groundhog/
- })
再插入些数据:
- populatePhones = function(area,start,stop) { for(var i=start; i < stop; i++) {
- var country = 1 + ((Math.random() * 8) << 0); var num = (country * 1e10) + (area * 1e7) + i; db.phones.insert({
- _id: num,
- components: {
- country: country,
- area: area,
- prefix: (i * 1e-4) << 0,
- number: i
- },
- display: "+" + country + " " + area + "-" + i });
- } }
- populatePhones( 800, 5550000, 5650000 )
- db.phones.find().limit(2)
- { "_id" : 18005550000, "components" : { "country" : 1, "area" : 800, "prefix" : 555, "number" : 5550000 }, "display" : "+1 800-5550000" }
- { "_id" : 88005550001, "components" : { "country" : 8, "area" : 800, "prefix" : 555, "number" : 5550001 }, "display" : "+8 800-5550001" }
查看数据库中的索引:
- db.system.indexes.find()
- { "name" : "_id_", "ns" : "book.phones", "key" : { "_id" : 1 } }
使用explain()函数查看执行计划:
- db.phones.find({display: "+1 800-5650001"}).explain() {
- "cursor" : "BasicCursor", "nscanned" : 109999, "nscannedObjects" : 109999, "n" : 1,
- "millis" : 52,
- "indexBounds" : {
- }
- }
创建索引:
- db.phones.ensureIndex(
- { display : 1 },
- { unique : true, dropDups : true }
- )
再次执行查询计划,看看两次的nscanned的区别:
- db.phones.find({ display: "+1 800-5650001" }).explain()
- {
- "cursor" : "BtreeCursor display_1", "nscanned" : 1,
- "nscannedObjects" : 1,
- "n" : 1,
- "millis" : 0,
- "indexBounds" : {
- "display" : [
- [
- "+1 800-5650001",
- "+1 800-5650001"
- ] ]
- } }
查看执行较慢的query:
- db.system.profile.find()
- {
- "ts" : ISODate("2011-12-05T19:26:40.310Z"), "op" : "query",
- "ns" : "book.phones",
- "query" : { "display" : "+1 800-5650001" }, "responseLength" : 146,
- "millis" : 0,
- "client" : "127.0.0.1", "user" : ""
- }
profile中默认是记录超过100ms的查询,可以通过db.setProfilingLevel(2)来设置记录的级别
建立索引通常是比较耗时的,建议在quiet time进行,最好在后台执行,如下:
- db.phones.ensureIndex({ "components.area": 1 }, { background : 1 })
查看某一个数据库的所有索引:
- db.system.indexes.find({ "ns" : "book.phones" }) {
- "name" : "_id_",
- "ns" : "book.phones", "key" : { "_id" : 1 }
- }
- {
- "_id" : ObjectId("4d2c96d1df18c2494fa3061c"), "ns" : "book.phones",
- "key" : { "display" : 1 },
- "name" : "display_1",
- "unique" : true,
- "dropDups" : true
- }
- {
- "_id" : ObjectId("4d2c982bdf18c2494fa3061d"), "ns" : "book.phones",
- "key" : { "components.area" : 1 },
- "name" : "components.area_1"
- }
集合查询:像关系型数据库一样,mongodb也为我们提供了丰富的结合查询函数
统计文档个数:
- db.phones.count({'components.number': { $gt : 5599999 } })
- 50000
distinct(): 去重
- db.phones.distinct('components.number', {'components.number': { $lt : 5550005 } })
- [ 5550000, 5550001, 5550002, 5550003, 5550004 ]
group():分组
- db.phones.group({
- initial: { count:0 },
- reduce: function(phone, output) { output.count++; }, cond: { 'components.number': { $gt : 5599999 } }, key: { 'components.area' : true }
- })
- [ { "800" : 50000, "855" : 50000 } ]
服务端命令:通常我们执行的这些命令都是在客户端执行的,mongodb为我们提供了一些在服务端执行命令的方式,以提高性能
eval:
- update_area = function() { db.phones.find().forEach(
- function(phone) { phone.components.area++; phone.display = "+"+
- phone.components.country+" "+ phone.components.area+"-"+ phone.components.number;
- db.phone.update({ _id : phone._id }, phone, false);
- }
- ) }
- > db.eval(update_area)
有一点需要注意的是:eval会阻塞mongod,所以不要太过频繁的执行,而且所执行的操作一定要快速能够执行完毕。
runCommand:
- > use admin
- > db.runCommand("top")
- > db.runCommand({ "count" : "phones" }) { "n" : 100000, "ok" : 1 }