xref: /dokuwiki/inc/Search/Collection/DirectCollection.php (revision 21fbd01b3c3eea88b767376b7b158f31f0f63127)
1d92c078cSAndreas Gohr<?php
2d92c078cSAndreas Gohr
3d92c078cSAndreas Gohrnamespace dokuwiki\Search\Collection;
4d92c078cSAndreas Gohr
5d92c078cSAndreas Gohruse dokuwiki\Search\Exception\IndexAccessException;
6*21fbd01bSAndreas Gohruse dokuwiki\Search\Exception\IndexIntegrityException;
7d92c078cSAndreas Gohruse dokuwiki\Search\Exception\IndexLockException;
8d92c078cSAndreas Gohruse dokuwiki\Search\Exception\IndexWriteException;
9d92c078cSAndreas Gohr
10d92c078cSAndreas Gohr/**
11d92c078cSAndreas Gohr * Abstract collection for direct 1:1 entity-token mappings
12d92c078cSAndreas Gohr *
13d92c078cSAndreas Gohr * In a direct collection each entity has exactly one token stored at the entity's position
14d92c078cSAndreas Gohr * in the token index (entity.RID === token.RID). No frequency or reverse indexes are used.
15d92c078cSAndreas Gohr *
16d92c078cSAndreas Gohr * Example: each page has exactly one title.
17d92c078cSAndreas Gohr *
18d92c078cSAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
19d92c078cSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
20d92c078cSAndreas Gohr */
21d92c078cSAndreas Gohrabstract class DirectCollection extends AbstractCollection
22d92c078cSAndreas Gohr{
23*21fbd01bSAndreas Gohr    /** @inheritdoc */
24*21fbd01bSAndreas Gohr    public function checkIntegrity(): void
25*21fbd01bSAndreas Gohr    {
26*21fbd01bSAndreas Gohr        $entityIndex = $this->getEntityIndex();
27*21fbd01bSAndreas Gohr        $tokenIndex = $this->getTokenIndex(0);
28*21fbd01bSAndreas Gohr
29*21fbd01bSAndreas Gohr        if ($entityIndex->exists() && $tokenIndex->exists()) {
30*21fbd01bSAndreas Gohr            $ec = count($entityIndex);
31*21fbd01bSAndreas Gohr            $tc = count($tokenIndex);
32*21fbd01bSAndreas Gohr            if ($ec !== $tc) {
33*21fbd01bSAndreas Gohr                throw new IndexIntegrityException(
34*21fbd01bSAndreas Gohr                    "Entity count ($ec) != token count ($tc)"
35*21fbd01bSAndreas Gohr                );
36*21fbd01bSAndreas Gohr            }
37*21fbd01bSAndreas Gohr        }
38*21fbd01bSAndreas Gohr    }
39*21fbd01bSAndreas Gohr
40d92c078cSAndreas Gohr    /**
41d92c078cSAndreas Gohr     * Store a single token for the given entity
42d92c078cSAndreas Gohr     *
43d92c078cSAndreas Gohr     * Takes the first token from the list and writes it directly at the entity's position
44d92c078cSAndreas Gohr     * in the token index. An empty list stores an empty string.
45d92c078cSAndreas Gohr     *
46d92c078cSAndreas Gohr     * @param string $entity The name of the entity
47d92c078cSAndreas Gohr     * @param string[] $tokens The list of tokens (only the first is used)
4883b3acccSAndreas Gohr     * @return static
49d92c078cSAndreas Gohr     * @throws IndexLockException
50d92c078cSAndreas Gohr     * @throws IndexAccessException
51d92c078cSAndreas Gohr     * @throws IndexWriteException
52d92c078cSAndreas Gohr     */
5383b3acccSAndreas Gohr    public function addEntity(string $entity, array $tokens): static
54d92c078cSAndreas Gohr    {
55d92c078cSAndreas Gohr        if (!$this->isWritable) {
56d92c078cSAndreas Gohr            throw new IndexLockException('Indexes not locked. Forgot to call lock()?');
57d92c078cSAndreas Gohr        }
58d92c078cSAndreas Gohr
59d92c078cSAndreas Gohr        $entityIndex = $this->getEntityIndex();
60d92c078cSAndreas Gohr        $entityId = $entityIndex->accessCachedValue($entity);
61d92c078cSAndreas Gohr
62d92c078cSAndreas Gohr        $token = $tokens[0] ?? '';
636734bb8cSAndreas Gohr        $tokenIndex = $this->getTokenIndex(0);
64d92c078cSAndreas Gohr        $tokenIndex->changeRow($entityId, $token);
65d92c078cSAndreas Gohr        $tokenIndex->save();
6683b3acccSAndreas Gohr
6783b3acccSAndreas Gohr        return $this;
68d92c078cSAndreas Gohr    }
69d92c078cSAndreas Gohr
70d92c078cSAndreas Gohr    /**
71d92c078cSAndreas Gohr     * Get the token stored for the given entity
72d92c078cSAndreas Gohr     *
73d92c078cSAndreas Gohr     * @param string $entity The name of the entity
74d92c078cSAndreas Gohr     * @return string The stored token, or empty string if none
75d92c078cSAndreas Gohr     * @throws IndexAccessException
76d92c078cSAndreas Gohr     * @throws IndexLockException
77d92c078cSAndreas Gohr     * @throws IndexWriteException
78d92c078cSAndreas Gohr     */
79d92c078cSAndreas Gohr    public function getToken(string $entity): string
80d92c078cSAndreas Gohr    {
81d92c078cSAndreas Gohr        $entityIndex = $this->getEntityIndex();
82d92c078cSAndreas Gohr        $entityId = $entityIndex->accessCachedValue($entity);
83d92c078cSAndreas Gohr
846734bb8cSAndreas Gohr        $tokenIndex = $this->getTokenIndex(0);
85d92c078cSAndreas Gohr        return $tokenIndex->retrieveRow($entityId);
86d92c078cSAndreas Gohr    }
87d92c078cSAndreas Gohr
886734bb8cSAndreas Gohr    /** @inheritdoc */
896734bb8cSAndreas Gohr    public function resolveTokenFrequencies(int $group, array $tokenIds): array
906734bb8cSAndreas Gohr    {
916734bb8cSAndreas Gohr        // In a DirectCollection, token RID = entity RID, frequency is always 1
926734bb8cSAndreas Gohr        $result = [];
936734bb8cSAndreas Gohr        foreach ($tokenIds as $tokenId) {
946734bb8cSAndreas Gohr            $result[$tokenId] = [$tokenId => 1];
956734bb8cSAndreas Gohr        }
966734bb8cSAndreas Gohr        return $result;
976734bb8cSAndreas Gohr    }
986734bb8cSAndreas Gohr
996734bb8cSAndreas Gohr    /** @inheritdoc */
1006734bb8cSAndreas Gohr    public function getEntitiesWithData(): array
1016734bb8cSAndreas Gohr    {
1026734bb8cSAndreas Gohr        $entityIndex = $this->getEntityIndex();
1036734bb8cSAndreas Gohr        $tokenIndex = $this->getTokenIndex(0);
1046734bb8cSAndreas Gohr
1056734bb8cSAndreas Gohr        $entities = [];
1066734bb8cSAndreas Gohr        foreach ($tokenIndex as $entityId => $token) {
1076734bb8cSAndreas Gohr            if ($token === '') continue;
1086734bb8cSAndreas Gohr            $name = $entityIndex->retrieveRow($entityId);
1096734bb8cSAndreas Gohr            if ($name !== '') {
1106734bb8cSAndreas Gohr                $entities[] = $name;
1116734bb8cSAndreas Gohr            }
1126734bb8cSAndreas Gohr        }
1136734bb8cSAndreas Gohr        return $entities;
1146734bb8cSAndreas Gohr    }
1156734bb8cSAndreas Gohr
116d92c078cSAndreas Gohr    /**
117d92c078cSAndreas Gohr     * Not actually used, because we override addEntity() to directly write the token.
118d92c078cSAndreas Gohr     * @inheritdoc
119d92c078cSAndreas Gohr     */
120d92c078cSAndreas Gohr    protected function countTokens(array $tokens): array
121d92c078cSAndreas Gohr    {
122d92c078cSAndreas Gohr        $token = $tokens[0] ?? '';
123d92c078cSAndreas Gohr        return [$token => 1];
124d92c078cSAndreas Gohr    }
125d92c078cSAndreas Gohr}
126