From 6c8d335ca5bcf20b469554c2b7e446bbe72667eb Mon Sep 17 00:00:00 2001 From: srstrong Date: Tue, 11 Oct 2011 16:55:21 +0300 Subject: [PATCH 01/17] Updated to match Tag type of 6 - although deprecated in the spec, MongoDB V2.0 is returning it in queries --- src/bson_binary.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index d96fbcf..38d1acf 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -47,6 +47,7 @@ get_field (<>) -> 3 -> get_document (Bin1); 4 -> get_array (Bin1); 5 -> {BinType, Bin, Bin2} = get_binary (Bin1), {{bin, BinType, Bin}, Bin2}; + 6 -> {null, Bin1}; 7 -> {Oid, Bin2} = get_oid (Bin1), {{Oid}, Bin2}; 8 -> <> = Bin1, {case Bit of 0 -> false; 1 -> true end, Bin2}; 9 -> get_unixtime (Bin1); From cd2e237b0e563416e717aa65f93c479d05ef1b32 Mon Sep 17 00:00:00 2001 From: Arlen Cuss Date: Fri, 2 Mar 2012 17:16:10 +1100 Subject: [PATCH 02/17] README cleanup, correct URL. --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 28e581e..02745e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -This is the Bson implementation for Erlang. Bson is a record-like data type with a standard binary representation defined at . This implements version 1.0 of that spec. The standard binary form allows for easy data interchange between systems. In particular, [MongoDB](http://www.mongodb.org) uses it for exchanging data between the MongoDB server and its clients. +This is the BSON implementation for Erlang. -The root Bson data type is `bson:document()`. Conceptually, it is a list of name-value pairs, analogous to an associative array, dictionary, or record. However, in this implementation, for writability and readability, the list of pairs is flattened, (ie. the tuples for each pair are elided), and the list is actually a tuple to distinguish it from list (array) of values. So a document is a tuple with alternating name and value elements, where a name is an `atom()` and a value is a `bson:value()`, which includes basic types like `boolean()`, `number()`, `atom()`, `bson:utf8()` (string), and compound types like `[bson:value()]` and `bson:document()`. For example, +BSON is a record-like data type with a standard binary representation defined at . This implements version 1.0 of that spec. The standard binary form allows for easy data interchange between systems. In particular, [MongoDB](http://www.mongodb.org) uses it for exchanging data between the MongoDB server and its clients. + +The root BSON data type is `bson:document()`, a list of name-value pairs, analogous to an associative array, dictionary, or record. In this implementation, for writability and readability, the list of pairs is flattened (i.e. the tuples for each pair are elided), and the list is actually a tuple to distinguish it from list (array) of values. Hence a document is a tuple with alternating name and value elements, where a name is an `atom()` and a value is a `bson:value()`, which includes basic types like `boolean()`, `number()`, `atom()`, `bson:utf8()` (string), and compound types like `[bson:value()]` and `bson:document()`. For example, > Doc = {x,<<"abc">>, y,[1,2,3], z,{a,'Foo', b,4.2}}. @@ -15,8 +17,8 @@ is a document with three fields: `{x,<<"abc">>}` and `{y,[1,2,3]}`, and `{z,{a,' > {x,<<"abc">>, y,[1,2,3], z,null, w,1} = bson:merge ({w,1, z,null}, Doc). > {w,1, x,<<"abc">>, y,[1,2,3], z,{a,'Foo', b,4.2}} = bson:append ({w,1}, Doc). -For the full list of `bson:value()` types see the [bson](http://github.com/TonyGen/bson-erlang/blob/master/src/bson.erl) module. Notice that an Erlang `string()` will be interpreted as a list of integers, so remember to alway delimit string literals with binary brackets (eg. `<<"abc">>`) and convert string variables using `bson:utf8`. You may be tempted to use atoms instead of strings, but you should only use atoms for enumerated types. +For the full list of `bson:value()` types see the [bson](http://github.com/mongodb/bson-erlang/blob/master/src/bson.erl) module. Notice that an Erlang `string()` will be interpreted as a list of integers, so remember to alway delimit string literals with binary brackets (eg. `<<"abc">>`) and convert string variables using `bson:utf8`. You may be tempted to use atoms instead of strings, but you should only use atoms for enumerated types. -There are some special `bson:value()` types like `bson:javascript()` that are tagged tuples, eg. `{javascript, {x,1}, <<"function (y) {return y + x}">>}`. But embedded documents are also tuples, so how do we distinguish between the two? The answer is the `bson:value()` types that are tagged tuples are purposely defined to have an odd number of elements to distinguish them from documents which have an even number of elements. +There are some special `bson:value()` types like `bson:javascript()` that are tagged tuples, eg. `{javascript, {x,1}, <<"function (y) {return y + x}">>}`. But embedded documents are also tuples, so how do we distinguish between the two? Tagged tuple `bson:value()` values intentionally have an odd number of elements, to distinguish them from documents, which always have an even number of elements, as they store key-value pairs. -[API Docs](http://api.mongodb.org/erlang/bson/) - Documentation generated from source code comments +[API Docs](http://api.mongodb.org/erlang/bson/) - Documentation generated from source code comments. From 39229190b8d90a1ceafaed51d5754a8f6dce33f5 Mon Sep 17 00:00:00 2001 From: Arlen Cuss Date: Fri, 2 Mar 2012 17:16:23 +1100 Subject: [PATCH 03/17] 'undefined' -> BSON null; BSON null -> 'undefined' --- src/bson_binary.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index d96fbcf..2ed470f 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -16,6 +16,7 @@ put_field (Name, Value) -> case Value of false -> <>; true -> <>; null -> <>; + undefined -> <>; 'MIN_KEY' -> <>; 'MAX_KEY' -> <>; {Oid} -> <>; @@ -50,7 +51,7 @@ get_field (<>) -> 7 -> {Oid, Bin2} = get_oid (Bin1), {{Oid}, Bin2}; 8 -> <> = Bin1, {case Bit of 0 -> false; 1 -> true end, Bin2}; 9 -> get_unixtime (Bin1); - 10 -> {null, Bin1}; + 10 -> {undefined, Bin1}; 11 -> {Pat, Bin2} = get_cstring (Bin1), {Opt, Bin3} = get_cstring (Bin2), From 82973b31bc47fd68d3c42eca639334779a49f0ef Mon Sep 17 00:00:00 2001 From: Arlen Cuss Date: Fri, 2 Mar 2012 17:16:23 +1100 Subject: [PATCH 04/17] 'undefined' -> BSON null; BSON null -> 'undefined' --- src/bson_binary.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index d96fbcf..2ed470f 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -16,6 +16,7 @@ put_field (Name, Value) -> case Value of false -> <>; true -> <>; null -> <>; + undefined -> <>; 'MIN_KEY' -> <>; 'MAX_KEY' -> <>; {Oid} -> <>; @@ -50,7 +51,7 @@ get_field (<>) -> 7 -> {Oid, Bin2} = get_oid (Bin1), {{Oid}, Bin2}; 8 -> <> = Bin1, {case Bit of 0 -> false; 1 -> true end, Bin2}; 9 -> get_unixtime (Bin1); - 10 -> {null, Bin1}; + 10 -> {undefined, Bin1}; 11 -> {Pat, Bin2} = get_cstring (Bin1), {Opt, Bin3} = get_cstring (Bin2), From be3872e410e52b3ef90ca199e32309bbd051a90e Mon Sep 17 00:00:00 2001 From: Arlen Cuss Date: Fri, 2 Mar 2012 23:41:10 +1100 Subject: [PATCH 05/17] Fix broken test. Shame on me! --- src/bson_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bson_tests.erl b/src/bson_tests.erl index 6118572..e579201 100644 --- a/src/bson_tests.erl +++ b/src/bson_tests.erl @@ -48,7 +48,7 @@ binary_test() -> k1, false, k2, true, l, Time, - m, null, + m, undefined, n, {regex, <<"foo">>, <<"bar">>}, o1, {javascript, {}, <<"function(x) = x + 1;">>}, o2, {javascript, {x, 0, y, <<"foo">>}, <<"function(a) = a + x">>}, From 25731b3a6a23e787954004aa1984ec63c2ac2904 Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Mon, 12 Mar 2012 11:26:54 +0400 Subject: [PATCH 06/17] Added '-type utf8()' to exports -- it's referenced from 'mongodb-erlang' --- src/bson.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bson.erl b/src/bson.erl index 58c3d34..e7a21ad 100644 --- a/src/bson.erl +++ b/src/bson.erl @@ -5,7 +5,7 @@ -export_type ([document/0, label/0, value/0]). -export_type ([bin/0, bfunction/0, uuid/0, md5/0, userdefined/0]). -export_type ([mongostamp/0, minmaxkey/0]). --export_type ([regex/0, unixtime/0]). +-export_type ([utf8/0, regex/0, unixtime/0]). -export_type ([javascript/0]). -export_type ([objectid/0, unixsecs/0]). From 43522e3aaaa6345616b4afcbb98f8f8da241d1d1 Mon Sep 17 00:00:00 2001 From: Sergei Lebedev Date: Mon, 12 Mar 2012 11:58:30 +0400 Subject: [PATCH 07/17] Added '-type arr()' to exports -- it's referenced from 'bson_binary.erl' --- src/bson.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bson.erl b/src/bson.erl index e7a21ad..43e559e 100644 --- a/src/bson.erl +++ b/src/bson.erl @@ -3,6 +3,7 @@ -export_type ([maybe/1]). -export_type ([document/0, label/0, value/0]). +-export_type ([arr/0]). -export_type ([bin/0, bfunction/0, uuid/0, md5/0, userdefined/0]). -export_type ([mongostamp/0, minmaxkey/0]). -export_type ([utf8/0, regex/0, unixtime/0]). From 1a39ba0111376daebdc45959fc7ae0aa7b758471 Mon Sep 17 00:00:00 2001 From: Julian Thatcher Date: Fri, 16 Mar 2012 01:36:51 +1100 Subject: [PATCH 08/17] bson_binary: Allow keys to be types other than atoms This is mainly nice for complex updates and selectors when using bson in conjunction with mongodb. --- src/bson_binary.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index 2ed470f..c4e6b47 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -32,7 +32,7 @@ put_field (Name, Value) -> case Value of is_binary (V) -> <>; is_tuple (V) -> <>; is_list (V) -> <>; - is_atom (V) -> <>; + is_atom (V) -> <>; is_integer (V) -> if ?fits_int32 (V) -> <>; ?fits_int64 (V) -> <>; @@ -91,7 +91,7 @@ put_document (Document) -> Bin = bson:doc_foldl (fun put_field_accum/3, <<>>, Document), <>. put_field_accum (Label, Value, Bin) -> - <>. + <>. -spec get_document (binary()) -> {bson:document(), binary()}. get_document (<>) -> @@ -162,3 +162,9 @@ put_oid (<>) -> Oid. -spec get_oid (binary()) -> {<<_:96>>, binary()}. get_oid (<>) -> {Oid, Bin}. + +-spec to_binary(term()) -> binary(). +to_binary(Bin) when is_binary(Bin) -> Bin; +to_binary(List) when is_list(List) -> list_to_binary(List); +to_binary(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8); +to_binary(Int) when is_integer(Int) -> to_binary(integer_to_list(Int)). From 3f9574c6db4b3098199863c5719ea26e1c0e6661 Mon Sep 17 00:00:00 2001 From: Julian Thatcher Date: Fri, 16 Mar 2012 14:30:33 +1100 Subject: [PATCH 09/17] Revert "bson_binary: Allow keys to be types other than atoms" This reverts commit 1a39ba0111376daebdc45959fc7ae0aa7b758471. --- src/bson_binary.erl | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index c4e6b47..2ed470f 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -32,7 +32,7 @@ put_field (Name, Value) -> case Value of is_binary (V) -> <>; is_tuple (V) -> <>; is_list (V) -> <>; - is_atom (V) -> <>; + is_atom (V) -> <>; is_integer (V) -> if ?fits_int32 (V) -> <>; ?fits_int64 (V) -> <>; @@ -91,7 +91,7 @@ put_document (Document) -> Bin = bson:doc_foldl (fun put_field_accum/3, <<>>, Document), <>. put_field_accum (Label, Value, Bin) -> - <>. + <>. -spec get_document (binary()) -> {bson:document(), binary()}. get_document (<>) -> @@ -162,9 +162,3 @@ put_oid (<>) -> Oid. -spec get_oid (binary()) -> {<<_:96>>, binary()}. get_oid (<>) -> {Oid, Bin}. - --spec to_binary(term()) -> binary(). -to_binary(Bin) when is_binary(Bin) -> Bin; -to_binary(List) when is_list(List) -> list_to_binary(List); -to_binary(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8); -to_binary(Int) when is_integer(Int) -> to_binary(integer_to_list(Int)). From 3508aad5342f683612121b357894c449ac6d21ab Mon Sep 17 00:00:00 2001 From: Julian Thatcher Date: Fri, 16 Mar 2012 14:30:44 +1100 Subject: [PATCH 10/17] Allow put_field_accum to take binary keys --- src/bson_binary.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index 2ed470f..79e64cb 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -90,8 +90,10 @@ get_cstring (Bin) -> % list_to_tuple (binary:split (Bin, <<0>>)). put_document (Document) -> Bin = bson:doc_foldl (fun put_field_accum/3, <<>>, Document), <>. -put_field_accum (Label, Value, Bin) -> - <>. +put_field_accum (Label, Value, Bin) when is_atom(Label) -> + <>; +put_field_accum (Label, Value, Bin) when is_binary(Label) -> + <>. -spec get_document (binary()) -> {bson:document(), binary()}. get_document (<>) -> From 05c838e2ef1e05cabb6a728357dbf78efa01a4cb Mon Sep 17 00:00:00 2001 From: Daniel Pasette Date: Fri, 23 Mar 2012 12:08:03 -0300 Subject: [PATCH 11/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28e581e..90a1c09 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ is a document with three fields: `{x,<<"abc">>}` and `{y,[1,2,3]}`, and `{z,{a,' > {x,<<"abc">>, y,[1,2,3], z,null, w,1} = bson:merge ({w,1, z,null}, Doc). > {w,1, x,<<"abc">>, y,[1,2,3], z,{a,'Foo', b,4.2}} = bson:append ({w,1}, Doc). -For the full list of `bson:value()` types see the [bson](http://github.com/TonyGen/bson-erlang/blob/master/src/bson.erl) module. Notice that an Erlang `string()` will be interpreted as a list of integers, so remember to alway delimit string literals with binary brackets (eg. `<<"abc">>`) and convert string variables using `bson:utf8`. You may be tempted to use atoms instead of strings, but you should only use atoms for enumerated types. +For the full list of `bson:value()` types see the [bson](http://github.com/mongodb/bson-erlang/blob/master/src/bson.erl) module. Notice that an Erlang `string()` will be interpreted as a list of integers, so remember to alway delimit string literals with binary brackets (eg. `<<"abc">>`) and convert string variables using `bson:utf8`. You may be tempted to use atoms instead of strings, but you should only use atoms for enumerated types. There are some special `bson:value()` types like `bson:javascript()` that are tagged tuples, eg. `{javascript, {x,1}, <<"function (y) {return y + x}">>}`. But embedded documents are also tuples, so how do we distinguish between the two? The answer is the `bson:value()` types that are tagged tuples are purposely defined to have an odd number of elements to distinguish them from documents which have an even number of elements. From 73fac727f470af6535bf9ba2ecdf3e15b1f1e4e4 Mon Sep 17 00:00:00 2001 From: Arlen Cuss Date: Tue, 3 Jul 2012 12:42:10 +1000 Subject: [PATCH 12/17] Treat deprecated "undefined" as 'null'. --- src/bson_binary.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bson_binary.erl b/src/bson_binary.erl index 79e64cb..8f7f2ee 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -48,6 +48,7 @@ get_field (<>) -> 3 -> get_document (Bin1); 4 -> get_array (Bin1); 5 -> {BinType, Bin, Bin2} = get_binary (Bin1), {{bin, BinType, Bin}, Bin2}; + 6 -> {undefined, Bin1}; % Treat the deprecated "undefined" value as null, which we call 'undefined'! 7 -> {Oid, Bin2} = get_oid (Bin1), {{Oid}, Bin2}; 8 -> <> = Bin1, {case Bit of 0 -> false; 1 -> true end, Bin2}; 9 -> get_unixtime (Bin1); From ac97a99d2fc6afdd90c439866d4e337831b38e38 Mon Sep 17 00:00:00 2001 From: Ali Sabil Date: Fri, 13 Jul 2012 17:56:02 +0200 Subject: [PATCH 13/17] Add bson:merge/3 bson:merge/3 works like bson:merge/2 but resolves conflicts using the provided merging function. --- src/bson.erl | 9 ++++++++- src/bson_tests.erl | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/bson.erl b/src/bson.erl index 43e559e..827f60c 100644 --- a/src/bson.erl +++ b/src/bson.erl @@ -10,7 +10,7 @@ -export_type ([javascript/0]). -export_type ([objectid/0, unixsecs/0]). --export ([lookup/2, lookup/3, at/2, include/2, exclude/2, update/3, merge/2, append/2]). +-export ([lookup/2, lookup/3, at/2, include/2, exclude/2, update/3, merge/2, merge/3, append/2]). -export ([doc_foldl/3, doc_foldr/3, fields/1, document/1]). -export ([utf8/1, str/1]). -export ([timenow/0, ms_precision/1, secs_to_unixtime/1, unixtime_to_secs/1]). @@ -120,6 +120,13 @@ merge (UpDoc, BaseDoc) -> Fun = fun (Label, Value, Doc) -> update (Label, Value, Doc) end, doc_foldl (Fun, BaseDoc, UpDoc). +-spec merge (document(), document(), fun((label(), value(), value()) -> value())) -> document(). +merge(UpDoc, BaseDoc, Fun) -> + Dict1 = orddict:from_list(bson:fields(UpDoc)), + Dict2 = orddict:from_list(bson:fields(BaseDoc)), + bson:document(orddict:merge(Fun, Dict1, Dict2)). + + -spec append (document(), document()) -> document(). %@doc Append two documents together append (Doc1, Doc2) -> list_to_tuple (tuple_to_list (Doc1) ++ tuple_to_list (Doc2)). diff --git a/src/bson_tests.erl b/src/bson_tests.erl index e579201..31240c5 100644 --- a/src/bson_tests.erl +++ b/src/bson_tests.erl @@ -16,6 +16,10 @@ bson_test() -> {a, 1} = bson:exclude ([b,c], Doc), {b, {x, 2, y, 3}, a, 1, c, 4.2} = bson:update (c, 4.2, Doc), {b, 0, a, 1, c, 2, d, 3} = bson:merge ({c, 2, d, 3, b, 0}, Doc), + {a, 1, b, {x, 2, y, 3}, c, 2, d, 3} = bson:merge ({c, 2, d, 3, b, 0}, Doc, fun + (b, _Value1, Value2) -> Value2; + (c, Value1, _Value2) -> Value1 + end), {a, 1, b, 2, c, 3, d, 4} = bson:append ({a, 1, b, 2}, {c, 3, d, 4}), [{b, {x, 2, y, 3}}, {a, 1}, {c, [mon, tue, wed]}] = bson:fields (Doc). From 5200e7122181a6387c14b853946c4bd2a298b0da Mon Sep 17 00:00:00 2001 From: Ali Sabil Date: Fri, 13 Jul 2012 18:05:54 +0200 Subject: [PATCH 14/17] Remove bson_tests and embed the tests into the bson and bson_binary modules --- src/bson.erl | 37 +++++++++++++++++++++++++ src/bson_binary.erl | 38 ++++++++++++++++++++++++++ src/bson_tests.erl | 66 --------------------------------------------- 3 files changed, 75 insertions(+), 66 deletions(-) delete mode 100644 src/bson_tests.erl diff --git a/src/bson.erl b/src/bson.erl index 827f60c..7d74816 100644 --- a/src/bson.erl +++ b/src/bson.erl @@ -238,3 +238,40 @@ objectid (UnixSecs, MachineAndProcId, Count) -> -spec objectid_time (objectid()) -> unixtime(). %@doc Time when object id was generated objectid_time ({<>}) -> secs_to_unixtime (UnixSecs). + + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +bson_test() -> + Doc = {b, {x, 2, y, 3}, + a, 1, + c, [mon, tue, wed]}, + {1} = bson:lookup (a, Doc), + {} = bson:lookup (d, Doc), + 2 = bson:lookup (d, Doc, 2), + 1 = bson:lookup (a, Doc, 3), + 1 = bson:at (a, Doc), + {'EXIT', {missing_field, _}} = (catch bson:at (d, Doc)), + {a, 1} = bson:include ([a], Doc), + {a, 1} = bson:exclude ([b,c], Doc), + {b, {x, 2, y, 3}, a, 1, c, 4.2} = bson:update (c, 4.2, Doc), + {b, 0, a, 1, c, 2, d, 3} = bson:merge ({c, 2, d, 3, b, 0}, Doc), + {a, 1, b, {x, 2, y, 3}, c, 2, d, 3} = bson:merge ({c, 2, d, 3, b, 0}, Doc, fun + (b, _Value1, Value2) -> Value2; + (c, Value1, _Value2) -> Value1 + end), + {a, 1, b, 2, c, 3, d, 4} = bson:append ({a, 1, b, 2}, {c, 3, d, 4}), + [{b, {x, 2, y, 3}}, {a, 1}, {c, [mon, tue, wed]}] = bson:fields (Doc). + +time_test() -> + {MegaSecs, Secs, _} = bson:timenow(), + {MegaSecs, Secs, 0} = bson:secs_to_unixtime (bson:unixtime_to_secs ({MegaSecs, Secs, 0})). + +objectid_test() -> + {<<1:32/big, 2:24/big, 3:16/big, 4:24/big>>} = bson:objectid (1, <<2:24/big, 3:16/big>>, 4), + UnixSecs = bson:unixtime_to_secs (bson:timenow()), + UnixTime = bson:objectid_time (bson:objectid (UnixSecs, <<2:24/big, 3:16/big>>, 4)), + UnixSecs = bson:unixtime_to_secs (UnixTime). + +-endif. diff --git a/src/bson_binary.erl b/src/bson_binary.erl index 8f7f2ee..a5de084 100644 --- a/src/bson_binary.erl +++ b/src/bson_binary.erl @@ -165,3 +165,41 @@ put_oid (<>) -> Oid. -spec get_oid (binary()) -> {<<_:96>>, binary()}. get_oid (<>) -> {Oid, Bin}. + + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +binary_test() -> + Doc = {'BSON', [<<"awesome">>, 5.05, 1986]}, + Bin = bson_binary:put_document (Doc), + Bin = <<49,0,0,0,4,66,83,79,78,0,38,0,0,0,2,48,0,8,0,0,0,97,119,101,115,111,109,101,0,1,49,0,51,51,51,51,51,51,20,64,16,50,0,194,7,0,0,0,0>>, + VBin = <<200,12,240,129,100,90,56,198,34,0,0>>, + Time = bson:timenow(), + Doc1 = {a, -4.230845, + b, <<"hello">>, + c, {x, -1, y, 2.2001}, + d, [23, 45, 200], + eeeeeeeee, {bin, bin, VBin}, + f, {bin, function, VBin}, + g, {bin, uuid, Bin}, + h, {bin, md5, VBin}, + i, {bin, userdefined, Bin}, + j, bson:objectid (bson:unixtime_to_secs (Time), <<2:24/big, 3:16/big>>, 4), + k1, false, + k2, true, + l, Time, + m, undefined, + n, {regex, <<"foo">>, <<"bar">>}, + o1, {javascript, {}, <<"function(x) = x + 1;">>}, + o2, {javascript, {x, 0, y, <<"foo">>}, <<"function(a) = a + x">>}, + p, atom, + q1, -2000444000, + q2, -8000111000222001, + r, {mongostamp, 100022, 995332003}, + s1, 'MIN_KEY', + s2, 'MAX_KEY'}, + Bin1 = bson_binary:put_document (Doc1), + {Doc1, <<>>} = bson_binary:get_document (Bin1). + +-endif. diff --git a/src/bson_tests.erl b/src/bson_tests.erl deleted file mode 100644 index 31240c5..0000000 --- a/src/bson_tests.erl +++ /dev/null @@ -1,66 +0,0 @@ --module(bson_tests). - --include_lib("eunit/include/eunit.hrl"). - -bson_test() -> - Doc = {b, {x, 2, y, 3}, - a, 1, - c, [mon, tue, wed]}, - {1} = bson:lookup (a, Doc), - {} = bson:lookup (d, Doc), - 2 = bson:lookup (d, Doc, 2), - 1 = bson:lookup (a, Doc, 3), - 1 = bson:at (a, Doc), - {'EXIT', {missing_field, _}} = (catch bson:at (d, Doc)), - {a, 1} = bson:include ([a], Doc), - {a, 1} = bson:exclude ([b,c], Doc), - {b, {x, 2, y, 3}, a, 1, c, 4.2} = bson:update (c, 4.2, Doc), - {b, 0, a, 1, c, 2, d, 3} = bson:merge ({c, 2, d, 3, b, 0}, Doc), - {a, 1, b, {x, 2, y, 3}, c, 2, d, 3} = bson:merge ({c, 2, d, 3, b, 0}, Doc, fun - (b, _Value1, Value2) -> Value2; - (c, Value1, _Value2) -> Value1 - end), - {a, 1, b, 2, c, 3, d, 4} = bson:append ({a, 1, b, 2}, {c, 3, d, 4}), - [{b, {x, 2, y, 3}}, {a, 1}, {c, [mon, tue, wed]}] = bson:fields (Doc). - -time_test() -> - {MegaSecs, Secs, _} = bson:timenow(), - {MegaSecs, Secs, 0} = bson:secs_to_unixtime (bson:unixtime_to_secs ({MegaSecs, Secs, 0})). - -objectid_test() -> - {<<1:32/big, 2:24/big, 3:16/big, 4:24/big>>} = bson:objectid (1, <<2:24/big, 3:16/big>>, 4), - UnixSecs = bson:unixtime_to_secs (bson:timenow()), - UnixTime = bson:objectid_time (bson:objectid (UnixSecs, <<2:24/big, 3:16/big>>, 4)), - UnixSecs = bson:unixtime_to_secs (UnixTime). - -binary_test() -> - Doc = {'BSON', [<<"awesome">>, 5.05, 1986]}, - Bin = bson_binary:put_document (Doc), - Bin = <<49,0,0,0,4,66,83,79,78,0,38,0,0,0,2,48,0,8,0,0,0,97,119,101,115,111,109,101,0,1,49,0,51,51,51,51,51,51,20,64,16,50,0,194,7,0,0,0,0>>, - VBin = <<200,12,240,129,100,90,56,198,34,0,0>>, - Time = bson:timenow(), - Doc1 = {a, -4.230845, - b, <<"hello">>, - c, {x, -1, y, 2.2001}, - d, [23, 45, 200], - eeeeeeeee, {bin, bin, VBin}, - f, {bin, function, VBin}, - g, {bin, uuid, Bin}, - h, {bin, md5, VBin}, - i, {bin, userdefined, Bin}, - j, bson:objectid (bson:unixtime_to_secs (Time), <<2:24/big, 3:16/big>>, 4), - k1, false, - k2, true, - l, Time, - m, undefined, - n, {regex, <<"foo">>, <<"bar">>}, - o1, {javascript, {}, <<"function(x) = x + 1;">>}, - o2, {javascript, {x, 0, y, <<"foo">>}, <<"function(a) = a + x">>}, - p, atom, - q1, -2000444000, - q2, -8000111000222001, - r, {mongostamp, 100022, 995332003}, - s1, 'MIN_KEY', - s2, 'MAX_KEY'}, - Bin1 = bson_binary:put_document (Doc1), - {Doc1, <<>>} = bson_binary:get_document (Bin1). From d3ecc7fe9a0946d27bc64300b30151e129bd6ee0 Mon Sep 17 00:00:00 2001 From: Ali Sabil Date: Fri, 13 Jul 2012 18:06:31 +0200 Subject: [PATCH 15/17] Replace bson.app with a bson.app.src --- ebin/bson.app | 7 ------- src/bson.app.src | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 ebin/bson.app create mode 100644 src/bson.app.src diff --git a/ebin/bson.app b/ebin/bson.app deleted file mode 100644 index 9fa5b43..0000000 --- a/ebin/bson.app +++ /dev/null @@ -1,7 +0,0 @@ -{application, bson, - [{description, "BSON are JSON-like objects with a standard binary serialization. See bsonspec.org."}, - {vsn, "0"}, - {modules, [bson, bson_binary, bson_tests]}, - {registered, []}, - {applications, [kernel, stdlib]} - ]}. diff --git a/src/bson.app.src b/src/bson.app.src new file mode 100644 index 0000000..1f2634c --- /dev/null +++ b/src/bson.app.src @@ -0,0 +1,7 @@ +%% ex: ts=4 sw=4 noexpandtab syntax=erlang +{application, bson, [ + {description, "BSON are JSON-like objects with a standard binary serialization. See bsonspec.org"}, + {vsn, git}, + {registered, []}, + {applications, [kernel, stdlib]} +]}. From 3e6d7d5c58935768104f38bf9df96f213e139df9 Mon Sep 17 00:00:00 2001 From: Ali Sabil Date: Fri, 20 Jul 2012 15:30:09 +0200 Subject: [PATCH 16/17] Add initial implementation of bson_schema bson_schema is a helper module allowing the validation of bson documents against a specified schema. --- src/bson_schema.erl | 108 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/bson_schema.erl diff --git a/src/bson_schema.erl b/src/bson_schema.erl new file mode 100644 index 0000000..f4ca092 --- /dev/null +++ b/src/bson_schema.erl @@ -0,0 +1,108 @@ +-module (bson_schema). + +-export ([validate/2, validate_value/2]). + + +-spec validate(bson:document(), bson:document()) -> bson:document(). +validate(Document, Spec) -> + bson:doc_foldl(fun(Key, KeySpec, Acc) -> + case bson:lookup(Key, Document) of + {Value} -> + bson:update(Key, validate_value(Value, KeySpec), Acc); + {} -> + case lists:member(required, KeySpec) of + true -> error(badarg, [Document, Spec]); + false -> Acc + end + end + end, {}, Spec). + + +-spec validate_value(term(), list()) -> term(). +validate_value(Value, []) -> + Value; + +validate_value(Value, [required | Rest]) -> + validate_value(Value, Rest); + +validate_value({<<_:96>>} = Value, [object_id | Rest]) -> + validate_value(Value, Rest); +validate_value(Value, [object_id | _] = Spec) -> + error(badarg, [Value, Spec]); + +validate_value(Value, [{atom, Values} | Rest] = Spec) -> + try lists:foreach(fun(V) -> + case V == Value orelse atom_to_binary(V, unicode) == Value of + true -> throw(V); + false -> false + end + end, Values) of + _ -> error(badarg, [Value, Spec]) + catch + throw:V -> validate_value(V, Rest) + end; + +validate_value(Value, [utf8 | Rest] = Spec) -> + case unicode:characters_to_binary(Value) of + {error, _, _} -> error(badarg, [Value, Spec]); + {incomplete, _, _} -> error(badarg, [Value, Spec]); + Data -> validate_value(Data, Rest) + end; + +validate_value(Value, [{length, Min, Max} | Rest] = Spec) when is_list(Value) -> + case within(length(Value), Min, Max) of + true -> validate_value(Value, Rest); + false -> error(badarg, [Value, Spec]) + end; +validate_value(Value, [{length, Min, Max} | Rest] = Spec) when is_binary(Value) -> + case within(byte_size(Value), Min, Max) of + true -> validate_value(Value, Rest); + false -> error(badarg, [Value, Spec]) + end; +validate_value(Value, [{length, _Min, _Max} | _] = Spec) -> + error(badarg, [Value, Spec]); + +validate_value(Value, [{float, Min, Max} | Rest] = Spec) when is_float(Value) -> + case within(Value, Min, Max) of + true -> validate_value(Value, Rest); + false -> error(badarg, [Value, Spec]) + end; +validate_value(Value, [{float, _Min, _Max} | _] = Spec) -> + error(badarg, [Value, Spec]); + +validate_value(Value, [{integer, Min, Max} | Rest] = Spec) when is_integer(Value) -> + case within(Value, Min, Max) of + true -> validate_value(Value, Rest); + false -> error(badarg, [Value, Spec]) + end; +validate_value(Value, [{integer, _Min, _Max} | _] = Spec) -> + error(badarg, [Value, Spec]); + +validate_value({A, B, C} = Value, [timestamp | Rest]) when is_integer(A), is_integer(B), is_integer(C) -> + validate_value(Value, Rest); +validate_value(Value, [timestamp | _] = Spec) -> + error(badarg, [Value, Spec]); + +validate_value(Value, [{list, Subspec} | Rest]) when is_list(Value) -> + validate_value([validate_value(V, Subspec) || V <- Value], Rest); +validate_value(Value, [{list, _Subspec} | _] = Spec) -> + error(badarg, [Value, Spec]); + +validate_value(Value, [{object, Subspec} | Rest]) when is_tuple(Value) -> + validate_value(validate(Value, Subspec), Rest); +validate_value(Value, [{object, _Subspec} | _] = Spec) -> + error(badarg, [Value, Spec]). + + +% @private +within(Value, '-infinity', Max) -> + Value =< Max; +within(Value, Min, Max) -> + Value >= Min andalso Value =< Max. + + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + + +-endif. From 9f6ec2c51db05a06c01c0ada0670dcdb6d11d731 Mon Sep 17 00:00:00 2001 From: Steve Strong Date: Tue, 24 Dec 2013 14:18:02 +0100 Subject: [PATCH 17/17] Added modules to .app.src --- src/bson.app.src | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bson.app.src b/src/bson.app.src index 1f2634c..31c0aeb 100644 --- a/src/bson.app.src +++ b/src/bson.app.src @@ -1,7 +1,9 @@ %% ex: ts=4 sw=4 noexpandtab syntax=erlang -{application, bson, [ +{application, bson, + [ {description, "BSON are JSON-like objects with a standard binary serialization. See bsonspec.org"}, - {vsn, git}, + {vsn, "1"}, {registered, []}, + {modules, []}, {applications, [kernel, stdlib]} ]}.