Mongo/Ruby undefined method `update' for Mongo::Collection - ruby

I am having an issue within stupid-simple piece of ruby/mongo code below:
require 'mongo'
client = Mongo::Client.new(['127.0.0.1:27017'], database: 'dbs')
items = client[:items].find('issues.category': 'general')
items.each do |item|
item2 = item
client[:items].update({ '_id': item['_id'] } , item2)
end
I get undefined method "update" for #<Mongo::Collection:0x4544580 namespace=dbs.items> (NoMethodError)

There is no update method for the MongoDB ruby driver, there is either update_one or update_many.
In your case, it looks like you're trying to update all:
client[:items].update_many({ :id => item['_id'] }, item2)
See docs here

Related

koala: how to get shares from Facebook Graph API

I'm trying to get the count of shares from a Facebook page, but I can't seem to get the result out of the hash.
Here's what I have:
y_response = #graph.get_connection('some_fb_page','posts',
{fields: ['message', 'id', 'from', 'type',
'properties', 'link', 'shares', 'likes.summary(true)',
'comments.summary(true)', 'created_time', 'updated_time']
})
So y_response is Koala::Facebook::API::GraphCollection
y_response.each do |post|
and each of the post elements is a Hash
puts post["shares"]
gives me: {"count"=>3}
but
puts post["shares"]["count"]
gives an
undefined method `[]' for nil:NilClass (NoMethodError)
I've also tried
puts post["shares"][:count]
puts post["shares"][count]
puts post["shares"].count
for laughs.
What am I doing wrong?
How do I get the count from the hash?

Mongoid::Contextual#each delegated to context.each, but context is nil

I have a Sinatra application connecting to MongoDB via Mongoid. It was working fine until a point where I started getting a weird error that I can't find anywhere on stackoverflow or anywhere.
Note that the same code is deployed on Heroku and it works fine with the same database version Mongo 3.0.7, ruby version 2.0.0 which I tried locally as well as 2.2.3. I get the error when I run shotgun through the web and when running Rspec:
Bible::Book#get_book_id is case-insensitive
Failure/Error: book[0]._id
Module::DelegationError:
Mongoid::Contextual#each delegated to context.each, but context is nil: #<Mongoid::Criteria
selector: {"title"=>"genesis"}
options: {}
class: Bible::Book
embedded: false>
# /Users/issa/.rvm/gems/ruby-2.2.3/gems/mongoid-5.0.0/lib/mongoid/contextual.rb:20:in `rescue in each'
# /Users/issa/.rvm/gems/ruby-2.2.3/gems/mongoid-5.0.0/lib/mongoid/contextual.rb:20:in `each'
# /Users/issa/.rvm/gems/ruby-2.2.3/gems/mongoid-5.0.0/lib/mongoid/criteria.rb:554:in `entries'
# /Users/issa/.rvm/gems/ruby-2.2.3/gems/mongoid-5.0.0/lib/mongoid/criteria.rb:554:in `method_missing'
# ./app/bible/book.rb:52:in `get_book_id'
# ./spec/bible/book_spec.rb:11:in `block (3 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# NoMethodError:
# undefined method `each' for nil:NilClass
# /Users/issa/.rvm/gems/ruby-2.2.3/gems/mongo-2.1.2/lib/mongo/cluster.rb:114:in `initialize'
My Rspec code:
RSpec.describe Bible::Book do
describe "#get_book_id" do
it "finds the id of genesis" do
expect(Bible::Book.get_book_id 'genesis').to eq 1
end
end
end
The code implementation is this:
module Bible
class Book
include Mongoid::Document
store_in collection: "bible_books"
field :title, type: String
field :testament, type: String
...
def self.get_book_id book_title
book = where(:title => book_title.downcase.strip)
return unless book
book[0]._id
end
end
end
My Gemfile has this part for MongoDB:
# MongoDB
...
gem 'mongoid', '~> 5.0'
gem 'bson_ext'
gem 'bson'
It's really strange that I get this error only on my machine, and just started out of the blue. I am using OS X El Capitan. I can access the mongodb collection from command line without issues and can run the same equivalent query to get the result I want:
> use bible
switched to db bible
> db.bible_books.find({"title": "genesis"})
{ "_id" : 1, "title" : "genesis", "testament" : "old", "chapters" : 50, "doc_type" : "book", "canon_type" : "canonical", "canon_order" : 1, "full_name" : { "en" : "The book of Genesis", "ar" : "سفر التكوين" }, "short_name" : { "en" : "Genesis", "ar" : "التكوين" }, "abbr_name" : { "ar" : "تك", "en" : "Gen" } }
I tried other collections, other queries, other variations, and still get the same error. I reverted to an older code from github that worked for sure, still the same issue, so it's clear to me that there's nothing wrong in my code or configuration. I tried different ruby versions from 2.0.0 - 2.2.3, different mongoid versions from 4 - 5 ... again the same versions of code and installation work fine on Heroku, but not my machine :(
I got similar error when wrong production configuration url was specified for mongoid: uri: <%= ENV['MONGOLAB_URI'] %>
I'm not sure if this is the source of your problem but I see a couple odd things in your get_book_id class method.
First of all, this will never leave book.nil?:
book = where(:title => book_title.downcase.strip)
That will leave you with a Mongoid::Contextual in book, the query may or may not find anything but book won't be nil in either case. That means that your:
return unless book
on the next line doesn't do anything useful. That also means that book[0] can be nil in here:
book[0]._id
You're also calling the _id method when you'd usually call id.
I think your method would be better off like this:
def self.get_book_id book_title
book = find_by(:title => book_title.downcase.strip)
return unless book
book.id
end
find_by(query) is just a short way of saying where(query).first so book = find_by(...) will leave you with a nil in book if it can't find anything and a single Bible::Book in book if it can find something.
If you have ActiveSupport available, you could also say:
find_by(:title => book_title.downcase.strip).try(:id)
to hide the nil check. You could also throw in an only call to only pull the _id out of MongoDB:
book = only(:id).find_by(:title => book_title.downcase.strip)
return unless book
book.id
# or, if ActiveSupport is around, just this:
only(:id).find_by(:title => book_title.downcase.strip).try(:id)

Extract a value from an OpenStruct Ruby object

I get the following Ruby object returned (from a query to the Google Analytics API using the garb gem, comes from the sample call shown on the README.md there, Exits.results(profile, :filters => {:page_path.eql => '/'}))
> data.results
=> [#<OpenStruct page_path="/", exits="3706", pageviews="10440">]
I'd to extract the pageviews value (10440), but cannot figure out how to do it. I see that my object, data.results is class array of length 1, but data.first is class OpenStruct with a return value that looks almost identical:
irb(main):140:0> data.results.class
=> Array
irb(main):141:0> data.results.length
=> 1
irb(main):142:0> data.first
=> #<OpenStruct page_path="/", exits="3706", pageviews="10440">
irb(main):143:0> data.first.class
=> OpenStruct
while data itself seems to be a custom return type called ResultsSet:
irb(main):144:0> data.class
=> Garb::ResultSet
irb(main):145:0> data
=> #<Garb::ResultSet:0x00000002411070 #results=[#<OpenStruct page_path="/", exits="3706", pageviews="10440">], #total_results=1, #sampled=false>
irb(main):146:0>
Lots of data structures, but no idea how to get my desired value out. I gathered OpenStruct was related to a hash, so I thought data.first["pageviews"] would do it,
NoMethodError: undefined method `[]' for #<OpenStruct page_path="/", exits="3706", pageviews="10440">
from (irb):146
from /usr/bin/irb:12:in `<main>'
Meanwhile data.first.keys returns nil. No idea how to get my data out, (short of converting the length-1 array, data.results to a string and parsing with grep, which seems crazy. Any ideas?
Please try this:
data.first.pageviews

Cannot find Document with Ruby and MongoDB when using ObjectId

I have some code written in Ruby 1.9.2 patch level 136 and I'm have an issue where when I perform a find via the _id in the raw ruby mongo driver I get a nil when trying to use a value from a csv file. Here's the code:
require 'mongo'
require 'csv'
require 'bson'
# Games database
gamedb = Mongo::Connection.new("localhost", 27017).db("gamedb")
#games = gamedb.collection("games")
# Loop over CSV data.
CSV.foreach("/tmp/somedata.csv") do |row|
puts row[0] # Puts the ObjectId
#game = #games.find( { "_id" => row[0] } ).first
puts #game.inspect
end
The CSV file looks like this:
_id,game_title,platform,upc_db_match,upc
4ecdacc339c7d7a2a6000002,TMNT,PSP,TMNT,085391157663
4ecdacc339c7d7a2a6000004,Super Mario Galaxy,Wii,Super Mario Galaxy,045496900434
4ecdacc339c7d7a2a6000005,Beowulf,PSP,Beowulf,097363473046
The first column is the objectId in Mongo that I already have. If I perform a local find from the mongo command line the values in the first column, I get the data I want. However, the code above returns nil on the #game.inspect call.
I've tried the following variations, which all produce nil:
#game = #games.find( { "_id" => row[0].to_s } ).first
#game = #games.find( { "_id" => row[0].to_s.strip } ).first
I've even tried building the ObjectId with the BSON classes as such:
#game = #games.find( { "_id" => BSON::ObjectId(row[0]) } ).first
or
#game = #games.find( { "_id" => BSON::ObjectId("#{row[0]}") } ).first
Both of which output the following error:
/Users/donnfelker/.rvm/gems/ruby-1.9.2-p136#upc-etl/gems/bson-1.4.0/lib/bson/types/object_id.rb:126:in `from_string': illegal ObjectId format: _id (BSON::InvalidObjectId)
from /Users/donnfelker/.rvm/gems/ruby-1.9.2-p136#upc-etl/gems/bson-1.4.0/lib/bson/types/object_id.rb:26:in `ObjectId'
from migrate_upc_from_csv.rb:14:in `block in <main>'
from /Users/donnfelker/.rvm/rubies/ruby-1.9.2-p136/lib/ruby/1.9.1/csv.rb:1768:in `each'
from /Users/donnfelker/.rvm/rubies/ruby-1.9.2-p136/lib/ruby/1.9.1/csv.rb:1202:in `block in foreach'
from /Users/donnfelker/.rvm/rubies/ruby-1.9.2-p136/lib/ruby/1.9.1/csv.rb:1340:in `open'
from /Users/donnfelker/.rvm/rubies/ruby-1.9.2-p136/lib/ruby/1.9.1/csv.rb:1201:in `foreach'
from migrate_upc_from_csv.rb:10:in `<main>'
The crazy thing is, if I manually create the BSON ObjectId by hand it works (as shown below):
#game = #games.find( { "_id" => BSON::ObjectId("4ecdacc339c7d7a2a6000004") } ).first
When I run #game.inspect I get my data back, as I would expect. However, If I change this to use row[0], I get nil.
Why? What am I doing wrong?
System Details
$ gem list
*** LOCAL GEMS ***
bson (1.4.0)
bson_ext (1.4.0)
mongo (1.4.0)
RVM Version: rvm 1.6.9
Ruby Version: ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
Mongo Version:
[initandlisten] db version v1.8.2, pdfile version 4.5
[initandlisten] git version: 433bbaa14aaba6860da15bd4de8edf600f56501b
Again, why? What am I doing wrong here? Thanks!
The first row is not being read as a header, to do that pass in :headers => true like this:
require 'csv'
# Loop over CSV data.
CSV.foreach("/tmp/somedata.csv", :headers => true) do |row|
puts row[0] # Puts the ObjectId
end
If you do not pass the :headers parameter in you can see the first row[0] object is the string "_id":
_id
4ecdacc339c7d7a2a6000002
4ecdacc339c7d7a2a6000004
4ecdacc339c7d7a2a6000005
When you include it, you are golden:
4ecdacc339c7d7a2a6000002
4ecdacc339c7d7a2a6000004
4ecdacc339c7d7a2a6000005
Are you sure your CSV parsing code isn't treating the headers as a first line of data and actually tries to do BSON::ObjectId("_id")? The error message kinda looks like it. Try with FasterCSV.foreach('/tmp/somedata.csv', :headers => true) and using row['_id'] (IIRC you'll still have to use BSON::ObjectID).

Ruby Mongo Driver - Find_by_Id

What am I doing wrong here? I know the _id is in the database but I get empty result.
#b = coll.find("_id" => "4db2ebee90036f010b000001")
Thanks
Use this:
coll.find(:_id => BSON::ObjectId('4db2ebee90036f010b000001')).each do |data|
puts data.inspect
end
#b will contain a cursor, not the result. You also need to use an object id proper.
You probably want this:
#b = coll.find_one(:_id => BSON::ObjectId('4db2ebee90036f010b000001'))
With Ruby 1.9.3 and mongoid 3.0.19
#coll = Coll.find( hash["_id"] )
or
#coll = Coll.find( "511296d2dfa18f07fa000009" )
find the record. Will only ever be one, _id is the primary key, it can never be double.
I would use something like first which returns a object since you have bigger problems if your primary id is duplicated in your database. The syntax is depending on your mongo gem version this one is for 2.1.0.
your_id = '4db2ebee90036f010b000001'
db = Client.new([ "localhost:27017" ], :database => "db")
coll = db[:testCollection]
res = coll.find(:_id => BSON::ObjectId(your_id)).first
Using, Ruby version 2.3.1p112, mongo (gem) 2.4.2 and BSON (gem) 4.2.2
The following worked for me
client = Mongo::Client.new(['127.0.0.1:3001'], :database=>'dbname')
collection = client[:users]
user = collection.find({_id:'XY3h5R7aJkh5FxFhJ'}).first

Resources