Source code for aiida_restapi.routers.querybuilder
"""Declaration of FastAPI router for AiiDA's QueryBuilder."""
from __future__ import annotations
import typing as t
from aiida import orm
from aiida.cmdline.utils.decorators import with_dbenv
from fastapi import APIRouter, Query, Request
from aiida_restapi.common.exceptions import QueryBuilderException
from aiida_restapi.jsonapi.adapters import JsonApiAdapter as JsonApi
from aiida_restapi.jsonapi.models import errors
from aiida_restapi.jsonapi.models.base import JsonApiResourceDocument
from aiida_restapi.jsonapi.responses import JsonApiResponse
from aiida_restapi.models.querybuilder import QueryBuilderDict
read_router = APIRouter(prefix='/querybuilder')
[docs]
@read_router.post(
'',
response_class=JsonApiResponse,
response_model=JsonApiResourceDocument,
response_model_exclude_none=True,
responses={
422: {
'model': t.Union[errors.RequestValidationError, errors.QueryBuilderError],
'description': 'Validation Error | Query Builder Error',
},
},
)
@with_dbenv()
async def query_builder(
request: Request,
query: QueryBuilderDict,
flat: t.Annotated[
bool,
Query(description='Whether to return results flat.'),
] = False,
full: t.Annotated[
bool,
Query(description='Whether to return full results (minimal=False).'),
] = False,
) -> dict[str, t.Any]:
"""Execute a QueryBuilder query based on the provided dictionary."""
query_dict = query.model_dump()
limit = query_dict.pop('limit', 10)
offset = query_dict.get('offset', 0)
try:
qb = orm.QueryBuilder.from_dict(query_dict)
total = qb.count()
qb.limit(limit)
results = qb.all(flat=flat)
except Exception as exception:
raise QueryBuilderException(str(exception)) from exception
if flat:
parsed = _to_resource_result(results, minimal=not full)
else:
parsed = [_to_resource_result(result, minimal=not full) for result in results]
result = {
'query_id': 'qb-result',
'results': parsed,
}
return JsonApi.resource(
request,
result,
resource_identity='query_id',
resource_type='qb-results',
meta={
'total': total,
'page': (offset // limit) + 1,
'page_size': limit,
},
)
[docs]
def _to_resource_result(raw: list[t.Any], minimal: bool = True) -> list[t.Any]:
"""Convert raw QueryBuilder results to JSON:API serializable format.
:param raw: The raw results from QueryBuilder.
:type raw: list[t.Any]
:param minimal: Whether to serialize entities in minimal form.
:type minimal: bool
:return: The parsed results.
:rtype: list[t.Any]
"""
parsed: list[t.Any] = []
for item in raw:
if isinstance(item, orm.Entity):
parsed.append(item.serialize(minimal=minimal))
else:
parsed.append(item)
return parsed