Many-to-Many extra field in activejdbc - postgresql

I have 3 tables:
person (person_id, person_name),
game (game_id, game_name)
and the linked table
play(person_id, game_id, score).
With ActiveJdbc I use many2many annotation and it works fine to get all games for 1 person
List<Game> games = person.get(Game.class, "person_id = ?", personId);
My question is how to get the value "score"?
I tried game.getScore() but it obviously didn't work
edit:
I want the complete list of game from 1 person. Here is the result I want
game1 | 21
game2 | 33
game3 | 44
In SQL in should be something like:
select game.name, play.score
from game join play on (game.game_id = play.game_id)
join person on (game.person_id = person.person_id)
where person_id = 1;

There is probably more than one way of doing this, but here is a way. You can think of Many-to-Many relationship as two One-to-Many relationships, where Play is a child of both, Person and Game. Then you approach from a different angle:
List<Play> plays = Play.where("person_id = ?", 1).include(Person.class, Game.class);
By using include() you are also optimizing and running fewer queries: http://javalite.io/lazy_and_eager.
Depending on your query conditions, you might have just one play:
Game game = plays.get(0).parent(Game.class);
Person = plays.get(0).parent(Person.class);
If course, you'd be able to get to your score:
int score = plays.get(0).getScore();
hope it helps!

Related

Many-To-Many relationships with EFCore and C# 5.0 : how to get the fields from the both of tables

I have the following database in PostgreSQL
EDIT: there is an Unique Key in PizzaTopping built with the fields Id_Pizza, Id_Topping
As you can see it's a Many-To-Many relationship.
When I ask Linqpad 6 to scaffold from my Database I have the following result:
The same result I have it when I use the EFCore Power Tools when I ask them to reverse engineer my database.
Reading from various sources, I've found, to ask EFCore to get the list of the toppings of the pizzas I should do something like that:
Pizzas.Where(p=>p.Description=="Margherita")
.Include(p=>p.PizzaToppings)
.ThenInclude(p=>p.IdToppingNavigation)
The query EFCore 5 returns this query:
SELECT P."Id_Pizza",
P."Description",
T0."Id_PizzaTopping",
T0."Id_Pizza",
T0."Id_Topping",
T0."Id_Topping0",
T0."Description"
FROM "Pizza" AS P
LEFT JOIN
(SELECT P0."Id_PizzaTopping",
P0."Id_Pizza",
P0."Id_Topping",
T."Id_Topping" AS "Id_Topping0",
T."Description"
FROM "PizzaTopping" AS P0
INNER JOIN "Topping" AS T ON P0."Id_Topping" = T."Id_Topping") AS T0 ON P."Id_Pizza" = T0."Id_Pizza"
WHERE P."Description" = 'Margherita'
ORDER BY P."Id_Pizza",
T0."Id_PizzaTopping",
T0."Id_Topping0"
Since I want to return a list with Pizza, topping like:
margherita, mozzarella
margherita, tomato sauce
marinara, garlic
marinara, tomato sauce
I tried with add .Select(topping=>topping.description) but it gets the pizza description.
So how can I take the toppings descriptions who they are in the topping table?
I tried to put a .Select() after the .ThenInclude() but I still see the Pizza entity and the p.PizzaToppings does not contain the property description of the topping table.
In this case you do not need Include but SelectMany with custom projection:
var query =
from p in Pizzas
from pt in p.PizzaToppings
select new
{
Pizza = p.Description,
Topping = pt.IdToppingNavigation.Description
}

Pony ORM JOIN syntax

I've started using Pony and haven't actually understood how to use joins yet. In examples I saw when left_join() was used with two for clauses, but when I try to repeat it in my code, I get error like "collection was expected, got "for p in Pond""
Maybe somebody could explain how to use it or point me to docs page where it's already explained?
Let's say we have the following entities:
from pony import orm
db = orm.Database()
class Person(db.Entity):
id = orm.PrimaryKey(int, auto=True)
name = orm.Required(str)
age = orm.Required(int)
contacts = orm.Set('Contact')
class Contact(db.Entity):
id = orm.PrimaryKey(int, auto=True)
person = orm.Required('Person')
type = orm.Required(str)
value = orm.Required(str)
db.generate_mapping(create_tables=True)
with orm.db_session:
john = Person(name='John', age=23)
mary = Person(name='Mary', age=21)
mike = Person(name='Mike', age=18)
arthur = Person(name='Arthur', age=25)
john.contacts.create(type='mobile', value='1234567')
john.contacts.create(type='email', value='john#example.com')
mary.contacts.create(type='mobile', value='76543321')
mary.contacts.create(type='skype', value='mary123')
mike.contacts.create(type='mobile', value='2345678')
Now we want to print person name and a contact info for each person older then 20. There are several ways how we can do it.
The first way is when we explicitly state the join condition. This way is pretty verbose:
query = orm.select(
(p.name, c.value)
for p in Person for c in Contact
if p.age > 20 and c.person == p
)
query.show()
In this query we explicitly state the join condition: c.person == p. The query will show us the following result:
p.name|c.type|c.value
------+------+----------------
John |email |john#example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
As you can see, Arthur was not included into result, albeit his age is greater than 20. This is because this type of join is inner join, and the result includes only persons for which it was possible to find at least one contact.
The second way of joining is when we loop over collection attribute:
query = orm.select(
(p.name, c.value)
for p in Person for c in p.contacts
if p.age > 20
)
query.show()
This type of joins is used most often. It is very convenient, because we don't need to explicitly specify the join condition. The result of query is the same as before:
p.name|c.type|c.value
------+------+----------------
John |email |john#example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
Arthur is still not in the list for the same reason as before. If we want to include Arthur into result, we need to use other type of join, namely, left join:
query = orm.left_join(
(p.name, c.value)
for p in Person for c in p.contacts
if p.age > 20
)
query.show()
In that case the result of query includes Arthur with the None value instead of the phone number:
p.name|c.type|c.value
------+------+----------------
Arthur|None |None
John |email |john#example.com
John |mobile|1234567
Mary |mobile|76543321
Mary |skype |mary123
When you use left_join you need to loop over collection. In that case Pony adds the join condition into the ON section of LEFT JOIN clause of SQL command.
You cannot do explicit join as in the very first query if you use left_join, because in that case Pony does not know which condition is to put into ON section of LEFT JOIN clause.
Sometimes it may be useful to specify content of the ON section manually. Right now Pony does not support such queries, but this feature may be added in the future.
When using PonyORM in many cases it is possible to retrieve data without making joins at all. For example, you can write the following loop to print person name and phone number:
with db_session:
for p in Person.select(lambda p: p.age > 20):
print(p.name)
for c in p.contacts:
print(c.type, c.value)
In other ORMs this will lead to "N+1 query" problem, where the contacts of each person are retrieved by separate SQL query. Pony tries to automatically optimize queries to avoid "N+1 query" pattern.
In some cases the joins are implicit. For example, to find all contacts of a person whose name is started with 'M', you can write:
query = select(c for c in Contact if c.person.name.startswith('M'))
for c in query:
print(c.person.name, c.type, c.value)
In that case the Person table is joined implicitly, just because you perform attribute traversing from Contact to Person.

Selecting records not found in other list of records

Scenario
Players take part in events. They should provide information if they're going to attend an event or not.
Problem to be solved
I want to select players that haven't provided information, if they plan to attend specific event.
Your goal
As being a beginner in the technologies used, I would appreciate your validation and recommendation for improvement of the solution suggested by me below.
Solution
Technologies: python, postgreSQL, and Pony ORM
Entity model in Pony ORM:
class Event(db.Entity):
_table_ = "event"
start = Required(date)
players = Set("Attendance")
class Player(db.Entity):
_table_ = "player"
first_name = Optional(str)
last_name = Required(str)
phone_number = Required(str)
email = Required(str)
events = Set("Attendance")
class Attendance(db.Entity):
_table_ = "attendance"
event = Required(Event)
player = Required(Player)
status = Required(bool)
PrimaryKey(event, player)
Idea:
Get list of players that provided the information if they attend the event
Get list of players that are not in list created in 1.
Current implementation of the idea:
players = select(p for p in Player if p not in select(p for p in Player
for a in Attendance if p == a.player and a.event == next_event))
It is possible to refactor your query to make it more simple. At first, we can replace explicit join of Player and Attendance inside the inner query to implicit join via attribute access:
select(p for p in Player if p not in select(p for p in Player
for attendance in p.events if attendance.event == next_event))
To simplify query further we can use attribute lifting by writing expression p.events.event:
select(p for p in Player if p not in select(
p for p in Player if next_event in p.events.event))
p.events expression returns a set of Attendance records. In Pony when you have a set instance this set has all attributes of its items, and when you access such attribute you will get a set of all values of corresponding items attribute. The value of expression p.events.event will be the set of all Event objects linked with particular player.
The next step to simplify the query is to replace generators to lambdas. This way the query looks a bit shorter:
Player.select(lambda p: p not in Player.select(
lambda p: next_event in p.events.event))
But the biggest simplification can be achieved if we realize that the inner query is unnecessary and rewrite query as:
Player.select(lambda p: next_event not in p.events.event)
I think this is the most concise way to write this query using PonyORM

Self m:n Relation

I have persons and a person can contact multiple other persons, so basically the "default" tables would be:
persons (id)
contacts (person1_id, person2_id)
With this schema, I'd have to issue queries like
SELECT *
FROM contacts c
WHERE ( person1_id = *id of person1* AND person2_id = *id of person2* )
OR
( person1_id = *id of person2* AND person2_id = *id of person1* )
to get the relation between two persons when I insert such a relation only once.
What is the common practice to deal with this situation?
Insert data once and do such an OR query
Insert the relation twice so that person1_id = id of person1 AND person2_id = id of person2 is enough
An entirely different approach?
Assuming:
The m:n table actually contains additional data, so if I create a relation for both ways, I'd have to duplicate the data
This is a core part of the application and most non-trivial queries involve at least a sub query that determines whether or not such a relation exists
If you write your insert logic such that person1_id < person2_id is true for all rows, then you can just write
SELECT *
FROM contacts c
WHERE person1_id = min(*id_of_person_1*, *id_of_person_2*)
AND person2_id = max(*id_of_person_1*, *id_of_person_2*)
Why don't you use Join between the tables?
something like this:
SELECT *
FROM contact c INNER JOIN person p ON p.id = c.person1_id
The the where and group bys you need to complete you're query =)
Take a look here how the results will be showed:
http://www.w3schools.com/Sql/sql_join_inner.asp
Regards,
Elkas
Try this one mate =)
SELECT c.person1_id as id_person_1, c.person2_id as id_person_2, p1.name as name_person_1, p2.name as name_person_2
FROM contact c
LEFT JOIN person p1 ON p1.id = c.person1_id
RIGHT JOIN person p2 ON p2.id = c.person2_id;
I don't know if it will work.. but give it try mate =)
"Insert the relation twice so that person1_id = id of person1 AND person2_id = id of person2 is enough"
That is how I'd do it, personally. It allows to deal with the situation where A has the contact details of B but not the other way around (e.g. a girl gives a guy her number at the bar saying "call me" as she walks out). It also makes the queries simpler.

simple LINQ query

i am having trouble joinging tables on a LINQ query.
(source: kalleload.net)
As you can see there are three tables there. On my webpage, i want to display the following data in a List View.
betid | bet.title | bet.description | match.location | match.begindate/enddate | teamone NAME | teamtwo Name.
so let me explain this.
I want 3 fields from the bets table.
I want 2 fields from the match table (where the match.matchid = bet.matchid)
I want 2 fields from the TEAM table (where match.teamone = team.teamid and match.teamtwo = team.teamid)
i hope this makes sense.
thankyou
It looks like you already have the relationships defined. If you are using the designer, you should have existing entities in the generated classes.
var result = bet.Select( b => new {
BetID = b.betid,
Title = b.title,
Description = b.description,
BeginDate = b.match.begindate,
EndDate = b.match.enddate,
TeamOneName = b.match.team_1.teamname,
TeamTwoName = b.match.team_2.teamname
} );
Note that I'm only guessing at the association names. You should be able to figure out the names given to them by the designer.
The key to this one is to include a self join on the team table, for team one and team two, like this:
var result = from bet in db.bet
join match in db.match on bet.matchid equals match.matchid
join team1 in db.team on match.teamone equals team1.teamid
join team2 in db.team on match.teamtwo equals team2.teamid
select new {bet.betid, bet.title, bet.description, match.location, match.begindate, match.enddate, team1.name, team2.name};