Skip to content

Commit 2b66844

Browse files
committed
FEATURE: Add Node-Type Filter
- Creates a method which allows to create a nodetype filter to include and exclude specific nodetypes - Adds a test for the new filter function - Adjusts existing tests to match the new circumstances
1 parent 2a7de69 commit 2b66844

File tree

5 files changed

+96
-21
lines changed

5 files changed

+96
-21
lines changed

Classes/Eel/ElasticSearchQueryBuilder.php

+50
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,56 @@ public function nodeType(string $nodeType): QueryBuilderInterface
131131
return $this->queryFilter('term', ['neos_type_and_supertypes' => $nodeType]);
132132
}
133133

134+
/**
135+
* Filter multiple node types
136+
*
137+
* @param array $expectedNodeTypes NodeTypes that should be expected
138+
* @param array $excludedNodeTypes NodeTypes that should be excluded
139+
* @return ElasticSearchQueryBuilder
140+
* @throws QueryBuildingException
141+
* @api
142+
*/
143+
public function nodeTypeFilter(array $expectedNodeTypes, array $excludedNodeTypes = []): QueryBuilderInterface
144+
{
145+
$excludeTerms = [];
146+
foreach ($excludedNodeTypes as $nodeType) {
147+
$excludeTerms[] = [
148+
'term' => [
149+
'neos_type_and_supertypes' => $nodeType
150+
]
151+
];
152+
}
153+
if (!empty($excludeTerms)) {
154+
$this->request->queryFilter(
155+
'bool',
156+
[
157+
'should' => $excludeTerms
158+
],
159+
'must_not'
160+
);
161+
}
162+
163+
$includeTerms = [];
164+
foreach ($expectedNodeTypes as $nodeType) {
165+
$includeTerms[] = [
166+
'term' => [
167+
'neos_type_and_supertypes' => $nodeType
168+
]
169+
];
170+
}
171+
if (!empty($includeTerms)) {
172+
$this->request->queryFilter(
173+
'bool',
174+
[
175+
'should' => $includeTerms
176+
],
177+
'must'
178+
);
179+
}
180+
181+
return $this;
182+
}
183+
134184
/**
135185
* Sort descending by $propertyName
136186
*

Configuration/Testing/NodeTypes.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
main:
2626
type: 'Neos.Neos:ContentCollection'
2727

28+
'Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document2':
29+
superTypes:
30+
'Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document': true
31+
2832
'Flowpack.ElasticSearch.ContentRepositoryAdaptor:Content':
2933
superTypes:
3034
'Neos.Neos:Content': true

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

Tests/Functional/Eel/ElasticSearchQueryTest.php

+16-4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ public function filterByNodeType(): void
9999
->log($this->getLogMessagePrefix(__METHOD__))
100100
->nodeType('Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document')
101101
->count();
102+
static::assertEquals(6, $resultCount);
103+
}
104+
105+
/**
106+
* @test
107+
*/
108+
public function filterByNodeTypes(): void
109+
{
110+
$resultCount = $this->getQueryBuilder()
111+
->log($this->getLogMessagePrefix(__METHOD__))
112+
->nodeTypeFilter(['Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document'], ['Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document2'])
113+
->count();
102114
static::assertEquals(4, $resultCount);
103115
}
104116

@@ -142,7 +154,7 @@ public function limitDoesNotImpactCount(): void
142154
->limit(1);
143155

144156
$resultCount = $query->count();
145-
static::assertEquals(4, $resultCount, 'Asserting the count query returns the total count.');
157+
static::assertEquals(6, $resultCount, 'Asserting the count query returns the total count.');
146158
}
147159

148160
/**
@@ -174,7 +186,7 @@ public function fieldBasedAggregations(): void
174186
->getAggregations();
175187

176188
static::assertArrayHasKey($aggregationTitle, $result);
177-
static::assertCount(3, $result[$aggregationTitle]['buckets']);
189+
static::assertCount(5, $result[$aggregationTitle]['buckets']);
178190

179191
$expectedChickenBucket = [
180192
'key' => 'chicken',
@@ -247,8 +259,8 @@ public function nodesWillBeSortedDesc(): void
247259
/** @var QueryResultInterface $result $node */
248260

249261
static::assertInstanceOf(QueryResultInterface::class, $result);
250-
static::assertCount(4, $result, 'The result should have 3 items');
251-
static::assertEquals(4, $result->count(), 'Count should be 3');
262+
static::assertCount(6, $result, 'The result should have 6 items');
263+
static::assertEquals(6, $result->count(), 'Count should be 6');
252264

253265
$node = $result->getFirst();
254266

Tests/Functional/Traits/ContentRepositoryNodeCreationTrait.php

+8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ protected function createNodesForNodeSearchTest(): void
5454
$newDocumentNode3->setProperty('title', 'egg');
5555
$newDocumentNode3->setProperty('title_analyzed', 'egg');
5656

57+
$newDocumentNode4 = $this->siteNode->createNode('test-node-4', $this->nodeTypeManager->getNodeType('Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document2'));
58+
$newDocumentNode4->setProperty('title', 'tiger');
59+
$newDocumentNode4->setProperty('title_analyzed', 'tiger');
60+
61+
$newDocumentNode5 = $this->siteNode->createNode('test-node-5', $this->nodeTypeManager->getNodeType('Flowpack.ElasticSearch.ContentRepositoryAdaptor:Document2'));
62+
$newDocumentNode5->setProperty('title', 'elephant');
63+
$newDocumentNode5->setProperty('title_analyzed', 'elephant');
64+
5765
$dimensionContext = $this->contextFactory->create([
5866
'workspaceName' => 'live',
5967
'dimensions' => ['language' => ['de']]

0 commit comments

Comments
 (0)