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