
org.integratedmodelling.engine.modelling.kbox.ModelKbox Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa - integratedmodelling.org - any
* other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite, meant to enable
* modular, collaborative, integrated development of interoperable data and model
* components. For details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the Affero General Public License Version 3 or any later version.
*
* This program is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for a
* particular purpose. See the Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA. The license is also available at:
* https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.modelling.kbox;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.h2gis.utilities.SpatialResultSet;
import org.integratedmodelling.api.engine.IModelingEngine;
import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.knowledge.IKnowledge;
import org.integratedmodelling.api.knowledge.IProperty;
import org.integratedmodelling.api.metadata.IModelMetadata;
import org.integratedmodelling.api.modelling.IModel;
import org.integratedmodelling.api.modelling.INamespace;
import org.integratedmodelling.api.modelling.IObservable;
import org.integratedmodelling.api.modelling.resolution.IModelPrioritizer;
import org.integratedmodelling.api.modelling.resolution.IResolutionScope;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.persistence.IKbox;
import org.integratedmodelling.api.space.ISpatialExtent;
import org.integratedmodelling.api.time.ITemporalExtent;
import org.integratedmodelling.common.ConceptPair;
import org.integratedmodelling.common.beans.requests.ModelQuery;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.owl.Knowledge;
import org.integratedmodelling.common.space.IGeometricShape;
import org.integratedmodelling.common.utils.Escape;
import org.integratedmodelling.common.vocabulary.NS;
import org.integratedmodelling.engine.geospace.Geospace;
import org.integratedmodelling.engine.geospace.literals.ShapeValue;
import org.integratedmodelling.engine.kbox.sql.SQL;
import org.integratedmodelling.engine.kbox.sql.h2.H2Database;
import org.integratedmodelling.engine.kbox.sql.h2.H2Kbox;
import org.integratedmodelling.engine.kbox.sql.h2.H2Serializer;
import org.integratedmodelling.engine.kbox.sql.h2.schema.CompoundSchema;
import org.integratedmodelling.engine.modelling.kbox.ModelData.Observable;
import org.integratedmodelling.engine.modelling.resolver.ResolutionScope;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import com.vividsolutions.jts.geom.Geometry;
/**
* The kbox that holds model data and searches them based on observable and
* context. Also dispatches the same search to the network and returns ranked
* data using the namespace's priorities.
*
* @author ferdinando.villa
*
*/
public class ModelKbox extends H2Kbox {
public static final String DUMMY_NAMESPACE_ID = "DUMMY_SEARCH_NS";
// change this when incompatible changes are made to force kbox reset.
public static final String KBOX_VERSION = "098v6";
static ModelKbox _this;
public static ModelKbox get() {
if (_this == null) {
H2Kbox.set("models_" + KBOX_VERSION, new ModelKbox("models_" + KBOX_VERSION, KLAB.ENGINE.getMonitor()));
_this = (ModelKbox) H2Kbox.get("models_" + KBOX_VERSION);
}
return _this;
}
public List retrieveAll() throws KlabException {
List ret = new ArrayList<>();
if (!database.hasTable("model")) {
return ret;
}
for (long oid : database.queryIds("SELECT oid FROM model;")) {
ret.add(deserialize(oid));
}
return ret;
}
/**
* Find and deserialize all modeldata matching the parameters. Do not rank
* or anything.
*
* @param observable
* @param context
* @throws KlabException
*/
public List queryModelData(IObservable observable, IResolutionScope context) throws KlabException {
List ret = new ArrayList<>();
if (!database.hasTable("model")) {
return ret;
}
String query = "SELECT model.oid FROM model INNER JOIN observable ON model.oid = observable.fid WHERE ";
query += "(" + scopeQuery(context, observable) + ")";
query += " AND (" + observableQuery(observable, context) + ")";
if (context.getScale().getSpace() != null) {
String sq = spaceQuery(context.getScale().getSpace());
if (!sq.isEmpty()) {
query += " AND (" + sq + ")";
}
}
String tquery = timeQuery(context.getScale().getTime());
if (!tquery.isEmpty()) {
query += " AND (" + tquery + ");";
}
// Env.logger.info(query);
final List oids = database.queryIds(query);
for (long l : oids) {
try {
ret.add(deserialize(l));
} catch (KlabException e) {
// ontologies do not reflect content any more: concepts not
// found
((ResolutionScope) context).getMonitor()
.warn("kbox is out of sync with knowledge base: " + e.getMessage());
}
}
KLAB.info(KLAB.ENGINE.getName() + ": model query for "
+ (context.isForInstantiation() ? "instantiation of " : "explanation of ") + observable + " found "
+ (ret.size() == 1 ? ret.get(0).name : (ret.size() + " models")));
return ret;
}
/**
* Like query() but returns ranked metadata, only for the local kbox
* (ranking version of queryModelData()).
*
* @param observable
* @param context
* @return ranked metadata, unsorted.
* @throws KlabException
*/
public List queryMetadata(IObservable observable, IResolutionScope context) throws KlabException {
IModelPrioritizer prioritizer = context.getPrioritizer();
List ret = new ArrayList<>();
/*
* only query locally if we've seen a model before.
*/
if (database.hasTable("model")) {
for (ModelData md : queryModelData(observable, context)) {
md.ranks = prioritizer.getRanks(md);
ret.add(md);
}
}
return ret;
}
/**
* Pass the output of queryModelData to a contextual prioritizer and return
* the ranked list of IModels. If we're a personal engine, also broadcast
* the query to the network and merge results before returning.
*
* @param observable
* @param context
* @return models resulting from query, best first.
* @throws KlabException
*/
public List query(IObservable observable, IResolutionScope context) throws KlabException {
IModelPrioritizer prioritizer = context.getPrioritizer();
ModelQueryResult ret = new ModelQueryResult(prioritizer, ((ResolutionScope) context).getMonitor());
Set local = new HashSet<>();
/*
* only query locally if we've seen a model before.
*/
if (database.hasTable("model")) {
for (ModelData md : queryModelData(observable, context)) {
local.add(md);
ret.addModelData(md);
}
}
/**
* If we're a modeling engine, dispatch the request to all nodes that
* allow it, which we do simply by using the result list as a
* distributed operation.
*/
if (KLAB.ENGINE instanceof IModelingEngine) {
ModelQuery mquery = new ModelQuery();
mquery.setObservable(
KLAB.MFACTORY.adapt(observable, org.integratedmodelling.common.beans.Observable.class));
mquery.setScope(KLAB.MFACTORY.adapt(context, org.integratedmodelling.common.beans.Scope.class));
ret.setQuery(mquery);
KLAB.ENGINE.getNetwork().broadcast(ret, ((ResolutionScope) context).getMonitor());
}
return ret;
}
/*
* Entirely TODO. For initialization we should use time only to select for
* most current info - either closer to the context or to today if time is
* null. For dynamic models we should either not have a context or cover the
* context. Guess this is the job of the prioritizer, and we should simply
* let anything through except when we look for process models.
*/
private String timeQuery(ITemporalExtent time) {
String ret = "";
// TODO Auto-generated method stub
return ret;
}
/*
* select models that intersect the given space or have no space at all.
*/
private String spaceQuery(ISpatialExtent space) {
if (space.getExtent().getShape().isEmpty()) {
return "";
}
return "model.space && '" + ((IGeometricShape) (space.getExtent().getShape())).getStandardizedGeometry()
+ "' OR ST_IsEmpty(model.space)";
}
/*
* TYPE: match exactly for now. OBSERVATION: match exactly; if observable
* has trait type and it's not subjective, we can also match that
* optionally. TRAITS: if observable has traits (classify by) we match that
* optionally ONLY IF NOT SUBJECTIVE. INHERENT: match exactly if in
* observable, otherwise match with closure and optionally.
*/
private String observableQuery(IObservable observable, IResolutionScope context) {
String ret = observable.getType() instanceof IProperty
? "observable.ptype = '" + ((Knowledge) observable.getType()).asText() + "'"
: "observable.type = '" + ((Knowledge) observable.getType()).asText() + "'";
String oret = "observable.otype = '" + observable.getObservationType() + "'";
if (observable.getObservationType().isAbstract()) {
oret += " OR " + joinStringConditions("observable.otype",
observable.getObservationType().getSemanticClosure(), "OR");
}
/*
* TODO add the trait (classify/discretize by) if not subjective,
* optionally. At the moment there are so few subjective classifications
* that it's not really necessary.
*/
if (!oret.isEmpty()) {
ret += " AND (" + oret + ")";
}
String tret = "";
if (!tret.isEmpty()) {
ret += " AND (" + tret + ")";
}
String iret = "";
if (observable.getContextType() != null) {
iret = joinStringConditions("observable.stype", observable.getContextType().getSemanticClosure(), "OR");
} else {
IKnowledge contextType = null;
if (NS.isDirect(observable) && !context.isForInstantiation()) {
contextType = (((ResolutionScope) context).getContextSubject() == null) ? null
: ((ResolutionScope) context).getContextSubject().getObservable().getType();
} else {
contextType = context.getSubject().getObservable().getType();
}
iret = "observable.stype IS NULL OR observable.stype = ''";
if (contextType != null) {
iret += " OR observable.stype = '" + ((Knowledge) contextType).asText() + "'";
// iret += " OR "
// + joinStringConditions("observable.stype",
// contextType.getSemanticClosure(), "OR");
}
}
if (!iret.isEmpty()) {
ret += " AND (" + iret + ")";
}
if (observable.getInherentType() != null) {
iret = joinStringConditions("observable.itype",
observable.getContextType() == null ? null : observable.getContextType().getSemanticClosure(),
"OR");
if (!iret.isEmpty()) {
ret += " AND (" + iret + ")";
}
}
/*
* detail level >= indicated, or -1 which is always the max.
*/
ret += " AND (observable.dtlvl < 0";
if (observable.getDetailLevel() > 0) {
ret += " OR observable.dtlvl >= " + observable.getDetailLevel();
}
ret += ")";
return ret;
}
/*
* select models that are [instantiators if required] AND:] [private and in
* the home namespace if not dummy OR] (non-private and non-scenario) OR (in
* any of the scenarios in the context).
*/
private String scopeQuery(IResolutionScope context, IObservable observable) {
String ret = "";
String namespaceId = context.getResolutionNamespace() == null ? DUMMY_NAMESPACE_ID
: context.getResolutionNamespace().getId();
if (!namespaceId.equals(DUMMY_NAMESPACE_ID)) {
// ret += "(model.isprivate AND model.namespaceid = '" + namespaceId
// + "')";
ret += "(model.namespaceid = '" + namespaceId + "')";
}
ret += (ret.isEmpty() ? "" : " OR ") + "((NOT model.isprivate) AND (NOT model.inscenario))";
if (context.getScenarios() != null && context.getScenarios().size() > 0) {
ret += " OR (" + joinStringConditions("model.namespaceid", context.getScenarios(), "OR") + ")";
}
if (NS.isCountable(observable)) {
if (context.isForInstantiation()
|| ((org.integratedmodelling.common.vocabulary.Observable) observable).isInstantiator()) {
ret = "(" + ret + ") AND model.isreification";
} else {
ret = "(" + ret + ") AND (NOT model.isreification)";
}
}
return ret;
}
private String joinStringConditions(String field, Collection> stringValues, String operator) {
String ret = "";
if (stringValues != null) {
for (Object o : stringValues) {
if (o instanceof IKnowledge) {
o = ((Knowledge) o).asText();
}
ret += (ret.isEmpty() ? "" : (" " + operator + " ")) + field + " = '" + o + "'";
}
}
return ret;
}
// /**
// * This one is for the query service only.
// * @param criteria
// * @param scenarios
// * @param traits
// * @param contextType
// * @param observationType
// * @param scale
// * @param types
// *
// * @return
// * @deprecated use JSON beans; move to services
// * @throws KlabException
// */
// @Deprecated
// public List query(IKnowledge subjectType, Set groups,
// List types, IScale
// scale, boolean isInstantiator, IConcept observationType, IConcept
// contextType,
// IConcept inherentType,
// int detailLevel, List traits, String[] scenarios, IMetadata
// criteria)
// throws KlabException {
//
// List ret = new ArrayList<>();
//
// if (!database.hasTable("model")) {
// return ret;
// }
//
// ResolutionScope context = new ResolutionScope(subjectType, scale, types,
// isInstantiator,
// observationType, contextType, inherentType, detailLevel, traits,
// scenarios,
// criteria);
// IModelPrioritizer prioritizer = context.getPrioritizer();
//
// for (ModelData md : this.queryModelData(context.getObservable(),
// context)) {
//
// if (md == null)
// continue;
//
// /**
// * FIXME! If groups == null or empty, NOTHING comes out.
// */
// if (groups != null) {
// /*
// * skip anything not authorized for the groups
// */
// //// if (!KLAB.NETWORK.getResourceCatalog().isAuthorized(md.projectId,
// groups)) {
// // KLAB.info("skipping " + md.name + ": unauthorized");
// // continue;
// // }
// }
//
// /*
// * TODO if there is a namespace whitelist/blacklist, filter
// */
// md.ranks = prioritizer.computeCriteria(md, context);
// ret.add(md);
// }
//
// return ret;
//
// }
/**
* @param name
* @return true if model with given id exists in database
* @throws KlabException
*/
public boolean hasModel(String name) throws KlabException {
if (!database.hasTable("model")) {
return false;
}
return database.queryIds("SELECT oid FROM model WHERE name = '" + name + "';").size() > 0;
}
@Override
public long store(Object o) throws KlabException {
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy