group method for activerecord. Docs are confusing? - activerecord

I don't get what this means in the Rails tutorial:
group(*args) public
Allows to specify a group attribute:
User.group(:name)
=> SELECT "users".* FROM "users" GROUP BY name
Returns an array with distinct records based on the group attribute:
User.group(:name) [User id: 3, name: "Foo", ...>, #User id: 2, name: "Oscar", ...>]
I don't see the grouping with the example they gave...

Group is most useful (I think) if you are trying to count stuff in your database or if you join multiple tables. Let me give a few examples.
1.
If you want to know how many users there are in your data base with each name then you can do:
User.group(:name).count
this will return a hash looking something like this:
{ ann: 4, bert: 15, cecilia: 3 ... }
I do not know why there are so many Berts in your database but anyway...
2.
If your users have related records (for instance cars) the you can use this to get the first car included in your activerecord model (the reason it will be the first is because of how group works and is further explained in the link below)
User.joins(:cars).select('users.*, cars.model as car_model, cars.name as car_name').group('users.id')
Now all records in this result will have a method called car_model and one called car_name.
You can count how many cars each user has with one single query.
User.joins(:cars).select('users.*, count(cars.id) as car_count').group('users.id')
Now all records will have a car_count.
For further reading: Mysql group tutorial
Hope this shed enough light over groups for you to try them out a little bit. I do not think you can fully understand them until you worked with them a little bit.

Related

Is there a way to compare each item to a aggreated value?

I'm new to graphQL and Hasura. I'm trying(in Hasura) to let me users provide custom aggregation (ideally in the form of a normal graphQL query) and have then each item the results compared against the aggreation.
Here's a example. Assume I have this schema:
USERTABLE:
userID
Name
Age
City
Country
Gender
HairColor
INCOMETABLE:
userID
Income
I created a relationship in hasura and I can query the data but my users want to do custom scoring of users' income level. For example, one user may want to query the data broken down by country and gender.
For the first example the result maybe:
{Country : Canada
{ gender : female
{ userID: 1,
Name: Nancy Smith,..
#data below is on aggregated results
rank: 1
%fromAverage: 35%
}...
Where I'm struggling is the data showing the users info relative to the aggregated data.
for Rank, I get the order by sorting but I'm not sure how to display the relative ranking and for the %fromAverage, I'm not sure how to do it at all.
Is there a way to do this in Hasura? I suspected that actions might be able to do this but I'm not sure.
You can use track a Postgres view. Your view would have as many fields as you'd like calculated in SQL and tracked as a separate "table" on your graphql api.
I am giving examples below based on a simplification where you have just table called contacts with just a single field called: id which is an auto-integer. I am just adding the id of the current contact to the avg(id) (a useless endeavor to be sure; just to illustrate...). Obviously you can customize the logic to your liking.
A simple implementation of a view would look like this (make sure to hit 'track this' in hasura:
CREATE OR REPLACE VIEW contact_with_custom AS
SELECT id, (SELECT AVG(ID) FROM contacts) + id as custom FROM contacts;
See Extend with views
Another option is to use a computed field. This is just a postgres function that takes a row as an argument and returns some data and it just adds a new field to your existing 'table' in the Graphql API that is the return value of said function. (you don't 'track this' function; once created in the SQL section of Hasura, you add it as a 'computed field' under 'Modify' for the relevant table) Important to note that this option does not allow you to filter by this computed function, whereas in a view, all fields are filterable.
In the same schema mentioned above, a function for a computed field would look like this:
CREATE OR REPLACE FUNCTION custom(contact contacts)
RETURNS Numeric AS $$
SELECT (SELECT AVG(ID) from contacts ) + contact.id
$$ LANGUAGE sql STABLE;
Then you select this function for your computed field, naming it whatever you'd like...
See Computed fields

Some help on a Union LINQ expression please

I need some help constructing a LINQ expression. I tend to use Lambda syntax.
I have 2 tables
OrderItem >- LibraryItem
OrderItem has a number of columns:
Id
FkLibraryItemId
Text
FkOrderId
LibraryItem has a number of Columns:
Id
Text
Type
Usually when selecting an "OrderItem", one picks a "Library Item". The "Id" and "Text" value are placed into the item record.
Sometimes a user may add a one off "OrderItem" which does not need storing in the "LibraryItem" table. It is simply stored in "OrderItem", but without a "FkLibraryItemId". So I have records in "OrderItem" that do not exist in "LibraryItem".
I need the LINQ to pull out all the relevant "LibraryItem" records of "Type=X" in addition to the "OrderItem" records for the relevant Order Id.
Many thanks in advance.
UPDATE:
I think I am talking about something like:
LibraryItem.Select(new{Id,Text}).Union(Order.Select(new{Id, Text})
context.OrderItems.Include("LibraryItems")
.Where(o => o.OrderId == orderId
&&(o.LibraryItem != null ? o.LibraryItem.Type == "X" : true))
Linq-to-SQL has a different syntax for Include, which basically eager-loads the reference objects (probably LoadWith, I don't remember at the moment).
Actually you can also do it with left inner join.

How can I add multiple instances to a Django reverse foreign key set in a minimal number of hits to the database?

For example, I have two models:
class Person(models.Model):
name = models.CharField(max_length=100)
class Job(models.Model):
title = models.CharField(max_length=100)
person = models.ForeignKey(Person)
I have a list of job ids--
job_ids = [1, 2, ....]
that are pks of Job model instances
I know I can do--
for id in job_ids:
person.jobs.add(job_id)
but this will be many more queries than if I could do--
person.jobs.add(job_ids)
where it would unpack the list and use bulk_create. How do I do this? Thanks.
Have you tried
person.jobs.add(*job_ids)
In my case I used a filter query and had a list of objects (as opposed to IDs). I was getting an error similar to
TypeError: 'MyModel' instance expected, got [<MyModel: MyModel Object>]
...before I included the asterisk.
credit (another SO question)
If you didn't create your jobs yet, you can create them by adding bulk=False
jobs_list=[
Job(title='job_1'),
Job(title='job_2')
[
person.jobs.add(*jobs_list, bulk=False) # your related_name should be 'jobs', otherwhise use 'job_sets'.

Access an ActiveRelation in a view

I have two models with the appropriate foreign key created in the people table:
class Person < ActiveRecord::Base
belongs_to :family
class Family < ActiveRecord::Base
has_many :people
If I do the following I get an object - #family_members - as an instance variable and I have no problems:
#family_members = Family.find(1)
I can access the 'child' people table fields easily in my view:
#family_members.people.first_name
However, if I use the arel way with "where" etc. I get an "ActiveRecord::Relation", not a normal object, which leaves me stumped as to how to access the same "first_name" field form the people table like I accessed above:
#family_members = Family.where(:id => 1)
or even
#family_members = Family.joins(:people).where(:id => 1)
(is the "joins" even required??)
I understand that using ".first" will cause the query to run:
#family_members = Family.where(:id => 1).first
But it returns an array, not an object, so if I use in my view:
#family_members.people.first_name
I get a "method 'people' unknown" error.
How can I access the 'first_name' field of the people table like I did with the object created by "find" but using an ActiveRecord relation?
* added information 7/15 ********
To clarify what I am looking for -- here is what I would have written if I were writing SQL instead of Arel:
SELECT f.home_phone, f.address, p.first_name, p.last_name, p.birthday
FROM families f INNER JOIN people p ON p.family.id = f.id WHERE family_id = 1
With that query's results loaded into a result set I could access:
myResultSet("home_phone") -- the home_phone from the families table
myResultSet("address") -- the address from the families table
myResultSet("first_name") -- the first_name from the people table
myResultSet("birthdate") -- the birthdate from the people table
If the two tables in the query have a same-named field I would just use "AS" to request one of the fields by another name.
I have used this kind of query/result set for many years in web apps and I am trying to deduce how to do the same in Rails and ActiveRecord.
#family_members.people.first_name shouldn't ever work so I'm surprised you find it working ... #family_members contains a Family object, #family_members.people is an array of Person objects.
The fact that you're calling it #family_members seems to make me think you're expecting it to be an array of Persons... in which case the correct code would be...
#family_members = Family.find(1).people # finds people in first Family object
If you expect #family_members to contain just the first family member, then...
#family_members = Family.find(1).people.first
If you want an array of first names of all family members, then...
#family_members = Family.find(1).people # finds people in 1st Family object
#family_members.map {|member| member.first_name} # array of first_name
#family_members = Family.find(1) and #family_members = Family.where(:id => 1) are functionally identical.. both retrieve the first Family object in the database in each case may contain zero, one, or multiple people.
Just to be clear, the "1" in all examples above refer to which Family object is retrieved, not which Person in the Family.

Finding Related Rows in Doctrine -- "Cleanest" Design?

I'm new to Doctrine, and I'm trying to get my head around both Doctrine and Symfony at the same time, so please bear with me.
I have a "Sentence" object. Sentences can be "rated" (scored out of five) by Users. I'm modelling this with the fairly obvious design where I have a Sentence table, a User table and a Rating table. Each row in the Rating table represents one user's rating of one sentence.
Rating:
...
columns:
rating: { type: integer } # This is my score out of five
sentence_id: { type: integer }
user_id: { type: integer }
relations:
sfGuardUser: { local: user_id }
Sentence: { local: sentence_id, onDelete: CASCADE, foreignAlias: Ratings }
That's working fine, but I'm struggling to find the cleanest way of asking questions of my model.
For example, in my code I have a logged-in User, related to my session, and I'm displaying a page representing a Sentence, which is also already sitting in a variable. I want to display the logged-in user's rating of that Sentence.
In raw SQL, effectively what I want to do is:
SELECT
rating
FROM
rating_table
WHERE
sentence_id = {$sentence->id} AND user_id = {$user->id}
Now, I know I could just do this on the RatingTable, simply by writing a new query with a bit of DQL, or even using one of the "magic" finders. But that seems a bit clumsy and not very OO, when I already have a Sentence object in memory with a getRatings() method generated by Doctrine for me which must already have the rating I want in its Collection of "all ratings for this sentence".
Is there an easy, efficient way that I'm missing of doing something like $sentence->getRatings()->findByUserId($user->getId())?
Or is it actually sensible just to ignore the fact that I've already got a collection of "ratings for this sentence" in memory and dive back out to the database, ignoring them completely?
There isnt a finder within a collection like that that im aware of (unlike with Propel)... if there is and I missed it in the Doctrine API then im going to leanr a blissful piece of code when someone posts it :-)
Having said that, IF you already have the collection loaded i would just add a custom method tot he model.. for example:
public function getUserRating($user){
if($user instanceof Doctrine_Record){ // or sfGuardUser depending on how tightly you want to couple
$user = $user->getId();
}
foreach($this->getRatings() as $rating){
if($user == $rating->getUserId()){
return $rating;
}
}
return null;
}

Resources