how can I run this query from the ruby driver?

73 views
Skip to first unread message

jason

unread,
Sep 14, 2010, 11:50:56 PM9/14/10
to mongodb-user
I have figured out that I need to run a special query,which I can do
fine from the js mongo interpreter. It is:
db.social_users.find({},{login:1,today_scores:1}).min( { today_scores:
{ '#rails' : 5000} }).max( { today_scores: { '#rails' :
1} }).limit(100);

Because of the special sorting requirements, I can't use the regular
ruby commands, but I see no way to run this from the ruby driver, how
am I supposed to pull the data out with the ruby drivers?
Is this even possible to run from ruby? If not, what are my
alternative options? My code is all written in ruby.

Chuck Remes

unread,
Sep 15, 2010, 12:21:58 AM9/15/10
to mongod...@googlegroups.com

All of the drivers support the full range of query commands though the syntax may be awkward at times.

See here: http://www.mongodb.org/display/DOCS/min+and+max+Query+Specifiers

I also found this as my first hit on google:

http://osdir.com/ml/mongodb-user/2010-01/msg01141.html

Looks to me like that query could be written in ruby as follows...

o = OrderedHash.new
o['$query'] = {}
o['$min'] = {'today_scores' => {'#rails' => 5000}}
o['$max'] = {'today_scores' => {'#rails' => 1}}

social_users.find(o, :fields => ['login', 'today_scores'], :limit => 100)

That's a bit awkward. :-\

cr

jason

unread,
Sep 15, 2010, 1:51:18 AM9/15/10
to mongodb-user
I saw that google hit earlier also. That is different from what I am
doing because I am sorting on an object, not on a.b

Kyle Banker

unread,
Sep 15, 2010, 10:16:34 AM9/15/10
to mongod...@googlegroups.com
Jason,

Chuck's Ruby example is equivalent. If it's not working, please provide some sample data with a test case.

Kyle

--
You received this message because you are subscribed to the Google Groups "mongodb-user" group.
To post to this group, send email to mongod...@googlegroups.com.
To unsubscribe from this group, send email to mongodb-user...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mongodb-user?hl=en.


jason

unread,
Sep 15, 2010, 4:15:30 PM9/15/10
to mongodb-user
Here is a sample script I am using:
require 'rubygems'
require 'mongo'
include Mongo

db = Connection.new.db('development')
coll = db.collection('social_users')
coll.find("today_scores.#rails" => {"$min" =>
5000},"today_scores.#rails" => {"$max" => 1}).limit(100).each { |row|
puts row }
coll.find("today_scores" => { "#rails" =>{"$min" =>
5000}},"today_scores" => { "#rails" =>{"$max" => 1}}).limit(100).each
{ |row| puts row }


I get 2 different errors for the 2 queries I tried, the first query:
coll.find("today_scores.#rails" => {"$min" =>
5000},"today_scores.#rails" => {"$max" => 1}).limit(100).each { |row|
puts row }

Mongo::OperationFailure: invalid operator: $max
from /usr/local/rvm/gems/ruby-1.9.2-p0/gems/mongo-1.0.7/lib/
mongo/cursor.rb:78:in `next_document'
from /usr/local/rvm/gems/ruby-1.9.2-p0/gems/mongo-1.0.7/lib/
mongo/cursor.rb:183:in `each'
from (irb):7
from /usr/local/rvm/rubies/ruby-1.9.2-p0/bin/irb:12:in
`<main>'

The second query:
coll.find("today_scores" => { "#rails" =>{"$min" =>
5000}},"today_scores" => { "#rails" =>{"$max" => 1}}).limit(100).each
{ |row| puts row }
just returns nil which is wrong since there is data returned when I
run from mongo

So it seems like I am writing the query wrong, I seemed to be
following correct syntax according to this though:
http://www.mongodb.org/display/DOCS/Ruby+Tutorial

I think this part is wrong also: "today_scores.#rails" that will not
use my index I have created since today_scores is an object.


Here are some sample docs:

{ "login" : "tanvlm", "today_scores" : { "#rails" : 0.2, "MAKE" :
0.15 } }
{ "login" : "scott", "today_scores" : { "#rails" : 10, "MAKE" :
0.112366 } }
{ "login" : "paul", "today_scores" : { "#rails" : 0.6, "MAKE" :
0.16654 } }
{ "login" : "nick", "today_scores" : { "#rails" : 0.2, "MAKE" :
0.1643266666 } }

This is the exact mongo query I am trying to translate into ruby:
db.social_users.find().min( { today_scores: { '#rails' :
5000} }).max( { today_scores: { '#rails' : 1} }).limit(5);


I am trying to use this index:
db.social_users.ensureIndex({today_scores:-1});
> > mongodb-user...@googlegroups.com<mongodb-user%2Bunsubscribe@google groups.com>
> > .

Kyle Banker

unread,
Sep 15, 2010, 5:18:25 PM9/15/10
to mongod...@googlegroups.com
Jason,

Your queries are not the same as Chuck's. Please paste in Chuck's queries exactly and let us know if they work.

Kyle

To unsubscribe from this group, send email to mongodb-user...@googlegroups.com.

Chuck Remes

unread,
Sep 15, 2010, 5:24:40 PM9/15/10
to mongod...@googlegroups.com
Jason,

put the query in like I showed in my response. If you want, print out the query hash that you will pass to the #find command and compare it to the one you have been trying. Notice they are very different.

"{\"$query\"=>{}, \"$min\"=>{\"today_scores\"=>{\"#rails\"=>5000}}, \"$max\"=>{\"today_scores\"=>{\"#rails\"=>1}}}"

versus

"{\"today_scores.#rails\"=>{\"$max\"=>1}}"

The top one is correct.

cr

> To unsubscribe from this group, send email to mongodb-user...@googlegroups.com.

jason

unread,
Sep 15, 2010, 5:50:13 PM9/15/10
to mongodb-user
Thanks, that did work. Thanks Chuck and Kyle. Is there a reason to
use OrderedHash? I tested out both variations of Hash vs OrderedHash
and did not see a difference.

Chuck Remes

unread,
Sep 15, 2010, 6:46:44 PM9/15/10
to mongod...@googlegroups.com
OrderedHash is required since the mongo server expects the _id field to show up first in the message. By default Ruby has unordered hashes in 1.8; I think 1.9 maintains order so you can use the built in Hash if you are running under 1.9.

cr

jason

unread,
Sep 15, 2010, 7:03:20 PM9/15/10
to mongodb-user
I have one more weird issue regarding this. For some variants of the
today_scores I pass, i get no data, when I can clearly see data in the
database.
For example this query:
db.social_users.find({},{login:1,today_scores:
1}).min( { today_scores: { 'eminem' : 5000} }).max( { today_scores:
{ 'eminem' : 1} }).limit(10);
returns 0 results, but I see records with data:
db.social_users.find({login: 'FC_Hollywood'},{login:1,today_scores:
1});
{ "_id" : ObjectId("4c8ebe686f7a7873fb00862b"), "login" :
"FC_Hollywood", "today_scores" : { "#rails" : 0, "optimist" : 0,
"jetsetter" : 0, "inception" : 0, "shark" : 0, "a-rod" : 0, "eminem" :
0.022727272727272728 } }


this record has a today_score with eminem. What am I doing wrong here?
I also tried dropping and recreating the index with:
db.runCommand({dropIndexes:'social_users', index : {'today_scores':
1}})
db.social_users.ensureIndex({today_scores:1});

Chuck Remes

unread,
Sep 15, 2010, 7:09:02 PM9/15/10
to mongod...@googlegroups.com
I don't know if your queries are wrong, but usually when a query doesn't return what I expect it's because I added some data using the ruby driver and then tried querying for it using the mongo javascript console.

Javascript has no notion of integers, only floats. So if you insert a record via ruby that uses integers and then try to query for it from the mongo console, I don't think you'll get anything back. Try writing the query you showed below in ruby and see if it returns what you expect. If so, then it's a datatype problem.

This is a "flaw" in the mongo console since *all* of the drivers preserve datatypes when they can during insert.

cr

jason

unread,
Sep 15, 2010, 7:20:32 PM9/15/10
to mongodb-user

I have tried with both ruby code and JavaScript code and both return
zero results. I also tried 0 vs 0.0 I'm at a loss on what I can do
to fix this.

Eliot Horowitz

unread,
Sep 15, 2010, 9:26:44 PM9/15/10
to mongod...@googlegroups.com
Max is exclusive, not inclusive.
That seems like it could be the issue above.

jason

unread,
Sep 16, 2010, 2:50:11 AM9/16/10
to mongodb-user

I dont think the issue has anything to do with exclusive vs inclusive,
if I remove the min function and only run the max function with any
value, I never get the record am looking for back
db.social_users.find({},{login:1,today_scores:
1}).max( { today_scores: { 'eminem' : 0} }).limit(100)
This record should show in the results:

db.social_users.find({login: 'FC_Hollywood'},{login:1,today_scores:
1});
{ "_id" : ObjectId("4c8ebe686f7a7873fb00862b"), "login" :
"FC_Hollywood", "today_scores" : { "#rails" : 0,"eminem" :
0.022727272727272728 } }

How can I get this to run?
Is it possible I have run into some bug in mongodb?

Eliot Horowitz

unread,
Sep 16, 2010, 2:34:31 PM9/16/10
to mongod...@googlegroups.com
Looks like you didn't follow the example.
Notice the ensureIndex has a -1 rather than 1

jason

unread,
Sep 16, 2010, 3:39:23 PM9/16/10
to mongodb-user

I've found exactly where the problem is. Before I change all my code
around, I am wondering if there is a way I can just change my query.
Eliot gave me an example that works before:
coll = db.foo
coll.drop()

coll.insert( { arr : [ { a : 1 } , { b : 2 } , { c : 3 } ] } )
coll.insert( { arr : [ { a : 2 } , { b : 3 } , { c : 1 } ] } )
coll.insert( { arr : [ { a : 3 } , { b : 1 } , { c : 2 } ] } )

coll.ensureIndex( { arr : -1 } )

coll.find().min( { arr : { a : 5000 } } ).max( { arr : { a :
1 } } ).forEach( printjson )


I had modified the data structure to use a set instead of an array so:
coll.insert( { arr : { a : 0.1 , b : 0.2 , c : 0.3 } } )
coll.insert( { arr : { a : 0.2 , b : 0.3 , c : 0.1 } } )
coll.insert( { arr : { a : 0.3 , b : 0.1 , c : 0.2} } )

Doing this causes max to not work. The reason I am using a set
instead is so that I can update the values relatively fast. I just
look up the key and update the value, but if I use an array, I need to
iterate through each item in the array and check if it has the key I
am looking for before I can update the value. This will kill my
performance if I need to iterate through the array everytime.
I would appreciate any help on this!


> Looks like you didn't follow the example.
> Notice the ensureIndex has a -1 rather than 1
Regarding the ensureIndex -1, even if I had used -1 instead of 1,
shouldnt the consequence just be an error is raised or that the query
runs longer since no index is used instead of returning the wrong
results.


jason
> >> > To unsubscribe from this group, send email to...
>
> read more »

Eliot Horowitz

unread,
Sep 16, 2010, 4:12:29 PM9/16/10
to mongod...@googlegroups.com
Using a set will not work - it has to be an array to use this method.

You can still use $inc : { "arr.b" : 5 } to update - so performance
shouldn't change.

-1 vs 1 should not error. its the equivialnt of saying ( a > 3 && a <
3 ) that just gives you no results, not an error

Reply all
Reply to author
Forward
0 new messages