Re: [mongoid] Re: Mongoid 3 - Rails 3: undefined method `call' for #<Hash:

807 views
Skip to first unread message

Durran Jordan

unread,
Aug 7, 2012, 8:22:53 AM8/7/12
to mon...@googlegroups.com
I'll have a chance to look at this tomorrow - since I haven't had the time to dig into will paginates code yet to see what is expected there... But it's on my list.

2012/8/6 Daly <ah...@bnotions.com>
Any one?


On Thursday, August 2, 2012 4:48:29 PM UTC-4, Daly wrote:
Details here: http://stackoverflow.com/questions/11784727/mongoid-3-access-map-reduce-results

The problem I have at the moment, in summary, is:

mr_collection = self.where(query).map_reduce(map, reduce).finalize(finalize).out({:replace => 'mr_results'})
limit = (options[:limit] || 10)
skip = (options[:skip].to_i || nil)
page = if skip >= limit
  ((skip+limit) / limit)
else
  1
end

sort = if options[:sort_by_vintage]
  [['value.vy', :desc], ['value.s', (options[:sort] || :desc)], ['value.pml', :asc]]
elsif options[:sort_by_sDate]
  [['value.sDate', :desc], ['value.s', (options[:sort] || :desc)], ['value.pml', :asc]]
else
  [['value.s', (options[:sort] || :desc)], ['value.pml', :asc]]
end
paginator = WillPaginate::Collection.new(page, limit, collection_count)
collection = mr_collection.find({},{
    :sort => sort,
    :limit => limit,
    :skip => skip
  }
)
Calling collection.to_a raises undefined method `call' for #<Hash:

This must be an issue with Moped::BSON::Document, because this works fine:

1.9.3p194 :007 > y = [{y: 20}, {z: 24}, {y: 100}].each
 => #<Enumerator: [{:y=>20}, {:z=>24}, {:y=>100}]:each> 
1.9.3p194 :008 > y.to_a
 => [{:y=>20}, {:z=>24}, {:y=>100}] 

But:

(rdb:1) collection
#<Enumerator: [{"_id"=>4545.0, "value"=>{"s"=>4.1, "p"=>14.25, "pml"=>0.019}}, {"_id"=>4555.0, "value"=>{"s"=>3.9, "p"=>13.95, "pml"=>0.0186}}, {"_id"=>4558.0, "value"=>{"s"=>4.1, "p"=>17.95, "pml"=>0.02393}}, {"_id"=>4788.0, "value"=>{"s"=>4.2, "p"=>14.95, "pml"=>0.01993}}, {"_id"=>5117.0, "value"=>{"s"=>3.7, "p"=>12.9, "pml"=>0.0172}}, {"_id"=>5118.0, "value"=>{"s"=>4.2, "p"=>12.9, "pml"=>0.0172}}, {"_id"=>5975.0}]
(rdb:1) collection.first.class
Moped::BSON::Document
(rdb:1) collection.to_a
.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/debug.rb:130:in `eval':undefined method `call' for #<Hash:0x007fa7bcf12ca8>

Trying this produces the same error:
collection = collection.each {|c| c.to_hash}.to_a

Any help appreciated. Thanks.





Daly

unread,
Aug 12, 2012, 3:51:57 PM8/12/12
to mon...@googlegroups.com
Thanks Durran, but this has nothing to do with will_paginate.

It's this:
collection = mr_collection.find({},{
    :sort => sort,
    :limit => limit,
    :skip => skip
  }
).to_a

the .to_a causes the "undefined method `call' for #<Hash:" problem.

BTW, I'm using ruby-1.9.3.

I'm converting a Rails2 app to Rails3.2, and this issue is blocking the whole migration process. Any help from the community is appreciated.

Regards,
Ahmed

Durran Jordan

unread,
Aug 12, 2012, 4:03:13 PM8/12/12
to mon...@googlegroups.com
Oh wow I wasn't even looking at that line - just completely went over my head.

What you are getting back from the map_reduce is not a collection object, it's a simple enumerable... If you want the actual resulting Query object from Moped then you would want to call #documents, so in your case:

mr_collection.documents.sort(sort).skip(skip).limit(limit)

Note Moped's syntax is not the same as the 10gen driver... Calling #find on a collection returns you a Query object, which is iterable, but lazy executed when you iterate. You can find the API docs with examples here: http://rdoc.info/github/mongoid/moped/frames

So note that your sorting options also need to change in this case... They need to be in the form of field/direction(1 or -1) pairs. Example:

collection.find.sort(field: -1)

2012/8/12 Daly <ah...@bnotions.com>

Daly

unread,
Aug 12, 2012, 4:11:41 PM8/12/12
to mon...@googlegroups.com
Thanks. Getting closer :)

Tried your suggestion, and I'm getting:
NoMethodError (private method `documents' called for #<Mongoid::Contextual::MapReduce:0x007fcb63fb1130>):

Daly

unread,
Aug 12, 2012, 4:17:21 PM8/12/12
to mon...@googlegroups.com
This did work, not sure if that's a good idea or not:

collection = mr_collection.send(:documents).sort(sort).limit(limit).skip(skip).to_a

Durran Jordan

unread,
Aug 12, 2012, 4:23:32 PM8/12/12
to mon...@googlegroups.com
You can stick with that for now, and I'll make access to the query public in the next release so you can remove the call to #send.

2012/8/12 Daly <ah...@bnotions.com>

Daly

unread,
Aug 12, 2012, 5:36:08 PM8/12/12
to mon...@googlegroups.com
Awesome. Thanks :)
Reply all
Reply to author
Forward
0 new messages