Create a foreign key using belongs_to between two mongoid collections - ruby

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.

Related

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, Mongoid join data from different models in Ruby on Rails

I'm adapting a Ruby on Rails application to work with MongoDB instead of PostgreSQL. I have this 2 class:
module Cms
class Content
include Mongoid::Document
include Mongoid::Orderable
field :title, type: String
field :position, type: String
field :text_body, type: String
field :expiration_date, type: Date
field :active, type: Boolean, default: false
validates :title, :text_body, presence: true
orderable base: 0
belongs_to :cms_content_category, class_name: 'Cms::ContentCategory'
scope :ordered, ->() { order(position: :asc) }
scope :not_expired, ->() { any_of({ expiration_date: nil }, { :expiration_date.gte => Date.today }) }
end
end
module Cms
class Submenu
include Mongoid::Document
include Mongoid::Orderable
field :title, type: String
field :position, type: String
field :external_url, type: String
field :open_as, type: String, default: '_self'
field :active, type: Boolean, default: false
delegate :url_helpers, to: :'Rails.application.routes'
validates :title, presence: true
validates :external_url, presence: true, if: Proc.new { |obj| obj.cms_content_id.nil? }
validates :cms_content_id, presence: true, if: Proc.new { |obj| obj.external_url.blank? }
orderable base: 0
belongs_to :cms_content, class_name: 'Cms::Content'
belongs_to :cms_menu, class_name: 'Cms::Menu'
scope :ordered, ->() { order(position: :asc) }
def url
self.external_url.present? ? self.external_url : url_helpers.content_show_path(self.cms_content_id)
end
end
end
The problem is that I don't know how to adapt the following SQL query to work with MongoDB.
scope :active, joins("FULL JOIN contents ON contents.id = cms_submenus.content_id")
.where("cms_submenus.active IS TRUE AND (
(cms_submenus.content_id IS NOT NULL AND ((contents.expiration_date >= :today OR expiration_date IS NULL) AND contents.archived IS NOT TRUE)) OR
(cms_submenus.external_url IS NOT NULL AND cms_submenus.external_url <> '')
)", today: Date.today)
This scope has to be called from Submenu model.
Someone has an idea on how to solve this problem?
I'm using Rails 4.2.0 with Ruby 2.2.0 and Mongoid 4.0.2 with MongoDB 2.6.8.
thank you

Unable to create correct Mongoid model from JSON

I'm trying to create a Mongoid model from the corresponding JSON structure.
However it does not include the embedded relation frameworks.
I'm using Mongoid 4.0. Am I doing something wrong or is this a bug?
If I store any embedded relation via store_as under a different name than the default serialization, it works as expected. Also, if I create the model in the database from JSON rather than initialize it everything's fine...
JSON input
{
"name": "MyName",
"frameworks": [
{
"name": "grails",
"runtime": "groovy",
"versions": []
}
]
}
Models
require 'mongoid'
class Vendor
include Mongoid::Document
include Mongoid::Attributes::Dynamic
# fields
field :name, type: String
# relations
embeds_many :frameworks
# validations
validates :name, presence: true
validates :frameworks, presence: true
end
class Framework
include Mongoid::Document
embedded_in :vendor
field :name, type: String
field :runtime, type: String
field :versions, type: Array
# validations
validates :name, presence: true
validates :runtime, presence: true
end
Test App
require 'json'
require 'require_relative'
require_relative 'vendor'
begin
json = JSON.parse(File.read('input.json'))
#profile = Vendor.new(json)
puts #profile.inspect
rescue JSON::ParserError => e
puts "Error: " << e.to_s
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.

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