Query¶
-
class
lsst.daf.butler.registry.queries.Query(*, graph: lsst.daf.butler.core.dimensions._graph.DimensionGraph, whereRegion: Optional[lsst.sphgeom._sphgeom.Region, None], managers: lsst.daf.butler.registry.queries._structs.RegistryManagers, doomed_by: Iterable[str] = ())¶ Bases:
abc.ABCAn abstract base class for queries that return some combination of
DatasetRefandDataCoordinateobjects.Parameters: - graph :
DimensionGraph Object describing the dimensions included in the query.
- whereRegion :
lsst.sphgeom.Region, optional Region that all region columns in all returned rows must overlap.
- managers :
RegistryManagers A struct containing the registry manager instances used by the query system.
- doomed_by :
Iterable[str], optional A list of messages (appropriate for e.g. logging or exceptions) that explain why the query is known to return no results even before it is executed. Queries with a non-empty list will never be executed.
Notes
The
Queryhierarchy abstracts over the database/SQL representation of a particular set of data IDs or datasets. It is expected to be used as a backend for other objects that provide more natural interfaces for one or both of these, not as part of a public interface to query results.Attributes Summary
datasetTypeThe DatasetTypeof datasets returned by this query, orNoneif there are no dataset results (DatasetTypeorNone).spatialAn iterator over the dimension element columns used in post-query filtering of spatial overlaps ( Iterator[DimensionElement]).sqlA SQLAlchemy object representing the full query ( sqlalchemy.sql.FromClauseorNone).Methods Summary
any(db, *, region, None] = None, execute, exact)Test whether this query returns any results. count(db, *, region, None] = None, exact)Count the number of rows this query would return. explain_no_results(db, *, region, …)Return human-readable messages that may help explain why the query yields no results. extractDataId(row, *, graph, records, …)Extract a data ID from a result row. extractDatasetRef(row, dataId, records, …)Extract a DatasetReffrom a result row.extractDimensionsTuple(row, dimensions)Extract a tuple of data ID values from a result row. getDatasetColumns()Return the columns for the datasets returned by this query. getDimensionColumn(name)Return the query column that contains the primary key value for the dimension with the given name. getRegionColumn(name)Return a region column for one of the dimension elements iterated over by spatial.isUnique()Return Trueif this query’s rows are guaranteed to be unique, andFalseotherwise.makeBuilder(summary)Return a QueryBuilderthat can be used to construct a newQuerythat is joined to (and hence constrained by) this one.materialize(db)Execute this query and insert its results into a temporary table. rows(db, *, region)Execute the query and yield result rows, applying predicate.subset(*, graph, None] = None, datasets, unique)Return a new Querywhose columns and/or rows are (mostly) subset of this one’s.Attributes Documentation
-
datasetType¶ The
DatasetTypeof datasets returned by this query, orNoneif there are no dataset results (DatasetTypeorNone).
-
spatial¶ An iterator over the dimension element columns used in post-query filtering of spatial overlaps (
Iterator[DimensionElement]).Notes
This property is intended primarily as a hook for subclasses to implement and the ABC to call in order to provide higher-level functionality; code that uses
Queryobjects (but does not implement one) should usually not have to access this property.
-
sql¶ A SQLAlchemy object representing the full query (
sqlalchemy.sql.FromClauseorNone).This is
Nonein the special case where the query has no columns, and only one logical row.
Methods Documentation
-
any(db: lsst.daf.butler.registry.interfaces._database.Database, *, region: Optional[lsst.sphgeom._sphgeom.Region, None] = None, execute: bool = True, exact: bool = True) → bool¶ Test whether this query returns any results.
Parameters: - db :
Database Object managing the database connection.
- region :
sphgeom.Region, optional A region that any result-row regions must overlap in order to be yielded. If not provided, this will be
self.whereRegion, if that exists.- execute :
bool, optional If
True, execute at least aLIMIT 1query if it cannot be determined prior to execution that the query would return no rows.- exact :
bool, optional If
True, run the full query and perform post-query filtering if needed, until at least one result row is found. IfFalse, the returned result does not account for post-query filtering, and hence may beTrueeven when all result rows would be filtered out.
Returns: - db :
-
count(db: lsst.daf.butler.registry.interfaces._database.Database, *, region: Optional[lsst.sphgeom._sphgeom.Region, None] = None, exact: bool = True) → int¶ Count the number of rows this query would return.
Parameters: - db :
Database Object managing the database connection.
- region :
sphgeom.Region, optional A region that any result-row regions must overlap in order to be yielded. If not provided, this will be
self.whereRegion, if that exists.- exact :
bool, optional If
True, run the full query and perform post-query filtering if needed to account for that filtering in the count. IfFalse, the result may be an upper bound.
Returns: - count :
int The number of rows the query would return, or an upper bound if
exact=False.
Notes
This counts the number of rows returned, not the number of unique rows returned, so even with
exact=Trueit may provide only an upper bound on the number of deduplicated result rows.- db :
-
explain_no_results(db: lsst.daf.butler.registry.interfaces._database.Database, *, region: Optional[lsst.sphgeom._sphgeom.Region, None] = None, followup: bool = True) → Iterator[str]¶ Return human-readable messages that may help explain why the query yields no results.
Parameters: - db :
Database Object managing the database connection.
- region :
sphgeom.Region, optional A region that any result-row regions must overlap in order to be yielded. If not provided, this will be
self.whereRegion, if that exists.- followup :
bool, optional If
True(default) perform inexpensive follow-up queries if no diagnostics are available from query generation alone.
Returns: - messages :
Iterator[str] String messages that describe reasons the query might not yield any results.
Notes
Messages related to post-query filtering are only available if
rows,any, orcountwas already called with the same region (withexact=Truefor the latter two).- db :
-
extractDataId(row: Optional[sqlalchemy.engine.RowProxy], *, graph: Optional[DimensionGraph] = None, records: Optional[Mapping[str, Mapping[tuple, DimensionRecord]]] = None) → DataCoordinate¶ Extract a data ID from a result row.
Parameters: - row :
sqlalchemy.engine.RowProxyorNone A result row from a SQLAlchemy SELECT query, or
Noneto indicate the row from anEmptyQuery.- graph :
DimensionGraph, optional The dimensions the returned data ID should identify. If not provided, this will be all dimensions in
QuerySummary.requested.- records :
Mapping[str,Mapping[tuple,DimensionRecord] ] Nested mapping containing records to attach to the returned
DataCoordinate, for whichhasRecordswill returnTrue. If provided, outer keys must include all dimension element names ingraph, and inner keys should be tuples of dimension primary key values in the same order aselement.graph.required. If not provided,DataCoordinate.hasRecordswill returnFalseon the returned object.
Returns: - dataId :
DataCoordinate A data ID that identifies all required and implied dimensions. If
records is not None, this is havehasRecords()returnTrue.
- row :
-
extractDatasetRef(row: sqlalchemy.engine.RowProxy, dataId: Optional[DataCoordinate] = None, records: Optional[Mapping[str, Mapping[tuple, DimensionRecord]]] = None) → DatasetRef¶ Extract a
DatasetReffrom a result row.Parameters: - row :
sqlalchemy.engine.RowProxy A result row from a SQLAlchemy SELECT query.
- dataId :
DataCoordinate Data ID to attach to the
DatasetRef. A minimal (i.e. base class)DataCoordinateis constructed fromrowifNone.- records :
Mapping[str,Mapping[tuple,DimensionRecord] ] Records to use to return an
ExpandedDataCoordinate. If provided, outer keys must include all dimension element names ingraph, and inner keys should be tuples of dimension primary key values in the same order aselement.graph.required.
Returns: - ref :
DatasetRef Reference to the dataset; guaranteed to have
DatasetRef.idnotNone.
- row :
-
extractDimensionsTuple(row: Optional[sqlalchemy.engine.RowProxy], dimensions: Iterable[Dimension]) → tuple¶ Extract a tuple of data ID values from a result row.
Parameters: Returns: - values :
tuple A tuple of dimension primary key values.
- values :
-
getDatasetColumns() → Optional[lsst.daf.butler.registry.queries._structs.DatasetQueryColumns, None]¶ Return the columns for the datasets returned by this query.
Returns: - columns :
DatasetQueryColumnsorNone Struct containing SQLAlchemy representations of the result columns for a dataset.
Notes
This method is intended primarily as a hook for subclasses to implement and the ABC to call in order to provide higher-level functionality; code that uses
Queryobjects (but does not implement one) should usually not have to call this method.- columns :
-
getDimensionColumn(name: str) → sqlalchemy.sql.elements.ColumnElement¶ Return the query column that contains the primary key value for the dimension with the given name.
Parameters: - name :
str Name of the dimension.
Returns: - column :
sqlalchemy.sql.ColumnElement. SQLAlchemy object representing a column in the query.
Notes
This method is intended primarily as a hook for subclasses to implement and the ABC to call in order to provide higher-level functionality; code that uses
Queryobjects (but does not implement one) should usually not have to call this method.- name :
-
getRegionColumn(name: str) → sqlalchemy.sql.elements.ColumnElement¶ Return a region column for one of the dimension elements iterated over by
spatial.Parameters: - name :
str Name of the element.
Returns: - column :
sqlalchemy.sql.ColumnElement SQLAlchemy representing a result column in the query.
Notes
This method is intended primarily as a hook for subclasses to implement and the ABC to call in order to provide higher-level functionality; code that uses
Queryobjects (but does not implement one) should usually not have to call this method.- name :
-
isUnique() → bool¶ Return
Trueif this query’s rows are guaranteed to be unique, andFalseotherwise.If this query has dataset results (
datasetTypeis notNone), uniqueness applies to theDatasetRefinstances returned byextractDatasetReffrom the result ofrows. If it does not have dataset results, uniqueness applies to theDataCoordinateinstances returned byextractDataId.
-
makeBuilder(summary: Optional[QuerySummary] = None) → QueryBuilder¶ Return a
QueryBuilderthat can be used to construct a newQuerythat is joined to (and hence constrained by) this one.Parameters: - summary :
QuerySummary, optional A
QuerySummaryinstance that specifies the dimensions and any additional constraints to include in the new query being constructed, orNoneto use the dimensions ofselfwith no additional constraints.
- summary :
-
materialize(db: lsst.daf.butler.registry.interfaces._database.Database) → ContextManager[lsst.daf.butler.registry.queries._query.Query]¶ Execute this query and insert its results into a temporary table.
Parameters: - db :
Database Database engine to execute the query against.
Returns: - context :
typing.ContextManager[MaterializedQuery] A context manager that ensures the temporary table is created and populated in
__enter__(returning aMaterializedQueryobject backed by that table), and dropped in__exit__. Ifselfis already aMaterializedQuery,__enter__may just returnselfand__exit__may do nothing (reflecting the fact that an outer context manager should already take care of everything else).
- db :
-
rows(db: Database, *, region: Optional[Region] = None) → Iterator[Optional[sqlalchemy.engine.RowProxy]]¶ Execute the query and yield result rows, applying
predicate.Parameters: - db :
Database Object managing the database connection.
- region :
sphgeom.Region, optional A region that any result-row regions must overlap in order to be yielded. If not provided, this will be
self.whereRegion, if that exists.
Yields: - db :
-
subset(*, graph: Optional[lsst.daf.butler.core.dimensions._graph.DimensionGraph, None] = None, datasets: bool = True, unique: bool = False) → lsst.daf.butler.registry.queries._query.Query¶ Return a new
Querywhose columns and/or rows are (mostly) subset of this one’s.Parameters: - graph :
DimensionGraph, optional Dimensions to include in the new
Querybeing constructed. IfNone(default),self.graphis used.- datasets :
bool, optional Whether the new
Queryshould include dataset results. Defaults toTrue, but is ignored ifselfdoes not include dataset results.- unique :
bool, optional Whether the new
Queryshould guarantee unique results (this may come with a performance penalty).
Returns: - query :
Query A query object corresponding to the given inputs. May be
selfif no changes were requested.
Notes
The way spatial overlaps are handled at present makes it impossible to fully guarantee in general that the new query’s rows are a subset of this one’s while also returning unique rows. That’s because the database is only capable of performing approximate, conservative overlaps via the common skypix system; we defer actual region overlap operations to per-result-row Python logic. But including the region columns necessary to do that postprocessing in the query makes it impossible to do a SELECT DISTINCT on the user-visible dimensions of the query. For example, consider starting with a query with dimensions (instrument, skymap, visit, tract). That involves a spatial join between visit and tract, and we include the region columns from both tables in the results in order to only actually yield result rows (see
predicateandrows) where the regions in those two columns overlap. If the user then wants to subset to just (skymap, tract) with unique results, we have two unpalatable options:- we can do a SELECT DISTINCT with just the skymap and tract columns in the SELECT clause, dropping all detailed overlap information and including some tracts that did not actually overlap any of the visits in the original query (but were regarded as _possibly_ overlapping via the coarser, common-skypix relationships);
- we can include the tract and visit region columns in the query, and continue to filter out the non-overlapping pairs, but completely disregard the user’s request for unique tracts.
This interface specifies that implementations must do the former, as that’s what makes things efficient in our most important use case (
QuantumGraphgeneration inpipe_base). We may be able to improve this situation in the future by putting exact overlap information in the database, either by using built-in (but engine-specific) spatial database functionality or (more likely) switching to a scheme in which pairwise dimension spatial relationships are explicitly precomputed (for e.g. combinations of instruments and skymaps).- graph :
- graph :