xref: /dokuwiki/inc/Search/Collection/DirectCollection.php (revision 06053dca2fac9a1da4eb1accf8c2488942da5d2a)
1<?php
2
3namespace dokuwiki\Search\Collection;
4
5use dokuwiki\Search\Exception\IndexAccessException;
6use dokuwiki\Search\Exception\IndexIntegrityException;
7use dokuwiki\Search\Exception\IndexLockException;
8use dokuwiki\Search\Exception\IndexWriteException;
9
10/**
11 * Abstract collection for direct 1:1 entity-token mappings
12 *
13 * In a direct collection each entity has exactly one token stored at the entity's position
14 * in the token index (entity.RID === token.RID). No frequency or reverse indexes are used.
15 *
16 * Example: each page has exactly one title.
17 *
18 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
19 * @author Andreas Gohr <andi@splitbrain.org>
20 */
21abstract class DirectCollection extends AbstractCollection
22{
23    /** @inheritdoc */
24    public function checkIntegrity(): void
25    {
26        $entityIndex = $this->getEntityIndex();
27        $tokenIndex = $this->getTokenIndex();
28
29        if ($entityIndex->exists() && $tokenIndex->exists()) {
30            $ec = count($entityIndex);
31            $tc = count($tokenIndex);
32            if ($ec !== $tc) {
33                throw new IndexIntegrityException(
34                    "Entity count ($ec) != token count ($tc)"
35                );
36            }
37        }
38    }
39
40    /**
41     * Store a single token for the given entity
42     *
43     * Takes the first token from the list and writes it directly at the entity's position
44     * in the token index. An empty list stores an empty string.
45     *
46     * @param string $entity The name of the entity
47     * @param string[] $tokens The list of tokens (only the first is used)
48     * @return static
49     * @throws IndexLockException
50     * @throws IndexAccessException
51     * @throws IndexWriteException
52     */
53    public function addEntity(string $entity, array $tokens): static
54    {
55        if (!$this->isWritable) {
56            throw new IndexLockException('Indexes not locked. Forgot to call lock()?');
57        }
58
59        $entityIndex = $this->getEntityIndex();
60        $entityId = $entityIndex->accessCachedValue($entity);
61
62        $token = $tokens[0] ?? '';
63        $tokenIndex = $this->getTokenIndex();
64        $tokenIndex->changeRow($entityId, $token);
65        $tokenIndex->save();
66
67        return $this;
68    }
69
70    /**
71     * Get the token stored for the given entity
72     *
73     * @param string $entity The name of the entity
74     * @return string The stored token, or empty string if none
75     * @throws IndexAccessException
76     * @throws IndexLockException
77     * @throws IndexWriteException
78     */
79    public function getToken(string $entity): string
80    {
81        $entityIndex = $this->getEntityIndex();
82        $entityId = $entityIndex->accessCachedValue($entity);
83
84        $tokenIndex = $this->getTokenIndex();
85        return $tokenIndex->retrieveRow($entityId);
86    }
87
88    /** @inheritdoc */
89    public function resolveTokenFrequencies(int $group, array $tokenIds): array
90    {
91        // In a DirectCollection, token RID = entity RID, frequency is always 1
92        $result = [];
93        foreach ($tokenIds as $tokenId) {
94            $result[$tokenId] = [$tokenId => 1];
95        }
96        return $result;
97    }
98
99    /** @inheritdoc */
100    public function getEntitiesWithData(): array
101    {
102        $entityIndex = $this->getEntityIndex();
103        $tokenIndex = $this->getTokenIndex();
104
105        $entities = [];
106        foreach ($tokenIndex as $entityId => $token) {
107            if ($token === '') continue;
108            $name = $entityIndex->retrieveRow($entityId);
109            if ($name !== '') {
110                $entities[] = $name;
111            }
112        }
113        return $entities;
114    }
115
116    /**
117     * Not actually used, because we override addEntity() to directly write the token.
118     * @inheritdoc
119     */
120    protected function countTokens(array $tokens): array
121    {
122        $token = $tokens[0] ?? '';
123        return [$token => 1];
124    }
125}
126