Skip to content

Commit

Permalink
Merge pull request #44 from tjbwsk/4
Browse files Browse the repository at this point in the history
fixes & enhancements
  • Loading branch information
wilr authored Nov 18, 2024
2 parents b353817 + 5781d68 commit cce9694
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 40 deletions.
48 changes: 34 additions & 14 deletions src/ElasticaService.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,25 @@ protected function getIndexConfig()
* @param Query|string|array $query
* @param array $options Options defined in \Elastica\Search
* @param bool $returnResultList
* @param bool|int $trackTotalHits
* @return ResultList | ResultSet
*/
public function search($query, $options = null, $returnResultList = true)
public function search($query, $options = null, $returnResultList = true, $trackTotalHits = false)
{
if ($returnResultList) {
return new ResultList($this->getIndex(), Query::create($query), $this->logger);
$query = Query::create($query);

if ($trackTotalHits) {
$query = $query->setTrackTotalHits(true);
}

return new ResultList($this->getIndex(), $query, $this->logger);
}

if ($trackTotalHits) {
$query = Query::create($query)->setTrackTotalHits(true);
}

return $this->getIndex()->search($query, $options);
}

Expand Down Expand Up @@ -383,10 +395,11 @@ public function getVersion(): string
/**
* Creates the index and the type mappings.
*
* @param bool $recreate
* @param bool $recreate
* @param string $class
* @throws Exception
*/
public function define($recreate = false)
public function define($recreate = false, $class = null)
{
$index = $this->getIndex();
$exists = $index->exists();
Expand All @@ -401,27 +414,33 @@ public function define($recreate = false)
$this->createIndex();
}

foreach ($this->getIndexedClasses() as $class) {
foreach ($this->getIndexedClasses($class) as $sng) {
/** @var Searchable */
$sng = singleton($class);
$props = $sng->getElasticaMapping();
$props->send($index);
}
}

/**
* Re-indexes each record in the index.
*
* @param int $chunkSize
* @param string $class
* @throws Exception
*/
public function refresh()
public function refresh($chunkSize = 1000, $class = null)
{
Versioned::withVersionedMode(
function () {
function () use ($chunkSize, $class) {
Versioned::set_stage(Versioned::LIVE);

foreach ($this->getIndexedClasses() as $class) {
foreach (DataObject::get($class) as $record) {
foreach ($this->getIndexedClasses($class) as $sng) {
if ($sng->hasMethod('getDataListToIndex')) {
$list = $sng->getDataListToIndex();
} else {
$list = $sng::get();
}

foreach ($list->chunkedFetch($chunkSize) as $record) {
// Only index records with Show In Search enabled, or those that don't expose that fielid
if (!$record->hasField('ShowInSearch') || $record->ShowInSearch) {
if ($this->index($record)) {
Expand All @@ -441,16 +460,17 @@ function () {
/**
* Gets the classes which are indexed (i.e. have the extension applied).
*
* @param string $class
* @return array
* @throws ReflectionException
*/
public function getIndexedClasses()
public function getIndexedClasses($class = null)
{
$classes = array();
foreach (ClassInfo::subclassesFor(DataObject::class) as $candidate) {
foreach ($class ? [$class] : ClassInfo::subclassesFor(DataObject::class) as $candidate) {
$candidateInstance = DataObject::singleton($candidate);
if ($candidateInstance->hasExtension($this->searchableExtensionClassName)) {
$classes[] = $candidate;
$classes[] = $candidateInstance;
}
}
return $classes;
Expand Down
30 changes: 26 additions & 4 deletions src/ReindexTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,33 @@ public function run($request)
print(Director::is_cli() ? "$content\n" : "<p>$content</p>");
};

$message('Defining the mappings');
$class = $request->getVar('class');

if ($class && !class_exists($class)) {
$message("Class {$class} does not exist");

return;
}

if ($class) {
$message('Defining the mappings for class ' . $class);
} else {
$message('Defining the mappings');
}

$recreate = (bool) $request->getVar('recreate');
$this->service->define($recreate);
$this->service->define($recreate, $class);

if ($class) {
$message('Refreshing the index for class ' . $class);
} else {
$message('Refreshing the index');
}

if (($chunkSize = (int) $request->getVar('chunkSize')) <= 0) {
$chunkSize = 1000;
}

$message('Refreshing the index');
$this->service->refresh();
$this->service->refresh($chunkSize, $class);
}
}
14 changes: 11 additions & 3 deletions src/ResultList.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public function __construct(Index $index, Query $query, LoggerInterface $logger
]
);

if (Versioned::get_reading_mode() == Versioned::LIVE) {
if (Versioned::get_reading_mode() === 'Stage.' . Versioned::LIVE) {
$publishedFilter = $query->hasParam('post_filter') ? $query->getParam('post_filter') : null;

if (!$publishedFilter) {
Expand Down Expand Up @@ -221,7 +221,15 @@ public function toArray()
return end($parts);
}, $documentIds);

foreach (DataObject::get($class)->byIDs($ids) as $record) {
$sng = singleton($class);

if ($sng->hasMethod('getDataListToIndex')) {
$list = $sng->getDataListToIndex();
} else {
$list = $sng::get();
}

foreach ($list->byIDs($ids) as $record) {
$retrieved[$class][$record->ID] = $record;
}
}
Expand Down Expand Up @@ -387,7 +395,7 @@ public function count(): int
*/
public function getTotalItems()
{
return $this->getResults()->getTotalHits();
return ($results = $this->getResults()) ? $results->getTotalHits() : 0;
}

/**
Expand Down
92 changes: 73 additions & 19 deletions src/Searchable.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,30 +221,43 @@ public function inheritedDatabaseFields()
* if needed. First we go through all the regular fields belonging to pages, then to the dataobjects related to
* those pages
*
* @param array $onlyFields
* @return array
*/
public function getElasticaFields()
public function getElasticaFields($onlyFields = [])
{
$indexedFields = $this->owner->indexedFields();

if ($onlyFields) {
$indexedFields = array_intersect_key($indexedFields, $onlyFields);
$indexedFields = array_replace_recursive($indexedFields, $onlyFields);
}

$result = [];
foreach ($this->owner->indexedFields() as $fieldName => $params) {
foreach ($indexedFields as $fieldName => $params) {
$field = isset($params['field'])
? $params['field']
: $fieldName;
$relationClass = isset($params['relationClass'])
? $params['relationClass']
: $this->owner->getRelationClass($fieldName);

// Don't send these to elasticsearch
unset($params['relationClass'], $params['field']);
unset($params['field']);

// Build nested field from relation
if ($relationClass) {
if (isset($params['relationClass'])) {
$relationClass = $params['relationClass'];

// Don't send these to elasticsearch
unset($params['relationClass']);

// Relations can add multiple fields, so merge them all here
$nestedFields = $this->getSearchableFieldsForRelation($fieldName, $params, $relationClass);
$result = array_merge($result, $nestedFields);
continue;
}

// Don't send these to elasticsearch
unset($params['only_fields']);

// Get extra params
$params = $this->getExtraFieldParams($field, $params);

Expand Down Expand Up @@ -352,22 +365,28 @@ public function getElasticaDocument()
* Get values for all searchable fields as an array.
* Similr to getSearchableFields() but returns field values instead of spec
*
* @param array $onlyFields
* @return array
*/
public function getSearchableFieldValues()
public function getSearchableFieldValues($onlyFields = [])
{
$indexedFields = $this->owner->indexedFields();

if ($onlyFields) {
$indexedFields = array_intersect_key($indexedFields, $onlyFields);
$indexedFields = array_replace_recursive($indexedFields, $onlyFields);
}

$fieldValues = [];
foreach ($this->owner->indexedFields() as $fieldName => $params) {
// Check nested relation class
$relationClass = isset($params['relationClass'])
? $params['relationClass']
: $this->owner->getRelationClass($fieldName);
foreach ($indexedFields as $fieldName => $params) {
$field = isset($params['field'])
? $params['field']
: $fieldName;

// Build nested field from relation
if ($relationClass) {
if (isset($params['relationClass'])) {
$relationClass = $params['relationClass'];

// Relations can add multiple fields, so merge them all here
$nestedFieldValues = $this->getSearchableFieldValuesForRelation($fieldName, $params, $relationClass);
$fieldValues = array_merge($fieldValues, $nestedFieldValues);
Expand All @@ -376,6 +395,8 @@ public function getSearchableFieldValues()

// Get value from object
if ($this->owner->hasField($field)) {
unset($params['only_fields']);

// Check field exists on parent
$params = $this->getExtraFieldParams($field, $params);
$fieldValue = $this->formatValue($params, $this->owner->relField($field));
Expand Down Expand Up @@ -559,7 +580,14 @@ protected function getSearchableFieldsForRelation($fieldName, $params, $classNam
}

// Get nested fields
$nestedFields = $related->getElasticaFields();

if (isset($params['only_fields'])) {
$nestedFields = $related->getElasticaFields($params['only_fields']);

unset($params['only_fields']);
} else {
$nestedFields = $related->getElasticaFields();
}

// Determine if merging into parent as either a multilevel object (default)
// or nested objects (requires 'nested' param to be set)
Expand Down Expand Up @@ -643,8 +671,15 @@ protected function getSearchableFieldValuesForRelation($fieldName, $params, $cla
* @var DataObject|Searchable $relationListItem
*/
foreach ($relatedList as $relationListItem) {
$relationValues[] = $relationListItem->getSearchableFieldValues();
if (isset($params['only_fields'])) {
$searchableFieldValues = $relationListItem->getSearchableFieldValues($params['only_fields']);
} else {
$searchableFieldValues = $relationListItem->getSearchableFieldValues();
}

$relationValues[] = $searchableFieldValues;
}

return [$fieldName => $relationValues];
}

Expand All @@ -653,9 +688,15 @@ protected function getSearchableFieldValuesForRelation($fieldName, $params, $cla
// Handle unary-multilevel
// I.e. Relation_Field = 'value'
if ($isUnary) {
if (isset($params['only_fields'])) {
$searchableFieldValues = $relatedItem->getSearchableFieldValues($params['only_fields']);
} else {
$searchableFieldValues = $relatedItem->getSearchableFieldValues();
}

// We will return multiple values, one for each sub-column
$fieldValues = [];
foreach ($relatedItem->getSearchableFieldValues() as $relatedFieldName => $relatedFieldValue) {
foreach ($searchableFieldValues as $relatedFieldName => $relatedFieldValue) {
$nestedName = "{$fieldName}_{$relatedFieldName}";
$fieldValues[$nestedName] = $relatedItem->IsInDB() ? $relatedFieldValue : null;
}
Expand All @@ -666,16 +707,29 @@ protected function getSearchableFieldValuesForRelation($fieldName, $params, $cla
// I.e. Relation_Field = ['value1', 'value2']
$fieldValues = [];


if (isset($params['only_fields'])) {
$elasticaFields = $relatedSingleton->getElasticaFields($params['only_fields']);
} else {
$elasticaFields = $relatedSingleton->getElasticaFields();
}

// Bootstrap set with empty arrays for each top level key
// This also ensures we set empty data if $relatedList is empty
foreach ($relatedSingleton->getElasticaFields() as $relatedFieldName => $spec) {
foreach ($elasticaFields as $relatedFieldName => $spec) {
$nestedName = "{$fieldName}_{$relatedFieldName}";
$fieldValues[$nestedName] = [];
}

// Add all documents to the list
foreach ($relatedList as $relatedListItem) {
foreach ($relatedListItem->getSearchableFieldValues() as $relatedFieldName => $relatedFieldValue) {
if (isset($params['only_fields'])) {
$searchableFieldValues = $relatedListItem->getSearchableFieldValues($params['only_fields']);
} else {
$searchableFieldValues = $relatedListItem->getSearchableFieldValues();
}

foreach ($searchableFieldValues as $relatedFieldName => $relatedFieldValue) {
$nestedName = "{$fieldName}_{$relatedFieldName}";
$fieldValues[$nestedName][] = $relatedFieldValue;
}
Expand Down

0 comments on commit cce9694

Please sign in to comment.