faststep 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Josh Clayton
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,84 @@
1
+ # Faststep
2
+
3
+ ## Mongo on Speed
4
+
5
+ ### Install
6
+
7
+ Add to your Gemfile and `bundle`; otherwise,
8
+
9
+ gem install faststep
10
+
11
+ ### Usage
12
+
13
+ Connect to mongo:
14
+
15
+ >> conn = Faststep::Connection.new("127.0.0.1", 27017)
16
+
17
+ Pick a database:
18
+
19
+ >> db = conn.db("awesome_development")
20
+
21
+ Have some fun!
22
+
23
+ >> db["users"].count
24
+ => 0
25
+ >> db["users"].insert(10000.times.map {|i| { :name => "Person #{i}" } }
26
+ => true
27
+ >> db["users"].count
28
+ => 10000
29
+ >> db["users"].count(:name => /0$/)
30
+ => 1000
31
+ >> db["users"].find(:name => /0$/).to_a
32
+ => [{"_id"=>BSON::ObjectId('4d83c53555ca381d572c1cf7'), "name"=>"Person 0"}, {"_id"=>BSON::ObjectId('4d83c53555ca381d572c1d01'), "name"=>"Person 10"}, ...]
33
+ >> db["users"].remove(:name => /0$/)
34
+ => nil
35
+ >> db["users"].count(:name => /0$/)
36
+ => 0
37
+ >> db["users"].count
38
+ => 9000
39
+ >> db["users"].drop
40
+ => true
41
+ >> db.drop
42
+ => true
43
+
44
+ ### Caveats
45
+
46
+ This is under heavy development (what gem isn't?) but there's a lot of stuff to be implemented still. Some methods return nil (remove, for example) when they shouldn't.
47
+ It's not nearly as smart as the mongo Ruby driver and it doesn't handle errors that well.
48
+
49
+ ### Todo
50
+
51
+ * Connection pooling
52
+ * Safe mode
53
+ * Allow updates to *not* be multi (currently unconfigurable)
54
+ * Master/slave
55
+ * Replica sets
56
+
57
+ Basically, most of http://www.mongodb.org/display/DOCS/Feature+Checklist+for+Mongo+Drivers
58
+
59
+ ### About
60
+
61
+ This is a C extension that uses the [Mongo C driver](https://github.com/mongodb/mongo-c-driver).
62
+ As of right now, I haven't modified any of the code provided in that driver in here.
63
+ That code's Apache license still applies, and since it's not a derivative work (I haven't
64
+ changed any of the source), I'm using an MIT license for Faststep.
65
+
66
+ I've basically copied the benchmark from the [Mongo Ruby driver](https://github.com/mongodb/mongo-ruby-driver)
67
+ to test Faststep's speed, and all operations are anywhere between 10% (finding ranges on large docs)
68
+ and 120% faster (inserting small/medium indexed documents and inserting small/medium documents in batches).
69
+ See https://gist.github.com/870297 for benchmarks.
70
+
71
+ I started writing this driver for a project I'm working on called [Crowdtap](http://crowdtap.com),
72
+ which uses Mongo as its primary data store. I'd dabbled in C extensions before and
73
+ figured I could leverage this knowledge to speed up certain operations we were doing.
74
+ If it wasn't for them and their belief that I could get something usable in a short time,
75
+ this project probably wouldn't exist; thanks guys!
76
+
77
+ ### License
78
+
79
+ The Mongo C driver code has retained its Apache license; I've released faststep under an MIT license (see
80
+ LICENSE for details).
81
+
82
+ ### Author
83
+
84
+ Written 2011 by Josh Clayton.
@@ -1,7 +1,7 @@
1
1
  #include "bson_ruby_conversion.h"
2
2
  #include "faststep_defines.h"
3
3
 
4
- bson* create_bson_from_ruby_hash(VALUE hash) {
4
+ bson* create_bson_from_ruby_hash(const VALUE hash) {
5
5
  bson* document = bson_malloc(sizeof(bson));
6
6
 
7
7
  if(NIL_P(hash)) {
@@ -18,27 +18,53 @@ bson* create_bson_from_ruby_hash(VALUE hash) {
18
18
  return document;
19
19
  }
20
20
 
21
- VALUE bool_to_ruby(bson_bool_t result) {
22
- return result ? Qtrue : Qfalse;
21
+ bson* bson_from_ruby_array(const VALUE array) {
22
+ VALUE hash = rb_hash_new();
23
+
24
+ if(RTEST(array)) {
25
+ int iterator;
26
+ for(iterator = 0; iterator < RARRAY_LEN(array); iterator++) {
27
+ rb_hash_aset(hash, rb_ary_entry(array, iterator), INT2NUM(1));
28
+ }
29
+ }
30
+
31
+ create_bson_from_ruby_hash(hash);
32
+ }
33
+
34
+ VALUE ruby_array_to_bson_ordered_hash(const VALUE array) {
35
+ VALUE order_as_ordered_hash = rb_funcall(rb_cBsonOrderedHash, rb_intern("new"), 0);
36
+ rb_iterate(rb_each, array, _map_assoc_ary_to_key_value_pair, order_as_ordered_hash);
37
+
38
+ return order_as_ordered_hash;
23
39
  }
24
40
 
25
- VALUE ruby_hash_from_bson(bson* bson) {
41
+ VALUE ruby_hash_from_bson(const bson* bson) {
26
42
  VALUE bson_buf = rb_str_new(bson->data, bson_size(bson));
27
43
 
28
44
  return rb_funcall(rb_mBson, rb_intern("deserialize"), 1, bson_buf);
29
45
  }
30
46
 
31
- VALUE ensure_document_ok(VALUE document) {
47
+ VALUE bool_to_ruby(const bson_bool_t result) {
48
+ return result ? Qtrue : Qfalse;
49
+ }
50
+
51
+
52
+ VALUE ensure_document_ok(const VALUE document) {
32
53
  if(rb_funcall(rb_mFaststepSupport, rb_intern("ok?"), 1, document) == Qfalse) {
33
- rb_raise(rb_cFaststepOperationFailure, _invalid_command_description(document));
54
+ rb_raise(rb_eFaststepOperationFailure, _invalid_command_description(document));
34
55
  }
35
56
  }
36
57
 
37
- static char* _invalid_command_description(VALUE document) {
58
+ static char* _invalid_command_description(const VALUE document) {
38
59
  VALUE message = rb_str_new2("Invalid command (");
39
- rb_str_concat(message, rb_funcall(rb_hash_aref(document, rb_str_new2("bad cmd")), rb_intern("inspect"), 0));
60
+ rb_str_concat(message, rb_inspect(rb_hash_aref(document, rb_str_new2("bad cmd"))));
40
61
  rb_str_concat(message, rb_str_new2("): "));
41
62
  rb_str_concat(message, rb_hash_aref(document, rb_str_new2("errmsg")));
42
63
 
43
64
  return RSTRING_PTR(message);
44
65
  }
66
+
67
+ static VALUE _map_assoc_ary_to_key_value_pair(const VALUE item, VALUE hash) {
68
+ rb_hash_aset(hash, rb_ary_entry(item, 0), rb_ary_entry(item, 1));
69
+ return hash;
70
+ }
@@ -2,9 +2,12 @@
2
2
  #include <ruby.h>
3
3
  #include "bson.h"
4
4
  #define BSON_RUBY_CONVERSION_H
5
- bson* create_bson_from_ruby_hash(VALUE);
6
- VALUE ruby_hash_from_bson(bson*);
7
- VALUE ensure_document_ok(VALUE);
8
- VALUE bool_to_ruby(bson_bool_t);
9
- static char* _invalid_command_description(VALUE);
5
+ bson* create_bson_from_ruby_hash(const VALUE);
6
+ bson* bson_from_ruby_array(const VALUE);
7
+ VALUE ruby_array_to_bson_ordered_hash(const VALUE);
8
+ VALUE ruby_hash_from_bson(const bson*);
9
+ VALUE bool_to_ruby(const bson_bool_t);
10
+ VALUE ensure_document_ok(const VALUE);
11
+ static char* _invalid_command_description(const VALUE);
12
+ static VALUE _map_assoc_ary_to_key_value_pair(const VALUE, VALUE);
10
13
  #endif
@@ -11,6 +11,8 @@ void faststep_collection_main() {
11
11
 
12
12
  rb_define_method(rb_cFaststepCollection, "initialize", faststep_collection_init, 2);
13
13
  rb_define_method(rb_cFaststepCollection, "ns", faststep_collection_ns, 0);
14
+ rb_define_method(rb_cFaststepCollection, "find", faststep_collection_find, -1);
15
+ rb_define_method(rb_cFaststepCollection, "find_one", faststep_collection_find_one, -1);
14
16
  rb_define_method(rb_cFaststepCollection, "count", faststep_collection_count, -1);
15
17
  rb_define_method(rb_cFaststepCollection, "insert", faststep_collection_insert, 1);
16
18
  rb_define_method(rb_cFaststepCollection, "update", faststep_collection_update, 2);
@@ -20,14 +22,14 @@ void faststep_collection_main() {
20
22
  return;
21
23
  }
22
24
 
23
- static VALUE faststep_collection_init(VALUE self, VALUE name, VALUE database) {
25
+ static VALUE faststep_collection_init(VALUE self, const VALUE name, const VALUE database) {
24
26
  rb_iv_set(self, "@name", name);
25
27
  rb_iv_set(self, "@db", database);
26
28
 
27
29
  return self;
28
30
  }
29
31
 
30
- VALUE faststep_collection_ns(VALUE self) {
32
+ VALUE faststep_collection_ns(const VALUE self) {
31
33
  VALUE db = rb_iv_get(self, "@db");
32
34
 
33
35
  char ns[255] = "";
@@ -51,7 +53,48 @@ static VALUE faststep_collection_count(int argc, VALUE* argv, VALUE self) {
51
53
  return ULL2NUM(count);
52
54
  }
53
55
 
54
- void build_collection_ns(char* ns, char* database, char* collection) {
56
+ static VALUE faststep_collection_find(int argc, VALUE* argv, VALUE self) {
57
+ VALUE selector, options;
58
+ rb_scan_args(argc, argv, "02", &selector, &options);
59
+ if(!RTEST(selector)) { selector = rb_hash_new(); }
60
+ if(!RTEST(options)) { options = rb_hash_new(); }
61
+
62
+ VALUE full_query = rb_hash_new();
63
+ rb_hash_aset(full_query, rb_str_new2("selector"), selector);
64
+ rb_funcall(full_query, rb_intern("merge"), 1, options);
65
+
66
+ return rb_funcall(rb_cFaststepCursor, rb_intern("new"), 2, self, full_query);
67
+ }
68
+
69
+ static VALUE faststep_collection_find_one(int argc, VALUE* argv, VALUE self) {
70
+ VALUE selector, options;
71
+ rb_scan_args(argc, argv, "02", &selector, &options);
72
+
73
+ if(!RTEST(options)) { options = rb_hash_new(); }
74
+
75
+ VALUE full_query = rb_hash_new();
76
+
77
+ if(RTEST(rb_funcall(selector, rb_intern("is_a?"), 1, rb_cBsonObjectId))) {
78
+ rb_hash_aset(full_query, rb_str_new2("_id"), selector);
79
+ } else {
80
+ switch(TYPE(selector)) {
81
+ case T_NIL:
82
+ break;
83
+ case T_HASH:
84
+ full_query = selector;
85
+ break;
86
+ default:
87
+ rb_raise(rb_eTypeError, "selector must be an instance of BSON::ObjectId, Hash, or nil");
88
+ }
89
+ }
90
+
91
+ rb_hash_aset(options, rb_str_new2("limit"), INT2NUM(-1));
92
+
93
+ VALUE result = rb_funcall(self, rb_intern("find"), 2, full_query, options);
94
+ return rb_funcall(result, rb_intern("first"), 0);
95
+ }
96
+
97
+ void build_collection_ns(char* ns, const char* database, const char* collection) {
55
98
  strcat(ns, database);
56
99
  strcat(ns, ".");
57
100
  strcat(ns, collection);
@@ -59,7 +102,7 @@ void build_collection_ns(char* ns, char* database, char* collection) {
59
102
  return;
60
103
  }
61
104
 
62
- static VALUE faststep_collection_insert(VALUE self, VALUE documents) {
105
+ static VALUE faststep_collection_insert(const VALUE self, const VALUE documents) {
63
106
  mongo_connection* conn = GetFaststepConnectionForCollection(self);
64
107
 
65
108
  VALUE ns = faststep_collection_ns(self);
@@ -73,7 +116,7 @@ static VALUE faststep_collection_insert(VALUE self, VALUE documents) {
73
116
  return Qtrue;
74
117
  }
75
118
 
76
- static VALUE faststep_collection_update(VALUE self, VALUE query, VALUE operations) {
119
+ static VALUE faststep_collection_update(const VALUE self, const VALUE query, const VALUE operations) {
77
120
  bson* bson_query = create_bson_from_ruby_hash(query);
78
121
  bson* bson_operations = create_bson_from_ruby_hash(operations);
79
122
 
@@ -102,7 +145,7 @@ static VALUE faststep_collection_remove(int argc, VALUE* argv, VALUE self) {
102
145
  return Qnil;
103
146
  }
104
147
 
105
- static VALUE faststep_collection_drop(VALUE self) {
148
+ static VALUE faststep_collection_drop(const VALUE self) {
106
149
  bson_bool_t result = mongo_cmd_drop_collection(GetFaststepConnectionForCollection(self),
107
150
  _ivar_name(rb_iv_get(self, "@db")),
108
151
  _ivar_name(self),
@@ -111,7 +154,7 @@ static VALUE faststep_collection_drop(VALUE self) {
111
154
  return bool_to_ruby(result);
112
155
  }
113
156
 
114
- static VALUE faststep_collection_create_index(VALUE self, VALUE indexes) {
157
+ static VALUE faststep_collection_create_index(const VALUE self, const VALUE indexes) {
115
158
  bson* bson_indexes = create_bson_from_ruby_hash(indexes);
116
159
 
117
160
  bson_bool_t result = mongo_create_index(GetFaststepConnectionForCollection(self),
@@ -124,13 +167,13 @@ static VALUE faststep_collection_create_index(VALUE self, VALUE indexes) {
124
167
  return bool_to_ruby(result);
125
168
  }
126
169
 
127
- static void _faststep_collection_insert_one(mongo_connection* conn, char* ns, VALUE document) {
170
+ static void _faststep_collection_insert_one(mongo_connection* conn, const char* ns, const VALUE document) {
128
171
  bson* bson_document = create_bson_from_ruby_hash(document);
129
172
  mongo_insert(conn, ns, bson_document);
130
173
  bson_destroy(bson_document);
131
174
  }
132
175
 
133
- static void _faststep_collection_insert_batch(mongo_connection* conn, char* ns, VALUE documents) {
176
+ static void _faststep_collection_insert_batch(mongo_connection* conn, const char* ns, const VALUE documents) {
134
177
  int total_document_count = RARRAY_LEN(documents);
135
178
  bson** bson_documents = (bson**)malloc(sizeof(bson*) * total_document_count);
136
179
 
@@ -167,7 +210,7 @@ static void _faststep_collection_insert_batch(mongo_connection* conn, char* ns,
167
210
  }
168
211
  }
169
212
 
170
- static void _faststep_collection_destroy(bson** bson_documents, int document_count) {
213
+ static void _faststep_collection_destroy(bson** bson_documents, const int document_count) {
171
214
  int iterator;
172
215
  for(iterator = 0; iterator < document_count; iterator++) {
173
216
  bson_destroy(bson_documents[iterator]);
@@ -175,11 +218,11 @@ static void _faststep_collection_destroy(bson** bson_documents, int document_cou
175
218
  return;
176
219
  }
177
220
 
178
- static char* _ivar_name(VALUE obj) {
179
- return RSTRING_PTR(rb_iv_get(obj, "@name"));
221
+ static char* _ivar_name(const VALUE object) {
222
+ return RSTRING_PTR(rb_iv_get(object, "@name"));
180
223
  }
181
224
 
182
- mongo_connection* GetFaststepConnectionForCollection(VALUE collection) {
225
+ mongo_connection* GetFaststepConnectionForCollection(const VALUE collection) {
183
226
  VALUE db = rb_iv_get(collection, "@db");
184
227
  VALUE connection = rb_iv_get(db, "@connection");
185
228
 
@@ -5,20 +5,22 @@
5
5
  #define COLLECTION_H
6
6
 
7
7
  void faststep_collection_main();
8
- static VALUE faststep_collection_init(VALUE, VALUE, VALUE);
8
+ static VALUE faststep_collection_init(VALUE, const VALUE, const VALUE);
9
+ static VALUE faststep_collection_find(int, VALUE*, const VALUE);
10
+ static VALUE faststep_collection_find_one(int, VALUE*, const VALUE);
9
11
  static VALUE faststep_collection_count(int, VALUE*, VALUE);
10
- static VALUE faststep_collection_insert(VALUE, VALUE);
11
- static VALUE faststep_collection_update(VALUE, VALUE, VALUE);
12
+ static VALUE faststep_collection_insert(const VALUE, const VALUE);
13
+ static VALUE faststep_collection_update(const VALUE, const VALUE, const VALUE);
12
14
  static VALUE faststep_collection_remove(int, VALUE*, VALUE);
13
- static VALUE faststep_collection_drop(VALUE);
14
- static VALUE faststep_collection_create_index(VALUE, VALUE);
15
- VALUE faststep_collection_ns(VALUE);
15
+ static VALUE faststep_collection_drop(const VALUE);
16
+ static VALUE faststep_collection_create_index(const VALUE, const VALUE);
17
+ VALUE faststep_collection_ns(const VALUE);
16
18
 
17
- static void _faststep_collection_insert_one(mongo_connection*, char*, VALUE);
18
- static void _faststep_collection_insert_batch(mongo_connection*, char*, VALUE);
19
- mongo_connection* GetFaststepConnectionForCollection(VALUE);
20
- static void _faststep_collection_destroy(bson**, int);
19
+ static void _faststep_collection_insert_one(mongo_connection*, const char*, const VALUE);
20
+ static void _faststep_collection_insert_batch(mongo_connection*, const char*, const VALUE);
21
+ mongo_connection* GetFaststepConnectionForCollection(const VALUE);
22
+ static void _faststep_collection_destroy(bson**, const int);
21
23
 
22
- void build_collection_ns(char*, char*, char*);
23
- static char* _ivar_name(VALUE);
24
+ void build_collection_ns(char*, const char*, const char*);
25
+ static char* _ivar_name(const VALUE);
24
26
  #endif
@@ -4,6 +4,7 @@
4
4
 
5
5
  void faststep_connection_main() {
6
6
  rb_cFaststepConnection = rb_define_class_under(rb_mFaststep, "Connection", rb_cObject);
7
+ rb_define_const(rb_cFaststepConnection, "DEFAULT_PORT", INT2FIX(27017));
7
8
 
8
9
  rb_define_attr(rb_cFaststepConnection, "host", 1, 0);
9
10
  rb_define_attr(rb_cFaststepConnection, "port", 1, 0);
@@ -15,11 +16,12 @@ void faststep_connection_main() {
15
16
  rb_define_method(rb_cFaststepConnection, "disconnect!", faststep_connection_disconnect, 0);
16
17
  rb_define_method(rb_cFaststepConnection, "connected?", faststep_connection_connected, 0);
17
18
  rb_define_method(rb_cFaststepConnection, "master?", faststep_connection_master, 0);
19
+ rb_define_method(rb_cFaststepConnection, "db", faststep_connection_db, 1);
18
20
 
19
21
  return;
20
22
  }
21
23
 
22
- static VALUE faststep_connection_init(VALUE self, VALUE host, VALUE port) {
24
+ static VALUE faststep_connection_init(VALUE self, const VALUE host, const VALUE port) {
23
25
  rb_iv_set(self, "@host", host);
24
26
  rb_iv_set(self, "@port", port);
25
27
 
@@ -28,7 +30,7 @@ static VALUE faststep_connection_init(VALUE self, VALUE host, VALUE port) {
28
30
  return self;
29
31
  }
30
32
 
31
- static VALUE faststep_connection_new(VALUE class, VALUE host, VALUE port) {
33
+ static VALUE faststep_connection_new(VALUE class, const VALUE host, const VALUE port) {
32
34
  mongo_connection* conn = bson_malloc(sizeof(mongo_connection));
33
35
 
34
36
  VALUE tdata = Data_Wrap_Struct(class, NULL, mongo_destroy, conn);
@@ -53,32 +55,36 @@ static VALUE faststep_connection_connect(VALUE self) {
53
55
  return Qnil;
54
56
  }
55
57
 
56
- static VALUE faststep_connection_disconnect(VALUE self) {
58
+ static VALUE faststep_connection_disconnect(const VALUE self) {
57
59
  mongo_disconnect(GetFaststepConnection(self));
58
60
 
59
61
  return Qnil;
60
62
  }
61
63
 
62
- static VALUE faststep_connection_connected(VALUE self) {
64
+ static VALUE faststep_connection_connected(const VALUE self) {
63
65
  return bool_to_ruby(GetFaststepConnection(self)->connected);
64
66
  }
65
67
 
66
- static VALUE faststep_connection_master(VALUE self) {
68
+ static VALUE faststep_connection_master(const VALUE self) {
67
69
  return bool_to_ruby(mongo_cmd_ismaster(GetFaststepConnection(self), NULL));
68
70
  }
69
71
 
72
+ static VALUE faststep_connection_db(const VALUE self, const VALUE database_name) {
73
+ return rb_funcall(rb_cFaststepDb, rb_intern("new"), 2, database_name, self);
74
+ }
75
+
70
76
  static void _faststep_connect_or_raise(mongo_connection* conn, mongo_connection_options* options) {
71
77
  mongo_connect(conn, options);
72
78
 
73
79
  if(conn->connected == 0) {
74
80
  mongo_destroy(conn);
75
- rb_raise(rb_cFaststepConnectionFailure, "unable to connect to Mongo");
81
+ rb_raise(rb_eFaststepConnectionFailure, "unable to connect to Mongo");
76
82
  }
77
83
 
78
84
  return;
79
85
  }
80
86
 
81
- mongo_connection* GetFaststepConnection(VALUE object) {
87
+ mongo_connection* GetFaststepConnection(const VALUE object) {
82
88
  mongo_connection* conn;
83
89
  Data_Get_Struct(object, mongo_connection, conn);
84
90
  return conn;
@@ -4,14 +4,15 @@
4
4
  #define CONNECTION_H
5
5
  void faststep_connection_main();
6
6
 
7
- static VALUE faststep_connection_init(VALUE, VALUE, VALUE);
8
- static VALUE faststep_connection_new(VALUE, VALUE, VALUE);
7
+ static VALUE faststep_connection_init(VALUE, const VALUE, const VALUE);
8
+ static VALUE faststep_connection_new(VALUE, const VALUE, const VALUE);
9
9
  static VALUE faststep_connection_connect(VALUE);
10
- static VALUE faststep_connection_disconnect(VALUE);
11
- static VALUE faststep_connection_connected(VALUE);
12
- static VALUE faststep_connection_master(VALUE);
10
+ static VALUE faststep_connection_disconnect(const VALUE);
11
+ static VALUE faststep_connection_connected(const VALUE);
12
+ static VALUE faststep_connection_master(const VALUE);
13
+ static VALUE faststep_connection_db(const VALUE, const VALUE);
13
14
 
14
- mongo_connection* GetFaststepConnection(VALUE);
15
+ mongo_connection* GetFaststepConnection(const VALUE);
15
16
 
16
17
  static void _faststep_connect_or_raise(mongo_connection*, mongo_connection_options*);
17
18
  #endif
@@ -9,53 +9,116 @@ void faststep_cursor_main() {
9
9
  rb_define_attr(rb_cFaststepCursor, "collection", 1, 0);
10
10
  rb_include_module(rb_cFaststepCursor, rb_mEnumerable);
11
11
 
12
- rb_define_singleton_method(rb_cFaststepCursor, "new", faststep_cursor_new, 2);
13
-
14
- rb_define_method(rb_cFaststepCursor, "initialize", faststep_cursor_init, 2);
15
- rb_define_method(rb_cFaststepCursor, "each", faststep_cursor_each, 0);
12
+ rb_define_method(rb_cFaststepCursor, "initialize", faststep_cursor_init, -1);
13
+ rb_define_method(rb_cFaststepCursor, "explain", faststep_cursor_explain, 0);
14
+ rb_define_method(rb_cFaststepCursor, "skip", faststep_cursor_skip, 1);
15
+ rb_define_method(rb_cFaststepCursor, "limit", faststep_cursor_limit, 1);
16
+ rb_define_method(rb_cFaststepCursor, "fields", faststep_cursor_fields, 1);
17
+ rb_define_method(rb_cFaststepCursor, "order", faststep_cursor_order, 1);
18
+ rb_define_method(rb_cFaststepCursor, "each", faststep_cursor_each, 0);
16
19
 
17
20
  return;
18
21
  }
19
22
 
20
- static VALUE faststep_cursor_init(VALUE self, VALUE collection, VALUE options) {
23
+ static VALUE faststep_cursor_init(int argc, VALUE* argv, VALUE self) {
24
+ VALUE collection, options;
25
+ rb_scan_args(argc, argv, "11", &collection, &options);
26
+
21
27
  rb_iv_set(self, "@collection", collection);
22
28
 
29
+ VALUE selector = rb_hash_aref(options, rb_str_new2("selector"));
30
+ if(NIL_P(selector)) { selector = rb_hash_new(); }
31
+ if(RTEST(rb_hash_aref(selector, rb_str_new2("$query")))) {
32
+ rb_iv_set(self, "@selector", rb_hash_aref(selector, rb_str_new2("$query")));
33
+ } else {
34
+ rb_iv_set(self, "@selector", selector);
35
+ }
36
+
37
+ rb_iv_set(self, "@explain", Qfalse);
38
+ rb_iv_set(self, "@fields", rb_hash_aref(options, rb_str_new2("fields")));
39
+ rb_iv_set(self, "@skip", rb_hash_aref(options, rb_str_new2("skip")));
40
+ rb_iv_set(self, "@limit", rb_hash_aref(options, rb_str_new2("limit")));
41
+
23
42
  return self;
24
43
  }
25
44
 
26
- static VALUE faststep_cursor_new(VALUE class, VALUE collection, VALUE options) {
27
- mongo_cursor* cursor = _faststep_build_mongo_cursor(collection, options);
45
+ static VALUE faststep_cursor_each(const VALUE self) {
46
+ mongo_cursor* cursor = _faststep_build_mongo_cursor(self);
28
47
 
29
- VALUE tdata = Data_Wrap_Struct(class, NULL, mongo_cursor_destroy, cursor);
48
+ while(mongo_cursor_next(cursor)) {
49
+ rb_yield(ruby_hash_from_bson(&cursor->current));
50
+ }
51
+ }
30
52
 
31
- VALUE argv[2];
32
- argv[0] = collection;
33
- argv[1] = options;
53
+ static VALUE faststep_cursor_explain(VALUE self) {
54
+ rb_iv_set(self, "@explain", Qtrue);
34
55
 
35
- rb_obj_call_init(tdata, 2, argv);
56
+ mongo_cursor* cursor = _faststep_build_mongo_cursor(self);
57
+ mongo_cursor_next(cursor);
36
58
 
37
- return tdata;
59
+ VALUE result = ruby_hash_from_bson(&cursor->current);
60
+ mongo_cursor_destroy(cursor);
61
+ return result;
38
62
  }
39
63
 
40
- static VALUE faststep_cursor_each(VALUE self) {
41
- mongo_cursor* cursor;
42
- Data_Get_Struct(self, mongo_cursor, cursor);
64
+ static VALUE faststep_cursor_skip(VALUE self, const VALUE skip_count) {
65
+ rb_iv_set(self, "@skip", skip_count);
66
+ return self;
67
+ }
43
68
 
44
- while(mongo_cursor_next(cursor)) {
45
- rb_yield(ruby_hash_from_bson(&cursor->current));
46
- }
69
+ static VALUE faststep_cursor_limit(VALUE self, const VALUE limit_count) {
70
+ rb_iv_set(self, "@limit", limit_count);
71
+ return self;
72
+ }
73
+
74
+ static VALUE faststep_cursor_fields(VALUE self, const VALUE fields) {
75
+ rb_iv_set(self, "@fields", fields);
76
+ return self;
47
77
  }
48
78
 
49
- static mongo_cursor* _faststep_build_mongo_cursor(VALUE collection, VALUE options) {
50
- bson* selector = create_bson_from_ruby_hash(rb_hash_aref(options, rb_str_new2("selector")));
79
+ static VALUE faststep_cursor_order(VALUE self, const VALUE order) {
80
+ rb_iv_set(self, "@order", ruby_array_to_bson_ordered_hash(order));
81
+ return self;
82
+ }
83
+
84
+ static mongo_cursor* _faststep_build_mongo_cursor(VALUE self) {
85
+ bson* selector = create_bson_from_ruby_hash(_faststep_build_full_query(self));
86
+ bson* fields = bson_from_ruby_array(rb_iv_get(self, "@fields"));
87
+
88
+ int limit = 0;
89
+ if(RTEST(rb_iv_get(self, "@limit"))) {
90
+ limit = -1*FIX2INT(rb_iv_get(self, "@limit"));
91
+ }
92
+
93
+ int skip = 0;
94
+ if(RTEST(rb_iv_get(self, "@skip"))) {
95
+ skip = FIX2INT(rb_iv_get(self, "@skip"));
96
+ }
97
+
98
+ VALUE collection = rb_iv_get(self, "@collection");
51
99
 
52
100
  mongo_cursor* result = mongo_find(GetFaststepConnectionForCollection(collection),
53
101
  RSTRING_PTR(rb_funcall(collection, rb_intern("ns"), 0)),
54
102
  selector,
55
- NULL,
56
- 0, 0, 0);
103
+ fields,
104
+ limit, skip, 0);
57
105
 
58
106
  bson_destroy(selector);
107
+ bson_destroy(fields);
59
108
 
60
109
  return result;
61
110
  }
111
+
112
+ static VALUE _faststep_build_full_query(VALUE self) {
113
+ if(RTEST(rb_iv_get(self, "@explain")) || RTEST(rb_iv_get(self, "@order"))) {
114
+ VALUE full_query = rb_hash_new();
115
+ rb_hash_aset(full_query, rb_str_new2("$query"), rb_iv_get(self, "@selector"));
116
+ rb_hash_aset(full_query, rb_str_new2("$explain"), rb_iv_get(self, "@explain"));
117
+ if(RTEST(rb_iv_get(self, "@order"))) {
118
+ rb_hash_aset(full_query, rb_str_new2("$orderby"), rb_iv_get(self, "@order"));
119
+ }
120
+ return full_query;
121
+ } else {
122
+ return rb_iv_get(self, "@selector");
123
+ }
124
+ }
@@ -3,8 +3,13 @@
3
3
  #include "mongo.h"
4
4
  #define CURSOR_h
5
5
  void faststep_cursor_main();
6
- static VALUE faststep_cursor_init(VALUE, VALUE, VALUE);
7
- static VALUE faststep_cursor_new(VALUE, VALUE, VALUE);
8
- static VALUE faststep_cursor_each(VALUE);
9
- static mongo_cursor* _faststep_build_mongo_cursor(VALUE, VALUE);
6
+ static VALUE faststep_cursor_init(int, VALUE*, VALUE);
7
+ static VALUE faststep_cursor_each(const VALUE);
8
+ static VALUE faststep_cursor_explain(VALUE);
9
+ static VALUE faststep_cursor_skip(VALUE, const VALUE);
10
+ static VALUE faststep_cursor_limit(VALUE, const VALUE);
11
+ static VALUE faststep_cursor_fields(VALUE, const VALUE);
12
+ static VALUE faststep_cursor_order(VALUE, const VALUE);
13
+ static mongo_cursor* _faststep_build_mongo_cursor(VALUE);
14
+ static VALUE _faststep_build_full_query(VALUE);
10
15
  #endif
@@ -2,6 +2,6 @@
2
2
  #include "faststep_defines.h"
3
3
 
4
4
  void faststep_exceptions_main() {
5
- rb_cFaststepConnectionFailure = rb_define_class_under(rb_mFaststep, "ConnectionFailure", rb_eStandardError);
6
- rb_cFaststepOperationFailure = rb_define_class_under(rb_mFaststep, "OperationFailure", rb_eStandardError);
5
+ rb_eFaststepConnectionFailure = rb_define_class_under(rb_mFaststep, "ConnectionFailure", rb_eStandardError);
6
+ rb_eFaststepOperationFailure = rb_define_class_under(rb_mFaststep, "OperationFailure", rb_eStandardError);
7
7
  }
@@ -7,6 +7,9 @@
7
7
  #include "support.h"
8
8
 
9
9
  VALUE rb_mBson;
10
+ VALUE rb_cBsonOrderedHash;
11
+ VALUE rb_cBsonObjectId;
12
+
10
13
  VALUE rb_mFaststep;
11
14
  VALUE rb_cFaststepConnection;
12
15
  VALUE rb_cFaststepDb;
@@ -14,12 +17,17 @@ VALUE rb_cFaststepCollection;
14
17
  VALUE rb_cFaststepCursor;
15
18
  VALUE rb_mFaststepSupport;
16
19
 
17
- VALUE rb_cFaststepConnectionFailure;
18
- VALUE rb_cFaststepOperationFailure;
20
+ VALUE rb_eFaststepConnectionFailure;
21
+ VALUE rb_eFaststepOperationFailure;
19
22
 
20
23
  void Init_faststep() {
21
24
  rb_mFaststep = rb_define_module("Faststep");
22
- rb_mBson = rb_const_get(rb_cObject, rb_intern("BSON"));
25
+ rb_define_const(rb_mFaststep, "DESCENDING", NUM2INT(-1));
26
+ rb_define_const(rb_mFaststep, "ASCENDING", NUM2INT(1));
27
+
28
+ rb_mBson = rb_const_get(rb_cObject, rb_intern("BSON"));
29
+ rb_cBsonOrderedHash = rb_const_get(rb_mBson, rb_intern("OrderedHash"));
30
+ rb_cBsonObjectId = rb_const_get(rb_mBson, rb_intern("ObjectId"));
23
31
 
24
32
  faststep_connection_main();
25
33
  faststep_db_main();
@@ -5,7 +5,9 @@ RUBY_EXTERN VALUE rb_cFaststepDb;
5
5
  RUBY_EXTERN VALUE rb_cFaststepCollection;
6
6
  RUBY_EXTERN VALUE rb_cFaststepCursor;
7
7
 
8
- RUBY_EXTERN VALUE rb_cFaststepConnectionFailure;
9
- RUBY_EXTERN VALUE rb_cFaststepOperationFailure;
8
+ RUBY_EXTERN VALUE rb_eFaststepConnectionFailure;
9
+ RUBY_EXTERN VALUE rb_eFaststepOperationFailure;
10
10
 
11
11
  RUBY_EXTERN VALUE rb_mBson;
12
+ RUBY_EXTERN VALUE rb_cBsonOrderedHash;
13
+ RUBY_EXTERN VALUE rb_cBsonObjectId;
@@ -1,13 +1,7 @@
1
1
  module Faststep
2
2
  class Connection
3
- DEFAULT_PORT = 27017
4
-
5
- def db(database_name)
6
- Db.new(database_name, self)
7
- end
8
-
9
3
  def drop_database(database_name)
10
- Db.new(database_name, self).drop
4
+ db(database_name).drop
11
5
  end
12
6
  end
13
7
  end
@@ -1,3 +1,3 @@
1
1
  module Faststep
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/faststep.rb CHANGED
@@ -3,7 +3,6 @@ require "bson"
3
3
  module Faststep
4
4
  autoload :Db, "faststep/db"
5
5
  autoload :Connection, "faststep/connection"
6
- autoload :Collection, "faststep/collection"
7
6
  autoload :Cursor, "faststep/cursor"
8
7
  end
9
8
 
@@ -28,6 +28,12 @@ describe Faststep::Collection do
28
28
  db["another.thing"].find({}).first["baz"].should == "qux"
29
29
  end
30
30
 
31
+ it "finds single documents" do
32
+ 10.times { db["something"].insert(:foo => "bar") }
33
+ db["something"].find_one({:foo => "bar"})["foo"].should == "bar"
34
+ db["something"].find_one(BSON::ObjectId.new).should be_nil
35
+ end
36
+
31
37
  it "updates documents" do
32
38
  db["something"].insert(:foo => "bar", :something => "fun")
33
39
  db["something"].update({ :something => "fun" }, { "$set" => { :foo => "awesome" } })
data/spec/cursor_spec.rb CHANGED
@@ -10,15 +10,69 @@ describe Faststep::Cursor do
10
10
  cursor = Faststep::Cursor.new(collection, {})
11
11
  cursor.to_a.length.should_not == 0
12
12
  cursor.each do |doc|
13
- doc[:foo].should == "bar"
13
+ doc["foo"].should == "bar"
14
14
  end
15
15
  end
16
16
 
17
- it "caches documents" do
18
- 10.times { collection.insert(:foo => "bar") }
17
+ it "finds specific fields for a document" do
18
+ collection.insert(:name => "John Doe", :age => 25, :gender => "Male", :postal_code => "02108")
19
+ collection.insert(:name => "Jane Doe", :age => 22, :gender => "Female", :postal_code => "02108")
19
20
 
20
- cursor = Faststep::Cursor.new(collection, {})
21
- cursor.to_a.length.should == 10
22
- cursor.to_a.length.should == 10
21
+ documents = collection.find.fields(%w(name age)).to_a
22
+
23
+ documents.each do |document|
24
+ document.has_key?("name").should be_true
25
+ document.has_key?("age").should be_true
26
+ document.has_key?("_id").should be_true
27
+ document.has_key?("gender").should be_false
28
+ document.has_key?("postal_code").should be_false
29
+ end
30
+ end
31
+
32
+ it "limits documents" do
33
+ 10.times.map { |i| collection.insert(:name => "Person #{i}") }
34
+
35
+ documents = collection.find.limit(2).to_a
36
+ documents.map {|doc| doc["name"] }.should == ["Person 0", "Person 1"]
37
+
38
+ documents = collection.find({}).limit(1).to_a
39
+ documents.length.should == 1
40
+ documents.first["name"].should == "Person 0"
41
+ end
42
+
43
+ it "skips documents" do
44
+ collection.insert(:name => "John Doe", :age => 25, :gender => "Male", :postal_code => "02108")
45
+ collection.insert(:name => "Jane Doe", :age => 22, :gender => "Female", :postal_code => "02108")
46
+ collection.insert(:name => "John Smith", :age => 40, :gender => "Male", :postal_code => "02108")
47
+
48
+ documents = collection.find.skip(1).to_a
49
+ documents.length.should == 2
50
+ documents[0]["name"].should == "Jane Doe"
51
+ documents[1]["name"].should == "John Smith"
52
+ end
53
+
54
+ it "orders documents" do
55
+ 10.times.map { |i| collection.insert(:name => "Person #{i}", :age => 24) }
56
+ collection.insert(:name => "Person 9", :age => 20)
57
+ collection.find.order([[:name, Faststep::DESCENDING], [:age, Faststep::ASCENDING]]).to_a.tap do |documents|
58
+ documents.first["name"].should == "Person 9"
59
+ documents.first["age"].should == 20
60
+ end
61
+
62
+ collection.find.order([[:age, Faststep::DESCENDING], [:name, Faststep::ASCENDING]]).to_a.tap do |documents|
63
+ documents.first["name"].should == "Person 0"
64
+ end
65
+
66
+ collection.find.order([[:age, Faststep::DESCENDING], [:name, Faststep::DESCENDING]]).to_a.tap do |documents|
67
+ documents.first["name"].should == "Person 9"
68
+ documents.first["age"].should == 24
69
+ end
70
+ end
71
+
72
+ it "explains queries" do
73
+ result = collection.find.explain
74
+ result.should have_key("cursor")
75
+ result.should have_key("nscanned")
76
+ result.should have_key("n")
23
77
  end
24
78
  end
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faststep
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
5
4
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 1
10
- version: 0.0.1
5
+ version: 0.0.2
11
6
  platform: ruby
12
7
  authors:
13
8
  - Josh Clayton
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-03-18 00:00:00 -04:00
13
+ date: 2011-03-20 00:00:00 -04:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
@@ -26,11 +21,6 @@ dependencies:
26
21
  requirements:
27
22
  - - "="
28
23
  - !ruby/object:Gem::Version
29
- hash: 15
30
- segments:
31
- - 0
32
- - 7
33
- - 6
34
24
  version: 0.7.6
35
25
  type: :runtime
36
26
  version_requirements: *id001
@@ -42,11 +32,6 @@ dependencies:
42
32
  requirements:
43
33
  - - "="
44
34
  - !ruby/object:Gem::Version
45
- hash: 23
46
- segments:
47
- - 1
48
- - 2
49
- - 4
50
35
  version: 1.2.4
51
36
  type: :runtime
52
37
  version_requirements: *id002
@@ -58,11 +43,6 @@ dependencies:
58
43
  requirements:
59
44
  - - "="
60
45
  - !ruby/object:Gem::Version
61
- hash: 23
62
- segments:
63
- - 1
64
- - 2
65
- - 4
66
46
  version: 1.2.4
67
47
  type: :runtime
68
48
  version_requirements: *id003
@@ -74,11 +54,6 @@ dependencies:
74
54
  requirements:
75
55
  - - "="
76
56
  - !ruby/object:Gem::Version
77
- hash: 27
78
- segments:
79
- - 2
80
- - 5
81
- - 0
82
57
  version: 2.5.0
83
58
  type: :development
84
59
  version_requirements: *id004
@@ -94,6 +69,8 @@ extra_rdoc_files: []
94
69
  files:
95
70
  - .gitignore
96
71
  - Gemfile
72
+ - LICENSE
73
+ - README.markdown
97
74
  - Rakefile
98
75
  - bench/standard_benchmark
99
76
  - ext/faststep/bson.c
@@ -127,7 +104,6 @@ files:
127
104
  - ext/faststep/support.h
128
105
  - faststep.gemspec
129
106
  - lib/faststep.rb
130
- - lib/faststep/collection.rb
131
107
  - lib/faststep/connection.rb
132
108
  - lib/faststep/cursor.rb
133
109
  - lib/faststep/db.rb
@@ -152,23 +128,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
128
  requirements:
153
129
  - - ">="
154
130
  - !ruby/object:Gem::Version
155
- hash: 3
156
- segments:
157
- - 0
158
131
  version: "0"
159
132
  required_rubygems_version: !ruby/object:Gem::Requirement
160
133
  none: false
161
134
  requirements:
162
135
  - - ">="
163
136
  - !ruby/object:Gem::Version
164
- hash: 3
165
- segments:
166
- - 0
167
137
  version: "0"
168
138
  requirements: []
169
139
 
170
140
  rubyforge_project:
171
- rubygems_version: 1.4.2
141
+ rubygems_version: 1.6.2
172
142
  signing_key:
173
143
  specification_version: 3
174
144
  summary: Mongo on Speed
@@ -1,21 +0,0 @@
1
- module Faststep
2
- class Collection
3
- def find(selector = {}, options = {})
4
- Cursor.new(self, "selector" => selector)
5
- end
6
-
7
- def find_one(spec_or_object_id=nil, opts={})
8
- spec = case spec_or_object_id
9
- when nil
10
- {}
11
- when BSON::ObjectId
12
- {:_id => spec_or_object_id}
13
- when Hash
14
- spec_or_object_id
15
- else
16
- raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
17
- end
18
- find(spec, opts.merge(:limit => -1)).first
19
- end
20
- end
21
- end