Anlegen von Indices
Wenn in eigenen Bundles OpenSearch Indices zur Persistierung von Daten (keine Einstellungen) verwendet werden sollen, dann empfiehlt es sich diese über den BPC Core anlegen zu lassen.
Dies bietet folgende Vorteile
-
es wird einiges an Boilerplate Code vermieden
-
einfache Festlegung der Index Settings und Mappings über eine JSON Datei
-
wenn der Index nicht vorhanden ist, dann wird er automatisch angelegt
-
die Indices werden mit einem Alias nach BPC Vorgaben erstellt
-
kann von der BPC Reindex Funktionalität aus aufgerufen werden
Vorgehen in Kürze
-
im
resources
Verzeichnis des eigenen Backend Bundles einemanaged_indices.json
anlegen -
dessen Inhalt dem vom BPC Core zur Verfügung gestellten
OpenSearchService
bekannt geben
Aufbau der JSON Datei
Im resources
Verzeichnis des eigenen Backend Bundles eine managed_indices.json
anlegen.
Diese kann für mehrere anzulegenden Indices verwendet werden und muss folgenden Aufbau haben.
{
"managedIndices": [
{
"name": "irgend-ein-index-name-1",
"create_on_start": true|false,
"hidden": true|false,
"settings": { ... },
"mappings": { ... }
},
{
"name": "irgend-ein-index-name-2",
"create_on_start": true|false,
"hidden": true|false,
"settings": { ... },
"mappings": { ... }
},
...
}
-
name
- der Name des Index bzw. dies wird dann der Alias des Index. -
create_on_start
- Flag ob der Index gleich beim Start des Bundles (Registrierung) angelegt werden soll. Wenn nicht gesetzt, dann ist es wie wenn der Wert auftrue
gesetzt wurde. -
hidden
- Flag ob der Index im Deployment-Dialog und bei Combobox-Auswahlmöglichkeiten angezeigt werden soll. Wenn nicht gesetzt, dann ist es wie wenn der Wert auffalse
gesetzt wurde. -
settings
- die Settings eines OpenSearch Index. Diese nur setzen, wenn man weiß, was man macht. Sind sie nicht gesetzt, dann werden beim Anlegen die festgelegten Werte aus der Core-Einstellung verwendet. "Core_IndexCreationSettings" verwendet. -
mappings
- die Mappings eines OpenSearch Index
Registrierung
Im folgenden ExampleBaseModule.java
Beispiel wird der OpenSearchService
vom BPC Core geholt und in der Methode createIndices(OpenSearchService es)
wird dann das managed_indices.json
geladen und über den OpenSearchService
registriert.
Bei der Gelegenheit auch mal die vom OpenSearchService
zur Verfügung gestellten Methoden anschauen.
package com.company.example;
import de.virtimo.bpc.api.ModuleManager;
import de.virtimo.bpc.api.exception.OpenSearchRelatedException;
import de.virtimo.bpc.api.service.OpenSearchService;
import de.virtimo.bpc.module.AbstractInstantiableModule;
import de.virtimo.bpc.module.JsonDefaultsUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public abstract class ExampleBaseModule extends AbstractInstantiableModule {
private static final Logger LOG = Logger.getLogger(ExampleBaseModule.class.getName());
private ServiceTracker<OpenSearchService, OpenSearchService> openSearchServiceTracker;
private OpenSearchService openSearchService;
public ExampleBaseModule(ModuleManager moduleManager) {
super(moduleManager);
}
@Override
public void setModuleBundle(Bundle moduleBundle) {
super.setModuleBundle(moduleBundle);
if (openSearchServiceTracker == null) {
openSearchServiceTracker = new OpenSearchServiceTracker(moduleBundle.getBundleContext());
openSearchServiceTracker.open();
}
}
@Override
public void destroy() {
super.destroy();
if (openSearchServiceTracker != null) {
openSearchServiceTracker.close();
openSearchServiceTracker = null;
}
}
public synchronized OpenSearchService getOpenSearchService() {
LOG.info("getOpenSearchService");
if (openSearchService == null) {
throw new IllegalStateException("OpenSearchService is not available at the moment.");
}
return openSearchService;
}
private synchronized void createIndices(OpenSearchService oss) throws OpenSearchRelatedException {
LOG.info("createIndices oss=" + oss);
Map<String, Object> managedIndicesConfigMap = JsonDefaultsUtil.loadJsonFileAsMap(
getModuleBundle(),
"managed_indices.json"
);
oss.prepareManagedIndices(managedIndicesConfigMap);
}
private class OpenSearchServiceTracker extends ServiceTracker<OpenSearchService, OpenSearchService> {
public OpenSearchServiceTracker(BundleContext context) {
super(context, OpenSearchService.class, null);
}
@Override
public OpenSearchService addingService(ServiceReference<OpenSearchService> reference) {
openSearchService = super.addingService(reference);
try {
createIndices(openSearchService);
} catch (OpenSearchRelatedException ex) {
LOG.log(Level.SEVERE, "Failed to create the managed indices.", ex);
}
return openSearchService;
}
@Override
public void removedService(ServiceReference<OpenSearchService> reference, OpenSearchService service) {
openSearchService = null;
super.removedService(reference, service);
}
}
}
Schreiben von Dokumenten
Das BPC geht davon aus, dass Indices folgenden Aufbau beim Namen haben <aliasname>_<timestamp>
.
Und daß ein Alias für diesen Index gesetzt ist.
Beim lesen/schreiben/löschen der Dokumente wird immer mit dem Alias und nie mit dem physischen Indexnamen gearbeitet.
Beim Schreiben von Dokumenten legt OpenSearch automatisch einen Index an (wenn nicht vorhanden) und schätzt die Feldtypen. Das ist problematisch, wenn zum Beispiel zur Laufzeit ein Index gelöscht wird. Beim nächsten Schreiben eines Dokumentes würde dann der Index falsch angelegt werden.
Möglichkeit 1
Um das zu umgehen, die Methoden index
, delete
, search
, searchByQuery
, etc. vom OpenSearchService
verwenden anstatt der vom RestHighLevelClient
.
Die Methoden haben die gleiche Signatur.
Allerdings werden die tückischen RuntimeExceptions (unchecked exception) von OpenSearch abgefangen und durch OpenSearchRelatedException
(checked exception) ersetzt.
Beim index
/bulk
-Aufruf wird dann, wenn nicht vorhanden, der Index anhand der Daten aus der managed_indices.json
angelegt.
Beispiel RestHighLevelClient
(managed_indices.json wird nicht verwendet)
IndexRequest myIndexRequest = ...;
RestHighLevelClient restHighLevelClient = openSearchService.getClient();
restHighLevelClient.index(myIndexRequest, RequestOptions.DEFAULT);
Beispiel OpenSearchService
(managed_indices.json wird verwendet)
IndexRequest myIndexRequest = ...;
openSearchService.index(myIndexRequest, RequestOptions.DEFAULT);
Möglichkeit 2
Eine Hilfsmethode einführen, die vor jeder Schreib-Aktion ausgeführt wird.
Folgend eine Beispielmethode für einen Index mit dem Alias 'books'.
Der OpenSearchService
hält den Status von Indices vor und kann dann bei nicht Existenz den Index per managed_indices.json
anlegen.
private static final String BOOKS_INDEX_ALIAS_NAME = "books";
private void createBooksIndexIfMissing() throws SystemException {
try {
BpcIndexState booksIndexState = oss.getIndexState(BOOKS_INDEX_ALIAS_NAME);
booksIndexState.prepareUsing(new BpcIndexCreateCallable() {
@Override
public String createIndex(OpenSearchService oss) throws OpenSearchRelatedException, ServiceNotFoundException, ModuleNotFoundException {
return oss.getManagedIndicesHandler().createManagedIndex(BOOKS_INDEX_ALIAS_NAME);
}
});
} catch (Exception ex) {
throw new SystemException(CoreErrorCode.UNEXPECTED, "Failed to prepare the books index.", ex);
}
}