I'm looking to get a .count based format, by grouping a method on the model.
What I've tried is along the lines of
Model.all.group(:age_at_x).count
and
Model.all.group_by { |v| v.age_at_x }.count
With age_at_x defined in the model. I know that this tries to group in MySQL with a method, but I can't seem to figure out to actually group by the method?
def age_at_x
date = Time.new(2016, 6, 25)
dob = self.birth
date.year - dob.year - ((date.month > dob.month || (date.month == dob.month && date.day >= dob.day)) ? 0 : 1)
end
I'm looking to count the different ages, and see how many of each - not the number of ages.
Furthermore in the future, to count by 18+ and under 18.
If I understand your question, you are trying to count people by diferentes ages, try that:
Model.select(:birth).distinct.count
Related
I have a table that has 2 columns "date_from" and "date_to". I have to make a query which should return results where "date_to" - "date_from" == "2 weeks".
Something like this:
User.where('date_to - date_from == 2.weeks').
Define this on user:
def self.two_weeks
where(date_to: date_from + 2.weeks)
end
Then you can just call User.two_weeks
Can also be done as a scope, if you prefer:
scope :two_weeks, -> { where(date_to: date_from + 2.weeks) }
It's good to keep these kinds of things on the model anyway, so that you can change them in one place if they need to be tweaked
This should work, for mysql at least
User.where('1 + DATEDIFF(date_to, date_from) >= 14')
Apparently, I could not compare the date in the peewee SQL.
START_DATE = datetime.datetime(2015, 7, 20, 0, 0, 0)
customer_records = Customers.select().\
join(Current_Insurers, on=(Customers.current_insurer == Current_Insurers.id)).\
switch(Current_Insurers).\
join(Insurers, on=(Current_Insurers.insurer == Insurers.id)).\
where(Customers.pol_type == "PC" & \
Current_Insurers.effective_date == START_DATE )
Where Customers, Current_Insurers, Insurers are three class. The result is always 0 records. But if I removed the datetime condition from the sql and compare as follows
customer_records = Customers.select().\
join(Current_Insurers, on=(Customers.current_insurer == Current_Insurers.id)).\
switch(Current_Insurers).\
join(Insurers, on=(Current_Insurers.insurer == Insurers.id)).\
where(Customers.pol_type == "PC"
for r in customer_records:
if(r.current_insurer.effective_date == START_DATE):
print(r.policy_id)
Surprisingly we can compare now and print out customers.
What do I need to do to add the datetime condition in the peewee sql?
Many thanks,
Apparently, I could not compare the date in the peewee SQL.
That's completely incorrect. Do you honestly think that the library would be that broken??
The problem is Python operator precedence. You need to wrap the equality expressions with parentheses. So you where clause should look like this instead:
where((Customers.pol_type == "PC") & \
(Current_Insurers.effective_date == START_DATE))
Additionally, it's typically only necessary to call switch() when you have multiple joins to a single model.
Put together, your query should be:
query = (Customers
.select()
.join(Current_Insurers, on=(Customer.current_insurer == Current_Insurers.id))
.join(Insurers, on=(Current_Insurers.insurer == Insurer.id))
.where(
(Customers.pol_type == "PC") &
(Current_Insurers.effective_date == START_DATE)))
I came here because I had the same problem abd subsequently the same question.
The cause of my issue was that mariaDB was stripping the milliseconds when the original insert was done and python/peewee was passing in the milliseconds in the predicate on the later update. Very frustrating.
I write query to find out how much the user has reduced calories in a week, but I have this error.
How to avoid mistakes?
def self.calories_burned(current_user)
week = ((created_at - current_user.first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
find_by_sql("
SELECT
count(*) as cnt,
WEEK(#{week}) as week_number
FROM
user_daily_updates
WHERE
user_id=#{current_user.id}
GROUP BY
week_number
")
end
When you write this:
def self.calories_burned(current_user)
etc...
end
it's a method that can only be called like this:
MyUserModel.calories_burned(some_user)
In this case you are running the code on the class before it is instantiated, this means that the model hasn't even attached itself to a connection to the database and because of that you will not be able to access attributes that pertain to your model.
On the other hand, if you write this:
def calories_burned
etc...
end
You don't need to pass the user to the method, you simply call it on the controller after instantiating your model, like this:
id = 123
current_user = MyUserModel.find(id)
current_user.calories_burned
where current_user.calories_burned will return the value you are looking for based on the current user.
After taking a closer look at your method
it should look more like this:
def calories_burned
week = ((created_at - first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
UserDailyUpdate.where(["user_id = ?", id]).where(["week = ?",week]]).count(:all, :group => 'week_number')
end
If I understood correctly what you were trying to do with your query, this should give you the same result. Now, I should mention I am assuming that when you created the table user_daily_updates, you also created a model UserDailyUpdate.
You should take a look at ActiveRecord's documentation for searching using conditions (all of section 2) in order to have a better understanding of what I just did.
Apneadiving is right, but I would also look to refactor your code:
def self.calories_burned(current_user)
week = ((##created_at## - current_user.first_program_started_at.utc.beginning_of_day) / 86400 / 7).ceil.to_i || 1
select("#{self.table_name}.*, count(*) as cnt, WEEK(#{week}) as week_number").where(user_id: current_user.id).group(:week_number)
end
You'll need to pull created_at from the db, as it won't be present unless you have an instance of a class already loaded
I have the following problem: In our database we record helpdesk tickets and we book hours under tickets. Between those is a visit report. So it is: ticket => visitreport => hours.
Hours have a certain 'kind' which is not determined by a type indicator in the hour record, but compiled by checking various properties of an hour. For example, an hour which has a customer but is not a service hour is always an invoice hour.
Last thing I want is that the definitions of those 'kinds' roam everywhere in the code. They must be at one place. Second, I want to be able to calculate totals of hours from various collections of hours. For example, a flattened collection of tickets with a certain date and a certain customer. Or all registrations which are marked as 'solution'.
I have decided to use a 'layered' database access approach. The same functions may provide data for screen representation but also for a report in .pdf . So the first step gathers all relevant data. That can be used for .pdf creation, but also for screen representation. In that case, it must be paged and ordered in a second step. That way I don't need separate queries which basically use the same data.
The amount of data may be large, like the creation of year totals. So the data from the first step should be queryable, not enumerable. To ensure I stay queryable even when I add the summation of hours in the results, I made the following function:
public static decimal TreeHours(this IEnumerable<Uren> h, FactHourType ht)
{
IQueryable<Uren> hours = h.AsQueryable();
ParameterExpression pe = Expression.Parameter(typeof(Uren), "Uren");
Expression left = Expression.Property(pe, typeof(Uren).GetProperty("IsOsab"));
Expression right = Expression.Constant(true, typeof(Boolean));
Expression isOsab = Expression.Equal(Expression.Convert(left, typeof(Boolean)), Expression.Convert(right, typeof(Boolean)));
left = Expression.Property(pe, typeof(Uren).GetProperty("IsKlant"));
right = Expression.Constant(true, typeof(Boolean));
Expression isCustomer = Expression.Equal(Expression.Convert(left, typeof(Boolean)), Expression.Convert(right, typeof(Boolean)));
Expression notOsab;
Expression notCustomer;
Expression final;
switch (ht)
{
case FactHourType.Invoice:
notOsab = Expression.Not(isOsab);
final = Expression.And(notOsab, isCustomer);
break;
case FactHourType.NotInvoice:
notOsab = Expression.Not(isOsab);
notCustomer = Expression.Not(isCustomer);
final = Expression.And(notOsab, notCustomer);
break;
case FactHourType.OSAB:
final = Expression.And(isOsab, isCustomer);
break;
case FactHourType.OsabInvoice:
final = Expression.Equal(isCustomer, Expression.Constant(true, typeof(Boolean)));
break;
case FactHourType.Total:
final = Expression.Constant(true, typeof(Boolean));
break;
default:
throw new Exception("");
}
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { hours.ElementType },
hours.Expression,
Expression.Lambda<Func<Uren, bool>>(final, new ParameterExpression[] { pe })
);
IQueryable<Uren> result = hours.Provider.CreateQuery<Uren>(whereCallExpression);
return result.Sum(u => u.Uren1);
}
The idea behind this function is that it should remain queryable so that I don't switch a shipload of data to enumerable.
I managed to stay queryable until the end. In step 1 I gather the raw data. In step 2 I order the data and subsequently I page it. In step 3 the data is converted to JSon and sent to the client. It totals hours by ticket.
The problem is: I get one query for the hours for each ticket. That's hundreds of queries! That's too much...
I tried the following approach:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Ticket>(t => t.Bezoekrapport);
options.LoadWith<Bezoekrapport>(b => b.Urens);
dc.LoadOptions = options;
Bezoekrapport is simply Dutch for 'visitreport'. When I look at the query which retrieves the tickets, I see it joins the Bezoekrapport/visitreport but not the hours which are attached to it.
A second approach I have used is manually joining the hours in LINQ, but that does not work as well.
I must do something wrong. What is the best approach here?
The following code snippets are how I retrieve the data. Upon calling toList() on strHours in the last step, I get a hailstorm of queries. I've been trying for two days to work around it but it just doesn't work... Something must be wrong in my approach or in the function TreeHours.
Step 1:
IQueryable<RelationHoursTicketItem> HoursByTicket =
from Ticket t in allTickets
let RemarkSolved = t.TicketOpmerkings.SingleOrDefault(tr => tr.IsOplossing)
let hours = t.Bezoekrapport.Urens.
Where(h =>
(dateFrom == null || h.Datum >= dateFrom)
&& (dateTo == null || h.Datum <= dateTo)
&& h.Uren1 > 0)
select new RelationHoursTicketItem
{
Date = t.DatumCreatie,
DateSolved = RemarkSolved == null ? (DateTime?)null : RemarkSolved.Datum,
Ticket = t,
Relatie = t.Relatie,
HoursOsab = hours.TreeHours(FactHourType.OSAB),
HoursInvoice = hours.TreeHours(FactHourType.Invoice),
HoursNonInvoice = hours.TreeHours(FactHourType.NotInvoice),
HoursOsabInvoice = hours.TreeHours(FactHourType.OsabInvoice),
TicketNr = t.Id,
TicketName = t.Titel,
TicketCategorie = t.TicketCategorie,
TicketPriority = t.TicketPrioriteit,
TicketRemark = RemarkSolved
};
Step 2
sort = sort ?? "TicketNr";
IQueryable<RelationHoursTicketItem> hoursByTicket = GetRelationHours(relation, dateFrom, dateTo, withBranches);
IOrderedQueryable<RelationHoursTicketItem> orderedResults;
if (dir == "ASC")
{
orderedResults = hoursByTicket.OrderBy(sort);
}
else
{
orderedResults = hoursByTicket.OrderByDescending(sort);
}
IEnumerable<RelationHoursTicketItem> pagedResults = orderedResults.Skip(start ?? 0).Take(limit ?? 25);
records = hoursByTicket.Count();
return pagedResults;
Step 3:
IEnumerable<RelationHoursTicketItem> hours = _hourReportService.GetRelationReportHours(relation, dateFrom, dateTo, metFilialen, start, limit, dir, sort, out records);
var strHours = hours.Select(h => new
{
h.TicketNr,
h.TicketName,
RelationName = h.Relatie.Naam,
h.Date,
TicketPriority = h.TicketPriority.Naam,
h.DateSolved,
TicketCategorie = h.TicketCategorie == null ? "" : h.TicketCategorie.Naam,
TicketRemark = h.TicketRemark == null ? "" : h.TicketRemark.Opmerking,
h.HoursOsab,
h.HoursInvoice,
h.HoursNonInvoice,
h.HoursOsabInvoice
});
I don't think your TreeHours extension method can be converted to SQL by LINQ in one go. So are evaluated on execution of each constructor of the row, causing a 4 calls to the database in this case per row.
I would simplfy your LINQ query to return you the raw data from SQL, using a simple JOIN to get all tickets and there hours. I would then group and filter the Hours by type in memory. Otherwise, if you really need to perform your operations in SQL then look at the CompiledQuery.Compile method. This should be able to handle not making a query per row. I'm not sure you'd get the switch in there but you may be able to convert it using the ?: operator.
I don't know if I'm just looking in the wrong places here or what, but does active record have a method for retrieving a random object?
Something like?
#user = User.random
Or... well since that method doesn't exist is there some amazing "Rails Way" of doing this, I always seem to be to verbose. I'm using mysql as well.
Most of the examples I've seen that do this end up counting the rows in the table, then generating a random number to choose one. This is because alternatives such as RAND() are inefficient in that they actually get every row and assign them a random number, or so I've read (and are database specific I think).
You can add a method like the one I found here.
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
end
end
This will make it so any Model you use has a method called random which works in the way I described above: generates a random number within the count of the rows in the table, then fetches the row associated with that random number. So basically, you're only doing one fetch which is what you probably prefer :)
You can also take a look at this rails plugin.
We found that offsets ran very slowly on MySql for a large table. Instead of using offset like:
model.find(:first, :offset =>rand(c))
...we found the following technique ran more than 10x faster (fixed off by 1):
max_id = Model.maximum("id")
min_id = Model.minimum("id")
id_range = max_id - min_id + 1
random_id = min_id + rand(id_range).to_i
Model.find(:first, :conditions => "id >= #{random_id}", :limit => 1, :order => "id")
Try using Array's sample method:
#user = User.all.sample(1)
In Rails 4 I would extend ActiveRecord::Relation:
class ActiveRecord::Relation
def random
offset(rand(count))
end
end
This way you can use scopes:
SomeModel.all.random.first # Return one random record
SomeModel.some_scope.another_scope.random.first
I'd use a named scope. Just throw this into your User model.
named_scope :random, :order=>'RAND()', :limit=>1
The random function isn't the same in each database though. SQLite and others use RANDOM() but you'll need to use RAND() for MySQL.
If you'd like to be able to grab more than one random row you can try this.
named_scope :random, lambda { |*args| { :order=>'RAND()', :limit=>args[0] || 1 } }
If you call User.random it will default to 1 but you can also call User.random(3) if you want more than one.
If you would need a random record but only within certain criteria you could use "random_where" from this code:
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
def self.random_where(*params)
if (c = where(*params).count) != 0
where(*params).find(:first, :offset =>rand(c))
end
end
end
end
For e.g :
#user = User.random_where("active = 1")
This function is very useful for displaying random products based on some additional criteria
Strongly Recommend this gem for random records, which is specially designed for table with lots of data rows:
https://github.com/haopingfan/quick_random_records
Simple Usage:
#user = User.random_records(1).take
All other answers perform badly with large database, except this gem:
quick_random_records only cost 4.6ms totally.
the accepted answer User.order('RAND()').limit(10) cost 733.0ms.
the offset approach cost 245.4ms totally.
the User.all.sample(10) approach cost 573.4ms.
Note: My table only has 120,000 users. The more records you have, the more enormous the difference of performance will be.
UPDATE:
Perform on table with 550,000 rows
Model.where(id: Model.pluck(:id).sample(10)) cost 1384.0ms
gem: quick_random_records only cost 6.4ms totally
Here is the best solution for getting random records from database.
RoR provide everything in ease of use.
For getting random records from DB use sample, below is the description for that with example.
Backport of Array#sample based on Marc-Andre Lafortune’s github.com/marcandre/backports/ Returns a random element or n random elements from the array. If the array is empty and n is nil, returns nil. If n is passed and its value is less than 0, it raises an ArgumentError exception. If the value of n is equal or greater than 0 it returns [].
[1,2,3,4,5,6].sample # => 4
[1,2,3,4,5,6].sample(3) # => [2, 4, 5]
[1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size
[].sample # => nil
[].sample(3) # => []
You can use condition with as per your requirement like below example.
User.where(active: true).sample(5)
it will return randomly 5 active user's from User table
For more help please visit : http://apidock.com/rails/Array/sample