Rails 3 Symbol.gte method - ruby-on-rails-3

I saw the following code example:
Subscription.where(:created_at.gte => t0)`
To me, this seems a little more ruby/rails-like as opposed to:
Subscription.where("created_at > ?", t0)`
However, attempting to reproduce this in my own code on results in:
undefined method `gte' for :created_at:Symbol
I'm not certain, but I believe this is a MongoDB method. If so is there any way I can extend ActiveRecord to make use of it?

You are correct. This is mongoid query DSL.
Similar way to extend ActiveRecord is achieved using squeel gem. However, it is slightly different.
Subscription.where{ created_at.gte => t0 }
Notice different brackets and created_at is not a symbol.

Related

Arel: Left outer join using symbols

I have this use case where I get the symbolized deep associations from a certain model, and I have to perform certain queries that involve using outer joins. How can one do it WITHOUT resorting to write the full SQL by hand?
Answers I don't want:
- using includes (doesn't solve deep associations very well ( .includes(:cars => [:windows, :engine => [:ignition]..... works unexpectedly ) and I don't want its side-effects)
- writing the SQL myself (sorry, it's 2013, cross-db support, etc etc..., and the objects I fetch are read_only, more side-effects)
I'd like to have an Arel solution. I know that using the arel_table's from the models I can construct SQL expressions, there's also a DSL for the joins, but somehow i cannot use it in the joins method from the model:
car = Car.arel_table
engine = Engine.arel_table
eng_exp = car.join(engine).on(car[:engine_id].eq(engine[:id]))
eng_exp.to_sql #=> GOOD! very nice!
Car.joins(eng_exp) #=> Breaks!!
Why this doesn't work is beyond me. I don't know exactly what is missing. But it's the closest thing to a solution I have now. If somebody could help me completing my example or provide me with a nice work-around or tell me when will Rails include such an obviously necessary feature will have my everlasting gratitude.
This is an old question, but for the benefit of anyone finding it through search engines:
If you want something you can pass into .joins, you can either use .create_join and .create_on:
join_on = car.create_on(car[:engine_id].eq(engine[:id]))
eng_join = car.create_join(engine, join_on, Arel::Nodes::OuterJoin)
Car.joins(eng_join)
OR
use the .join_sources from your constructed join object:
eng_exp = car.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.joins(eng_exp.join_sources)
I found a blog post that purports to address this problem: http://blog.donwilson.net/2011/11/constructing-a-less-than-simple-query-with-rails-and-arel/
Based on this (and my own testing), the following should work for your situation:
car = Car.arel_table
engine = Engine.arel_table
sql = car.project(car[Arel.star])
.join(engine, Arel::Nodes::OuterJoin).on(car[:engine_id].eq(engine[:id]))
Car.find_by_sql(sql)
If you don't mind adding a dependency and skipping AREL altogether, you could use Ernie Miller's excellent Squeel gem. It would be something like
Car.joins{engine.outer}.where(...)
This would require that the Car model be associated with Engine like so:
belongs_to :engine

mongoid query - calling the size method produces an error

When I execute this query:
User.where(:comments.size => 10)
I am getting the following error:
undefined method `size' for :comments:Symbol
But according to the documentation here:
http://mongoid.org/docs/querying/criteria.html
This should be possible. So, why the error?
Note: 'comments' is separate collection from User with a 'has_and_belongs_to_many' relationship.
I am using mongoid 3.0.0 and bson_ext 1.6.1
Thanks in advance!
This will work if User embeds Comments but not when you relate User to Comments. It works for embedding because of the $size operator (although, this is not a super efficient query to perform. Better to cache the size in a separate field).
Use with_size, not size, with Mongoid 3. It will translate to the MongoDB $size operator.
Queryable#with_size: Add $size selection. Matches documents who's array field has the exact size of the provided value. This is named with_size not to conflict with Ruby's Enumerable#size or Symbol#size." (from the Origin Selection documentation)

NHibernate QueryOver order by first non-null value (coalescing)

What I'm trying to come up is something that's expressed like this:
var result = Session.QueryOver<Foo>().OrderBy(f => f.UpdatedAt ?? f.CreatedAt);
Sure enough, this doesn't work. Rough equivalent of this in T-SQL is
... order by coalesce(f.UpdatedAt, f.CreatedAt)
What's the kosher way to do "coalescing" in NHibernate QueryOver?
.OrderBy(Projections.SqlFunction("coalesce",
NHibernateUtil.DateTime,
Projections.Property<Foo>(x => x.UpdatedAt),
Projections.Property<Foo>(x => x.CreatedAt)))
Diego's answer is the way I came up with, but it seemed to be too verbose to me, so I asked a question, and got an excellent answer. Basically, you can register your own extensions and then just do
.OrderBy(f => f.UpdatedAt.IfNull(f.CreatedAt));
where IfNull is your new extension. I've even submitted an improvement proposal to NH Jira, but got no response yet.

Rails 3 ActiveRecord query using both SQL IN and SQL OR operators

I'm writing a Rails 3 ActiveRecord query using the "where" syntax, that uses both the SQL IN and the SQL OR operator and can't figure out how to use both of them together.
This code works (in my User model):
Question.where(:user_id => self.friends.ids)
#note: self.friends.ids returns an array of integers
but this code
Question.where(:user_id => self.friends.ids OR :target => self.friends.usernames)
returns this error
syntax error, unexpected tCONSTANT, expecting ')'
...user_id => self.friends.ids OR :target => self.friends.usern...
Any idea how to write this in Rails, or just what the raw SQL query should be?
You don't need to use raw SQL, just provide the pattern as a string, and add named parameters:
Question.where('user_id in (:ids) or target in (:usernames)',
:ids => self.friends.ids, :usernames => self.friends.usernames)
Or positional parameters:
Question.where('user_id in (?) or target in (?)',
self.friends.ids, self.friends.usernames)
You can also use the excellent Squeel gem, as #erroric pointed out on his answer (the my { } block is only needed if you need access to self or instance variables):
Question.where { user_id.in(my { self.friends.ids }) |
target.in(my { self.friends.usernames }) }
Though Rails 3 AR doesn't give you an or operator you can still achieve the same result without going all the way down to SQL and use Arel directly. By that I mean that you can do it like this:
t = Question.arel_table
Question.where(t[:user_id].in(self.friends.ids).or(t[:username].in(self.friends.usernames)))
Some might say it ain't so pretty, some might say it's pretty simply because it includes no SQL. Anyhow it most certainly could be prettier and there's a gem for it too: MetaWhere
For more info see this railscast: http://railscasts.com/episodes/215-advanced-queries-in-rails-3
and MetaWhere site: http://metautonomo.us/projects/metawhere/
UPDATE: Later Ryan Bates has made another railscast about metawhere and metasearch: http://railscasts.com/episodes/251-metawhere-metasearch
Later though Metawhere (and search) have become more or less legacy gems. I.e. they don't even work with Rails 3.1. The author felt they (Metawhere and search) needed drastic rewrite. So much that he actually went for a new gem all together. The successor of Metawhere is Squeel. Read more about the authors announcement here:
http://erniemiller.org/2011/08/31/rails-3-1-and-the-future-of-metawhere-and-metasearch/
and check out the project home page:
http://erniemiller.org/projects/squeel/
"Metasearch 2.0" is called Ransack and you can read something about it from here:
http://erniemiller.org/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/
Alternatively, you could use Squeel. To my eyes, it is simpler. You can accomplish both the IN (>>) and OR (|) operations using the following syntax:
Question.where{(:user_id >> my{friends.id}) | (:target >> my{friends.usernames})}
I generally wrap my conditions in (...) to ensure the appropriate order of operation - both the INs happen before the OR.
The my{...} block executes methods from the self context as defined before the Squeel call - in this case Question. Inside of the Squeel block, self refers to a Squeel object and not the Question object (see the Squeel Readme for more). You get around this by using the my{...} wrapper to restore the original context.
raw SQL
SELECT *
FROM table
WHERE user_id in (LIST OF friend.ids) OR target in (LIST OF friends.usernames)
with each list comma separate. I don't know the Rails ActiveRecord stuff that well. For AND you would just put a comma between those two conditions, but idk about OR

How can I randomize DataMapper collection and convert it to JSON?

I'm pulling my hair out trying to build a little random photo JSON feed using DataMapper/Sinatra. Here's what I have so far..
Photo.favorites.to_json(:methods => [:foo, :bar])
So that works fine. The to_json method is provided in the dm-serializer library. All I want to do is randomize that feed so the photos don't show up in the same order every time. Since DataMapper doesn't have built-in support for random selects, I tried sorting the results, but to_json gets mad because the sort_by turns the DataMapper::Collection into an Array..
Photo.favorites.sort_by{rand}.to_json(:methods => [:foo, :bar])
# wrong argument type Hash (expected Data)
I searched for that error and saw a lot of Rails-related stuff about ActiveRecord and conflicts between competing to_json methods, but nothing really about DataMapper. A lot of people recommended using json_pure instead of the json gem, so I gave that a try by adding require 'json/pure' to my Sinatra app. Now the query above gives me this error instead..
Photo.favorites.sort_by{rand}.to_json(:methods => [:foo, :bar])
# undefined method `[]' for #<JSON::Pure::Generator::State:0x106499880>
I also tried doing the randomization with straight SQL:
def self.random
repository(:default).adapter.query('SELECT * FROM photos WHERE favorite = 1 ORDER BY RAND();')
end
But that doesn't really work for me because it returns Struct objects with attributes, rather than instances of the actual Photo class. This means I can't leverage the handy to_json arguments like :methods.
Lastly I tried using find_by_sql, but I guess the method's been removed from DataMapper?
def self.random
find_by_sql("SELECT * FROM `photos` ORDER BY RAND();")
end
# undefined method `find_by_sql' for Photo:Class
Sheesh! Any thoughts on how to resolve this?
The find_by_sql method was moved to the dm-ar-finders plugin.