ArticleDocumentSource
in package
implements
DocumentSourceInterface, HighlightableDocumentSourceInterface, VisibilityConstrainedDocumentSourceInterface
Builds the Elasticsearch documents of an article: one document per (article, language), one index per language (`blog_article_{alias}`), `_id` = `{articleId}_{languageId}`.
ArticleText has a derived identity (languageId plus parent) and no PK of its own.
Visibility is enforced by the database (SearchIdFilter is phaseOneOnly). A deactivated
article is removed from the index (empty buildDocuments()); published is an indexed field
and the Elasticsearch query pre-narrows by it as a query-time condition
(getVisibilityFilters()).
Table of Contents
Interfaces
- DocumentSourceInterface
- Source of documents for one indexed entity (Article, Advert, Message…).
- HighlightableDocumentSourceInterface
- Source that can supply highlighted snippets for search results.
- VisibilityConstrainedDocumentSourceInterface
- Source that can pass its visibility conditions to the ES query.
Constants
Properties
- $em : EntityManagerInterface
- $languageManager : LanguageManager
- $languages : array<int, string>|null
- $service : ArticleService
Methods
- __construct() : mixed
- buildDocuments() : array<string, array<string, array<string, mixed>>>
- Builds the documents of an entity as `indexBase => (_id => document)`. An empty array means the entity must not be indexed (deactivated or without an active translation), so it is deleted.
- expectedDocumentBounds() : array{min: int, max: int}
- Mirrors the `buildDocuments()` guards: a document exists only for an activated article (`activated` = 1) with at least one active translation. The minimum is the number of such articles (count distinct, so the dot-path join on translations does not multiply rows), the maximum is the minimum times the number of languages (an upper estimate, not every article has all languages active).
- fetchExistingIds() : array<string|int, int>
- Returns which of the given ids really exist in the DB; orphan cleanup deletes from ES the documents of entities no longer in the DB.
- fetchFresh() : array<int, object>
- Returns fresh entities after commit (service `find` with `useCache: false`). An entity from `completedSave` is not authoritative, because `afterSave` changes the DB through `updateColumn` after the flush. A missing id is omitted.
- fetchIdsChangedSince() : array<string|int, int>
- Returns ids of entities changed or created since `$since` (DB, no cache) as the input of the incremental sync. It must cover both insert and update (`insertTime` or `updateDatetime`); mutations outside the ORM (`updateColumn`, raw SQL) are never covered and the nightly full reindex catches them up.
- fetchReindexBatch() : array<int, object>
- Returns a batch of entities for a full reindex by key: `id > $afterId`, ordered ascending by id, at most `$limit` (with includes, no cache). The array key is the entity id; the last one becomes the cursor the next part of the reindex continues from.
- getAllDocumentIds() : array<string, array<string|int, string>>
- Returns every possible `_id` of the entity across languages as `indexBase => [_id, …]`.
- getEntityIdField() : string
- Returns the ES document field holding the entity id (e.g. `articleId`); sync sorts and pages (`search_after`) by it during the orphan scan.
- getHighlightFields() : array<string, array<int, string>>
- Highlights the name and perex, exactly what the search list renders (`.list-item__title` / `.list-item__desc`). `content` is not highlighted: it is not in the list, and `number_of_fragments: 0` would pull whole article bodies through Elasticsearch.
- getIndexBaseForLanguage() : string|null
- Returns the index base for a language alias (`cs`), null when the language does not exist.
- getIndexBases() : array<string|int, string>
- Returns index base names without prefix and version; one per language for translated entities (e.g. `['blog_article_cs', 'blog_article_en']`).
- getIndexDefinition() : array<string, mixed>
- Returns the index definition (settings and mappings) for index creation.
- getKey() : string
- Returns the source key for the `enabledSources` allowlist and `EsSearcher`, e.g. `blog_article`.
- getSearchFields() : array<string, int|float>
- Returns the `multi_match` fields with boosts, e.g. `['name' => 3, 'perex' => 2, 'content' => 1]`.
- getVisibilityFilters() : array<int, array<string, mixed>>
- Pre-narrows the Elasticsearch query by the publication date (query-time, cannot go stale).
- getLanguages() : array<int, string>
- Assigns the cache only after a successful load: an exception from `findAll()` must not leave the cache marked as loaded and empty.
- toIds() : array<string|int, int>
- toPlainText() : string
- `content` and `perex` are sanitized TinyMCE HTML; only plain text goes to the index.
Constants
INCLUDES
private
mixed
INCLUDES
= ['translations', 'categories', 'tags']
KEY
private
mixed
KEY
= 'blog_article'
Properties
$em read-only
private
EntityManagerInterface
$em
$languageManager read-only
private
LanguageManager
$languageManager
$languages
private
array<int, string>|null
$languages
= null
languageId => alias
$service read-only
private
ArticleService
$service
Methods
__construct()
public
__construct(ArticleService $service, LanguageManager $languageManager, EntityManagerInterface $em) : mixed
Parameters
- $service : ArticleService
- $languageManager : LanguageManager
- $em : EntityManagerInterface
buildDocuments()
Builds the documents of an entity as `indexBase => (_id => document)`. An empty array means the entity must not be indexed (deactivated or without an active translation), so it is deleted.
public
buildDocuments(object $entity) : array<string, array<string, array<string, mixed>>>
Parameters
- $entity : object
Return values
array<string, array<string, array<string, mixed>>>expectedDocumentBounds()
Mirrors the `buildDocuments()` guards: a document exists only for an activated article (`activated` = 1) with at least one active translation. The minimum is the number of such articles (count distinct, so the dot-path join on translations does not multiply rows), the maximum is the minimum times the number of languages (an upper estimate, not every article has all languages active).
public
expectedDocumentBounds() : array{min: int, max: int}
Return values
array{min: int, max: int}fetchExistingIds()
Returns which of the given ids really exist in the DB; orphan cleanup deletes from ES the documents of entities no longer in the DB.
public
fetchExistingIds(array<string|int, mixed> $ids) : array<string|int, int>
Parameters
- $ids : array<string|int, mixed>
Return values
array<string|int, int>fetchFresh()
Returns fresh entities after commit (service `find` with `useCache: false`). An entity from `completedSave` is not authoritative, because `afterSave` changes the DB through `updateColumn` after the flush. A missing id is omitted.
public
fetchFresh(array<string|int, mixed> $ids) : array<int, object>
Parameters
- $ids : array<string|int, mixed>
Return values
array<int, object> —keyed by id
fetchIdsChangedSince()
Returns ids of entities changed or created since `$since` (DB, no cache) as the input of the incremental sync. It must cover both insert and update (`insertTime` or `updateDatetime`); mutations outside the ORM (`updateColumn`, raw SQL) are never covered and the nightly full reindex catches them up.
public
fetchIdsChangedSince(DateTimeImmutable $since) : array<string|int, int>
Parameters
- $since : DateTimeImmutable
Return values
array<string|int, int>fetchReindexBatch()
Returns a batch of entities for a full reindex by key: `id > $afterId`, ordered ascending by id, at most `$limit` (with includes, no cache). The array key is the entity id; the last one becomes the cursor the next part of the reindex continues from.
public
fetchReindexBatch(int $afterId, int $limit) : array<int, object>
Parameters
- $afterId : int
- $limit : int
Return values
array<int, object> —keyed by id, ascending
getAllDocumentIds()
Returns every possible `_id` of the entity across languages as `indexBase => [_id, …]`.
public
getAllDocumentIds(int $id) : array<string, array<string|int, string>>
It serves targeted deletion of orphan language documents (orphanRemoval on translations)
and delete; delete_by_query must never be used.
Parameters
- $id : int
Return values
array<string, array<string|int, string>>getEntityIdField()
Returns the ES document field holding the entity id (e.g. `articleId`); sync sorts and pages (`search_after`) by it during the orphan scan.
public
getEntityIdField() : string
Return values
stringgetHighlightFields()
Highlights the name and perex, exactly what the search list renders (`.list-item__title` / `.list-item__desc`). `content` is not highlighted: it is not in the list, and `number_of_fragments: 0` would pull whole article bodies through Elasticsearch.
public
getHighlightFields() : array<string, array<int, string>>
The base field comes first (stemming also highlights inflected forms), .folded is the
fallback for queries without diacritics. Both are needed: the Czech analysis has stopwords,
so a stopword query matches only the .folded variant.
Return values
array<string, array<int, string>>getIndexBaseForLanguage()
Returns the index base for a language alias (`cs`), null when the language does not exist.
public
getIndexBaseForLanguage(string|null $languageAlias) : string|null
Parameters
- $languageAlias : string|null
Return values
string|nullgetIndexBases()
Returns index base names without prefix and version; one per language for translated entities (e.g. `['blog_article_cs', 'blog_article_en']`).
public
getIndexBases() : array<string|int, string>
Return values
array<string|int, string>getIndexDefinition()
Returns the index definition (settings and mappings) for index creation.
public
getIndexDefinition(string $indexBase) : array<string, mixed>
Parameters
- $indexBase : string
Return values
array<string, mixed>getKey()
Returns the source key for the `enabledSources` allowlist and `EsSearcher`, e.g. `blog_article`.
public
getKey() : string
Return values
stringgetSearchFields()
Returns the `multi_match` fields with boosts, e.g. `['name' => 3, 'perex' => 2, 'content' => 1]`.
public
getSearchFields() : array<string, int|float>
Return values
array<string, int|float>getVisibilityFilters()
Pre-narrows the Elasticsearch query by the publication date (query-time, cannot go stale).
public
getVisibilityFilters() : array<int, array<string, mixed>>
An article with published in the future is in the index (publication is not a
buildDocuments() guard because it changes without a write), so without this the relevance
path counts it in totalCount and it takes a page slot even though the database phase
drops it.
Mirrors the front end ArticleListTrait::prepareVisibilityFilters():
DateBeforeFilter('published', new DateTime()) means DATE(published) < today
(format Y-m-d, null does not pass), expressed as lt now/d.
IMPORTANT: time_zone is required: without it now/d rounds to midnight UTC and between
00:00 and 02:00 Prague time articles published yesterday vanish from the search although
the database shows them. The forFree category does not belong here: it depends on user
access, so only the database evaluates it.
Return values
array<int, array<string, mixed>>getLanguages()
Assigns the cache only after a successful load: an exception from `findAll()` must not leave the cache marked as loaded and empty.
private
getLanguages() : array<int, string>
Return values
array<int, string> —languageId => alias
toIds()
private
toIds(array<string|int, object> $entities) : array<string|int, int>
Parameters
- $entities : array<string|int, object>
Return values
array<string|int, int>toPlainText()
`content` and `perex` are sanitized TinyMCE HTML; only plain text goes to the index.
private
toPlainText(string|null $html) : string
Parameters
- $html : string|null