Get EmbeddedDocument's Parent Document's ID in MongoMapper - ruby

Suppose the following model.
class Person
include MongoMapper::Document
key :name, String
key :surname, String
many :children
end
class Child
include MongoMapper::EmbeddedDocument
key :name, String
end
Plus, the following query (with Sinatra):
get 'child/:id' do
#child = Child.find(params[:id])
end
Is there a way to get the ID of the Person that that Child belongs to?

I think what you're looking for is this:
class Child
include MongoMapper::EmbeddedDocument
embedded_in :parent
key :name, String
end
I'm not quite sure how your query works - I'm not seeing that there's a find on the Child class since it's an EmbeddedDocument. However:
Person.where("children._id" => params[:id]).first.parent
should work.

Related

Querying mongoid for value in attribute array

I need to search within Mongoid objects that have array attributes. Here are the relevant objects:
class Author
include Mongoid::Document
field :name, type: String
class Book
include Mongoid::Document
field :name, type: String
field :authors, type: Array
I can see that at least one book has a given author:
Book.all.sample.authors
=> [BSON::ObjectId('5363c73a4d61635257805e00'),
BSON::ObjectId('5363c73a4d61635257835e00'),
BSON::ObjectId('5363c73a4d61635257c75e00'),
BSON::ObjectId('5363c73b4d616352574a5f00')]
But I'm unable to find books that have that author.
Book.where(authors: '5363c73a4d61635257805e00').first
=> nil
I've tried the solution listed here: https://groups.google.com/forum/#!topic/mongoid/csNOcugYH0U but it didn't work for me:
Book.any_in(:author => ["5363c73b4d616352574a5f00"]).first
=> nil
I'm not sure what I'm doing wrong. Any ideas? I'd prefer to use Mongoid Origin commands.
This output:
Book.all.sample.authors
=> [BSON::ObjectId('5363c73a4d61635257805e00'),
BSON::ObjectId('5363c73a4d61635257835e00'),
BSON::ObjectId('5363c73a4d61635257c75e00'),
BSON::ObjectId('5363c73b4d616352574a5f00')]
tells us that authors contains BSON::ObjectIds. ObjectIds are often presented as Strings and sometimes you can use a String instead of a full blown ObjectId (such as with Model.find) but they're still not Strings. You are searching the array for a String:
Book.where(authors: '5363c73a4d61635257805e00')
but '5363c73a4d61635257805e00' and ObjectId('5363c73a4d61635257805e00') are not the same thing inside MongoDB. You need to search for the right thing:
Book.where(authors: BSON::ObjectId('5363c73a4d61635257805e00'))
You might want to monkey patch a to_bson_id method into various places. Something like this:
class String
def to_bson_id
BSON::ObjectId.from_string(self)
end
end
module Mongoid
module Document
def to_bson_id
id
end
end
end
module BSON
class ObjectId
def to_bson_id
self
end
end
end
class NilClass
def to_bson_id
self
end
end
Should do the trick. Then you can say things like:
Book.where(authors: '5363c73a4d61635257805e00'.to_bson_id)
Book.where(authors: some_string_or_object_id.to_bson_id)
and The Right Thing happens.
You might want to rename authors to author_ids to make its nature a little clearer.

Ruby datamapper associations

I am just learning Ruby and datamapper, I have read the docs about associations from the official DataMapper site, but I still have two problems.
First whenever I add associated object, I can not see it when displaying all objects.
I have test class like:
class Test
include DataMapper::Resource
property :id, Serial
property :name, String
has 1, :phonen, :through => Resource
end
And then phonen class like:
class Phonen
include DataMapper::Resource
property :id, Serial
property :number, String
belongs_to :test
end
Then I am creating those 2 objects
#test = Test.create(
:name => "Name here"
)
#phone = Phonen.create(
:number => "Phone number"
)
#test.phonen = #phone
#test.save
And I want to display them like that (I want to return json)
get '/' do
Test.all.to_json
end
What am I doing wrong? maybe its something with the to_json...
I honestly don't know..
But I have one additional question to this topic, lets say I managed to connect those two classes, if I display JSON will I get Phonen { } or just inside class { }?
I know its probably very easy question, but I can't figure it out. That's why I decided to ask you guys. Thanks for help
Test.all
Is returning an active record association in array form, not a hash, when you try to convert to json it's failing.
You can try:
render json: Test.all
As asked in this question:
Ruby array to JSON and Rails JSON rendering

FactoryGirl.create doesn't save Mongoid relations

I have two classes:
class User
include Mongoid::Document
has_one :preference
attr_accessible :name
field :name, type: String
end
class Preference
include Mongoid::Document
belongs_to :user
attr_accessible :somepref
field :somepref, type: Boolean
end
And I have two factories:
FactoryGirl.define do
factory :user do
preference
name 'John'
end
end
FactoryGirl.define do
factory :preference do
somepref true
end
end
After I create a User both documents are saved in the DB, but Preference document is missing user_id field and so has_one relation doesn't work when I read User from the DB.
I've currently fixed it by adding this piece of code in User factory:
after(:create) do |user|
#user.preference.save! #without this user_id field doesn't get saved
end
Can anyone explain to me why is this happening and is there a better fix?
Mongoid seems to be lacking support here.
When FactoryGirl creates a user, it first has to create the preference for that new user. As the new user does not have an id yet, the preference can't store it either.
In general, when you try create parent & child models in one operation, you need two steps:
create the parent, persist to database so it get's an id.
create the child for the parent and persist it.
Step two would end up in an after(:create) block. Like this:
FactoryGirl.define do
factory :user do
name 'John'
after(:create) do |user|
preference { create(:preference, user: user) }
end
end
end
As stated in this answer:
To ensure that you can always immediately read back the data you just
wrote using Mongoid, you need to set the database session options
consistency: :strong, safe: true
neither of which are the default.

Sequel - Query Many to Many Associations

I am having issues constructing the proper models, associations, and query for the following scenario and then returning results as JSON using Sequel with Ruby.
The database structure___
You can create a list of books. Each library contains books. Defined by the following:
db.create_table(:books) do
primary_key :id
String :name
String :author
DateTime :created
end
db.create_table(:libraries) do
primary_key :id
String :name
String :city
String :state
DateTime :created
end
db.create_table(:libraries_books) do
Integer :library_id
Integer :book_id
primary_key [:library_id, :book_id]
end
class Library < Sequel::Model(:libraries)
many_to_many :libraries_books, :left_key=>:library_id, :right_key=>:book_id, :join_table=>:libraries_books
one_to_many :libraries_books, :key=>:library_id
end
class LibraryBook < Sequel::Model(:libraries_books)
many_to_one :libraries
many_to_one :books
end
I am trying to determine the correct way to access all the book names for a given library. I initially tried to follow the Sequel Associations guide but was not able to figure out how I could use LibraryBook with associations to get all the books for a library and join on the Book model to get the proper columns.
After getting stuck with some of the methods described, I attempted to create my own query as such:
LibraryBook.select(:books.*)
.join_table(:inner, :libraries, :id => :library_id)
.join_table(:inner, :books, :id => :book_id)
.where(:library_id => 1)
Which seems to get me partially there. However, when I use the serialization extension, I get an error when the results are being converted:
undefined method `book_id' for #<LibraryGame:0x007fa9e904b470>
Any insight into that can be provided would be very helpful!
Try the following:
db.create_table(:books) do
primary_key :id
String :name
String :author
DateTime :created
end
db.create_table(:libraries) do
primary_key :id
String :name
String :city
String :state
DateTime :created
end
db.create_table(:books_libraries) do
foreign_key :library_id, :libraries, key: :id
foreign_key :book_id, :books, key: :id, index: true
primary_key [:library_id, :book_id]
end
class Library < Sequel::Model
many_to_many :books
end
class Book < Sequel::Model
many_to_many :libraries
end
Note renaming the libraries_books table to books_libraries and the use of the foreign_key directive for referential integrity. Conventions should allow things to just work.
Library[7].books # returns all books for library '7'
Or alternatively:
Book.where(libraries: Library[7])
Or multiple libraries:
Book.where(libraries: Library.where(id: [3,7,9]))
If sequel is not able to do the inflection for Library/Libraries then you may need to add your own inflection rule, eg:
Sequel.inflections do |inflect|
inflect.irregular 'Library', 'Libraries'
end

User relation to group and company MongoMapper

I have need to create a system where a user can login with a user account where a user is a member of a group like admin or editor. Also a user is a member of a company. in both cases of groups and company's they can have multiple users but user can be only a member of one company and multiple groups.
The relations that i can get out of this is that a group has many users, company has many users, user has one company, user has many groups.
but my problem then how do i create this with ruby and mongoMapper? i have look at the documentation and other sources but did not find a good solution or explanation on how to use or set this up.
If anyone have a better way of doing it also welcome.
these are the current classes i have written.
class User
include MongoMapper::Document
key :username, String
key :password, String
key :name, String
belongs_to :group
belongs_to :company
end
class Group
include MongoMapper::Document
key :group_id, Integer
key :name, String
key :accesLevel, Integer
many :user
end
class Company
include MongoMapper::Document
key :name, String
many :user
end
After some more google searching i have found a solution.
first i made the user class look like this:
class User
include MongoMapper::Document
key :username, String
key :password, String
key :name, String
key :companyID
key :groupID
timestamps!
end
then the group and company class like this:
class Company
include MongoMapper::Document
key :name, String
timestamps!
end
class Group
include MongoMapper::Document
key :name, String
key :accesLevel, Integer
timestamps!
end
With these classes in place i changed my controller to first create a company and then a group these could also be loaded but for the ease of testing it was not necessary to do this so that i didn't need to write the code for this.
company = Company.new
company.name = "comp"
group = Group.new
group.name = "admin"
user = User.new
user.name = "user1"
user.username = "user1"
user.password = "passuser1"
user.groupID = group.id
user.companyID = company.id
db_config = YAML::load(File.open('./dbconfig.yml'))
MongoMapper.connection = Mongo::Connection.new(db_config['hostname'])
MongoMapper.database = db_config['name']
company.save
group.save
user.save

Resources