Mongoid saving only ids and leaving out other fields - ruby

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])

Related

rails elasticsearch-model multiple params query

I want to run multiple params of search with elasticsearch-model gem like below. Before i used gem tire to search records using multiple params of queries.
My model:
class Book < ApplicationRecord
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :id, type: 'integer'
indexes :title
indexes :author
indexes :category
indexes :price
end
def self.search(params)
tire.search(load: true) do
query do
boolean do
must { string params[:title] } if params[:title].present?
must { string params[:author] } if params[:author].present?
must { string params[:category] } if params[:category].present?
should { range :price, { gte: params[:price_range_min], lte:
params[:price_range_max] } } if
params[:price_range_min].present?
&& params[:price_range_max].present?
end
end
end
end
end
My controller:
class BookController < ApplicationController
def index
#books = Book.search(params)
#books =
Kaminari.paginate_array(#books).page(params[:page]).per(10)
end
end
I followed this documentation elasticsearch-model but i am getting results only for two params of field.
Any help?

Mongoid nested embedded document save ignores query

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

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.

parse json to object ruby

I looked into different resources and still get confused on how to parse a json format to a custom object, for example
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
and JSON file
{
"Resident": [
{
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}
]
}
what's the correct way to parse the json file into a array of 3 Resident object?
Today i was looking for something that converts json to an object, and this works like a charm:
person = JSON.parse(json_string, object_class: OpenStruct)
This way you could do person.education.school or person[0].education.school if the response is an array
I'm leaving it here because might be useful for someone
The following code is more simple:
require 'json'
data = JSON.parse(json_data)
residents = data['Resident'].map { |rd| Resident.new(rd['phone'], rd['addr']) }
If you're using ActiveModel::Serializers::JSON you can just call from_json(json) and your object will be mapped with those values.
class Person
include ActiveModel::Serializers::JSON
attr_accessor :name, :age, :awesome
def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end
def attributes
instance_values
end
end
json = {name: 'bob', age: 22, awesome: true}.to_json
person = Person.new
person.from_json(json) # => #<Person:0x007fec5e7a0088 #age=22, #awesome=true, #name="bob">
person.name # => "bob"
person.age # => 22
person.awesome # => true
require 'json'
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
s = '{"Resident":[{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"}]}'
j = JSON.parse(s)
objects = j['Resident'].inject([]) { |o,d| o << Resident.new( d['phone'], d['addr'] ) }
p objects[0].phone
"12345"
We recently released a Ruby library static_struct that solves the issue. Check it out.

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