Mongoid nested embedded document save ignores query - ruby

I'm having trouble saving the correct nested embedded document with mongoid. I've tried this in both v3.1.6 and v4.0.0 and the result is the same. The parent document is saved, but the nested document I modified ignores the changes, and instead updates the first nested document instead.
Assume some models like so:
Mongoid.configure do |config|
config.sessions = {default: {hosts: ["localhost:27017"], database: "test_mongoid"}}
end
class Band
include Mongoid::Document
field :name, type: String
embeds_many :members
end
class Member
include Mongoid::Document
embedded_in :band
field :name, type: String
embeds_many :instruments
end
class Instrument
include Mongoid::Document
embedded_in :member
field :name, type: String
def make_badass!
self.name = "Badass #{self.name}"
self.save
end
end
And a program that runs:
Band.destroy_all
a_band = {
name: "The Beatles",
members: [
{
name: 'Paul',
instruments: [
{
name: 'Bass guitar'
},
{
name: 'Voice'
}
]
}
]
}
Band.create(a_band)
the_beatles = Band.first
puts the_beatles.as_document
paul = the_beatles.members.first
voice = paul.instruments.where({name: "Voice"}).first
voice.make_badass!
puts Band.first.as_json
The database should now contain:
{
"_id": ObjectId('53aa7d966d6172889c000000'),
"name" : "The Beatles",
"members" : [
{
"_id" : ObjectId('53aa7d966d6172889c010000'),
"name" : "Paul",
"instruments" : [
{"_id" : ObjectId('53aa7d966d6172889c020000'), "name" : "Bass guitar"},
{"_id" : ObjectId('53aa7d966d6172889c030000'), "name" : "Voice"}
]
}
]
}
But instead, it contains:
{
"_id": ObjectId('53aa7d966d6172889c000000'),
"name" : "The Beatles",
"members" : [
{
"_id" : ObjectId('53aa7d966d6172889c010000'),
"name" : "Paul",
"instruments" : [
{"_id" : ObjectId('53aa7d966d6172889c020000'), "name" : "Badass Voice"},
{"_id" : ObjectId('53aa7d966d6172889c030000'), "name" : "Voice"}
]
}
]
}
What would be a working way of having the correct embedded document to change from within the instance method of Instrument?
Thanks for your help!

Mongoid is quickly forcing me to become an alcoholic. Hopefully it will help someone in the same situation.
class Instrument
include Mongoid::Document
embedded_in :member
field :name, type: String
def make_badass
self.name = "Badass #{self.name}"
self.member.band.save
end
def unset_name
# self.unset :name does not work
self.remove_attribute :name
self.member.band.save
end
end

Related

Getting BSON string from from mongoid document object

Given an example document as below is there a way to get a bson string representing the document?
class Foobar
include Mongoid::Document
store_in collection :foobars
field :n, as: :name, type: String
field :ts, as: :install_timestamp, type: DateTime
end
ie
john_smith = Foobar.create(name: "John Smith", install_timestamp: Time.now)
john_smith.bson_string
which would return one of the following
{
"n": "John Smith",
"ts": {"$date": {"$numberLong":"1573596151000"} }
}
or
{
"n": "John Smith",
"ts" : ISODate("2019-11-12T22:02:31.848Z"),
}
I've tried doing this utilizing the underlying BSON library and the to_bson function but that returns a bytebuffer which I can't manage to get converted into a string.
Not completely sure, if this is what you need, but
class Foobar
include Mongoid::Document
store_in collection: :foobars
field :n, as: :name, type: String
field :ts, as: :install_timestamp, type: DateTime
end
john_smith = Foobar.create(name: "John Smith", install_timestamp: Time.now)
# => #<Foobar _id: 5dcca0b9909208042530925a, n(name): "John Smith", ts(install_timestamp): 2019-11-14 00:32:57 UTC>
john_smith_attributes = john_smith.attributes
# => {"_id"=>BSON::ObjectId('5dcca0b9909208042530925a'), "n"=>"John Smith", "ts"=>2019-11-14 00:32:57 UTC}
bson = john_smith_attributes.to_bson
# => #<BSON::ByteBuffer:0x00007fd59d16eb10>
bson_string = bson.to_s
# => "4\x00\x00\x00\a_id\x00]\xCC\xA0\xB9\x90\x92\b\x04%0\x92Z\x02n\x00\v\x00\x00\x00John Smith\x00\tts\x00\xC2\xD5Sgn\x01\x00\x00\x00"
BSON::Document.from_bson(BSON::ByteBuffer.new(bson_string))
# => {"_id"=>BSON::ObjectId('5dcca0b9909208042530925a'), "n"=>"John Smith", "ts"=>2019-11-14 00:32:57 UTC}
Links to docs:
BSON::ByteBuffer#to_s
from_bson and to_bson
attributes
Hope this helps.

Ruby Mongoid DateTime.gte/lte query not working

I have a mongoid class User with a DateTime field called last_notification_check
not all the User objects have this field, because its only added after the first time a user logs in.
I need to query for the following:
User.where(:last_notification_check.lte => (Time.now - 60).utc)
this should return a response of 1 user object, but it continuously returns nil
my mongoid object that it should be able to pick up:
{
"_id" : ObjectId("590ad06f78cee4ffcd612d5e"),
"active" : true,
"first_name" : "Test",
"last_name" : "Person",
"suffix" : null,
"email" : "test#email.com",
"encrypted_password" : "s7FThz4a68nBKx6aGJ19Bw==",
"admin" : true,
"updated_at" : ISODate("2017-05-28T21:29:30.486+0000"),
"created_at" : ISODate("2017-05-04T06:55:42.990+0000"),
"title" : "Test User",
"last_notificaton_check" : ISODate("2017-05-28T21:29:30.485+0000")
}
I get the following results.. not sure what to do...
User.where(:last_notification_check.gte => Time.now)
# nil
User.where(:last_notification_check.lte => Time.now)
# nil
User.where(:first_name => 'Test').each do |user|
puts user.last_name
end
# "Person"

Mongoid saving only ids and leaving out other fields

I am using Nokogiri and mongoid in a test project.
Here's my code:
urls = Array[
'http://www.example.com/cairo',
'http://www.example.com/alexandria',
]
urls.each do |url|
doc = Nokogiri::HTML(open(url))
#Theater and location details here
#movies scrapper starts here
movies = doc.css('.itemContainer')
movies.each do |movie|
#movie title
title = movie.css("h3.catItemTitle a").text
#More code here
#movie synopsis
synopsis = movie.css(".catItemIntroText p").text
Movie.create! {
{title: title}
{synopsis: synopsis }
#{movielength: movielength }
#embedded theater collection
{theaters: {theater: theater,
address: address
}
}
}
end
end
My mongoid models look like this:
require 'mongoid'
class Movie
include Mongoid::Document
field :title, :type => String
field :synopsis, :type => String
attr_accessible :title, :synopsis
embeds_many :theaters
end
When I run the Nokogiri script only the mongodb objectid gets saved and the field details are not created or saved.
Here's a sample of my database:
] }
{ "_id" : ObjectId("52be9b3c4cfad19f0c000011") }
{ "_id" : ObjectId("52be9b3c4cfad19f0c000012") }
{ "_id" : ObjectId("52be9b3c4cfad19f0c000013") }
has more
Everything works well using Ruby puts to output but saving in mongodb is a hassle. What am I doing wrong? I am using Mongoid (3.1.6) with Ruby 1.9.3p448 on Mac OS Mavericks.
You're nesting the hashes. Do this instead for a starter:
Movie.create!(title: title, synopsis: synopsis, theaters: [theater])

How to build a json object of a Mongoid object with children?

I have two simple classes
class Band
include Mongoid::Document
field :name, type:String
has_many :members
end
class Member
include Mongoid::Document
field :name, type: String
belongs_to :band
end
After I have created two object for test purposes
Band.create(title: 'New Band')
Band.members.create(name: 'New Member')
I got next db state:
> db.bands.find()
{ "_id" : ObjectId("..."), "title" : "New Band" }
> db.members.find()
{ "_id" : ObjectId("..."), "name" : "New Member", "band_id" : ObjectId("...") }
When I try to build json object of Band object I get data without children:
{"_id":"...","title":"New Band"}
But I need something like that:
{"_id":"...","title":"New Band", "members" : {"_id":"...","title":"New Member"}}
How to build json with children??
You can override serializable_hash:
class Member
include Mongoid::Document
field :name, type: String
belongs_to :band
def serializable_hash(options={})
{
id: id,
name: name
}
end
end
class Band
include Mongoid::Document
field :title, type: String
has_many :members
def serializable_hash(options={})
{
id: id,
title: title,
members: members.inject([]) { |acc, m| acc << m.serializable_hash; acc }
}
end
end
Suppose you have a band with a member:
band = Band.create(title: 'New Band')
band.members.create(name: 'New Member')
In that case band.to_json will return you something like that:
"{\"id\":...,\"title\":\"New Band\",\"members\":[{\"id\":...,\"name\":\"New Member\"}]}"
Try this:
a_band = Band.last
a_band.as_json(methods: [:members])
Mongoid auto-generates helper methods for your relations, and you can include these methods when you build your JSON object. You can use a_band.members to fetch the band's members out of the db, so you can include that method in your JSON object, like any other method on the model.

On MongoMapper, what is the difference between a Document and an EmbeddedDocument?

This example makes it seem like both are used (included) in order to make a class a persistent model, but it is not clear when I should use one or the other.
A MongoMapper::Document is saved to the database as a top-level record. A MongoMapper::EmbeddedDocument is saved within another document. For instance, let's say I have a blogging application. I have a Post and Comment model. They might look something like this:
require 'mongo_mapper'
MongoMapper.database = 'test'
class Post
include MongoMapper::Document
key :title
key :body
key :comments
many :comments
end
class Comment
include MongoMapper::EmbeddedDocument
key :author
key :body
end
p = Post.new(:title => 'Some post', :body => 'About something')
p.comments << Comment.new(:author => 'Emily', :body => 'A comment')
p.save
puts Post.all.map(&:inspect)
Will produce a document in your mongo database that looks like this:
{ "_id" : ObjectId("4c4dcf4b712337464e000001"),
"title" : "Some post",
"body" : "About something",
"comments" : [
{
"body" : "A comment",
"author" : "Emily",
"_id" : ObjectId("4c4dcf4b712337464e000002")
}
] }
In terms of interacting with them through MongoMapper, only a MongoMapper::Document response to methods like find and save. A MongoMapper::EmbeddedDocument can only be accessed through its parent document. The implication of this is that you should only use MongoMapper::EmbeddedDocument for models that are clearly subsidiary to their parent models and will only be used in the context of that parent.

Resources