mongodb many to many without join table - ruby-on-rails

Originally, I specified a relationship where contact has_many services. Therefore, services has a foreign key of contact_id:
class Contact
include Mongoid::Document
field :name, type: String
end
class Service
field :name, type: String
field :contact_id, type: Integer
end
Now there is a possibility to add an additional contact to a service, so service has many contacts. However, the contacts that are added are ones that already exist independently. So I do not want to embed one entity inside another. A contact and service will always live independently. No embedding.
So should I just store the ids of the contacts inside an array of Service? In other words, my new models will look like this:
class Contact
include Mongoid::Document
field :name, type: String
end
class Service
field :name, type: String
field :contact_id, type: Integer
field :contact_ids, type: Array, default: []
end
Or is there a better solution to address the many to many problem here (without embedding one document in another)?

For the Many-To-Many, you don't have 36 options : you actually have 2 :
Array of IDs on One side like you did
Array of IDs on Both sides.
The cool thing with the "both sides" solution is that you can find query documents from both collections to get the links.
Example with books and authors :
db.books.findOne()
{
_id: 1,
title: "The Great Gatsby",
authors: [1, 5]
}
db.authors.findOne()
{
_id: 1,
firstName: "F. Scott",
lastName: "Fitzgerald",
books: [1, 3, 20]
}

Related

update_attributes with included array one to many

How in rails, do you call update_attributes() on a active record object, and have it create appropriate rows in one-to-many associated table based on an array param.
Is this possible? Or do I need to manually loop through that param's array and insert the many rows manually via create_record() etc?
To be clear, I might have users table with 1-to-many addresses. I want to call users.update_attributes() passing in the user details to be updated, but also provide an array of addresses mapping to the addresses table.
In your User model:
model User
has_many :addresses
accepts_nested_attributes_for :addresses
end
now user can be created with this set of params
params = { user: {
name: 'Dimitri', address_attributes: [
{ country: 'Georgia', city: 'Abasha', line: '35 Kacharava Str.' },
{ country: 'USA', city: 'Los Angeles', line: '10 Infinite Loop' },
{ country: '', _destroy: '1' } # this will be ignored
]
}}
User.create(params[:user])
more details can be found from here.

MongoDB creates parent objects when I try to create child objects

This model:
class SimCustomer < Customer
index({ user_id: 1 }, { background: true })
belongs_to :user, :inverse_of => :sim_customers
end
inherits from this model:
class Customer
include Mongoid::Document
include Mongoid::Timestamps
field :mail_address, type: String
end
I create the indexes from my terminal:
bundle exec rake db:mongoid:create_indexes
But this creates indexes on the Customer instead of the SimCustomer:
I, [2014-11-13T16:21:17.210343 #11407] INFO -- : MONGOID: Created indexes on Customer:
I, [2014-11-13T16:21:17.210381 #11407] INFO -- : MONGOID: Index: {:user_id=>1}, Options: {:background=>true}
And when I try to batch insert SimCustomer objects it creates Customer objects instead:
SimCustomer.collection.insert(Array.new << {mail_address: "hello#hello.com", user_id: "54652f5b43687229b4060000"})
# => #<Customer _id: 54654b7b6220ff4f28364ee9, created_at: nil, updated_at: nil, mail_address: "hello#hello.com", _type: "Customer">
How can I fix this?
This sets up Single Collection Inheritance:
class SimCustomer < Customer
That means that both Customer and SimCustomer will be stored in the customers collection inside MongoDB and they'll be differentiated using the _type field.
Specifying an index in SimCustomer:
class SimCustomer < Customer
index({ user_id: 1 }, { background: true })
will create the index on the customers collection because that's where SimCustomers are stored.
The same collection chicanery is causing your problem with your bulk insert. If you look at SimCustomer.collection.name you'll find that it says 'customers' so of course SimCustomer.collection.insert will create new Customers. If you want to create SimCustomers by hand then specify the _type:
SimCustomer.collection.insert(
_type: 'SimCustomer',
mail_address: "hello#hello.com",
user_id: "54652f5b43687229b4060000"
)
Note that I dropped that strange looking Array.new << stuff, I don't know where you learned that from but it is unnecessary when inserting on object and odd looking if you were inserting several, if you want to insert several then just use an array literal:
SimCustomer.collection.insert([
{ ... },
{ ... },
...
])
Your next problem is going to be that string in user_id. That really should be a Moped::BSON::ObjectId or you'll end up with a string inside the database and that will make a mess of your queries. Mongoid may know what type a property should be but neither Moped nor MongoDB will. You'll want to use Moped::BSON::ObjectId('54652f5b43687229b4060000') instead.

How to create model with an array field which contains another documents as an embedded documents in Mongodb (Mongoid)

I am using Rails 4 with Mongoid for an event based application.
I am trying to create a model where I want to add an array field with embedded documents in that array. This embedded documents will contain user's geo coordinate and timestamp. After every 5 minutes I will be pushing user's latest coordinates to user's (location) array. can someone please help me, How can i create that.
My sample model and desired documents are as below.
class User
include Mongoid::Document
field :name, type: String
field :locations, type: Array
end
Here I want to push
Here is sample document that I am looking for as a result:
{ _id : ObjectId(...),
name : "User_name",
locations : [ {
_id : ObjectID(...),
time : "...." ,
loc : [ 55.5, 42.3 ]
} ,
{
_id : ObjectID(...),
time : "...",
loc : [ -74 , 44.74 ]
}
]
}
I was able to add the value in location array without embedded document through IRB, but as I will be using MongoDB's Geospatial queries later on, so I want to use 2D indexes and rest of the stuff Mongo Documentation mentioned.
Hence I believe it needs to have array of documents which contain the latitude & longitude. which will also save my time to code.
Also can I make the time of the location as documents '_id' ? (It can help me to reduce the query overhead)
I would really appriciate if someone can help me with the structure of model i should write or guide me to the references.
P.S: Let me know if you suggest some extra references/help about storing geospatial data in mongoDB which can be helpful for me.
Hope this will help somebody.
If you want to embed documents you can use embedded_many feature of mongoid, which handles such relations. It allows you to define index on embedded documents as well
http://mongoid.org/en/mongoid/docs/relations.html#embeds_many
Mongoid points out, that 2D indexes should be applied to arrays:
http://mongoid.org/en/mongoid/docs/indexing.html
In your case models may look like this:
class User
include Mongoid::Document
field :name, type: String
embeds_many :locations
index({ "locations.loc" => "2d" })
accepts_nested_attributes_for :locations # see http://mongoid.org/en/mongoid/docs/nested_attributes.html#common
end
class Location
include Mongoid::Document
field :time, type: DateTime # see http://mongoid.org/en/mongoid/docs/documents.html#fields
field :loc, type: Array
embedded_in :user
end
But beware of using update and nested attributes - it allows you only update attributes, but not delete or reject them. It's preferrable to use (association)_attributes= methods instead:
#user = User.new({ name: 'John Doe' })
#user.locations_attributes = {
"0" => {
_id : ObjectID(...),
time : "...." ,
loc : [ 55.5, 42.3 ]
} ,
"1" => {
_id : ObjectID(...),
time : "...",
loc : [ -74 , 44.74 ]
}
}
#user.save!

Javascript Nested Object Representation in Rails Model?

This could be a very basic question but forgive my limitless of my knowledge. How would you represent Js nested object (Backbone Model) in Rails Model ?
var UserSchema = new mongoose.Schema({
_id: ShortId,
name :{
first: {type: String},
last: {type: String}
}
});
The Mongoid model would be
class User
include Mongoid::Document
field :first, type: String
field :last, type: String
end
There's the issue of mapping from Mongoid/MongoDB "_id" to Backbone "id". The choices to address this are summarized in several discussions that can be Googled, an example is http://dzello.com/blog/2011/12/24/tame-the-mongoid-id-field-in-your-rails-and-backbone-js-app/
Different people have chosen either of the two solutions, telling Backbone to use "_id" or rewriting #to_json in Mongoid to "id". Either will work, you may want to experiment to see what you prefer.

How can I index a hash field in MongoDB or Mongoid?

I have the following mongo document:
{
_id: 'someid',
name: 'John Doe',
address: {
city: 'Osaka',
country: 'Japan'
}
}
How do I index by city and country?
From the MongoDB documentation:
Indexing on Embedded Fields
You can create indexes on fields embedded in sub-documents, just as you can index top-level fields in documents. [...] Instead, indexes on embedded fields allow you to use a “dot notation,” to introspect into sub-documents.
[...]
db.people.ensureIndex( { "address.zipcode": 1 } )
Mongoid uses the same Dot Notation:
You can define indexes on embedded document fields as well.
class Person
include Mongoid::Document
embeds_many :addresses
index "addresses.street"
end
So you want something like this:
class C
include Mongoid::Document
index 'address.city'
index 'address.country'
#...
end

Resources