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.
Related
I am using mongoid on a rails project.
I have an API call that fetches client records in JSON format (array of hashes).
users = api.get_users # Returns JSON
To leverage Mongo's search, sort, and pagination, I'd like to store the records I get through API in the database.
Of course I could run over every record in the JSON and do something like User.create(user), but I would like to just import all records at once and create each record in the database. Perhaps using https://docs.mongodb.com/manual/reference/program/mongoimport ?
Any suggestions?
You're right -- because your data is in JSON format, mongoimport is the tool you want to use. Once you've imported your data, you can set up Mongoid document schemas to match the data you've imported.
Here's a helpful mongoimport tutorial if you want to try it on some sample data.
For the inventory data in this tutorial, you could set up the schema:
# JSON data: { "item": "journal", "qty": 25, "size": { "h": 14, "w": 21, "uom": "cm" }, "status": "A" }
class Inventory
include Mongoid::Document
store_in collection: 'inventory'
field :item, type: String
field :qty, type: Integer
field :size, type: Hash
field :status, type: String
end
Some things to note here:
Make sure that you import your data to the default collection name for the model you want to create (i.e. import to the "users" collection for a model called User) OR indicate on the model which collection the data is stored in (using the store_in method)
When you import data as a Hash, you have some options on how to represent that in Mongoid. I've just used a Hash in this example, but you could also make size its own embedded document.
There are records in my database with null _id, but mongoid model returns record with filled, nonexistent _id:
MyModel.where('deleted_at' => { '$exists' => false }).first
#=> #<MyModel _id: 581208bcfca2f42fb9c9faa0...
But when I search this model in mongo client:
>db.my_model.find({deleted_at: null})[0]
{
"_id": null,
....
}
When I try to find this record in rails console:
MyModel.where(id: "581208bcfca2f42fb9c9faa0").count
=> 0
Is there any explanation for this case?
The _id field is the primary key for every document. It's called _id and is also accessible via id. From Mongo Code Source you can see the alias internally.
Also you can find this information, here in Documents Fields docs.
Mongo advises using the _id field: BSON and ObjectID docs
I have imported CSV file in Neo4j database. Part of code looks like this:
LOAD CSV WITH HEADERS from "file:///AccountTry.csv" as row
WITH row, split(row.birth_date, '-') as date
CREATE (a:AccountTest {id: toInteger(row.id), account_type: row.account_type, first_name: row.first_name, last_name: row.last_name })
and that works fine. I can see all my nodes and their attributes in neo4j browser.
Then, i created model in rails application:
class AccountTest
include Neo4j::ActiveNode
property :first_name, type: String
property :id, type: Integer
property :last_name, type: String
property :account_type, type: String
end
I made migration and that works fine. When i open rails console and try "AccountTest.first" i get all atributes fine, only property "AccountTest.id: nil".
Why is "id" nil? I have 6 nodes and all of them in rails application have id = nil, but in Neo4j browser all of them have correct ids.
I don't think you need to declare the id property in the AccountTest class. This property is the primary key and is there by default.
As this is an additional property (different form the id generated by Neo4j) I suggest that you rename the property on AccountTest (e.g. account_id) and change the CSV import to load the value into the account_id field.
It seems like Mongoid is now the superior ORM for Mongo based on performance and development activity. Unfortunately, we're on MongoMapper and need to migrate.
Are there any concerns or stumbling blocks we should be aware of? We have found a few outdated articles on Google and tried posting on the Mongoid Google Groups (though we were prohibited), but would love thoughts from SO members who have done this in the past.
We're on Rails 3.2.12.
Thanks!
Both of them are great MongoDB Libraries for Ruby. But if you want to switch, here are some notes:
Migrating MongoMapper ORM to Mongoid ORM - Notes
Configure the database connection.
Replace configuration yaml file(includes replica configuration).
Configure Mongoid specific options. e.g - raise_not_found_error: false. if you don't want an error every time a query returns nothing...
Change all models definations - include MongoMapper::Document to include Mongoid::Document
Change the format for all fields definitions.
In mongoid, you should specipy the timestamp: include Mongoid::Timestamps
Change validation. e.g: :in => ARRAY, will be: validates :name, presence: true, inclusion: { in: ARRAY }
Change indexes.
Change order_by format. e.g: MM: Model.all(:order => 'name'). Mongoid: Model.order_by('name ASC')
Error is a keyword in Mongoid. So if you have a model named Error, you should change it.
Pagination format is different, using another gem.
The primary key index entry in MM is id. In Mongoid it's _id, if you have other code relying on .id in the object JSON, you can override as_json function in your Model to create the JSON structure you want.
In MM, Model.fields(:id, :name) ,limits the fields returned from the database to those supplied to the method. In Mongoid it's Model.only(:name,:id)
Some queries changes:
Selecting objects by array: MM: Model.where(:attr.in => [ ] ) and Model.where(:attr => [ ] ) . Mongoid is only: Model.where(:attr.in => [ ] )
Map option of MM is equivalent to the Mid's pluck. Model.map(&:name) --to-- Model.pluck(:name)
Mongoid doesn't support find query for nil. e.g: value = nil. Model.find(value) will throw an error : "Calling Document .find with nil is invalid". So in mongoid we should do: Model.find(value || "").
In MM: Model.find_or_initialize_by_name("BOB"). In Mongoid Model.find_or_initialize_by(name: "BOB").
MM can be used in those two options: Model.where({:name => 'BOB'}).first, and also Model.first({:name => 'BOB'}). Mongoid has only first option.
In MM, to update multiple objects: Model.set({conditions},attr_to_update). In Mongoid: Model.where(conditions).update_all(attr_to_update).
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.