2016年3月9日 星期三

mongodb howto

前言:

前幾天看中視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翻成指標, 
       之前没碰過資料庫程設的讀者有可能會在這個地方產生混淆
       若能自行校對,那麼此本仍是很不錯的中文雲端參考書 (注意"能校對"前提!)

沒有留言:

張貼留言