Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
M
Metabase
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Engineering Digital Service
Metabase
Commits
cb576c54
Commit
cb576c54
authored
7 years ago
by
Ryan Senior
Browse files
Options
Downloads
Patches
Plain Diff
Added support for multiple min/max affecting criteria clauses
parent
b1e496aa
Loading
Loading
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/metabase/query_processor/middleware/binning.clj
+40
-29
40 additions, 29 deletions
src/metabase/query_processor/middleware/binning.clj
test/metabase/query_processor/middleware/binning_test.clj
+56
-0
56 additions, 0 deletions
test/metabase/query_processor/middleware/binning_test.clj
with
96 additions
and
29 deletions
src/metabase/query_processor/middleware/binning.clj
+
40
−
29
View file @
cb576c54
...
...
@@ -4,17 +4,22 @@
[
metabase.query-processor.interface
:as
i
])
(
:import
[
metabase.query_processor.interface
BinnedField
ComparisonFilter
BetweenFilter
]))
(
defn-
update!
[
^
clojure.lang.ITransientAssociative
coll
k
f
]
(
defn-
update!
"Similar to `clojure.core/update` but works on transient maps"
[
^
clojure.lang.ITransientAssociative
coll
k
f
]
(
assoc!
coll
k
(
f
(
get
coll
k
))))
(
defn-
filter->field-map
[
mbql-filter
]
(
defn-
filter->field-map
"A bit of a stateful hack using clojure.walk/prewalk to find any
comparison or between filter. This should be replaced by a zipper
for a more functional/composable approach to this problem."
[
mbql-filter
]
(
let
[
acc
(
transient
{})]
(
clojure.
walk/prewalk
(
walk/prewalk
(
fn
[
x
]
(
when
(
or
(
instance?
BetweenFilter
x
)
(
and
(
instance?
ComparisonFilter
x
)
(
contains?
#
{
:<
:>
:<=
:>=
}
(
:filter-type
x
))))
(
when
(
or
(
instance?
BetweenFilter
x
)
(
and
(
instance?
ComparisonFilter
x
)
(
contains?
#
{
:<
:>
:<=
:>=
}
(
:filter-type
x
))))
(
update!
acc
(
get-in
x
[
:field
:field-id
])
#
(
if
(
seq
%
)
(
conj
%
x
)
[
x
])))
...
...
@@ -26,29 +31,35 @@
(
u/round-to-decimals
5
(
/
(
-
max-value
min-value
)
num-bins
)))
(
defn-
extract-bounds
[{
field-id
:field-id,
global-min
:min-value,
global-max
:max-value
}
field-filter-map
]
;; Assuming only one for now
(
let
[
bound-1
(
first
(
for
[{
:keys
[
value
filter-type
]}
(
get
field-filter-map
field-id
)
:when
(
or
(
=
:>
filter-type
)
(
=
:>=
filter-type
))]
(
:value
value
)
))
bound-2
(
first
(
for
[{
:keys
[
value
filter-type
]}
(
get
field-filter-map
field-id
)
:when
(
or
(
=
:<
filter-type
)
(
=
:<=
filter-type
))]
(
:value
value
)))
comparison-bounds
(
when
(
and
bound-1
bound-2
)
(
if
(
>
bound-1
bound-2
)
[
bound-2
bound-1
]
[
bound-1
bound-2
]))
;;Assuming either >/< or between
between-bounds
(
first
(
for
[{
:keys
[
filter-type
min-value
max-value
]}
(
get
field-filter-map
field-id
)
:when
(
=
:between
filter-type
)]
[
min-value
max-value
]))]
(
or
(
seq
comparison-bounds
)
(
seq
between-bounds
)
[
global-min
global-max
])))
(
defn-
extract-bounds
"Given query criteria, find a min/max value for the binning strategy
using the greatest user specified min value and the smallest user
specified max value. When a user specified min or max is not found,
use the global min/max for the given field."
[{
field-id
:field-id,
global-min
:min-value,
global-max
:max-value
}
field-filter-map
]
(
let
[
user-maxes
(
for
[{
:keys
[
filter-type
]
:as
query-filter
}
(
get
field-filter-map
field-id
)
:when
(
contains?
#
{
:<
:<=
:between
}
filter-type
)]
(
if
(
=
:between
filter-type
)
(
get-in
query-filter
[
:max-val
:value
])
(
get-in
query-filter
[
:value
:value
])))
user-mins
(
for
[{
:keys
[
filter-type
]
:as
query-filter
}
(
get
field-filter-map
field-id
)
:when
(
contains?
#
{
:>
:>=
:between
}
filter-type
)]
(
if
(
=
:between
filter-type
)
(
get-in
query-filter
[
:min-val
:value
])
(
get-in
query-filter
[
:value
:value
])))]
(
defn-
update-bin-width
[
breakouts
filter-field-map
]
[(
or
(
when
(
seq
user-mins
)
(
apply
max
user-mins
))
global-min
)
(
or
(
when
(
seq
user-maxes
)
(
apply
min
user-maxes
))
global-max
)]))
(
defn-
update-bin-width
"Calculates the bin width given the global min/max and user
specified crtieria that could impact that min/max. Throws an
Exception if no min/max values are found."
[
breakouts
filter-field-map
]
(
mapv
(
fn
[{
:keys
[
field
num-bins
]
:as
breakout
}]
(
if
(
instance?
BinnedField
breakout
)
(
let
[[
min-value
max-value
]
(
extract-bounds
field
filter-field-map
)]
...
...
This diff is collapsed.
Click to expand it.
test/metabase/query_processor/middleware/binning_test.clj
0 → 100644
+
56
−
0
View file @
cb576c54
(
ns
metabase.query-processor.middleware.binning-test
(
:require
[
expectations
:refer
[
expect
]]
[
metabase.query-processor.middleware.binning
:refer
:all
]
[
metabase.query-processor.expand
:as
ql
]
[
metabase.test.util
:as
tu
]))
(
tu/resolve-private-vars
metabase.query-processor.middleware.binning
filter->field-map
extract-bounds
)
(
expect
{}
(
filter->field-map
(
ql/and
(
ql/=
(
ql/field-id
1
)
10
)
(
ql/=
(
ql/field-id
2
)
10
))))
(
expect
{
1
[(
ql/<
(
ql/field-id
1
)
10
)
(
ql/>
(
ql/field-id
1
)
1
)]
2
[(
ql/>
(
ql/field-id
2
)
20
)
(
ql/<
(
ql/field-id
2
)
10
)]
3
[(
ql/between
(
ql/field-id
3
)
5
10
)]}
(
filter->field-map
(
ql/and
(
ql/<
(
ql/field-id
1
)
10
)
(
ql/>
(
ql/field-id
1
)
1
)
(
ql/>
(
ql/field-id
2
)
20
)
(
ql/<
(
ql/field-id
2
)
10
)
(
ql/between
(
ql/field-id
3
)
5
10
))))
(
expect
[
1
10
]
(
extract-bounds
{
:field-id
1
:min-value
100
:max-value
1000
}
{
1
[(
ql/>
(
ql/field-id
1
)
1
)
(
ql/<
(
ql/field-id
1
)
10
)]}))
(
expect
[
1
10
]
(
extract-bounds
{
:field-id
1
:min-value
100
:max-value
1000
}
{
1
[(
ql/between
(
ql/field-id
1
)
1
10
)]}))
(
expect
[
100
1000
]
(
extract-bounds
{
:field-id
1
:min-value
100
:max-value
1000
}
{}))
(
expect
[
500
1000
]
(
extract-bounds
{
:field-id
1
:min-value
100
:max-value
1000
}
{
1
[(
ql/>
(
ql/field-id
1
)
500
)]}))
(
expect
[
100
500
]
(
extract-bounds
{
:field-id
1
:min-value
100
:max-value
1000
}
{
1
[(
ql/<
(
ql/field-id
1
)
500
)]}))
(
expect
[
600
700
]
(
extract-bounds
{
:field-id
1
:min-value
100
:max-value
1000
}
{
1
[(
ql/>
(
ql/field-id
1
)
200
)
(
ql/<
(
ql/field-id
1
)
800
)
(
ql/between
(
ql/field-id
1
)
600
700
)]}))
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment