Skip to content

Commit

Permalink
Add geometries to KG
Browse files Browse the repository at this point in the history
  • Loading branch information
Ferdinando Villa committed Nov 16, 2024
1 parent 7236fd9 commit b1a8fbe
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ static ObservationImpl createObservation(Scope scope, Object... resolvables) {
ret.setGeometry(geometry);
ret.setMetadata(metadata);
ret.setObservable(observable);
ret.setUrn(resourceUrn == null ? modelUrn : resourceUrn);
ret.setValue(defaultValue);
ret.setName(name);
return ret;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ public abstract class AbstractKnowledgeGraph implements KnowledgeGraph {
protected static int MAX_CACHED_OBSERVATIONS = 400;

protected ContextScope scope;
protected LoadingCache<Long, RuntimeAsset> assetCache =
CacheBuilder.newBuilder().maximumSize(MAX_CACHED_OBSERVATIONS).build(new CacheLoader<Long,
RuntimeAsset>() {
@Override
public RuntimeAsset load(Long key) throws Exception {
return retrieve(key, RuntimeAsset.class, scope);
}
});
// protected LoadingCache<Long, RuntimeAsset> assetCache =
// CacheBuilder.newBuilder().maximumSize(MAX_CACHED_OBSERVATIONS).build(new CacheLoader<Long,
// RuntimeAsset>() {
// @Override
// public RuntimeAsset load(Long key) throws Exception {
// return retrieve(key, RuntimeAsset.class, scope);
// }
// });

/**
* Return a RuntimeAsset representing the overall dataflow related to the scope, so that it can be used
Expand Down Expand Up @@ -96,12 +96,7 @@ protected abstract void link(RuntimeAsset source, RuntimeAsset destination,

@Override
public <T extends RuntimeAsset> T get(long id, Class<T> resultClass) {
try {
return (T) assetCache.get(id);
} catch (ExecutionException e) {
scope.error(e);
return null;
}
return retrieve(id, resultClass, scope);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.integratedmodelling.klab.api.scope.SessionScope;
import org.integratedmodelling.klab.api.scope.UserScope;
import org.integratedmodelling.klab.api.services.Reasoner;
import org.integratedmodelling.klab.api.services.resolver.Coverage;
import org.integratedmodelling.klab.api.services.runtime.Actuator;
import org.integratedmodelling.klab.api.services.runtime.Dataflow;
import org.integratedmodelling.klab.api.services.runtime.objects.ContextInfo;
Expand Down Expand Up @@ -178,6 +179,9 @@ public void close() throws IOException {
observation.setResolved(true);
}
update(observation, scope);
if (observation.getGeometry() != null) {
storeGeometry(observation.getGeometry(), observation);
}
} else if (asset instanceof Dataflow<?> dataflow) {
// TODO we should keep the actuator and add it with the subordinate
// operation at
Expand Down Expand Up @@ -257,6 +261,7 @@ public Operation operation(Agent agent, Activity parentActivity, Activity.Type a
protected EagerResult query(String query, Map<String, Object> parameters, Scope scope) {
if (isOnline()) {
try {
System.out.printf("\nQUERY " + query + "\n WITH " + parameters);
return driver.executableQuery(query).withParameters(parameters).execute();
} catch (Throwable t) {
if (scope != null) {
Expand Down Expand Up @@ -465,8 +470,8 @@ protected <T> List<T> adapt(EagerResult query, Class<T> cls, Scope scope) {
instance.setId(node.get("id").asLong());

// SHIT, THE GEOMETRY - geometry, metadata etc
var gResult = query("MATCH (o:Observation)-[:HAS_GEOMETRY]->(g:Geometry) WHERE id" +
"(o) = $id RETURN g", Map.of("id", node.get("id").asLong()), scope);
var gResult = query("MATCH (o:Observation)-[:HAS_GEOMETRY]->(g:Geometry) WHERE o.id" +
" = $id RETURN g", Map.of("id", node.get("id").asLong()), scope);

if (gResult == null || !gResult.records().isEmpty()) {
instance.setGeometry(adapt(gResult, Geometry.class, scope).getFirst());
Expand Down Expand Up @@ -547,8 +552,11 @@ public void clear() {

@Override
protected <T extends RuntimeAsset> T retrieve(Object key, Class<T> assetClass, Scope scope) {
var result = query("MATCH (n:{assetLabel} {id: $id}) return n".replace("{assetLabel}",
getLabel(assetClass)), Map.of("id", key), null);
var result =
assetClass == RuntimeAsset.class ?
query("MATCH (n {id: $id}) return n", Map.of("id", key), null) :
query("MATCH (n:{assetLabel} {id: $id}) return n".replace("{assetLabel}",
getLabel(assetClass)), Map.of("id", key), null);
var adapted = adapt(result, assetClass, scope);
return adapted.isEmpty() ? null : adapted.getFirst();
}
Expand All @@ -557,8 +565,6 @@ protected <T extends RuntimeAsset> T retrieve(Object key, Class<T> assetClass, S
@Override
protected long store(RuntimeAsset asset, Scope scope, Object... additionalProperties) {

// TODO store the geometry when the asset is an observation or actuator w/coverage (use cached)!

var type = getLabel(asset);
var props = asParameters(asset, additionalProperties);
var ret = nextKey();
Expand All @@ -569,6 +575,7 @@ protected long store(RuntimeAsset asset, Scope scope, Object... additionalProper
if (result != null && result.records().size() == 1) {
setId(asset, ret);
}

return ret;
}

Expand All @@ -583,11 +590,73 @@ protected long store(Transaction transaction, RuntimeAsset asset, Scope scope,
Queries.CREATE_WITH_PROPERTIES.replace("{type}", type),
Map.of("properties", props), scope);
if (result != null && result.hasNext()) {

setId(asset, ret);
var geometry = switch (asset) {
case Observation observation -> observation.getGeometry();
case Actuator actuator -> actuator.getCoverage();
default -> null;
};

if (geometry != null) {
storeGeometry(geometry, asset);
}
}

return ret;
}

private void storeGeometry(Geometry geometry, RuntimeAsset asset) {

// TODO have a multi-cache ordered by size

// Must be called after update() and this may happen more than once, so we must check to avoid
// multiple relationships.
var exists =
query("MATCH (n:{assetLabel} {id: $assetId})-[:HAS_GEOMETRY]->(g:Geometry) RETURN g".replace("{assetLabel}",
getLabel(asset)), Map.of("assetId", getId(asset)), scope);

if (exists != null && !exists.records().isEmpty()) {
return;
}

if (!(geometry instanceof Scale)) {
// only record fully specified scales, not syntactic specifications
geometry = Scale.create(geometry);
}

double coverage = geometry instanceof Coverage cov ? cov.getCoverage() : 1.0;

// the idea is that looking up the size before the monster string can be faster.
var query = "MATCH (g:Geometry) WHERE g.size = $size AND g.definition = $definition RETURN g";
long id;
var result = query(query, Map.of("size", geometry.size(), "definition", geometry.encode()), scope);
if (result == null || result.records().isEmpty()) {
id = nextKey();
// TODO more geometry data (bounding box, time boundaries etc.)
System.out.println("GEOMETRY NONEXISTENT");
query("CREATE (g:Geometry {size: $size, definition: $definition, id: $id}) RETURN g", Map.of(
"size",
geometry.size(), "definition", geometry.encode(), "id", id), scope);
} else {
id = result.records().getFirst().values().getFirst().get("id").asLong();
System.out.println("GEOMETRY EXISTED");
}

// TODO more properties pertaining to the link (e.g. separate space/time coverages etc)
var properties = Map.of("coverage", coverage);

// link it with the associated coverage
var rel = query(("MATCH (n:{assetLabel}), (g:Geometry) WHERE n.id = $assetId AND g.id = $geometryId" +
" CREATE (n)" +
"-[r:HAS_GEOMETRY]->(g) SET r = $properties RETURN r").replace("{assetLabel}",
getLabel(asset)),
Map.of("assetId", getId(asset), "geometryId", id, "properties", properties), scope);

System.out.printf("POOH");

}

@Override
protected void link(RuntimeAsset source, RuntimeAsset destination,
DigitalTwin.Relationship relationship, Scope scope,
Expand Down Expand Up @@ -617,7 +686,7 @@ protected void link(Transaction transaction, RuntimeAsset source, RuntimeAsset d
var sourceQuery = matchAsset(source, "n", "sourceId");
var targetQuery = matchAsset(destination, "c", "targetId");
var props = asParameters(null, additionalProperties);
var query = ("match (n:{fromLabel}), (c:{toLabel}) WHERE {sourceQuery} AND {targetQuery} CREATE (n)" +
var query = ("MATCH (n:{fromLabel}), (c:{toLabel}) WHERE {sourceQuery} AND {targetQuery} CREATE (n)" +
"-[r:{relationshipLabel}]->(c) SET r = $properties RETURN r")
.replace("{sourceQuery}", sourceQuery)
.replace("{targetQuery}", targetQuery)
Expand Down Expand Up @@ -673,7 +742,10 @@ private Object getId(RuntimeAsset asset) {

private void setId(RuntimeAsset asset, long id) {
switch (asset) {
case ObservationImpl observation -> observation.setId(id);
case ObservationImpl observation -> {
observation.setId(id);
observation.setUrn(scope.getId() + "." + id);
}
case ActuatorImpl actuator -> actuator.setInternalId(id);
case ActivityImpl activity -> activity.setId(id);
case AgentImpl agent -> agent.setId(id);
Expand Down Expand Up @@ -996,16 +1068,15 @@ public void update(RuntimeAsset runtimeAsset, ContextScope scope, Object... para
protected synchronized long nextKey() {
var ret = -1L;
var lastActivity = System.currentTimeMillis();
var result = query("MATCH (n:Statistics) return n.id", Map.of(), scope);
var result = query("MATCH (n:Statistics) return n.nextId", Map.of(), scope);
if (result != null) {

if (result.records().isEmpty()) {
ret = 1;
query("CREATE (n:Statistics {id: 1})", Map.of(), scope);
query("CREATE (n:Statistics {nextId: 1})", Map.of(), scope);
} else {
var id = result.records().getFirst().get(result.keys().getFirst()).asLong();
ret = id + 1;
query("MATCH (n:Statistics) WHERE n.id = $id SET n.id = $nextId, n.lastActivity = " +
query("MATCH (n:Statistics) WHERE n.nextId = $id SET n.nextId = $nextId, n.lastActivity = " +
"$lastActivity", Map.of("id", id, "nextId"
, ret, "lastActivity", lastActivity), scope);
}
Expand Down

0 comments on commit b1a8fbe

Please sign in to comment.