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.
Related
How to make searchkick search integer fields?
Let's say i have a Book model with three properties namely name:string, author:string and pages:integer.
I want to search according to pages field. Right now if i use a query like below it works for string fields i.e name and author but it doesnt work for pages field which is of integer type.
Book.search(q,
misspellings: { below: 5 },
fields: [:name, :author, :pages],
order: { name: 'asc' },
page: params[:page],
per_page: 20)
I go to console and just searched Book.search(120, fields: [:pages]) and it returns empty result even though there are records with pages 120. Why is searchkick not searching for integer fields? I appreciate any help to this dilemma i am facing. Thanks!
I fixed it with this
In Book model
def search_data
{
name: name,
author: author,
pages: pages.to_s
}
end
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]
}
I want to delete a field in a document using ROR.
I have already tried
book.remove_attribute(:name)
book.unset(:name)
But they both set the attribute to nil and it is still present in the object.
I want it to vanish from my document. Any help is welcome.
When you access a document via mongoid, it returns you a Ruby object. You can actually see the data stored in the document only via mongo shell (just type 'mongo' in you terminal).
The object is created by Mongoid (MongoDB ODM/wrapper for rails). This object may occasionally look different from the document.
For example
When you unset a field, that field is entirely removed from that document. BUT, since your model still has that field on it, MONGOID returns you a nil attribute for that field, instead of giving you different number of fields for objects of same model.
Model book.rb
class Book
include Mongoid::Document
field :name
field :author
end
In rails console, type
Book.create(name: "b1", author: "a1")
=> #<Book _id: 555231746c617a1c99030000, name: "b1", author: "a1">
In Mongo shell
db.books.find()
{ "_id" : ObjectId("555231746c617a1c99030000"), "name" : "b1", "author" : "a1" }
Now, we unset.
In rails console
Book.first.unset(:name)
=> #<Book _id: 555231746c617a1c99030000, name: nil, author: "a1">
In Mongo shell
db.books.find()
{ "_id" : ObjectId("555231746c617a1c99030000"), "author" : "a1" }
If however you still dont want to see the field in your rails console (mind you, this is not taking up any extra space in db) you can always remove the field from the model. If you do that, you will no longer be able to access this field through rails/mongoid on any object. It will only be present on the document and accessible through mongo shell.
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!
I would like to know if it is possible to get the types (as known by AR - eg in the migration script and database) programmatically (I know the data exists in there somewhere).
For example, I can deal with all the attribute names:
ar.attribute_names.each { |name| puts name }
.attributes just returns a mapping of the names to their current values (eg no type info if the field isn't set).
Some places I have seen it with the type information:
in script/console, type the name of an AR entity:
>> Driver
=> Driver(id: integer, name: string, created_at: datetime, updated_at: datetime)
So clearly it knows the types. Also, there is .column_for_attribute, which takes an attr name and returns a column object - which has the type buried in the underlying database column object, but it doesn't appear to be a clean way to get it.
I would also be interested in if there is a way that is friendly for the new "ActiveModel" that is coming (rails3) and is decoupled from database specifics (but perhaps type info will not be part of it, I can't seem to find out if it is).
Thanks.
In Rails 3, for your model "Driver", you want Driver.columns_hash.
Driver.columns_hash["name"].type #returns :string
If you want to iterate through them, you'd do something like this:
Driver.columns_hash.each {|k,v| puts "#{k} => #{v.type}"}
which will output the following:
id => integer
name => string
created_at => datetime
updated_at => datetime
In Rails 5, you can do this independently of the Database. That's important if you use the new Attributes API to define (additional) attributes.
Getting all attributes from a model class:
pry> User.attribute_names
=> ["id",
"firstname",
"lastname",
"created_at",
"updated_at",
"email",...
Getting the type:
pry> User.type_for_attribute('email')
=> #<ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString:0x007ffbab107698
#limit=255,
#precision=nil,
#scale=nil>
That's sometimes more information than needed. There's a convenience function that maps all these types down to a core set (:integer, :string etc.)
> User.type_for_attribute('email').type
=> :string
You can also get all that data in one call with attribute_types which returns a 'name': type hash.
You can access the types of the columns by doing this:
#script/console
Driver.columns.each {|c| puts c.type}
If you want to get a list of all column types in a particular Model, you could do:
Driver.columns.map(&:type) #gets them all
Driver.columns.map(&:type).uniq #gets the unique ones
In rails 5 this will give you a list of all field names along with their data type:
Model_Name.attribute_names.each do |k| puts "#{k} = #{Model_Name.type_for_attribute(k).type}" end
Rails 5+ (works with virtual attributes as well):
Model.attribute_types['some_attribute'].type
This snippet will give you all the attributes of a model with the associated database data types in a hash. Just replace Post with your Active Record Model.
Post.attribute_names.map {|n| [n.to_sym,Post.type_for_attribute(n).type]}.to_h
Will return a hash like this.
=> {:id=>:integer, :title=>:string, :body=>:text, :created_at=>:datetime, :updated_at=>:datetime, :topic_id=>:integer, :user_id=>:integer}
Assuming Foobar is your Active Record model. You can also do:
attributes = Foobar.attribute_names.each_with_object({}) do |attribute_name, hash|
hash[attribute_name.to_sym] = Foobar.type_for_attribute(attribute_name).type
end
Works on Rails 4 too
In Rails 4 You would use Model.column_types.