private synchronized void initIndex() {
        if (!isInitialising.get()) {
            isInitialising.set(true);
            try {
                LOGGER.info("{} - Initialising content index in {}",
                        RE_INDEX_JOB_NAME,
                        docIndexDir.toAbsolutePath().normalize());
                this.indexableMap = indexables.stream()
                        .collect(Collectors.toMap(ContentIndexable::getType, Function.identity()));

                final Map<String, Analyzer> analyzerMap = Map.of(
                        TYPE, AnalyzerFactory.create(AnalyzerType.KEYWORD, false),
                        UUID, AnalyzerFactory.create(AnalyzerType.KEYWORD, false),
                        NAME, AnalyzerFactory.create(AnalyzerType.KEYWORD, false),
                        EXTENSION, AnalyzerFactory.create(AnalyzerType.KEYWORD, false),
//            DATA, AnalyzerFactory.create(AnalyzerType.KEYWORD, false),
//            DATA_CS, AnalyzerFactory.create(AnalyzerType.KEYWORD, true),
                        DATA_NGRAM, new NGramAnalyzer(),
                        DATA_CS_NGRAM, new NGramCSAnalyzer(),
                        TEXT, AnalyzerFactory.create(AnalyzerType.KEYWORD, true));

                analyzer = new PerFieldAnalyzerWrapper(new KeywordAnalyzer(), analyzerMap);

                Files.createDirectories(docIndexDir);

                final boolean validIndex = isValidIndex(docIndexDir, analyzer);
                if (!validIndex) {
                    FileUtil.deleteContents(docIndexDir);
                }

                changes = new ArrayBlockingQueue<>(10_000);

                // Create lucene directory object.
                directory = new NIOFSDirectory(docIndexDir, Lucene980LockFactory.get());

            } catch (final IOException e) {
                initialisationError = e;
                LOGGER.error(e::getMessage, e);
                throw new UncheckedIOException(e);
            }

            // Make sure all docs are up-to-date
            final Runnable runnable = securityContext.asProcessingUserResult(() ->
                    taskContextFactory.context(
                            RE_INDEX_JOB_NAME + " (Initialisation)",
                            TerminateHandlerFactory.NOOP_FACTORY,
                            taskContext -> {
                                doReindex();
                            }));
            runnable.run();

            // Let other threads know it is all good to use now
            isInitialising.set(false);
            indexInitialisedLatch.countDown();

            // Not the index is good to use start up the thread that will handle EntityEvents
            initQueueProcessorThread();
            LOGGER.info("{} - Initialisation of content index complete", RE_INDEX_JOB_NAME);
        } else {
            LOGGER.debug("Another thread is initialising the index");
        }
    }
Creation of the content index (or re-indexing it) is triggered by a user doing a content
search. This ensures that only UI nodes create the index.
The index is also updated on a doc basis by this class handling {@link EntityEvent}s however
they will be ignored if the index is not initialised.
There is also a managed job that will trigger a full re-index
stroom-index/stroom-index-lucene980/src/main/java/stroom/contentindex/LuceneContentIndex.java