Skip to content

Commit 69dcc34

Browse files
authored
Merge pull request #90 from oskardydo/Q3-initiative
[TASK] Add suggestion endpoint
2 parents f829c63 + adfb4ba commit 69dcc34

17 files changed

+797
-149
lines changed

.ddev/config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ php_version: "8.2"
55
webserver_type: nginx-fpm
66
router_http_port: "80"
77
router_https_port: "443"
8-
xdebug_enabled: true
8+
xdebug_enabled: trueent: [ ]
99
additional_hostnames: []
1010
additional_fqdns: []
1111
use_dns_when_possible: true
@@ -156,7 +156,7 @@ omit_containers: [db]
156156
# In this case the user must provide all such settings.
157157

158158
# You can inject environment variables into the web container with:
159-
# web_environment:
159+
# web_environment:
160160
# - SOMEENV=somevalue
161161
# - SOMEOTHERENV=someothervalue
162162

@@ -167,7 +167,7 @@ omit_containers: [db]
167167
# For advanced users only!
168168

169169
# provider: default # Currently "default", "pantheon", "ddev-live"
170-
#
170+
#
171171
# Many ddev commands can be extended to run tasks before or after the
172172
# ddev command is executed, for example "post-start", "post-import-db",
173173
# "pre-composer", "post-composer"

.ddev/docker-compose.environment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ services:
44
environment:
55
- APP_ENV=dev
66
- APP_SECRET=ddf011a53a72d4f85e6fcfd4d1a72737
7-
- DOCS_ROOT_PATH=../docs_server/docs.typo3.org/Web
7+
- DOCS_ROOT_PATH=/var/www/html/docs_server/docs.typo3.org/Web
88
- ELASTICA_HOST=elasticsearch
99
- PHP_IDE_CONFIG=serverName=t3docs-search-indexer.ddev.site

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/_docs
2+
/docs_server*
23

34
###> symfony/framework-bundle ###
45
.env

config/Elasticorn/docsearch/IndexConfiguration.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
number_of_shards: 4
22
number_of_replicas: 1
33
analysis:
4+
tokenizer:
5+
autocomplete_small_tokenizer:
6+
type: ngram
7+
min_gram: 1
8+
max_gram: 2
9+
token_chars: ["letter", "digit"]
10+
autocomplete_large_tokenizer:
11+
type: ngram
12+
min_gram: 3
13+
max_gram: 4
14+
token_chars: ["letter", "digit"]
415
filter:
516
typo3_stemmer:
617
type: stemmer
@@ -17,4 +28,16 @@ analysis:
1728
- asciifolding
1829
- typo3_filter
1930
- typo3_stemmer
31+
typo3_autocomplete_small:
32+
type: custom
33+
tokenizer: autocomplete_small_tokenizer
34+
filter:
35+
- lowercase
36+
- asciifolding
37+
typo3_autocomplete_large:
38+
type: custom
39+
tokenizer: autocomplete_large_tokenizer
40+
filter:
41+
- lowercase
42+
- asciifolding
2043

config/Elasticorn/docsearch/Mapping.yaml

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,57 @@ manual_type:
1010
type: keyword
1111
manual_version:
1212
type: keyword
13+
fields:
14+
small_suggest:
15+
type: search_as_you_type
16+
analyzer: typo3_autocomplete_small
17+
large_suggest:
18+
type: search_as_you_type
19+
analyzer: typo3_autocomplete_large
1320
manual_language:
1421
type: keyword
1522
manual_slug:
1623
type: keyword
1724
manual_keywords:
1825
type: keyword
26+
manual_vendor:
27+
type: keyword
28+
fields:
29+
small_suggest:
30+
type: search_as_you_type
31+
analyzer: typo3_autocomplete_small
32+
large_suggest:
33+
type: search_as_you_type
34+
analyzer: typo3_autocomplete_large
35+
manual_package:
36+
type: keyword
37+
fields:
38+
small_suggest:
39+
type: search_as_you_type
40+
analyzer: typo3_autocomplete_small
41+
large_suggest:
42+
type: search_as_you_type
43+
analyzer: typo3_autocomplete_large
44+
manual_extension:
45+
type: keyword
46+
fields:
47+
small_suggest:
48+
type: search_as_you_type
49+
analyzer: typo3_autocomplete_small
50+
large_suggest:
51+
type: search_as_you_type
52+
analyzer: typo3_autocomplete_large
53+
option:
54+
type: keyword
55+
fields:
56+
small_suggest:
57+
type: search_as_you_type
58+
analyzer: typo3_autocomplete_small
59+
large_suggest:
60+
type: search_as_you_type
61+
analyzer: typo3_autocomplete_large
62+
option_keywords:
63+
type: keyword
1964
fragment:
2065
type: keyword
2166
page_title:
@@ -34,4 +79,6 @@ snippet_content:
3479
content_hash:
3580
type: keyword
3681
major_versions:
37-
type: keyword
82+
type: keyword
83+
is_core:
84+
type: boolean

src/Config/Labels.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Config;
6+
7+
class Labels
8+
{
9+
public const MAP = [
10+
'manual_vendor' => 'Vendor',
11+
'manual_package' => 'Package',
12+
'manual_version' => 'Version',
13+
'is_core' => 'Core?',
14+
'manual_type' => 'Document Type',
15+
'major_versions' => 'Major Version',
16+
'manual_language' => 'Language',
17+
'option' => 'Option',
18+
'optionaggs' => 'Option',
19+
];
20+
21+
public static function getLabelForEsColumn(string $filter, string $default = ''): string
22+
{
23+
if ($default !== '') {
24+
return self::MAP[$filter] ?? $default;
25+
}
26+
27+
return self::MAP[$filter] ?? $filter;
28+
}
29+
}

src/Config/ManualType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ public static function getMap(): array
3737
'typo3cms' => self::ExceptionReference->value,
3838
];
3939
}
40-
}
40+
}

src/Controller/SearchController.php

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
use App\Dto\SearchDemand;
66
use App\Repository\ElasticRepository;
77
use Elastica\Exception\InvalidException;
8-
use JsonException;
98
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
9+
use Symfony\Component\HttpFoundation\JsonResponse;
1010
use Symfony\Component\HttpFoundation\Request;
1111
use Symfony\Component\HttpFoundation\Response;
1212
use Symfony\Component\Routing\Annotation\Route;
@@ -36,6 +36,7 @@ public function search(Request $request): Response
3636
if ($request->query->get('q', '') === '') {
3737
return $this->redirectToRoute('index');
3838
}
39+
3940
$searchDemand = SearchDemand::createFromRequest($request);
4041

4142
return $this->render('search/search.html.twig', [
@@ -46,31 +47,49 @@ public function search(Request $request): Response
4647
]);
4748
}
4849

49-
/**
50-
* @return Response
51-
* @throws InvalidException|JsonException
52-
*/
5350
#[Route(path: '/suggest', name: 'suggest')]
5451
public function suggest(Request $request): Response
5552
{
5653
$searchDemand = SearchDemand::createFromRequest($request);
54+
$jsonData = [
55+
'demand' => $searchDemand->toArray(),
56+
'suggest' => $this->elasticRepository->suggestScopes($searchDemand)
57+
];
5758

58-
$results = $this->elasticRepository->suggest($searchDemand);
59-
$suggestions = [];
60-
foreach ($results['results'] as $result) {
61-
$hit = $result->getData();
62-
$suggestions[] = [
63-
'label' => $hit['snippet_title'],
64-
'value' => $hit['snippet_title'],
65-
'url' => 'https://docs.typo3.org/' . $hit['manual_slug'] . '/' . $hit['relative_url'] . '#' . $hit['fragment'],
66-
'group' => $hit['manual_title'],
67-
'content' => \mb_substr((string)$hit['snippet_content'], 0, 100)
68-
];
69-
}
70-
$jsonBody = \json_encode($suggestions, JSON_THROW_ON_ERROR);
59+
$searchResults = $this->elasticRepository->searchDocumentsForSuggest($searchDemand);
60+
$jsonData['time'] = $searchResults['time'];
61+
62+
$jsonData['results'] = array_map(static function ($result) {
63+
return $result->getData();
64+
}, $searchResults['results']);
65+
66+
return new JsonResponse($jsonData);
67+
}
68+
69+
#[Route(path: '/suggest/list', name: 'suggest-list')]
70+
public function suggestList(Request $request): Response
71+
{
72+
$searchDemand = SearchDemand::createFromRequest($request);
73+
$jsonData = [
74+
'demand' => $searchDemand->toArray(),
75+
'suggest' => $this->elasticRepository->suggestScopes($searchDemand)
76+
];
77+
78+
return new JsonResponse($jsonData);
79+
}
80+
81+
#[Route(path: '/suggest/results', name: 'suggest-results')]
82+
public function suggestResults(Request $request): Response
83+
{
84+
$searchDemand = SearchDemand::createFromRequest($request);
85+
86+
$searchResults = $this->elasticRepository->searchDocumentsForSuggest($searchDemand);
87+
$jsonData['time'] = $searchResults['time'];
88+
89+
$jsonData['results'] = array_map(static function ($result) {
90+
return $result->getData();
91+
}, $searchResults['results']);
7192

72-
$response = new Response();
73-
$response->setContent($jsonBody);
74-
return $response;
93+
return new JsonResponse($jsonData);
7594
}
7695
}

src/Dto/Manual.php

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ class Manual
99
{
1010
public function __construct(
1111
private readonly string $absolutePath,
12-
private readonly string $title,
12+
private readonly string $name,
1313
private readonly string $type,
1414
private readonly string $version,
1515
private readonly string $language,
1616
private readonly string $slug,
17-
private readonly array $keywords
17+
private readonly array $keywords,
18+
private readonly string $vendor = '',
19+
private readonly bool $isCore = false,
1820
) {
1921
}
2022

@@ -43,19 +45,23 @@ public static function createFromFolder(\SplFileInfo $folder, $changelog = false
4345

4446
$map = ManualType::getMap();
4547
$type = $map[$type] ?? $type;
48+
$isCore = in_array($type, [ManualType::SystemExtension->value, ManualType::Typo3Manual->value, ManualType::CoreChangelog->value], true);
4649

4750
$keywords = [];
4851
if ($type === ManualType::SystemExtension->value || $type === ManualType::CommunityExtension->value) {
4952
$keywords[] = $name;
5053
}
54+
5155
return new Manual(
5256
$folder,
53-
implode('/', [$vendor, $name]),
57+
$name,
5458
$type,
5559
$version,
5660
$language,
5761
implode('/', $values),
58-
$keywords
62+
$keywords,
63+
$vendor,
64+
$isCore,
5965
);
6066
}
6167

@@ -98,6 +104,7 @@ public function getSubManuals(): array
98104
->depth(0);
99105

100106
$subManuals = [];
107+
101108
foreach ($finder as $changelogFolder) {
102109
$subManuals[] = self::createFromFolder($changelogFolder, true);
103110
}
@@ -111,7 +118,32 @@ public function getAbsolutePath(): string
111118

112119
public function getTitle(): string
113120
{
114-
return $this->title;
121+
$titleParts = [];
122+
123+
if ($this->vendor !== '') {
124+
$titleParts[] = $this->vendor;
125+
}
126+
127+
if ($this->name !== '') {
128+
$titleParts[] = $this->name;
129+
}
130+
131+
return implode('/', $titleParts);
132+
}
133+
134+
public function getName(): string
135+
{
136+
return $this->name;
137+
}
138+
139+
public function getVendor(): string
140+
{
141+
return $this->vendor;
142+
}
143+
144+
public function isCore(): bool
145+
{
146+
return $this->isCore;
115147
}
116148

117149
public function getType(): string

0 commit comments

Comments
 (0)