| defmodule Plausible.Stats.QueryTest do |
| use Plausible.DataCase |
| alias Plausible.Stats |
| alias Plausible.Stats.{ParsedQueryParams, QueryBuilder, QueryInclude} |
|
|
| @user_id 123 |
|
|
| setup [:create_user, :create_site] |
|
|
| describe "timeseries" do |
| test "breakdown by time:minute (internal API), counts visitors and visits in all buckets their session was active in", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 00:10:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :visits, :pageviews], |
| input_date_range: {:datetime_range, ~U[2021-01-01 00:00:00Z], ~U[2021-01-01 00:10:00Z]}, |
| dimensions: ["time:minute"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01 00:00:00"], metrics: [1, 1, 1]}, |
| %{dimensions: ["2021-01-01 00:01:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:02:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:03:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:04:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:05:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:06:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:07:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:08:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:09:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 00:10:00"], metrics: [1, 1, 1]} |
| ] |
| end |
|
|
| test "breakdown by time:hour (internal API), counts visitors and visits in all buckets their session was active in", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 00:20:00]), |
| build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 00:40:00]), |
| build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 01:00:00]), |
| build(:pageview, user_id: @user_id, timestamp: ~N[2021-01-01 01:20:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :visits, :visit_duration], |
| input_date_range: {:date_range, ~D[2021-01-01], ~D[2021-01-02]}, |
| dimensions: ["time:hour"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01 00:00:00"], metrics: [1, 1, 0]}, |
| %{dimensions: ["2021-01-01 01:00:00"], metrics: [1, 1, 3600]} |
| ] |
| end |
|
|
| test "shows month to date with time labels trimmed", %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, timestamp: ~N[2021-01-15 00:00:00]), |
| build(:pageview, timestamp: ~N[2021-01-16 00:00:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :month, |
| dimensions: ["time:day"], |
| include: %QueryInclude{trim_relative_date_range: true}, |
| now: ~U[2021-01-15 12:00:00Z] |
| }) |
|
|
| %Stats.QueryResult{results: results, query: query} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01"], metrics: [1]}, |
| %{dimensions: ["2021-01-15"], metrics: [1]} |
| ] |
|
|
| assert query[:date_range] == [ |
| "2021-01-01T00:00:00Z", |
| "2021-01-15T23:59:59Z" |
| ] |
| end |
|
|
| test "visitors and visits are smeared across time:minute buckets but visit_duration is not", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 00:10:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:05:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:08:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :visits, :visit_duration, :pageviews], |
| input_date_range: {:datetime_range, ~U[2021-01-01 00:00:00Z], ~U[2021-01-01 00:30:00Z]}, |
| dimensions: ["time:minute"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01 00:00:00"], metrics: [1, 1, 0, 1]}, |
| %{dimensions: ["2021-01-01 00:01:00"], metrics: [1, 1, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:02:00"], metrics: [1, 1, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:03:00"], metrics: [1, 1, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:04:00"], metrics: [1, 1, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:05:00"], metrics: [2, 2, 0, 1]}, |
| %{dimensions: ["2021-01-01 00:06:00"], metrics: [2, 2, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:07:00"], metrics: [2, 2, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:08:00"], metrics: [2, 2, 180, 1]}, |
| %{dimensions: ["2021-01-01 00:09:00"], metrics: [1, 1, 0, 0]}, |
| %{dimensions: ["2021-01-01 00:10:00"], metrics: [1, 1, 600, 1]} |
| ] |
| end |
| end |
|
|
| describe "timeseries with comparisons" do |
| test "returns more original time range buckets than comparison buckets", |
| %{site: site} do |
| populate_stats(site, [ |
| |
| build(:pageview, user_id: 123, timestamp: ~N[2026-01-03 00:00:00]), |
| build(:pageview, user_id: 123, timestamp: ~N[2026-01-03 00:10:00]), |
| build(:pageview, timestamp: ~N[2026-01-05 00:00:00]), |
| |
| build(:pageview, timestamp: ~N[2025-12-16 00:00:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :pageviews], |
| input_date_range: {:date_range, ~D[2025-12-25], ~D[2026-01-06]}, |
| dimensions: ["time:week"], |
| include: %QueryInclude{ |
| compare: {:date_range, ~D[2025-12-12], ~D[2025-12-21]}, |
| time_labels: true, |
| time_label_result_indices: true |
| } |
| }) |
|
|
| %Stats.QueryResult{results: results, comparison_results: comparison_results, meta: meta} = |
| Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2025-12-29"], metrics: [1, 2]}, |
| %{dimensions: ["2026-01-05"], metrics: [1, 1]} |
| ] |
|
|
| assert comparison_results == [ |
| %{dimensions: ["2025-12-15"], metrics: [1, 1], change: [0, 100]} |
| ] |
|
|
| assert meta[:time_labels] == ["2025-12-25", "2025-12-29", "2026-01-05"] |
| assert meta[:time_label_result_indices] == [nil, 0, 1] |
| assert meta[:comparison_time_labels] == ["2025-12-12", "2025-12-15"] |
| assert meta[:comparison_time_label_result_indices] == [nil, 0] |
| end |
|
|
| test "can return more comparison time buckets than original time range buckets", |
| %{site: site} do |
| populate_stats(site, [ |
| |
| build(:pageview, user_id: 123, timestamp: ~N[2021-02-01 00:00:00]), |
| build(:pageview, user_id: 123, timestamp: ~N[2021-02-01 00:10:00]), |
| build(:pageview, timestamp: ~N[2021-02-01 00:00:00]), |
| |
| build(:pageview, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, timestamp: ~N[2021-01-02 00:00:00]), |
| build(:pageview, timestamp: ~N[2021-01-04 00:00:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :pageviews], |
| input_date_range: {:date_range, ~D[2021-02-01], ~D[2021-02-01]}, |
| dimensions: ["time:day"], |
| include: %QueryInclude{ |
| compare: {:date_range, ~D[2021-01-01], ~D[2021-01-05]}, |
| time_labels: true, |
| time_label_result_indices: true |
| } |
| }) |
|
|
| %Stats.QueryResult{results: results, comparison_results: comparison_results, meta: meta} = |
| Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-02-01"], metrics: [2, 3]} |
| ] |
|
|
| assert comparison_results == [ |
| %{dimensions: ["2021-01-01"], metrics: [2, 2], change: [0, 50]}, |
| %{dimensions: ["2021-01-02"], metrics: [1, 1], change: nil}, |
| %{dimensions: ["2021-01-04"], metrics: [1, 1], change: nil} |
| ] |
|
|
| assert meta[:time_labels] == ["2021-02-01"] |
|
|
| assert meta[:comparison_time_labels] == [ |
| "2021-01-01", |
| "2021-01-02", |
| "2021-01-03", |
| "2021-01-04", |
| "2021-01-05" |
| ] |
|
|
| assert meta[:time_label_result_indices] == [0] |
| assert meta[:comparison_time_label_result_indices] == [0, 1, nil, 2, nil] |
| end |
| end |
|
|
| describe "session smearing respects query date range boundaries" do |
| test "time:hour does not include buckets from outside the query range", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 23:55:00]), |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-02 00:10:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-02 23:55:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-03 00:10:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:date_range, ~D[2021-01-02], ~D[2021-01-02]}, |
| dimensions: ["time:hour"], |
| include: %QueryInclude{total_rows: true} |
| }) |
|
|
| %Stats.QueryResult{results: results, meta: meta} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-02 00:00:00"], metrics: [1]}, |
| %{dimensions: ["2021-01-02 23:00:00"], metrics: [1]} |
| ] |
|
|
| assert meta[:total_rows] == 2 |
| end |
|
|
| test "time:hour does not include buckets from outside the query range (non-UTC timezone)", |
| %{site: site} do |
| |
| site = %{site | timezone: "America/New_York"} |
|
|
| populate_stats(site, [ |
| |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 04:55:00]), |
| |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 05:10:00]), |
| |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-02 04:55:00]), |
| |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-02 05:10:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:date_range, ~D[2021-01-01], ~D[2021-01-01]}, |
| dimensions: ["time:hour"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01 00:00:00"], metrics: [1]}, |
| %{dimensions: ["2021-01-01 23:00:00"], metrics: [1]} |
| ] |
| end |
|
|
| test "time:minute does not include buckets from outside the query range", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 00:05:00]), |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 00:20:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:08:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:10:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:datetime_range, ~U[2021-01-01 00:08:00Z], ~U[2021-01-01 00:12:00Z]}, |
| dimensions: ["time:minute"], |
| include: %QueryInclude{total_rows: true} |
| }) |
|
|
| %Stats.QueryResult{results: results, meta: meta} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01 00:08:00"], metrics: [2]}, |
| %{dimensions: ["2021-01-01 00:09:00"], metrics: [2]}, |
| %{dimensions: ["2021-01-01 00:10:00"], metrics: [2]}, |
| %{dimensions: ["2021-01-01 00:11:00"], metrics: [1]}, |
| %{dimensions: ["2021-01-01 00:12:00"], metrics: [1]} |
| ] |
|
|
| assert meta[:total_rows] == 5 |
| end |
|
|
| test "time:minute does not include buckets from outside the query range (non-UTC timezone)", |
| %{site: site} do |
| |
| site = %{site | timezone: "America/New_York"} |
|
|
| populate_stats(site, [ |
| |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 04:59:00]), |
| |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 05:02:00]), |
| |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-02 04:59:00]), |
| |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-02 05:01:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :day, |
| relative_date: ~D[2021-01-01], |
| dimensions: ["time:minute"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["2021-01-01 00:00:00"], metrics: [1]}, |
| %{dimensions: ["2021-01-01 00:01:00"], metrics: [1]}, |
| %{dimensions: ["2021-01-01 00:02:00"], metrics: [1]}, |
| %{dimensions: ["2021-01-01 23:59:00"], metrics: [1]} |
| ] |
| end |
|
|
| test "time:day clamps sessions extending past the query range end into the last bucket", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-31 23:55:00]), |
| build(:pageview, user_id: 1, timestamp: ~N[2021-02-01 00:05:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:date_range, ~D[2021-01-01], ~D[2021-01-31]}, |
| dimensions: ["time:day"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| |
| assert results == [ |
| %{dimensions: ["2021-01-31"], metrics: [1]} |
| ] |
| end |
|
|
| test "time:week clamps sessions extending past the query range end into the last bucket", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-31 23:55:00]), |
| build(:pageview, user_id: 1, timestamp: ~N[2021-02-01 00:05:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:date_range, ~D[2021-01-01], ~D[2021-01-31]}, |
| dimensions: ["time:week"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| |
| |
| assert results == [ |
| %{dimensions: ["2021-01-25"], metrics: [1]} |
| ] |
| end |
|
|
| test "time:month clamps sessions extending past the query range end into the last bucket", |
| %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-02-28 23:55:00]), |
| build(:pageview, user_id: 1, timestamp: ~N[2021-03-01 00:05:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:date_range, ~D[2021-01-01], ~D[2021-02-28]}, |
| dimensions: ["time:month"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| |
| |
| assert results == [ |
| %{dimensions: ["2021-02-01"], metrics: [1]} |
| ] |
| end |
| end |
|
|
| describe "group_conversion_rate with time:minute and time:hour dimensions" do |
| test "unique conversions are not smeared when querying visitors without conversion_rate", |
| %{site: site} do |
| insert(:goal, site: site, event_name: "Signup") |
|
|
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:event, name: "Signup", user_id: 1, timestamp: ~N[2021-01-01 00:05:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:00:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: {:datetime_range, ~U[2021-01-01 00:00:00Z], ~U[2021-01-01 00:10:00Z]}, |
| dimensions: ["time:minute"], |
| filters: [[:is, "event:goal", ["Signup"]]], |
| skip_goal_existence_check: true |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert [%{dimensions: ["2021-01-01 00:05:00"], metrics: [1]}] = results |
| end |
|
|
| test "unique conversions are not smeared across all session minutes via timeSlots", |
| %{site: site} do |
| insert(:goal, site: site, event_name: "Signup") |
|
|
| populate_stats(site, [ |
| build(:pageview, user_id: 1, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:event, name: "Signup", user_id: 1, timestamp: ~N[2021-01-01 00:05:00]), |
| build(:pageview, user_id: 2, timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, user_id: 3, timestamp: ~N[2021-01-01 00:05:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :group_conversion_rate], |
| input_date_range: {:datetime_range, ~U[2021-01-01 00:00:00Z], ~U[2021-01-01 00:10:00Z]}, |
| dimensions: ["time:minute"], |
| filters: [[:is, "event:goal", ["Signup"]]], |
| skip_goal_existence_check: true |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert [%{dimensions: ["2021-01-01 00:05:00"], metrics: [1, 50.0]}] = results |
| end |
|
|
| test "conversion_rate with time:hour and session filter does not crash", |
| %{site: site} do |
| insert(:goal, site: site, event_name: "Signup") |
|
|
| populate_stats(site, [ |
| build(:pageview, user_id: 1, pathname: "/blog", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:event, name: "Signup", user_id: 1, timestamp: ~N[2021-01-01 00:05:00]), |
| build(:pageview, user_id: 2, pathname: "/blog", timestamp: ~N[2021-01-01 00:10:00]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors, :conversion_rate], |
| input_date_range: {:datetime_range, ~U[2021-01-01 00:00:00Z], ~U[2021-01-01 23:59:59Z]}, |
| dimensions: ["time:hour"], |
| filters: [ |
| [:is, "event:goal", ["Signup"]], |
| [:is, "visit:entry_page", ["/blog"]] |
| ], |
| skip_goal_existence_check: true |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert [%{dimensions: ["2021-01-01 00:00:00"], metrics: [1, 50.0]}] = results |
| end |
| end |
|
|
| describe "include.empty_metrics" do |
| test "if not asked for, no empty_metrics are returned under meta", %{site: site} do |
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :all |
| }) |
|
|
| %Stats.QueryResult{meta: meta} = Stats.query(site, query) |
|
|
| assert is_nil(meta[:empty_metrics]) |
| end |
|
|
| for {metric, expected} <- [ |
| {:visitors, 0}, |
| {:visits, 0}, |
| {:pageviews, 0}, |
| {:views_per_visit, 0.0}, |
| {:bounce_rate, 0.0}, |
| {:visit_duration, nil} |
| ] do |
| test "default: empty #{metric} is #{expected}", %{site: site} do |
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [unquote(metric)], |
| input_date_range: :all, |
| include: %QueryInclude{empty_metrics: true} |
| }) |
|
|
| %Stats.QueryResult{meta: meta} = Stats.query(site, query) |
|
|
| assert meta[:empty_metrics] == [unquote(expected)] |
| end |
| end |
|
|
| for {metric, expected} <- [ |
| {:time_on_page, nil}, |
| {:scroll_depth, nil} |
| ] do |
| test "page filter: empty #{metric} is #{expected}", %{site: site} do |
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [unquote(metric)], |
| input_date_range: :all, |
| include: %QueryInclude{empty_metrics: true}, |
| filters: [[:is, "event:page", ["/"]]] |
| }) |
|
|
| %Stats.QueryResult{meta: meta} = Stats.query(site, query) |
|
|
| assert meta[:empty_metrics] == [unquote(expected)] |
| end |
| end |
|
|
| for {metric, expected} <- [ |
| {:events, 0}, |
| {:group_conversion_rate, 0.0} |
| ] do |
| test "goal filter: empty #{metric} is #{expected}", %{site: site} do |
| insert(:goal, site: site, event_name: "Signup") |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [unquote(metric)], |
| input_date_range: :all, |
| include: %QueryInclude{empty_metrics: true}, |
| filters: [[:is, "event:goal", ["Signup"]]] |
| }) |
|
|
| %Stats.QueryResult{meta: meta} = Stats.query(site, query) |
|
|
| assert meta[:empty_metrics] == [unquote(expected)] |
| end |
| end |
|
|
| @tag :ee_only |
| test "goal filter: empty revenue metrics", %{site: site} do |
| insert(:goal, site: site, event_name: "Purchase", currency: "EUR") |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:average_revenue, :total_revenue], |
| input_date_range: :all, |
| include: %QueryInclude{empty_metrics: true}, |
| filters: [[:is, "event:goal", ["Purchase"]]] |
| }) |
|
|
| %Stats.QueryResult{meta: meta} = Stats.query(site, query) |
|
|
| assert meta[:empty_metrics] == [ |
| %{currency: :EUR, long: "€0.00", short: "€0.0", value: 0.0}, |
| %{currency: :EUR, long: "€0.00", short: "€0.0", value: 0.0} |
| ] |
| end |
|
|
| test "is ignored when event:goal dimension used", %{site: site} do |
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :all, |
| include: %QueryInclude{empty_metrics: true}, |
| dimensions: ["event:goal"] |
| }) |
|
|
| %Stats.QueryResult{meta: meta} = Stats.query(site, query) |
|
|
| assert is_nil(meta[:empty_metrics]) |
| end |
| end |
|
|
| describe "event:prop_key dimension" do |
| test "returns distinct prop keys with visitor counts", %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, "meta.key": ["author"], "meta.value": ["uku"]), |
| build(:pageview, "meta.key": ["author"], "meta.value": ["marko"]), |
| build(:pageview, "meta.key": ["logged_in"], "meta.value": ["true"]), |
| build(:pageview) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :all, |
| dimensions: ["event:prop_key"] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert [ |
| %{dimensions: ["author"], metrics: [2]}, |
| %{dimensions: ["logged_in"], metrics: [1]} |
| ] = Enum.sort_by(results, &hd(&1.dimensions)) |
| end |
|
|
| test "supports order_by", %{site: site} do |
| populate_stats(site, [ |
| build(:pageview, "meta.key": ["author"], "meta.value": ["a"]), |
| build(:pageview, "meta.key": ["author"], "meta.value": ["b"]), |
| build(:pageview, "meta.key": ["author"], "meta.value": ["b"]), |
| build(:pageview, "meta.key": ["logged_in"], "meta.value": ["true"]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :all, |
| dimensions: ["event:prop_key"], |
| order_by: [{"event:prop_key", :desc}] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [ |
| %{dimensions: ["logged_in"], metrics: [1]}, |
| %{dimensions: ["author"], metrics: [3]} |
| ] |
| end |
|
|
| test "works combined with a filter", %{site: site} do |
| populate_stats(site, [ |
| build(:event, name: "purchase", "meta.key": ["author"], "meta.value": ["uku"]), |
| build(:pageview, "meta.key": ["author"], "meta.value": ["marko"]), |
| build(:pageview, "meta.key": ["logged_in"], "meta.value": ["true"]) |
| ]) |
|
|
| {:ok, query} = |
| QueryBuilder.build(site, %ParsedQueryParams{ |
| metrics: [:visitors], |
| input_date_range: :all, |
| dimensions: ["event:prop_key"], |
| filters: [[:is, "event:name", ["purchase"]]] |
| }) |
|
|
| %Stats.QueryResult{results: results} = Stats.query(site, query) |
|
|
| assert results == [%{dimensions: ["author"], metrics: [1]}] |
| end |
| end |
| end |
|
|