xref: /dokuwiki/_test/tests/Search/Collection/LookupCollectionTest.php (revision ede4646658cf51245060332d97a319a39c788ea1)
1*ede46466SAndreas Gohr<?php
2*ede46466SAndreas Gohr
3*ede46466SAndreas Gohrnamespace dokuwiki\test\Search\Collection;
4*ede46466SAndreas Gohr
5*ede46466SAndreas Gohruse dokuwiki\Search\Collection\PageMetaCollection;
6*ede46466SAndreas Gohruse dokuwiki\Search\Exception\IndexLockException;
7*ede46466SAndreas Gohruse dokuwiki\Search\Index\MemoryIndex;
8*ede46466SAndreas Gohr
9*ede46466SAndreas Gohrclass LookupCollectionTest extends \DokuWikiTest
10*ede46466SAndreas Gohr{
11*ede46466SAndreas Gohr    /**
12*ede46466SAndreas Gohr     * Add data and directly check the underlying indexes for correctness
13*ede46466SAndreas Gohr     */
14*ede46466SAndreas Gohr    public function testAddEntity()
15*ede46466SAndreas Gohr    {
16*ede46466SAndreas Gohr        $index = new MockLookupCollection('a_entity', 'a_token', 'a_freq', 'a_reverse');
17*ede46466SAndreas Gohr        $index->lock();
18*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg', 'wiki:icon.svg']);
19*ede46466SAndreas Gohr        $index->unlock();
20*ede46466SAndreas Gohr
21*ede46466SAndreas Gohr        // check entity index
22*ede46466SAndreas Gohr        $idxEntity = new MemoryIndex('a_entity');
23*ede46466SAndreas Gohr        $this->assertEquals('wiki:start', $idxEntity->retrieveRow(0));
24*ede46466SAndreas Gohr
25*ede46466SAndreas Gohr        // check token index (single file, no suffix)
26*ede46466SAndreas Gohr        $idxToken = new MemoryIndex('a_token');
27*ede46466SAndreas Gohr        $this->assertEquals('wiki:logo.png', $idxToken->retrieveRow(0));
28*ede46466SAndreas Gohr        $this->assertEquals('wiki:banner.jpg', $idxToken->retrieveRow(1));
29*ede46466SAndreas Gohr        $this->assertEquals('wiki:icon.svg', $idxToken->retrieveRow(2));
30*ede46466SAndreas Gohr
31*ede46466SAndreas Gohr        // check frequency index — all frequencies are 1 (written without *1)
32*ede46466SAndreas Gohr        $idxFreq = new MemoryIndex('a_freq');
33*ede46466SAndreas Gohr        $this->assertEquals('0', $idxFreq->retrieveRow(0)); // entity 0 with implicit freq 1
34*ede46466SAndreas Gohr        $this->assertEquals('0', $idxFreq->retrieveRow(1));
35*ede46466SAndreas Gohr        $this->assertEquals('0', $idxFreq->retrieveRow(2));
36*ede46466SAndreas Gohr
37*ede46466SAndreas Gohr        // check reverse index
38*ede46466SAndreas Gohr        $idxRev = new MemoryIndex('a_reverse');
39*ede46466SAndreas Gohr        $this->assertEquals('0:1:2', $idxRev->retrieveRow(0));
40*ede46466SAndreas Gohr    }
41*ede46466SAndreas Gohr
42*ede46466SAndreas Gohr    /**
43*ede46466SAndreas Gohr     * Duplicate tokens should be deduplicated
44*ede46466SAndreas Gohr     */
45*ede46466SAndreas Gohr    public function testAddEntityDedup()
46*ede46466SAndreas Gohr    {
47*ede46466SAndreas Gohr        $index = new MockLookupCollection('b_entity', 'b_token', 'b_freq', 'b_reverse');
48*ede46466SAndreas Gohr        $index->lock();
49*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:logo.png', 'wiki:banner.jpg']);
50*ede46466SAndreas Gohr        $index->unlock();
51*ede46466SAndreas Gohr
52*ede46466SAndreas Gohr        $idxToken = new MemoryIndex('b_token');
53*ede46466SAndreas Gohr        $this->assertEquals('wiki:logo.png', $idxToken->retrieveRow(0));
54*ede46466SAndreas Gohr        $this->assertEquals('wiki:banner.jpg', $idxToken->retrieveRow(1));
55*ede46466SAndreas Gohr
56*ede46466SAndreas Gohr        $idxRev = new MemoryIndex('b_reverse');
57*ede46466SAndreas Gohr        $this->assertEquals('0:1', $idxRev->retrieveRow(0));
58*ede46466SAndreas Gohr    }
59*ede46466SAndreas Gohr
60*ede46466SAndreas Gohr    /**
61*ede46466SAndreas Gohr     * Updating an entity should remove old tokens and add new ones
62*ede46466SAndreas Gohr     */
63*ede46466SAndreas Gohr    public function testUpdateEntity()
64*ede46466SAndreas Gohr    {
65*ede46466SAndreas Gohr        $index = new MockLookupCollection('c_entity', 'c_token', 'c_freq', 'c_reverse');
66*ede46466SAndreas Gohr
67*ede46466SAndreas Gohr        // initial add
68*ede46466SAndreas Gohr        $index->lock();
69*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg']);
70*ede46466SAndreas Gohr        $index->unlock();
71*ede46466SAndreas Gohr
72*ede46466SAndreas Gohr        // update: remove logo, keep banner, add icon
73*ede46466SAndreas Gohr        $index->lock();
74*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:banner.jpg', 'wiki:icon.svg']);
75*ede46466SAndreas Gohr        $index->unlock();
76*ede46466SAndreas Gohr
77*ede46466SAndreas Gohr        // logo should be removed from frequency index
78*ede46466SAndreas Gohr        $idxFreq = new MemoryIndex('c_freq');
79*ede46466SAndreas Gohr        $this->assertEquals('', $idxFreq->retrieveRow(0)); // logo removed
80*ede46466SAndreas Gohr        $this->assertEquals('0', $idxFreq->retrieveRow(1)); // banner still on entity 0
81*ede46466SAndreas Gohr        $this->assertEquals('0', $idxFreq->retrieveRow(2)); // icon added on entity 0
82*ede46466SAndreas Gohr
83*ede46466SAndreas Gohr        // reverse index should only have banner and icon
84*ede46466SAndreas Gohr        $idxRev = new MemoryIndex('c_reverse');
85*ede46466SAndreas Gohr        $this->assertEquals('1:2', $idxRev->retrieveRow(0));
86*ede46466SAndreas Gohr    }
87*ede46466SAndreas Gohr
88*ede46466SAndreas Gohr    /**
89*ede46466SAndreas Gohr     * Test reverse assignments returns two-level structure with empty group key
90*ede46466SAndreas Gohr     */
91*ede46466SAndreas Gohr    public function testReverseAssignments()
92*ede46466SAndreas Gohr    {
93*ede46466SAndreas Gohr        $index = new MockLookupCollection('d_entity', 'd_token', 'd_freq', 'd_reverse');
94*ede46466SAndreas Gohr        $index->lock();
95*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg']);
96*ede46466SAndreas Gohr        $index->unlock();
97*ede46466SAndreas Gohr
98*ede46466SAndreas Gohr        $result = $index->getReverseAssignments('wiki:start');
99*ede46466SAndreas Gohr        $this->assertEquals(['' => [0 => 0, 1 => 0]], $result);
100*ede46466SAndreas Gohr    }
101*ede46466SAndreas Gohr
102*ede46466SAndreas Gohr    /**
103*ede46466SAndreas Gohr     * Adding entity without lock should throw exception
104*ede46466SAndreas Gohr     */
105*ede46466SAndreas Gohr    public function testAddEntityWithoutLock()
106*ede46466SAndreas Gohr    {
107*ede46466SAndreas Gohr        $this->expectException(IndexLockException::class);
108*ede46466SAndreas Gohr
109*ede46466SAndreas Gohr        $index = new MockLookupCollection();
110*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png']);
111*ede46466SAndreas Gohr    }
112*ede46466SAndreas Gohr
113*ede46466SAndreas Gohr    /**
114*ede46466SAndreas Gohr     * Adding empty token list should clear entity from indexes
115*ede46466SAndreas Gohr     */
116*ede46466SAndreas Gohr    public function testEmptyTokens()
117*ede46466SAndreas Gohr    {
118*ede46466SAndreas Gohr        $index = new MockLookupCollection('f_entity', 'f_token', 'f_freq', 'f_reverse');
119*ede46466SAndreas Gohr
120*ede46466SAndreas Gohr        // add some tokens first
121*ede46466SAndreas Gohr        $index->lock();
122*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png']);
123*ede46466SAndreas Gohr        $index->unlock();
124*ede46466SAndreas Gohr
125*ede46466SAndreas Gohr        // now clear
126*ede46466SAndreas Gohr        $index->lock();
127*ede46466SAndreas Gohr        $index->addEntity('wiki:start', []);
128*ede46466SAndreas Gohr        $index->unlock();
129*ede46466SAndreas Gohr
130*ede46466SAndreas Gohr        // frequency index should be empty for this token
131*ede46466SAndreas Gohr        $idxFreq = new MemoryIndex('f_freq');
132*ede46466SAndreas Gohr        $this->assertEquals('', $idxFreq->retrieveRow(0));
133*ede46466SAndreas Gohr
134*ede46466SAndreas Gohr        // reverse index should be empty
135*ede46466SAndreas Gohr        $idxRev = new MemoryIndex('f_reverse');
136*ede46466SAndreas Gohr        $this->assertEquals('', $idxRev->retrieveRow(0));
137*ede46466SAndreas Gohr    }
138*ede46466SAndreas Gohr
139*ede46466SAndreas Gohr    /**
140*ede46466SAndreas Gohr     * Test that PageMetaCollection('relation_media') uses correct index names
141*ede46466SAndreas Gohr     */
142*ede46466SAndreas Gohr    public function testMediaCollection()
143*ede46466SAndreas Gohr    {
144*ede46466SAndreas Gohr        $index = new PageMetaCollection('relation_media');
145*ede46466SAndreas Gohr        $index->lock();
146*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg']);
147*ede46466SAndreas Gohr        $index->unlock();
148*ede46466SAndreas Gohr
149*ede46466SAndreas Gohr        $idxToken = new MemoryIndex('relation_media_w');
150*ede46466SAndreas Gohr        $this->assertEquals('wiki:logo.png', $idxToken->retrieveRow(0));
151*ede46466SAndreas Gohr        $this->assertEquals('wiki:banner.jpg', $idxToken->retrieveRow(1));
152*ede46466SAndreas Gohr
153*ede46466SAndreas Gohr        $idxRev = new MemoryIndex('relation_media_p');
154*ede46466SAndreas Gohr        $this->assertEquals('0:1', $idxRev->retrieveRow(0));
155*ede46466SAndreas Gohr    }
156*ede46466SAndreas Gohr
157*ede46466SAndreas Gohr    /**
158*ede46466SAndreas Gohr     * Test that PageMetaCollection('relation_references') uses correct index names
159*ede46466SAndreas Gohr     */
160*ede46466SAndreas Gohr    public function testReferencesCollection()
161*ede46466SAndreas Gohr    {
162*ede46466SAndreas Gohr        $index = new PageMetaCollection('relation_references');
163*ede46466SAndreas Gohr        $index->lock();
164*ede46466SAndreas Gohr        $index->addEntity('wiki:start', ['wiki:syntax', 'wiki:welcome']);
165*ede46466SAndreas Gohr        $index->unlock();
166*ede46466SAndreas Gohr
167*ede46466SAndreas Gohr        $idxToken = new MemoryIndex('relation_references_w');
168*ede46466SAndreas Gohr        $this->assertEquals('wiki:syntax', $idxToken->retrieveRow(0));
169*ede46466SAndreas Gohr        $this->assertEquals('wiki:welcome', $idxToken->retrieveRow(1));
170*ede46466SAndreas Gohr
171*ede46466SAndreas Gohr        $idxRev = new MemoryIndex('relation_references_p');
172*ede46466SAndreas Gohr        $this->assertEquals('0:1', $idxRev->retrieveRow(0));
173*ede46466SAndreas Gohr
174*ede46466SAndreas Gohr        $result = $index->getReverseAssignments('wiki:start');
175*ede46466SAndreas Gohr        $this->assertEquals(['' => [0 => 0, 1 => 0]], $result);
176*ede46466SAndreas Gohr    }
177*ede46466SAndreas Gohr
178*ede46466SAndreas Gohr    /**
179*ede46466SAndreas Gohr     * resolveTokens should deduplicate and assign frequency 1 under empty group key
180*ede46466SAndreas Gohr     */
181*ede46466SAndreas Gohr    public function testResolveTokens()
182*ede46466SAndreas Gohr    {
183*ede46466SAndreas Gohr        $index = new MockLookupCollection('rt_entity', 'rt_token', 'rt_freq', 'rt_reverse');
184*ede46466SAndreas Gohr        $index->lock();
185*ede46466SAndreas Gohr
186*ede46466SAndreas Gohr        $result = $this->callInaccessibleMethod($index, 'resolveTokens', [
187*ede46466SAndreas Gohr            ['wiki:logo.png', 'wiki:banner.jpg', 'wiki:logo.png'],
188*ede46466SAndreas Gohr        ]);
189*ede46466SAndreas Gohr
190*ede46466SAndreas Gohr        // all tokens under empty group key
191*ede46466SAndreas Gohr        $this->assertArrayHasKey('', $result);
192*ede46466SAndreas Gohr        $this->assertCount(2, $result['']); // deduplicated
193*ede46466SAndreas Gohr
194*ede46466SAndreas Gohr        // token IDs are sequential: logo=0, banner=1
195*ede46466SAndreas Gohr        $this->assertEquals(1, $result[''][0]); // logo freq=1
196*ede46466SAndreas Gohr        $this->assertEquals(1, $result[''][1]); // banner freq=1
197*ede46466SAndreas Gohr    }
198*ede46466SAndreas Gohr
199*ede46466SAndreas Gohr    /**
200*ede46466SAndreas Gohr     * resolveTokens with empty input should return empty array
201*ede46466SAndreas Gohr     */
202*ede46466SAndreas Gohr    public function testResolveTokensEmpty()
203*ede46466SAndreas Gohr    {
204*ede46466SAndreas Gohr        $index = new MockLookupCollection('rte_entity', 'rte_token', 'rte_freq', 'rte_reverse');
205*ede46466SAndreas Gohr        $index->lock();
206*ede46466SAndreas Gohr
207*ede46466SAndreas Gohr        $result = $this->callInaccessibleMethod($index, 'resolveTokens', [[]]);
208*ede46466SAndreas Gohr
209*ede46466SAndreas Gohr        $this->assertEmpty($result);
210*ede46466SAndreas Gohr    }
211*ede46466SAndreas Gohr
212*ede46466SAndreas Gohr    /**
213*ede46466SAndreas Gohr     * countTokens should deduplicate and assign frequency 1
214*ede46466SAndreas Gohr     */
215*ede46466SAndreas Gohr    public function testCountTokens()
216*ede46466SAndreas Gohr    {
217*ede46466SAndreas Gohr        $index = new MockLookupCollection();
218*ede46466SAndreas Gohr
219*ede46466SAndreas Gohr        $result = $this->callInaccessibleMethod($index, 'countTokens', [
220*ede46466SAndreas Gohr            ['wiki:logo.png', 'wiki:banner.jpg', 'wiki:logo.png'],
221*ede46466SAndreas Gohr        ]);
222*ede46466SAndreas Gohr
223*ede46466SAndreas Gohr        $this->assertEquals([
224*ede46466SAndreas Gohr            'wiki:logo.png' => 1,
225*ede46466SAndreas Gohr            'wiki:banner.jpg' => 1,
226*ede46466SAndreas Gohr        ], $result);
227*ede46466SAndreas Gohr    }
228*ede46466SAndreas Gohr}
229