1<?php
2
3namespace dokuwiki\plugin\struct\meta;
4
5/**
6 * Class SearchCloud
7 *
8 * The same as @see SearchConfig, but executed a search that is not pid-focused
9 *
10 * @package dokuwiki\plugin\struct\meta
11 */
12class SearchCloud extends SearchConfig
13{
14
15    protected $limit = '';
16
17
18    /**
19     * Transform the set search parameters into a statement
20     *
21     * @return array ($sql, $opts) The SQL and parameters to execute
22     */
23    public function getSQL()
24    {
25        if (!$this->columns) throw new StructException('nocolname');
26
27        $QB = new QueryBuilder();
28        reset($this->schemas);
29        $schema = current($this->schemas);
30        $datatable = 'data_' . $schema->getTable();
31
32        $QB->addTable($datatable);
33
34        // add conditional page clauses if pid has a value
35        $subAnd = $QB->filters()->whereSubAnd();
36        $subAnd->whereAnd("$datatable.pid = ''");
37        $subOr = $subAnd->whereSubOr();
38        $subOr->whereAnd("GETACCESSLEVEL($datatable.pid) > 0");
39        $subOr->whereAnd("PAGEEXISTS($datatable.pid) = 1");
40        $subOr->whereAnd('ASSIGNED != 0');
41
42        // add conditional schema assignment check
43        $QB->addLeftJoin(
44            $datatable,
45            'schema_assignments',
46            '',
47            "$datatable.pid != ''
48                    AND $datatable.pid = schema_assignments.pid
49                    AND schema_assignments.tbl = '{$schema->getTable()}'"
50        );
51
52        $QB->filters()->whereAnd("$datatable.latest = 1");
53        $QB->filters()->where('AND', 'tag IS NOT \'\'');
54
55        $col = $this->columns[0];
56        if ($col->isMulti()) {
57            $multitable = "multi_{$col->getTable()}";
58            $MN = $QB->generateTableAlias('M');
59
60            $QB->addLeftJoin(
61                $datatable,
62                $multitable,
63                $MN,
64                "$datatable.pid = $MN.pid AND
65                     $datatable.rid = $MN.rid AND
66                     $datatable.rev = $MN.rev AND
67                     $MN.colref = {$col->getColref()}"
68            );
69
70            $col->getType()->select($QB, $MN, 'value', 'tag');
71            $colname = $MN . '.value';
72        } else {
73            $col->getType()->select($QB, $datatable, $col->getColName(), 'tag');
74            $colname = $datatable . '.' . $col->getColName();
75        }
76        $QB->addSelectStatement("COUNT($colname)", 'count');
77        $QB->addSelectColumn('schema_assignments', 'assigned', 'ASSIGNED');
78        $QB->addGroupByStatement('tag');
79        $QB->addOrderBy('count DESC');
80
81        list($sql, $opts) = $QB->getSQL();
82        return [$sql . $this->limit, $opts];
83    }
84
85    /**
86     * We do not have pagination in clouds, so we can work with a limit within SQL
87     *
88     * @param int $limit
89     */
90    public function setLimit($limit)
91    {
92        $this->limit = " LIMIT $limit";
93    }
94
95    /**
96     * Execute this search and return the result
97     *
98     * The result is a two dimensional array of Value()s.
99     *
100     * @return Value[][]
101     */
102    public function execute()
103    {
104        list($sql, $opts) = $this->getSQL();
105
106        /** @var \PDOStatement $res */
107        $res = $this->sqlite->query($sql, $opts);
108        if ($res === false) throw new StructException("SQL execution failed for\n\n$sql");
109
110        $result = [];
111        $rows = $this->sqlite->res2arr($res);
112
113        foreach ($rows as $row) {
114            if (!empty($this->config['min']) && $this->config['min'] > $row['count']) {
115                break;
116            }
117
118            $row['tag'] = new Value($this->columns[0], $row['tag']);
119            $result[] = $row;
120        }
121
122        $this->sqlite->res_close($res);
123        $this->count = count($result);
124        return $result;
125    }
126}
127