Skip to content

Commit

Permalink
Added sorting by entry and exit rate, improved entry and exit rate ca…
Browse files Browse the repository at this point in the history
…lculation.
  • Loading branch information
Kugelschieber committed Sep 16, 2024
1 parent cb0379e commit 41bef79
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 44 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* added storing hostname
* added reading and filtering for hostnames
* added reading visitors by weekday and hour
* added sorting by entry and exit rate
* improved entry and exit rate calculation
* fixed storing milliseconds for timestamps
* updated dependencies

Expand Down
20 changes: 20 additions & 0 deletions pkg/analyzer/filter_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ var (
Name: "entries",
}

// FieldEntryRate is a query result column.
FieldEntryRate = Field{
querySessions: `toFloat64OrDefault(entries / greatest((SELECT uniq(visitor_id, session_id)%s FROM "session"%s WHERE %s), 1))`,
queryPageViews: `toFloat64OrDefault(entries / greatest((SELECT uniq(visitor_id, session_id)%s FROM "session"%s WHERE %s), 1))`,
queryImported: `toFloat64OrDefault(entries / greatest((SELECT uniq(visitor_id, session_id)%s FROM "session"%s WHERE %s) + (SELECT sum(sessions) FROM "%s" WHERE %s), 1))`,
queryDirection: "DESC",
filterTime: true,
Name: "entry_rate",
}

// FieldExitPath is a query result column.
FieldExitPath = Field{
querySessions: "exit_path",
Expand All @@ -116,6 +126,16 @@ var (
Name: "exits",
}

// FieldExitRate is a query result column.
FieldExitRate = Field{
querySessions: `toFloat64OrDefault(exits / greatest((SELECT uniq(visitor_id, session_id)%s FROM "session"%s WHERE %s), 1))`,
queryPageViews: `toFloat64OrDefault(exits / greatest((SELECT uniq(visitor_id, session_id)%s FROM "session"%s WHERE %s), 1))`,
queryImported: `toFloat64OrDefault(exits / greatest((SELECT uniq(visitor_id, session_id)%s FROM "session"%s WHERE %s) + (SELECT sum(sessions) FROM "%s" WHERE %s), 1))`,
queryDirection: "DESC",
filterTime: true,
Name: "exit_rate",
}

// FieldVisitors is a query result column.
FieldVisitors = Field{
querySessions: "uniq(t.visitor_id)",
Expand Down
37 changes: 2 additions & 35 deletions pkg/analyzer/pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func (pages *Pages) Entry(filter *Filter) ([]model.EntryStats, error) {
FieldHostname,
FieldEntryPath,
FieldEntries,
FieldEntryRate,
}
groupBy := []Field{
FieldHostname,
Expand Down Expand Up @@ -173,13 +174,6 @@ func (pages *Pages) Entry(filter *Filter) ([]model.EntryStats, error) {
}

pathList := getPathList(stats)
totalSessions, err := pages.totalSessions(filter)

if err != nil {
return nil, err
}

totalSessionsFloat64 := float64(totalSessions)
total, err := pages.totalVisitorsSessions(filter, pathList)

if err != nil {
Expand All @@ -191,7 +185,6 @@ func (pages *Pages) Entry(filter *Filter) ([]model.EntryStats, error) {
if stats[i].Path == total[j].Path {
stats[i].Visitors = total[j].Visitors
stats[i].Sessions = total[j].Sessions
stats[i].EntryRate = float64(stats[i].Entries) / totalSessionsFloat64
break
}
}
Expand Down Expand Up @@ -243,6 +236,7 @@ func (pages *Pages) Exit(filter *Filter) ([]model.ExitStats, error) {
FieldHostname,
FieldExitPath,
FieldExits,
FieldExitRate,
}
groupBy := []Field{
FieldHostname,
Expand Down Expand Up @@ -270,13 +264,6 @@ func (pages *Pages) Exit(filter *Filter) ([]model.ExitStats, error) {
return nil, err
}

totalSessions, err := pages.totalSessions(filter)

if err != nil {
return nil, err
}

totalSessionsFloat64 := float64(totalSessions)
pathList := getPathList(stats)
total, err := pages.totalVisitorsSessions(filter, pathList)

Expand All @@ -289,13 +276,6 @@ func (pages *Pages) Exit(filter *Filter) ([]model.ExitStats, error) {
if stats[i].Path == total[j].Path {
stats[i].Visitors = total[j].Visitors
stats[i].Sessions = total[j].Sessions

if totalSessions == 0 {
stats[i].ExitRate = 1
} else {
stats[i].ExitRate = float64(stats[i].Exits) / totalSessionsFloat64
}

break
}
}
Expand Down Expand Up @@ -341,19 +321,6 @@ func (pages *Pages) Conversions(filter *Filter) (*model.ConversionsStats, error)
return stats, nil
}

func (pages *Pages) totalSessions(filter *Filter) (int, error) {
filter = pages.analyzer.getFilter(filter)
filterQuery, filterArgs := filter.buildTimeQuery()
query := fmt.Sprintf("SELECT uniq(visitor_id, session_id) FROM session %s HAVING sum(sign) > 0", filterQuery)
stats, err := pages.store.SelectTotalSessions(filter.Ctx, query, filterArgs...)

if err != nil {
return 0, err
}

return stats, nil
}

func (pages *Pages) totalVisitorsSessions(filter *Filter, paths []string) ([]model.TotalVisitorSessionStats, error) {
if len(paths) == 0 {
return []model.TotalVisitorSessionStats{}, nil
Expand Down
42 changes: 35 additions & 7 deletions pkg/analyzer/pages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,8 +1061,8 @@ func TestAnalyzer_EntryExitPages(t *testing.T) {
assert.Equal(t, "/bar", entries[1].Path)
assert.Equal(t, 8, entries[0].Entries)
assert.Equal(t, 4, entries[1].Entries)
assert.InDelta(t, 0.8888, entries[0].EntryRate, 0.001)
assert.InDelta(t, 0.4444, entries[1].EntryRate, 0.001)
assert.InDelta(t, 0.6153, entries[0].EntryRate, 0.001)
assert.InDelta(t, 0.3076, entries[1].EntryRate, 0.001)
exits, err = analyzer.Pages.Exit(&Filter{
From: util.PastDay(3),
To: util.Today(),
Expand All @@ -1076,9 +1076,9 @@ func TestAnalyzer_EntryExitPages(t *testing.T) {
assert.Equal(t, 6, exits[0].Exits)
assert.Equal(t, 5, exits[1].Exits)
assert.Equal(t, 1, exits[2].Exits)
assert.InDelta(t, 0.6666, exits[0].ExitRate, 0.001)
assert.InDelta(t, 0.5555, exits[1].ExitRate, 0.001)
assert.InDelta(t, 0.1111, exits[2].ExitRate, 0.001)
assert.InDelta(t, 0.4615, exits[0].ExitRate, 0.001)
assert.InDelta(t, 0.3846, exits[1].ExitRate, 0.001)
assert.InDelta(t, 0.0769, exits[2].ExitRate, 0.001)
entries, err = analyzer.Pages.Entry(&Filter{
From: util.PastDay(3),
To: util.Today(),
Expand All @@ -1092,7 +1092,7 @@ func TestAnalyzer_EntryExitPages(t *testing.T) {
assert.Equal(t, "/", entries[0].Path)
assert.Equal(t, "Home", entries[0].Title)
assert.Equal(t, 8, entries[0].Entries)
assert.InDelta(t, 0.8888, entries[0].EntryRate, 0.001)
assert.InDelta(t, 0.6153, entries[0].EntryRate, 0.001)
assert.Equal(t, 23, entries[0].AverageTimeSpentSeconds)
exits, err = analyzer.Pages.Exit(&Filter{
From: util.PastDay(3),
Expand All @@ -1106,7 +1106,7 @@ func TestAnalyzer_EntryExitPages(t *testing.T) {
assert.Equal(t, "/bar", exits[0].Path)
assert.Equal(t, "Bar", exits[0].Title)
assert.Equal(t, 6, exits[0].Exits)
assert.InDelta(t, 0.6666, exits[0].ExitRate, 0.001)
assert.InDelta(t, 0.4615, exits[0].ExitRate, 0.001)
}

func TestAnalyzer_EntryExitPagesSortVisitors(t *testing.T) {
Expand Down Expand Up @@ -1138,6 +1138,20 @@ func TestAnalyzer_EntryExitPagesSortVisitors(t *testing.T) {
assert.Equal(t, "/", entries[1].Path)
assert.Equal(t, 1, entries[0].Visitors)
assert.Equal(t, 2, entries[1].Visitors)
entries, err = analyzer.Pages.Entry(&Filter{Sort: []Sort{{Field: FieldEntryRate, Direction: pkg.DirectionDESC}}})
assert.NoError(t, err)
assert.Len(t, entries, 2)
assert.Equal(t, "/", entries[0].Path)
assert.Equal(t, "/foo", entries[1].Path)
assert.Equal(t, 2, entries[0].Visitors)
assert.Equal(t, 1, entries[1].Visitors)
entries, err = analyzer.Pages.Entry(&Filter{Sort: []Sort{{Field: FieldEntryRate, Direction: pkg.DirectionASC}}})
assert.NoError(t, err)
assert.Len(t, entries, 2)
assert.Equal(t, "/foo", entries[0].Path)
assert.Equal(t, "/", entries[1].Path)
assert.Equal(t, 1, entries[0].Visitors)
assert.Equal(t, 2, entries[1].Visitors)
exits, err := analyzer.Pages.Exit(&Filter{Sort: []Sort{{Field: FieldVisitors, Direction: pkg.DirectionDESC}}})
assert.NoError(t, err)
assert.Len(t, exits, 2)
Expand All @@ -1152,6 +1166,20 @@ func TestAnalyzer_EntryExitPagesSortVisitors(t *testing.T) {
assert.Equal(t, "/", exits[1].Path)
assert.Equal(t, 1, exits[0].Visitors)
assert.Equal(t, 2, exits[1].Visitors)
exits, err = analyzer.Pages.Exit(&Filter{Sort: []Sort{{Field: FieldExitRate, Direction: pkg.DirectionDESC}}})
assert.NoError(t, err)
assert.Len(t, exits, 2)
assert.Equal(t, "/", exits[0].Path)
assert.Equal(t, "/foo", exits[1].Path)
assert.Equal(t, 2, exits[0].Visitors)
assert.Equal(t, 1, exits[1].Visitors)
exits, err = analyzer.Pages.Exit(&Filter{Sort: []Sort{{Field: FieldExitRate, Direction: pkg.DirectionASC}}})
assert.NoError(t, err)
assert.Len(t, exits, 2)
assert.Equal(t, "/foo", exits[0].Path)
assert.Equal(t, "/", exits[1].Path)
assert.Equal(t, 1, exits[0].Visitors)
assert.Equal(t, 2, exits[1].Visitors)
}

func TestAnalyzer_EntryExitPagesEvents(t *testing.T) {
Expand Down
8 changes: 6 additions & 2 deletions pkg/db/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,7 @@ func (client *Client) SelectEntryStats(ctx context.Context, includeTitle bool, q
if err := rows.Scan(&result.Hostname,
&result.Path,
&result.Entries,
&result.EntryRate,
&result.Title); err != nil {
return nil, err
}
Expand All @@ -1281,7 +1282,8 @@ func (client *Client) SelectEntryStats(ctx context.Context, includeTitle bool, q

if err := rows.Scan(&result.Hostname,
&result.Path,
&result.Entries); err != nil {
&result.Entries,
&result.EntryRate); err != nil {
return nil, err
}

Expand Down Expand Up @@ -1310,6 +1312,7 @@ func (client *Client) SelectExitStats(ctx context.Context, includeTitle bool, qu
if err := rows.Scan(&result.Hostname,
&result.Path,
&result.Exits,
&result.ExitRate,
&result.Title); err != nil {
return nil, err
}
Expand All @@ -1322,7 +1325,8 @@ func (client *Client) SelectExitStats(ctx context.Context, includeTitle bool, qu

if err := rows.Scan(&result.Hostname,
&result.Path,
&result.Exits); err != nil {
&result.Exits,
&result.ExitRate); err != nil {
return nil, err
}

Expand Down

0 comments on commit 41bef79

Please sign in to comment.