1212 */
1313
1414use Flowpack \ElasticSearch \ContentRepositoryAdaptor \Indexer \Error \ErrorInterface ;
15+ use Flowpack \ElasticSearch \ContentRepositoryAdaptor \LoggerInterface ;
1516use Flowpack \ElasticSearch \ContentRepositoryAdaptor \Mapping \NodeTypeMappingBuilder ;
1617use Flowpack \ElasticSearch \ContentRepositoryAdaptor \Service \ErrorHandlingService ;
1718use Flowpack \ElasticSearch \ContentRepositoryAdaptor \Service \IndexWorkspaceTrait ;
18- use TYPO3 \Flow \Annotations as Flow ;
19- use TYPO3 \Flow \Cli \CommandController ;
19+ use Flowpack \ElasticSearch \Domain \Model \Mapping ;
20+ use Flowpack \ElasticSearch \Transfer \Exception \ApiException ;
21+ use Neos \ContentRepository \Domain \Factory \NodeFactory ;
22+ use Neos \ContentRepository \Domain \Model \Workspace ;
23+ use Neos \ContentRepository \Domain \Repository \NodeDataRepository ;
24+ use Neos \ContentRepository \Domain \Repository \WorkspaceRepository ;
25+ use Neos \ContentRepository \Domain \Service \ContentDimensionPresetSourceInterface ;
26+ use Neos \ContentRepository \Domain \Service \Context ;
27+ use Neos \ContentRepository \Domain \Service \ContextFactoryInterface ;
28+ use Neos \ContentRepository \Search \Indexer \NodeIndexerInterface ;
29+ use Neos \Flow \Annotations as Flow ;
30+ use Neos \Flow \Cli \CommandController ;
31+ use Neos \Flow \Configuration \ConfigurationManager ;
32+ use Neos \Flow \ObjectManagement \ObjectManagerInterface ;
33+ use Symfony \Component \Yaml \Yaml ;
2034
2135/**
2236 * Provides CLI features for index handling
@@ -35,31 +49,31 @@ class NodeIndexCommandController extends CommandController
3549
3650 /**
3751 * @Flow\Inject
38- * @var \Flowpack\ElasticSearch\ContentRepositoryAdaptor\Indexer\NodeIndexer
52+ * @var NodeIndexerInterface
3953 */
4054 protected $ nodeIndexer ;
4155
4256 /**
4357 * @Flow\Inject
44- * @var \TYPO3\TYPO3CR\Domain\Repository\ WorkspaceRepository
58+ * @var WorkspaceRepository
4559 */
4660 protected $ workspaceRepository ;
4761
4862 /**
4963 * @Flow\Inject
50- * @var \TYPO3\TYPO3CR\Domain\Repository\ NodeDataRepository
64+ * @var NodeDataRepository
5165 */
5266 protected $ nodeDataRepository ;
5367
5468 /**
5569 * @Flow\Inject
56- * @var \TYPO3\TYPO3CR\Domain\Factory\ NodeFactory
70+ * @var NodeFactory
5771 */
5872 protected $ nodeFactory ;
5973
6074 /**
6175 * @Flow\Inject
62- * @var \TYPO3\Neos\Domain\Service\ ContentDimensionPresetSourceInterface
76+ * @var ContentDimensionPresetSourceInterface
6377 */
6478 protected $ contentDimensionPresetSource ;
6579
@@ -71,16 +85,22 @@ class NodeIndexCommandController extends CommandController
7185
7286 /**
7387 * @Flow\Inject
74- * @var \Flowpack\ElasticSearch\ContentRepositoryAdaptor\ LoggerInterface
88+ * @var LoggerInterface
7589 */
7690 protected $ logger ;
7791
7892 /**
7993 * @Flow\Inject
80- * @var \TYPO3\Flow\Configuration\ ConfigurationManager
94+ * @var ConfigurationManager
8195 */
8296 protected $ configurationManager ;
8397
98+ /**
99+ * @Flow\Inject
100+ * @var ContextFactoryInterface
101+ */
102+ protected $ contextFactory ;
103+
84104 /**
85105 * @var array
86106 */
@@ -93,8 +113,8 @@ class NodeIndexCommandController extends CommandController
93113 */
94114 public function initializeObject ($ cause )
95115 {
96- if ($ cause === \ TYPO3 \ Flow \ Object \ ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED ) {
97- $ this ->settings = $ this ->configurationManager ->getConfiguration (\ TYPO3 \ Flow \ Configuration \ ConfigurationManager::CONFIGURATION_TYPE_SETTINGS , 'TYPO3.TYPO3CR .Search ' );
116+ if ($ cause === ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED ) {
117+ $ this ->settings = $ this ->configurationManager ->getConfiguration (ConfigurationManager::CONFIGURATION_TYPE_SETTINGS , 'Neos.ContentRepository .Search ' );
98118 }
99119 }
100120
@@ -107,8 +127,8 @@ public function showMappingCommand()
107127 {
108128 $ nodeTypeMappingCollection = $ this ->nodeTypeMappingBuilder ->buildMappingInformation ($ this ->nodeIndexer ->getIndex ());
109129 foreach ($ nodeTypeMappingCollection as $ mapping ) {
110- /** @var \Flowpack\ElasticSearch\Domain\Model\ Mapping $mapping */
111- $ this ->output (\ Symfony \ Component \ Yaml \ Yaml::dump ($ mapping ->asArray (), 5 , 2 ));
130+ /** @var Mapping $mapping */
131+ $ this ->output (Yaml::dump ($ mapping ->asArray (), 5 , 2 ));
112132 $ this ->outputLine ();
113133 }
114134 $ this ->outputLine ('------------ ' );
@@ -133,6 +153,69 @@ public function showMappingCommand()
133153 }
134154 }
135155
156+ /**
157+ * Index a single node by the given identifier and workspace name
158+ *
159+ * @param string $identifier
160+ * @param string $workspace
161+ * @return void
162+ */
163+ public function indexNodeCommand ($ identifier , $ workspace = null )
164+ {
165+ if ($ workspace === null && $ this ->settings ['indexAllWorkspaces ' ] === false ) {
166+ $ workspace = 'live ' ;
167+ }
168+
169+ $ indexNode = function ($ identifier , Workspace $ workspace , array $ dimensions ) {
170+ $ context = $ this ->createContentContext ($ workspace ->getName (), $ dimensions );
171+ $ node = $ context ->getNodeByIdentifier ($ identifier );
172+ if ($ node === null ) {
173+ $ this ->outputLine ('Node with the given identifier is not found. ' );
174+ $ this ->quit ();
175+ }
176+ $ this ->outputLine ();
177+ $ this ->outputLine ('Index node "%s" (%s) ' , [
178+ $ node ->getLabel (),
179+ $ node ->getIdentifier (),
180+ ]);
181+ $ this ->outputLine (' workspace: %s ' , [
182+ $ workspace ->getName ()
183+ ]);
184+ $ this ->outputLine (' node type: %s ' , [
185+ $ node ->getNodeType ()->getName ()
186+ ]);
187+ $ this ->outputLine (' dimensions: %s ' , [
188+ json_encode ($ dimensions )
189+ ]);
190+ $ this ->nodeIndexer ->indexNode ($ node );
191+ };
192+
193+ $ indexInWorkspace = function ($ identifier , Workspace $ workspace ) use ($ indexNode ) {
194+ $ combinations = $ this ->contentDimensionCombinator ->getAllAllowedCombinations ();
195+ if ($ combinations === []) {
196+ $ indexNode ($ identifier , $ workspace , []);
197+ } else {
198+ foreach ($ combinations as $ combination ) {
199+ $ indexNode ($ identifier , $ workspace , $ combination );
200+ }
201+ }
202+ };
203+
204+ if ($ workspace === null ) {
205+ foreach ($ this ->workspaceRepository ->findAll () as $ workspace ) {
206+ $ indexInWorkspace ($ identifier , $ workspace );
207+ }
208+ } else {
209+ /** @var Workspace $workspaceInstance */
210+ $ workspaceInstance = $ this ->workspaceRepository ->findByIdentifier ($ workspace );
211+ if ($ workspaceInstance === null ) {
212+ $ this ->outputLine ('The given workspace (%s) does not exist. ' , [$ workspace ]);
213+ $ this ->quit (1 );
214+ }
215+ $ indexInWorkspace ($ identifier , $ workspaceInstance );
216+ }
217+ }
218+
136219 /**
137220 * Index all nodes by creating a new index and when everything was completed, switch the index alias.
138221 *
@@ -141,29 +224,22 @@ public function showMappingCommand()
141224 * @param integer $limit Amount of nodes to index at maximum
142225 * @param boolean $update if TRUE, do not throw away the index at the start. Should *only be used for development*.
143226 * @param string $workspace name of the workspace which should be indexed
144- * @param string $postfix Index postfix, index with the same postifix will be deleted if exist
227+ * @param string $postfix Index postfix, index with the same postfix will be deleted if exist
145228 * @return void
146229 */
147230 public function buildCommand ($ limit = null , $ update = false , $ workspace = null , $ postfix = null )
148231 {
232+ if ($ workspace !== null && $ this ->workspaceRepository ->findByIdentifier ($ workspace ) === null ) {
233+ $ this ->logger ->log ('The given workspace ( ' . $ workspace . ') does not exist. ' , LOG_ERR );
234+ $ this ->quit (1 );
235+ }
236+
149237 if ($ update === true ) {
150238 $ this ->logger ->log ('!!! Update Mode (Development) active! ' , LOG_INFO );
151239 } else {
152- $ this ->nodeIndexer ->setIndexNamePostfix ($ postfix ?: time ());
153- if ($ this ->nodeIndexer ->getIndex ()->exists () === true ) {
154- $ this ->logger ->log (sprintf ('Deleted index with the same postfix (%s)! ' , $ postfix ), LOG_WARNING );
155- $ this ->nodeIndexer ->getIndex ()->delete ();
156- }
157- $ this ->nodeIndexer ->getIndex ()->create ();
158- $ this ->logger ->log ('Created index ' . $ this ->nodeIndexer ->getIndexName (), LOG_INFO );
159-
160- $ nodeTypeMappingCollection = $ this ->nodeTypeMappingBuilder ->buildMappingInformation ($ this ->nodeIndexer ->getIndex ());
161- foreach ($ nodeTypeMappingCollection as $ mapping ) {
162- /** @var \Flowpack\ElasticSearch\Domain\Model\Mapping $mapping */
163- $ mapping ->apply ();
164- }
165- $ this ->logger ->log ('Updated Mapping. ' , LOG_INFO );
240+ $ this ->createNewIndex ($ postfix );
166241 }
242+ $ this ->applyMapping ();
167243
168244 $ this ->logger ->log (sprintf ('Indexing %snodes ... ' , ($ limit !== null ? 'the first ' . $ limit . ' ' : '' )), LOG_INFO );
169245
@@ -197,7 +273,7 @@ public function buildCommand($limit = null, $update = false, $workspace = null,
197273 $ this ->outputLine ('<error>Error</error> ' . $ error ->message ());
198274 }
199275 $ this ->outputLine ();
200- $ this ->outputLine ('<error>Check your logs for more informations </error> ' );
276+ $ this ->outputLine ('<error>Check your logs for more information </error> ' );
201277 } else {
202278 $ this ->logger ->log ('Done. (indexed ' . $ count . ' nodes) ' , LOG_INFO );
203279 }
@@ -225,9 +301,66 @@ public function cleanupCommand()
225301 } else {
226302 $ this ->logger ->log ('Nothing to remove. ' );
227303 }
228- } catch (\ Flowpack \ ElasticSearch \ Transfer \ Exception \ ApiException $ exception ) {
304+ } catch (ApiException $ exception ) {
229305 $ response = json_decode ($ exception ->getResponse ());
230- $ this ->logger ->log (sprintf ('Nothing removed. ElasticSearch responded with status %s, saying "%s" ' , $ response ->status , $ response ->error ));
306+ $ this ->logger ->log (sprintf ('Nothing removed. ElasticSearch responded with status %s, saying "%s: %s" ' , $ response ->status , $ response ->error ->type , $ response ->error ->reason ));
307+ }
308+ }
309+
310+ /**
311+ * Create a ContentContext based on the given workspace name
312+ *
313+ * @param string $workspaceName Name of the workspace to set for the context
314+ * @param array $dimensions Optional list of dimensions and their values which should be set
315+ * @return Context
316+ */
317+ protected function createContentContext ($ workspaceName , array $ dimensions = [])
318+ {
319+ $ contextProperties = [
320+ 'workspaceName ' => $ workspaceName ,
321+ 'invisibleContentShown ' => true ,
322+ 'inaccessibleContentShown ' => true
323+ ];
324+
325+ if ($ dimensions !== []) {
326+ $ contextProperties ['dimensions ' ] = $ dimensions ;
327+ $ contextProperties ['targetDimensions ' ] = array_map (function ($ dimensionValues ) {
328+ return array_shift ($ dimensionValues );
329+ }, $ dimensions );
330+ }
331+
332+ return $ this ->contextFactory ->create ($ contextProperties );
333+ }
334+
335+ /**
336+ * Create a new index with the given $postfix.
337+ *
338+ * @param string $postfix
339+ * @return void
340+ */
341+ protected function createNewIndex ($ postfix )
342+ {
343+ $ this ->nodeIndexer ->setIndexNamePostfix ($ postfix ?: time ());
344+ if ($ this ->nodeIndexer ->getIndex ()->exists () === true ) {
345+ $ this ->logger ->log (sprintf ('Deleted index with the same postfix (%s)! ' , $ postfix ), LOG_WARNING );
346+ $ this ->nodeIndexer ->getIndex ()->delete ();
347+ }
348+ $ this ->nodeIndexer ->getIndex ()->create ();
349+ $ this ->logger ->log ('Created index ' . $ this ->nodeIndexer ->getIndexName (), LOG_INFO );
350+ }
351+
352+ /**
353+ * Apply the mapping to the current index.
354+ *
355+ * @return void
356+ */
357+ protected function applyMapping ()
358+ {
359+ $ nodeTypeMappingCollection = $ this ->nodeTypeMappingBuilder ->buildMappingInformation ($ this ->nodeIndexer ->getIndex ());
360+ foreach ($ nodeTypeMappingCollection as $ mapping ) {
361+ /** @var Mapping $mapping */
362+ $ mapping ->apply ();
231363 }
364+ $ this ->logger ->log ('Updated Mapping. ' , LOG_INFO );
232365 }
233366}
0 commit comments