226 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php declare(strict_types = 1);
 | 
						|
namespace WapplerSystems\Meilisearch\Domain\Search\Statistics;
 | 
						|
 | 
						|
/***************************************************************
 | 
						|
 *  Copyright notice
 | 
						|
 *
 | 
						|
 *  (c) 2016 Thomas Hohn <tho@systime.dk>
 | 
						|
 *  All rights reserved
 | 
						|
 *
 | 
						|
 *  This script is part of the TYPO3 project. The TYPO3 project is
 | 
						|
 *  free software; you can redistribute it and/or modify
 | 
						|
 *  it under the terms of the GNU General Public License as published by
 | 
						|
 *  the Free Software Foundation; either version 3 of the License, or
 | 
						|
 *  (at your option) any later version.
 | 
						|
 *
 | 
						|
 *  The GNU General Public License can be found at
 | 
						|
 *  http://www.gnu.org/copyleft/gpl.html.
 | 
						|
 *
 | 
						|
 *  This script is distributed in the hope that it will be useful,
 | 
						|
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 *  GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 *  This copyright notice MUST APPEAR in all copies of the script!
 | 
						|
 ***************************************************************/
 | 
						|
 | 
						|
use WapplerSystems\Meilisearch\System\Records\AbstractRepository;
 | 
						|
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 | 
						|
 | 
						|
/**
 | 
						|
 * Calculates the SearchQueryStatistics
 | 
						|
 *
 | 
						|
 * @author Thomas Hohn <tho@systime.dk>
 | 
						|
 */
 | 
						|
class StatisticsRepository extends AbstractRepository
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * @var string
 | 
						|
     */
 | 
						|
    protected $table = 'tx_meilisearch_statistics';
 | 
						|
 | 
						|
    /**
 | 
						|
     * Fetches must popular search keys words from the table tx_meilisearch_statistics
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @param int $days number of days of history to query
 | 
						|
     * @param int $limit
 | 
						|
     * @return mixed
 | 
						|
     */
 | 
						|
    public function getSearchStatistics(int $rootPageId, int $days = 30, $limit = 10)
 | 
						|
    {
 | 
						|
        $now = time();
 | 
						|
        $timeStart = (int)($now - 86400 * $days); // 86400 seconds/day
 | 
						|
        $limit = (int)$limit;
 | 
						|
 | 
						|
        return $this->getPreparedQueryBuilderForSearchStatisticsAndTopKeywords($rootPageId, $timeStart, $limit)
 | 
						|
            ->execute()->fetchAll();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns prepared QueryBuilder for two purposes:
 | 
						|
     * for getSearchStatistics() and getTopKeyWordsWithOrWithoutHits() methods
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @param int $timeStart
 | 
						|
     * @param int $limit
 | 
						|
     * @return QueryBuilder
 | 
						|
     */
 | 
						|
    protected function getPreparedQueryBuilderForSearchStatisticsAndTopKeywords(int $rootPageId, int $timeStart, int $limit) : QueryBuilder
 | 
						|
    {
 | 
						|
        $countRows = $this->countByRootPageId($rootPageId);
 | 
						|
        $queryBuilder = $this->getQueryBuilder();
 | 
						|
        $statisticsQueryBuilder = $queryBuilder
 | 
						|
            ->select('keywords')
 | 
						|
            ->add('select', $queryBuilder->expr()->count('keywords', 'count'), true)
 | 
						|
            ->add('select', $queryBuilder->expr()->avg('num_found', 'hits'), true)
 | 
						|
            ->add('select', '(' . $queryBuilder->expr()->count('keywords') . ' * 100 / ' . $countRows . ') AS percent', true)
 | 
						|
            ->from($this->table)
 | 
						|
            ->andWhere(
 | 
						|
                $queryBuilder->expr()->gt('tstamp', $timeStart),
 | 
						|
                $queryBuilder->expr()->eq('root_pid', $rootPageId)
 | 
						|
            )
 | 
						|
            ->groupBy('keywords')
 | 
						|
            ->orderBy('count', 'DESC')
 | 
						|
            ->addOrderBy('hits', 'DESC')
 | 
						|
            ->addOrderBy('keywords', 'ASC')
 | 
						|
            ->setMaxResults($limit);
 | 
						|
 | 
						|
        return $statisticsQueryBuilder;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Find Top search keywords with results
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @param int $days number of days of history to query
 | 
						|
     * @param int $limit
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function getTopKeyWordsWithHits(int $rootPageId, int $days = 30, int $limit = 10) : array
 | 
						|
    {
 | 
						|
        return $this->getTopKeyWordsWithOrWithoutHits($rootPageId, $days, $limit, false);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Find Top search keywords without results
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @param int $days number of days of history to query
 | 
						|
     * @param int $limit
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function getTopKeyWordsWithoutHits(int $rootPageId, int $days = 30, int $limit = 10) : array
 | 
						|
    {
 | 
						|
        return $this->getTopKeyWordsWithOrWithoutHits($rootPageId, $days, $limit, true);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Find Top search keywords with or without results
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @param int $days number of days of history to query
 | 
						|
     * @param int $limit
 | 
						|
     * @param bool $withoutHits
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    protected function getTopKeyWordsWithOrWithoutHits(int $rootPageId, int $days = 30, int $limit = 10, bool $withoutHits = false) : array
 | 
						|
    {
 | 
						|
        $now = time();
 | 
						|
        $timeStart = $now - 86400 * $days; // 86400 seconds/day
 | 
						|
 | 
						|
        $queryBuilder = $this->getPreparedQueryBuilderForSearchStatisticsAndTopKeywords($rootPageId, $timeStart, $limit);
 | 
						|
        // Check if we want without or with hits
 | 
						|
        if ($withoutHits === true) {
 | 
						|
            $queryBuilder->andWhere($queryBuilder->expr()->eq('num_found', 0));
 | 
						|
        } else {
 | 
						|
            $queryBuilder->andWhere($queryBuilder->expr()->gt('num_found', 0));
 | 
						|
        }
 | 
						|
 | 
						|
        return $queryBuilder->execute()->fetchAll();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get number of queries over time
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @param int $days number of days of history to query
 | 
						|
     * @param int $bucketSeconds Seconds per bucket
 | 
						|
     * @return array [labels, data]
 | 
						|
     */
 | 
						|
    public function getQueriesOverTime(int $rootPageId, int $days = 30, int $bucketSeconds = 3600) : array
 | 
						|
    {
 | 
						|
        $now = time();
 | 
						|
        $timeStart = $now - 86400 * intval($days); // 86400 seconds/day
 | 
						|
 | 
						|
        $queryBuilder = $this->getQueryBuilder();
 | 
						|
        $result = $queryBuilder
 | 
						|
            ->addSelectLiteral(
 | 
						|
                'FLOOR(tstamp/' . $bucketSeconds . ') AS bucket',
 | 
						|
                '(tstamp - (tstamp % 86400)) AS timestamp',
 | 
						|
                $queryBuilder->expr()->count('*', 'numQueries')
 | 
						|
            )
 | 
						|
            ->from($this->table)
 | 
						|
            ->andWhere(
 | 
						|
                $queryBuilder->expr()->gt('tstamp', $timeStart),
 | 
						|
                $queryBuilder->expr()->eq('root_pid', $rootPageId)
 | 
						|
            )
 | 
						|
            ->groupBy('bucket', 'timestamp')
 | 
						|
            ->orderBy('bucket', 'ASC')
 | 
						|
            ->execute()->fetchAll();
 | 
						|
 | 
						|
        return $result;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Regurns a result set by given plugin.tx_meilisearch.search.frequentSearches.select configuration.
 | 
						|
     *
 | 
						|
     * @param array $frequentSearchConfiguration
 | 
						|
     * @return array Array of frequent search terms, keys are the terms, values are hits
 | 
						|
     */
 | 
						|
    public function getFrequentSearchTermsFromStatisticsByFrequentSearchConfiguration(array $frequentSearchConfiguration) : array
 | 
						|
    {
 | 
						|
        $queryBuilder = $this->getQueryBuilder();
 | 
						|
        $resultSet = $queryBuilder
 | 
						|
            ->addSelectLiteral(
 | 
						|
                $frequentSearchConfiguration['select.']['SELECT']
 | 
						|
            )
 | 
						|
            ->from($frequentSearchConfiguration['select.']['FROM'])
 | 
						|
            ->add('where', $frequentSearchConfiguration['select.']['ADD_WHERE'], true)
 | 
						|
            ->add('groupBy', $frequentSearchConfiguration['select.']['GROUP_BY'], true)
 | 
						|
            ->add('orderBy', $frequentSearchConfiguration['select.']['ORDER_BY'])
 | 
						|
            ->setMaxResults((int)$frequentSearchConfiguration['limit'])
 | 
						|
            ->execute()->fetchAll();
 | 
						|
 | 
						|
        return $resultSet;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Persists statistics record
 | 
						|
     *
 | 
						|
     * @param array $statisticsRecord
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function saveStatisticsRecord(array $statisticsRecord)
 | 
						|
    {
 | 
						|
        $queryBuilder = $this->getQueryBuilder();
 | 
						|
        $queryBuilder->insert($this->table)->values($statisticsRecord)->execute();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Counts rows for specified site
 | 
						|
     *
 | 
						|
     * @param int $rootPageId
 | 
						|
     * @return int
 | 
						|
     */
 | 
						|
    public function countByRootPageId(int $rootPageId): int
 | 
						|
    {
 | 
						|
        $queryBuilder = $this->getQueryBuilder();
 | 
						|
        return (int)$this->getQueryBuilder()
 | 
						|
            ->count('*')
 | 
						|
            ->from($this->table)
 | 
						|
            ->andWhere($queryBuilder->expr()->eq('root_pid', $rootPageId))
 | 
						|
            ->execute()->fetchColumn(0);
 | 
						|
    }
 | 
						|
}
 |