diff --git a/xyz-hub-service/src/main/java/com/here/xyz/hub/Config.java b/xyz-hub-service/src/main/java/com/here/xyz/hub/Config.java index 28fb43f3ae..45b6f601c4 100644 --- a/xyz-hub-service/src/main/java/com/here/xyz/hub/Config.java +++ b/xyz-hub-service/src/main/java/com/here/xyz/hub/Config.java @@ -473,5 +473,5 @@ public List getDefaultStorageIds(String region) { */ public ConnectorMapping DEFAULT_CONNECTOR_MAPPING_STRATEGY = RANDOM; - public boolean USE_WRITE_FEATURES_EVENT = false; + public boolean USE_WRITE_FEATURES_EVENT = true; } diff --git a/xyz-hub-service/src/main/java/com/here/xyz/hub/rest/FeatureApi.java b/xyz-hub-service/src/main/java/com/here/xyz/hub/rest/FeatureApi.java index 0265178f7f..b631e25921 100644 --- a/xyz-hub-service/src/main/java/com/here/xyz/hub/rest/FeatureApi.java +++ b/xyz-hub-service/src/main/java/com/here/xyz/hub/rest/FeatureApi.java @@ -586,9 +586,45 @@ private UpdateStrategy toUpdateStrategy(Space space, IfExists ifExists, IfNotExi ); } + private String writeHook() + { + return // TODO: paths = [name, jsonpath] needs to be filled from index configuration + """ + feature => { + + const paths = [ + ["$alias1", "$.properties.name"], + ["$alias2", "$.properties.location.lat"], + ["$alias3", "$.properties.tags[*]"] +]; + +function extractJsonPaths(feature, paths) { + const result = {}; + + for (const [name, jsonPath] of paths) { + try { + const value = jp.query(feature, jsonPath); + // decide whether to store arrays or single values: + result[name] = value.length === 1 ? value[0] : value; + } catch (err) { + result[name] = null; // or undefined / error message + console.error(`Error evaluating JSONPath for ${name}:`, err.message); + } + } + + return result; +} + + return ["searchable",extractJsonPaths(feature, paths) ]; +} + + """; +} + private Set toModifications(RoutingContext context, Space space, FeatureModificationList featureModificationList, boolean versionConflictDetectionEnabled) { List featureHooks = new ArrayList<>(); + List writeHooks = new ArrayList<>(); List addTags = getAddTags(context); List removeTags = getRemoveTags(context); String idPrefix = getIdPrefix(context); @@ -599,13 +635,20 @@ private Set toModifications(RoutingContext context, Space space, F if (idPrefix != null) featureHooks.add("feature => feature.id = \"" + idPrefix + "\" + feature.id"); + boolean useWriteHook = true; + + if( useWriteHook ) + writeHooks.add(writeHook()); // todo: pass searchable aliases to use within writehook + return featureModificationList.getModifications().stream() .map(modification -> new Modification() .withFeatureData(modification.getFeatureData()) .withUpdateStrategy(toUpdateStrategy(space, modification.getOnFeatureExists(), modification.getOnFeatureNotExists(), modification.getOnMergeConflict(), versionConflictDetectionEnabled)) .withPartialUpdates(modification.getOnFeatureExists() == PATCH) - .withFeatureHooks(featureHooks.isEmpty() ? null : featureHooks)) + .withFeatureHooks(featureHooks.isEmpty() ? null : featureHooks) + .withWriteHooks(writeHooks.isEmpty() ? null : writeHooks) + ) .collect(Collectors.toSet()); } diff --git a/xyz-models/src/main/java/com/here/xyz/events/WriteFeaturesEvent.java b/xyz-models/src/main/java/com/here/xyz/events/WriteFeaturesEvent.java index 936b3e711f..5efd49fbe9 100644 --- a/xyz-models/src/main/java/com/here/xyz/events/WriteFeaturesEvent.java +++ b/xyz-models/src/main/java/com/here/xyz/events/WriteFeaturesEvent.java @@ -59,6 +59,7 @@ public static class Modification { private List featureIds; //To be used only for deletions private boolean partialUpdates; private List featureHooks; //NOTE: The featureHooks will be applied in the given order + private List writeHooks; //NOTE: The Hooks will be applied in the given order public UpdateStrategy getUpdateStrategy() { return updateStrategy; @@ -124,5 +125,20 @@ public Modification withFeatureHooks(List featureHooks) { setFeatureHooks(featureHooks); return this; } + + public List getWriteHooks() { + return writeHooks; + } + + public void setWriteHooks(List writeHooks) { + this.writeHooks = writeHooks; + } + + public Modification withWriteHooks(List writeHooks) { + setWriteHooks(writeHooks); + return this; + } + + } } diff --git a/xyz-util/src/main/java/com/here/xyz/util/db/pg/Script.java b/xyz-util/src/main/java/com/here/xyz/util/db/pg/Script.java index c81cd6d47b..afb1e6e4b6 100644 --- a/xyz-util/src/main/java/com/here/xyz/util/db/pg/Script.java +++ b/xyz-util/src/main/java/com/here/xyz/util/db/pg/Script.java @@ -131,8 +131,7 @@ public void install() { } } catch (SQLException | IOException e) { - logger.warn("Unable to install script {} on DB {}. Falling back to previous version if possible.", getScriptName(), - getDbId(), e); + logger.warn("Unable to install script {} on DB {}. Falling back to previous version if possible.", getScriptName(), getDbId(), e); } } @@ -140,10 +139,47 @@ private String getDbId() { return dataSourceProvider.getDatabaseSettings() == null ? "unknown" : dataSourceProvider.getDatabaseSettings().getId(); } + private SQLQuery addJsLibRegisterFunctions(SQLQuery scriptContent) throws IOException + { + String + libPath = String.format("%s/lib-js",getScriptResourceFolder()), + registerSqlFunc = + """ + CREATE OR REPLACE FUNCTION libjs_${{regFunctionName}}() RETURNS text AS + $body$ + with indata as + ( select $rfc$${{regFunctionCode}}$rfc$ as code ) + select regexp_replace(code, '^var\\s+[^\\(]+','') as code from indata + $body$ + LANGUAGE sql IMMUTABLE PARALLEL SAFE; + """; + + List queries = new ArrayList<>(); + queries.add(scriptContent); + + List