peewee ORM - filter on a join using attributes from all models involved in the join - peewee

#coleifer, Thank you for giving us Peewee.
I have a join from two Models that are connected by an FK constraint. I am querying the combined result with a query like the one below:
Model1.select(Model1, Model2).join(Model2).dicts()
I want to apply filters on the combined query, using attributes from both Models.
I was guided towards filter from the answer in this question: filter with dynamic dict peewee ORM
I can apply filter on Model1 attributes by appending filter at the end of join, like this: Model1.select(Model1, Model2).join(Model2).filter(Model1.name == 'foo').dicts() and that works as expected. I followed the documentation here: http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.filter
If I attempt to apply any filter with attributes from Model2 like filter(Model2.id == 22), it fails with an AttributeError.
I tried to create a CTE and applied filter on it but didn't work throwing an AttributeError.
is it possible to achieve what I am attempting? Please can you guide?

You should use .where() and not .filter():
query = (Model1
.select(...)
.join(Model2)
.where((Model1.field == 1) & (Model2.other == 2)))
The docs provide many examples, please consult them. http://docs.peewee-orm.com/en/latest/peewee/querying.html#filtering-records

Related

Filter by two tagging systems

I have a post relation and tag_system_one and tag_system_two.
There is a many-to-many relationship between post and tag_system_one through tag_system_one_post.
There is a many-to-many relationship between post and tag_system_two through tag_system_two_post.
I would like a SQL query for multi tag search.
When there is only one tag system the query is pretty simple:
SELECT post.*
FROM post
JOIN tag_system_one_post ON post.id = tag_system_one_post.fk_post
JOIN tag_system_one ON tag_system_one_post.fk_tag_system_one = tag_system_one.id
WHERE tag_system_one.id IN (500, 533)
GROUP BY post.id
HAVING COUNT(*) = 2;
In this example, it retrieves all posts with the two tags 500 and 533 (intersect).
Posts that only have tag `500` but not `533` will not be shown.
I am able to enforce the above statement because I am using COUNT(*) = 2. But this will not work when I introduce another tagging system (tag_system_two).
Is there a way to do this without a subquery?
After thinking a while I think the solution is:
HAVING COUNT(*) =
no. of items selected in tagging_system_one
MULTIPLIED BY
no. of items selected in tagging_system_two

Rails 3, ActiveRecord, PostgreSQL - ".uniq" command doesn't work?

I have following query:
Article.joins(:themes => [:users]).where(["articles.user_id != ?", current_user.id]).order("Random()").limit(15).uniq
and gives me the error
PG::Error: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ...s"."user_id" WHERE (articles.user_id != 1) ORDER BY Random() L...
When I update the original query to
Article.joins(:themes => [:users]).where(["articles.user_id != ?", current_user.id]).order("Random()").limit(15)#.uniq
so the error is gone... In MySQL .uniq works, in PostgreSQL not. Exist any alternative?
As the error states for SELECT DISTINCT, ORDER BY expressions must appear in select list.
Therefore, you must explicitly select for the clause you are ordering by.
Here is an example, it is similar to your case but generalize a bit.
Article.select('articles.*, RANDOM()')
.joins(:users)
.where(:column => 'whatever')
.order('Random()')
.uniq
.limit(15)
So, explicitly include your ORDER BY clause (in this case RANDOM()) using .select(). As shown above, in order for your query to return the Article attributes, you must explicitly select them also.
I hope this helps; good luck
Just to enrich the thread with more examples, in case you have nested relations in the query, you can try with the following statement.
Person.find(params[:id]).cars.select('cars.*, lower(cars.name)').order("lower(cars.name) ASC")
In the given example, you're asking all the cars for a given person, ordered by model name (Audi, Ferrari, Porsche)
I don't think this is a better way, but may help to address this kind of situation thinking in objects and collections, instead of a relational (Database) way.
Thanks!
I assume that the .uniq method is translated to a DISTINCT clause on the SQL. PostgreSQL is picky (pickier than MySQL) -- all fields in the select list when using DISTINCT must be present in the ORDER_BY (and GROUP_BY) clauses.
It's a little unclear what you are attempting to do (a random ordering?). In addition to posting the full SQL sent, if you could explain your objective, that might be helpful in finding an alternative.
I just upgraded my 100% working and tested application from 3.1.1 to 3.2.7 and now have this same PG::Error.
I am using Cancan...
#users = User.accessible_by(current_ability).order('lname asc').uniq
Removing the .uniq solves the problem and it was not necessary anyway for this simple query.
Still looking through the change notes between 3.1.1 and 3.2.7 to see what caused this to break.

Joining with a tags table -- should I join in PHP or on the DB server?

Using the Toxi solution, how should I select the tags for a certain "Bookmark" (to keep the Delicious theme):
I can either:
1) Join in a single query:
select bookmark.title, bookmark.url,
(
SELECT group_concat( tags.name ) as tagNames
FROM taggings INNER JOIN tags
ON taggings.tagId_fk=tags.tagId
WHERE taggings.bookmarkId_fk = bookmarks.bookmarkId_fk
)
from bookmarks
where bookmarks.id=1 ;
^^ That gives
title url tagNames
A bkmrk http://url.com tag1,tag2,tag3
2) Use two queries: one to retrieve the bookmark id's to display, then another to retrieve the tags for those bookmarks. The results can then be merged in PHP.
So really this question is: In general efficiency/database load-wise is it better to do more joining in a single query or multiple queries?
How do you make that kind of decision? Or do you simply not think about it until load causes a problem?
Server side is more efficient.
In both cases, the server must read all of the tags.
If you bring them to PHP, then they must all travel over the wire and PHP has to fiddle with them.
If you do them on the server, the finished answer (smaller) comes over the wire ready for PHP to pass it up to the UI.

Datastore Query filtering on list

Select all records, ID which is not in the list
How to make like :
query = Story.all()
query.filter('ID **NOT IN** =', [100,200,..,..])
There's no way to do this efficiently in App Engine. You should simply select everything without that filter, and filter out any matching entities in your code.
This is now supported via GQL query
The 'IN' and '!=' operators in the Python runtime are actually
implemented in the SDK and translate to multiple queries 'under the
hood'.
For example, the query "SELECT * FROM People WHERE name IN ('Bob',
'Jane')" gets translated into two queries, equivalent to running
"SELECT * FROM People WHERE name = 'Bob'" and "SELECT * FROM People
WHERE name = 'Jane'" and merging the results. Combining multiple
disjunctions multiplies the number of queries needed, so the query
"SELECT * FROM People WHERE name IN ('Bob', 'Jane') AND age != 25"
generates a total of four queries, for each of the possible conditions
(age less than or greater than 25, and name is 'Bob' or 'Jane'), then
merges them together into a single result set.
source: appengine blog
This is an old question, so I'm not sure if the ID is a non-key property. But in order to answer this:
query = Story.all()
query.filter('ID **NOT IN** =', [100,200,..,..])
...With ndb models, you can definitely query for items that are in a list. For example, see the docs here for IN and !=. Here's how to filter as the OP requested:
query = Story.filter(Story.id.IN([100,200,..,..])
We can even query for items that in a list of repeated keys:
def all(user_id):
# See if my user_id is associated with any Group.
groups_belonged_to = Group.query().filter(user_id == Group.members)
print [group.to_dict() for group in belong_to]
Some caveats:
There's docs out there that mention that in order to perform these types of queries, Datastore performs multiple queries behind the scenes, which (1) might take a while to execute, (2) take longer if you searching in repeated properties, and (3) will up your costs with more operations.

How do I search a "Property Bag" table in SQL?

I have a basic "property bag" table that stores attributes about my primary table "Card." So when I want to start doing some advanced searching for cards, I can do something like this:
SELECT dbo.Card.Id, dbo.Card.Name
FROM dbo.Card
INNER JOIN dbo.CardProperty ON dbo.CardProperty.IdCrd = dbo.Card.Id
WHERE dbo.CardProperty.IdPrp = 3 AND dbo.CardProperty.Value = 'Fiend'
INTERSECT
SELECT dbo.Card.Id, dbo.Card.Name
FROM dbo.Card
INNER JOIN dbo.CardProperty ON dbo.CardProperty.IdCrd = dbo.Card.Id
WHERE (dbo.CardProperty.IdPrp = 10 AND (dbo.CardProperty.Value = 'Wind' OR dbo.CardProperty.Value = 'Fire'))
What I need to do is to extract this idea into some kind of stored procedure, so that ideally I can pass in a list of property/value combinations and get the results of the search.
Initially this is going to be a "strict" search meaning that the results must match all elements in the query, but I'd also like to have a "loose" query so that it would match any of the results in the query.
I can't quite seem to wrap my head around this one. My previous version of this was to do generate some massive SQL query to execute with a lot of AND/OR clauses in it, but I'm hoping to do something a little more elegant this time. How do I go about doing this?
it seems to me that you have an EAV model here.
if you're using sql server 2005 and up i'd suggest you use XML datatype for this:
http://weblogs.sqlteam.com/mladenp/archive/2006/10/14/14032.aspx
makes searching and stuff much easier with built in xml querying capabilities.
if you can't change your model then look at this:
http://weblogs.sqlteam.com/davidm/articles/12117.aspx

Resources