/** * index this node, and add it to the current bulk request. * * @param NodeInterface $node * @param string $targetWorkspaceName In case this is triggered during publishing, a workspace name will be passed in * @return void * @throws \TYPO3\TYPO3CR\Search\Exception\IndexingException */ public function indexNode(NodeInterface $node, $targetWorkspaceName = NULL) { $contextPath = $node->getContextPath(); if ($targetWorkspaceName !== NULL) { $contextPath = str_replace($node->getContext()->getWorkspace()->getName(), $targetWorkspaceName, $contextPath); } $contextPathHash = sha1($contextPath); $nodeType = $node->getNodeType(); $mappingType = $this->getIndex()->findType(NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($nodeType)); // Remove document with the same contextPathHash but different NodeType, required after NodeType change $this->getIndex()->request('DELETE', '/_query', array(), json_encode(['query' => ['bool' => ['must' => ['ids' => ['values' => [$contextPathHash]]], 'must_not' => ['term' => ['_type' => str_replace('.', '/', $node->getNodeType()->getName())]]]]])); if ($node->isRemoved()) { // TODO: handle deletion from the fulltext index as well $mappingType->deleteDocumentById($contextPathHash); $this->logger->log(sprintf('NodeIndexer: Removed node %s from index (node flagged as removed). ID: %s', $contextPath, $contextPathHash), LOG_DEBUG, NULL, 'ElasticSearch (CR)'); return; } $logger = $this->logger; $fulltextIndexOfNode = array(); $nodePropertiesToBeStoredInIndex = $this->extractPropertiesAndFulltext($node, $fulltextIndexOfNode, function ($propertyName) use($logger, $contextPathHash) { $logger->log(sprintf('NodeIndexer (%s) - Property "%s" not indexed because no configuration found.', $contextPathHash, $propertyName), LOG_DEBUG, NULL, 'ElasticSearch (CR)'); }); $document = new ElasticSearchDocument($mappingType, $nodePropertiesToBeStoredInIndex, $contextPathHash); $documentData = $document->getData(); if ($targetWorkspaceName !== NULL) { $documentData['__workspace'] = $targetWorkspaceName; } $dimensionCombinations = $node->getContext()->getDimensions(); if (is_array($dimensionCombinations)) { $documentData['__dimensionCombinations'] = $dimensionCombinations; } if ($this->isFulltextEnabled($node)) { if ($this->isFulltextRoot($node)) { // for fulltext root documents, we need to preserve the "__fulltext" field. That's why we use the // "update" API instead of the "index" API, with a custom script internally; as we // shall not delete the "__fulltext" part of the document if it has any. $this->currentBulkRequest[] = array(array('update' => array('_type' => $document->getType()->getName(), '_id' => $document->getId())), array('script' => ' fulltext = (ctx._source.containsKey("__fulltext") ? ctx._source.__fulltext : new LinkedHashMap()); fulltextParts = (ctx._source.containsKey("__fulltextParts") ? ctx._source.__fulltextParts : new LinkedHashMap()); ctx._source = newData; ctx._source.__fulltext = fulltext; ctx._source.__fulltextParts = fulltextParts ', 'params' => array('newData' => $documentData), 'upsert' => $documentData, 'lang' => 'groovy')); } else { // non-fulltext-root documents can be indexed as-they-are $this->currentBulkRequest[] = array(array('index' => array('_type' => $document->getType()->getName(), '_id' => $document->getId())), $documentData); } $this->updateFulltext($node, $fulltextIndexOfNode, $targetWorkspaceName); } $this->logger->log(sprintf('NodeIndexer: Added / updated node %s. ID: %s', $contextPath, $contextPathHash), LOG_DEBUG, NULL, 'ElasticSearch (CR)'); }
/** * index this node, and add it to the current bulk request. * * @param NodeInterface $node * @param string $targetWorkspaceName In case this is triggered during publishing, a workspace name will be passed in * @return void * @throws \TYPO3\TYPO3CR\Search\Exception\IndexingException */ public function indexNode(NodeInterface $node, $targetWorkspaceName = null) { $indexer = function (NodeInterface $node, $targetWorkspaceName = null) { $contextPath = $node->getContextPath(); if ($this->settings['indexAllWorkspaces'] === false) { // we are only supposed to index the live workspace. // We need to check the workspace at two occasions; checking the // $targetWorkspaceName and the workspace name of the node's context as fallback if ($targetWorkspaceName !== null && $targetWorkspaceName !== 'live') { return; } if ($targetWorkspaceName === null && $node->getContext()->getWorkspaceName() !== 'live') { return; } } if ($targetWorkspaceName !== null) { $contextPath = str_replace($node->getContext()->getWorkspace()->getName(), $targetWorkspaceName, $contextPath); } $contextPathHash = sha1($contextPath); $nodeType = $node->getNodeType(); $mappingType = $this->getIndex()->findType(NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($nodeType)); // Remove document with the same contextPathHash but different NodeType, required after NodeType change $this->logger->log(sprintf('NodeIndexer: Removing node %s from index (if node type changed from %s). ID: %s', $contextPath, $node->getNodeType()->getName(), $contextPathHash), LOG_DEBUG, null, 'ElasticSearch (CR)'); $this->getIndex()->request('DELETE', '/_query', [], json_encode(['query' => ['bool' => ['must' => ['ids' => ['values' => [$contextPathHash]]], 'must_not' => ['term' => ['_type' => NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($node->getNodeType()->getName())]]]]])); if ($node->isRemoved()) { // TODO: handle deletion from the fulltext index as well $mappingType->deleteDocumentById($contextPathHash); $this->logger->log(sprintf('NodeIndexer: Removed node %s from index (node flagged as removed). ID: %s', $contextPath, $contextPathHash), LOG_DEBUG, null, 'ElasticSearch (CR)'); return; } $logger = $this->logger; $fulltextIndexOfNode = []; $nodePropertiesToBeStoredInIndex = $this->extractPropertiesAndFulltext($node, $fulltextIndexOfNode, function ($propertyName) use($logger, $contextPathHash) { $logger->log(sprintf('NodeIndexer (%s) - Property "%s" not indexed because no configuration found.', $contextPathHash, $propertyName), LOG_DEBUG, null, 'ElasticSearch (CR)'); }); $document = new ElasticSearchDocument($mappingType, $nodePropertiesToBeStoredInIndex, $contextPathHash); $documentData = $document->getData(); if ($targetWorkspaceName !== null) { $documentData['__workspace'] = $targetWorkspaceName; } $dimensionCombinations = $node->getContext()->getDimensions(); if (is_array($dimensionCombinations)) { $documentData['__dimensionCombinations'] = $dimensionCombinations; $documentData['__dimensionCombinationHash'] = md5(json_encode($dimensionCombinations)); } if ($this->isFulltextEnabled($node)) { if ($this->isFulltextRoot($node)) { // for fulltext root documents, we need to preserve the "__fulltext" field. That's why we use the // "update" API instead of the "index" API, with a custom script internally; as we // shall not delete the "__fulltext" part of the document if it has any. $this->currentBulkRequest[] = [['update' => ['_type' => $document->getType()->getName(), '_id' => $document->getId()]], ['script' => ' fulltext = (ctx._source.containsKey("__fulltext") ? ctx._source.__fulltext : new LinkedHashMap()); fulltextParts = (ctx._source.containsKey("__fulltextParts") ? ctx._source.__fulltextParts : new LinkedHashMap()); ctx._source = newData; ctx._source.__fulltext = fulltext; ctx._source.__fulltextParts = fulltextParts ', 'params' => ['newData' => $documentData], 'upsert' => $documentData, 'lang' => 'groovy']]; } else { // non-fulltext-root documents can be indexed as-they-are $this->currentBulkRequest[] = [['index' => ['_type' => $document->getType()->getName(), '_id' => $document->getId()]], $documentData]; } $this->updateFulltext($node, $fulltextIndexOfNode, $targetWorkspaceName); } $this->logger->log(sprintf('NodeIndexer: Added / updated node %s. ID: %s Context: %s', $contextPath, $contextPathHash, json_encode($node->getContext()->getProperties())), LOG_DEBUG, null, 'ElasticSearch (CR)'); }; $dimensionCombinations = $this->contentDimensionCombinator->getAllAllowedCombinations(); $workspaceName = $targetWorkspaceName ?: 'live'; $nodeIdentifier = $node->getIdentifier(); if ($dimensionCombinations !== []) { foreach ($dimensionCombinations as $combination) { $context = $this->contextFactory->create(['workspaceName' => $workspaceName, 'dimensions' => $combination]); $node = $context->getNodeByIdentifier($nodeIdentifier); if ($node !== null) { $indexer($node, $targetWorkspaceName); } } } else { $context = $this->contextFactory->create(['workspaceName' => $workspaceName]); $node = $context->getNodeByIdentifier($nodeIdentifier); if ($node !== null) { $indexer($node, $targetWorkspaceName); } } }