1
0
قرینه از https://github.com/matomo-org/matomo.git synced 2025-08-22 15:07:44 +00:00
Files
matomo/tests/PHPUnit/Integration/SegmentTest.php
Michal Kleiner d99a5c1a68 Use inner join when log_link_visit_action and log_visit tables are joined (#23424)
* Use inner join when link visit action and visit tables are joined and limit to cases when no extra table or join information is provided
* Update expected query in test
2025-07-22 09:40:15 +12:00

2768 خطوط
134 KiB
PHP

<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Tests\Integration;
use Exception;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Columns\Dimension;
use Piwik\Columns\DimensionMetricFactory;
use Piwik\Columns\MetricsList;
use Piwik\Common;
use Piwik\Config;
use Piwik\Date;
use Piwik\Db;
use Piwik\DbHelper;
use Piwik\Piwik;
use Piwik\Plugin\ArchivedMetric;
use Piwik\Plugins\SegmentEditor\API;
use Piwik\Segment;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Tracker\Action;
use Piwik\Tracker\LogTable;
use Piwik\Tracker\TableLogAction;
use Piwik\Plugins\SegmentEditor\API as SegmentEditorApi;
/**
* @group Core
* @group Segment
*/
class SegmentTest extends IntegrationTestCase
{
private $exampleSegment = 'visitCount>=1';
public function setUp(): void
{
parent::setUp();
// setup the access layer (required in Segment constructor testing if anonymous is allowed to use segments)
FakeAccess::$superUser = true;
Fixture::createWebsite('2015-01-01 00:00:00');
Config::getInstance()->General['enable_browser_archiving_triggering'] = 1;
self::$fixture->getTestEnvironment()->overrideConfig('General', 'enable_browser_archiving_triggering', 1);
self::$fixture->getTestEnvironment()->save();
}
public function testGetHashReturnsCorrectHashWhenDefinitionIsFromGetStringFromSegmentTableDefinition()
{
// definition is encoded as it would be in the URL
$idSegment = API::getInstance()->add('test segment', 'pageUrl%3D%3Dhttps%25253A%25252F%25252Fserenity.org%25252Fparticipate%25252F');
$segmentInfo = API::getInstance()->get($idSegment);
$segment = new Segment($segmentInfo['definition'], []);
$hash = $segment->getHash();
$this->assertEquals($segmentInfo['hash'], $hash);
$segmentStringFromObject = $segment->getOriginalString();
$segment2 = new Segment($segmentStringFromObject, []);
$hash = $segment2->getHash();
$this->assertEquals($segmentInfo['hash'], $hash);
}
public static function removeExtraWhiteSpaces($valueToFilter)
{
if (is_array($valueToFilter)) {
return array_map([self::class, 'removeExtraWhiteSpaces'], $valueToFilter);
} else {
$result = trim(preg_replace('/[\s]+/', ' ', $valueToFilter));
$result = preg_replace('/\s*([()])\s*/', '$1', $result);
return $result;
}
}
public function getCommonTestData()
{
$encodedComplexValue = urlencode(urlencode('s#2&#--_*+?# #5"\'&<>.22,3'));
return array(
// Normal segment
array('countryCode==France', array(
'where' => ' log_visit.location_country = ? ',
'bind' => array('France'))),
// unescape the comma please
array('countryCode==a\,==', array(
'where' => ' log_visit.location_country = ? ',
'bind' => array('a,=='))),
// AND, with 2 values rewrites
array('countryCode==a;visitorType!=returning;visitorType==new', array(
'where' => ' log_visit.location_country = ? AND ( log_visit.visitor_returning IS NULL OR log_visit.visitor_returning <> ? ) AND log_visit.visitor_returning = ? ',
'bind' => array('a', '1', '0'))),
// OR, with 2 value rewrites
array('referrerType==search,referrerType==direct', array(
'where' => ' (log_visit.referer_type = ? OR log_visit.referer_type = ? )',
'bind' => array(Common::REFERRER_TYPE_SEARCH_ENGINE,
Common::REFERRER_TYPE_DIRECT_ENTRY))),
// IS NOT NULL
array('browserCode==ff;referrerKeyword!=', array(
'where' => ' log_visit.config_browser_name = ? AND ( log_visit.referer_keyword IS NOT NULL AND log_visit.referer_keyword <> \'\' AND log_visit.referer_keyword <> \'0\' ) ',
'bind' => array('ff')
)),
array('referrerKeyword!=,browserCode==ff', array(
'where' => ' (( log_visit.referer_keyword IS NOT NULL AND log_visit.referer_keyword <> \'\' AND log_visit.referer_keyword <> \'0\' ) OR log_visit.config_browser_name = ? )',
'bind' => array('ff')
)),
// IS NULL
array('browserCode==ff;referrerKeyword==', array(
'where' => ' log_visit.config_browser_name = ? AND ( log_visit.referer_keyword IS NULL OR log_visit.referer_keyword = \'\' OR log_visit.referer_keyword = \'0\' ) ',
'bind' => array('ff')
)),
array('referrerKeyword==,browserCode==ff', array(
'where' => ' (( log_visit.referer_keyword IS NULL OR log_visit.referer_keyword = \'\' OR log_visit.referer_keyword = \'0\' ) OR log_visit.config_browser_name = ? )',
'bind' => array('ff')
)),
array(urlencode('browserCode!=' . $encodedComplexValue . ',browserCode==' . $encodedComplexValue . ';browserCode!=' . $encodedComplexValue), [
'where' => ' (( log_visit.config_browser_name IS NULL OR log_visit.config_browser_name <> ? ) OR log_visit.config_browser_name = ?) AND ( log_visit.config_browser_name IS NULL OR log_visit.config_browser_name <> ? ) ',
'bind' => [
's#2&#--_*+?# #5"\'&<>.22,3',
's#2&#--_*+?# #5"\'&<>.22,3',
's#2&#--_*+?# #5"\'&<>.22,3',
],
])
);
}
/**
* @dataProvider getCommonTestData
*/
public function testCommon($segment, $expected)
{
$select = 'log_visit.idvisit';
$from = 'log_visit';
$expected = array(
'sql' => '
SELECT
log_visit.idvisit
FROM
' . Common::prefixTable('log_visit') . ' AS log_visit
WHERE
' . $expected['where'],
'bind' => $expected['bind']
);
$segment = new Segment($segment, $idSites = array());
$sql = $segment->getSelectQuery($select, $from, false);
$this->assertQueryDoesNotFail($sql);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($sql));
// calling twice should give same results
$sql = $segment->getSelectQuery($select, array($from));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($sql));
$this->assertEquals(32, strlen($segment->getHash()));
}
/**
* @return iterable<string, array{string, string, array{where: string, bind: string}}>
*/
public function getCommonSubqueryTestData(): iterable
{
$encodedValueOr = urlencode(urlencode('a,b'));
$encodedValueAnd = urlencode(urlencode('a;b'));
$escapedValueOr = urlencode(urlencode('a\,b'));
$escapedValueAnd = urlencode(urlencode('a\;b'));
$segmentFrom = '2020-02-02 02:00:00';
$whereSingle = '(log_visit.idvisit NOT IN(SELECT log_visit.idvisit FROM log_visit AS log_visit LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit WHERE(log_visit.visit_last_action_time >= ?)AND(log_link_visit_action.idaction_name = ?)))';
$whereMultiAnd = '(log_visit.idvisit NOT IN(SELECT log_visit.idvisit FROM log_visit AS log_visit LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit WHERE(log_visit.visit_last_action_time >= ?)AND((log_link_visit_action.idaction_name = ? OR log_link_visit_action.idaction_name = ?))))';
$whereMultiOr = '(log_visit.idvisit NOT IN(SELECT log_visit.idvisit FROM log_visit AS log_visit LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit WHERE(log_visit.visit_last_action_time >= ?)AND(log_link_visit_action.idaction_name = ? AND log_link_visit_action.idaction_name = ?)))';
yield 'normal segment' => [
'pageTitle!=a',
$segmentFrom,
[
'where' => $whereSingle,
'bind' => [$segmentFrom, '1'],
],
];
yield 'segment with AND in value' => [
'pageTitle!=' . $encodedValueAnd,
$segmentFrom,
[
'where' => $whereSingle,
'bind' => [$segmentFrom, '3'],
],
];
yield 'segment with AND in value, already escaped' => [
'pageTitle!=' . $escapedValueAnd,
$segmentFrom,
[
'where' => $whereSingle,
'bind' => [$segmentFrom, '3'],
],
];
yield 'segment with OR in value' => [
'pageTitle!=' . $encodedValueOr,
$segmentFrom,
[
'where' => $whereSingle,
'bind' => [$segmentFrom, '4'],
],
];
yield 'segment with OR in value, already escaped' => [
'pageTitle!=' . $escapedValueOr,
$segmentFrom,
[
'where' => $whereSingle,
'bind' => [$segmentFrom, '4'],
],
];
yield 'segment with two values, AND operator' => [
'pageTitle!=a;pageTitle!=b',
$segmentFrom,
[
'where' => $whereMultiAnd,
'bind' => [$segmentFrom, '1', '2'],
],
];
yield 'segment with two values, OR operator' => [
'pageTitle!=a,pageTitle!=b',
$segmentFrom,
[
'where' => $whereMultiOr,
'bind' => [$segmentFrom, '1', '2'],
],
];
yield 'mixed operator in value and two segments' => [
'pageTitle!=' . $encodedValueAnd . ';pageTitle!=' . $encodedValueOr,
$segmentFrom,
[
'where' => $whereMultiAnd,
'bind' => [$segmentFrom, '3', '4'],
],
];
yield 'mixed operator in value and two segments, already escaped' => [
'pageTitle!=' . $escapedValueAnd . ',pageTitle!=' . $escapedValueOr,
$segmentFrom,
[
'where' => $whereMultiOr,
'bind' => [$segmentFrom, '3', '4'],
],
];
}
/**
* @dataProvider getCommonSubqueryTestData
*
* @param array{where: string, bind: string} $expected
*/
public function testCommonSubquery(string $segment, string $segmentFrom, array $expected): void
{
$this->insertPageUrlAsAction('a', 'idaction_name', Action::TYPE_PAGE_TITLE);
$this->insertPageUrlAsAction('b', 'idaction_name', Action::TYPE_PAGE_TITLE);
$this->insertPageUrlAsAction('a;b', 'idaction_name', Action::TYPE_PAGE_TITLE);
$this->insertPageUrlAsAction('a,b', 'idaction_name', Action::TYPE_PAGE_TITLE);
$select = 'log_visit.idvisit';
$from = 'log_visit';
$expected = array(
'sql' => '
SELECT
log_visit.idvisit
FROM
' . Common::prefixTable('log_visit') . ' AS log_visit
WHERE
' . $expected['where'],
'bind' => $expected['bind']
);
$segment = new Segment($segment, $idSites = array(), Date::factory($segmentFrom));
$sql = $segment->getSelectQuery($select, $from, false);
$this->assertQueryDoesNotFail($sql);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($sql));
// calling twice should give same results
$sql = $segment->getSelectQuery($select, array($from));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($sql));
$this->assertEquals(32, strlen($segment->getHash()));
}
public function testGetSelectQueryWhenNoJoin()
{
$select = '*';
$from = 'log_visit';
$where = 'idsite = ?';
$bind = array(1);
$segment = 'deviceBrand==Apple;visitorType==new';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
*
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
WHERE
( idsite = ? )
AND
( log_visit.config_device_brand = ? AND log_visit.visitor_returning = ? )",
"bind" => array(1, 'AP', 0));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinVisitOnLogLinkVisitAction()
{
$select = '*';
$from = 'log_link_visit_action';
$where = 'log_link_visit_action.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test;visitorType==new';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
*
FROM
" . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
INNER JOIN " . Common::prefixTable('log_visit') . " AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
WHERE
( log_link_visit_action.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? AND log_visit.visitor_returning = ? )",
"bind" => array(1, 'Test', 0));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinActionOnVisit()
{
$select = 'sum(log_visit.visit_total_actions) as nb_actions, max(log_visit.visit_total_actions) as max_actions, sum(log_visit.visit_total_time) as sum_visit_length';
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test;visitorType==new';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
sum(log_inner.visit_total_actions) as nb_actions, max(log_inner.visit_total_actions) as max_actions, sum(log_inner.visit_total_time) as sum_visit_length
FROM
(
SELECT
log_visit.visit_total_actions,
log_visit.visit_total_time
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? AND log_visit.visitor_returning = ? )
GROUP BY log_visit.idvisit
ORDER BY NULL
) AS log_inner",
"bind" => array(1, 'Test', 0));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinConversionOnLogLinkVisitAction()
{
$select = '*';
$from = 'log_link_visit_action';
$where = 'log_link_visit_action.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test;visitConvertedGoalId==1;siteSearchCount==5';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
*
FROM
" . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
LEFT JOIN " . Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idvisit = log_link_visit_action.idvisit
WHERE
( log_link_visit_action.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? AND log_conversion.idgoal = ? AND log_link_visit_action.search_count = ? )",
"bind" => array(1, 'Test', 1, 5));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinActionOnConversion()
{
$select = '*';
$from = 'log_conversion';
$where = 'log_conversion.idvisit = ?';
$bind = array(1);
$segment = 'visitConvertedGoalId!=2;siteSearchCategory==Test;visitConvertedGoalId==1';
$segment = new Segment($segment, $idSites = array(), Date::factory('2020-02-02 02:00:00'));
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$logVisit = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logConversionTable = Common::prefixTable('log_conversion');
$expected = array(
"sql" => "
SELECT *
FROM $logConversionTable AS log_conversion
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action
ON log_link_visit_action.idvisit = log_conversion.idvisit
LEFT JOIN $logVisit AS log_visit ON log_visit.idvisit = log_conversion.idvisit
WHERE (log_conversion.idvisit = ?)
AND (log_link_visit_action.search_cat = ?
AND log_conversion.idgoal = ?
AND (
log_visit.idvisit NOT IN (
SELECT log_visit.idvisit
FROM $logVisit AS log_visit
LEFT JOIN log_conversion AS log_conversion
ON log_conversion.idvisit = log_visit.idvisit
WHERE (log_visit.visit_last_action_time >= ?) AND (log_conversion.idgoal = ?)
)
)
)",
"bind" => array(1, 'Test', 1, '2020-02-02 02:00:00', 2));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenSegmentHasComplexSqlExpression()
{
$select = '*';
$from = 'log_conversion';
$where = '';
$bind = [];
$segment = 'customSegment==2';
$segment = new Segment($segment, $idSites = []);
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = [
'sql' => '
SELECT
*
FROM ' . Common::prefixTable('log_conversion') . ' AS log_conversion
LEFT JOIN ' . Common::prefixTable('log_visit') . ' AS log_visit ON log_visit.idvisit = log_conversion.idvisit
WHERE (UNIX_TIMESTAMP(log_visit.visit_first_action_time) - log_visit.visitor_seconds_since_first) = ? ',
'bind' => [2],
];
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinConversionOnVisit()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
$segment = 'visitConvertedGoalId==1';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
log_inner.*
FROM
(
SELECT
log_visit.*
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
( log_conversion.idgoal = ? )
GROUP BY log_visit.idvisit
ORDER BY NULL
) AS log_inner",
"bind" => array(1, 1));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinConversionOnly()
{
$select = 'log_conversion.*';
$from = 'log_conversion';
$where = 'log_conversion.idvisit = ?';
$bind = array(1);
$segment = 'visitConvertedGoalId==1';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
log_conversion.*
FROM
" . Common::prefixTable('log_conversion') . " AS log_conversion
WHERE
( log_conversion.idvisit = ? )
AND
( log_conversion.idgoal = ? )",
"bind" => array(1, 1));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinVisitOnConversion()
{
$select = '*';
$from = 'log_conversion';
$where = 'log_conversion.idvisit = ?';
$bind = array(1);
$segment = 'visitConvertedGoalId==1,visitServerHour==12';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
*
FROM
" . Common::prefixTable('log_conversion') . " AS log_conversion
LEFT JOIN " . Common::prefixTable('log_visit') . " AS log_visit ON log_visit.idvisit = log_conversion.idvisit
WHERE
( log_conversion.idvisit = ? )
AND
( (log_conversion.idgoal = ? OR HOUR(log_visit.visit_last_action_time) = ? ))",
"bind" => array(1, 1, 12));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinLogLinkVisitActionOnActionOnVisitWithSameTableAlias()
{
$actionType = 3;
$idSite = 1;
$select = 'log_link_visit_action.custom_dimension_1,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`';
$from = array(
'log_link_visit_action',
array('table' => 'log_visit', 'joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'),
array('table' => 'log_action', 'joinOn' => 'log_link_visit_action.idaction_url = log_action.idaction'),
'log_visit'
);
$where = 'log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$logVisitTable = Common::prefixTable('log_visit');
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$expected = array(
"sql" => "
SELECT log_link_visit_action.custom_dimension_1,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`
FROM $logLinkVisitActionTable AS log_link_visit_action
LEFT JOIN $logVisitTable AS log_visit
ON log_visit.idvisit = log_link_visit_action.idvisit
LEFT JOIN $logActionTable AS log_action
ON log_link_visit_action.idaction_url = log_action.idaction
WHERE ( log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ? )
AND ( log_action.type = ? )",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoiningManyCustomTablesItShouldKeepTheOrderAsDefined()
{
$actionType = 3;
$idSite = 1;
$select = 'log_link_visit_action.custom_dimension_1,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`';
$from = array(
'log_link_visit_action',
'log_visit',
array(
'table' => 'log_link_visit_action',
'tableAlias' => 'log_link_visit_action_foo',
'joinOn' => 'log_link_visit_action.idvisit = log_link_visit_action_foo.idvisit',
),
array(
'table' => 'log_action',
'tableAlias' => 'log_action_foo',
'joinOn' => 'log_link_visit_action_foo.idaction_url = log_action_foo.idaction',
),
array(
'table' => 'log_link_visit_action',
'tableAlias' => 'log_link_visit_action_bar',
'joinOn' => "log_link_visit_action.idvisit = log_link_visit_action_bar.idvisit"
),
array(
'table' => 'log_action',
'tableAlias' => 'log_action_bar',
'joinOn' => "log_link_visit_action_bar.idaction_url = log_action_bar.idaction"
),
array(
'table' => 'log_link_visit_action',
'tableAlias' => 'log_link_visit_action_baz',
'joinOn' => "log_link_visit_action.idvisit = log_link_visit_action_baz.idvisit"
),
array(
'table' => 'log_action',
'tableAlias' => 'log_action_baz',
'joinOn' => "log_link_visit_action_baz.idaction_url = log_action_baz.idaction"
),
'log_action',
);
$where = 'log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logVisitTable = Common::prefixTable('log_visit');
$expected = array(
"sql" => "
SELECT log_link_visit_action.custom_dimension_1,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`
FROM $logLinkVisitActionTable AS log_link_visit_action
LEFT JOIN $logActionTable AS log_action ON log_link_visit_action.idaction_url = log_action.idaction
LEFT JOIN $logVisitTable AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action_foo ON log_link_visit_action.idvisit = log_link_visit_action_foo.idvisit
LEFT JOIN $logActionTable AS log_action_foo ON log_link_visit_action_foo.idaction_url = log_action_foo.idaction
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action_bar ON log_link_visit_action.idvisit = log_link_visit_action_bar.idvisit
LEFT JOIN $logActionTable AS log_action_bar ON log_link_visit_action_bar.idaction_url = log_action_bar.idaction
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action_baz ON log_link_visit_action.idvisit = log_link_visit_action_baz.idvisit
LEFT JOIN $logActionTable AS log_action_baz ON log_link_visit_action_baz.idaction_url = log_action_baz.idaction
WHERE ( log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ? )
AND ( log_action.type = ? )",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinLogLinkVisitActionOnActionOnVisitWithNoTableAliasButDifferentJoin()
{
$actionType = 3;
$idSite = 1;
$select = 'log_link_visit_action.custom_dimension_1,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`';
$from = array(
'log_link_visit_action',
array('table' => 'log_visit', 'joinOn' => 'log_visit.idvisit = log_link_visit_action.idvisit'),
array('table' => 'log_action', 'joinOn' => 'log_link_visit_action.idaction_name = log_action.idaction')
);
$where = 'log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$logVisitTable = Common::prefixTable('log_visit');
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$expected = array(
"sql" => "
SELECT log_link_visit_action.custom_dimension_1,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`
FROM $logLinkVisitActionTable AS log_link_visit_action
LEFT JOIN $logVisitTable AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
LEFT JOIN $logActionTable AS log_action ON log_link_visit_action.idaction_name = log_action.idaction
WHERE ( log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ? )
AND ( log_action.type = ? )",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
/**
* visit is joined on action, then conversion is joined
* make sure that conversion is joined on action not visit
*/
public function testGetSelectQueryWhenJoinVisitAndConversionOnLogLinkVisitAction()
{
$select = '*';
$from = 'log_link_visit_action';
$where = false;
$bind = array();
$segment = 'visitServerHour==12;visitConvertedGoalId==1';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
*
FROM
" . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
LEFT JOIN " . Common::prefixTable('log_visit') . " AS log_visit ON log_visit.idvisit = log_link_visit_action.idvisit
LEFT JOIN " . Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idvisit = log_link_visit_action.idvisit
WHERE
HOUR(log_visit.visit_last_action_time) = ? AND log_conversion.idgoal = ? ",
"bind" => array(12, 1));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
/**
* join conversion on visit, then actions
* make sure actions are joined before conversions
*/
public function testGetSelectQueryWhenJoinConversionAndActionOnVisitAndPageUrlSet()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'visitConvertedGoalId==1;visitServerHour==12;siteSearchCategory==Test;pageUrl!=';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = array(
"sql" => "
SELECT
log_inner.*
FROM (
SELECT
log_visit.*
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN " . Common::prefixTable('log_conversion') . " AS log_conversion ON log_conversion.idvisit = log_visit.idvisit
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE
log_conversion.idgoal = ? AND HOUR(log_visit.visit_last_action_time) = ? AND log_link_visit_action.search_cat = ?
AND ((
log_action_segment_log_link_visit_actionidaction_url.name IS NOT NULL
AND log_action_segment_log_link_visit_actionidaction_url.name <> ''
AND log_action_segment_log_link_visit_actionidaction_url.name <> '0' )
AND log_action_segment_log_link_visit_actionidaction_url.type = '1')
GROUP BY log_visit.idvisit
ORDER BY NULL
) AS log_inner",
"bind" => array(1, 12, 'Test'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinVisitOnAction()
{
$actionType = 3;
$idSite = 1;
$select = 'count(distinct log_visit.idvisitor) AS `1`,
count(*) AS `2`,
sum(log_visit.visit_total_actions) AS `3`';
$from = 'log_visit';
$where = 'log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite IN (?)';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$logVisitTable = Common::prefixTable('log_visit');
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$expected = array(
"sql" => "
SELECT count(distinct log_inner.idvisitor) AS `1`, count(*) AS `2`, sum(log_inner.visit_total_actions) AS `3` FROM ( SELECT log_visit.idvisitor, log_visit.visit_total_actions
FROM $logVisitTable AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action
ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logActionTable AS log_action
ON log_link_visit_action.idaction_url = log_action.idaction
WHERE ( log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite IN (?) )
AND ( log_action.type = ? )
GROUP BY log_visit.idvisit
ORDER BY NULL ) AS log_inner",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinLogLinkVisitActionOnActionOnVisit()
{
$actionType = 3;
$idSite = 1;
$select = 'log_link_visit_action.custom_dimension_1,
actionAlias.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case visitAlias.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`';
$from = array(
'log_link_visit_action',
array('table' => 'log_visit', 'tableAlias' => 'visitAlias', 'joinOn' => 'visitAlias.idvisit = log_link_visit_action.idvisit'),
array('table' => 'log_action', 'tableAlias' => 'actionAlias', 'joinOn' => 'log_link_visit_action.idaction_url = actionAlias.idaction')
);
$where = 'log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$logVisitTable = Common::prefixTable('log_visit');
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$expected = array(
"sql" => "
SELECT log_link_visit_action.custom_dimension_1,
actionAlias.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case visitAlias.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`
FROM $logLinkVisitActionTable AS log_link_visit_action
LEFT JOIN $logVisitTable AS visitAlias ON visitAlias.idvisit = log_link_visit_action.idvisit
LEFT JOIN $logActionTable AS actionAlias ON log_link_visit_action.idaction_url = actionAlias.idaction
LEFT JOIN $logActionTable AS log_action ON log_link_visit_action.idaction_url = log_action.idaction
WHERE ( log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ? )
AND ( log_action.type = ? )",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
private function assertQueryDoesNotFail($query)
{
Db::fetchAll($query['sql'], $query['bind']);
}
public function testGetSelectQueryWhenJoinLogLinkVisitActionOnAction()
{
$actionType = 3;
$idSite = 1;
$select = 'log_link_visit_action.custom_dimension_1,
sum(log_link_visit_action.time_spent) as `13`';
$from = 'log_link_visit_action';
$where = 'log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$expected = array(
"sql" => "
SELECT log_link_visit_action.custom_dimension_1, sum(log_link_visit_action.time_spent) as `13`
FROM $logLinkVisitActionTable AS log_link_visit_action
LEFT JOIN $logActionTable AS log_action
ON log_link_visit_action.idaction_url = log_action.idaction
WHERE ( log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ? )
AND ( log_action.type = ? )",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinConversionOnAction()
{
$actionType = 3;
$idSite = 1;
$select = 'log_conversion.idgoal AS `idgoal`,
log_conversion.custom_dimension_1 AS `custom_dimension_1`,
count(*) AS `1`,
count(distinct log_conversion.idvisit) AS `3`,';
$from = 'log_conversion';
$where = 'log_conversion.server_time >= ?
AND log_conversion.server_time <= ?
AND log_conversion.idsite IN (?)';
$bind = array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite);
$segment = 'actionType==' . $actionType;
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$logConversionsTable = Common::prefixTable('log_conversion');
$logActionTable = Common::prefixTable('log_action');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$expected = array(
"sql" => "
SELECT log_conversion.idgoal AS `idgoal`, log_conversion.custom_dimension_1 AS `custom_dimension_1`, count(*) AS `1`, count(distinct log_conversion.idvisit) AS `3`,
FROM $logConversionsTable AS log_conversion
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action
ON log_link_visit_action.idvisit = log_conversion.idvisit
LEFT JOIN $logActionTable AS log_action
ON log_link_visit_action.idaction_url = log_action.idaction
WHERE ( log_conversion.server_time >= ?
AND log_conversion.server_time <= ?
AND log_conversion.idsite IN (?) )
AND ( log_action.type = ? )",
"bind" => array('2015-11-30 11:00:00', '2015-12-01 10:59:59', $idSite, $actionType));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenUnionOfSegmentsAreUsed()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'actionUrl=@myTestUrl';
$segment = new Segment($segment, $idSites = array());
$logVisitTable = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => " SELECT log_inner.* FROM (
SELECT log_visit.* FROM $logVisitTable AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action
ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE ((log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1')
OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '3')
OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '2')
OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '10') )
GROUP BY log_visit.idvisit ORDER BY NULL ) AS log_inner",
"bind" => array('%myTestUrl%', '%myTestUrl%', '%myTestUrl%', '%myTestUrl%'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenUnionOfSegmentsAreUsedWithNotContainsCompareUsesSubQueryWithGivenStartDate()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'actionUrl!@myTestUrl';
$segment = new Segment($segment, $idSites = array(), Date::factory('2020-02-02 02:00:00'), Date::factory('2020-02-29 02:00:00'));
$logVisitTable = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => " SELECT log_visit.* FROM $logVisitTable AS log_visit
WHERE ( log_visit.idvisit NOT IN (
SELECT log_visit.idvisit
FROM $logVisitTable AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE ( log_visit.visit_last_action_time >= ? AND log_visit.visit_last_action_time <= ? )
AND ( ((log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1')
OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '3')
OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '2')
OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '10') )))
)",
"bind" => array('2020-02-02 02:00:00', '2020-02-29 02:00:00', '%myTestUrl%', '%myTestUrl%', '%myTestUrl%', '%myTestUrl%'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenUnionOfSegmentsAreUsedWithNotContainsCompareUsesNoSubQueryWithoutStartDate()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'actionUrl!@myTestUrl';
// When no start date is given for the segment object, it will not generate a subquery, as it might have too many results
// instead it will try to directly join the tables, which might cause incorrect results for action dimensions
$segment = new Segment($segment, $idSites = array(), $startDate = null, $endDate = null);
$logVisitTable = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => " SELECT log_inner.* FROM (
SELECT log_visit.* FROM $logVisitTable AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE ((( log_link_visit_action.idaction_url IS NULL OR log_action_segment_log_link_visit_actionidaction_url.name NOT LIKE ? ) AND log_action_segment_log_link_visit_actionidaction_url.type = '1')
OR (( log_link_visit_action.idaction_url IS NULL OR log_action_segment_log_link_visit_actionidaction_url.name NOT LIKE ? ) AND log_action_segment_log_link_visit_actionidaction_url.type = '3')
OR (( log_link_visit_action.idaction_url IS NULL OR log_action_segment_log_link_visit_actionidaction_url.name NOT LIKE ? ) AND log_action_segment_log_link_visit_actionidaction_url.type = '2')
OR (( log_link_visit_action.idaction_url IS NULL OR log_action_segment_log_link_visit_actionidaction_url.name NOT LIKE ? ) AND log_action_segment_log_link_visit_actionidaction_url.type = '10') )
GROUP BY log_visit.idvisit ORDER BY NULL )
AS log_inner",
"bind" => array('%myTestUrl%', '%myTestUrl%', '%myTestUrl%', '%myTestUrl%'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenUsingNotEqualsCompareOnActionDimension()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'siteSearchCategory!=myCategory';
$segment = new Segment($segment, $idSites = array(), Date::factory('2020-02-02 02:00:00'));
$logVisitTable = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => " SELECT log_visit.* FROM $logVisitTable AS log_visit
WHERE ( log_visit.idvisit NOT IN (
SELECT log_visit.idvisit FROM $logVisitTable AS log_visit LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE ( log_visit.visit_last_action_time >= ? ) AND ( log_link_visit_action.search_cat = ? )) ) ",
"bind" => array('2020-02-02 02:00:00', 'myCategory'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenUsingNotEqualsAndNotContainsCompareOnActionDimensionWithIdSitesAndDates()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'siteSearchCategory!=myCategory;actionUrl!@myTestUrl';
$segment = new Segment($segment, $idSites = array(1,5), Date::factory('2020-02-02 12:00:00'), Date::factory('2020-02-05 09:00:00'));
$logVisitTable = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => "SELECT log_visit.*
FROM $logVisitTable AS log_visit
WHERE (log_visit.idvisit NOT IN (
SELECT log_visit.idvisit
FROM log_visit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action
ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url
ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE (log_visit.idsite IN (?,?) AND log_visit.visit_last_action_time >= ? AND
log_visit.visit_last_action_time <= ?)
AND ((log_link_visit_action.search_cat = ? OR
(log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND
log_action_segment_log_link_visit_actionidaction_url.type = '1') OR
(log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND
log_action_segment_log_link_visit_actionidaction_url.type = '3') OR
(log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND
log_action_segment_log_link_visit_actionidaction_url.type = '2') OR
(log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND
log_action_segment_log_link_visit_actionidaction_url.type = '10') ))
))",
"bind" => array(1, 5, '2020-02-02 12:00:00', '2020-02-05 09:00:00', 'myCategory', '%myTestUrl%', '%myTestUrl%', '%myTestUrl%', '%myTestUrl%'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinConversionOnLogLinkVisitActionSegmentUsesPageUrl()
{
$this->insertPageUrlAsAction('example.com/anypage');
$this->insertPageUrlAsAction('example.com/anypage_bis');
$pageUrlFoundInDb = 'example.com/page.html?hello=world';
$actionIdFoundInDb = $this->insertPageUrlAsAction($pageUrlFoundInDb);
$select = 'log_conversion.idgoal AS `idgoal`,
SUM(log_conversion.items) AS `8`';
$from = 'log_conversion';
$where = 'log_conversion.idsite IN (?)';
$bind = array(1);
$segment = 'pageUrl==' . urlencode($pageUrlFoundInDb);
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => "
SELECT
log_conversion.idgoal AS `idgoal`,
SUM(log_conversion.items) AS `8`
FROM
" . Common::prefixTable('log_conversion') . " AS log_conversion
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_conversion.idvisit
WHERE
( log_conversion.idsite IN (?) )
AND
( log_link_visit_action.idaction_url = ? )",
"bind" => array(1, $actionIdFoundInDb));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
/**
* Dataprovider for testBogusSegment(_shouldThrowException
*/
public function getBogusSegments()
{
return array(
array('referrerType==not'),
array('someRandomSegment==not'),
array('A=B')
);
}
/**
* @dataProvider getBogusSegments
*/
public function testBogusSegmentShouldThrowException($segment)
{
$this->expectException(\Exception::class);
new Segment($segment, $idSites = array());
}
public function testGetSelectQueryWhenLimitInnerQueryShouldHaveLimitAndNoGroupBy()
{
$select = 'sum(log_visit.visit_total_time) as sum_visit_length';
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test';
$segment = new Segment($segment, $idSites = array());
$orderBy = false;
$groupBy = false;
$limit = 33;
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit);
$expected = array(
"sql" => "
SELECT
sum(log_inner.visit_total_time) as sum_visit_length
FROM
(
SELECT
log_visit.visit_total_time
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? )
ORDER BY NULL
LIMIT 0, 33
) AS log_inner",
"bind" => array(1, 'Test'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenLimitWithCustomJoinsAndSameColumns()
{
$select = "log_action_visit_entry_idaction_name.name AS 'EntryPageTitle', log_action_idaction_event_action.name AS 'EventAction', count(distinct log_visit.idvisit) AS 'nb_uniq_visits', count(distinct log_visit.idvisitor) AS 'nb_uniq_visitors', sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) AS 'bounce_count', sum(log_visit.visit_total_actions) AS 'sum_actions', sum(log_visit.visit_goal_converted) AS 'sum_visit_goal_converted'";
$from = array('log_visit', array('table' => 'log_action', 'tableAlias' => 'log_action_visit_entry_idaction_name', 'joinOn' => 'log_visit.visit_entry_idaction_name = log_action_visit_entry_idaction_name.idaction'), 'log_link_visit_action', array('table' => 'log_action', 'tableAlias' => 'log_action_idaction_event_action', 'joinOn' => 'log_link_visit_action.idaction_event_action = log_action_idaction_event_action.idaction'));
$where = '';
$bind = array(1);
$segment = '';
$segment = new Segment($segment, $idSites = array());
$orderBy = 'nb_uniq_visits, log_action_idaction_event_action.name';
$groupBy = 'log_action_visit_entry_idaction_name.name, log_action_idaction_event_action.name';
$limit = 33;
$logVisitTable = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logActionTable = Common::prefixTable('log_action');
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit);
$expected = array(
"sql" => "
SELECT log_inner.name AS 'EntryPageTitle', log_inner.name02fd90a35677a359ea5611a4bc456a6f AS 'EventAction', count(distinct log_inner.idvisit) AS 'nb_uniq_visits', count(distinct log_inner.idvisitor) AS 'nb_uniq_visitors', sum(case log_inner.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) AS 'bounce_count', sum(log_inner.visit_total_actions) AS 'sum_actions', sum(log_inner.visit_goal_converted) AS 'sum_visit_goal_converted'
FROM (
SELECT log_action_visit_entry_idaction_name.name, log_action_idaction_event_action.name as name02fd90a35677a359ea5611a4bc456a6f, log_visit.idvisit, log_visit.idvisitor, log_visit.visit_total_actions, log_visit.visit_goal_converted
FROM $logVisitTable AS log_visit
LEFT JOIN $logActionTable AS log_action_visit_entry_idaction_name ON log_visit.visit_entry_idaction_name = log_action_visit_entry_idaction_name.idaction
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logActionTable AS log_action_idaction_event_action ON log_link_visit_action.idaction_event_action = log_action_idaction_event_action.idaction
ORDER BY nb_uniq_visits, log_action_idaction_event_action.name LIMIT 0, 33 )
AS log_inner
GROUP BY log_inner.name, log_inner.name02fd90a35677a359ea5611a4bc456a6f
ORDER BY nb_uniq_visits, log_inner.name02fd90a35677a359ea5611a4bc456a6f",
"bind" => array(1));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenLimitWithCustomJoinsAndSameColumnsAndSimilarColumns()
{
$select = 'log_link_visit_action.idvisit,
log_visit.idvisit,
count(log_visit.idvisit) as numvisits,
count(distinct log_visit.idvisit ) as numvisitors,
log_visit.idvisitor,
log_action.name as url,
sum(log_link_visit_action.time_spent) as `13`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`';
$from = array('log_visit', 'log_link_visit_action');
$where = '';
$bind = array(1);
$segment = '';
$segment = new Segment($segment, $idSites = array());
$orderBy = 'url, log_visit.idvisit';
$groupBy = 'log_visit.idvisit, log_visit.idvisit , log_visit.idvisitor, log_visit.idvisitor , log_link_visit_action.idvisit';
$limit = 33;
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit);
$logLinkVisitAction = Common::prefixTable('log_link_visit_action');
// should have replaced some idvisit columns but not idvisitor column
$expected = array(
"sql" => "
SELECT
log_inner.idvisit,
log_inner.idvisit5d489886e80b4258a9407b219a4e2811,
count(log_inner.idvisit5d489886e80b4258a9407b219a4e2811) as numvisits,
count(distinct log_inner.idvisit5d489886e80b4258a9407b219a4e2811 ) as numvisitors,
log_inner.idvisitor,
log_inner.name as url,
sum(log_inner.time_spent) as `13`,
sum(case log_inner.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`
FROM
(
SELECT
log_link_visit_action.idvisit,
log_visit.idvisit as idvisit5d489886e80b4258a9407b219a4e2811,
log_visit.idvisitor,
log_action.name,
log_link_visit_action.time_spent,
log_visit.visit_total_actions
FROM
log_visit AS log_visit LEFT JOIN $logLinkVisitAction AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
ORDER BY
url, log_visit.idvisit LIMIT 0, 33
) AS log_inner
GROUP BY
log_inner.idvisit5d489886e80b4258a9407b219a4e2811, log_inner.idvisit5d489886e80b4258a9407b219a4e2811 , log_inner.idvisitor, log_inner.idvisitor , log_inner.idvisit
ORDER BY
url, log_inner.idvisit5d489886e80b4258a9407b219a4e2811",
"bind" => array(1));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenLimitAndOffsetOuterQueryShouldNotHaveOffset()
{
$select = 'sum(log_visit.visit_total_time) as sum_visit_length';
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test';
$segment = new Segment($segment, $idSites = array());
$orderBy = false;
$groupBy = false;
$limit = 33;
$offset = 10;
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit, $offset);
$expected = array(
"sql" => "
SELECT
sum(log_inner.visit_total_time) as sum_visit_length
FROM
(
SELECT
log_visit.visit_total_time
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? )
ORDER BY NULL
LIMIT 10, 33
) AS log_inner",
"bind" => array(1, 'Test'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenOffsetIsZero()
{
$select = 'sum(log_visit.visit_total_time) as sum_visit_length';
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test';
$segment = new Segment($segment, $idSites = array());
$orderBy = false;
$groupBy = false;
$limit = 33;
$offset = 0;
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit, $offset);
$expected = array(
"sql" => "
SELECT
sum(log_inner.visit_total_time) as sum_visit_length
FROM
(
SELECT
log_visit.visit_total_time
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? )
ORDER BY NULL
LIMIT 0, 33
) AS log_inner",
"bind" => array(1, 'Test'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenLimitIsZero()
{
$select = 'sum(log_visit.visit_total_time) as sum_visit_length';
$from = 'log_visit';
$where = 'log_visit.idvisit = ?';
$bind = array(1);
$segment = 'siteSearchCategory==Test';
$segment = new Segment($segment, $idSites = array());
$orderBy = false;
$groupBy = false;
$limit = 0;
$offset = 10;
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit, $offset);
$expected = array(
"sql" => "
SELECT
sum(log_inner.visit_total_time) as sum_visit_length
FROM
(
SELECT
log_visit.visit_total_time
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
( log_visit.idvisit = ? )
AND
( log_link_visit_action.search_cat = ? )
GROUP BY log_visit.idvisit
ORDER BY NULL
) AS log_inner",
"bind" => array(1, 'Test'));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenPageUrlExistsAsStatementAND()
{
$pageUrlFoundInDb = 'example.com/page.html?hello=world';
$actionIdFoundInDb = $this->insertPageUrlAsAction($pageUrlFoundInDb);
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'visitServerHour==3;pageUrl==' . urlencode($pageUrlFoundInDb);
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => "
SELECT
log_inner.*
FROM
(
SELECT
log_visit.*
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE HOUR(log_visit.visit_last_action_time) = ?
AND log_link_visit_action.idaction_url = ?
GROUP BY log_visit.idvisit
ORDER BY NULL
) AS log_inner",
"bind" => array(3, $actionIdFoundInDb));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenPageUrlDoesNotExistAsStatementAND()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'visitServerHour==12;pageUrl==xyz';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => "
SELECT
log_visit.*
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
WHERE HOUR(log_visit.visit_last_action_time) = ?
AND (1 = 0) ",
"bind" => array(12));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenPageUrlDoesNotExistAsStatementOR()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$segment = 'visitServerHour==12,pageUrl==xyz';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = array(
"sql" => "
SELECT
log_visit.*
FROM
" . Common::prefixTable('log_visit') . " AS log_visit
WHERE (HOUR(log_visit.visit_last_action_time) = ?
OR (1 = 0) )",
"bind" => array(12));
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenManyIdActionSegmentsAreUsedORAND()
{
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
$this->insertPageUrlAsAction('test-content-name', 'idaction_content_name', Action::TYPE_CONTENT);
$this->insertPageUrlAsAction('content-pice', 'idaction_content_piece', Action::TYPE_CONTENT_PIECE);
$this->insertPageUrlAsAction('test page title', 'visit_entry_idaction_name', Action::TYPE_PAGE_TITLE);
$this->insertPageUrlAsAction('abc.com/page/url', 'visit_entry_idaction_url', Action::TYPE_PAGE_URL);
$this->insertPageUrlAsAction('abc.com/page/url2', 'visit_entry_idaction_url', Action::TYPE_PAGE_URL);
$this->insertPageUrlAsAction('mypage title', 'idaction_name', Action::TYPE_PAGE_TITLE);
$this->insertPageUrlAsAction('blahblah.com/', 'idaction_url', Action::TYPE_PAGE_URL);
$this->insertPageUrlAsAction('exitpage.com/sadlkfjasldkfj', 'visit_exit_idaction_url', Action::TYPE_PAGE_URL);
$this->insertPageUrlAsAction('http://anothersite.com/landing/page', 'idaction_url', Action::TYPE_OUTLINK);
$this->insertPageUrlAsAction('023984ASADF', 'idaction_sku', Action::TYPE_ECOMMERCE_ITEM_SKU);
$this->insertPageUrlAsAction('the test product name', 'idaction_name', Action::TYPE_ECOMMERCE_ITEM_NAME);
$this->insertPageUrlAsAction('ecom', 'idaction_event_category', Action::TYPE_ECOMMERCE_ITEM_CATEGORY);
$this->insertPageUrlAsAction('http://thing.com/path/to/place', 'idaction_url', Action::TYPE_EVENT);
$this->insertPageUrlAsAction('http://otherthing.org/path/to/anotherplace', 'idaction_url', Action::TYPE_EVENT);
$logVisit = Common::prefixTable('log_visit');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$logConversionItem = Common::prefixTable('log_conversion_item');
$segment = 'contentTarget!@' . urlencode(urlencode('http://landingpage.example.com'))
. ',contentName==' . urlencode(urlencode('test-content-name'))
. ';contentInteraction=@' . urlencode(urlencode('buy'))
. ';contentPiece!=' . urlencode(urlencode('content-pice'))
. ',contentPiece=@' . urlencode(urlencode('something'))
. ',entryPageTitle!=' . urlencode(urlencode('test page title'))
. ',downloadUrl=@' . urlencode(urlencode('downloadsite1'))
. ';downloadUrl!@' . urlencode(urlencode('mirror'))
. ',siteSearchKeyword=@' . urlencode(urlencode('a sitesearch query'))
. ';exitPageTitle!@' . urlencode(urlencode('another page title'))
. ';entryPageUrl!=' . urlencode(urlencode('https://abc.com/page/url'))
. ';entryPageUrl!=' . urlencode(urlencode('https://abc.com/page/url2'))
. ',pageTitle==' . urlencode(urlencode('mypage title'))
. ';pageUrl!@' . urlencode(urlencode('http://def.com/wherever'))
. ';pageUrl==' . urlencode(urlencode('https://blahblah.com/'))
. ',exitPageUrl=@' . urlencode(urlencode('https://exitpage.com/sadlkfjasldkfj'))
. ';outlinkUrl==' . urlencode(urlencode('http://anothersite.com/landing/page'))
. ',actionUrl=@' . urlencode(urlencode('ancientsymbol'))
. ',actionUrl=@' . urlencode(urlencode('modernsymbol'))
. ';productViewSku!@' . urlencode(urlencode('123423SDL'))
. ';productCategory=@' . urlencode(urlencode('A product category'))
. ';productSku==' . urlencode(urlencode('023984ASADF'))
. ',productName!=' . urlencode(urlencode('the test product name'))
. ',productName=@' . urlencode(urlencode('alkjbsl'))
. ',productViewCategory!@' . urlencode(urlencode('round products'))
. ',productViewName=@' . urlencode(urlencode('the first round product'))
. ',eventAction=@' . urlencode(urlencode('product-view'))
. ';eventName=@' . urlencode(urlencode('product 1'))
. ';eventCategory!=' . urlencode(urlencode('ecom'))
. ';eventUrl==' . urlencode(urlencode('http://thing.com/path/to/place'))
. ';eventUrl!=' . urlencode(urlencode('http://otherthing.org/path/to/anotherplace'))
. ';eventUrl=@' . urlencode(urlencode('https://securething.cloud/path/to/secureplace'));
$segment = new Segment($segment, $idSites = array(), Date::factory('2020-02-02 02:00:00'));
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$expected = [
'sql' => "
SELECT
log_inner.*
FROM
(
SELECT
log_visit.*
FROM
$logVisit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_content_interaction ON log_link_visit_action.idaction_content_interaction = log_action_segment_log_link_visit_actionidaction_content_interaction.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_content_piece ON log_link_visit_action.idaction_content_piece = log_action_segment_log_link_visit_actionidaction_content_piece.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_name ON log_link_visit_action.idaction_name = log_action_segment_log_link_visit_actionidaction_name.idaction
LEFT JOIN $logAction AS log_action_segment_log_visitvisit_exit_idaction_name ON log_visit.visit_exit_idaction_name = log_action_segment_log_visitvisit_exit_idaction_name.idaction
LEFT JOIN $logAction AS log_action_segment_log_visitvisit_exit_idaction_url ON log_visit.visit_exit_idaction_url = log_action_segment_log_visitvisit_exit_idaction_url.idaction
LEFT JOIN $logConversionItem AS log_conversion_item ON log_conversion_item.idvisit = log_visit.idvisit
AND `log_conversion_item`.deleted = 0
LEFT JOIN $logAction AS log_action_segment_log_conversion_itemidaction_category ON log_conversion_item.idaction_category = log_action_segment_log_conversion_itemidaction_category.idaction
LEFT JOIN $logAction AS log_action_segment_log_conversion_itemidaction_category2 ON log_conversion_item.idaction_category2 = log_action_segment_log_conversion_itemidaction_category2.idaction
LEFT JOIN $logAction AS log_action_segment_log_conversion_itemidaction_category3 ON log_conversion_item.idaction_category3 = log_action_segment_log_conversion_itemidaction_category3.idaction
LEFT JOIN $logAction AS log_action_segment_log_conversion_itemidaction_category4 ON log_conversion_item.idaction_category4 = log_action_segment_log_conversion_itemidaction_category4.idaction
LEFT JOIN $logAction AS log_action_segment_log_conversion_itemidaction_category5 ON log_conversion_item.idaction_category5 = log_action_segment_log_conversion_itemidaction_category5.idaction
LEFT JOIN $logAction AS log_action_segment_log_conversion_itemidaction_name ON log_conversion_item.idaction_name = log_action_segment_log_conversion_itemidaction_name.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_name ON log_link_visit_action.idaction_product_name = log_action_segment_log_link_visit_actionidaction_product_name.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_event_action ON log_link_visit_action.idaction_event_action = log_action_segment_log_link_visit_actionidaction_event_action.idaction
WHERE
(
log_link_visit_action.idaction_content_name = ?
OR(
log_visit.idvisit NOT IN(
SELECT
log_visit.idvisit
FROM
$logVisit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_content_target ON log_link_visit_action.idaction_content_target = log_action_segment_log_link_visit_actionidaction_content_target.idaction
WHERE
(
log_visit.visit_last_action_time >= ?
)
AND(
(
log_action_segment_log_link_visit_actionidaction_content_target.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_content_target.type = '15'
)
)
)
)
)
AND(
log_action_segment_log_link_visit_actionidaction_content_interaction.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_content_interaction.type = '16'
)
AND(
(
log_action_segment_log_link_visit_actionidaction_content_piece.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_content_piece.type = '14'
)
OR(
log_visit.visit_entry_idaction_name IS NULL
OR log_visit.visit_entry_idaction_name <> ?
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '3'
)
OR(
log_visit.idvisit NOT IN(
SELECT
log_visit.idvisit
FROM
$logVisit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
WHERE
(
log_visit.visit_last_action_time >= ?
)
AND(
log_link_visit_action.idaction_content_piece = ?
)
)
)
)
AND(
(
log_action_segment_log_link_visit_actionidaction_name.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_name.type = '8'
)
OR(
log_visit.idvisit NOT IN(
SELECT
log_visit.idvisit
FROM
$logVisit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE
(
log_visit.visit_last_action_time >= ?
)
AND(
(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '3'
)
)
)
)
)
AND(
(
log_visit.visit_exit_idaction_name IS NULL
OR log_action_segment_log_visitvisit_exit_idaction_name.name NOT LIKE ?
)
AND log_action_segment_log_visitvisit_exit_idaction_name.type = '4'
)
AND(
log_visit.visit_entry_idaction_url IS NULL
OR log_visit.visit_entry_idaction_url <> ?
)
AND(
(
log_visit.visit_entry_idaction_url IS NULL
OR log_visit.visit_entry_idaction_url <> ?
)
OR log_link_visit_action.idaction_name = ?
)
AND(
log_link_visit_action.idaction_url = ?
OR(
log_action_segment_log_visitvisit_exit_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_exit_idaction_url.type = '1'
)
)
AND(
log_link_visit_action.idaction_url = ?
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '1'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '3'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '2'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '10'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '1'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '3'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '2'
)
OR(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '10'
)
)
AND(
(
log_action_segment_log_conversion_itemidaction_category.name LIKE ?
AND log_action_segment_log_conversion_itemidaction_category.type = '7'
)
OR(
log_action_segment_log_conversion_itemidaction_category2.name LIKE ?
AND log_action_segment_log_conversion_itemidaction_category2.type = '7'
)
OR(
log_action_segment_log_conversion_itemidaction_category3.name LIKE ?
AND log_action_segment_log_conversion_itemidaction_category3.type = '7'
)
OR(
log_action_segment_log_conversion_itemidaction_category4.name LIKE ?
AND log_action_segment_log_conversion_itemidaction_category4.type = '7'
)
OR(
log_action_segment_log_conversion_itemidaction_category5.name LIKE ?
AND log_action_segment_log_conversion_itemidaction_category5.type = '7'
)
)
AND(
log_conversion_item.idaction_sku = ?
OR(
log_action_segment_log_conversion_itemidaction_name.name LIKE ?
AND log_action_segment_log_conversion_itemidaction_name.type = '6'
)
OR(
log_action_segment_log_link_visit_actionidaction_product_name.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_name.type = '6'
)
OR(
log_action_segment_log_link_visit_actionidaction_event_action.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_event_action.type = '11'
)
OR(
log_visit.idvisit NOT IN(
SELECT
log_visit.idvisit
FROM
$logVisit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logConversionItem AS log_conversion_item ON log_conversion_item.idvisit = log_visit.idvisit
AND `log_conversion_item`.deleted = 0
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_cat ON log_link_visit_action.idaction_product_cat = log_action_segment_log_link_visit_actionidaction_product_cat.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_cat2 ON log_link_visit_action.idaction_product_cat2 = log_action_segment_log_link_visit_actionidaction_product_cat2.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_cat3 ON log_link_visit_action.idaction_product_cat3 = log_action_segment_log_link_visit_actionidaction_product_cat3.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_cat4 ON log_link_visit_action.idaction_product_cat4 = log_action_segment_log_link_visit_actionidaction_product_cat4.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_cat5 ON log_link_visit_action.idaction_product_cat5 = log_action_segment_log_link_visit_actionidaction_product_cat5.idaction
WHERE
(
log_visit.visit_last_action_time >= ?
)
AND(
log_conversion_item.idaction_name = ?
AND(
(
log_action_segment_log_link_visit_actionidaction_product_cat.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_cat.type = '7'
)
OR(
log_action_segment_log_link_visit_actionidaction_product_cat2.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_cat2.type = '7'
)
OR(
log_action_segment_log_link_visit_actionidaction_product_cat3.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_cat3.type = '7'
)
OR(
log_action_segment_log_link_visit_actionidaction_product_cat4.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_cat4.type = '7'
)
OR(
log_action_segment_log_link_visit_actionidaction_product_cat5.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_cat5.type = '7'
)
)
)
)
)
)
AND(
log_action_segment_log_link_visit_actionidaction_name.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_name.type = '12'
)
AND(1 = 0)
AND(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '10'
)
AND(
log_visit.idvisit NOT IN(
SELECT
log_visit.idvisit
FROM
$logVisit AS log_visit
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_product_sku ON log_link_visit_action.idaction_product_sku = log_action_segment_log_link_visit_actionidaction_product_sku.idaction
WHERE
(
log_visit.visit_last_action_time >= ?
)
AND(
(
(
log_action_segment_log_link_visit_actionidaction_url.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_url.type = '1'
)
OR(
log_action_segment_log_link_visit_actionidaction_product_sku.name LIKE ?
AND log_action_segment_log_link_visit_actionidaction_product_sku.type = '5'
)
OR(1 = 0)
OR(1 = 0)
)
)
)
)
GROUP BY
log_visit.idvisit
ORDER BY
NULL
) AS log_inner",
'bind' => [
'1',
'2020-02-02 02:00:00',
'%http://landingpage.example.com%',
'%buy%',
'%something%',
'3',
'%downloadsite1%',
'2020-02-02 02:00:00',
'2',
'%a sitesearch query%',
'2020-02-02 02:00:00',
'%mirror%',
'%another page title%',
'4',
'5',
'6',
'7',
'%exitpage.com/sadlkfjasldkfj%',
'9',
'%ancientsymbol%',
'%ancientsymbol%',
'%ancientsymbol%',
'%ancientsymbol%',
'%modernsymbol%',
'%modernsymbol%',
'%modernsymbol%',
'%modernsymbol%',
'%A product category%',
'%A product category%',
'%A product category%',
'%A product category%',
'%A product category%',
'10',
'%alkjbsl%',
'%the first round product%',
'%product-view%',
'2020-02-02 02:00:00',
'11',
'%round products%',
'%round products%',
'%round products%',
'%round products%',
'%round products%',
'%product 1%',
'%securething.cloud/path/to/secureplace%',
'2020-02-02 02:00:00',
'%def.com/wherever%',
'%123423SDL%',
],
];
$this->assertQueryDoesNotFail($query);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenPageUrlDoesNotExistAsBothStatementsORAND()
{
list($pageUrlFoundInDb, $actionIdFoundInDb) = $this->insertActions();
$select = 'log_visit.*';
$from = 'log_visit';
$where = false;
$bind = array();
/**
* pageUrl==xyz -- Matches none
* pageUrl!=abcdefg -- Matches all
* pageUrl=@does-not-exist -- Matches none
* pageUrl=@found-in-db -- Matches all
* pageUrl=='.urlencode($pageUrlFoundInDb) -- Matches one
* pageUrl!@not-found -- matches all
* pageUrl!@found -- Matches none
*/
$segment = 'visitServerHour==12,pageUrl==xyz;pageUrl!=abcdefg,pageUrl=@does-not-exist,pageUrl=@found-in-db,pageUrl==' . urlencode($pageUrlFoundInDb) . ',pageUrl!@not-found,pageUrl!@found';
$segment = new Segment($segment, $idSites = array(), Date::factory('2020-02-02 02:00:00'));
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$logVisit = Common::prefixTable('log_visit');
$logLinkVisitAction = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$expected = array(
"sql" => "
SELECT log_inner.*
FROM (
SELECT log_visit.*
FROM $logVisit AS log_visit
LEFT JOIN $logLinkVisitAction AS log_link_visit_action
ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url
ON log_link_visit_action.idaction_url =
log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE (HOUR(log_visit.visit_last_action_time) = ? OR (1 = 0)) " . // pageUrl==xyz
" AND (
(log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1') " . // pageUrl=@does-not-exist
" OR (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1') " . // pageUrl=@found-in-db
" OR log_link_visit_action.idaction_url = ? " . // pageUrl=='.urlencode($pageUrlFoundInDb)
" OR (
log_visit.idvisit NOT IN (
SELECT log_visit.idvisit
FROM $logVisit AS log_visit
LEFT JOIN $logLinkVisitAction AS log_link_visit_action
ON log_link_visit_action.idvisit = log_visit.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url
ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE (log_visit.visit_last_action_time >= ?)
AND ((1 = 0) " . // pageUrl!=abcdefg
" AND (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1')" . // pageUrl!@not-found
" AND (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1')" . // pageUrl!@found
" )
)
)
)
GROUP BY log_visit.idvisit
ORDER BY NULL
) AS log_inner",
"bind" => [
'12',
'%does-not-exist%',
'%found-in-db%',
'1',
'2020-02-02 02:00:00',
'%not-found%',
'%found%',
],
);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
// se https://github.com/piwik/piwik/issues/9194
public function testGetSelectQueryWhenQueryingLogConversionWithSegmentThatUsesLogLinkVisitActionShouldUseSubselect()
{
$select = 'log_conversion.idgoal AS `idgoal`,
count(*) AS `1`,
count(distinct log_conversion.idvisit) AS `3`,
ROUND(SUM(log_conversion.revenue),2) AS `2`,
SUM(log_conversion.items) AS `8`';
$from = 'log_conversion';
$where = 'log_conversion.server_time >= ? AND log_conversion.server_time <= ? AND log_conversion.idsite IN (?)';
$groupBy = 'log_conversion.idgoal';
$bind = array('2015-10-14 11:00:00', '2015-10-15 10:59:59', 1);
$segment = 'pageUrl=@/';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy = false, $groupBy);
$this->assertQueryDoesNotFail($query);
$logConversionTable = Common::prefixTable('log_conversion');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$expectedBind = $bind;
$expectedBind[] = '%/%';
$expected = array(
"sql" => "
SELECT log_inner.idgoal AS `idgoal`, count(*) AS `1`, count(distinct log_inner.idvisit) AS `3`, ROUND(SUM(log_inner.revenue),2) AS `2`, SUM(log_inner.items) AS `8`
FROM (
SELECT log_conversion.idgoal, log_conversion.idvisit, log_conversion.revenue, log_conversion.items
FROM $logConversionTable AS log_conversion
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_conversion.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE ( log_conversion.server_time >= ?
AND log_conversion.server_time <= ?
AND log_conversion.idsite IN (?) )
AND ( (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1') )
GROUP BY CONCAT(log_conversion.idvisit, '_' , log_conversion.idgoal, '_', log_conversion.buster)
ORDER BY NULL )
AS log_inner GROUP BY log_inner.idgoal",
"bind" => $expectedBind);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
// se https://github.com/piwik/piwik/issues/9194
public function testGetSelectQueryWhenQueryingLogConversionWithSegmentThatUsesLogLinkVisitActionAndGroupsForUsageInConversionsByTypeOfVisitShouldUseSubselect()
{
$select = 'log_conversion.idgoal AS `idgoal`,
log_conversion.referer_type AS `referer_type`,
log_conversion.referer_name AS `referer_name`,
log_conversion.referer_keyword AS `referer_keyword`,
count(*) AS `1`,
count(distinct log_conversion.idvisit) AS `3`,
ROUND(SUM(log_conversion.revenue),2) AS `2`';
$from = 'log_conversion';
$where = 'log_conversion.server_time >= ? AND log_conversion.server_time <= ? AND log_conversion.idsite IN (?)';
$groupBy = 'log_conversion.idgoal, log_conversion.referer_type, log_conversion.referer_name, log_conversion.referer_keyword';
$bind = array('2015-10-14 11:00:00', '2015-10-15 10:59:59', 1);
$segment = 'pageUrl=@/';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy = false, $groupBy);
$this->assertQueryDoesNotFail($query);
$logConversionTable = Common::prefixTable('log_conversion');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logAction = Common::prefixTable('log_action');
$expectedBind = $bind;
$expectedBind[] = '%/%';
$expected = array(
"sql" => "
SELECT log_inner.idgoal AS `idgoal`,
log_inner.referer_type AS `referer_type`,
log_inner.referer_name AS `referer_name`,
log_inner.referer_keyword AS `referer_keyword`,
count(*) AS `1`,
count(distinct log_inner.idvisit) AS `3`,
ROUND(SUM(log_inner.revenue),2) AS `2`
FROM (
SELECT log_conversion.idgoal, log_conversion.referer_type, log_conversion.referer_name, log_conversion.referer_keyword, log_conversion.idvisit, log_conversion.revenue
FROM $logConversionTable AS log_conversion
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_conversion.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE ( log_conversion.server_time >= ?
AND log_conversion.server_time <= ?
AND log_conversion.idsite IN (?) )
AND ( (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1') )
GROUP BY CONCAT(log_conversion.idvisit, '_' , log_conversion.idgoal, '_', log_conversion.buster)
ORDER BY NULL )
AS log_inner GROUP BY log_inner.idgoal, log_inner.referer_type, log_inner.referer_name, log_inner.referer_keyword",
"bind" => $expectedBind);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
// see https://github.com/piwik/piwik/issues/9194
public function testGetSelectQueryWhenQueryingLogConversionWithSegmentThatUsesLogLinkVisitActionAndLogVisitShouldUseSubselectGroupedByIdVisitAndBuster()
{
$select = 'log_conversion.idgoal AS `idgoal`,
count(*) AS `1`,
count(distinct log_conversion.idvisit) AS `3`,
ROUND(SUM(log_conversion.revenue),2) AS `2`';
$from = 'log_conversion';
$where = 'log_conversion.server_time >= ? AND log_conversion.server_time <= ? AND log_conversion.idsite IN (?)';
$groupBy = 'log_conversion.idgoal';
$bind = array('2015-10-14 11:00:00', '2015-10-15 10:59:59', 1);
$segment = 'visitorType==returning,visitorType==returningCustomer;pageUrl=@/';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy = false, $groupBy);
$this->assertQueryDoesNotFail($query);
$logConversionTable = Common::prefixTable('log_conversion');
$logLinkVisitActionTable = Common::prefixTable('log_link_visit_action');
$logVisitTable = Common::prefixTable('log_visit');
$logAction = Common::prefixTable('log_action');
$expectedBind = $bind;
$expectedBind[] = 1;
$expectedBind[] = 2;
$expectedBind[] = '%/%';
$expected = array(
"sql" => "
SELECT log_inner.idgoal AS `idgoal`, count(*) AS `1`, count(distinct log_inner.idvisit) AS `3`, ROUND(SUM(log_inner.revenue),2) AS `2`
FROM (
SELECT log_conversion.idgoal, log_conversion.idvisit, log_conversion.revenue
FROM $logConversionTable AS log_conversion
LEFT JOIN $logLinkVisitActionTable AS log_link_visit_action ON log_link_visit_action.idvisit = log_conversion.idvisit
LEFT JOIN $logVisitTable AS log_visit ON log_visit.idvisit = log_conversion.idvisit
LEFT JOIN $logAction AS log_action_segment_log_link_visit_actionidaction_url ON log_link_visit_action.idaction_url = log_action_segment_log_link_visit_actionidaction_url.idaction
WHERE ( log_conversion.server_time >= ?
AND log_conversion.server_time <= ?
AND log_conversion.idsite IN (?) )
AND ( (log_visit.visitor_returning = ? OR log_visit.visitor_returning = ?)
AND (log_action_segment_log_link_visit_actionidaction_url.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_url.type = '1') )
GROUP BY CONCAT(log_conversion.idvisit, '_' , log_conversion.idgoal, '_', log_conversion.buster)
ORDER BY NULL ) AS log_inner
GROUP BY log_inner.idgoal",
"bind" => $expectedBind);
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testGetSelectQueryWhenJoinCustomLogTableTwoTablesRemovedFromLogVisitFirstThenJoinTableAdjacentToLogVisit()
{
$this->defineEntitiesNotDirectlyJoinableToVisit();
$segment = new Segment('', $idSites = array());
$select = 'count(distinct log_thing.idlogthing) as nb_things, count(log_thing_event.idlogthingevent) as nb_thing_events';
$from = [
'log_thing',
'log_thing_event',
];
$where = '';
$bind = [];
$query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy = false);
$this->assertQueryDoesNotFail($query);
$expected = <<<SQL
SELECT count(distinct log_thing.idlogthing) as nb_things, count(log_thing_event.idlogthingevent) as nb_thing_events FROM log_thing AS log_thing LEFT JOIN log_thing_event AS log_thing_event ON `log_thing_event`.`idlogthing` = `log_thing`.`idlogthing`
SQL;
$sql = $this->removeExtraWhiteSpaces($query['sql']);
$expected = $this->removeExtraWhiteSpaces($expected);
$this->assertEquals($expected, $sql);
}
public function testGetSelectQueryWhenUrls()
{
$select = 'sum(log_visit.idvisit)';
$from = 'log_visit';
$where = 'log_visit.idsite = ?';
$bind = array(1);
$logVisitTable = Common::prefixTable('log_visit');
$logAction = Common::prefixTable('log_action');
$segment = 'entryPageUrl=@https%253A%252F%252Fmatomo.org%252Ffaq%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fdocs%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fcontact%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fhelp%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Ftag-manager-training%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fweb-analytics-training%252F,entryPageUrl=@https%253A%252F%252Fdeveloper.matomo.org%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Ftranslations%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fchangelog%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fsupport%252F,entryPageUrl=@https%253A%252F%252Fmatomo.org%252Fjobs%252F';
$segment = new Segment($segment, $idSites = array());
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$this->assertQueryDoesNotFail($query);
$expected = [
"sql" => "SELECT sum(log_visit.idvisit)
FROM $logVisitTable AS log_visit
LEFT JOIN $logAction AS log_action_segment_log_visitvisit_entry_idaction_url ON log_visit.visit_entry_idaction_url = log_action_segment_log_visitvisit_entry_idaction_url.idaction
WHERE (log_visit.idsite = ?)
AND (
(
(
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
OR (
log_action_segment_log_visitvisit_entry_idaction_url.name LIKE ?
AND log_action_segment_log_visitvisit_entry_idaction_url.type = '1'
)
)
)",
"bind" => [
'1',
'%matomo.org/faq/%',
'%matomo.org/docs/%',
'%matomo.org/contact/%',
'%matomo.org/help/%',
'%matomo.org/tag-manager-training/%',
'%matomo.org/web-analytics-training/%',
'%developer.matomo.org/%',
'%matomo.org/translations/%',
'%matomo.org/changelog/%',
'%matomo.org/support/%',
'%matomo.org/jobs/%',
],
];
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
public function testSegmentExpressionParseSubExpressionsIntoSqlExpressionsRecognizesAvailableTablesCorrectly()
{
$segment = 'pageTitle=@abc';
$from = [
'log_link_visit_action',
[
'table' => 'log_action',
'tableAlias' => 'log_action_idaction_name',
'joinOn' => 'log_link_visit_action.idaction_name = log_action_idaction_name.idaction',
],
[
'table' => 'log_visit',
'join' => 'RIGHT JOIN',
],
'log_action',
];
$originalFrom = $from;
$segment = new Segment($segment, []);
$segmentExpression = $segment->getSegmentExpression();
$segmentExpression->parseSubExpressionsIntoSqlExpressions($from);
$sql = $segmentExpression->getSql();
$expected = [
"where" => "(log_action_segment_log_link_visit_actionidaction_name.name LIKE ? AND log_action_segment_log_link_visit_actionidaction_name.type = '4')",
"bind" => ['%abc%'],
];
$this->assertEquals(SegmentTest::removeExtraWhiteSpaces($expected), SegmentTest::removeExtraWhiteSpaces($sql));
$expectedFrom = array_merge(
$originalFrom,
[
[
'table' => 'log_action',
'tableAlias' => 'log_action_segment_log_link_visit_actionidaction_name',
'field' => 'log_action_segment_log_link_visit_actionidaction_name.name',
'joinOn' => 'log_link_visit_action.idaction_name = log_action_segment_log_link_visit_actionidaction_name.idaction',
'discriminator' => 'log_action_segment_log_link_visit_actionidaction_name.type = \'4\'',
],
]
);
$this->assertEquals($expectedFrom, $from);
}
/**
* @param $pageUrlFoundInDb
* @return string
* @throws Exception
*/
private function insertPageUrlAsAction($pageUrlFoundInDb, $idActionColumn = 'idaction_url', $idActionType = Action::TYPE_PAGE_URL)
{
TableLogAction::loadIdsAction(array(
$idActionColumn => array($pageUrlFoundInDb, $idActionType)
));
$actionIdFoundInDb = Db::fetchOne("SELECT idaction from " . Common::prefixTable('log_action') . " WHERE name = ?", $pageUrlFoundInDb);
$this->assertNotEmpty($actionIdFoundInDb, "Action $pageUrlFoundInDb was not found in the " . Common::prefixTable('log_action') . " table.");
return $actionIdFoundInDb;
}
/**
* @return array
*/
private function insertActions()
{
$pageUrlFoundInDb = 'example.com/found-in-db';
$actionIdFoundInDb = $this->insertPageUrlAsAction($pageUrlFoundInDb);
// Adding some other actions to make test case more realistic
$this->insertPageUrlAsAction('example.net/found-in-db-bis');
$this->insertPageUrlAsAction('example.net/found-in-db-ter');
$this->insertPageUrlAsAction('example.net/page-not-found');
return array($pageUrlFoundInDb, $actionIdFoundInDb);
}
public function provideContainerConfig()
{
return array(
'Piwik\Access' => new FakeAccess(),
);
}
public function testWillBeArchivedByDefaultAllSegmentsWillBeArchived()
{
$this->assertWillBeArchived($this->exampleSegment);
}
public function testWillBeArchivedSegmentsWillNotBeArchivedWhenBrowserArchivingIsDisabledAndNoSuchSegmentExists()
{
$this->disableSegmentBrowserArchiving();
$this->assertNotWillBeArchived($this->exampleSegment);
}
public function testWillBeArchivedSegmentsWillBeArchivedWhenBrowserArchivingIsDisabledButBrowserSegmentsArchivingEnabled()
{
$this->disableBrowserArchiving();
$this->assertWillBeArchived($this->exampleSegment);
}
public function testWillSegmentBeArchivedSegmentsWillBeArchivedWhenBrowserArchivingDisabledAndSegmentExistsNotAutoArchiveAndSegmentBrowserArchivingDisabled()
{
$this->disableSegmentBrowserArchiving();
SegmentEditorApi::getInstance()->add('My Name', $this->exampleSegment, $idSite = false, $autoArchive = false);
$this->assertWillBeArchived($this->exampleSegment);
}
public function testWillSegmentBeArchivedSegmentsWillBeArchivedWhenBrowserArchivingDisabledButSegmentExistsWithAuthoArchive()
{
$this->disableSegmentBrowserArchiving();
SegmentEditorApi::getInstance()->add('My Name', $this->exampleSegment, $idSite = false, $autoArchive = true);
$this->assertWillBeArchived($this->exampleSegment);
}
public function testWillBeArchivedAnEmptySegmentShouldBeAlwaysArchived()
{
$this->assertWillBeArchived(false);
$this->disableSegmentBrowserArchiving();
$this->assertWillBeArchived(false);
}
/**
* @dataProvider getTestDataForCombine
*/
public function testCombineShouldCombineSegmentConditionsProperly($segment, $operator, $toCombine, $expected)
{
$newSegment = Segment::combine($segment, $operator, $toCombine);
$this->assertEquals($expected, $newSegment);
}
public function getTestDataForCombine()
{
return [
['', ';', '', ''],
['browserCode==ff;visitCount>1', ';', '', 'browserCode==ff;visitCount>1'],
['', ';', 'visitCount>1', 'visitCount>1'],
['browserCode==ff;visitCount>1', ';', 'visitCount>1', 'browserCode==ff;visitCount>1'],
['visitCount>1;browserCode==ff', ';', 'visitCount>1', 'visitCount>1;browserCode==ff'],
['visitCount>1,browserCode==ff', ';', 'visitCount>1', 'visitCount>1,browserCode==ff;visitCount>1'],
['browserCode==ff', ';', 'visitCount>1', 'browserCode==ff;visitCount>1'],
['browserCode==ff;visitCount>1', ',', 'visitCount>2', 'browserCode==ff;visitCount>1,visitCount>2'],
['visitorType==new', ';', 'visitorType==new', 'visitorType==new'],
// urlencoding test
[urlencode('browserCode==ff;visitCount>1'), ';', 'visitCount>1', urlencode('browserCode==ff;visitCount>1')],
['browserCode==ff;visitCount>1', ';', urlencode('visitCount>1'), 'browserCode==ff;visitCount>1'],
['browserCode==ff;' . urlencode('visitCount>1'), ';', 'visitCount>1', 'browserCode==ff;' . urlencode('visitCount>1')],
];
}
/**
* @dataProvider getTestDataForGetStoredSegmentName
*/
public function testGetStoredSegmentName($segment, $expectedName)
{
SegmentEditorApi::getInstance()->add('test segment 1', 'browserCode==ff');
SegmentEditorApi::getInstance()->add('test segment 2', urlencode('browserCode==ch'));
SegmentEditorApi::getInstance()->add('test segment 3', 'pageUrl=@' . urlencode('/a/b?d=blahfty'));
SegmentEditorApi::getInstance()->add('test segment 4', 'pageUrl=@' . urlencode(urlencode('/a/b?d=wafty')));
SegmentEditorApi::getInstance()->add('test segment 5', urlencode('pageUrl=@' . urlencode(urlencode('/a/b?d=woo'))));
$segmentObj = new Segment($segment, [1]);
$this->assertEquals($expectedName, $segmentObj->getStoredSegmentName(1));
}
public function getTestDataForGetStoredSegmentName()
{
return [
['browserCode==ff', 'test segment 1'],
[urlencode('browserCode==ff'), 'test segment 1'],
['browserCode==ch', 'test segment 2'],
[urlencode('browserCode==ch'), 'test segment 2'],
['pageUrl=@' . urlencode('/a/b?d=blahfty'), 'test segment 3'],
['pageUrl=@' . urlencode(urlencode('/a/b?d=blahfty')), 'test segment 3'],
['pageUrl=@' . urlencode(urlencode('/a/b?d=wafty')), 'test segment 4'],
[urlencode('pageUrl=@' . urlencode(urlencode('/a/b?d=wafty'))), 'test segment 4'],
['pageUrl=@' . urlencode(urlencode('/a/b?d=woo')), 'test segment 5'],
[urlencode('pageUrl=@' . urlencode(urlencode('/a/b?d=woo'))), 'test segment 5'],
// these test cases won't pass because the value is encoded, but the operator isn't in one of the segments. kept here just
// so there's a `record that they won't work
// ['pageUrl=@' . urlencode('/a/b?d=wafty'), 'test segment 4'],
// [urlencode('pageUrl=@' . urlencode('/a/b?d=wafty')), 'test segment 4'],
// [urlencode('pageUrl=@' . urlencode('/a/b?d=woo')), 'test segment 5'],
// ['pageUrl=@' . urlencode('/a/b?d=woo'), 'test segment 5'],
];
}
private function assertWillBeArchived($segmentString)
{
$this->assertTrue($this->willSegmentByArchived($segmentString));
}
private function assertNotWillBeArchived($segmentString)
{
$this->assertFalse($this->willSegmentByArchived($segmentString));
}
private function willSegmentByArchived($segmentString)
{
$segment = new Segment($segmentString, $idSites = array(1));
return $segment->willBeArchived();
}
private function disableBrowserArchiving()
{
Rules::setBrowserTriggerArchiving(false);
}
private function disableSegmentBrowserArchiving()
{
$this->disableBrowserArchiving();
$config = Config::getInstance();
$general = $config->General;
$general['browser_archiving_disabled_enforce'] = '1';
$config->General = $general;
}
public static function provideContainerConfigBeforeClass()
{
return [
'observers.global' => [
['Segment.addSegments', \Piwik\DI::value(function (Segment\SegmentsList $list) {
$segment = new \Piwik\Plugin\Segment();
$segment->setSegment('customSegment');
$segment->setType(\Piwik\Plugin\Segment::TYPE_DIMENSION);
$segment->setName('Custom Segment');
$segment->setSqlSegment('(UNIX_TIMESTAMP(log_visit.visit_first_action_time) - log_visit.visitor_seconds_since_first)');
$list->addSegment($segment);
})],
],
];
}
protected static function configureFixture($fixture)
{
parent::configureFixture($fixture);
$fixture->createSuperUser = true;
$fixture->extraPluginsToLoad = ['ExamplePlugin'];
}
private function defineEntitiesNotDirectlyJoinableToVisit()
{
// create database tables
DbHelper::createTable('log_thing', '
`idlogthing` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`idsite` INT UNSIGNED NOT NULL,
`value` INT UNSIGNED NOT NULL,
`name` VARCHAR(100) NOT NULL DEFAULT \'\',
PRIMARY KEY (`idlogthing`)
');
DbHelper::createTable('log_thing_event', '
`idlogthingevent` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`idsite` INT UNSIGNED NOT NULL,
`idlogthing` BIGINT UNSIGNED NOT NULL,
`idvisit` BIGINT(10) UNSIGNED NOT NULL,
`server_time` DATETIME NOT NULL,
`category` VARCHAR(50) NOT NULL DEFAULT \'\',
PRIMARY KEY (`idlogthingevent`)
');
// add logtable classes
$logThings = new class () extends LogTable {
public function getName()
{
return 'log_thing';
}
public function getIdColumn()
{
return 'idlogthing';
}
public function getPrimaryKey()
{
return ['idlogthing'];
}
public function getWaysToJoinToOtherLogTables()
{
return ['log_thing_event' => 'idlogthing'];
}
};
$logThingEvents = new class () extends LogTable {
public function getname()
{
return 'log_thing_event';
}
public function getIdColumn()
{
return 'idlogthingevent';
}
public function getColumnToJoinOnIdVisit()
{
return 'idvisit';
}
public function getPrimaryKey()
{
return array('idlogthingevent');
}
public function getDateTimeColumn()
{
return 'server_time';
}
};
Piwik::addAction('LogTables.addLogTables', function (&$logTables) use ($logThings, $logThingEvents) {
$logTables[] = $logThings;
$logTables[] = $logThingEvents;
});
// add Dimension classes
$thingEventIdDimension = new class () extends Dimension {
protected $nameSingular = 'CustomReports_ThingEvent';
protected $namePlural = 'CustomReports_ThingEvents';
protected $segmentName = 'thingEventId';
protected $category = 'CustomReports_Things';
protected $dbTableName = 'log_thing_event';
protected $columnName = 'idlogthingevent';
protected $sqlSegment = 'log_thing_event.idlogthingevent';
public function getId()
{
return 'CustomReports.ThingEvent';
}
public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
$metric = $dimensionMetricFactory->createCustomMetric('nb_things', 'things', ArchivedMetric::AGGREGATION_COUNT);
$metricsList->addMetric($metric);
}
};
$thingValueDimension = new class () extends Dimension {
protected $nameSingular = 'CustomReports_ThingValue';
protected $namePlural = 'CustomReports_ThingValues';
protected $segmentName = 'thingValue';
protected $category = 'CustomReports_Things';
protected $dbTableName = 'log_thing';
protected $columnName = 'value';
protected $sqlSegment = 'log_thing.value';
public function getId()
{
return 'CustomReports.ThingValue';
}
public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
parent::configureMetrics($metricsList, $dimensionMetricFactory);
$metric = $dimensionMetricFactory->createCustomMetric('sum_thing_value', 'thing value', ArchivedMetric::AGGREGATION_SUM);
$metricsList->addMetric($metric);
$metric = $dimensionMetricFactory->createCustomMetric('max_thing_value', 'thing value', ArchivedMetric::AGGREGATION_MAX);
$metricsList->addMetric($metric);
$metric = $dimensionMetricFactory->createCustomMetric('min_thing_value', 'thing value', ArchivedMetric::AGGREGATION_MIN);
$metricsList->addMetric($metric);
}
};
$thingNameDimension = new class () extends Dimension {
protected $nameSingular = 'CustomReports_ThingName';
protected $namePlural = 'CustomReports_ThingNames';
protected $segmentName = 'thingName';
protected $category = 'CustomReports_Things';
protected $dbTableName = 'log_thing';
protected $columnName = 'name';
protected $sqlSegment = 'log_thing.name';
public function getId()
{
return 'CustomReports.ThingName';
}
};
$thingCategoryDimension = new class () extends Dimension {
protected $nameSingular = 'CustomReports_ThingCategory';
protected $namePlural = 'CustomReports_ThingCategories';
protected $segmentName = 'thingCategory';
protected $category = 'CustomReports_Things';
protected $dbTableName = 'log_thing_event';
protected $columnName = 'category';
protected $sqlSegment = 'log_thing_event.category';
public function getId()
{
return 'CustomReports.ThingCategory';
}
};
Piwik::addAction('Dimension.addDimensions', function (&$dimensions) use ($thingEventIdDimension, $thingValueDimension, $thingNameDimension, $thingCategoryDimension) {
$dimensions[] = $thingEventIdDimension;
$dimensions[] = $thingValueDimension;
$dimensions[] = $thingNameDimension;
$dimensions[] = $thingCategoryDimension;
});
}
}