Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e9b1557

Browse files
committedSep 28, 2022
FEATURE: Add Node-Type Filter
Create a method which allows to create a nodetype filter to include and exclude specific nodetypes
1 parent 2a7de69 commit e9b1557

File tree

2 files changed

+111
-17
lines changed

2 files changed

+111
-17
lines changed
 

‎Classes/Eel/ElasticSearchQueryBuilder.php

+93
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\ElasticSearchClient;
1919
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Exception;
2020
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Exception\QueryBuildingException;
21+
use Neos\ContentRepository\Domain\Service\NodeTypeManager;
2122
use Neos\Flow\Log\ThrowableStorageInterface;
2223
use Neos\Flow\Log\Utility\LogEnvironment;
2324
use Neos\Flow\Persistence\Exception\IllegalObjectTypeException;
@@ -61,6 +62,12 @@ class ElasticSearchQueryBuilder implements QueryBuilderInterface, ProtectedConte
6162
*/
6263
protected $throwableStorage;
6364

65+
/**
66+
* @Flow\Inject
67+
* @var NodeTypeManager
68+
*/
69+
protected $nodeTypeManager;
70+
6471
/**
6572
* @var boolean
6673
*/
@@ -131,6 +138,92 @@ public function nodeType(string $nodeType): QueryBuilderInterface
131138
return $this->queryFilter('term', ['neos_type_and_supertypes' => $nodeType]);
132139
}
133140

141+
/**
142+
* Filter multiple node types
143+
*
144+
* @param string $nodeTypeFilter
145+
* @return ElasticSearchQueryBuilder
146+
* @throws QueryBuildingException
147+
* @api
148+
*/
149+
public function nodeTypeFilter(string $nodeTypeFilter): QueryBuilderInterface
150+
{
151+
$nodeTypeFilterConstraints = $this->getNodeTypeFilterConstraints($nodeTypeFilter);
152+
153+
$excludeShould = [];
154+
foreach ($nodeTypeFilterConstraints['excludeNodeTypes'] as $nodeType) {
155+
$excludeShould[] = [
156+
'term' => [
157+
'neos_type_and_supertypes' => $nodeType
158+
]
159+
];
160+
}
161+
if (!empty($excludeShould)) {
162+
$this->request->queryFilter(
163+
'bool',
164+
[
165+
'should' => $excludeShould
166+
],
167+
'must_not'
168+
);
169+
}
170+
171+
foreach ($nodeTypeFilterConstraints['includeNodeTypes'] as $nodeType) {
172+
$includeShould[] = [
173+
'term' => [
174+
'neos_type_and_supertypes' => $nodeType
175+
]
176+
];
177+
}
178+
if (!empty($includeShould)) {
179+
$this->request->queryFilter(
180+
'bool',
181+
[
182+
'should' => $includeShould
183+
],
184+
'must'
185+
);
186+
}
187+
188+
return $this;
189+
}
190+
191+
/**
192+
* Generates a two-dimensional array with the filters. First level is:
193+
* 'excludeNodeTypes'
194+
* 'includeNodeTypes'
195+
*
196+
* Both are numeric arrays with the respective node types that are included or excluded.
197+
*
198+
* @param string $nodeTypeFilter
199+
* @return array
200+
*/
201+
protected function getNodeTypeFilterConstraints(string $nodeTypeFilter): array
202+
{
203+
$constraints = [
204+
'excludeNodeTypes' => [],
205+
'includeNodeTypes' => []
206+
];
207+
208+
$nodeTypeFilterParts = Arrays::trimExplode(',', $nodeTypeFilter);
209+
foreach ($nodeTypeFilterParts as $nodeTypeFilterPart) {
210+
$nodeTypeFilterPart = trim($nodeTypeFilterPart);
211+
$constraintType = 'includeNodeTypes';
212+
213+
if (strpos($nodeTypeFilterPart, '!') === 0) {
214+
$nodeTypeFilterPart = substr($nodeTypeFilterPart, 1);
215+
$constraintType = 'excludeNodeTypes';
216+
}
217+
218+
$nodeTypeFilterPartSubTypes = array_merge([$nodeTypeFilterPart], $this->nodeTypeManager->getSubNodeTypes($nodeTypeFilterPart));
219+
foreach ($nodeTypeFilterPartSubTypes as $nodeTypeFilterPartSubType) {
220+
$constraints[$constraintType][(string)$nodeTypeFilterPartSubType] = (string)$nodeTypeFilterPartSubType;
221+
}
222+
}
223+
224+
return $constraints;
225+
}
226+
134227
/**
135228
* Sort descending by $propertyName
136229
*

‎README.md

+18-17
Original file line numberDiff line numberDiff line change
@@ -427,23 +427,24 @@ Furthermore, the following operators are supported:
427427

428428
As **value**, the following methods accept a simple type, a node object or a DateTime object.
429429

430-
| Query Operator | Description |
431-
|----------------|-------------|
432-
|`nodeType('Your.Node:Type')` |Filters on the given NodeType|
433-
|`exactMatch('propertyName', value)` |Supports simple types: `exactMatch('tag', 'foo')`, or node references: `exactMatch('author', authorNode)`|
434-
|`exclude('propertyName', value)` |Excludes results by property - the negation of exactMatch.
435-
|`greaterThan('propertyName', value, [clauseType])` |Range filter with property values greater than the given value|
436-
|`greaterThanOrEqual('propertyName', value, [clauseType])`|Range filter with property values greater than or equal to the given value|
437-
|`lessThan('propertyName', value, [clauseType])` |Range filter with property values less than the given value|
438-
|`lessThanOrEqual('propertyName', value, [clauseType])`|Range filter with property values less than or equal to the given value|
439-
|`sortAsc('propertyName')` / `sortDesc('propertyName')`|Can also be used multiple times, e.g. `sortAsc('tag').sortDesc('date')` will first sort by tag ascending, and then by date descending.|
440-
|`limit(5)` |Only return five results. If not specified, the default limit by Elasticsearch applies (which is at 10 by default)|
441-
|`from(5)` |Return the results starting from the 6th one|
442-
|`prefix('propertyName', 'prefix', [clauseType])` |Adds a prefix filter on the given field with the given prefix|
443-
|`geoDistance(propertyName, geoPoint, distance, [clauseType])`. |Filters documents that include only hits that exists within a specific distance from a geo point.|
444-
|`fulltext('searchWord', options)` |Does a query_string query on the Fulltext index using the searchword and additional [options](https://www.elastic.co/guide/en/elasticsearch/reference/7.6/query-dsl-query-string-query.html) to the query_string. Recommendation: **use simpleQueryStringFulltext instead, as it yields better results and is more tolerant to user input**.|
445-
|`simpleQueryStringFulltext('searchWord', options)` |Does a simple_query_string query on the Fulltext index using the searchword and additional [options](https://www.elastic.co/guide/en/elasticsearch/reference/8.3/query-dsl-simple-query-string-query.html) to the simple_query_string. Supports phrase matching like `"firstname lastname"` and tolerates broken input without exceptions (in contrast to `fulltext()`)|
446-
|`highlight(fragmentSize, fragmentCount, noMatchSize, field)` |Configure result highlighting for every fulltext field individually|
430+
| Query Operator | Description |
431+
|----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
432+
| `nodeType('Your.Node:Type')` | Filters on the given NodeType |
433+
| `nodeTypeFilter('Your.Node:Type,!Your.ExcludedNode:Type')` | Filters multiple NodeTypes |
434+
| `exactMatch('propertyName', value)` | Supports simple types: `exactMatch('tag', 'foo')`, or node references: `exactMatch('author', authorNode)` |
435+
| `exclude('propertyName', value)` | Excludes results by property - the negation of exactMatch. |
436+
| `greaterThan('propertyName', value, [clauseType])` | Range filter with property values greater than the given value |
437+
| `greaterThanOrEqual('propertyName', value, [clauseType])` | Range filter with property values greater than or equal to the given value |
438+
| `lessThan('propertyName', value, [clauseType])` | Range filter with property values less than the given value |
439+
| `lessThanOrEqual('propertyName', value, [clauseType])` | Range filter with property values less than or equal to the given value |
440+
| `sortAsc('propertyName')` / `sortDesc('propertyName')` | Can also be used multiple times, e.g. `sortAsc('tag').sortDesc('date')` will first sort by tag ascending, and then by date descending. |
441+
| `limit(5)` | Only return five results. If not specified, the default limit by Elasticsearch applies (which is at 10 by default) |
442+
| `from(5)` | Return the results starting from the 6th one |
443+
| `prefix('propertyName', 'prefix', [clauseType])` | Adds a prefix filter on the given field with the given prefix |
444+
| `geoDistance(propertyName, geoPoint, distance, [clauseType])`. | Filters documents that include only hits that exists within a specific distance from a geo point. |
445+
| `fulltext('searchWord', options)` | Does a query_string query on the Fulltext index using the searchword and additional [options](https://www.elastic.co/guide/en/elasticsearch/reference/7.6/query-dsl-query-string-query.html) to the query_string. Recommendation: **use simpleQueryStringFulltext instead, as it yields better results and is more tolerant to user input**. |
446+
| `simpleQueryStringFulltext('searchWord', options)` | Does a simple_query_string query on the Fulltext index using the searchword and additional [options](https://www.elastic.co/guide/en/elasticsearch/reference/8.3/query-dsl-simple-query-string-query.html) to the simple_query_string. Supports phrase matching like `"firstname lastname"` and tolerates broken input without exceptions (in contrast to `fulltext()`) |
447+
| `highlight(fragmentSize, fragmentCount, noMatchSize, field)` | Configure result highlighting for every fulltext field individually |
447448

448449
## Search Result Highlighting
449450

0 commit comments

Comments
 (0)
Please sign in to comment.