fix(labrinth): fix filtering not working unless breakdown set to same field (#6608)

* fix(labrinth): return columns used in filters

fixes modrinth/code#6607

* chore(labrinth): sqlx query data update

* don't implicitly breakdown when filtering by a metric

* fix docs

---------

Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
This commit is contained in:
penguinencounter
2026-07-12 11:31:56 +02:00
committed by GitHub
co-authored by aecsocket
parent e1556a8ee3
commit 7c4df7442b
5 changed files with 120 additions and 24 deletions
@@ -97,7 +97,7 @@ If you're prepared to contribute by submitting a pull request, ensure you have m
- `cargo fmt --all` has been run.
- `cargo clippy --all-targets` has been run.
- `cargo sqlx prepare` has been run.
- `cargo sqlx prepare -- --tests` has been run.
> Note: If you encounter issues with `sqlx` saying 'no queries found' after running `cargo sqlx prepare`, you may need to ensure the installed version of `sqlx-cli` matches the current version of `sqlx` used [in labrinth](https://github.com/modrinth/labrinth/blob/master/Cargo.toml).
@@ -430,7 +430,7 @@ function getCountryFilterValue(
return undefined
}
return dataPoint.country ?? null
return 'country' in dataPoint ? (dataPoint.country ?? null) : undefined
}
function getMonetizationFilterValue(
@@ -439,6 +439,9 @@ function getMonetizationFilterValue(
if (dataPoint.metric_kind !== 'views' && dataPoint.metric_kind !== 'downloads') {
return undefined
}
if (!('monetized' in dataPoint)) {
return undefined
}
if (typeof dataPoint.monetized !== 'boolean') {
return null
}
@@ -463,7 +466,7 @@ function getDownloadSourceFilterValue(
return undefined
}
return dataPoint.user_agent ?? null
return 'user_agent' in dataPoint ? (dataPoint.user_agent ?? null) : undefined
}
function getDownloadReasonFilterValue(
@@ -473,7 +476,7 @@ function getDownloadReasonFilterValue(
return undefined
}
return dataPoint.reason ?? null
return 'reason' in dataPoint ? (dataPoint.reason ?? null) : undefined
}
function getUserIdFilterValue(
@@ -483,7 +486,7 @@ function getUserIdFilterValue(
return undefined
}
return dataPoint.user_id ?? null
return 'user_id' in dataPoint ? (dataPoint.user_id ?? null) : undefined
}
function getVersionFilterValue(
@@ -493,7 +496,7 @@ function getVersionFilterValue(
return undefined
}
return dataPoint.version_id ?? null
return 'version_id' in dataPoint ? (dataPoint.version_id ?? null) : undefined
}
function getGameVersionFilterValue(
@@ -503,7 +506,7 @@ function getGameVersionFilterValue(
return undefined
}
return dataPoint.game_version ?? null
return 'game_version' in dataPoint ? (dataPoint.game_version ?? null) : undefined
}
function getLoaderFilterValue(
@@ -513,5 +516,5 @@ function getLoaderFilterValue(
return undefined
}
return dataPoint.loader ?? null
return 'loader' in dataPoint ? (dataPoint.loader ?? null) : undefined
}
@@ -376,18 +376,39 @@ pub(crate) async fn fetch(
let use_columns = &[
("use_project_id", uses(F::ProjectId)),
("use_domain", uses(F::Domain)),
(
"use_domain",
uses(F::Domain) || !metrics.filter_by.domain.is_empty(),
),
(
"use_user_agent",
uses(F::UserAgent) || !metrics.filter_by.user_agent.is_empty(),
),
("use_version_id", uses(F::VersionId)),
(
"use_version_id",
uses(F::VersionId) || !metrics.filter_by.version_id.is_empty(),
),
("use_dependent_project_id", uses(F::DependentProjectId)),
("use_monetized", uses(F::Monetized)),
("use_country", uses(F::Country)),
("use_reason", uses(F::Reason)),
("use_game_version", uses(F::GameVersion)),
("use_loader", uses(F::Loader)),
(
"use_monetized",
uses(F::Monetized) || !metrics.filter_by.monetized.is_empty(),
),
(
"use_country",
uses(F::Country) || !metrics.filter_by.country.is_empty(),
),
(
"use_reason",
uses(F::Reason) || !metrics.filter_by.reason.is_empty(),
),
(
"use_game_version",
uses(F::GameVersion) || !metrics.filter_by.game_version.is_empty(),
),
(
"use_loader",
uses(F::Loader) || !metrics.filter_by.loader.is_empty(),
),
];
let mut query = cx
@@ -453,7 +474,7 @@ pub(crate) async fn fetch(
bucket: row.bucket,
project_id: row.project_id,
domain: uses_column("use_domain").then(|| row.domain.clone()),
user_agent: uses(F::UserAgent)
user_agent: uses_column("use_user_agent")
.then_some(normalized_source)
.flatten(),
version_id: uses_column("use_version_id").then_some(row.version_id),
@@ -505,6 +526,30 @@ pub(crate) async fn fetch(
) {
continue;
}
if !uses(F::Domain) {
key.domain = None;
}
if !uses(F::UserAgent) {
key.user_agent = None;
}
if !uses(F::VersionId) {
key.version_id = None;
}
if !uses(F::Monetized) {
key.monetized = None;
}
if !uses(F::Country) {
key.country = None;
}
if !uses(F::Reason) {
key.reason = None;
}
if !uses(F::GameVersion) {
key.game_version = None;
}
if !uses(F::Loader) {
key.loader = None;
}
*output_buckets.entry(key).or_default() += downloads;
}
@@ -184,10 +184,22 @@ pub(crate) async fn fetch(
let uses = |field| metrics.bucket_by.contains(&field);
let use_columns = &[
("use_project_id", uses(F::ProjectId)),
("use_version_id", uses(F::VersionId)),
("use_loader", uses(F::Loader)),
("use_game_version", uses(F::GameVersion)),
("use_country", uses(F::Country)),
(
"use_version_id",
uses(F::VersionId) || !metrics.filter_by.version_id.is_empty(),
),
(
"use_loader",
uses(F::Loader) || !metrics.filter_by.loader.is_empty(),
),
(
"use_game_version",
uses(F::GameVersion) || !metrics.filter_by.game_version.is_empty(),
),
(
"use_country",
uses(F::Country) || !metrics.filter_by.country.is_empty(),
),
];
let uses_column = |name| {
use_columns
@@ -265,6 +277,18 @@ pub(crate) async fn fetch(
) {
continue;
}
if !uses(F::VersionId) {
key.version_id = None;
}
if !uses(F::Loader) {
key.loader = None;
}
if !uses(F::GameVersion) {
key.game_version = None;
}
if !uses(F::Country) {
key.country = None;
}
*output_buckets.entry(key).or_default() += seconds;
}
@@ -143,10 +143,22 @@ pub(crate) async fn fetch(
let uses = |field| metrics.bucket_by.contains(&field);
let use_columns = &[
("use_project_id", uses(F::ProjectId)),
("use_domain", uses(F::Domain)),
("use_site_path", uses(F::SitePath)),
("use_monetized", uses(F::Monetized)),
("use_country", uses(F::Country)),
(
"use_domain",
uses(F::Domain) || !metrics.filter_by.domain.is_empty(),
),
(
"use_site_path",
uses(F::SitePath) || !metrics.filter_by.site_path.is_empty(),
),
(
"use_monetized",
uses(F::Monetized) || !metrics.filter_by.monetized.is_empty(),
),
(
"use_country",
uses(F::Country) || !metrics.filter_by.country.is_empty(),
),
];
let uses_column = |name| {
use_columns
@@ -211,6 +223,18 @@ pub(crate) async fn fetch(
) {
continue;
}
if !uses(F::Domain) {
key.domain = None;
}
if !uses(F::SitePath) {
key.site_path = None;
}
if !uses(F::Monetized) {
key.monetized = None;
}
if !uses(F::Country) {
key.country = None;
}
*output_buckets.entry(key).or_default() += views;
}