/** * {@inheritdoc} * * @param \TYPO3\Eel\FlowQuery\FlowQuery $flowQuery the FlowQuery object * @param array $arguments filter arguments for this operation * @return void|integer with the number of elements */ public function evaluate(\TYPO3\Eel\FlowQuery\FlowQuery $flowQuery, array $arguments) { if (count($arguments) == 0) { return count($flowQuery->getContext()); } else { $flowQuery->pushOperation('count', array()); $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); $until = array(); foreach ($flowQuery->getContext() as $contextNode) { $prevNodes = $this->getPrevForNode($contextNode); if (isset($arguments[0]) && !empty($arguments[0])) { $untilQuery = new FlowQuery($prevNodes); $untilQuery->pushOperation('filter', array($arguments[0])); $until = $untilQuery->get(); } if (isset($until) && !empty($until)) { $until = end($until); $prevNodes = $this->getNodesUntil($prevNodes, $until); } if (is_array($prevNodes)) { foreach ($prevNodes as $prevNode) { if ($prevNode !== NULL && !isset($outputNodePaths[$prevNode->getPath()])) { $outputNodePaths[$prevNode->getPath()] = TRUE; $output[] = $prevNode; } } } } $flowQuery->setContext($output); if (isset($arguments[1]) && !empty($arguments[1])) { $flowQuery->pushOperation('filter', array($arguments[1])); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $siteNode = $contextNode->getContext()->getCurrentSiteNode(); $parentNodes = $this->getParents($contextNode, $siteNode); if (isset($arguments[0]) && !empty($arguments[0] && isset($parentNodes[0]))) { $untilQuery = new FlowQuery(array($parentNodes[0])); $untilQuery->pushOperation('closest', array($arguments[0])); $until = $untilQuery->get(); } if (isset($until) && is_array($until) && !empty($until) && isset($until[0])) { $parentNodes = $this->getNodesUntil($parentNodes, $until[0]); } if (is_array($parentNodes)) { foreach ($parentNodes as $parentNode) { if ($parentNode !== null && !isset($outputNodePaths[$parentNode->getPath()])) { $outputNodePaths[$parentNode->getPath()] = true; $output[] = $parentNode; } } } } $flowQuery->setContext($output); if (isset($arguments[1]) && !empty($arguments[1])) { $flowQuery->pushOperation('filter', $arguments[1]); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { if (!isset($arguments[0]) || empty($arguments[0])) { throw new \TYPO3\Eel\FlowQuery\FlowQueryException('closest() requires a filter argument', 1332492263); } $output = array(); foreach ($flowQuery->getContext() as $contextNode) { $contextNodeQuery = new FlowQuery(array($contextNode)); $contextNodeQuery->pushOperation('first', array()); $contextNodeQuery->pushOperation('filter', $arguments); $parentsQuery = new FlowQuery(array($contextNode)); $contextNodeQuery->pushOperation('add', array($parentsQuery->parents($arguments[0])->get())); foreach ($contextNodeQuery as $result) { $output[$result->getPath()] = $result; } } $flowQuery->setContext(array_values($output)); }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $prevNode = $this->getPrevForNode($contextNode); if ($prevNode !== NULL && !isset($outputNodePaths[$prevNode->getPath()])) { $outputNodePaths[$prevNode->getPath()] = TRUE; $output[] = $prevNode; } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $parentNode = $contextNode->getParent(); if ($parentNode !== null && !isset($outputNodePaths[$parentNode->getPath()])) { $output[] = $parentNode; $outputNodePaths[$parentNode->getPath()] = true; } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery * @param array $arguments * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $subject = $arguments[0]; if (!isset($subject) || empty($subject)) { $flowQuery->setContext(array()); return; } $filteredContext = array(); $context = $flowQuery->getContext(); if (is_string($subject)) { foreach ($context as $contextElement) { $contextElementQuery = new FlowQuery(array($contextElement)); $contextElementQuery->pushOperation('children', $arguments); if ($contextElementQuery->count() > 0) { $filteredContext[] = $contextElement; } } } else { if ($subject instanceof \TYPO3\Eel\FlowQuery\FlowQuery) { $elements = $subject->get(); } elseif ($subject instanceof \Traversable) { $elements = iterator_to_array($subject); } elseif (is_object($subject)) { $elements = array($subject); } elseif (is_array($subject)) { $elements = $subject; } else { throw new \TYPO3\Eel\FlowQuery\FizzleException('supplied argument for has operation not supported', 1332489625); } foreach ($elements as $element) { if ($element instanceof NodeInterface) { $parentsQuery = new FlowQuery(array($element)); /** @var NodeInterface $parent */ foreach ($parentsQuery->parents(array())->get() as $parent) { /** @var NodeInterface $contextElement */ foreach ($context as $contextElement) { if ($contextElement->getIdentifier() === $parent->getIdentifier()) { $filteredContext[] = $contextElement; } } } } } $filteredContext = array_unique($filteredContext); } $flowQuery->setContext($filteredContext); }
/** * {@inheritdoc} * * @param \TYPO3\Eel\FlowQuery\FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return mixed|null if the operation is final, the return value */ public function evaluate(\TYPO3\Eel\FlowQuery\FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { foreach ($contextNode->getChildNodes() as $childNode) { if (!isset($outputNodePaths[$childNode->getPath()])) { $output[] = $childNode; $outputNodePaths[$childNode->getPath()] = TRUE; } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param \TYPO3\Eel\FlowQuery\FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return mixed|null if the operation is final, the return value */ public function evaluate(\TYPO3\Eel\FlowQuery\FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $siteNode = $this->nodeRepository->getContext()->getCurrentSiteNode(); while ($contextNode->getParent() !== $siteNode) { $contextNode = $contextNode->getParent(); if (!isset($outputNodePaths[$contextNode->getPath()])) { $output[] = $contextNode; $outputNodePaths[$contextNode->getPath()] = TRUE; } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { $nextNodes = $this->getNextForNode($contextNode); if (is_array($nextNodes)) { foreach ($nextNodes as $nextNode) { if ($nextNode !== null && !isset($outputNodePaths[$nextNode->getPath()])) { $outputNodePaths[$nextNode->getPath()] = true; $output[] = $nextNode; } } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); foreach ($flowQuery->getContext() as $contextNode) { /** @var NodeInterface $contextNode */ $siteNode = $contextNode->getContext()->getCurrentSiteNode(); while ($contextNode !== $siteNode && $contextNode->getParent() !== null) { $contextNode = $contextNode->getParent(); if (!isset($outputNodePaths[$contextNode->getPath()])) { $output[] = $contextNode; $outputNodePaths[$contextNode->getPath()] = true; } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $output = array(); $outputNodePaths = array(); /** @var NodeInterface $contextNode */ foreach ($flowQuery->getContext() as $contextNode) { $outputNodePaths[$contextNode->getPath()] = true; } foreach ($flowQuery->getContext() as $contextNode) { $parentNode = $contextNode->getParent(); if ($parentNode instanceof NodeInterface) { foreach ($parentNode->getChildNodes() as $childNode) { if (!isset($outputNodePaths[$childNode->getPath()])) { $output[] = $childNode; $outputNodePaths[$childNode->getPath()] = true; } } } } $flowQuery->setContext($output); if (isset($arguments[0]) && !empty($arguments[0])) { $flowQuery->pushOperation('filter', $arguments); } }
/** * Optimize for typical use cases, filter by node name and filter * by NodeType (instanceof). These cases are now optimized and will * only load the nodes that match the filters. * * @param FlowQuery $flowQuery * @param array $parsedFilter * @return boolean */ protected function earlyOptimizationOfFilters(FlowQuery $flowQuery, array $parsedFilter) { $optimized = false; $output = array(); $outputNodePaths = array(); foreach ($parsedFilter['Filters'] as $filter) { $instanceOfFilters = array(); $attributeFilters = array(); if (isset($filter['AttributeFilters'])) { foreach ($filter['AttributeFilters'] as $attributeFilter) { if ($attributeFilter['Operator'] === 'instanceof' && $attributeFilter['Identifier'] === null) { $instanceOfFilters[] = $attributeFilter; } else { $attributeFilters[] = $attributeFilter; } } } // Only apply optimization if there's a property name filter or a instanceof filter or another filter already did optimization if (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter']) || count($instanceOfFilters) > 0 || $optimized === true) { $optimized = true; $filteredOutput = array(); $filteredOutputNodePaths = array(); // Optimize property name filter if present if (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter'])) { $nodePath = isset($filter['PropertyNameFilter']) ? $filter['PropertyNameFilter'] : $filter['PathFilter']; /** @var NodeInterface $contextNode */ foreach ($flowQuery->getContext() as $contextNode) { $childNode = $contextNode->getNode($nodePath); if ($childNode !== null && !isset($filteredOutputNodePaths[$childNode->getPath()])) { $filteredOutput[] = $childNode; $filteredOutputNodePaths[$childNode->getPath()] = true; } } } elseif (count($instanceOfFilters) > 0) { // Optimize node type filter if present $allowedNodeTypes = array_map(function ($instanceOfFilter) { return $instanceOfFilter['Operand']; }, $instanceOfFilters); /** @var NodeInterface $contextNode */ foreach ($flowQuery->getContext() as $contextNode) { /** @var NodeInterface $childNode */ foreach ($contextNode->getChildNodes(implode($allowedNodeTypes, ',')) as $childNode) { if (!isset($filteredOutputNodePaths[$childNode->getPath()])) { $filteredOutput[] = $childNode; $filteredOutputNodePaths[$childNode->getPath()] = true; } } } } // Apply attribute filters if present if (isset($filter['AttributeFilters'])) { $attributeFilters = array_reduce($filter['AttributeFilters'], function ($filters, $attributeFilter) { return $filters . $attributeFilter['text']; }); $filteredFlowQuery = new FlowQuery($filteredOutput); $filteredFlowQuery->pushOperation('filter', array($attributeFilters)); $filteredOutput = $filteredFlowQuery->get(); } // Add filtered nodes to output foreach ($filteredOutput as $filteredNode) { if (!isset($outputNodePaths[$filteredNode->getPath()])) { $output[] = $filteredNode; } } } } if ($optimized === true) { $flowQuery->setContext($output); } return $optimized; }
/** * {@inheritdoc} * * @param FlowQuery $flowQuery the FlowQuery object * @param array $arguments the arguments for this operation * @return void */ public function evaluate(FlowQuery $flowQuery, array $arguments) { $context = $flowQuery->getContext(); if (!isset($context[0]) || empty($arguments[0])) { return; } $result = array(); $selectorAndFilter = $arguments[0]; $parsedFilter = NULL; $parsedFilter = \TYPO3\Eel\FlowQuery\FizzleParser::parseFilterGroup($selectorAndFilter); if (isset($parsedFilter['Filters']) && $this->hasOnlyInstanceOfFilters($parsedFilter['Filters'])) { $nodeTypes = array(); foreach ($parsedFilter['Filters'] as $filter) { $nodeTypes[] = $filter['AttributeFilters'][0]['Operand']; } /** @var \TYPO3\TYPO3CR\Domain\Model\NodeInterface $contextNode */ foreach ($context as $contextNode) { $result = array_merge($result, $this->nodeDataRepository->findByParentAndNodeTypeInContext($contextNode->getPath(), implode(',', $nodeTypes), $contextNode->getContext(), TRUE)); } } else { foreach ($parsedFilter['Filters'] as $filter) { $filterResults = array(); $generatedNodes = FALSE; if (isset($filter['IdentifierFilter'])) { if (!preg_match(\TYPO3\Flow\Validation\Validator\UuidValidator::PATTERN_MATCH_UUID, $filter['IdentifierFilter'])) { throw new \TYPO3\Eel\FlowQuery\FlowQueryException('find() requires a valid identifier', 1332492263); } /** @var \TYPO3\TYPO3CR\Domain\Model\NodeInterface $contextNode */ foreach ($context as $contextNode) { $filterResults = array($contextNode->getContext()->getNodeByIdentifier($filter['IdentifierFilter'])); } $generatedNodes = TRUE; } elseif (isset($filter['IdentifierFilter'])) { if (!preg_match(\TYPO3\Flow\Validation\Validator\UuidValidator::PATTERN_MATCH_UUID, $filter['IdentifierFilter'])) { throw new \TYPO3\Eel\FlowQuery\FlowQueryException('find() requires a valid identifier', 1332492263); } /** @var \TYPO3\TYPO3CR\Domain\Model\NodeInterface $contextNode */ foreach ($context as $contextNode) { $filterResults = array($contextNode->getContext()->getNodeByIdentifier($filter['IdentifierFilter'])); } $generatedNodes = TRUE; } elseif (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter'])) { $nodePath = isset($filter['PropertyNameFilter']) ? $filter['PropertyNameFilter'] : $filter['PathFilter']; foreach ($context as $contextNode) { $node = $contextNode->getNode($nodePath); if ($node !== NULL) { array_push($filterResults, $node); } } $generatedNodes = TRUE; } if (isset($filter['AttributeFilters']) && $filter['AttributeFilters'][0]['Operator'] === 'instanceof') { foreach ($context as $contextNode) { $filterResults = array_merge($filterResults, $this->nodeDataRepository->findByParentAndNodeTypeInContext($contextNode->getPath(), $filter['AttributeFilters'][0]['Operand'], $contextNode->getContext(), TRUE)); } unset($filter['AttributeFilters'][0]); $generatedNodes = TRUE; } if (isset($filter['AttributeFilters']) && count($filter['AttributeFilters']) > 0) { if (!$generatedNodes) { throw new \TYPO3\Eel\FlowQuery\FlowQueryException('find() needs an identifier, path or instanceof filter for the first filter part', 1436884196); } $filterQuery = new FlowQuery($filterResults); foreach ($filter['AttributeFilters'] as $attributeFilter) { $filterQuery->pushOperation('filter', array($attributeFilter['text'])); } $filterResults = $filterQuery->get(); } $result = array_merge($result, $filterResults); } } $flowQuery->setContext(array_unique($result)); }
/** * 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->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; $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[] = 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 Context: %s', $contextPath, $contextPathHash, json_encode($node->getContext()->getProperties())), LOG_DEBUG, null, 'ElasticSearch (CR)'); }; $combinations = $this->contentDimensionCombinator->getAllAllowedCombinations(); $contextProperties = $node->getContext()->getProperties(); foreach ($combinations as $combination) { $dimensions = array_merge($contextProperties['dimensions'], $combination); $targetDimensions = array_merge($contextProperties['targetDimensions'], ['language' => $combination['language'][0]]); $query = new FlowQuery([$node]); $query->pushOperation('context', [['dimensions' => $dimensions, 'targetDimensions' => $targetDimensions]]); /** @var NodeInterface $indexableNode */ $indexableNode = $query->get(0); if ($indexableNode instanceof NodeInterface) { $indexer($indexableNode, $targetWorkspaceName); } } }