Creating Mongoid embedded documents - ruby

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

Related

Create a foreign key using belongs_to between two mongoid collections

There are two classes.
First class
class Baby
include Mongoid::Document
field :first_name, type: String
field :surname, type: String
field :dob, type: Integer
belongs_to :parent, foreign_key: :username
validates :parent, :presence => true
end
Second class
class Parent
include Mongoid::Document
field :username, type: String
field :first_name, type: String
field :surname, type: String
field :email, type: String
field :password, type: String
has_many :baby
end
When I create a new instance of the Baby class, I pass in a username. This should correlate to a username in the Parent document. Effectively storing a relation between the Baby and the Parent.
Baby.create!(first_name: 'James', surname: 'Barr', dob: '17-09-2012', username: 'foo123')
I keep getting this every time:
Mongoid::Errors::Validations:
message:
Validation of Baby failed.
summary:
The following errors were found: Parent can't be blank, Parent can't be blank
resolution:
Try persisting the document with valid data or remove the validations.

Neo4j and Ruby/Rails: How to only return nodes based on user permissions

How to return an index of only Items where the User has permission?
How to return an index of only Items where the Group a User is in has permission?
How to return a single item only if a User has permission?
How to return a single item only if the Group a User is in has permission?
Note: I know it's possible to query for Item.all, then iterate through the array and pull out only items where .has_permissions == User, but this completely ignores the benefits of having everything in a graph, so is not an answer.
To keep this simple, let's say there are 3 objects:
An Item
A User
A Group
Typical graph situations:
(User)<-[:HAS_PERMISSIONS]-(Item)
(Group)<-[:HAS_PERMISSIONS]-(Item)
(Group)-[:HAS_MEMBERS]->(User)
With the models:
class Item
include Neo4j::ActiveNode
property :name, type: String
property :description, type: String
has_many :out, :user_permission_to, type: :PERMISSION_TO, model_class: :User
has_many :out, :group_permission_to, type: :PERMISSION_TO, model_class: :Group
end
class Identity
include Neo4j::ActiveNode
property :username, type: String
has_many :in, :permission_to, type: :PERMISSION_TO, model_class: :Item
has_many :in, :groups, type: :GROUP_MEMBER, model_class: :Group
end
class Group
include Neo4j::ActiveNode
property :group_name, type: String
has_many :in, :permission_to, type: :PERMISSION_TO, model_class: :Item
has_many :out, :members, type: :GROUP_MEMBER, model_class: :User
end
And with the simple controller:
# GET /items
def index
#items = Item.all
render json: #items
end
# GET /item/1
def show
render json: #item
end
For starters, I'd suggest checking out this article (the second half covers access control which is very similar)
"How to return an index of only Items where the User has permission?"
You could do this a couple of ways. More explicitly:
identity.as(:id).query.match("(id)<-[PERMISSION_TO*1..2]-(item:Item)").pluck(:item)
Alternatively, I think that this would work:
identity.permission_to(rel_length: 1..2)
"How to return an index of only Items where the Group a User is in has permission?"
Simple:
identity.groups.permission_to
"How to return a single item only if a User has permission?"
For the two solutions above:
identity.as(:id).query.match("(id)<-[PERMISSION_TO*1..2]-(item:Item)").limit(1).pluck(:item)
# or
identity.permission_to(rel_length: 1..2).first
"How to return a single item only if the Group a User is in has permission?"
identity.groups.permission_to
Separately, some feedback:
Using the term "index" the way you're using it is a bit confusing because Neo4j has indexes which allow for performant querying of properties on labels.
I would probably make my models like this:
class Item
include Neo4j::ActiveNode
property :name, type: String
property :description, type: String
has_many :in, :users_with_permission, type: :CAN_ACCESS, model_class: :Identity
has_many :in, :groups_with_permission, type: :CAN_ACCESS, model_class: :Group
end
class Identity
include Neo4j::ActiveNode
property :username, type: String
has_many :out, :accessible_items, type: :CAN_ACCESS, model_class: :Item
has_many :out, :groups, type: :IN_GROUP # Don't need `model_class: :Group` here
end
class Group
include Neo4j::ActiveNode
property :group_name, type: String
has_many :out, :accessible_items, type: :CAN_ACCESS, model_class: :Item
has_many :in, :members, type: :IN_GROUP, model_class: :Identity
# You could also do:
# has_many :in, :members, model_class: :Identity, origin: :groups
end

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.

Mongoid - getting all attributes including embedded documents

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"}]}

Resources