Skip to content

chore: remove tuple queries#833

Open
cdc-as81 wants to merge 7 commits intomainfrom
cdc-as81-remove-tuple-queries
Open

chore: remove tuple queries#833
cdc-as81 wants to merge 7 commits intomainfrom
cdc-as81-remove-tuple-queries

Conversation

@cdc-as81
Copy link
Copy Markdown
Collaborator

Summary

This PR introduces entity-scoped query/property-bundle construction with with!(Entity, ...) and Entity while preserving compatibility with the legacy tuple-based query API.

What Changed

  • Added and documented with!(Entity, ...) as the preferred public constructor for:
    • entity initialization bundles passed to add_entity(...)
    • entity-scoped queries
  • Kept EntityPropertyTuple<E, T> as the public wrapper for entity-scoped property bundles
  • Moved tuple query behavior behind an internal QueryTuple<E> trait in query_impls.rs
  • Added Query::is_empty_query() so whole-population queries can be handled explicitly
  • Made entity ZSTs implement Query<E> and PropertyList<E> so callers can use Person / with!(Person) for whole-population operations
  • Updated query/count/sample code paths to use is_empty_query() instead of special-casing ()
  • Updated docs and tests to use the entity-scoped forms consistently

Backward Compatibility

Legacy query forms are still supported:

  • Bare query tuples like (Age(42),) and (Age(42), RiskCategory::High) continue to work
  • Empty tuple queries () are still supported where type inference is available or the entity type is specified explicitly

This keeps older code working while allowing newer call sites to use the clearer with!(Entity, ...) form.

Why

The previous API relied heavily on raw tuples and () for queries, which made entity scope implicit. This change makes the public API more explicit and readable:

  • with!(Person, Age(42)) is clearly a Person query
  • with!(Person) and Person clearly represent whole-population queries
  • Tuple-specific machinery remains internal, while compatibility is preserved

Testing

Added and updated tests to cover:

  • with!(Entity) empty-query behavior
  • with!(Entity, ...) single- and multi-property queries
  • Direct use of EntityPropertyTuple
  • Continued support for legacy tuple queries
  • Continued support for legacy empty tuple whole-population queries

@cdc-as81 cdc-as81 changed the title chore: make entity queries entity-scoped with with!(Entity, ...) chore: remove tuple queries Mar 30, 2026
@cdc-as81 cdc-as81 linked an issue Mar 30, 2026 that may be closed by this pull request
@github-actions
Copy link
Copy Markdown

Benchmark Results

Hyperfine

Command Mean [ms] Min [ms] Max [ms] Relative
large_sir::baseline 2.9 ± 0.1 2.8 3.1 1.00
large_sir::entities 12.7 ± 0.5 12.1 15.0 4.40 ± 0.20

Criterion

Regressions (slower)
Group Bench Param Change CI Lower CI Upper
indexing query_people_indexed_multi-property_entities 26.802% 25.721% 28.149%
indexing query_people_single_indexed_property_entities 24.936% 23.695% 26.285%
indexing query_people_multiple_individually_indexed_properties_entities 14.514% 14.186% 14.894%
indexing with_query_results_indexed_multi-property_entities 9.259% 8.570% 9.947%
counts single_property_unindexed_entities 8.868% 6.601% 11.256%
large_dataset bench_query_population_property_entities 8.294% 6.924% 9.803%
counts concrete_plus_derived_unindexed_entities 7.752% 5.445% 10.364%
sample_entity sample_entity_single_property_unindexed 1000 6.793% 4.038% 9.653%
indexing query_people_count_indexed_multi-property_entities 3.995% 3.736% 4.277%
counts multi_property_unindexed_entities 3.957% 2.314% 5.732%
sampling sampling_single_unindexed_concrete_plus_derived_entities 3.862% 3.756% 3.964%
indexing query_people_count_multiple_individually_indexed_properties_enti 2.508% 2.345% 2.663%
sampling sampling_multiple_unindexed_entities 2.490% 2.091% 2.902%
large_dataset bench_query_population_multi_indexed_entities 2.328% 1.299% 3.616%
sampling sampling_multiple_known_length_entities 2.242% 1.784% 2.593%
sampling count_and_sampling_single_unindexed_concrete_plus_derived_entiti 2.139% 1.993% 2.328%
sample_entity sample_entity_multi_property_indexed 100000 1.967% 1.479% 2.376%
sample_entity sample_entity_multi_property_indexed 1000 1.890% 1.474% 2.435%
sample_entity sample_entity_whole_population 1000 1.663% 1.125% 2.212%
Improvements (faster)
Group Bench Param Change CI Lower CI Upper
large_dataset bench_match_entity -6.439% -6.791% -6.141%
examples example-basic-infection -6.227% -6.662% -5.827%
large_dataset bench_query_population_derived_property_entities -4.754% -5.094% -4.451%
sampling sampling_single_l_reservoir_entities -3.900% -4.036% -3.772%
sample_entity sample_entity_single_property_indexed 10000 -3.502% -3.683% -3.274%
algorithm_benches algorithm_sampling_multiple_known_length -3.190% -3.737% -2.691%
counts reindex_after_adding_more_entities -2.290% -2.700% -1.905%
sample_entity sample_entity_single_property_indexed 100000 -2.231% -2.695% -1.819%
examples example-births-deaths -2.034% -2.288% -1.794%
sample_entity sample_entity_single_property_unindexed 100000 -1.799% -2.197% -1.434%
Unchanged / inconclusive (CI crosses 0%)
Group Bench Param Change CI Lower CI Upper
sample_entity sample_entity_single_property_unindexed 10000 2.219% 0.640% 3.941%
large_dataset bench_filter_indexed_entity -2.015% -13.857% 10.669%
large_dataset bench_filter_unindexed_entity 1.865% -3.123% 6.821%
sample_entity sample_entity_multi_property_indexed 10000 1.481% 0.846% 2.145%
algorithm_benches algorithm_sampling_multiple_l_reservoir -1.480% -1.884% -0.981%
sample_entity sample_entity_single_property_indexed 1000 -1.223% -1.588% -0.891%
indexing with_query_results_multiple_individually_indexed_properties_enti 1.190% 0.897% 1.519%
sampling sampling_single_known_length_entities -1.137% -1.462% -0.425%
counts multi_property_indexed_entities 1.072% 0.544% 1.448%
counts index_after_adding_entities 0.802% 0.572% 1.061%
indexing with_query_results_single_indexed_property_entities -0.665% -0.845% -0.476%
large_dataset bench_query_population_indexed_property_entities -0.582% -0.781% -0.365%
sample_entity sample_entity_whole_population 10000 -0.478% -1.828% 0.411%
indexing query_people_count_single_indexed_property_entities -0.476% -1.178% 0.043%
sampling count_and_sampling_single_known_length_entities -0.402% -0.999% 0.193%
algorithm_benches algorithm_sampling_single_rand_reservoir -0.351% -0.822% 0.161%
counts single_property_indexed_entities 0.284% -0.110% 0.564%
sample_entity sample_entity_whole_population 100000 0.189% -0.055% 0.437%
algorithm_benches algorithm_sampling_single_known_length -0.164% -0.589% 0.120%
sampling sampling_multiple_l_reservoir_entities -0.076% -0.197% 0.048%
algorithm_benches algorithm_sampling_single_l_reservoir 0.050% -0.269% 0.398%
large_dataset bench_query_population_multi_unindexed_entities 0.046% -0.218% 0.331%
sampling sampling_single_unindexed_entities 0.032% -0.027% 0.095%

github-actions bot added a commit that referenced this pull request Mar 30, 2026
Copy link
Copy Markdown
Collaborator

@RobertJacobsonCDC RobertJacobsonCDC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes all examples and tests to use the with! macro consistently for

  • Query instances (query_entity_count, with_query_results, match_entity, sample_entity)
  • PropertyList instances (add_entity)

It also introduces the private trait QueryTuple, which mirrors Query, though it's not clear what its purpose is. QueryTuple is implemented for tuples up to size 20.

What this PR doesn't do is remove tuple queries. Query is still implemented for () and for tuples of properties up to length 20. I think the intention of the issue was that we'd remove the Query impl for all tuples and require client code to use the with! macro (and thus EntityPropertyTuple).

Because you already have a blanket impl<E: Entity, T: QueryTuple<E>> Query<E> for EntityPropertyTuple<E, T>, you can achieve this by just removing the implementation

            impl<
                E: Entity,
                #(
                    T~N : Property<E>,
                )*
            > Query<E> for (
                #(
                    T~N,
                )*
            ) {...}

from the impl_query! macro definition. Then QueryTuple<E> would make sense: it allows EntityPropertyTuple<E, T> to implement Query without the tuple T implementing Query.

We have a blanket implementation of PropertyList for EntityPropertyTuple<E, T> whenever T: PropertyList. We could also require users to use the with! macro for property lists (adding new entities), in which case we would remove the implementation of PropertyList for tuples up to size 20 as well. We could use the same hypothetical mechanism as for tuples: introduce a private PropertyListTuple trait that mirrors PropertyList and provide a blanket impl<E: Entity, T: PropertyListTuple<E>> PropertyList<E> for EntityPropertyTuple<E, T> that delegates to PropertyListTuple<E>.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

Benchmark Results

Hyperfine

Command Mean [ms] Min [ms] Max [ms] Relative
large_sir::baseline 2.8 ± 0.1 2.8 3.1 1.00
large_sir::entities 13.3 ± 0.8 12.9 16.2 4.71 ± 0.29

Criterion

Regressions (slower)
Group Bench Param Change CI Lower CI Upper
counts multi_property_unindexed_entities 14.890% 13.536% 16.143%
sampling sampling_single_l_reservoir_entities 10.648% 8.923% 12.717%
indexing with_query_results_single_indexed_property_entities 9.136% 8.771% 9.580%
large_dataset bench_query_population_multi_unindexed_entities 7.658% 5.790% 9.624%
large_dataset bench_query_population_property_entities 7.650% 4.347% 11.129%
sampling sampling_single_unindexed_concrete_plus_derived_entities 6.603% 6.421% 6.792%
sample_entity sample_entity_single_property_indexed 1000 5.487% 5.172% 5.806%
sample_entity sample_entity_single_property_unindexed 1000 3.410% 2.803% 3.976%
counts reindex_after_adding_more_entities 2.902% 2.533% 3.504%
indexing query_people_count_single_indexed_property_entities 2.848% 2.234% 3.262%
indexing with_query_results_multiple_individually_indexed_properties_enti 2.643% 2.125% 2.985%
sample_entity sample_entity_single_property_indexed 10000 2.624% 1.827% 3.325%
examples example-births-deaths 2.141% 1.593% 2.668%
sampling sampling_single_unindexed_entities 2.013% 1.917% 2.107%
sample_entity sample_entity_single_property_indexed 100000 1.741% 1.345% 2.058%
sampling count_and_sampling_single_unindexed_concrete_plus_derived_entiti 1.516% 1.339% 1.709%
counts index_after_adding_entities 1.170% 1.043% 1.296%
Improvements (faster)
Group Bench Param Change CI Lower CI Upper
sample_entity sample_entity_single_property_unindexed 10000 -8.918% -9.276% -8.596%
sampling sampling_multiple_known_length_entities -5.766% -8.397% -3.319%
indexing query_people_single_indexed_property_entities -2.615% -2.811% -2.466%
sample_entity sample_entity_multi_property_indexed 100000 -2.573% -3.489% -1.817%
large_dataset bench_match_entity -2.247% -2.624% -1.672%
algorithm_benches algorithm_sampling_multiple_known_length -1.934% -2.140% -1.744%
sample_entity sample_entity_multi_property_indexed 1000 -1.648% -2.064% -1.241%
Unchanged / inconclusive (CI crosses 0%)
Group Bench Param Change CI Lower CI Upper
sample_entity sample_entity_single_property_unindexed 100000 -1.985% -2.962% -0.720%
examples example-basic-infection -1.764% -5.103% 3.927%
sampling sampling_single_known_length_entities -1.488% -2.213% -0.644%
indexing with_query_results_indexed_multi-property_entities 1.452% 0.919% 1.945%
indexing query_people_indexed_multi-property_entities 1.250% 0.660% 1.764%
indexing query_people_count_multiple_individually_indexed_properties_enti 1.167% 0.520% 1.722%
indexing query_people_count_indexed_multi-property_entities 1.083% 0.677% 1.363%
sampling sampling_multiple_unindexed_entities 1.052% 0.976% 1.139%
algorithm_benches algorithm_sampling_multiple_l_reservoir -0.981% -1.370% -0.658%
sampling sampling_multiple_l_reservoir_entities -0.921% -3.842% 1.387%
sample_entity sample_entity_whole_population 10000 -0.837% -1.088% -0.608%
sample_entity sample_entity_multi_property_indexed 10000 -0.757% -1.108% -0.518%
sample_entity sample_entity_whole_population 1000 -0.721% -0.956% -0.495%
counts single_property_indexed_entities 0.614% 0.052% 1.126%
large_dataset bench_filter_unindexed_entity 0.589% -2.394% 3.819%
sampling count_and_sampling_single_known_length_entities 0.550% -0.220% 1.469%
large_dataset bench_query_population_multi_indexed_entities 0.514% 0.265% 0.799%
indexing query_people_multiple_individually_indexed_properties_entities -0.497% -1.129% 0.300%
sample_entity sample_entity_whole_population 100000 -0.472% -0.749% -0.179%
algorithm_benches algorithm_sampling_single_known_length 0.441% 0.129% 0.863%
counts multi_property_indexed_entities -0.191% -0.641% 0.195%
counts single_property_unindexed_entities -0.112% -0.508% 0.463%
large_dataset bench_query_population_indexed_property_entities 0.104% -0.244% 0.666%
large_dataset bench_filter_indexed_entity -0.102% -6.987% 8.264%
algorithm_benches algorithm_sampling_single_l_reservoir 0.022% -0.082% 0.113%
counts concrete_plus_derived_unindexed_entities -0.016% -0.304% 0.305%
algorithm_benches algorithm_sampling_single_rand_reservoir -0.003% -0.241% 0.183%
large_dataset bench_query_population_derived_property_entities 0.002% -0.968% 0.875%

github-actions bot added a commit that referenced this pull request Apr 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

Benchmark Results

Hyperfine

Command Mean [ms] Min [ms] Max [ms] Relative
large_sir::baseline 2.9 ± 0.1 2.8 3.1 1.00
large_sir::entities 13.9 ± 0.2 13.7 14.6 4.85 ± 0.12

Criterion

Regressions (slower)
Group Bench Param Change CI Lower CI Upper
examples example-basic-infection 20.021% 19.706% 20.356%
indexing with_query_results_single_indexed_property_entities 19.406% 18.695% 20.070%
indexing query_people_count_single_indexed_property_entities 19.400% 18.634% 20.270%
examples example-births-deaths 17.741% 16.253% 19.092%
sampling sampling_single_known_length_entities 10.037% 9.576% 10.430%
indexing with_query_results_multiple_individually_indexed_properties_enti 6.459% 6.114% 7.000%
indexing query_people_indexed_multi-property_entities 6.206% 5.991% 6.474%
indexing with_query_results_indexed_multi-property_entities 5.361% 4.299% 6.381%
counts multi_property_indexed_entities 4.710% 4.169% 5.136%
counts index_after_adding_entities 3.677% 3.236% 4.041%
counts reindex_after_adding_more_entities 3.404% 3.008% 3.896%
large_dataset bench_match_entity 1.525% 1.087% 1.990%
Improvements (faster)
Group Bench Param Change CI Lower CI Upper
sampling count_and_sampling_single_unindexed_concrete_plus_derived_entiti -32.012% -32.048% -31.973%
sampling sampling_single_unindexed_concrete_plus_derived_entities -25.435% -26.292% -24.538%
large_dataset bench_query_population_multi_unindexed_entities -13.650% -15.022% -12.231%
indexing query_people_single_indexed_property_entities -6.370% -7.525% -5.103%
sampling sampling_single_l_reservoir_entities -6.171% -7.140% -5.497%
sample_entity sample_entity_whole_population 100000 -5.828% -6.634% -5.154%
indexing query_people_count_indexed_multi-property_entities -5.624% -5.828% -5.409%
sample_entity sample_entity_whole_population 10000 -5.084% -5.781% -4.579%
sample_entity sample_entity_whole_population 1000 -4.451% -4.838% -3.995%
large_dataset bench_query_population_derived_property_entities -3.895% -4.251% -3.428%
indexing query_people_count_multiple_individually_indexed_properties_enti -3.093% -3.368% -2.859%
algorithm_benches algorithm_sampling_multiple_known_length -2.609% -2.791% -2.413%
sampling count_and_sampling_single_known_length_entities -2.366% -2.663% -2.074%
sampling sampling_multiple_l_reservoir_entities -2.069% -2.145% -1.984%
algorithm_benches algorithm_sampling_multiple_l_reservoir -1.678% -2.232% -1.277%
sample_entity sample_entity_single_property_unindexed 100000 -1.560% -1.894% -1.219%
Unchanged / inconclusive (CI crosses 0%)
Group Bench Param Change CI Lower CI Upper
counts single_property_unindexed_entities 2.292% 0.793% 4.018%
counts multi_property_unindexed_entities 1.832% 0.225% 3.593%
sample_entity sample_entity_single_property_indexed 100000 -1.233% -2.035% -0.502%
sample_entity sample_entity_single_property_indexed 10000 -1.065% -1.663% -0.586%
large_dataset bench_query_population_indexed_property_entities -0.953% -1.382% -0.391%
sampling sampling_multiple_known_length_entities 0.887% 0.648% 1.108%
counts single_property_indexed_entities -0.797% -1.629% -0.068%
large_dataset bench_filter_unindexed_entity 0.775% -2.204% 3.848%
sample_entity sample_entity_single_property_unindexed 10000 0.747% 0.244% 1.111%
algorithm_benches algorithm_sampling_single_known_length -0.742% -1.691% -0.020%
algorithm_benches algorithm_sampling_single_rand_reservoir -0.727% -1.246% -0.348%
large_dataset bench_query_population_multi_indexed_entities 0.703% 0.202% 1.186%
counts concrete_plus_derived_unindexed_entities 0.488% -0.272% 1.353%
algorithm_benches algorithm_sampling_single_l_reservoir -0.466% -0.782% -0.146%
sample_entity sample_entity_multi_property_indexed 10000 -0.437% -1.406% 0.409%
sampling sampling_single_unindexed_entities -0.417% -0.894% -0.054%
sampling sampling_multiple_unindexed_entities 0.295% -0.021% 0.735%
sample_entity sample_entity_single_property_indexed 1000 -0.249% -0.875% 0.605%
sample_entity sample_entity_multi_property_indexed 100000 -0.180% -1.044% 0.649%
sample_entity sample_entity_single_property_unindexed 1000 -0.131% -0.506% 0.331%
indexing query_people_multiple_individually_indexed_properties_entities 0.096% -0.123% 0.308%
sample_entity sample_entity_multi_property_indexed 1000 -0.084% -1.044% 0.795%
large_dataset bench_filter_indexed_entity 0.022% -7.159% 8.392%
large_dataset bench_query_population_property_entities 0.008% -0.456% 0.553%

github-actions bot added a commit that referenced this pull request Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up: remove tuple queries

3 participants