faststep 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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