Map/Reduce on Scoped collection with Rails and Mongoid - ruby-on-rails

I am trying to map/reduce a scope in a Rails 3.1 app using MongoDB with Mongoid.
The results seem odd, so I wonder if map_reduce can be applied on a prescoped collection for example like that:
current_user.tasks.for_year_and_month(year, month).collection.map_reduce(map, reduce, :out => "res")
for_year_and_month scopes tasks on a given month, but the results from map_reduce seem to include other tasks too. Now I wonder whether my map/reduce functions are wrong, or map/reduce can not be applied on pre-scoped collections.
If so, I had to do all the scope's work in my emit function what would make things even worse. I can't believe that.
Can anyone enlighten me?
Regards
Felix

The scope is ignored because you're working directly on collection.
You can pass the :query option to map_reduce and this will filter documents.
Eg:
Task.collection.map_reduce(map,reduce,out:{merge:'res'},query:{user_id: x, ...})

This is what you're after:
results = Task.collection.map_reduce(map, reduce, {query: {month: month, year: year}, out: "reduced_task_results"})
filtered_results = results.find({})

You can use the scope selector from Mongoid in the query option:
scoped = current_user.tasks.whatever
Task.map_reduce( ..., :query => scoped.selector)

Related

Rails: uniq vs. distinct

Can someone briefly explain to me the difference in use between the methods uniq and distinct?
I've seen both used in similar context, but the difference isnt quite clear to me.
Rails queries acts like arrays, thus .uniq produces the same result as .distinct, but
.distinct is sql query method
.uniq is array method
Note: In Rails 5+ Relation#uniq is deprecated and recommended to use Relation#distinct instead.
See http://edgeguides.rubyonrails.org/5_0_release_notes.html#active-record-deprecations
Hint:
Using .includes before calling .uniq/.distinct can slow or speed up your app, because
uniq won't spawn additional sql query
distinct will do
But both results will be the same
Example:
users = User.includes(:posts)
puts users
# First sql query for includes
users.uniq
# No sql query! (here you speed up you app)
users.distinct
# Second distinct sql query! (here you slow down your app)
This can be useful to make performant application
Hint:
Same works for
.size vs .count;
present? vs .exists?
map vs pluck
Rails 5.1 has removed the uniq method from Activerecord Relation and added distinct method...
If you use uniq with query it will just convert the Activerecord Relaction to Array class...
You can not have Query chain if you added uniq there....(i.e you can not do User.active.uniq.subscribed it will throw error undefined method subscribed for Array )
If your DB is large and you want to fetch only required distinct entries its good to use distinct method with Activerecord Relation query...
From the documentation:
uniq(value = true)
Alias for ActiveRecord::QueryMethods#distinct
Its not exactly answer your question, but what I know is:
If we consider ActiveRecord context then uniq is just an alias for distinct. And both work as removing duplicates on query result set(which you can say up to one level).
And at array context uniq is so powerful that it removes duplicates even if the elements are nested. for example
arr = [["first"], ["second"], ["first"]]
and if we do
arr.uniq
answer will be : [["first"], ["second"]]
So even if elements are blocks it will go in deep and removes duplicates.
Hope it helps you in some ways.

What is the fastest way to find the first record in Rails (ActiveRecord)?

I would like to know which method is fastest for return a record.
Class.where(:type => 4).first
Class.find(:first, :conditions => ["type = ?", 4])
Is the execution exactly the same?
The most performant, assuming you don't care about the order, is to use find_by:
Class.find_by(type: 4)
From https://github.com/rubocop-hq/rails-style-guide/issues/76
This method has been added on Rails 4 and it is defined like this:
def find_by(*args)
where(*args).take
end
So, take differs from first in regards to the order of your records. first will return the first record according to the order of the primary key while take will just return whatever the database spits out first.
So while using where().take is equivalent to find_by and choosing whether to use one of the other is a matter of taste, where().first differs from find_by in a subtle and not so obvious way.
Both would produce the same query.
According to the latest information, under Rails 3.1, passing in :conditions will be deprecated.
Hence, right now, the best way to execute the query is to use:
Class.where(:type => 4).first

Doctrine: Multiple (whereIn OR whereIn) query?

I'm having trouble crafting a fairly simple query with Doctrine...
I have two arrays ($countries, $cities) and I need to check whether database record values would match any inside either. I'm looking for something like:
->whereIn('country', 'city', $countries, $cities)
... with 'country' being a WHERE IN for $countries and 'city' being a WHERE IN for $city.
I could separate the two out but the needed query has lots of other conditions so that's not possible. The resulting SQL I'm after would be:
SELECT ...
WHERE ...
AND ...
AND ...
AND ('country' IN (1,2,3) OR 'city' IN (7,8,9))
AND ...
AND ...;
One could therefore think of it also as a bracketing issue only. Anyone know if this is possible with Doctrine DQL? I've looked through the documentation but can't find any direction.
Thanks
After an hour of experimenting on this nonsense, here's the syntax to make it work.
$q->andWhere('country IN ? OR city IN ?', array(array(1, 2, 3), array(7, 8, 9)));
Why not use something like?
$countryIds=[1,2,3];
$cityIds=[7,8,9];
$q->whereIn('country',$countryIds)->andWhereIn('city',$cityIds);
Also, chain them together for context (most Doctrine methods return $this).
see http://www.symfony-project.org/doctrine/1_2/en/06-Working-With-Data

Is there a way to make ActiveRecord write 'WHERE (a,b) in ((1,2),(3,4))' using AR finders

Is there a way to make ActiveRecord write 'WHERE (a,b) in ((1,2),(3,4))' using AR finders.
I would think
Widget.find(:all, :conditions => ['(a,b) in (?)', [[1,2][3,4]]])
but the inner arrays get converted to YAML?!?! Right now I'm using find_by_sql. Is there a better way to write this?
You can do this:
Widget.all(:conditions => ["(a,b) in ((?),(?))", [1,2], [3,4]])
Although that's not ideal if you have a variable number of values. Sounds like a good opportunity for a patch to ActiveRecord!
Update: a kludge for a variable number of values. Still better than doing find_by_sql...but you're right, it should be supported by AR natively.
values = [[1,2],[3,4]]
Widget.all(:conditions => ["(a,b) in (#{Array.new(values.length,'(?)').join(',')})", *values])
Depending on how dynamic you need this to be, you could always do it in a string, like this:
Widget.all(:conditions => ["(a,b) in ((1,2), (3,4))"])
This at least saves you from doing the find_by_sql call. Could you also use an OR operator to split up the two arrays?

How can I combine record results in Rails?

So I have two separate queries:
tagged_items = Item.tagged_with(params[:s], :on => :tags)
searched_items = Item.find(:all, :conditions => ["MATCH(title) AGAINST (? IN BOOLEAN MODE)", "*#{params[:s]}*"])
The first tagged_items is using the acts_as_taggable_on plugin to find all the items tagged with XYZ.
The second, searched_items, is used to search the items table for the search term.
So, how could I combine (and avoid duplicates) the results of these two?
Check out named_scope. The second query can be converted to named_scope easily, I'm not sure about the first one, but if you can rewrite it using find, you're home.
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html
items = (tagged_items + searched_items).unique
But it would be much better if you could fetch them with single query.
This approach...
#items = tagged_items | searched_items
...would make more sense if you're looking to use the results of these queries in a View, instead of working with an Array, and accomplishes the de-duplication as well.

Resources