Reputation: 15526
I'm working with a Key
model backed by Postgres that is a generic table to hold API keys:
class Key(Model):
__tablename__ = "keys"
id = Column(Integer, primarykey=True)
user_id = Column(Integer, ForeignKey("users.id"))
brokerage_id = Column(Integer, ForeignKey("brokerages.id"))
account_id = Column(Integer, ForeignKey("accounts.id"))
key = Column(String(128))
value = Column(String(128))
In the below example, user 2
has three keys. All three are associated with brokerage 2
and account 2
. This is represented by IDs 4 through 6. For this site, the user has an authentication token plus two query IDs.
id user_id brokerage_id account_id key value
--------------------------------------------------------------------
4 2 2 2 token 999999999999
5 2 2 2 query_id 888888
6 2 2 2 query_id 777777
7 1 2 3 token 444444444444
I am trying to construct a query so that my result will be modeled as such:
[(user_id, brokerage_id, account_id, token, [query_id_1, query_id_2, ...]), ...]
So for the above example, it would look like this
[(2, 2, 2, 999999999999, [888888, 777777]), (1, 2, 3, 444444444444, [])]
I've got the following queries which select the token and the query_ids
tokens = db.session.query(
Key.user_id, Key.brokerage_id, Key.account_id, Key.value
).filter(Key.key=='token').all()
query_ids = db.session.query(
Key.user_id, Key.brokerage_id, Key.account_id, Key.value
).filter(Key.key=='query_id').all()
I've tried using subquery
in various ways but cannot quite get the output to resemble what I need. How can I construct a query to return results in a way that align to my list of tuples, above?
Adding the final working query here thanks to @rfkortekaas
from sqlalchemy.orm import aliased
from sqlalchemy import func, and_
from project.models import Key
from project.extensions import db
key_token = aliased(Key)
q = db.session.query(
key_token.user_id,
key_token.brokerage_id,
key_token.account_id,
key_token.value.label('token'),
func.array_agg(Key.value).label('query_ids')
).join(
Key,
and_(
key_token.user_id == Key.user_id,
key_token.brokerage_id == Key.brokerage_id,
key_token.account_id == Key.account_id,
Key.key == 'query_id'
)
).filter(
key_token.key == 'token'
).group_by(
key_token.user_id,
key_token.brokerage_id,
key_token.account_id,
key_token.value
)
results = q.all()
Upvotes: 0
Views: 354
Reputation: 6514
You can use the array_agg
function from PostgreSQL to create an array of the results:
from sqlalchemy.orm import aliased
key_token = aliased(Key)
stmt = select(key_token.user_id,
key_token.brokerage_id,
key_token.account_id,
key_token.value.label('token'),
func.array_agg(Key.value).label('query_ids')
).join(Key,
and_(key_token.user_id == Key.user_id,
key_token.brokerage_id == Key.brokerage_id,
key_token.account_id == Key.account_id,
Key.key == 'query_id'))\
.where(key_token.key == 'token')\
.group_by(key_token.user_id,
key_token.brokerage_id,
key_token.account_id,
key_token.value)
keys = session.execute(stmt).all()
for row in keys:
print(row)
Results in:
user_id | brokerage_id | account_id | token | query_ids |
---|---|---|---|---|
1 | 2 | 3 | '44' | ['4'] |
2 | 2 | 1 | '33" | ['6'] |
2 | 2 | 2 | '99" | ['8', '7] |
For the following dataset:
user_id | brokerage_id | account_id | key | value |
---|---|---|---|---|
2 | 2 | 2 | token | '99' |
2 | 2 | 1 | token | '33' |
2 | 2 | 1 | query_id | '6' |
2 | 2 | 2 | query_id | '8' |
2 | 2 | 2 | query_id | '7' |
1 | 2 | 3 | token | '44' |
1 | 2 | 3 | query_id | '4' |
Upvotes: 2