RUBY-245 Unique object id with timestamp
This commit is contained in:
parent
8df0ff23f5
commit
a8ce896c9f
@ -881,7 +881,7 @@ static VALUE method_deserialize(VALUE self, VALUE bson) {
|
|||||||
return elements_to_hash(buffer, remaining);
|
return elements_to_hash(buffer, remaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE objectid_generate(VALUE self)
|
static VALUE objectid_generate(int argc, VALUE* args, VALUE self)
|
||||||
{
|
{
|
||||||
VALUE oid;
|
VALUE oid;
|
||||||
unsigned char oid_bytes[12];
|
unsigned char oid_bytes[12];
|
||||||
@ -889,7 +889,11 @@ static VALUE objectid_generate(VALUE self)
|
|||||||
unsigned short pid;
|
unsigned short pid;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
t = htonl((int)time(NULL));
|
if(argc == 0 || (argc == 1 && *args == Qnil)) {
|
||||||
|
t = htonl((int)time(NULL));
|
||||||
|
} else {
|
||||||
|
t = htonl(FIX2INT(rb_funcall(*args, rb_intern("to_i"), 0)));
|
||||||
|
}
|
||||||
MEMCPY(&oid_bytes, &t, unsigned char, 4);
|
MEMCPY(&oid_bytes, &t, unsigned char, 4);
|
||||||
|
|
||||||
MEMCPY(&oid_bytes[4], hostname_digest, unsigned char, 3);
|
MEMCPY(&oid_bytes[4], hostname_digest, unsigned char, 3);
|
||||||
@ -969,7 +973,7 @@ void Init_cbson() {
|
|||||||
Digest = rb_const_get(rb_cObject, rb_intern("Digest"));
|
Digest = rb_const_get(rb_cObject, rb_intern("Digest"));
|
||||||
DigestMD5 = rb_const_get(Digest, rb_intern("MD5"));
|
DigestMD5 = rb_const_get(Digest, rb_intern("MD5"));
|
||||||
|
|
||||||
rb_define_method(ObjectId, "generate", objectid_generate, 0);
|
rb_define_method(ObjectId, "generate", objectid_generate, -1);
|
||||||
|
|
||||||
if (gethostname(hostname, MAX_HOSTNAME_LENGTH) != 0) {
|
if (gethostname(hostname, MAX_HOSTNAME_LENGTH) != 0) {
|
||||||
rb_raise(rb_eRuntimeError, "failed to get hostname");
|
rb_raise(rb_eRuntimeError, "failed to get hostname");
|
||||||
|
@ -33,6 +33,8 @@ module BSON
|
|||||||
@@lock = Mutex.new
|
@@lock = Mutex.new
|
||||||
@@index = 0
|
@@index = 0
|
||||||
|
|
||||||
|
attr_accessor :data
|
||||||
|
|
||||||
# Create a new object id. If no parameter is given, an id corresponding
|
# Create a new object id. If no parameter is given, an id corresponding
|
||||||
# to the ObjectId BSON data type will be created. This is a 12-byte value
|
# to the ObjectId BSON data type will be created. This is a 12-byte value
|
||||||
# consisting of a 4-byte timestamp, a 3-byte machine id, a 2-byte process id,
|
# consisting of a 4-byte timestamp, a 3-byte machine id, a 2-byte process id,
|
||||||
@ -40,12 +42,15 @@ module BSON
|
|||||||
#
|
#
|
||||||
# @param [Array] data should be an array of bytes. If you want
|
# @param [Array] data should be an array of bytes. If you want
|
||||||
# to generate a standard MongoDB object id, leave this argument blank.
|
# to generate a standard MongoDB object id, leave this argument blank.
|
||||||
def initialize(data=nil)
|
#
|
||||||
@data = data || generate
|
# @option opts :data (nil) An array of bytes to use as the object id.
|
||||||
|
# @option opts :time (nil) The value of this object ids timestamp. Note that
|
||||||
|
# the remaining bytes will consist of the standard machine id, pid, and counter. If
|
||||||
|
# you need a zeroed timestamp, used ObjectId.from_time.
|
||||||
|
def initialize(data=nil, time=nil)
|
||||||
|
@data = data || generate(time)
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :data
|
|
||||||
|
|
||||||
# Determine if the supplied string is legal. Legal strings will
|
# Determine if the supplied string is legal. Legal strings will
|
||||||
# consist of 24 hexadecimal characters.
|
# consist of 24 hexadecimal characters.
|
||||||
#
|
#
|
||||||
@ -62,14 +67,23 @@ module BSON
|
|||||||
#
|
#
|
||||||
# @param [Time] time a utc time to encode as an object id.
|
# @param [Time] time a utc time to encode as an object id.
|
||||||
#
|
#
|
||||||
|
# @option opts [:unique] (false) If false, the object id's bytes
|
||||||
|
# succeeding the timestamp will be zeroed; if true, they'll
|
||||||
|
# consist of the standard machine id, pid, and counter.
|
||||||
|
#
|
||||||
# @return [Mongo::ObjectId]
|
# @return [Mongo::ObjectId]
|
||||||
#
|
#
|
||||||
# @example Return all document created before Jan 1, 2010.
|
# @example Return all document created before Jan 1, 2010.
|
||||||
# time = Time.utc(2010, 1, 1)
|
# time = Time.utc(2010, 1, 1)
|
||||||
# time_id = ObjectId.from_time(time)
|
# time_id = ObjectId.from_time(time)
|
||||||
# collection.find({'_id' => {'$lt' => time_id}})
|
# collection.find({'_id' => {'$lt' => time_id}})
|
||||||
def self.from_time(time)
|
def self.from_time(time, opts={})
|
||||||
self.new([time.to_i,0,0].pack("NNN").unpack("C12"))
|
unique = opts.fetch(:unique, false)
|
||||||
|
if unique
|
||||||
|
self.new(nil, time)
|
||||||
|
else
|
||||||
|
self.new([time.to_i,0,0].pack("NNN").unpack("C12"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adds a primary key to the given document if needed.
|
# Adds a primary key to the given document if needed.
|
||||||
@ -145,10 +159,10 @@ module BSON
|
|||||||
def to_json(*a)
|
def to_json(*a)
|
||||||
"{\"$oid\": \"#{to_s}\"}"
|
"{\"$oid\": \"#{to_s}\"}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create the JSON hash structure convert to MongoDB extended format. Rails 2.3.3
|
# Create the JSON hash structure convert to MongoDB extended format. Rails 2.3.3
|
||||||
# introduced as_json to create the needed hash structure to encode objects into JSON.
|
# introduced as_json to create the needed hash structure to encode objects into JSON.
|
||||||
#
|
#
|
||||||
# @return [Hash] the hash representation as MongoDB extended JSON
|
# @return [Hash] the hash representation as MongoDB extended JSON
|
||||||
def as_json(options ={})
|
def as_json(options ={})
|
||||||
{"$oid" => to_s}
|
{"$oid" => to_s}
|
||||||
@ -166,12 +180,16 @@ module BSON
|
|||||||
private
|
private
|
||||||
|
|
||||||
# This gets overwritten by the C extension if it loads.
|
# This gets overwritten by the C extension if it loads.
|
||||||
def generate
|
def generate(oid_time=nil)
|
||||||
oid = ''
|
oid = ''
|
||||||
|
|
||||||
# 4 bytes current time
|
# 4 bytes current time
|
||||||
time = Time.new.to_i
|
if oid_time
|
||||||
oid += [time].pack("N")
|
t = oid_time.to_i
|
||||||
|
else
|
||||||
|
t = Time.new.to_i
|
||||||
|
end
|
||||||
|
oid += [t].pack("N")
|
||||||
|
|
||||||
# 3 bytes machine
|
# 3 bytes machine
|
||||||
oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
|
oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
|
||||||
|
@ -126,14 +126,27 @@ class ObjectIdTest < Test::Unit::TestCase
|
|||||||
time = Time.now.utc
|
time = Time.now.utc
|
||||||
id = ObjectId.from_time(time)
|
id = ObjectId.from_time(time)
|
||||||
|
|
||||||
|
assert id.to_a[4, 8].all? {|byte| byte == 0 }
|
||||||
assert_equal time.to_i, id.generation_time.to_i
|
assert_equal time.to_i, id.generation_time.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_from_time_unique
|
||||||
|
time = Time.now.utc
|
||||||
|
id = ObjectId.from_time(time, :unique => true)
|
||||||
|
|
||||||
|
mac_id = Digest::MD5.digest(Socket.gethostname)[0, 3].unpack("C3")
|
||||||
|
assert_equal id.to_a[4, 3], mac_id
|
||||||
|
assert_equal time.to_i, id.generation_time.to_i
|
||||||
|
|
||||||
|
id2 = ObjectId.new(nil, time)
|
||||||
|
assert_equal time.to_i, id2.generation_time.to_i
|
||||||
|
end
|
||||||
|
|
||||||
def test_json
|
def test_json
|
||||||
id = ObjectId.new
|
id = ObjectId.new
|
||||||
assert_equal "{\"$oid\": \"#{id}\"}", id.to_json
|
assert_equal "{\"$oid\": \"#{id}\"}", id.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_as_json
|
def test_as_json
|
||||||
id = ObjectId.new
|
id = ObjectId.new
|
||||||
assert_equal({"$oid" => id.to_s}, id.as_json)
|
assert_equal({"$oid" => id.to_s}, id.as_json)
|
||||||
|
Loading…
Reference in New Issue
Block a user