Skip to content

Commit 262c606

Browse files
committed
introduce data aggregation routine
We now aggregate statistics data in the cleanup routine preserving only distinct entries for each date/referrer/target combination with the sum of all hits.
1 parent 89c71cd commit 262c606

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

inc/class-statify-cron.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,75 @@ public static function cleanup_data() {
3838
)
3939
);
4040

41+
// Aggregate.
42+
self::aggregate_data();
43+
4144
// Optimize DB.
4245
$wpdb->query(
4346
"OPTIMIZE TABLE `$wpdb->statify`"
4447
);
4548
}
49+
50+
/**
51+
* Aggregate data in database.
52+
*
53+
* @since 1.9
54+
*/
55+
public static function aggregate_data() {
56+
global $wpdb;
57+
58+
// Get date of last aggregation.
59+
if ( isset( self::$_options['last_aggregation'] ) ) {
60+
// Value saved, use it.
61+
$start = self::$_options['last_aggregation'];
62+
} else {
63+
// No? We need to clean up all data. Let's determine the oldest data in the database.
64+
$start = $wpdb->get_col( "SELECT MIN(`created`) FROM `$wpdb->statify`" );
65+
$start = $start[0];
66+
}
67+
68+
if ( is_null( $start ) ) {
69+
// No data available, i.e not cleaned up yet and no data in database.
70+
return;
71+
}
72+
73+
$now = new DateTime();
74+
$date = new DateTime( $start );
75+
76+
// Iterate over every day from start (inclusive) til now.
77+
while ( $date < $now ) {
78+
$agg = $wpdb->get_results(
79+
$wpdb->prepare(
80+
"SELECT `created`, `referrer`, `target`, SUM(`hits`) as `hits` FROM `$wpdb->statify` WHERE `created` = %s GROUP BY `created`, `referrer`, `target`",
81+
$date->format( 'Y-m-d' )
82+
),
83+
ARRAY_A
84+
);
85+
86+
// Remove non-aggregated data and insert aggregates within one transaction.
87+
$wpdb->query( 'START TRANSACTION' );
88+
$res = $wpdb->query(
89+
$wpdb->prepare(
90+
"DELETE FROM `$wpdb->statify` WHERE `created` = %s",
91+
$date->format( 'Y-m-d' )
92+
)
93+
);
94+
if ( false !== $res ) {
95+
foreach ( $agg as $a ) {
96+
if ( false === $wpdb->insert( $wpdb->statify, $a ) ) {
97+
$wpdb->query( 'ROLLBACK' );
98+
break;
99+
}
100+
}
101+
}
102+
$wpdb->query( 'COMMIT' );
103+
104+
// Continue with next day.
105+
$date->modify( '+1 day' );
106+
}
107+
108+
// Remember last aggregation date.
109+
self::$_options['last_aggregation'] = $now->format( 'Y-m-d' );
110+
update_option( 'statify', self::$_options );
111+
}
46112
}

tests/test-cron.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,51 @@ public function test_cronjob() {
7373
$this->assertEquals( 2, $v['count'], 'Unexpected visit count' );
7474
}
7575
}
76+
77+
/**
78+
* Test Statify Cron Job execution.
79+
*
80+
* @runInSeparateProcess Must not preserve global constant.
81+
* @preserveGlobalState disabled
82+
*/
83+
public function test_aggregation() {
84+
global $wpdb;
85+
86+
// Insert test data: 2 days with 3 and 4 distinct combinations of referrer and target.
87+
$date = new DateTime();
88+
$this->insert_test_data( $date->format( 'Y-m-d' ), '', '', 2 );
89+
$this->insert_test_data( $date->format( 'Y-m-d' ), 'https://statify.pluginkollektiv.org/', '/', 3 );
90+
$this->insert_test_data( $date->format( 'Y-m-d' ), 'https://statify.pluginkollektiv.org/', '/test/', 4 );
91+
$this->insert_test_data( $date->format( 'Y-m-d' ), 'https://pluginkollektiv.org/', '/', 5 );
92+
$date->modify( '-1 days' );
93+
$this->insert_test_data( $date->format( 'Y-m-d' ), 'https://statify.pluginkollektiv.org/', '/', 4 );
94+
$this->insert_test_data( $date->format( 'Y-m-d' ), 'https://statify.pluginkollektiv.org/', '/test/', 3 );
95+
$this->insert_test_data( $date->format( 'Y-m-d' ), 'https://pluginkollektiv.org/', '/', 2 );
96+
97+
// Get baseline.
98+
$this->assertEquals( 23, $wpdb->get_var( "SELECT COUNT(*) FROM `$wpdb->statify`" ), 'Unexpected number of entries before aggregation' );
99+
$stats = $this->get_stats();
100+
101+
// Trigger aggregation.
102+
Statify_Cron::aggregate_data();
103+
104+
// Verify results.
105+
$this->assertEquals( 7, $wpdb->get_var( "SELECT COUNT(*) FROM `$wpdb->statify`" ), 'Unexpected number of entries after aggregation' );
106+
$stats2 = $this->get_stats();
107+
$this->assertEquals( $stats, $stats2, 'Statistics data should be the same after aggregation' );
108+
// Check one single row explicitly.
109+
$this->assertEquals(
110+
3,
111+
$wpdb->get_var(
112+
$wpdb->prepare(
113+
"SELECT hits FROM `$wpdb->statify` WHERE created = %s AND referrer = %s AND target = %s",
114+
$date->format( 'Y-m-d' ),
115+
'https://statify.pluginkollektiv.org/',
116+
'/test/'
117+
)
118+
),
119+
'Unexpected hit count after aggregation'
120+
);
121+
122+
}
76123
}

tests/test-tracking.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,11 @@ function ( $data, $id ) use ( &$capture ) {
361361
$this->assertNotNull( $stats['visits'][0]['count'], 'Request not tracked' );
362362
$this->assertNotEmpty( $capture, 'Hook stativy__visit_saved has not fired' );
363363
$this->assertTrue( is_numeric( $capture['id'] ) && $capture['id'] > 0, 'unexpected entry ID' );
364-
$this->assertCount( 3, $capture['data'], 'unexpected number of data fields' );
364+
$this->assertCount( 4, $capture['data'], 'unexpected number of data fields' );
365365
$this->assertEquals( ( new DateTime() )->format( 'Y-m-d' ), $capture['data']['created'], 'unexpected creation date' );
366366
$this->assertEquals( 'https://statify.pluginkollektiv.org/', $capture['data']['referrer'], 'unexpected referrer' );
367367
$this->assertEquals( '/page', $capture['data']['target'], 'unexpected target' );
368+
$this->assertEquals( 1, $capture['data']['hits'], 'unexpected hits' );
368369
}
369370

370371
/**

0 commit comments

Comments
 (0)