Mongoid - get a field from document - ruby-on-rails

I have the following model
class MyModel
field :title, type: String
field :url, type: String
end
I would like to make a query that will return only the title field for each document.
How can I do it?

Use only or without:
Queryable#only
Limit the fields that are returned to the provided fields.
Queryable#without
Limit the fields that are returned everything except the provided fields.
So you'd say:
MyModel.query_method_calls.only(:title)
# or
MyModel.query_method_calls.without(:url)
Note that only will make a mess of things if you're using Mongoid3 with the Identity Map enabled, you'll get half-formed objects stuck in the identity map cache and end up wondering where all the strange nil attributes are coming from. If this applies to you then bypass Mongoid and use the underlying Moped interface:
Query#select
Select the fields you want returned.
This approach would look like:
MyModel.collection.find(...).select(:title => 1)
You'd be working with hashes instead of Mongoid objects in this case.

Related

Rails how to query a hash in a where clause

Is there a way with active record sql server adapter to query a hash field? I have a field called properties that is stored like this:
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
summary: There is a problem
optional_information: Help!!
location: '28288'
reported_phone: "(555) 555-0352"
reported_email: person#email.com
affected_person_id: '1234'
affected_person_name: 'Tester'
affected_person_phone: '(555) 555-0352'
affected_person_email: person#email.com
I want to set up a query so that I pass a param through called filter and query either the summary field of the hash. Is this possible?
Trying to do somethings like this:
#requests = current_user.requests.where("properties->>'summary' = ?", '%#{params[:filter]}%') if params[:filter].present?
UPDATE: Per the advice below I changed the model to serialize the field as JSON. I also ran a migration to change the DB field type to JSON. Here is what the field looks like now. Still trying to figure out how to access and query the individual values.
{"summary":"Need bed raised","optional_information":"too low","location":"26679","reported_phone":"(555) 334-1324","reported_email":"mail#mail.com","affected_person_id":"1231232","affected_person_name":"testuserJ ","affected_person_phone":"(555) 334-1324","affected_person_email":"mail#mail.com"}

Rails 4 update_all and set value from another field

I need to do some bulk updates in some models and set value of a field as value of another field.
Right now I can do that with raw sql like this:
ActiveRecord::Base.connection.execute("UPDATE `deleted_contents` SET `deleted_contents`.`original_id` = `deleted_contents`.`id` WHERE `deleted_contents`.`original_id` is NULL")
This is working fine, however I need to do this using ActiveRecord query interface due to many reasons.
I tried:
DeletedContent.where(original_id: nil).update_all(original_id: value_of_id_column)
For value_of_id_column I tried :id, self.id, id, etc, nothing works. What should I set for value_of_id_column to get the original query generated by rails? Is this possible, or using the raw sql is the only solution?
Also I do not want to iterate over each record and update. This is not a valid solution for me:
DeletedContent.where(original_id: nil).each do |deleted_content|
update_each_record
end
I'm pretty sure you cannot obtain that query by passing a hash to update_all.
The closest to what you want to obtain would be:
DeletedContent.where(original_id: nil).update_all("original_id = id")

How to search for a record and then delete it

How to search for the ttoken record by mobile no. and then delete that record?
User.rb:
field :ttokens, type: Hash, :default => {} # Stored as hash over numbers
Normally the value of ttokens, in the rails console, are as follows:
ttokens: {"919839398393"=>{"atoken"=>"f704e803061e594150b09ad8akabfc6105ac85ab", "confirmed"=>true}, "91812798765"=>{"atoken"=>"255cb70926978b93eik67edb52fa23a163587b4b", "confirmed"=>true}}
I need a mongodb query to search for the ttoken record by mobile number and then delete that record. The DB used is MongoDB. Any guidance or help would be highly appreciated.
You need to use MongoDB 'dot notation' for the embedded element, which means the "key" must be a string type of notation. Also apply $exists to match where the key in the has is present and the .unset() method from mongoid:
User.where('_id'=> userId, 'ttokens.919839398393'=> { '$exists' => true }).unset(
'ttokens.919839398393'
)
This is effectively the $unset operator of MongoDB, which removes "keys" from the document by the path specified.
From the sample document this would match and remove the first key, leaving only the other.

Scope and Field as Parameter in Rails

I've added a scope to a Rails model that allows for searching based on a specified parameter field using a range. Here is what it looks like:
scope :upcoming, lambda { |field|
where("to_char(#{field}, 'DDD') BETWEEN :alpha AND :omega",
alpha: Time.now.advance(days: 4).strftime('%j'),
omega: Time.now.advance(days: 8).strftime('%j'),
)
}
Event.upcoming(:registration) # Query all events with registration shortly.
Event.upcoming(:completion) # Query all events with completion shortly.
The above works fine, however in creating I read the Ruby on Rails Guides and found the following:
Putting the variable directly into the conditions string will pass the variable to the database as-is. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
Although the scope is currently never called with a user parameter, I am curious if a way exists of setting the field without using the interpolation in order to better conform with the above recommendation. I've tried using another named parameter, however this will escape the field with quotes (and thus cause it to fail). Any ideas?
I would recommend validating the field parameter against the model's attributes, essentially using the model as a whitelist for values that are allowed to be passed. Something like this:
scope :upcoming, lambda { |field|
if column_names.include? field.to_s
where("to_char(#{field}, 'DDD') BETWEEN :alpha AND :omega",
alpha: Time.now.advance(days: 4).strftime('%j'),
omega: Time.now.advance(days: 8).strftime('%j'),
)
else
# throw some error or return nil
end
}
Okay, reading all the way to the end might help(thanks rubyprince). It looks like you are doing a between query on a field that is storing a date in Oracle. The problem is that to_char is looking for a variable, not a string. And the act of escaping a variable in rails turns it into a string. So, in this particular case you might convert :alpha and :omega into the format of the value stored in field. That way you can escape field in a straightforward manner. Of course there is the issue with Oracle treating dates as Time. I'm guessing that is why you converted to day-of-year for the compare. If you are using the Oracle Enhanced Adaptor you can set
self.emulate_dates_by_column_name = true
to make sure that the field is treated like a date. Then use the to_date function(which takes a string) with :alpha and :omega
scope :upcoming, lambda { |field|
where(":field BETWEEN to_date(:alpha,'yyyy/mm/dd') AND to_date(:omega,'yyyy/mm/dd')",
field: field,
alpha: Time.now.advance(days: 4).strftime('%Y/%m/%d'),
omega: Time.now.advance(days: 8).strftime('%Y/%m/%d'),
)
}
I have no way of testing this so I might be off in the weeds here.
Validating user input as per Jordan is always a good idea.

mongoid update_attributes changing datatypes

Im creating a simple rails app to modify data in an existing mongo database. I'm using mongoid for the interaction and can read/destroy objects just fine.
The problem comes is my mongo document has a 'node' which is a bunch of key value pairs with vary depending on the record. When i load the record like so:
MongoObject.find(BSON::ObjectId('ABC1234567890'))
=> #<MongoObject _id: ABC1234567890, node: {"totallogins"=>11, "id"=>"logIns"}>
I'm using a standard rails form to update the values so the post data looks like:
{"commit"=>"Edit", "utf8"=>"✓", "id"=>"ABC1234567890", "mongo_object"=>{"node"=>{"totallogins"=>"12", "id"=>"logIns"}}
If i then do:
#mongo_object.update_attributes(params[:mongo_object])
This works but changes the datatype of "totallogins" from an int to a string because the post data is a string.
Now active record deals with this itself but i need a solution that will work with mongoid.
Any ideas how i can do this?
Thanks. Unfortunately i can't as the fields for node are totally dynamic so i can't define them. I've come up with the following solution but its a tad ugly:
#mongo_object.node.each do |k,v|
new_value = params[:mongo_object][:node][k.to_sym]
new_value = new_value.to_i if v.class == Fixnum
#mongo_object.node[k] = new_value
end
#mongo_object.save
If you make the node an embedded_document, then you can explicitly set the field types when you declare them.
class Node
include Mongoid::Document
embedded_in :mongo_object
field :totallogins, type: Integer
...
end
http://mongoid.org/docs/documents/ mentions how to deal with types; perhaps make sure your type is an Integer?

Resources