Selecting records not found in other list of records - ponyorm

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

Related

Many-to-Many extra field in activejdbc

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!

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.

What does Query Projection mean in Entity Framework?

I am looking at some EF examples and trying to decipher what 'Query Projection' exactly equates to when doing LINQ to Entities or EntitySQL. I believe it is when the query results are filtered and projected into an anonymous type but not 100% sure.
Can someone please define this and maybe provide a small L2E query that uses an example of it?
Projection is when the result of a query is output to a different type than the one queried. Another article defined it as : the process of transforming the results of a query
Projection can be to an anonymous type, but could also be to a concrete type. If you come from a SQL world, it is akin to the columns listed in your SELECT clause.
Example selecting a sub-set of an object into an concrete type:
ParentObj.Select(x=> new ParentSlim { ParentID = x.ParentID, Name = x.Name } );
.
Example merging to object into a 3rd anonymous type:
Note: the select new portion is the projection.
from P in ParentObj.AsQueryable()
join C in ChildObj.AsQueryable() on P.ParentID == C.ParentID
select new { // <-- look ma, i'm projecting!
ParentID = P.ParentID,
Name = P.Name,
SubName = C.Name
RandomDate = DateTime.UtcNow()
}

JPA Critiera query with many joins using three OR predicates

I'm writing a query using JPA Criteria API where I want to get all travels matching any of these predicates:
Get all travels (MasterTravels) that I own (owner = user)
Get all travels where I am a passenger (Passenger, user = user)
Get all travels that I have applied to (MasterTravelApplication, user = user)
The first predicate is easy. I just check if owner = user in MasterTravel. For the second predicate I need to join MasterTravel->MTT->Travel->Passenger and then check if Passenger_.user = user. For the third predicate I need to join in another direction MasterTravel->MasterTravelApplication and check for MasterTravelApplication_.user = user. This is what breaks my query since I've got the two first working but when I try to add the last predicate it stops working.
Should I make a new root for the last join, or how can I take the union of Joins 1 and Joins 2 below?
CriteriaQuery<MasterTravel> q = cb.createQuery(MasterTravel.class);
Root<MasterTravel> root = q.from(MasterTravel.class);
// Joins 1, these works fine
Join<MasterTravel, MTT> mtts = root.join(MasterTravel_.mtt);
Join<MTT, Travel> travels = mtts.join(MTT_.travel);
Join<Travel, Passenger> passengers = travels.join(Travel_.passengers);
// Joins 2, this doesn't work
Join<MasterTravel, MasterTravelApplication> application = root.join(MasterTravel_.applications);
Predicate p = cb.or(
cb.equal(passengers.get(Passenger_.user), user),
cb.equal(root.get(MasterTravel_.owner), user),
cb.equal(application.get(MasterTravelApplication_.user), user)); // When I add this it stops working
q.select(root);
q.where(p);
q.orderBy(cb.desc(root.get(MasterTravel_.createdTimestamp)));
q.distinct(true);
List<MasterTravel> resultList = em.createQuery(q).getResultList();
You don't describe the links between entities, but I would guess that a master travel doesn't necessarily have at least one application, and since you're doing an inner join, all the master travels that don't have any application are not found.
Use the join method taking a JoinType as argument, and use a JoinType.LEFT.

Entity Sql for a Many to Many relationship

Consider two tables Bill and Product with a many to many relationship. How do you get all the bills for a particular product using Entity Sql?
Something like this
SELECT B FROM [Container].Products as P
OUTER APPLY P.Bills AS B
WHERE P.ProductID == 1
will produce a row for each Bill
Another option is something like this:
SELECT P, (SELECT B FROM P.Bills)
FROM [Container].Products AS P
WHERE P.ProductID == 1
Which will produce a row for each matching Product (in this case just one)
and the second column in the row will include a nested result set containing the bills for that product.
Hope this helps
Alex
You need to use some linq like this;
...
using (YourEntities ye = new YourEntities())
{
Product myProduct = ye.Product.First(p => p.ProductId = idParameter);
var bills = myProduct.Bill.Load();
}
...
This assumes that you have used the entitiy framework to build a model for you data.
The bills variable will hold a collection of Bill objects that are related to your product object.
Hope it helps.