Closed oniietzschan closed 1 month ago
Oh, I kind of figured it out myself. Someone else might find elements of this snippet useful:
import asyncio
from azaka import AND, Client, Node, OR, select, Paginator
query = (
select(
'title',
'platforms',
'languages'
)
.frm('vn')
.where(AND(
Node('lang') == 'en',
Node('platform') == 'p98'
))
.sort('title')
)
async def main() -> None:
async with Client() as client:
pages = Paginator(client, query, max_results_per_page=50)
while await pages.next():
resp = pages.current()
for vn in resp.results:
print(vn.id, vn.title)
asyncio.run(main())
Hi! Thanks for using Azaka! Yeah I have done a horrible job at documenting stuff :( I will try my best to get everything documented as soon as possible. Do you have any more issues?
Hm, the one other mildly-unintuitive thing I came across was the syntax for filtering on related items, ex:
def find_releases_for_vn(client: Client, vn_id: str):
query = (
select()
.frm('release')
.where(Node('vn') == (Node('id') == vn_id))
)
return client.execute(query=query)
To be perfectly honest, it feel it might be easier for people to understand if you just preferred a style where you passed in what literally ends up being in the query, ex.
query = (
select()
.frm('release')
.where(['vn', '=', ['id', '=', vn_id]])
)
Once I understood that Node() was basically syntactic sugar for that, it was easy enough to understand. But at the start it was a bit of a barrier to understanding how to go from the VNDB docs (https://api.vndb.org/kana#filters) to what your library seemed to prefer. Just my 2 cents -- but maybe there's some value to Node() that hasn't clicked for me yet.
Yes Node is there for some simple abstraction over the low level encoding. I decided to abstract it away because i felt that writing all those nested lists by hand is error prone and hard to debug especially when queries are big and complex. However yeah you can pass your filters as lists to the where
function.
querya = (
select("id", "average")
.frm("vn")
.where(
["and",
["or",
["lang", "=", "en"],
["lang", "=", "de"],
["lang", "=", "fr"]
],
["olang", "!=", "ja"],
["release", "=", ["and",
["released", ">=", "2020-01-01"],
["producer", "=", ["id", "=", "p30"]],
],
],
]
)
)
Node comes in handy when you have to build giant filters such as the one above
queryb = (
select("id", "average")
.frm("vn")
.where(
AND(
OR(
Node("lang") == "en",
Node("lang") == "de",
Node("lang") == "fr"
),
Node("olang") != "ja",
Node("release") == AND(
Node("released") >= "2020-01-01",
Node("producer") == (Node("id") == "p30"),
),
)
)
)
I think the latter is more clear and easier to build in general.
One more thing, though really silly but you can alias Node to lets say Release to make it look even more clear and specific
query = (
select()
.frm('release')
.where(Release('vn') == (Release('id') == vn_id))
)
I do understand that due to the nature of this library, users are required to constantly cross-reference VNDB API and adding my own abstraction layer over anything can cause alot of confusion so i will try to use the list approach over Node approach in documentation and examples and introduce the concept of Node as an extension separately.
Anyways to finish this, yeah i have plans to extend Node, such as i am thinking of using in
operator to flatten out OR chains
OR( Node("lang") == "en", Node("lang") == "de", Node("lang") == "fr" )
to
Node("lang") in ["en", "de", "fr"]
I figured out that you can write something like this:
I'm not sure if there's a better way to accomplish this that makes use of the Node construct?
Anyways, I'm just suggesting that you add a example to the docs that represents a best practice for using AND / OR. Otherwise using this library has been pretty intuitive so far.