/**
  * (Re-) indexes an object to the ElasticSearch index, no matter if the change is actually required.
  *
  * @param object $object
  * @param string $signalInformation Signal information, if called from a signal
  * @param \Flowpack\ElasticSearch\Domain\Model\Client $client
  *
  * @return void
  */
 public function indexObject($object, $signalInformation = null, Client $client = null)
 {
     $type = $this->getIndexTypeForObject($object, $client);
     if ($type === null) {
         return null;
     }
     $data = $this->getIndexablePropertiesAndValuesFromObject($object);
     $id = $this->persistenceManager->getIdentifierByObject($object);
     $document = new Document($type, $data, $id);
     $document->store();
 }
    /**
     * 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);
         }
     }
 }