New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] 7.0 kernel branch #1875

Merged
merged 34 commits into from Dec 15, 2017
Commits
Jump to file or symbol
Failed to load files and symbols.
+361 −172
Diff settings

Always

Just for now

Viewing a subset of changes. View all

[SPI] Add Content\Handler->loadContentInfoList() and use for loading …

…all in one go when building LocationSearchResult
  • Loading branch information...
andrerom authored and alongosz committed Jan 29, 2017
commit 283dac6b509566cb7026c901b622f1e8e619e18c
@@ -49,4 +49,43 @@ public function __construct(
$this->persistenceHandler = $persistenceHandler;
$this->logger = $logger;
}
/**
* Helper for getting multiple cache items in one call and do the id extraction for you.
*
* Cache items must be stored with a key in the following format "${keyPrefix}${id}", like "ez-content-info-${id}",
* in order for this method to be able to prefix key on id's and also extract key prefix afterwards.
*
* @param array $ids
* @param string $keyPrefix
*
* @return array Format [id[] $cacheMisses, CacheItem[<id>] $list], list contains hits & misses (if there where any).
*/
final protected function getMultipleCacheItems(array $ids, string $keyPrefix): array
{
if (empty($ids)) {
return [[], []];
}
$cacheKeys = [];
foreach (array_unique($ids) as $id) {
$cacheKeys[] = $keyPrefix . $id;
}
$list = [];
$cacheMisses = [];
$keyPrefixLength = strlen($keyPrefix);
foreach ($this->cache->getItems($cacheKeys) as $key => $cacheItem) {
$id = substr($key, $keyPrefixLength);
if ($cacheItem->isHit()) {
$list[$id] = $cacheItem->get();
continue;
}
$cacheMisses[] = $id;
$list[$id] = $cacheItem;
}
return [$cacheMisses, $list];
}
}
@@ -96,6 +96,30 @@ public function loadContentInfo($contentId)
return $contentInfo;
}
public function loadContentInfoList(array $contentIds)
{
list($cacheMisses, $list) = $this->getMultipleCacheItems($contentIds, 'ez-content-info-');
if (empty($cacheMisses)) {
return $list;
}
// Load cache misses
$this->logger->logCall(__METHOD__, array('content' => $cacheMisses));
$cacheMissList = $this->persistenceHandler->contentHandler()->loadContentInfoList($cacheMisses);
// Populate cache misses with data and set final info data instead on list
foreach ($cacheMissList as $id => $contentInfo) {
$this->cache->save(
$list[$id]
->set($contentInfo)
->tag($this->getCacheTags($contentInfo))
);
$list[$id] = $contentInfo;
}
return $list;
}
/**
* {@inheritdoc}
*/
@@ -74,6 +74,7 @@ public function providerForCachedLoadMethods(): array
['load', [2, 1], 'ez-content-2-1-' . ContentHandler::ALL_TRANSLATIONS_KEY, $content],
['load', [2, 1, ['eng-GB', 'eng-US']], 'ez-content-2-1-eng-GB|eng-US', $content],
['loadContentInfo', [2], 'ez-content-info-2', $info],
['loadContentInfoList', [[2]], 'ez-content-info-2', [2 => $info], true],
['loadContentInfoByRemoteId', ['3d8jrj'], 'ez-content-info-byRemoteId-3d8jrj', $info],
['loadVersionInfo', [2, 1], 'ez-content-version-info-2-1', $version],
['listVersions', [2], 'ez-content-2-version-list', [$version]],
@@ -176,6 +176,18 @@
*/
abstract public function loadContentInfo($contentId);
/**
* Loads rows of info for content identified by $contentIds.
*
* @see loadContentInfo For the returned structure.
* @see \eZ\Publish\SPI\Persistence\Content\Handler::loadContentInfoList For how this will only return items found and not throw.
*
* @param array $contentIds
*
* @return array[]
*/
abstract public function loadContentInfoList(array $contentIds);
/**
* Loads version info for content identified by $contentId and $versionNo.
* Will basically return a hash containing all field values from ezcontentobject_version table plus following keys:
@@ -852,51 +852,29 @@ public function load($contentId, $version, array $translations = null)
}
/**
* @see loadContentInfo(), loadContentInfoByRemoteId()
* @see loadContentInfo(), loadContentInfoByRemoteId(), loadContentInfoList()
*
* @param string $column
* @param mixed $id
*
* @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
* @param array $ids Array of int, or if $bindype is set to Connection::PARAM_STR_ARRAY then array of string
* @param int $bindType One of Connection::PARAM_*_ARRAY constants.
*
* @return array
*/
private function internalLoadContentInfo($column, $id)
private function internalLoadContentInfo(string $column, array $ids, int $bindType = Connection::PARAM_INT_ARRAY): array
{
/** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
$query = $this->dbHandler->createSelectQuery();
$query->select(
'ezcontentobject.*',
$this->dbHandler->aliasedColumn($query, 'main_node_id', 'ezcontentobject_tree')
)->from(
$this->dbHandler->quoteTable('ezcontentobject')
)->leftJoin(
$this->dbHandler->quoteTable('ezcontentobject_tree'),
$query->expr->lAnd(
$query->expr->eq(
$this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_tree'),
$this->dbHandler->quoteColumn('id', 'ezcontentobject')
),
$query->expr->eq(
$this->dbHandler->quoteColumn('main_node_id', 'ezcontentobject_tree'),
$this->dbHandler->quoteColumn('node_id', 'ezcontentobject_tree')
)
)
)->where(
$query->expr->eq(
$this->dbHandler->quoteColumn($column, 'ezcontentobject'),
$query->bindValue($id, null, $column === 'id' ? PDO::PARAM_INT : PDO::PARAM_STR)
)
);
$statement = $query->prepare();
$statement->execute();
$row = $statement->fetch(PDO::FETCH_ASSOC);
if (empty($row)) {
throw new NotFound('content', "$column: $id");
}
return $row;
$q = $this->connection->createQueryBuilder();
$q
->select('c.*', 't.main_node_id AS ezcontentobject_tree_main_node_id')
->from('ezcontentobject', 'c')
->leftJoin(
'c',
'ezcontentobject_tree',
't',
'c.id = t.contentobject_id AND t.node_id = t.main_node_id'
)->where("c.${column} IN (:ids)")
->setParameter('ids', $ids, $bindType);
return $q->execute()->fetchAll();
}
/**
@@ -913,7 +891,17 @@ private function internalLoadContentInfo($column, $id)
*/
public function loadContentInfo($contentId)
{
return $this->internalLoadContentInfo('id', $contentId);
$results = $this->internalLoadContentInfo('id', [$contentId]);
if (empty($results)) {
throw new NotFound('content', "id: $contentId");
}
return $results[0];
}
public function loadContentInfoList(array $contentIds)
{
return $this->internalLoadContentInfo('id', $contentIds);
}
/**
@@ -929,7 +917,12 @@ public function loadContentInfo($contentId)
*/
public function loadContentInfoByRemoteId($remoteId)
{
return $this->internalLoadContentInfo('remote_id', $remoteId);
$results = $this->internalLoadContentInfo('remote_id', [$remoteId], Connection::PARAM_STR_ARRAY);
if (empty($results)) {
throw new NotFound('content', "remote_id: $remoteId");
}
return $results[0];
}
/**
@@ -319,6 +319,17 @@ public function loadContentInfo($contentId)
}
}
public function loadContentInfoList(array $contentIds)
{
try {
return $this->innerGateway->loadContentInfoList($contentIds);
} catch (DBALException $e) {
throw new RuntimeException('Database error', 0, $e);
} catch (PDOException $e) {
throw new RuntimeException('Database error', 0, $e);
}
}
/**
* Loads version info for content identified by $contentId and $versionNo.
* Will basically return a hash containing all field values from ezcontentobject_version table plus following keys:
@@ -339,6 +339,20 @@ public function loadContentInfo($contentId)
return $this->treeHandler->loadContentInfo($contentId);
}
public function loadContentInfoList(array $contentIds)
{
$list = $this->mapper->extractContentInfoFromRows(
$this->contentGateway->loadContentInfoList($contentIds)
);
$listByContentId = [];
foreach ($list as $item) {
$listByContentId[$item->id] = $item;
}
return $listByContentId;
}
/**
* Returns the metadata object for a content identified by $remoteId.
*
@@ -8,6 +8,7 @@
*/
namespace eZ\Publish\Core\Repository\Helper;
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandler;
use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler;
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
@@ -321,10 +322,11 @@ public function buildRelationDomainObject(
* Builds domain location object from provided persistence location.
*
* @param \eZ\Publish\SPI\Persistence\Content\Location $spiLocation
* @param \eZ\Publish\SPI\Persistence\Content\ContentInfo|null $contentInfo
*
* @return \eZ\Publish\API\Repository\Values\Content\Location
*/
public function buildLocationDomainObject(SPILocation $spiLocation)
public function buildLocationDomainObject(SPILocation $spiLocation, SPIContentInfo $contentInfo = null)
{
// TODO: this is hardcoded workaround for missing ContentInfo on root location
if ($spiLocation->id == 1) {
@@ -348,7 +350,7 @@ public function buildLocationDomainObject(SPILocation $spiLocation)
);
} else {
$contentInfo = $this->buildContentInfoDomainObject(
$this->contentHandler->loadContentInfo($spiLocation->contentId)
$contentInfo ?: $this->contentHandler->loadContentInfo($spiLocation->contentId)
);
}
@@ -369,6 +371,40 @@ public function buildLocationDomainObject(SPILocation $spiLocation)
);
}
/**
* Build API Location and corresponding ContentInfo domain objects and apply to LocationSearchResult.
*
* Loading of ContentInfo objects are done in one operation.
*
* @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result SPI search result with SPI Location items as hits
*
* @return \eZ\Publish\SPI\Persistence\Content\Location[] Locations we did not find content info for is retunred as an array.
*/
public function buildLocationDomainObjectsOnSearchResult(SearchResult $result)
{
$contentIds = [];
foreach ($result->searchHits as $hit) {
$contentIds[] = $hit->valueObject->contentId;
}
$missingLocations = [];
$contentInfoList = $this->contentHandler->loadContentInfoList($contentIds);
foreach ($result->searchHits as $key => $hit) {
if (isset($contentInfoList[$hit->valueObject->contentId])) {
$hit->valueObject = $this->buildLocationDomainObject(
$hit->valueObject,
$contentInfoList[$hit->valueObject->contentId]
);
} else {
$missingLocations[] = $hit->valueObject;
unset($result->searchHits[$key]);
--$result->totalCount;
}
}
return $missingLocations;
}
/**
* Creates an array of SPI location create structs from given array of API location create structs.
*
@@ -334,21 +334,9 @@ public function findLocations(LocationQuery $query, array $languageFilter = arra
$result = $this->searchHandler->findLocations($query, $languageFilter);
foreach ($result->searchHits as $key => $hit) {
try {
$hit->valueObject = $this->domainMapper->buildLocationDomainObject(
$hit->valueObject
);
} catch (APINotFoundException $e) {
// Most likely stale data, so we register content for background re-indexing.
$this->backgroundIndexer->registerLocation($hit->valueObject);
unset($result->searchHits[$key]);
--$result->totalCount;
} catch (APIUnauthorizedException $e) {
// Most likely stale cached permission criterion, as ttl is only a few seconds we don't react to this
unset($result->searchHits[$key]);
--$result->totalCount;
}
$missingLocations = $this->domainMapper->buildLocationDomainObjectsOnSearchResult($result);
foreach ($missingLocations as $missingLocation) {
$this->backgroundIndexer->registerLocation($missingLocation);
}
return $result;
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.