Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bson null fixed #5

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bcfab7b
dvv's test
dvv Dec 7, 2010
cb25d82
Merge branch 'master' of git://github.com/naturalethic/coffee-mongo
dvv Dec 7, 2010
2f815b2
remove .clear()
dvv Dec 7, 2010
c9fe04a
Merge branch 'master' of git://github.com/naturalethic/coffee-mongo
dvv Dec 7, 2010
8393374
attempt to make unique indices
dvv Dec 7, 2010
00923bd
hz
dvv Dec 8, 2010
93f3361
Merge branch 'master' of git://github.com/naturalethic/coffee-mongo
dvv Dec 8, 2010
7c3b87e
use automatic splat last parameter
Dec 8, 2010
83acbe3
.find() -- support for sort, fields subset, skip, limit
Dec 8, 2010
5b8e116
rql
Dec 8, 2010
c945af4
Resource Query Language added
Dec 8, 2010
ab941c7
make Database an event emitter
Dec 8, 2010
f322f6d
fixes
dvv Dec 8, 2010
cdcc4f4
moved rql and store
dvv Dec 8, 2010
7831510
fixes; no hard limit
dvv Dec 8, 2010
ee0dbc8
Merge branch 'master' of git://github.com/naturalethic/coffee-mongo i…
dvv Dec 8, 2010
7d100c3
fixes
dvv Dec 8, 2010
df6b3f2
hz
dvv Dec 8, 2010
227eb5c
Merge branch 'master' of git://github.com/naturalethic/coffee-mongo i…
dvv Dec 8, 2010
b2b5e9a
notes
dvv Dec 8, 2010
0467a89
notes
dvv Dec 8, 2010
7795bdb
flags to .find()
dvv Dec 11, 2010
72e5d90
.save fixed
dvv Dec 12, 2010
6dbc6af
regexp
dvv Dec 14, 2010
e1c6374
merge
dvv Dec 15, 2010
e044a2f
fixmerged
dvv Dec 15, 2010
d3cc3b0
bson
dvv Dec 21, 2010
87d9691
undefineds-fixed
dvv Dec 23, 2010
fde12eb
failing-regexp-test
dvv Dec 25, 2010
03c7786
full-db-url-parser
dvv Dec 25, 2010
501371a
attempt-to-bsoncode
dvv Dec 26, 2010
e5bd29f
fixes
dvv Dec 27, 2010
417c0ed
regexp-fixed
dvv Jan 15, 2011
90ff246
function?
dvv Jan 17, 2011
4a052f0
methods
dvv Jan 23, 2011
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions lib/bson.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,16 @@ class BSONObjectID extends BSONBuffer
value: ->
@toHex()

class BSONCode extends BSONBuffer
type: 0x0F

constructor: (value) ->

str = value.toString()
super str.length + 10
# len_32, string, \0, 5_32, \0
@write str + '\u0000\u0005\u0000\u0000\u0000\u0000'

class BSONBoolean extends BSONBuffer
type: 0x08

Expand Down Expand Up @@ -300,6 +310,7 @@ class BSONElement extends BSONBuffer
constructor: (args...) ->
if args[0] instanceof Buffer
else
#console.log 'ELEMENT', args, typeof args[1]
switch typeof args[1]
when 'boolean'
v = new BSONBoolean args[1]
Expand All @@ -317,8 +328,19 @@ class BSONElement extends BSONBuffer
v = new BSONArray args[1]
else if args[1] instanceof BSONBuffer
v = args[1]
else if args[1] is null
v = new BSONNull()
else
v = new BSONDocument args[1]
when 'function'
if args[1] instanceof RegExp
v = new BSONRegExp args[1]
else
v = new BSONCode args[1]
#when 'undefined'
# # TODO: HOWTO JUST IGNORE THIS KEY?!
# v = new BSONNull()
#console.log 'TYPE', args[0], typeof args[1] unless v
throw Error 'unsupported bson value' if not v?
k = new BSONKey args[0]
super 1 + k.length + v.length
Expand All @@ -344,10 +366,11 @@ class BSONDocument extends BSONBuffer
else
@_value = value
els = []
# DVV: kick off undefineds
if @ instanceof BSONArray
els.push new BSONElement i, v for v, i in value
els.push new BSONElement i, v for v, i in value when v isnt undefined
else
els.push new BSONElement k, v for k, v of value
els.push new BSONElement k, v for k, v of value when v isnt undefined
length = 5
length += el.length for el in els
super length
Expand All @@ -357,6 +380,8 @@ class BSONDocument extends BSONBuffer
el.copy @, i
i += el.length
@[@length - 1] = 0
#sys = require 'util'
#console.log 'DOC', sys.inspect(value), sys.inspect(@)

value: ->
return @_value if @_value
Expand Down Expand Up @@ -386,6 +411,7 @@ _type =
0x09: BSONDate
0x0A: BSONNull
0x0B: BSONRegExp
0x0F: BSONCode
0x10: BSONInt32
0x12: BSONInt64

Expand All @@ -403,6 +429,7 @@ module.exports =
BSONFloat: BSONFloat
BSONString: BSONString
BSONRegExp: BSONRegExp
BSONCode: BSONCode
BSONObjectID: BSONObjectID
BSONBoolean: BSONBoolean
BSONDate: BSONDate
Expand All @@ -421,6 +448,7 @@ module.exports =
Float: BSONFloat
String: BSONString
RegExp: BSONRegExp
Code: BSONCode
ObjectID: BSONObjectID
Boolean: BSONBoolean
Date: BSONDate
Expand All @@ -429,4 +457,3 @@ module.exports =
Int64: BSONInt64
Element: BSONElement
Document: BSONDocument

113 changes: 92 additions & 21 deletions lib/mongo.coffee
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
events = require 'events'
net = require 'net'
url = require 'url'
bson = require './bson'

# Represents a Mongo database
#
# Takes:
# name : name of the database
# name : name of the database, or url
# e.g. 'mongodb://user:[email protected]:27068/irc'
# options : options hash
# host : default 'localhost'
# port : default 27017
# limit : default limit for find responses (default 100)
# hex : uses hex strings instead of binary ObjectID
# idfactory : a function that provides ids (default to ObjectID)
#
Expand All @@ -20,18 +22,23 @@ bson = require './bson'
# Gives:
# error : error, if any
# id : a new unique id for the provided collection
class Database
class Database extends events.EventEmitter
constructor: (@name, options) ->
options = options or {}
@host = options.localhost or 'localhost'
@port = options.port or 27017
@limit = options.limit or 100
@idfactory = options.idfactory
if not @idfactory
if options.hex
@idfactory = (collection, next) -> next null, new bson.ObjectID().toHex()
else
@idfactory = (collection, next) -> next null, new bson.ObjectID()
conn = url.parse @name
if conn.protocol is 'mongodb:'
@host = conn.hostname
@port = +conn.port if conn.port
@auth = conn.auth if conn.auth # FIXME: what is options analog?
@name = conn.pathname.substring(1) if conn.pathname
@connections = []

# Close all open connections. Use at your own risk.
Expand Down Expand Up @@ -91,15 +98,31 @@ class Database
# Gives:
# error : error
update: (collection, args..., next) ->
update = args.pop() or {}
changes = args.pop() or {}
query = args.pop() or {}
@connection (error, connection) =>
connection.retain()
connection.send (@compose collection, 2001, 0, 2, query, update)
connection.send (@compose collection, 2001, 0, 2, query, changes)
@last_error connection, (error, mongo_error) ->
connection.release()
next mongo_error if next

# Inserts or updates the document in a collection
#
# Takes:
# collection : collection name
# document : the document
#
# Gives:
# error : error
save: (collection, document, next) ->
document ?= {}
if not document._id
# TODO: fill with defaults first?
@insert collection, document, next
else
@update collection, {_id: document._id}, document, next

# Find documents in a collection
#
# Takes:
Expand All @@ -117,22 +140,25 @@ class Database
find: (collection, args..., next) ->
options = if args.length == 2 then args.pop() else {}
query = args.pop() or {}
options.limit ?= @limit
options.limit ?= 0
options.skip ?= 0
fields = {}
if options.fields
if options.fields instanceof Array
for field in options.fields
fields[field] = 1
else
fields = options.fields
fields = options.fields or {}
# DVV: native driver allows for DEselecting fields by marking them 0, e.g. {foo: 0, bar: 0}
# DVV: what is array alternative?! Think array should not be allowed; plus we save ticks
# DVV: hashes are ordered so no point in array
if fields instanceof Array
fields = {} # N.B. may come _only_ from options.fields, so safe to reset
for field in options.fields
fields[field] = 1
if options.sort
query =
$query: query
$orderby: options.sort
flags = 0
flags += 32+1 if options.tailable
@connection (error, connection) =>
connection.retain()
connection.send (@compose collection, 2004, 0, options.skip, options.limit, query, fields), (error, data) =>
connection.send (@compose collection, 2004, flags, options.skip, options.limit, query, fields), (error, data) =>
documents = @decompose data
@last_error connection, (error, mongo_error) ->
connection.release()
Expand All @@ -147,7 +173,7 @@ class Database
# Gives:
# error : error
# document : the found document, or null
find_one: (collection, args..., next) ->
findOne: (collection, args..., next) ->
options = if args.length == 2 then args.pop() else {}
query = args.pop() or {}
options.limit = 1
Expand Down Expand Up @@ -214,6 +240,7 @@ class Database
# Gives:
# error : error
index: (collection, keys, next) ->
# FIXME: count = Object.keys(keys).length ?
count = 0
count++ for k of keys
for key, unique of keys
Expand All @@ -223,6 +250,7 @@ class Database
document.unique = !!unique
document.key = {}
document.key[key] = 1
# FIXME: ensureIndex command?
@insert_without_id 'system.indexes', document, (error, document) =>
next error if next and (error or not --count)

Expand All @@ -235,6 +263,7 @@ class Database
# Gives:
# error : error
removeIndex: (collection, key, next) ->
# FIXME: same technique as in .index() to process multiple keys?
options = {}
options.dropIndexes = collection
options.index = {}
Expand Down Expand Up @@ -275,7 +304,43 @@ class Database
# error : error
# document : result document
drop: (collection, next) ->
@command 'drop', { drop: collection}, (error, document) ->
@command 'drop', {drop: collection}, (error, document) ->
if document and document.errmsg
error = { code: document.code, message: document.errmsg }
document = null
next(error, (if document then document.value else null)) if next

# Evaluates the code at server-side
#
# Takes:
# code : the code as string
#
# Gives:
# error : error
# document : result document
eval: (code, args..., next) ->
#@command 'eval', {$eval: code, args: args or []}, (error, document) ->
bcode = new bson.Code code

'''
composition = [
new bson.Int32 0
new bson.String code
new bson.Int32 0x05
new bson.Boolean false
]
i = 0
for item in composition
i += item.length
composition[0] = new bson.Int32 i
buffer = new Buffer i
for item in composition
item.copy buffer, i
i += item.length
buffer
'''

@command 'eval', {$eval: code}, (error, document) ->
if document and document.errmsg
error = { code: document.code, message: document.errmsg }
document = null
Expand Down Expand Up @@ -339,7 +404,7 @@ class Database
buffer

# Decompose a mongo binary message
decompose: (buffer) ->
decompose: (buffer, fn) ->
i = 4 * 3
code = (new bson.Int32 buffer.slice i).value()
i += 4
Expand All @@ -355,11 +420,17 @@ class Database
documents = []
while count--
document = new bson.Document buffer.slice i
documents.push document.value()
if fn
fn document.value()
else
documents.push document.value()
i += document.length
else
throw Error "unsupported response code: #{code}"
documents
if fn
fn undefined
else
documents

_buffer_grow_size = 10000

Expand Down Expand Up @@ -403,5 +474,5 @@ class Connection
@stream.end()

module.exports =
# ObjectID: ObjectID
ObjectID: bson.ObjectID
Database: Database
12 changes: 10 additions & 2 deletions test/mongo.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,15 @@ runner.mettle ->
assert.equal error, null
assert.equal documents.length, 1
assert.deepEqual @iceland, documents[0]
@next()
@db.find 'Country', { name: 'Iceland' }, (error, documents) =>
assert.equal error, null
assert.equal documents.length, 1
assert.deepEqual @iceland, documents[0]
@db.find 'Country', { name: /ce/i, grow: -> +1 }, (error, documents) =>
assert.equal error, null
assert.equal documents.length, 1
assert.deepEqual @iceland, documents[0]
@next()

runner.mettle ->
@tell 'find with limit/skip'
Expand Down Expand Up @@ -193,4 +201,4 @@ runner.mettle ->
@db.find 'Planet', { }, (error, documents) =>
assert.equal documents.length, 1
assert.equal documents[0].name, 'Venus'
@next()
@next()