1111 * source code.
1212 */
1313
14- use Flowpack \ElasticSearch \ContentRepositoryAdaptor \ElasticSearchClient ;
14+ use Flowpack \ElasticSearch \ContentRepositoryAdaptor \Eel \ElasticSearchQueryBuilder ;
15+ use Flowpack \ElasticSearch \ContentRepositoryAdaptor \Eel \ElasticSearchQueryResult ;
16+ use Neos \ContentRepository \Domain \Model \NodeInterface ;
1517use Neos \Flow \Annotations as Flow ;
1618use Neos \Flow \Mvc \Controller \ActionController ;
1719use Neos \Flow \Mvc \View \JsonView ;
1820
21+ /**
22+ * Class SuggestController
23+ */
1924class SuggestController extends ActionController
2025{
2126 /**
2227 * @Flow\Inject
23- * @var ElasticSearchClient
28+ * @var ElasticSearchQueryBuilder
2429 */
25- protected $ elasticSearchClient ;
30+ protected $ elasticSearchQueryBuilder ;
2631
2732 /**
2833 * @var array
@@ -32,26 +37,81 @@ class SuggestController extends ActionController
3237 ];
3338
3439 /**
40+ * @param NodeInterface $contextNode
3541 * @param string $term
36- *
3742 * @return void
3843 */
39- public function indexAction ($ term )
44+ public function indexAction (NodeInterface $ contextNode , $ term )
4045 {
41- $ request = [
42- 'suggests ' => [
46+ $ result = [
47+ 'completions ' => [],
48+ 'suggestions ' => []
49+ ];
50+
51+ if (!is_string ($ term )) {
52+ $ result ['errors ' ] = ['term has to be a string ' ];
53+ $ this ->view ->assign ('value ' , $ result );
54+ return ;
55+ }
56+
57+ $ term = strtolower ($ term );
58+
59+ // TODO: cache query by node identifier
60+
61+ /** @var ElasticSearchQueryBuilder $query */
62+ $ query = $ this ->elasticSearchQueryBuilder ->query ($ contextNode );
63+ $ query
64+ ->queryFilter ('prefix ' , [
65+ '__completion ' => $ term
66+ ])
67+ ->limit (0 )
68+ ->aggregation ('autocomplete ' , [
69+ 'terms ' => [
70+ 'field ' => '__completion ' ,
71+ 'order ' => [
72+ '_count ' => 'desc '
73+ ],
74+ 'include ' => [
75+ 'pattern ' => $ term . '.* '
76+ ]
77+ ]
78+ ])
79+ ->suggestions ('suggestions ' , [
4380 'text ' => $ term ,
44- 'term ' => [
45- 'field ' => '_all '
81+ 'completion ' => [
82+ 'field ' => '__suggestions ' ,
83+ 'fuzzy ' => true ,
84+ 'context ' => [
85+ 'parentPath ' => $ contextNode ->getPath (),
86+ 'workspace ' => 'live ' ,
87+ 'dimensionCombinationHash ' => md5 (json_encode ($ contextNode ->getContext ()->getDimensions ())),
88+ ]
4689 ]
47- ]
48- ];
90+ ]);
91+
92+ try {
93+ /** @var ElasticSearchQueryResult $queryResult */
94+ $ queryResult = $ query ->execute ();
95+ } catch (\Exception $ e ) {
96+ $ result ['errors ' ] = ['Could not execute query ' ];
97+ $ this ->view ->assign ('value ' , $ result );
98+ return ;
99+ }
100+
101+ $ aggregations = $ queryResult ->getAggregations ();
102+
103+ // Extract autocomplete options
104+ $ autoCompletionOptions = array_map (function ($ option ) {
105+ return $ option ['key ' ];
106+ }, $ aggregations ['autocomplete ' ]['buckets ' ]);
107+ $ result ['completions ' ] = $ autoCompletionOptions ;
49108
50- $ response = $ this ->elasticSearchClient ->getIndex ()->request ('POST ' , '/_suggest ' , [], json_encode ($ request ))->getTreatedContent ();
51- $ suggestions = array_map (function ($ option ) {
52- return $ option ['text ' ];
53- }, $ response ['suggests ' ][0 ]['options ' ]);
109+ // Extract suggestion options
110+ $ suggestionOptions = $ queryResult ->getSuggestions ();
111+ if (count ($ suggestionOptions ['suggestions ' ][0 ]['options ' ]) > 0 ) {
112+ $ result ['suggestions ' ] = $ suggestionOptions ['suggestions ' ][0 ]['options ' ];
113+ }
54114
55- $ this ->view ->assign ('value ' , $ suggestions );
115+ $ this ->view ->assign ('value ' , $ result );
56116 }
57117}
0 commit comments