CoolCMS API

MessageDocumentSource
in package
implements HighlightableDocumentSourceInterface, VisibilityConstrainedDocumentSourceInterface

FinalYes

Builds Elasticsearch documents of discussion messages.

Message has no language column, so there is one global, language-agnostic index discussion_message (unlike Blog/Advert). The document _id is {messageId}_0, where 0 is the "no language" placeholder (EsSearcher reads the id from _id with strtok '_').

Only thread roots (level = 1) are indexed, because the front list and full-text search look only for them (see MessageListTrait::prepareFilters()). An empty buildDocuments() (reply, deleted or banned message) makes the indexer delete the document.

The DB decides visibility (SearchIdFilter is phaseOneOnly); the flags in the document are for ES-side filtering. Tree delete (TreeTrait::deleteNode()) does not fire the completedDelete hook, so deleted threads are covered only by the sync orphan cleanup (id in ES without a DB row); a soft delete (deleted = 1) is caught up by the nightly full reindex.

Table of Contents

Interfaces

HighlightableDocumentSourceInterface
Source that can supply highlighted snippets for search results.
VisibilityConstrainedDocumentSourceInterface
Source that can pass its visibility conditions to the ES query.

Constants

INCLUDES  : mixed = ['categories']
Relations `buildDocuments()` needs (`categoryIds`).
KEY  : mixed = 'discussion_message'

Properties

$em  : EntityManagerInterface
$service  : MessageService

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 a non-deleted, non-banned thread root (level=1). One global index, so min equals max (no language multiplier as in Blog).
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>>
Returns the highlight fields for `subject` and `text`, exactly what the thread list shows as title and description.
getIndexBaseForLanguage()  : string
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 ES query by visibility.
toIds()  : array<string|int, int>
toPlainText()  : string
Strips HTML (`subject`/`text` may carry rich text) so only plain text is indexed.

Constants

INCLUDES

Relations `buildDocuments()` needs (`categoryIds`).

private mixed INCLUDES = ['categories']

Properties

Methods

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 a non-deleted, non-banned thread root (level=1). One global index, so min equals max (no language multiplier as in Blog).

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
string

getHighlightFields()

Returns the highlight fields for `subject` and `text`, exactly what the thread list shows as title and description.

public getHighlightFields() : array<string, array<int, string>>

Always both the base field and .folded: Czech analysis has stopwords and a stemmer, so a one-word query often matches only .folded.

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
Parameters
$languageAlias : string|null
Return values
string

getIndexBases()

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
string

getSearchFields()

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 ES query by visibility.

public getVisibilityFilters() : array<int, array<string, mixed>>

The list is short for Discussion on purpose: the index already holds only what it should, because buildDocuments() does not index replies or deleted and banned threads. Of the filters the front MessageListTrait::prepareFilters() builds (level=1, banned=false, deleted=false, deactivated=false, approvedAt not null), two remain that the index does not cover: deactivated (deactivation uses updateColumn() without rebuilding the document, so the thread stays in the index) and approval (an unapproved thread is indexed with approved: false; approving in the admin goes through save and completedSave rebuilds the document).

Approval is must_not approved=false, not term approved=true: documents indexed before approval existed lack the field and would otherwise vanish from search until a reindex.

IMPORTANT: the DB stays the authority. The caller's filters apply on both paths; this only keeps the relevance path from paginating over threads the DB drops anyway.

Return values
array<int, array<string, mixed>>

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()

Strips HTML (`subject`/`text` may carry rich text) so only plain text is indexed.

private toPlainText(string $value) : string
Parameters
$value : string
Return values
string
On this page

Search results