use crate::{
ArcGISGeometry, FeatureQueryParams, FeatureServiceClient, FeatureSet, GeometryType, LayerId,
ObjectId, ResponseFormat, Result, SpatialRel,
};
use tracing::instrument;
pub struct QueryBuilder<'a> {
client: &'a FeatureServiceClient<'a>,
layer_id: LayerId,
params: FeatureQueryParams,
}
impl<'a> QueryBuilder<'a> {
#[instrument(skip(client))]
pub(crate) fn new(client: &'a FeatureServiceClient<'a>, layer_id: LayerId) -> Self {
tracing::debug!(layer_id = %layer_id, "Creating QueryBuilder");
Self {
client,
layer_id,
params: FeatureQueryParams::default(),
}
}
pub fn where_clause(mut self, clause: impl Into<String>) -> Self {
self.params.set_where_clause(clause.into());
self
}
pub fn out_fields(mut self, fields: &[&str]) -> Self {
self.params
.set_out_fields(Some(fields.iter().map(|s| s.to_string()).collect()));
self
}
pub fn return_geometry(mut self, return_geom: bool) -> Self {
self.params.set_return_geometry(return_geom);
self
}
pub fn format(mut self, format: ResponseFormat) -> Self {
self.params.set_format(format);
self
}
pub fn pbf(self) -> Self {
self.format(ResponseFormat::Pbf)
}
pub fn json(self) -> Self {
self.format(ResponseFormat::Json)
}
pub fn geojson(self) -> Self {
self.format(ResponseFormat::GeoJson)
}
pub fn spatial_filter(
mut self,
geometry: ArcGISGeometry,
geometry_type: GeometryType,
spatial_rel: SpatialRel,
) -> Self {
self.params.set_geometry(Some(geometry));
self.params.set_geometry_type(Some(geometry_type));
self.params.set_spatial_rel(Some(spatial_rel));
self
}
pub fn limit(mut self, count: u32) -> Self {
self.params.set_result_record_count(Some(count));
self
}
pub fn offset(mut self, offset: u32) -> Self {
self.params.set_result_offset(Some(offset));
self
}
pub fn object_ids(mut self, ids: &[ObjectId]) -> Self {
self.params.set_object_ids(Some(ids.to_vec()));
self
}
pub fn distinct(mut self, distinct: bool) -> Self {
self.params.set_return_distinct_values(Some(distinct));
self
}
pub fn ids_only(mut self, ids_only: bool) -> Self {
self.params.set_return_ids_only(Some(ids_only));
self
}
pub fn count_only(mut self, count_only: bool) -> Self {
self.params.set_return_count_only(Some(count_only));
self
}
pub fn order_by(mut self, fields: &[&str]) -> Self {
self.params
.set_order_by_fields(Some(fields.iter().map(|s| s.to_string()).collect()));
self
}
pub fn group_by(mut self, fields: &[&str]) -> Self {
self.params
.set_group_by_fields(Some(fields.iter().map(|s| s.to_string()).collect()));
self
}
pub fn statistics(mut self, stats: Vec<crate::StatisticDefinition>) -> Self {
self.params.set_out_statistics(Some(stats));
self
}
pub fn having(mut self, clause: impl Into<String>) -> Self {
self.params.set_having(Some(clause.into()));
self
}
pub fn out_sr(mut self, wkid: i32) -> Self {
self.params.set_out_sr(Some(wkid));
self
}
#[instrument(skip(self), fields(layer_id = %self.layer_id))]
pub async fn execute(self) -> Result<FeatureSet> {
tracing::debug!("Executing single-page query");
self.client
.query_with_params(self.layer_id, self.params)
.await
}
#[instrument(skip(self), fields(layer_id = %self.layer_id))]
pub async fn execute_all(mut self) -> Result<FeatureSet> {
tracing::debug!("Executing auto-paginated query");
let mut all_features = Vec::new();
let mut offset = 0u32;
let page_size = (*self.params.result_record_count()).unwrap_or(1000);
let mut geometry_type = None;
loop {
self.params.set_result_offset(Some(offset));
self.params.set_result_record_count(Some(page_size));
tracing::debug!(
offset = offset,
page_size = page_size,
"Fetching page of results"
);
let mut page = self
.client
.query_with_params(self.layer_id, self.params.clone())
.await?;
if geometry_type.is_none() {
geometry_type = *page.geometry_type();
}
let feature_count = page.features().len();
tracing::debug!(
feature_count = feature_count,
exceeded_limit = page.exceeded_transfer_limit(),
"Page retrieved"
);
all_features.append(page.features_mut());
if feature_count == 0 || !*page.exceeded_transfer_limit() {
tracing::debug!(
total_features = all_features.len(),
"Auto-pagination complete"
);
break;
}
offset += page_size;
}
Ok(FeatureSet::new(geometry_type, all_features, None, false))
}
}