Add BQL query method to FavaClient
Implemented query_bql() method to enable efficient Beancount Query Language (BQL) queries against Fava API. This is the foundation for replacing manual balance aggregation (115 lines) with optimized BQL queries. Benefits: - Efficient server-side filtering and aggregation - 5-10x expected performance improvement - Cleaner, more maintainable code Next: Implement get_user_balance_bql() using this method. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6d6ac190c7
commit
e1ad3bc5a5
1 changed files with 55 additions and 0 deletions
|
|
@ -491,6 +491,61 @@ class FavaClient:
|
||||||
logger.error(f"Fava connection error: {e}")
|
logger.error(f"Fava connection error: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
async def query_bql(self, query_string: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Execute arbitrary Beancount Query Language (BQL) query.
|
||||||
|
|
||||||
|
This is a general-purpose method for executing BQL queries against Fava/Beancount.
|
||||||
|
Use this for efficient aggregations, filtering, and data retrieval.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query_string: BQL query (e.g., "SELECT account, sum(position) WHERE account ~ 'User-abc'")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{
|
||||||
|
"rows": [[col1, col2, ...], ...],
|
||||||
|
"types": [{"name": "col1", "type": "str"}, ...],
|
||||||
|
"column_names": ["col1", "col2", ...]
|
||||||
|
}
|
||||||
|
|
||||||
|
Example:
|
||||||
|
result = await fava.query_bql("SELECT account, sum(position) WHERE account ~ 'User-abc'")
|
||||||
|
for row in result["rows"]:
|
||||||
|
account, balance = row
|
||||||
|
print(f"{account}: {balance}")
|
||||||
|
|
||||||
|
See:
|
||||||
|
https://beancount.github.io/docs/beancount_query_language.html
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
||||||
|
response = await client.get(
|
||||||
|
f"{self.base_url}/query",
|
||||||
|
params={"query_string": query_string}
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
# Fava returns: {"data": {"rows": [...], "types": [...]}}
|
||||||
|
data = result.get("data", {})
|
||||||
|
rows = data.get("rows", [])
|
||||||
|
types = data.get("types", [])
|
||||||
|
column_names = [t.get("name") for t in types]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"rows": rows,
|
||||||
|
"types": types,
|
||||||
|
"column_names": column_names
|
||||||
|
}
|
||||||
|
|
||||||
|
except httpx.HTTPStatusError as e:
|
||||||
|
logger.error(f"BQL query error: {e.response.status_code} - {e.response.text}")
|
||||||
|
logger.error(f"Query was: {query_string}")
|
||||||
|
raise
|
||||||
|
except httpx.RequestError as e:
|
||||||
|
logger.error(f"Fava connection error: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
async def get_account_transactions(
|
async def get_account_transactions(
|
||||||
self,
|
self,
|
||||||
account_name: str,
|
account_name: str,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue