【知識小課堂】 mongodb 之 objectId
一、OBJECTID
因公司開發人員在使用MONGODB時,總遇到一些小問題。為了增加大家的mongodb 資料庫知識。
決定每週進行一、兩次的知識小課堂。這裡把內容整理出來,上傳到部落格中。也算是自己的一個mongodb 歷程吧
1.1 結構
ObjectId("52cbab42231dea1e819b2a37"),
ObjectId("52cbab5b231dea1e819b2a38"),
ObjectId("52cbab70231dea1e819b2a39"),
52cbab70 時間戳
231dea 機器號
1e81 程序ID
9b2a39 自增數
以上數字為 16進製表示
特點:
1.24個16進位制資料,使用 12位元組的儲存空間。
2.最後3個位元組為:自動增長。可確保每秒生成的值也不一樣,一秒最多允許每個程序擁有256^3個不同ObjectId
(後面3位16進位制存在量可以達到: 16,777,216
也可以從儲存量計算出來 FFFFFF 轉換成10進位制就是16,777,215)
3.可轉移到客戶端生成,而減輕伺服器負擔(需要客戶端的驅動程式)
下面介紹幾個相關的函式
>> x=ObjectId() ObjectId("53b3a89bf988c39955a30f9e") > x ObjectId("53b3a89bf988c39955a30f9e") >x.str 53b3a89bf988c39955a30f9e > x.toString() ObjectId("53b3a89bf988c39955a30f9e") > x.getTimestamp() ISODate("2014-07-02T06:37:15Z") > x.valueOf() 53b3a89bf988c39955a30f9e >
函式說明:
1.Str 功能 與valueOf()相同
2.toString() 把一個物件轉換成
了一字串。這時兩個objectId可以對比。
3.getTimestamp() 抽取 時間
我們再來看看一個有意思的查詢:
既然可以把從objectId 獲取日期,那我是否可以使用一個日期做為條件,使用_id 欄位來進行查詢呢。
我的方法是
使用 函式 getTimestamp()先把欄位 進行處理,這在一般的關係型資料庫非常常用。<span style="font-size:18px;">> a = db.order.findOne() { "_id" : ObjectId("5331128631a4804b226471e4"), "md5" : "cacae8722c325d62e795e5c273d5f49b", …… } > a."_id" Wed Jul 2 14:50:57.494 SyntaxError: Unexpected string > a._id.getTimestamp() ISODate("2014-03-25T05:22:14Z") > new Date("2014,03,25") ISODate("2014-03-24T16:00:00Z") > new Date("2014,03,25") ISODate("2014-03-24T16:00:00Z") > db.order.find({"_id.getTimestamp()":{$gt:new Date("2014,03,25")}}).count() 0 > db.order.find({_id.getTimestamp():{$gt:new Date("2014,03,25")}}).count() Wed Jul 2 14:54:00.298 SyntaxError: Unexpected token . > </span>
可以看到,看來是行不通。mongodb 無法識別 處理後的欄位。
那隻能自己生成一個objectId 再進行對比了。
我以前一直以為是無法進行兩個 objectId 來進行對比的。
但查詢了官方資料。並沒有看到相關按時間生成新的objectID 的方法/函式。
(http://docs.mongodb.org/manual/reference/method/)
後來找到了開發牛人的一個便方,解決了此問題。程式碼如下:
<span style="font-size:18px;">#構建一個指定日期的objectId()
> var timestamp = Math.floor(new Date(2014,03,01).getTime() / 1000); #getTime() 返回毫秒數
> var hex = (‘00000000’ + timestamp.toString(16)).substr(-8); #前填充0
> var v_objectId = new ObjectId(hex + new ObjectId().str.substring(8)); #更換掉前面的時間值
</span>
其它方法也比較簡單,就是把一個日期轉換成16進位制後,替換到一個新生成的objectId 中去。
當自己構造好一個objectId後,後面我們來使用它進行查詢:
<span style="font-size:18px;">#用objectId() 來進行對比查詢
> db.order.find({_id:{$gt:v_objectId}})
{ "_id" : ObjectId("533e1049e4271af009000005"), "md5" : "a99038f392284b7dac895dc0030486c8",
...
}
</span>
可以看到,查詢結果出來了。從上面可以看出。兩個objectId 是可以進行對比的。
從上面示例程式碼看到
雖然可以從ojbectId 中提取一些資訊。但如果要使用 objectid中的資訊來做為查詢條件,相對還是比較麻煩的。
就象我上面的,如果我要從objectID 的建立日期 來做為查詢條件,我自己得先用查詢時間構建一個objectID,然後再進行查詢。
如果查詢條件還有範圍什麼的。還不如再建立一個欄位create_dt 來儲存記錄的建立時間。以後查詢統計用此欄位,還更方便。
我們再來看看查詢的計劃,看到已使用了索引,此計劃雖然不能說明什麼。但也可以讓我知道是用_id 欄位的索引。
<span style="font-size:18px;">> db.order.find({_id:{$gt:v_objectId}}).explain()
{
"cursor" : "BtreeCursor _id_",
"isMultiKey" : false,
"n" : 3,
"nscannedObjects" : 3,
"nscanned" : 3,
"nscannedObjectsAllPlans" : 3,
"nscannedAllPlans" : 3,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"_id" : [
[
ObjectId("533991005c1e4132ccbaf755"),
ObjectId("ffffffffffffffffffffffff")
]
]
},
"server" : "localhost.localdomain:27017"
}
>
</span>