Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Source ID Handler #394

Merged
merged 11 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/NAMESPACES.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ In the tags Naksha stores:

**Note**: The **grid** is automatically set by the Naksha storage engine at part of the normal triggers. The **grid** is based upon the mass center of the geometry (`ST_GeoHash(ST_Centroid(geo),14)`). If the feature does not have a geometry, the `id` is used to create a geo-hash replacement. It can be used for work distribution.

## MOM-Metadata [`@ns:com:here:meta`]
## MOM-Metadata [`@ns:com:here:mom:meta`]

The MOM metadata was traditionally stored in the base-collection of the Data-Hub and extended the data originating from the RMOB. The Data-Hub was the predecessor of XYZ-Hub, which is the predecessor of the Interactive-Map-Service. Naksha will continue to support this namespace, but only through the `lib-naksha-moderation`. The updates to this namespace are now-a-days done by the `Wikvaya` service (aka Map-Creator Middleware), which eventually will be replaced by the `lib-naksha-moderation`.

Expand All @@ -50,7 +50,7 @@ The MOM metadata was traditionally stored in the base-collection of the Data-Hub

The **sourceId** is used differently in different contexts. For example, in Map-Creator, for normal edits, the app and date are stored here, like `COM_1000001_20220713`. For the UTM (User-Task-Management) the task-id is stored in it, for example `MapTask:LqBzOJyAo2pTa12l`. In other contexts it is used to logically group parts of a distributed transaction to later query all collections that have features belonging to the same logical transaction.

## Moderation-Metadata [`@ns:com:here:delta`]
## Moderation-Metadata [`@ns:com:here:mom:delta`]

The moderation metadata was historically managed by the Map-Creator Middleware and were part of the moderation process. This namespace only exists for features being in the moderation process, it does not exist in the base collections (so not in the consistent store). The updates to this namespace are now-a-days done by the `Wikvaya` service (aka Map-Creator Middleware), which eventually will be replaced by the `lib-naksha-moderation`.

Expand Down
2 changes: 1 addition & 1 deletion docs/diagrams/architecture_base.drawio
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@
<mxCell id="P7LG4y36yVNv2cbaxUf2-325" value="+&amp;gt; altitude : Double" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;gradientDirection=east;rounded=0;shadow=0;glass=0;" parent="P7LG4y36yVNv2cbaxUf2-322" vertex="1">
<mxGeometry y="82" width="240" height="28" as="geometry" />
</mxCell>
<mxCell id="P7LG4y36yVNv2cbaxUf2-328" value="The concept of the Naksha base library (&lt;b&gt;lib-base&lt;/b&gt;) is to provide an in-memory data model that is flexible and multi-platform (JVM and JavaScript in the Browser and PogstresQL database via Plv8). The base library defines a set of interfaces and one singleton that need to be provided by the platform. The platform grants access to the concrete representations of the interfaces by providing the central static singleton: &lt;b&gt;Nak&lt;/b&gt;. This is used to access the data in memory.&amp;nbsp;The in memory data does only persist out of the following basic pieces: Maps, Arrays, numbers, booleans, strings and byte-buffers for binary encoding and modification. To improve cooperation with platform code, each of the interfaces should be provided as close to native code as possible. To be able to do this, all interfaces are empty and for each of the interfaces the &lt;b&gt;Nak&lt;/b&gt; comes with&amp;nbsp;static helper methods to manipulate them. Only the multi-platform code is limited to the static &lt;b&gt;Nak&lt;/b&gt; methods, platform bound code can directly interact with the platform specific implementations, for example in Java &lt;b style=&quot;&quot;&gt;JvmPObject&lt;/b&gt; is implemented as &lt;b&gt;&lt;i&gt;LinkedHashMap&lt;/i&gt;&lt;/b&gt; and therefore can be used like this, while in JavaScript it is a plain &lt;b&gt;Object&lt;/b&gt; and can be used like this. In Java, you can simply always cast down a &lt;b&gt;PObject&lt;/b&gt; into a &lt;b&gt;PJvmObject&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Based upon this in-memory data, multi-platform code can be written that implements APIs and business logic to be available on all platforms (even within the database, e.g. as pg_cron job). The big advantage of the separate in-memory data and distinct APIs is that every library can come with its own APIs an memory model, without the need to convert the in-memory data. For example, in the Naksha-Hub space pipeline, one handler may need properties from &lt;b&gt;@ns:com:here:delta&lt;/b&gt; and will use the corresponding data-model API with the feature objects. The next handler may want to access the &lt;b&gt;@ns:com:here:meta&lt;/b&gt; namespace and can do so, without any change to the in-memory data. Eventually, when the data need to be written into the database or send to the client, the corresponding encoders can transform the in-memory data without any further knowledge about the specific details of the data-model." style="html=1;shadow=0;dashed=0;shape=mxgraph.bootstrap.rrect;rSize=5;strokeColor=#999999;strokeWidth=1;fillColor=#FFFFFF;fontColor=#6C767D;whiteSpace=wrap;align=left;verticalAlign=middle;spacingLeft=10;fontSize=14;spacing=10;" parent="1" vertex="1">
<mxCell id="P7LG4y36yVNv2cbaxUf2-328" value="The concept of the Naksha base library (&lt;b&gt;lib-base&lt;/b&gt;) is to provide an in-memory data model that is flexible and multi-platform (JVM and JavaScript in the Browser and PogstresQL database via Plv8). The base library defines a set of interfaces and one singleton that need to be provided by the platform. The platform grants access to the concrete representations of the interfaces by providing the central static singleton: &lt;b&gt;Nak&lt;/b&gt;. This is used to access the data in memory.&amp;nbsp;The in memory data does only persist out of the following basic pieces: Maps, Arrays, numbers, booleans, strings and byte-buffers for binary encoding and modification. To improve cooperation with platform code, each of the interfaces should be provided as close to native code as possible. To be able to do this, all interfaces are empty and for each of the interfaces the &lt;b&gt;Nak&lt;/b&gt; comes with&amp;nbsp;static helper methods to manipulate them. Only the multi-platform code is limited to the static &lt;b&gt;Nak&lt;/b&gt; methods, platform bound code can directly interact with the platform specific implementations, for example in Java &lt;b style=&quot;&quot;&gt;JvmPObject&lt;/b&gt; is implemented as &lt;b&gt;&lt;i&gt;LinkedHashMap&lt;/i&gt;&lt;/b&gt; and therefore can be used like this, while in JavaScript it is a plain &lt;b&gt;Object&lt;/b&gt; and can be used like this. In Java, you can simply always cast down a &lt;b&gt;PObject&lt;/b&gt; into a &lt;b&gt;PJvmObject&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Based upon this in-memory data, multi-platform code can be written that implements APIs and business logic to be available on all platforms (even within the database, e.g. as pg_cron job). The big advantage of the separate in-memory data and distinct APIs is that every library can come with its own APIs an memory model, without the need to convert the in-memory data. For example, in the Naksha-Hub space pipeline, one handler may need properties from &lt;b&gt;@ns:com:here:mom:delta&lt;/b&gt; and will use the corresponding data-model API with the feature objects. The next handler may want to access the &lt;b&gt;@ns:com:here:mom:meta&lt;/b&gt; namespace and can do so, without any change to the in-memory data. Eventually, when the data need to be written into the database or send to the client, the corresponding encoders can transform the in-memory data without any further knowledge about the specific details of the data-model." style="html=1;shadow=0;dashed=0;shape=mxgraph.bootstrap.rrect;rSize=5;strokeColor=#999999;strokeWidth=1;fillColor=#FFFFFF;fontColor=#6C767D;whiteSpace=wrap;align=left;verticalAlign=middle;spacingLeft=10;fontSize=14;spacing=10;" parent="1" vertex="1">
<mxGeometry x="730" y="-190" width="950" height="312" as="geometry" />
</mxCell>
<mxCell id="ji72XSmRw8APm09SylYo-2" value="The subscription notification is sent to virtual subscription collections. The write-results are initially &lt;i&gt;null&lt;/i&gt;, but can be filled using a feature loader, which fetch the states that were part of the transactions. Ones a transaction is processed, the &lt;b&gt;&lt;i&gt;seqNumber&lt;/i&gt;&lt;/b&gt; of the subscription-state should be set to the &lt;b&gt;&lt;i&gt;seqNumber&lt;/i&gt;&lt;/b&gt; of the successfully processed transaction and then &lt;i&gt;save()&lt;/i&gt; of the subscription state should be called." style="html=1;shadow=0;dashed=0;shape=mxgraph.bootstrap.rrect;rSize=5;strokeColor=#999999;strokeWidth=1;fillColor=#FFFFFF;fontColor=#6C767D;whiteSpace=wrap;align=left;verticalAlign=middle;spacingLeft=10;fontSize=14;spacing=10;" parent="1" vertex="1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@
// * Multiple parameter values concatenated with "," (COMMA) delimiter, will result into OR list.
// * <br>
// * So, "p.prop_1=value_1,value_11" will form OR condition as (p.prop_1=value_1 OR p.prop_1=value_11).
// * <br>
// * NOTE that OR condition is supported only for the same one key and multiple values only, not for multiple key value pairs.
// * The reason is to prevent complication when transformation between property search and other types of search like tag search is employed (for example through Source ID Handler).
// * So, "?p.property_name_1=value_1 OR p.@ns:com:here:mom:meta.sourceId=abc" through Source ID Handler would then become an OR between a property search (the first clause unchanged) and a tag search (the second clause transformed), which is not supported.
// * Only AND relation is supported between different types of search (property, tag, spatial,...).
// * </p>
// *
// * @param queryParams API query parameter from where property search params need to be extracted
Expand Down
2 changes: 1 addition & 1 deletion here-naksha-lib-base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ All proxies should end with the postfix `{name}Proxy`, for example `XyzFeaturePr
## Late binding
Proxies are late bound. For this purpose the parameterless primary constructor is invoked (via reflection), when the proxy is dynamically bound to an existing native object. This situation can be handled by the object through overriding of the `bind` method.

For example, in the Naksha-Hub pipelines all features are exposed as `XyzFeatureProxy` instances. However, these are only proxies created at the underlying in-memory data via `Base.proxy(data, XyzFeatureProxy::class)`. So, when a handler needs values from `properties.@ns:com:here:delta`, these are available through the standard data mode implemented in `XyzFeatureProxy` and can be used directly like `feature.getProperties().getDelta()`.
For example, in the Naksha-Hub pipelines all features are exposed as `XyzFeatureProxy` instances. However, these are only proxies created at the underlying in-memory data via `Base.proxy(data, XyzFeatureProxy::class)`. So, when a handler needs values from `properties.@ns:com:here:mom:delta`, these are available through the standard data mode implemented in `XyzFeatureProxy` and can be used directly like `feature.getProperties().getDelta()`.

However, if a handler is part of a custom extension, it may want to access a custom namespace, for example `properties.@ns:com:customData`. The default `XyzFeatureProxy` does not expose it. If the default **XyzFeatureProxy** would be a normal [POJO](https://en.wikipedia.org/wiki/Plain_old_Java_object) the handler would need to first convert the Xyz-Feature into some proprietary object, then it could modify it. However, after doing so, it would have to convert the proprietary object back into the XYZ-Feature for the next handler. This even would require to keep all unknown properties intact and unchanged. The effort to do this is immense and the code will become very slow due to all the data transformations. To solve this problem (and many other), `lib-base` comes with an abstraction between the in memory data storage and the data model. When being based upon `lib-base`, the handler only need to create his own data model, for example:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import naksha.base.AnyObject;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;

Expand All @@ -65,11 +66,12 @@ public static String loadFileOrFail(final @NotNull String fileName) {
return loadFileOrFail(TEST_DATA_FOLDER, fileName);
}

public static <T> T parseJsonFileOrFail(final @NotNull String fileName, final @NotNull Class<T> type) {
public static <T extends AnyObject> T parseJsonFileOrFail(
final @NotNull String fileName, final @NotNull Class<T> type) {
return parseJson(loadFileOrFail(fileName), type);
}

public static <T> T parseJsonFileOrFail(
public static <T extends AnyObject> T parseJsonFileOrFail(
final @NotNull String rootPath, final @NotNull String fileName, final @NotNull Class<T> type) {
return parseJson(loadFileOrFail(rootPath, fileName), type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,29 @@

import static com.here.naksha.lib.core.exceptions.UncheckedException.unchecked;

import com.here.naksha.lib.core.util.json.Json;
import com.here.naksha.lib.core.view.ViewDeserialize;
import com.here.naksha.lib.core.view.ViewSerialize;
import naksha.base.*;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;

/**
* @deprecated use {@link naksha.base.Platform} .fromJson() and .toJson() where possible.
*/
public class JsonUtil {

private JsonUtil() {}

public static <T> T parseJson(final @NotNull String jsonStr, final @NotNull Class<T> type) {
public static <T extends AnyObject> T parseJson(final @NotNull String jsonStr, final @NotNull Class<T> type) {
T obj = null;
try (final Json json = Json.get()) {
obj = json.reader(ViewDeserialize.Storage.class).forType(type).readValue(jsonStr);
try {
obj = JvmProxyUtil.box(Platform.fromJSON(jsonStr, FromJsonOptions.DEFAULT), type);
} catch (Exception ex) {
Assertions.fail("Unable tor parse jsonStr " + jsonStr, ex);
Assertions.fail("Unable to parse jsonStr " + jsonStr, ex);
return null;
}
return obj;
}

public static String toJson(final @NotNull Object obj) {
String jsonStr = null;
try (final Json json = Json.get()) {
jsonStr = json.writer(ViewSerialize.Storage.class).writeValueAsString(obj);
try {
jsonStr = Platform.toJSON(obj, ToJsonOptions.DEFAULT);
} catch (Exception ex) {
throw unchecked(ex);
}
Expand Down
Loading
Loading