前言:
前幾天看中視60分鐘談雲端談大數據
使己想找找去年作的mongodb筆記
竟没有在記憶中的目錄
一度以為是因年初換硬碟時 有操作失誤而錯漏
心多跳了一下下, 覺得還是在網上放一份較保險
不過對節目裡那郭中研院的講要求資料100%正確...有點怪怪的(或許是剪輯造成的誤解),
對資料一致性的要求因雲端而從 傳統ACID的強一致 跳到 BASE的軟狀態與最終一致(or弱一致)
在很多情況, 依照使用情形的不同, 在當下取用狀態就是不一致 i.e.非100%正確
例如一些社交性的軟體在某些日常社交資料, 大數據統計某些偏好趨勢等
簡而言之, 資料正確百分百 不算是 雲端資料庫 的必要條件
2016/3/9
-----------------------------------------------------------------------------------
MongoDB - 較適合對資料庫與程設已有基礎者
MongoDB 屬NoSQL(Not only SQL, 雲端常用)中的 文件資料庫
MongoDB的collection 由 文件(documents) 組成, collection 類似 關聯式資料庫的table
雖亦有field(s), 但不為其預先定義schema
如多數 文件資料庫 般支援JSON格式; 亦同時支援二進位的BSON格式
安裝:
urpmi mongodb # 安裝 client
urpmi mongodb-server # 安裝 server
設定:
最原先的mongodb是將資料預設存於 /data/db/
然所用的rpm已改預設至 /var/lib/mongo/ 中 (設定檔 /etc/mongod.conf 內可改預設)
啟動server: <註一>
systemctl start mongod.service # 舊法 service mongod start 或仍可用
預設為單機,故 netstat -antup 有類似下行顯示其中:
tcp 0 0 127.0.0.1:27017 0.0.0.0:* LISTEN 29100/mongod
mongo - MongoDB Shell: mongo is an interactive JavaScript shell interface to MongoDB <ref.1>
啟動 mongo - 於同上已執行server的主機上執行 mongo 進入 MongoDB Shell 來與上面啟動之server交談:
> show dbs // 檢視現有資料庫 '>' 為 MongoDB Shell 的提示符號
> use <資料庫名> // 切換資料庫
> exit // 離開 MongoDB Shell
注意!最初啟動時有行訊息為:connecting to: test 表預設連接至 資料庫名test之資料庫
但用 show dbs 時卻看不到 test資料庫 的存在,這是因為mongodb的資料庫是自動(隱式)建立,
多是當第一次揷入資料物件於collection時, 自動建立資料庫與collection
最初雖然已切換至 test資料庫, 然而由於尙未執行操作,此時 test資料庫 實際上並不存在,
故用 show dbs 自然看不到它; 同理以 use 來切換資料庫時亦然
而collection與index的情況與上類似 e.g.當第一次對collection操作時自動建立collection; index亦然
底下皆在 MongoDB Shell 中操作,一些例參考<ref.2>:
// 於Shell內可用諸如help/db.help()/db.mycoll.help()/db.mycoll.find().help()等等來參考說明
// 當直接輸入省略()的方法名,會出現其方法的原始程式,亦為不錯的參考資訊
> use mydb // 不用預設資料庫test, 切換至 資料庫mydb
switched to db mydb
> j={name:"mongo"} // 建立一資料物件j, 其中field名為name, field值為mongo (<自>亦可視field為資料物件的屬性)
{ "name" : "mongo" }
> k=j // 由於是 JavaScript shell, 故j,t的內涵實則是指向資料物件的指標(pointer), 故 k與j指向同一個資料物件
{ "name" : "mongo" }
> k.name="hahaha" // 將 name屬性 的值 改成hahaha 注意:(資料)物件的屬性 亦即為 資料物件field
hahaha
> j // 因隱含性質是指標(pointer), 故j與k的顯示的結果相同
{ "name" : "hahaha" }
> k.name="mongo" // 改回 "mongo"
> j // 結果一様與k同
{ "name" : "mongo" }
> show dbs // 檢視現有資料庫, 因上面所建資料物件尙未儲存, 故資料庫mydb尙未建立
local 0.078GB // 注意 0.078GB 僅為參考, 會因版本等相異情況而有所不同
> db.things.save(j) // 將j的資料物件插入至collection things中
> show dbs // 由此可知,上一指令除建立collection things外,亦自動建立 資料庫mydb
local 0.078GB
mydb 0.078GB
> show collections // 顯示當下用的資料庫(此處為mydb)中有那些collections
system.indexes // <--- 此collections即是 系統為於此 資料庫mydb 自動建立index的存放處 (index結構為B-tree)
things
> t={x:3} // 建立一資料物件t 注意!因t,j為local, 故exit後t,j定義消失, 即使作了底下的db...save()亦然
{ "x" : 3 } // 注意一開始定義時只有屬性x (亦即field x), 尙無field _id
> db.things.save(t) // 將資料物件t增至collection things 中
WriteResult({ "nInserted" : 1 })
> db.things.find() // 顯示collection things 的 documents <註二>
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
> t // 即 t 於上面.save(t)後 方有field _id, 且_id與以.save(t)增添至things內的document所產生的_id相同
{ "x" : 3, "_id" : ObjectId("556b87c341e6e4838babbdc1") }
// 若前面是用db.things.insert(t), 則上行仍只為 { "x" : 3 }
> use test // 切換至 資料庫test
switched to db test
> db.things.save(t) // 類似上,只不過現在於 資料庫test上操作, 此行亦自動建立 資料庫test與其內的colleciton things
WriteResult({ "nInserted" : 1 })
> db.things.find() // 由於已有 t._id 故.save(t)時以沿用其_id來增添document (注意與 mydb內document 為個別的實體)
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
> show dbs
local 0.078GB
mydb 0.078GB
test 0.078GB
> db.things.insert({x:7}) // 效果同 db.things.save({x:7}) 亦相當於SQL的 INSERT INTO things VALUES (7);
WriteResult({ "nInserted" : 1 })
> db.things.find()
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
{ "_id" : ObjectId("556b87c341e6e4838babbdc2"), "x" : 7 }
// 注意! 下行remove()中若未加參數或以空文件{}當參數, 則會將所有collection中的documents全部刪除! 用時須謹慎
> db.things.remove({x:3}) // 類似於SQL的 DELETE FROM things WHERE x=3; (另外,可利用field _id刪除所指定document)
WriteResult({ "nRemoved" : 1 })
> db.things.find() // 顯示結果 資料庫test的 collection things 中 只剰一document
{ "_id" : ObjectId("556b87c341e6e4838babbdc2"), "x" : 7 }
> db.things.remove({x:7}) // 相當於SQL的 DELETE FROM things WHERE x=7; 即當中有field x為7者皆刪除
WriteResult({ "nRemoved" : 1 })
> db.things.find() // 資料庫test的 collection things 中已無資料
> show collections // 然而 資料庫test 中的things仍存在,顯示 雖mongo會自動建立collection,但不會自動刪除
system.indexes
things
> db.things.drop() // 於資料庫test中 刪除collection things
true
> show collections // 資料庫test中顯示已無things
system.indexes
> show dbs // 同上理 資料庫test 仍存在
local 0.078GB
mydb 0.078GB
test 0.078GB
> db.dropDatabase() // 刪除目前所在資料庫 i.e.資料庫test
{ "dropped" : "test", "ok" : 1 }
> show dbs // 顯示已無 資料庫test
local 0.078GB
mydb 0.078GB
> use mydb // 切換回到 資料庫mydb
switched to db mydb
> for (var i=1; i<=20; i++) db.things.save({x:4,j:i}) // 利用JavaScript的for loop來增添資料, 注意此處x,j是field名
WriteResult({ "nInserted" : 1 })
> db.things.find() // 因資料過多而只顯示前面部份
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 1 }
......
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 18 }
Type "it" for more
> it // 打 it 方接續顯示上面未顯示的部份
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 19 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 20 }
> db.things.find().limit(5).skip(2) // 跳過前兩筆後顯示五筆; 相當於SQL的 SELECT * from things LIMIT 5 SKIP 2;
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("..........略............"), "x" : 4, "j" : 5 }
> db.things.find({x:3}) // 相當於SQL的 SELECT * from things WHERE x=3;
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
> db.things.find({x:3},{_id:1}) // 類似上一個結果,然只顯示field _id
{ "_id" : ObjectId("556b87c341e6e4838babbdc1") }
> db.things.distinct("x") // 相當於SQL的 SELECT DISTINCT x from things;
[ 4, 3 ]
> db.things.find({$or:[{name:"mongo"},{x:3}]}) // 相當於SQL的 SELECT * from things WHERE name="mongo" OR x=3;
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
> var tstcursor=db.things.find() // 宣告變數tstcursor為 db...find()傳回的object cursor
> while (tstcursor.hasNext()) printjson(tstcursor.next()) // <註三>
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
......
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }
// 下行相當於SQL的 SELECT * from things;
> db.things.find().forEach(printjson) // .forEach(func)為cursor method 相當於上兩行scripts的總效果
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
.....
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }
> db.things.find({x:{$ne:4}}) // 排除field x為4的documents; 相當於SQL的 SELECT * from things WHERE x!=4;
{ "_id" : ObjectId("..........略............"), "name" : "mongo" }
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 3 }
> db.things.find({name:/ng/}) // 相當於SQL的 SELECT * from things WHERE name LIKE '%ng%';
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
> db.things.find({name:/^mon/}) // 相當於SQL的 SELECT * from things WHERE name LIKE 'mon%';
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
> db.things.find({x:4,j:10}) // 相當於SQL的 SELECT * from things WHERE x=4 AND j=10;
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 10 }
// 下行效果同 db.things.count() // 相當於SQL的 SELECT COUNT(*) FROM things;
> db.things.find().itcount() // 傳回collection things中 documents 個數 (此處22個資料物件,故顯示22)
22
> db.things.find({j:{$exists:true}}).count() // 相當於SQL的 SELECT COUNT(j) FROM things; 即有field j者方計數
20
> var tstcursor=db.things.find() // 重新定義tstcursor於things開頭
> printjson(tstcursor[0]) // 由此可知cursor可如同陣列般操作,陣列開頭註標為0 <註四>
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
> printjson(tstcursor[21]) // 由於有22個資料物件,故 tstcursor[21] 為最後一個
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }
> printjson(tstcursor[22]) // 最後一個之後的皆視為未定義
undefined
> db.things.find({name:"mongo"}).forEach(printjson) // 相當於SQL的 SELECT * from things WHERE name="mongo";
{ "_id" : ObjectId("......略......"), "name" : "mongo" }
> db.things.find({x:4}).forEach(printjson) // 相當於SQL的 SELECT * from things WHERE x=4;
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }
......
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 20 }
// 下行的true換成1亦可 i.e. 下行 <=> db.things.find({x:4},{j:1}).forEach(printjson)
> db.things.find({x:4},{j:true}).forEach(printjson) // 相當於SQL的 SELECT j from things WHERE x=4;
{ "_id" : ObjectId("......略......"), "j" : 1 }
......
{ "_id" : ObjectId("......略......"), "j" : 20 }
> db.things.findOne({x:4}) // <=> db.things.find({x:4}).limit(1) 而SQL為 SELECT * from things WHERE x=4 LIMIT 1;
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }
> var tstdoc=db.things.findOne({x:4}) // 與上類似,然將變數tstdoc定義成db...findOne()傳回的document <註五>
> tstdoc
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }
> db.things.find({x:4}).limit(3) // 由傳回結果易知,當limit()內參數>1 時傳回的不再是document而是cursor <註六>
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("......略......"), "x" : 4, "j" : 3 }
> db.things.update({x:3},{$set:{x:1}}) // 相當於SQL的 UPDATE things SET x=1 WHERE x=3; (若有相同亦只改最前頭)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.things.find({x:{$gte:1,$lte:3}}) // 由此顯示結果可知,x=3的資料物件 成功改成x=1
{ "_id" : ObjectId("556b87c341e6e4838babbdc1"), "x" : 1 }
> var dmongo=db.things.findOne({name:"mongo"})
> dmongo // <=> print(tojson(dmongo))
{ "_id" : ObjectId("556ae901b81b03c8400c2c04"), "name" : "mongo" }
> dmongo.type="database" // 增一field於 資料物件dmongo中 (其field名為type, field值為database)
database
> dmongo
{
"_id" : ObjectId("556ae901b81b03c8400c2c04"),
"name" : "mongo",
"type" : "database"
}
> db.things.findOne({name:"mongo"}) // 因尙未save(), 故db未更動
{ "_id" : ObjectId("556ae901b81b03c8400c2c04"), "name" : "mongo" }
> db.things.save(dmongo) // 以.save()來更新document資料 (注意若dmongo的_id於things中找不到,則是增添新document)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.things.findOne({name:"mongo"}) // 由結果亦知:因collection things中能找到與dmongo相同的_id, 故是更新document
{
"_id" : ObjectId("556ae901b81b03c8400c2c04"),
"name" : "mongo",
"type" : "database"
}
// 嵌入文件 (已設 資料物件dmongo開始如上定義, 且collection things中已有相同document)
> dmongo.newtstd={a:1,b:2} // 對 資料物件dmongo 再增一field ,此一新field名newtstd, 其值{a:1,b:2}又為一document
{ "a" : 1, "b" : 2 }
> db.things.save(dmongo) // 以dmongo來更新 資料庫mydb中things裡的document
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.things.findOne({name:"mongo"}) // 由查詢顯示可知: 子文件 {a:1,b:2} 被嵌入至 父文件 中
{
"_id" : ObjectId("556ae901b81b03c8400c2c04"),
"name" : "mongo",
"type" : "database",
"newtstd" : {
"a" : 1,
"b" : 2
}
}
> db.things.findOne({"newtstd.b":2}) // 內嵌文件 之查詢例
{
"_id" : ObjectId("556ae901b81b03c8400c2c04"),
"name" : "mongo",
"type" : "database",
"newtstd" : {
"a" : 1,
"b" : 2
}
}
// 參考(reference) 例 - 設操作延續上面
> var other={s:"other thing",n:1} // 建一資料物件other, 其有s,n兩fields
> db.otherthings.save(other) // 自動建立collection otherthings, 並將上行document增添其中
WriteResult({ "nInserted" : 1 })
> db.otherthings.find()
{ "_id" : ObjectId("556d98fe41e6e4838babbdd6"), "s" : "other thing", "n" : 1 }
> var mongo=db.things.findOne({name:"mongo"})
> mongo
{
"_id" : ObjectId("556ae901b81b03c8400c2c04"),
"name" : "mongo",
"type" : "database",
"newtstd" : {
"a" : 1,
"b" : 2
}
}
// 下下行於資料物件mongo增一field otherdata,其值為一reference, 此reference用來參考otherthings中的某特定document
// 注意上面.save(other)已定other._id為ObjectId("556d98fe41e6e4838babbdd6"), 故reference知道要參考哪個document
> mongo.otherdata=new DBRef('otherthings',other._id)
DBRef("otherthings", ObjectId("556d98fe41e6e4838babbdd6"))
> mongo
{
"_id" : ObjectId("556ae901b81b03c8400c2c04"),
"name" : "mongo",
"type" : "database",
"newtstd" : {
"a" : 1,
"b" : 2
},
"otherdata" : DBRef("otherthings", ObjectId("556d98fe41e6e4838babbdd6"))
}
> db.things.save(mongo) // 使 資料庫內的docuement 亦更新
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
// 由下行可知,我們已可經由 things中某一特定document的field otherdata 參考到 otherthings中某一特定document
> db.things.findOne({name:"mongo"}).otherdata.fetch()
{
"_id" : ObjectId("556d98fe41e6e4838babbdd6"),
"s" : "other thing",
"n" : 1
}
> other.n=3
3
> db.otherthings.save(other) // 更新collection otherthings中此一特定document
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.otherthings.find()
{ "_id" : ObjectId("556d98fe41e6e4838babbdd6"), "s" : "other thing", "n" : 3 }
> db.things.findOne({name:"mongo"}).otherdata.fetch() // 從 things 中亦可得知 otherthings 中的更新
{
"_id" : ObjectId("556d98fe41e6e4838babbdd6"),
"s" : "other thing",
"n" : 3
}
> db.things.getIndexes() // 查看於collection things已建之索引 (此為系統為field _id自動建的遞增索引)
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "mydb.things"
}
]
> db.otherthings.getIndexes() // 查看於collection otherthings已建之索引 (此亦為系統為field _id自動建的)
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "mydb.otherthings"
}
]
> db.system.indexes.find() // 此可列出當前 資料庫於各collection所建的全部部索引
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
// 下行相當於SQL的 CREATE INDEX <自設索引名> ON things(j DESC)
> db.things.ensureIndex({j:-1}) // 對 things中的field j建立遞減索引 (-1表遞減; 1表遞增) <註七>
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
{ "v" : 1, "key" : { "j" : -1 }, "name" : "j_-1", "ns" : "mydb.things" }
> db.things.find({j:{$gt:15}}).count() // 相當於SQL的 SELECT COUNT(*) FROM things WHERE j>15; 理論上index後會較快
5
> db.things.find().sort({j:-1}) // 以field j遞減方式顯示結果,相當於SQL的 SELECT * FROM things ORDER BY j DESC;
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "x" : 4, "j" : 20 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "x" : 4, "j" : 1 }
......
> db.things.find().sort({j:1}) // 以field j 遞增方式顯示結果; 因是建立遞減索引, 故此應比上面慢 (此例不明顯,須很多data方能看出)
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "x" : 4, "j" : 1 }
......
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "x" : 4, "j" : 20 }
> db.things.find({j:{$gte:11}},{j:1}).sort({j:-1}) // 相當於SQL的 SELECT j FROM things WHERE j>=11 ORDER BY j DESC;
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "j" : 20 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdcc"), "j" : 11 }
> db.things.find({j:{$lte:7}},{j:1}).sort({j:-1}) // 相當於SQL的 SELECT j FROM things WHERE j<=7 ORDER BY j DESC;
{ "_id" : ObjectId("556ba28d41e6e4838babbdc8"), "j" : 7 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "j" : 1 }
> db.things.find({j:{$gt:7,$lt:11}},{j:1}).sort({j:-1}) // SELECT j FROM things WHERE j>7 AND j<11 ORDER BY j DESC;
{ "_id" : ObjectId("556ba28d41e6e4838babbdcb"), "j" : 10 }
{ "_id" : ObjectId("556ba28d41e6e4838babbdca"), "j" : 9 }
{ "_id" : ObjectId("556ba28d41e6e4838babbdc9"), "j" : 8 }
> db.things.ensureIndex({"newtstd.b":1}) // 以newtstd.b為key,對things.newtstd內嵌文件建立遞增索引
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
{ "v" : 1, "key" : { "j" : -1 }, "name" : "j_-1", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "newtstd.b" : 1 }, "name" : "newtstd.b_1", "ns" : "mydb.things" }
// 下行所建索引以field j作遞增排序,當j相同情況下, 按照name作遞減排序, 若前二field皆相同, 以 newtsd.b 作遞減排序
> db.things.ensureIndex({j:1, name:-1, "newtstd.b":1}) // 多columns又含內嵌文件的索引例
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}
> db.system.indexes.find()
...
{ "v" : 1, "key" : { "newtstd.b" : 1 }, "name" : "newtstd.b_1", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "j" : 1, "name" : 1, "newtstd.b" : -1 }, "name" : "j_1_name_1_newtstd.b_-1", "ns" : "mydb.things" }
> db.things.update({x:4},{$inc:{j:10}},false,true) // 相當於SQL的 UPDATE things SET j=j+10 WHERE x=4;
WriteResult({ "nMatched" : 20, "nUpserted" : 0, "nModified" : 20 })
> db.things.reIndex() // 重建索引
> db.things.find({x:4},{j:1}).sort({j:-1})
{ "_id" : ObjectId("556ba28e41e6e4838babbdd5"), "j" : 30 }
......
{ "_id" : ObjectId("556ba28d41e6e4838babbdc2"), "j" : 11 }
> db.things.dropIndex({j:-1}) // 刪除一特定索引,field(s)須全符合方刪除其索引
{ ... , "ok" : 1 }
> db.system.indexes.find() // 由結果知刪除成功
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
{ "v" : 1, "key" : { "newtstd.b" : 1 }, "name" : "newtstd.b_1", "ns" : "mydb.things" }
......
// 下行以runCommand模式刪除索引:效果相當於 db.things.dropIndex({"newtstd.b":1})
> db.runCommand({dropIndexes:'things', index: {"newtstd.b":1}})
{ ... , "ok" : 1 }
> db.system.indexes.find() // 由結果知刪除成功
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
......
> db.things.dropIndexes() // 除預設的 _id 索引外,其餘的collection things中索引全都刪除
{
"nIndexesWas" : 2,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.things" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "mydb.otherthings" }
> db.things.reIndex() // 重建索引, 注意!! 重建"不能"復原刪除!只是把既有index重建索引, 故.indexes.find() 結果同上
// map/reduce: 注意map輸出與reduce的輸入 兩者的資料型態須相等
// added in 2016/3/12
I.法
>
db.things.mapReduce(
function() {
var x = this.x;
emit(x, this.j);
} ,
function (key, vals) {
var total = 0;
for (var i = 0; i < vals.length; i++) {
total += vals[i];
}
return total;
}, { out : { inline : 1 } } )
輸出如下:
{
"results" : [
{ // { "_id" : ObjectId(...), "z" : 15 } and the last document
"_id" : null,
"value" : 0
},
{ // { "_id" : ObjectId(...), "x" : 1 }
"_id" : 1,
"value" : null
},
{ // { "_id" : ObjectId(...), "j" : 11~30, "x" : 4 }
"_id" : 4,
"value" : 410 // 11+12+...+30 = 410
}
],
"timeMillis" : 2,
"counts" : {
"input" : 23,
"emit" : 23,
"reduce" : 2,
"output" : 3
},
"ok" : 1,
}
II.法
>
db.things.mapReduce(
function() {
var x = this.x;
emit(x, {count: 1, j: this.j} );
} ,
function(key, values) {
var result = {count: 0, j: 0};
values.forEach(function(value) {
result.count += value.count;
result.j += value.j;
} );
return result;
}, { out : { inline : 1 } } )
輸出如下: // 結果類似I.法, 唯此處加了計數器
{
"results" : [
{
"_id" : null,
"value" : {
"count" : 2,
"j" : 0
}
},
{
"_id" : 1,
"value" : {
"count" : 1,
"j" : null
}
},
{
"_id" : 4,
"value" : {
"count" : 20,
"j" : 410
}
}
],
"timeMillis" : 3,
"counts" : {
"input" : 23,
"emit" : 23,
"reduce" : 2,
"output" : 3
},
"ok" : 1,
}
<註一>
a.
一開始啟動server發現有問題無法啟動, 以 systemctl -l status mongod.service 查詢:
情況<i> 當含有如下錯誤訊息:
...warning... servers don't have journaling enabled by default. Please use --journal if you want durability.
......
...mongod... ERROR: child process failed, exited with error number 1
經查發現自己的 mongodb-server 的設定檔/etc/mongod.conf內的log設定與其所預建的log目錄不符:
/etc/mongod.conf 中預設是 logpath=/var/log/mongodb/mongod.log
然urpmi mongodb-server 安裝時是建 /var/log/mongo/mongod.log
故產生錯誤
自行解決方式:
經查google後發現置於 /var/log/mongodb/mongod.log 的數量遠大於 /var/log/mongo/mongod.log
本想改以 /var/log/mongodb/mongod.log 為主
然為求未來rpm update 的方便,還是改/etc/mongod.conf 設定成 logpath=/var/log/mongo/mongod.log
然未來新版本若更動亦不排除再改設定
情況<ii>
然若 systemctl -l status mongod.service 查詢出現
...
...ERROR: child process failed, exited with error number 100
...
則查看看/var/lib/mongo/ 目錄中是否有mongod.lock
若server没啟動而此竟然有此當存在, 則表其為先前的程序,
很可能是先前有不正常結束的情況(e.g.當機)而未淸,
此時刪此mongod.lock後通常再執行systemctl start mongod.service即可
若mongodb內先前已存有data, 則一進入後先執行db.repairDatabase()來修復會較保險
b.更新時有問題,出現
ERROR: 'script' failed for mongodb-server-3.0.4-1-omv2014.0.i586:
error: %post(mongodb-server-3.0.4-1.i586) 短命令稿執行失敗,離開狀態 1
亦不能啟動server
為排除變因,先
]# mv /var/lib/mongo /var/lib/mongo.old
]# mkdir /var/lib/mongo
]# chown mongod.mongod /var/lib/mongo
其後用 urpmi --replacefiles --replacepkgs mongodb-server 再重裝, 雖安裝没再出現錯誤,然執行仍core dump,
或許從2.x更新至新版3.0.4有啥問題... (己没找到解決方式,故改回用2.x)
<註二>
傳回結果開頭總會是field "_id"
ObjectId未定義時為系統自動分配,依情況而不同,此處從略
由 db.things.find() 顯示查詢結果亦知,資料庫是 以_id為名的field 來存ObjectId(), 其亦具SQL的primary key性質
由help的db...find() 說明 list objects in collection 可知
我們可視db....find() 所顯示的 一{...} 即為一資料物件,此物件以 "_id":ObjectId("......略......") 屬性作為開頭
而於MongoDB中,此一資料物件即為一document
雖單用db.things.find()所顯示的是查詢結果,然實際上是一object cursor(DBCursor), 故之後有例將變數定義成此cursor
例中剛開始定義j等資料物件{...}最初可視為尙無field _id的document(s), 當第一次.save(j)後才有_id
field _id 的亦有SQL中 primary key 的效果
但若是用db.thing.insert(j) 則直接打j時{...}將無field _id, 而db.things.find()仍有此物件_id就是
i.e. 兩方法僅使local變數相異, 內部儲存資料物件兩方法皆必有物件_id就是
注意!雖然多數可以對應SQL, 然而多數情況應最好同時將{...}視為(資料)物件,較符合物件導向精神
<註三>
利用while loop與object cursor顯示things中所有資料 (i.e. iterator方式)
tstcursor.hasNext() 與 tstcursor.next() 為Cursor methods
方法printjson()以JSON形式顯示資料
<自>我們可視object cursor本身即為一物件,其內部有一指標成員(pointer)最初指向 collection第一個待顯示資料物件 的前一位,
方法hasNext()是讀此指標成員來判斷cursor下一位是否有資料物件;
方法next()是 先顯示cursor下一位的資料物件後再將cursor內部的指標成員移至此資料物件
<註四>
注意當cursor一用陣列來操作時,
cursor將停止如<註三>般的iterator方式 (<註三>的<自>中所說的內部指標,相當於指向 cursor[0]的前一位)
且cursor會把註標所指的資料物件皆載入記憶體,若查詢結果較大,有可能因此耗盡記憶體而發生記憶體溢位
故應依自身情況選擇: 當cursor用iterator方式時,可隨時改用陣列方式,而cursor[0]會成為改用當時尙未顯示的第一個資料物件
然若是已用陣列操作時想換用iterator方式, 只能重新定義cursor
<註五>
注意! 傳回結果實際上是與 database資料 隔離的新資料物件, 而tstdoc指向此新物件
故未經諸如.save()存入/更新時, 改此物件是不會更動到database的
<註六>
注意用limit()限制查詢結果個數可減輕資料庫與網路的負荷,因而增加效率,改善效能
<註七>
注意索引的遞增遞減是指索引建存遞增遞減,與用.find()所顯示的不見得同向,
顯示是用諸如.find().sort()來控制顯示時呈現出來的遞增遞減
ref.0 MongoDB Manual - http://www.mongodb.org/display/DOCS/Manual
ref.1 man mongo
ref.2 挑戰大數據 第二版 - http://datasearch.ruc.edu.cn/NoSQL/
書中p.11-52 表11-10 為 SQL與MongoDB查詢敍述比較
此書可能是人民大學的學生上Big Data相關課程的内容彙整,
也許因課程繁重而筆記匆匆, 以致書印錯誤一些些
且又没索引; 某部份中文用詞若能保留英文應該會比較好,
例如常用的'行'與'列'恰與己所學相反
以前所學 column譯為行 而 row譯為列 - i.e.直行橫列
然書中把 column譯為列 而 row譯為行
再例如程設裡 指標一般是pointer,而書中把資料庫的cursor翻成指標,
之前没碰過資料庫程設的讀者有可能會在這個地方產生混淆
若能自行校對,那麼此本仍是很不錯的中文雲端參考書 (注意"能校對"前提!)
沒有留言:
張貼留言