I'm using RubyMoney (money-rails) https://github.com/RubyMoney/money-rails with Mongoid. Something like this:
class Order
include Mongoid::Document
field :order_amount, :type => Money, :default => Money.new(0)
...
end
How can I aggregate on a Money field? For example, when I run Order.sum(:order_amount), I get 0 even if the field has a value like #<Money fractional:5500 currency:USD>. I've also tried Order.sum(:order_amount_cents) and that also returns 0.
This is the generated Ruby MongoDB command:
command={:aggregate=>"orders", :pipeline=>[{"$match"=>{"order_amount"=>{"$nin"=>[nil]}}}, {"$group"=>{"_id"=>"order_amount", "count"=>{"$sum"=>1}, "max"=>{"$max"=>"$order_amount"}, "min"=>{"$min"=>"$order_amount"}, "sum"=>{"$sum"=>"$order_amount"}, "avg"=>{"$avg"=>"$order_amount"}}}]} (2.4292ms)
I've also tried using the moped aggregate command with Order.collection.aggregate and that doesn't seem to work either.
Order.collection.aggregate(
{
'$group' => {
'_id' => '$order_amount',
'totalOrder' => { '$sum' => '$order_amount' }
}
}
)
Any help please?
I just addded an answer to this here but just in case someone else comes to this post as I did, I was able to sum money-rails values using something like this:
Order.sum("order_amount.cents")
This will return just the cents values from the Money-rails structure:
{ cents: < value >, currency_iso: < value > }
So in order to get the sum in money-rails format you would use:
Money.new(Order.sum("order_amount.cents"))
Please note that this is when using mongoid v3, because of how money-rails is serialized.
I believe that with previous versions of mongoid, money-rails objects were serialized using "fractional" instead of cents, so you would have to use:
Money.new(Order.sum("order_amount.fractional"))
Hope that helps!
Related
This query is not working, pease help. I'm trying to include a second and a third deep-level of association.
Pedido > has_one(products_pedido) > has_one(product_size)
#pedidos = Pedido.includes(:pedidos_payments, :products_pedidos => { :product_size } , :estado, :brand, :customer ).where(:is_quote => false)
Ps: I know products_pedido is mispelled according to ActiveRecord good practices :).
Without a stacktrace here's what I suggest:
Assuming your has_one method name is products_pedidos, your issue looks like a problem with your hash syntax.
Your syntax creates a hash with key products_pedidos that returns a hash without a value. This is probably where the error is occurring.
#pedidos = Pedido.includes(:products_pedidos => { :product_size })
What you likely want is this which returns a hash with key products_pedidos with value product_size
#pedidos = Pedido.includes({products_pedidos: :product_size })
The Entire query might look like:
#pedidos = Pedido.includes(
:pedidos_payments,
{products_pedidos :product_size},
:estado,
:brand,
:customer
).where(is_quote: false)
Here's a great post explaining a bit more about ActiveRecord nested relationship loading: Rails - Nested includes on Active Records?. I'd also suggest fixing the naming on products_pedido to follow good naming practices.
I'm using the elasticsearch-rails gem and the elasticsearch-model gem and writing a query that happens to be really huge just because of the way the gem accepts queries.
The query itself isn't very long, but it's the filters that are very, very long, and I need to pass variables in to filter out the results correctly. Here is an example:
def search_for(input, question_id, tag_id)
query = {
:query => {
:filtered => {
:query => {
:match => {
:content => input
}
},
:filter => {
:bool => {
:must => [
{
# another nested bool with should
},
{
# another nested bool with must for question_id
},
{
# another nested bool with must for tag_id
}
]
}
}
}
}
}
User.search(query) # provided by elasticsearch-model gem
end
For brevity's sake, I've omitted the other nested bools, but as you can imagine, this can get quite long quite fast.
Does anyone have any ideas on how to store this? I was thinking of a yml file, but it seems wrong especially because I need to pass in question_id and tag_id. Any other ideas?
If anyone is familiar with those gems and knows whether the gem's search method accepts other formats, I'd like to know that, too. Looks to me that it just wants something that can turn into a hash.
I think using a method is fine. I would separate the searching from the query:
def query_for(input, question_id, tag_id)
query = {
:query => {
...
end
search query_for(input, question_id, tag_id)
Also, I see that this search functionality is in the User model, but I wonder if it is belongs there. Would it make more sense to have a Search or Query model?
I have a model Event that is connected to MongoDB using Mongoid:
class Event
include Mongoid::Document
include Mongoid::Timestamps
field :user_name, type: String
field :action, type: String
field :ip_address, type: String
scope :recent, -> { where(:created_at.gte => 1.month.ago) }
end
Usually when I use ActiveRecord, I can do something like this to group results:
#action_counts = Event.group('action').where(:user_name =>"my_name").recent.count
And I get results with the following format:
{"action_1"=>46, "action_2"=>36, "action_3"=>41, "action_4"=>40, "action_5"=>37}
What is the best way to do the same thing with Mongoid?
Thanks in advance
I think you'll have to use map/reduce to do that. Look at this SO question for more details:
Mongoid Group By or MongoDb group by in rails
Otherwise, you can simply use the group_by method from Enumerable. Less efficient, but it should do the trick unless you have hundreds of thousands documents.
EDIT: Example of using map/reduce in this case
I'm not really familiar with it but by reading the docs and playing around I couldn't reproduce the exact same hash you want but try this:
def self.count_and_group_by_action
map = %Q{
function() {
key = this.action;
value = {count: 1};
emit(key, value);
# emit a new document {"_id" => "action", "value" => {count: 1}}
# for each input document our scope is applied to
}
}
# the idea now is to "flatten" the emitted documents that
# have the same key. Good, but we need to do something with the values
reduce = %Q{
function(key, values) {
var reducedValue = {count: 0};
# we prepare a reducedValue
# we then loop through the values associated to the same key,
# in this case, the 'action' name
values.forEach(function(value) {
reducedValue.count += value.count; # we increment the reducedValue - thx captain obvious
});
# and return the 'reduced' value for that key,
# an 'aggregate' of all the values associated to the same key
return reducedValue;
}
}
self.map_reduce(map, reduce).out(inline: true)
# we apply the map_reduce functions
# inline: true is because we don't need to store the results in a collection
# we just need a hash
end
So when you call:
Event.where(:user_name =>"my_name").recent.count_and_group_by_action
It should return something like:
[{ "_id" => "action1", "value" => { "count" => 20 }}, { "_id" => "action2" , "value" => { "count" => 10 }}]
Disclaimer: I'm no mongodb nor mongoid specialist, I've based my example on what I could find in the referenced SO question and Mongodb/Mongoid documentation online, any suggestion to make this better would be appreciated.
Resources:
http://docs.mongodb.org/manual/core/map-reduce/
http://mongoid.org/en/mongoid/docs/querying.html#map_reduce
Mongoid Group By or MongoDb group by in rails
I have to add more currencies to the app like on this web app http://www.designconnected.com/
as you can see, it converts the price in whatever currency you select and keep it this way.
I've been looking for gems that are out of date already, tutorials couldn't find any and in stackoverflow there are a few questions about it but none of them got what I need.
if any of you know a better gem, recently released... please let me know.
or if there is no gem for it, should I add a currency_id to current_user so the app will show the proper currency for this user.. but then where do I take the currency rates from.. I've been looking for a solution for 3 days now and nothing.
Thank you for any given advice..
this urls have been checked:
https://stackoverflow.com/questions/1368010/rails-currency-gem-or-plugin
Rails 3 - Multiple Currencies
https://github.com/RubyMoney/money
the last one in combination with https://github.com/RubyMoney/google_currency looks like it's the one I need.. but now would be the right time to get a tutorial on how to use this.
Please help with some ideas on how to get it started if there is no way to find/get a full tutorial about this.
Thank you.
https://github.com/RubyMoney/money-rails and https://github.com/RubyMoney/google_currency is the way to go. It's not what I asked or in the question, but anyway it's the closest answer I have right now. These are a few steps I did to get this working:
In gem file:
gem "json" #if you don't have it
gem "money"
gem "google_currency"
Create a file money.rb in config/initializers
require 'money'
require 'money/bank/google_currency'
require 'json'
MultiJson.engine = :json_gem
Money.default_bank = Money::Bank::GoogleCurrency.new
In product.rb (or whatever model you have that needs the price to be converted)
composed_of :price,
:class_name => "Money",
:mapping => [%w(price price), %w(currency currency_as_string)],
:constructor => Proc.new { |price, currency| Money.new(price || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
And in the view file:
<%= number_to_currency(product.price.exchange_to(:EUR)) %>
For exemple, I have an IT locale (Italian language) - Italy currency now is Euro:
You'll have the prices converted in EUR.. It worked for me really nice, money gem convert the price from USD in EUR using Google_currency, and the locale yml file changes the Currency for this locale so you'll have the price looking like XXX,XX EUR and not $XXX,XX.
To display the right currency for each locale you need to add:
it:
number:
currency:
format:
format: "%n %u"
unit: "EUR"
In it.yml file or for other language you have with currency for that country.
You probably don't need a gem for this. You can make a call directly to google's currency API at any point in your code using the URL's explained here. This could be done in your model or via AJAX directly in the view.
http://motyar.blogspot.com/2011/12/googles-currency-converter-and-json-api.html
I want my rails app to accept dates for a date field in the format dd/mm/yyyy.
In my model I have tried to convert the date to the American standard which I think the Date.parse method that Rails will call on it is expecting:
before_validation :check_due_at_format
def check_due_at_format
self.due_at = Date.strptime(self.due_at,"%d/%m/%Y").to_time
end
However, this returns:
TypeError in OrdersController#update
can't dup NilClass
If it is useful to know, the Items form fields are a nested for within Orders, and Orders are set to:
accepts_nested_attributes_for :items, :reject_if => lambda { |a| a[:quantity].blank? && a[:due_at].blank? }, :allow_destroy => :true
So the items are being validated and saved/updated on #order.save/#order.update_attributes
Thank you!
It may be just a case of the due_at value being nil. In your case it's an empty string, but ignored because of the :reject_if option on accepts_nested_attributes_for, and so it remains as nil.
>> Date.strptime(nil, "%d/%m/%Y")
TypeError: can't dup NilClass
Take care of it with some conditional then.
self.due_at = Date.strptime(self.due_at,"%d/%m/%Y").to_time unless self.due_at.nil?
I have struck exactly the same problem in Ruby 1.8.7 and the only way that I could solve it was to do the self assignment in two steps!!
xxx = Date.strptime(self.due_at,"%d/%m/%Y").to_time
self.due_at = xxx
I can' believe that this should be necessary. The only thing I can think of is that ruby is assigning fields in the new target date class on a piecemeal basis before it has finished using the due_at source string on the right hand side.
The problem does not seem to exist in Ruby 1.9.3