Skip to content

Commit 1ab5d86

Browse files
committed
MERGE: Merge branch '2.0'
2 parents cd739c7 + 3420a86 commit 1ab5d86

10 files changed

Lines changed: 235 additions & 185 deletions

File tree

File renamed without changes.

Classes/Flowpack/ElasticSearch/ContentRepositoryAdaptor/Eel/ElasticSearchQueryBuilder.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -643,16 +643,17 @@ public function getFullElasticSearchHitForNode(NodeInterface $node)
643643
*/
644644
public function fetch()
645645
{
646+
$request = $this->getRequest();
646647
$timeBefore = microtime(true);
647-
$response = $this->elasticSearchClient->getIndex()->request('GET', '/_search', array(), json_encode($this->request));
648+
$response = $this->elasticSearchClient->getIndex()->request('GET', '/_search', array(), json_encode($request));
648649
$timeAfterwards = microtime(true);
649650

650651
$this->result = $response->getTreatedContent();
651652

652653
$this->result['nodes'] = array();
653654
if ($this->logThisQuery === true) {
654655
$this->logger->log(sprintf('Query Log (%s): %s -- execution time: %s ms -- Limit: %s -- Number of results returned: %s -- Total Results: %s',
655-
$this->logMessage, json_encode($this->request), (($timeAfterwards - $timeBefore) * 1000), $this->limit, count($this->result['hits']['hits']), $this->result['hits']['total']), LOG_DEBUG);
656+
$this->logMessage, json_encode($request), (($timeAfterwards - $timeBefore) * 1000), $this->limit, count($this->result['hits']['hits']), $this->result['hits']['total']), LOG_DEBUG);
656657
}
657658
if (array_key_exists('hits', $this->result) && is_array($this->result['hits']) && count($this->result['hits']) > 0) {
658659
$this->result['nodes'] = $this->convertHitsToNodes($this->result['hits']);
@@ -684,7 +685,7 @@ public function execute()
684685
public function count()
685686
{
686687
$timeBefore = microtime(true);
687-
$request = $this->request;
688+
$request = $this->getRequest();
688689
foreach ($this->unsupportedFieldsInCountRequest as $field) {
689690
if (isset($request[$field])) {
690691
unset($request[$field]);
@@ -698,7 +699,7 @@ public function count()
698699
$count = $treatedContent['count'];
699700

700701
if ($this->logThisQuery === true) {
701-
$this->logger->log('Count Query Log (' . $this->logMessage . '): ' . json_encode($this->request) . ' -- execution time: ' . (($timeAfterwards - $timeBefore) * 1000) . ' ms -- Total Results: ' . $count, LOG_DEBUG);
702+
$this->logger->log('Count Query Log (' . $this->logMessage . '): ' . json_encode($request) . ' -- execution time: ' . (($timeAfterwards - $timeBefore) * 1000) . ' ms -- Total Results: ' . $count, LOG_DEBUG);
702703
}
703704

704705
return $count;

Classes/Flowpack/ElasticSearch/ContentRepositoryAdaptor/Eel/ElasticSearchQueryResult.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,3 @@ public function allowsCallOfMethod($methodName)
255255
return true;
256256
}
257257
}
258-

Classes/Flowpack/ElasticSearch/ContentRepositoryAdaptor/Indexer/NodeIndexer.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ public function getIndex()
137137
public function indexNode(NodeInterface $node, $targetWorkspaceName = null)
138138
{
139139
$indexer = function (NodeInterface $node, $targetWorkspaceName = null) {
140-
141140
$contextPath = $node->getContextPath();
142141

143142
if ($this->settings['indexAllWorkspaces'] === false) {
@@ -164,7 +163,7 @@ public function indexNode(NodeInterface $node, $targetWorkspaceName = null)
164163
$mappingType = $this->getIndex()->findType(NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($nodeType));
165164

166165
// Remove document with the same contextPathHash but different NodeType, required after NodeType change
167-
$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)');
166+
$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)');
168167
$this->getIndex()->request('DELETE', '/_query', array(), json_encode([
169168
'query' => [
170169
'bool' => [
@@ -273,11 +272,10 @@ public function indexNode(NodeInterface $node, $targetWorkspaceName = null)
273272
} else {
274273
$context = $this->contextFactory->create(array('workspaceName' => $workspaceName));
275274
$node = $context->getNodeByIdentifier($nodeIdentifier);
276-
if ($node !== NULL) {
275+
if ($node !== null) {
277276
$indexer($node, $targetWorkspaceName);
278277
}
279278
}
280-
281279
}
282280

283281
/**

Classes/Flowpack/ElasticSearch/ContentRepositoryAdaptor/ViewHelpers/GetHitArrayForNodeViewHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class GetHitArrayForNodeViewHelper extends AbstractViewHelper
4545
* @param array|string $path
4646
* @return array
4747
*/
48-
public function render(ElasticSearchQueryResult $queryResultObject, NodeInterface $node, $path = NULL)
48+
public function render(ElasticSearchQueryResult $queryResultObject, NodeInterface $node, $path = null)
4949
{
5050
$hitArray = $queryResultObject->searchHitForNode($node);
5151

README.md

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ Furthermore, the following operators are supported:
212212
* `greaterThanOrEqual('propertyName', value)` -- range filter with property values greater than or equal to the given value
213213
* `lessThan('propertyName', value)` -- range filter with property values less than the given value
214214
* `lessThanOrEqual('propertyName', value)` -- range filter with property values less than or equal to the given value
215-
* `sortAsc('propertyName')` and `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.
215+
* `sortAsc('propertyName')` and `sortDesc('propertyName')` -- can also be used multiple times, e.g. `sortAsc('tag').sortDesc(`date')`
216+
will first sort by tag ascending, and then by date descending.
216217
* `limit(5)` -- only return five results. If not specified, the default limit by Elasticsearch applies (which is at 10 by default)
217218
* `from(5)` -- return the results starting from the 6th one
218219
* `fulltext(...)` -- do a query_string query on the Fulltext Index
@@ -255,6 +256,18 @@ prototype(Acme.Blog:SingleTag) < prototype(TYPO3.Neos:Template) {
255256
}
256257
```
257258

259+
#### Making OR queries
260+
261+
There's no OR operator provided in this package, so you need to use a custom Elasticsearch query filter for that:
262+
263+
```
264+
....queryFilter('bool', {should: [
265+
{term: {tags: tagNode.identifier}},
266+
{term: {places: tagNode.identifier}},
267+
{term: {projects: tagNode.identifier}}
268+
]})
269+
```
270+
258271
## Aggregations
259272

260273
Aggregation is an easy way to aggregate your node data in different ways. Elasticsearch provides a couple of different types of
@@ -272,7 +285,9 @@ You can nest aggregations by providing a parent name.
272285

273286

274287
### Examples
288+
275289
#### Add a average aggregation
290+
276291
To add an average aggregation you can use the fieldBasedAggregation. This snippet would add an average aggregation for
277292
a property price:
278293
```
@@ -284,6 +299,7 @@ Now you can access your aggregations inside your fluid template with
284299
```
285300

286301
#### Create a nested aggregation
302+
287303
In this scenario you could have a node that represents a product with the properties price and color. If you would like
288304
to know the average price for all your colors you just nest an aggregation in your TypoScript:
289305
```
@@ -299,6 +315,7 @@ fieldBasedAggregation("anotherAggregation", "field", "avg", "colors.avgprice")
299315
```
300316

301317
#### Add a custom aggregation
318+
302319
To add a custom aggregation you can use the `aggregation()` method. All you have to do is to provide an array with your
303320
aggregation definition. This example would do the same as the fieldBasedAggregation would do for you:
304321
```
@@ -311,6 +328,7 @@ nodes = ${Search.query(site)...aggregation("color", this.aggregationDefinition).
311328
```
312329

313330
#### Product filter
331+
314332
This is a more complex scenario. With this snippet we will create a full product filter based on your selected Nodes. Imagine
315333
an NodeTye ProductList with an property `products`. This property contains a comma separated list of sku's. This could also
316334
be a reference on other products.
@@ -356,8 +374,8 @@ be fetched and passed to your template.
356374

357375
**Important notice**
358376

359-
If you do use the terms filter be aware of Elasticsearchs analyze functionality. You might want to disable this for all
360-
your filterable properties like this:
377+
If you do use the terms filter be aware of Elasticsearchs analyze functionality for strings. You might want to disable this
378+
for all your filterable properties, or else filtering won't work on them properly:
361379
```
362380
'Vendor.Name:Product'
363381
properties:
@@ -373,7 +391,7 @@ your filterable properties like this:
373391

374392
## Sorting
375393

376-
This package adapts ElasticSearchs sorting capabilities. You can add multiple sort operations to your query.
394+
This package adapts Elasticsearchs sorting capabilities. You can add multiple sort operations to your query.
377395
Right now there are three methods you can use:
378396

379397
* `sortAsc('propertyName')`
@@ -382,13 +400,16 @@ Right now there are three methods you can use:
382400

383401
Just append those method to your query like this:
384402
```
385-
# sort ascending by property title
403+
# Sort ascending by property title
404+
386405
nodes = ${q(Search.query(site).....sortAsc("title").execute())}
387406
388-
# sort for multiple properties
407+
# Sort for multiple properties
408+
389409
nodes = ${q(Search.query(site).....sortAsc("title").sortDesc("name").execute())}
390410
391-
# custom sort opertation
411+
# Custom sort operation
412+
392413
geoSorting = TYPO3.TypoScript:RawArray {
393414
_geo_distance = TYPO3.TypoScript:RawArray {
394415
latlng = TYPO3.TypoScript:RawArray {
@@ -407,6 +428,7 @@ Check https://www.elastic.co/guide/en/elasticsearch/reference/current/search-req
407428
options.
408429

409430
### Example with pagination and sort by distance
431+
410432
This is how a more complex example could look like. Imagine you a want to render a list of nodes and in addition to each
411433
node you want to display the distance to a specific point.
412434

@@ -454,6 +476,7 @@ The ViewHelper will use \TYPO3\Flow\Utility\Arrays::getValueByPath() to return a
454476
of an array or a string. Check the documentation \TYPO3\Flow\Utility\Arrays::getValueByPath() for more informations.
455477

456478
**Important notice**
479+
457480
The ViewHelper GetHitArrayForNode will return the raw hit result array. The path poperty allows you to access some
458481
specific data like the the sort data. If there is only one value for your path the value will be returned.
459482
If there is more data the full array will be returned by GetHitArrayForNode-VH. So you might have to use the
@@ -489,26 +512,30 @@ Elasticsearch offers an easy way to get query suggestions based on your query. C
489512
you can build and use suggestion in your search.
490513

491514
**Suggestion methods implemented**
492-
There are two methods implemented. `suggestions` is a generic one that allows to build the suggestion query of your
493-
dreams. The other method is `termSuggestions` and is meant for basic term suggestions. They can be added to your totaly
515+
516+
There are two methods implemented. `suggestions` is a generic one that allows to build the suggestion query of your
517+
dreams. The other method is `termSuggestions` and is meant for basic term suggestions. They can be added to your totaly
494518
awesome TS search query.
495-
519+
496520
* `suggestions($name, array $suggestionDefinition)` -- generic method to be filled with your own suggestionQuery
497521
* `termSuggestions($term, $field = '_all', $name = 'suggestions'` -- simple term suggestion query on all fields
498522

499523
### Examples
524+
500525
#### Add a simple suggestion to a query
526+
501527
Simple suggestion that returns a suggestion based on the sent term
502528

503529
```
504530
suggestions = $(Search.query(site)...termSuggestions('someTerm')}
505531
```
506-
You can access your suggestions inside your fluid template with
532+
You can access your suggestions inside your fluid template with
507533
```
508534
{nodes.suggestions}
509535
```
510536

511537
### Add a custom suggestion
538+
512539
Phrase query that returns query suggestions
513540

514541
```
@@ -529,7 +556,8 @@ suggestions = ${Search.query(site)...suggestions('my_suggestions', this.suggesti
529556

530557
## Advanced: Configuration of Indexing
531558

532-
**Normally, this does not need to be touched, as this package supports all Neos data types natively.**
559+
**The default configuration supports most usecases and often may not need to be touched, as this package comes
560+
with sane defaults for all Neos data types.**
533561

534562
Indexing of properties is configured at two places. The defaults per-data-type are configured
535563
inside `TYPO3.TYPO3CR.Search.defaultConfigurationPerType` of `Settings.yaml`.
@@ -575,6 +603,9 @@ TYPO3:
575603
indexing: '${(node.hiddenBeforeDateTime ? Date.format(node.hiddenBeforeDateTime, "Y-m-d\TH:i:sP") : null)}'
576604
```
577605

606+
If your nodetypes schema defines custom properties of type DateTime, you have got to provide similar configuration for
607+
them as well in your `NodeTypes.yaml`, or else they will not be indexed correctly.
608+
578609
There are a few indexing helpers inside the `Indexing` namespace which are usable inside the
579610
`indexing` expression. In most cases, you don't need to touch this, but they were needed to build up
580611
the standard indexing configuration:
@@ -647,6 +678,12 @@ currently configured in PHP, the configuration for any property in a node which
647678
This is important so that Date- and Time-based searches work as expected, both when using formatted DateTime strings and
648679
when using relative DateTime calculations (eg.: `now`, `now+1d`).
649680

681+
If you want to filter items by date, e.g. to show items with date later than today, you can create a query like this:
682+
683+
```
684+
${...greaterThan('date', Date.format(Date.Now(), "Y-m-d\TH:i:sP"))...}
685+
```
686+
650687
For more information on Elasticsearch's Date Formats,
651688
[click here](http://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html).
652689

Tests/Functional/Eel/ElasticSearchQueryTest.php

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Command\NodeIndexCommandController;
1515
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryBuilder;
1616
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryResult;
17+
use TYPO3\Flow\Persistence\QueryResultInterface;
1718
use TYPO3\TYPO3CR\Domain\Model\NodeInterface;
1819
use TYPO3\TYPO3CR\Domain\Model\Workspace;
1920
use TYPO3\TYPO3CR\Domain\Repository\NodeDataRepository;
@@ -67,19 +68,20 @@ class ElasticSearchQueryTest extends \TYPO3\Flow\Tests\FunctionalTestCase
6768
*/
6869
protected $nodeDataRepository;
6970

70-
71+
/**
72+
* @var boolean
73+
*/
7174
protected static $indexInitialized = false;
7275

73-
7476
public function setUp()
7577
{
7678
parent::setUp();
77-
$this->workspaceRepository = $this->objectManager->get('TYPO3\TYPO3CR\Domain\Repository\WorkspaceRepository');
79+
$this->workspaceRepository = $this->objectManager->get(WorkspaceRepository::class);
7880
$liveWorkspace = new Workspace('live');
7981
$this->workspaceRepository->add($liveWorkspace);
8082

81-
$this->nodeTypeManager = $this->objectManager->get('TYPO3\TYPO3CR\Domain\Service\NodeTypeManager');
82-
$this->contextFactory = $this->objectManager->get('TYPO3\TYPO3CR\Domain\Service\ContextFactoryInterface');
83+
$this->nodeTypeManager = $this->objectManager->get(NodeTypeManager::class);
84+
$this->contextFactory = $this->objectManager->get(ContextFactoryInterface::class);
8385
$this->context = $this->contextFactory->create([
8486
'workspaceName' => 'live',
8587
'dimensions' => ['language' => ['en_US']],
@@ -90,9 +92,9 @@ public function setUp()
9092
$this->siteNode = $rootNode->createNode('welcome', $this->nodeTypeManager->getNodeType('TYPO3.Neos.NodeTypes:Page'));
9193
$this->siteNode->setProperty('title', 'welcome');
9294

93-
$this->nodeDataRepository = $this->objectManager->get('TYPO3\TYPO3CR\Domain\Repository\NodeDataRepository');
95+
$this->nodeDataRepository = $this->objectManager->get(NodeDataRepository::class);
9496

95-
$this->nodeIndexCommandController = $this->objectManager->get('Flowpack\ElasticSearch\ContentRepositoryAdaptor\Command\NodeIndexCommandController');
97+
$this->nodeIndexCommandController = $this->objectManager->get(NodeIndexCommandController::class);
9698

9799
$this->createNodesForNodeSearchTest();
98100
}
@@ -104,12 +106,13 @@ public function tearDown()
104106
}
105107

106108
/**
107-
* @return \Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryBuilder
109+
* @return ElasticSearchQueryBuilder
108110
*/
109111
protected function getQueryBuilder()
110112
{
111113
/** @var ElasticSearchQueryBuilder $query */
112-
$query = $this->objectManager->get('Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryBuilder');
114+
$query = $this->objectManager->get(ElasticSearchQueryBuilder::class);
115+
113116
return $query->query($this->siteNode);
114117
}
115118

@@ -221,9 +224,9 @@ public function nodesWillBeSortedDesc()
221224
->sortDesc('title')
222225
->execute();
223226

224-
/** @var \TYPO3\Flow\Persistence\QueryResultInterface $result $node */
227+
/** @var QueryResultInterface $result $node */
225228

226-
$this->assertInstanceOf(\TYPO3\Flow\Persistence\QueryResultInterface::class, $result);
229+
$this->assertInstanceOf(QueryResultInterface::class, $result);
227230
$this->assertCount(3, $result, 'The result should have 3 items');
228231
$this->assertEquals(3, $result->count(), 'Count should be 3');
229232

Tests/Unit/Eel/ElasticSearchQueryBuilderResultTest.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* The TYPO3 project - inspiring people to share! *
1212
* */
1313

14+
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQuery;
1415
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryBuilder;
1516
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryResult;
1617

@@ -25,14 +26,14 @@ class ElasticSearchQueryBuilderResultTest extends \TYPO3\Flow\Tests\UnitTestCase
2526
*/
2627
public function ifNoAggregationsAreSetInTheQueyBuilderResultAnEmptyArrayWillBeReturnedIfYouFetchTheAggregations()
2728
{
28-
$resultArrayWithoutAggregations = array(
29-
"nodes" => array("some", "nodes")
30-
);
29+
$resultArrayWithoutAggregations = [
30+
"nodes" => ["some", "nodes"]
31+
];
3132

32-
$queryBuilder = $this->getMock(ElasticSearchQueryBuilder::class, array("fetch"));
33+
$queryBuilder = $this->getMockBuilder(ElasticSearchQueryBuilder::class)->setMethods(["fetch"])->getMock();
3334
$queryBuilder->method("fetch")->will($this->returnValue($resultArrayWithoutAggregations));
3435

35-
$esQuery = new \Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQuery($queryBuilder);
36+
$esQuery = new ElasticSearchQuery($queryBuilder);
3637

3738
$queryResult = new ElasticSearchQueryResult($esQuery);
3839

@@ -41,4 +42,4 @@ public function ifNoAggregationsAreSetInTheQueyBuilderResultAnEmptyArrayWillBeRe
4142
$this->assertTrue(is_array($actual));
4243
$this->assertEmpty($actual);
4344
}
44-
}
45+
}

0 commit comments

Comments
 (0)