Skip to content

Commit f7c9376

Browse files
committed
FEATURE: More like this
This implements an convinent API to build more like this queries. For details see [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-mlt-query.html)
1 parent bf84d67 commit f7c9376

4 files changed

Lines changed: 80 additions & 2 deletions

File tree

Classes/Driver/AbstractQuery.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ public function aggregation($name, array $aggregationDefinition, $parentPath = '
109109
}
110110
}
111111

112+
/**
113+
* {@inheritdoc}
114+
* @throws Exception\QueryBuildingException
115+
*/
116+
public function moreLikeThis(array $like, array $fields = [], $options = [])
117+
{
118+
$moreLikeThis = $options;
119+
$moreLikeThis['like'] = $like;
120+
121+
if (!empty($fields)) {
122+
$moreLikeThis['fields'] = $fields;
123+
}
124+
125+
$this->appendAtPath('query.bool.filter.bool.must', ['more_like_this' => $moreLikeThis]);
126+
}
127+
112128
/**
113129
* This is an low level method for internal usage.
114130
*
@@ -119,7 +135,6 @@ public function aggregation($name, array $aggregationDefinition, $parentPath = '
119135
* @param string $parentPath The parent path to add the sub aggregation to
120136
* @param string $name The name to identify the resulting aggregation
121137
* @param array $aggregationConfiguration
122-
* @return QueryInterface
123138
* @throws Exception\QueryBuildingException
124139
*/
125140
protected function addSubAggregation($parentPath, $name, $aggregationConfiguration)

Classes/Driver/QueryInterface.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ public function aggregation($name, array $aggregationDefinition, $parentPath = '
111111
*/
112112
public function suggestions($name, array $suggestionDefinition);
113113

114+
/**
115+
* This method is used to define a more like this query.
116+
* The More Like This Query (MLT Query) finds documents that are "like" a given text
117+
* or a given set of documents
118+
*
119+
* @param array $like An array of strings or documents
120+
* @param array $fields Fields to compare other docs with
121+
* @param array $options Additional options for the more_like_this quey
122+
* @return void
123+
*/
124+
public function moreLikeThis(array $like, array $fields = [], array $options = []);
125+
114126
/**
115127
* Add a query filter
116128
*

Classes/Eel/ElasticSearchQueryBuilder.php

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* source code.
1212
*/
1313

14+
use Neos\Flow\Annotations as Flow;
1415
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Driver\QueryInterface;
1516
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\Dto\SearchResult;
1617
use Flowpack\ElasticSearch\ContentRepositoryAdaptor\ElasticSearchClient;
@@ -21,7 +22,6 @@
2122
use Neos\ContentRepository\Domain\Model\NodeInterface;
2223
use Neos\ContentRepository\Search\Search\QueryBuilderInterface;
2324
use Neos\Eel\ProtectedContextAwareInterface;
24-
use Neos\Flow\Annotations as Flow;
2525
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
2626
use Neos\Flow\Utility\Now;
2727
use Neos\Utility\Arrays;
@@ -567,6 +567,7 @@ public function fetch()
567567
try {
568568
$timeBefore = microtime(true);
569569
$request = $this->request->getRequestAsJson();
570+
570571
$response = $this->elasticSearchClient->getIndex()->request('GET', '/_search', [], $request);
571572
$timeAfterwards = microtime(true);
572573

@@ -670,6 +671,48 @@ public function highlight($fragmentSize, $fragmentCount = null)
670671
return $this;
671672
}
672673

674+
/**
675+
* This method is used to define a more like this query.
676+
* The More Like This Query (MLT Query) finds documents that are "like" a given set of documents.
677+
* See: https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-mlt-query.html
678+
*
679+
* @param array $like An array of strings or documents
680+
* @param array $fields Fields to compare other docs with
681+
* @param array $options Additional options for the more_like_this quey
682+
* @return ElasticSearchQueryBuilder
683+
*/
684+
public function moreLikeThis($like, array $fields = [], array $options = [])
685+
{
686+
$like = is_array($like) ? $like : [$like];
687+
688+
$getDocumentDefinitionByNode = function (QueryInterface $request, NodeInterface $node): array {
689+
$request->queryFilter('term', ['__identifier' => $node->getIdentifier()]);
690+
$response = $this->elasticSearchClient->getIndex()->request('GET', '/_search', [], $request->toArray())->getTreatedContent();
691+
692+
$respondedDocuments = Arrays::getValueByPath($response, 'hits.hits');
693+
if (count($respondedDocuments) === 0) {
694+
throw new Exception(sprintf('The node with identifier %s was not found in the elasticsearch index', $node->getIdentifier()), 1536485615);
695+
}
696+
$respondedDocument = current($respondedDocuments);
697+
return [
698+
'_id' => $respondedDocument['_id'],
699+
'_type' => $respondedDocument['_type'],
700+
'_index' => $respondedDocument['_index'],
701+
];
702+
};
703+
704+
foreach ($like as $key => $likeElement) {
705+
if ($likeElement instanceof NodeInterface) {
706+
$like[$key] = $getDocumentDefinitionByNode(clone $this->request, $likeElement);
707+
}
708+
}
709+
710+
$this->request->moreLikeThis($like, $fields, $options);
711+
712+
return $this;
713+
}
714+
715+
673716
/**
674717
* Sets the starting point for this query. Search result should only contain nodes that
675718
* match the context of the given node and have it as parent node in their rootline.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ Furthermore, the following operators are supported:
133133
* `from(5)` -- return the results starting from the 6th one
134134
* `fulltext('searchWord', options)` -- do a query_string query on the Fulltext index using the searchword and additional [options](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-query-string-query.html) to the query_string
135135

136+
#### moreLikeThis(like, fields, options)
137+
138+
The More Like This Query (MLT Query) finds documents that are "like" a given text or a given set of documents.
139+
140+
* `like` Single value or an array of strings or nodes.
141+
* `fields` An array of fields which are used to compare other docs with the given "like" definition.
142+
* `options` Additional options for the `more_like_this` query. See the [elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-mlt-query.html) for what is possible.
143+
136144
Furthermore, there is a more low-level operator which can be used to add arbitrary Elasticsearch filters:
137145

138146
* `queryFilter("filterType", {option1: "value1"}, [clauseType])`

0 commit comments

Comments
 (0)