Mongoid OR query syntax - ruby-on-rails

This must be asked alot but it is very poorly documented. There is no mention at http://mongoid.org/en/mongoid/docs/querying.html
I'm trying to check whether a user exists (below is an AND query), how can I change it to an OR type query
Username.where(:username=>#username, :email=>#email)
(Either the email or the username must match).
I have found some pretty complicated ways online including sending a direct javascript (from): http://omarqureshi.net/articles/2010-6-17-using-or-in-mongoid
Surely there must be a simple clear syntax to do this correctly?

For the sake of others who end up on this page, the updated syntax is now
Username.or({username: #username}, {email: #email})
Please refer to the updated docs or relevant impl.

Yeah, this used to be documented better. Try this:
Username.any_of({:username => #username},
{:email => #email})

There is a typo in #miguel-savignano's response. According to Stackoverflow the "edit queue is full", which is why I didn't submit an edit.
Proper syntax:
Username.or({username: #username}).or({email: #email})
A more concise solution:
Username.or({username: #username}, {email: #email})
The Mongo selector will resolve to:
{"$or"=>[{"username"=>#username}, {"email"=>#email}]}
I found this question as I was trying to solve for creating "or" queries.
If you are looking to match a string or any one of an array of elements, then you will need to write a Mongoid query with the Mongo '$in' selector.
For example, you have a specific username and an array of emails. You would like to return all results where at least one of the fields matches either the username or one of the emails within the array.
#username = "jess"
#emails = ["hi#mail.com", "test#mail.com", "foo#mail.com"]
User.or({username: ""}, {email: {'$in': #emails}})
The Mongo selector will resolve to:
{"$or"=>[{"first_name"=>""}, {"email"=>{:$in=>["hi#mail.com", "test#mail.com", "foo#mail.com"]}}]}
If you have Users with 2 of the 3 emails in your database, then the selector will return a count of 2.
If you have a User with the username "jess" and 3 additional Users each with one of the given emails, then the selector will return a count of 4.

Also, in Mongoid 5.0, if you still want to use the where method, use the following
Username.where('$or' => [ { username: #username }, { email: #email } ])
this is very useful when you are building a kind of query hash in a dynamic way and you need to keep the where method

In Mongoid 5.0, this works for me
Username.or({username: #username}).or({email: #email})

Related

Rails ActiveRecord update_all array of ids

I have an ActiveRecord Model called Animal.
Animal has id and client_id.
In my app have an array called #selectedanimals that contains the id's of the animals I want to update such as: #selectedanimals: ["6", "14", "5"].
I have the value of a new client_id for these animals like #newclient.id and I want to update all of these Animal records with the new client_id.
What I have now is:
Animal.update_all({:client_id => #transferclient.id}, {:id => #selecteanimals})
I know this is not 100% correct because it is having a problem with :id.
I get an error like this:
Called id for nil, which would mistakenly be 8 -- if you really wanted the id of nil, use object_id
Forgive my ignorance but this is my first time using update_all and I don't see any examples where you pass it an array of the ids of the records you want to update so any help would be appreciated much.
EDIT:
Apparently the #transferclient.id was not properly defined. That was my problem.
Thanks all.
I am new to Ruby so I don't know if there's been some recent change in the API but Danpe's answer as well as the initial suggestion that actually worked according to the asker, didn't work for me. They both raised a syntax error.
The solution that worked for me and is actually proposed in the API documentation is this:
Animal.where(:id => #selectedanimals).update_all(:client_id => #transferclient.id)
You need to check if id is inside that array:
Animal.update_all({:client_id => #transferclient.id}, {"id IN (?)" , #selecteanimals})

Search hash results for similar matches rails 3

I'm using the Facebook API to pull a list of all of my friends. However, I don't want to store the results in the database. I just want to take the hash of results that I get back and perform selects on the results.
However, I'm having a hard time doing that...
I get results like this...
[{"name"=>"John Smith", "id"=>"12345"}, {"name"=>"Jane Doe", "id"=>"23456"}, {"name"=>"Samuel Jackson", "id"=>"34567"}, {"name"=>"Kate Upton", "id"=>"45678"}]
They're stored in #friends
My select is:
#friends.select{|key, hash| hash["name"] == "John Smith" }
but I keep getting an error "undefined method `[]' for nil:NilClass"
Two questions:
1. How can I get the search of the hash to return John Smith (without storing in a database, and without modifying the initial Facebook API query)
2. Is it possible to use "Like" rather than "==" so that it returns results containing the word?
Thank you!
You are getting "undefined method [] for nil:NilClass" because you are using the method select wrongly. Try doing this:
#friends.select{ |hash| hash["name"] == "John Smith" }
So I believe this answers your first question. You don't need to store anything in the database, but in the instance variable #friends, and use select correctly.
Regarding the second question, I believe you should look into regular expressions, as it really depends on what do you mean by "like", but if you say that you want:
it returns results containing the word?
Well, if you want to check for a specific word (let's say Smith), you would do the select as:
#friends.select{ |hash| hash["name"].include? "Smith" }
Which would give you all the elements on the array which name includes the word Smith.

Rails: Getting column value from query

Seems like it should be able to look at a simple tutorial or find an aswer with a quick google, but I can't...
codes = PartnerCode.find_by_sql "SELECT * from partner_codes where product = 'SPANMEX' and isused = 'false' limit 1"
I want the column named code, I want just the value. Tried everything what that seems logical. Driving me nuts because everything I find shows an example without referencing the actual values returned
So what is the object returned? Array, hash, ActiveRecord? Thanks in advance.
For Rails 4+ (and a bit earlier I think), use pluck:
Partner.where(conditions).pluck :code
> ["code1", "code2", "code3"]
map is inefficient as it will select all columns first and also won't be able to optimise the query.
You need this one
Partner.where( conditions ).map(&:code)
is shorthand for
Partner.where( conditions ).map{|p| p.code}
PS
if you are often run into such case you will like this gem valium by ernie
it gives you pretty way to get values without instantiating activerecord object like
Partner.where( conditions ).value_of :code
UPDATED:
if you need access some attribute and after that update record
save instance first in some variable:
instance=Partner.where( conditions ).first
then you may access attributes like instance.code and update some attribute
instance.update_attribute || instance.update_attributes
check documentation at api.rubyonrails.org for details

Nested model error messages

I am using Ruby on Rails 3.0.9 and I am trying to validate a nested model. Supposing that I run validation for the "main" model and that generates some errors for the nested model I get the following:
#user.valid?
#user.errors.inspect
# => {:"account.firstname"=>["is too short", "can not be blank"], :"account.lastname"=>["is too short", "can not be blank"], :account=>["is invalid"]}
How you can see the RoR framework creates an errors hash having following keys: account.firstname, account.lastname, account. Since I would like to display error messages on the front-end content by handling those error key\value pairs with JavaScript (BTW: I use jQuery) that involves CSS properties I thought to "prepare" that data and to change those keys to account_firstname, account_lastname, account (note: I substitute the . with the _ character).
How can I change key values from, for example, account.firstname to account_firstname?
And, mostly important, how I should handle this situation? Is what I am trying to do a "good" way to handle nested model errors? If no, what is the common\best approach to do that?
I've made a quick Concern which shows full error messages for nested models:
https://gist.github.com/4710856
#1.9.3-p362 :008 > s.all_full_error_messages
# => ["Purchaser can't be blank", "Consumer email can't be blank", "Consumer email is invalid", "Consumer full name can't be blank"]
Some creative patching of the Rails errors hash will let you achieve your aim. Create an initializer in config/initalizers, let call it errors_hash_patch.rb and put the following in it:
ActiveModel::Errors.class_eval do
def [](attribute)
attribute = attribute.to_sym
dotted_attribute = attribute.to_s.gsub("_", ".").to_sym
attribute_result = get(attribute)
dotted_attribute_result = get(dotted_attribute)
if attribute_result
attribute_result
elsif dotted_attribute_result
dotted_attribute_result
else
set(attribute, [])
end
end
end
All you're doing in here is simply overriding the accessor method [] to try a little harder. More specifically, if the key you're looking for has underscores, it will try to look it up as is, but if it can't find anything it will also replace all the underscores with dots and try to look that up as well. Other than that the behaviour is the same as the regular [] method. For example, let's say you have an errors hash like the one from your example:
errors = {:"account.firstname"=>["is too short", "can not be blank"], :"account.lastname"=>["is too short", "can not be blank"], :account=>["is invalid"]}
Here are some of the ways you can access it and the results that come back:
errors[:account] => ["is invalid"]
errors[:"account.lastname"] => ["is too short", "can not be blank"]
errors[:account_lastname] => ["is too short", "can not be blank"]
errors[:blah] => []
We don't change the way the keys are stored in the errors hash, so we won't accidentally break libraries and behaviours that may rely on the format of the the hash. All we're doing is being a little smarter regarding how we access the data in the hash. Of course, if you DO want to change the data in the hash, the pattern is the same you will just need to override the []= method, and every time rails tries to store keys with dots in them, just change the dots to underscores.
As to your second question, even though I have shown you how to do what you're asking, in general it is best to try and comply with the way rails tries to do things, rather than trying to bend rails to your will. In your case, if you want to display the error messages via javascript, presumably your javascript will have access to a hash of error data, so why not tweak this data with javascript to be in the format that you need it to be. Alternatively you may clone the error data inside a controller and tweak it there (before your javascript ever has access to it). It is difficult to give advice without knowing more about your situation (how are you writing your forms, what exactly is your validation JS trying to do etc.), but those are some general guidelines.
I had the same problem with AngularJs, so I decided to overwrite the as_json method for the ActiveModel::Errors class in an initializer called active_model_errors.rb so that it can replace . for _
Here is the initializer code:
module ActiveModel
class Errors
def as_json(options=nil)
hash = {}
to_hash(options && options[:full_messages]).each{ |k,v| hash[k.to_s.sub('.', '_')] = messages[k] }
hash
end
end
end
I hope it can be helpful for someone
I'm not sure but I think you can't change that behavior without pain. But you could give a try to solutions like http://bcardarella.com/post/4211204716/client-side-validations-3-0

Parse a string as if it were a querystring in Ruby on Rails

I have a string like this:
"foo=bar&bar=foo&hello=hi"
Does Ruby on Rails provide methods to parse this as if it is a querystring, so I get a hash like this:
{
:foo => "bar",
:bar => "foo",
:hello => "hi"
}
Or must I write it myself?
EDIT
Please note that the string above is not a real querystring from a URL, but rather a string stored in a cookie from Facebook Connect.
The answer depends on the version of Rails that you are using. If you are using 2.3 or later, use Rack's builtin parser for params
Rack::Utils.parse_nested_query("a=2") #=> {"a" => "2"}
If you are on older Rails, you can indeed use CGI::parse. Note that handling of hashes and arrays differs in subtle ways between modules so you need to verify whether the data you are getting is correct for the method you choose.
You can also include Rack::Utils into your class for shorthand access.
The
CGI::parse("foo=bar&bar=foo&hello=hi")
Gives you
{"foo"=>["bar"], "hello"=>["hi"], "bar"=>["foo"]}
Edit:
As specified by Ryan Long this version accounts for multiple values of the same key, which is useful if you want to parse arrays too.
Edit 2:
As Ben points out, this may not handle arrays well when they are formatted with ruby on rails style array notation.
The rails style array notation is: foo[]=bar&foo[]=nop. That style is indeed handled correctly with Julik's response.
This version will only parse arrays correctly, if you have the params like foo=bar&foo=nop.
Edit : as said in the comments, symolizing keys can bring your server down if someone want to hurt you. I still do it a lot when I work on low profile apps because it makes things easier to work with but I wouldn't do it anymore for high stake apps
Do not forget to symbolize the keys for obtaining the result you want
Rack::Utils.parse_nested_query("a=2&b=tralalala").deep_symbolize_keys
this operation is destructive for duplicates.
If you talking about the Urls that is being used to get data about the parameters them
> request.url
=> "http://localhost:3000/restaurants/lokesh-dhaba?data=some&more=thisIsMore"
Then to get the query parameters. use
> request.query_parameters
=> {"data"=>"some", "more"=>"thisIsMore"}
If you want a hash you can use
Hash[CGI::parse(x).map{|k,v| [k, v.first]}]

Resources