Mongoid - getting all attributes including embedded documents - ruby

Is there an easy way to get all attributes of a Mongoid document, including those of embedded documents?
For example, if I have the following documents:
class Person
include Mongoid::Document
embeds_many :phone_numbers
field :name
end
class PhoneNumner
include Mongoid::Document
embedded_in :person, :inverse_of => :phone_numbers
field :number
end
I would like to get a Person's attributes and phone numbers like this:
{ :name => "Jenny", :phone_numbers => [{ :number => '867-5309' }, { :number => '867-5309' }] }

Since embedded documents are really just other attributes on the parent document, you can get to them like so:
person = Person.create
person.phone_numbers.create(:number => "123-456-7890")
person.attributes
# => {"_id"=>"4c48ff26f7e2da3704000001",
# "phone_numbers"=>
# [{"number"=>"123-456-7890", "_id"=>"4c48ff26f7e2da3704000002"}]}

Related

Mongo object's id as uploadings dirname in CarrierWave

I'm using Sinatra with Mongoid and CarrierWave. I need to store document's attachments in /public/attachments/DOCUMENTS_ID.
Model of Mongo document:
class Dcmnt
include Mongoid::Document
store_in collection: 'dcmnts'
field :published, type: Boolean
field :name, type: String
field :description, type: String
field :additional, type: String
field :created_at, type: Date
mount_uploader :attachment, Uploader, type: String
end
And action's code:
post '/admin/create' do
params.delete 'submit'
d = Dcmnt.new(
:published => params[:published],
:name => params[:name],
:description => params[:description],
:additional => params[:additional],
:created_at => Time.now
)
d.attachment = params[:photos]
d.save
end
When I'm setting up unloader like this:
class Uploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'public/attachments/' + d.id
end
end
It doesn't works for some amzaing reason. Can you help me implement this feature?
Accessing models attributes in CarrierWave is provided via model key word
class Uploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'attachments/' + model.id
end
end

Mongoid: filtering an embedded collection by sub-sub-documents multiple fields

I am a beginner in mogo and mongoid.
I it possible to filter sub-documents collection by a sub-sub-documents multiple fields ($elemMatch)? I'm trying to make a parametrized scope for an embedded collection.
Set up:
class Product
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String, default: ''
embeds_many :versions, class_name: self.name, validate: false, cyclic: true
embeds_many :flags
end
class Flag
include Mongoid::Document
include Mongoid::Timestamps
field :text, type: String
field :state, type: Boolean
end
Typically now i want to filter my versions within single product by flags state and name:
Product.first.versions.where('$elemMatch' => {'flags.text' => 'normalized', 'flags.state' => true}) dosn't work.
Either don't work:
Product.first.versions.elem_match(flags: {text: 'normalized', state: true})
Product.first.versions.where(:flags.elem_match => {text: 'normalized', state: true})
Product.first.versions.where(flags: {'$elemMatch' => {text: 'normalized', state: true}})
Is there a way to do this? Thanks.

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.

How to prevent attachments from being stored in _source with Elasticsearch and Tire?

I've got some PDF attachments being indexed in Elasticsearch, using the Tire gem. It's all working great, but I'm going to have many GB of PDFs, and we will likely store the PDFs in S3 for access. Right now the base64-encoded PDFs are being stored in Elasticsearch _source, which will make the index huge. I want to have the attachments indexed, but not stored, and I haven't yet figured out the right incantation to put in Tire's "mapping" block to prevent it. The block is like this right now:
mapping do
indexes :id, :type => 'integer'
indexes :title
indexes :last_update, :type => 'date'
indexes :attachment, :type => 'attachment'
end
I've tried some variations like:
indexes :attachment, :type => 'attachment', :_source => { :enabled => false }
And it looks nice when I run the tire:import rake task, but it doesn't seem to make a difference. Does anyone know A) if this is possible? and B) how to do it?
Thanks in advance.
The _source field settings contain a list of fields what should be excluded from the source. I would guess that in case of tire, something like this should do it:
mapping :_source => { :excludes => ['attachment'] } do
indexes :id, :type => 'integer'
indexes :title
indexes :last_update, :type => 'date'
indexes :attachment, :type => 'attachment'
end
#imotov 's solution does not work for me. When I execute the curl command
curl -X GET "http://localhost:9200/user_files/user_file/_search?pretty=true" -d '{"query":{"query_string":{"query":"rspec"}}}'
I can still see the content of the attachment file included in the search results.
"_source" : {"user_file":{"id":5,"folder_id":1,"updated_at":"2012-08-16T11:32:41Z","attachment_file_size":179895,"attachment_updated_at":"2012-08-16T11:32:41Z","attachment_file_name":"hw4.pdf","attachment_content_type":"application/pdf","created_at":"2012-08-16T11:32:41Z","attachment_original":"JVBERi0xL .....
Here's my implementation:
include Tire::Model::Search
include Tire::Model::Callbacks
def self.search(folder, params)
tire.search() do
query { string params[:query], default_operator: "AND"} if params[:query].present?
filter :term, folder_id: folder.id
highlight :attachment_original, :options => {:tag => "<em>"}
end
end
mapping :_source => { :excludes => ['attachment_original'] } do
indexes :id, :type => 'integer'
indexes :folder_id, :type => 'integer'
indexes :attachment_file_name
indexes :attachment_updated_at, :type => 'date'
indexes :attachment_original, :type => 'attachment'
end
def to_indexed_json
to_json(:methods => [:attachment_original])
end
def attachment_original
if attachment_file_name.present?
path_to_original = attachment.path
Base64.encode64(open(path_to_original) { |f| f.read })
end
end

Creating Mongoid embedded documents

I am using Mongoid for the first time. I want to store a collection of emails that have a subject, body, and arrays of to, cc, and bcc recipients. Example:
{to: [{email: 'andrew#example.com', name: 'Andrew'}], cc: ...
However, I can't seem to figure out how to model this data using Mongoid. I think the term for these are called embedded documents, but everything I've tried does not seem to work right. How do I create my models correctly with Mongoid?
Here is the solution. You can specify class name if you want to reuse a class for multiple fields:
class Email
include Mongoid::Document
embeds_many :to_recipients, :class_name => "Recipient"
embeds_many :cc_recipients, :class_name => "Recipient"
embeds_many :bcc_recipients, :class_name => "Recipient"
embeds_one :from, :class_name => "Recipient"
field :subject, type: String
field :body_text, type: String
field :body_html, type: String
end
class Recipient
include Mongoid::Document
field :email_address, type: String
field :name, type: String
validates :email_address, :presence => true
embedded_in :emails
end

Resources