
prerna.engine.impl.owl.WriteOWLEngine Maven / Gradle / Ivy
The newest version!
package prerna.engine.impl.owl;
import java.io.Closeable;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;
import org.apache.jena.vocabulary.OWL;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import prerna.engine.api.IDatabaseEngine;
import prerna.engine.api.IHeadersDataRow;
import prerna.engine.api.IRawSelectWrapper;
import prerna.engine.impl.rdf.RDFFileSesameEngine;
import prerna.masterdatabase.utility.MetadataUtility;
import prerna.query.querystruct.AbstractQueryStruct;
import prerna.query.querystruct.SelectQueryStruct;
import prerna.query.querystruct.selectors.QueryColumnSelector;
import prerna.query.querystruct.selectors.QueryFunctionHelper;
import prerna.query.querystruct.selectors.QueryFunctionSelector;
import prerna.rdf.engine.wrappers.WrapperManager;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.PixelOperationType;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.util.Constants;
import prerna.util.UploadUtilities;
import prerna.util.Utility;
public class WriteOWLEngine extends AbstractOWLEngine implements Closeable {
private static final Logger classLogger = LogManager.getLogger(WriteOWLEngine.class);
// private final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
private final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss");
// hashtable of concepts
protected Hashtable conceptHash = new Hashtable();
// hashtable of relationships
protected Hashtable relationHash = new Hashtable();
// hashtable of properties
protected Hashtable propHash = new Hashtable();
// set of conceptual names
protected Set pixelNames = new HashSet();
private final Semaphore writeSemaphore;
private IDatabaseEngine.DATABASE_TYPE dbType = IDatabaseEngine.DATABASE_TYPE.RDBMS;
public WriteOWLEngine(Semaphore writeSemaphore,
RDFFileSesameEngine baseDataEngine,
IDatabaseEngine.DATABASE_TYPE dbType,
String engineId,
String engineName) {
super(baseDataEngine, engineId, engineName);
this.dbType = dbType;
if(writeSemaphore == null) {
throw new NullPointerException("Cannot have a null semaphore");
}
loadDatabaseValues();
this.writeSemaphore = writeSemaphore;
}
@Override
public void close() throws IOException {
this.writeSemaphore.release();
}
protected void loadDatabaseValues() {
Hashtable conceptHash = new Hashtable<>();
Hashtable propHash = new Hashtable<>();
Hashtable relationHash = new Hashtable<>();
boolean isRdbms = (this.dbType == IDatabaseEngine.DATABASE_TYPE.RDBMS
|| this.dbType == IDatabaseEngine.DATABASE_TYPE.IMPALA);
List concepts = this.getPhysicalConcepts();
for (String cUri : concepts) {
String tableName = Utility.getInstanceName(cUri);
// add to concept hash
conceptHash.put(tableName, cUri);
// add all the props as well
List props = this.getPropertyUris4PhysicalUri(cUri);
for (String p : props) {
String propName = null;
if (isRdbms) {
propName = Utility.getClassName(p);
} else {
propName = Utility.getInstanceName(p);
}
propHash.put(tableName + "%" + propName, p);
}
}
List rels = this.getPhysicalRelationships();
for (String[] r : rels) {
String startT = null;
String startC = null;
String endT = null;
String endC = null;
String pred = null;
startT = Utility.getInstanceName(r[0]);
endT = Utility.getInstanceName(r[1]);
pred = Utility.getInstanceName(r[2]);
if (isRdbms) {
startC = Utility.getClassName(r[0]);
endC = Utility.getClassName(r[1]);
}
relationHash.put(startT + startC + endT + endC + pred, r[2]);
}
setConceptHash(conceptHash);
setPropHash(propHash);
setRelationHash(relationHash);
}
/**
* @throws Exception
*
*/
public void createEmptyOWLFile() throws Exception {
this.baseDataEngine.close();
this.baseDataEngine.deleteFile();
UploadUtilities.generateEmptyRDFXMLFile(this.baseDataEngine.getFilePath());
this.baseDataEngine.reloadFile();
// clear out the values in the maps
this.loadDatabaseValues();
}
/**
*
* @throws Exception
*/
public void reloadOWLFile() throws Exception {
this.baseDataEngine.reloadFile();
}
/*
* Adding into the OWL
*/
/////////////////// ADDING CONCEPTS INTO THE OWL //////////////////
/**
* Add a concept to the OWL If RDF : a concept has a data type (String) If RDBMS
* : this will represent a table and not have a datatype
*
* @param tableName
* @param dataType
* @param conceptual
* @return
*/
public String addConcept(String tableName, String dataType, String conceptual) {
// since RDF uses this multiple times, don't create it each time and just store
// it in a hash to send back
if (!conceptHash.containsKey(tableName)) {
// here is the logic to create the physical uri for the concept
// the base URI for the concept will be the baseNodeURI
String subject = BASE_NODE_URI + "/" + tableName;
// now lets start to add the triples
// lets add the triples pertaining to those numbered above
// 1) adding the physical URI concept as a subClassOf the baseNodeURI
this.addToBaseEngine(subject, RDFS.SUBCLASSOF.stringValue(), BASE_NODE_URI);
// 2) now lets add the dataType of the concept
// this will only apply if it is RDF
if (dataType != null) {
String typeObject = "TYPE:" + dataType;
this.addToBaseEngine(subject, RDFS.CLASS.stringValue(), typeObject);
}
if (MetadataUtility.ignoreConceptData(this.dbType)) {
// add an ignore data tag so we can easily query
this.addToBaseEngine(subject, RDFS.DOMAIN.toString(), "noData", false);
}
// 3) now lets add the physical URI to the pixel name URI
String pixelName = Utility.cleanVariableString(tableName);
pixelNames.add(pixelName);
String pixelUri = BASE_NODE_URI + "/" + pixelName;
this.addToBaseEngine(subject, PIXEL_RELATION_URI, pixelUri);
// 4) let us add the original table name as the conceptual name
if (conceptual == null) {
conceptual = tableName;
}
this.addToBaseEngine(subject, CONCEPTUAL_RELATION_URI, conceptual, false);
// store it in the hash for future use
// NOTE : The hash contains the physical URI
conceptHash.put(tableName, subject);
}
return conceptHash.get(tableName);
}
public String addConcept(String tableName, String dataType) {
return addConcept(tableName, dataType, null);
}
public String addConcept(String concept) {
return addConcept(concept, "STRING", null);
}
////////////////////////////////// END ADDING CONCEPTS INTO THE OWL //////////////////////////////////
////////////////////////////////// ADDING RELATIONSHIP INTO THE OWL //////////////////////////////////
/**
* Add a relationship between two concepts In RDBMS : the predicate must be
* fromTable.fromColumn.toTable.toColumn
*
* @param fromTable
* @param toTable
* @param predicate
* @return
*/
public String addRelation(String fromTable, String toTable, String predicate) {
// since RDF uses this multiple times, don't create it each time and just store
// it in a hash to send back
if (!relationHash.containsKey(fromTable + toTable + predicate)) {
// need to make sure both the fromConcept and the toConcept are already defined
// as concepts
// TODO: this works for RDBMS even though it only takes in the concept names
// because we usually perform
// the addConcept call before... this is really just intended to retrieve the
String fromConceptURI = addConcept(fromTable, null, null);
String toConceptURI = addConcept(toTable, null, null);
// create the base relationship uri
String baseRelationURI = SEMOSS_URI_PREFIX + DEFAULT_RELATION_CLASS;
String predicateSubject = baseRelationURI + "/" + predicate;
// now lets start to add the triples
// lets add the triples pertaining to those numbered above
// 1) now add the physical relationship URI
this.addToBaseEngine(predicateSubject, RDFS.SUBPROPERTYOF.stringValue(), baseRelationURI);
// 2) now add the relationship between the two nodes
this.addToBaseEngine(fromConceptURI, predicateSubject, toConceptURI);
// lastly, store it in the hash for future use
relationHash.put(fromTable + toTable + predicate, predicateSubject);
}
return relationHash.get(fromTable + toTable + predicate);
}
////////////////////////////////// END ADDING RELATIONSHIP INTO THE OWL //////////////////////////////////
////////////////////////////////// ADDING PROPERTIES TO CONCEPTS IN THE OWL //////////////////////////////////
/**
* Add a property to a given concept
*
* @param tableName
* @param propertyCol
* @param dataType
* @param adtlDataType
* @param conceptual
* @return
*/
public String addProp(String tableName, String propertyCol, String dataType, String adtlDataType, String conceptual) {
if (!propHash.containsKey(tableName + "%" + propertyCol)) {
String conceptURI = addConcept(tableName, null, null);
// create the property URI
String property = null;
if (this.dbType == IDatabaseEngine.DATABASE_TYPE.SESAME) {
// THIS IS BECAUSE OF LEGACY QUERIES!!!
property = BASE_PROPERTY_URI + "/" + propertyCol;
} else {
property = BASE_PROPERTY_URI + "/" + propertyCol + "/" + tableName;
}
// now lets start to add the triples
// lets add the triples pertaining to those numbered above
// 1) adding the property as type of base property URI
this.addToBaseEngine(property, RDF.TYPE.stringValue(), BASE_PROPERTY_URI);
// 2) adding the property to the concept
this.addToBaseEngine(conceptURI, OWL.DatatypeProperty.toString(), property);
// 3) adding the property data type
String typeObject = "TYPE:" + dataType;
this.addToBaseEngine(property, RDFS.CLASS.stringValue(), typeObject);
// 4) adding the property additional data type, if available
if (adtlDataType != null && !adtlDataType.isEmpty()) {
String adtlTypeObject = "ADTLTYPE:" + adtlDataType;
this.addToBaseEngine(property, ADDITIONAL_DATATYPE_RELATION_URI, adtlTypeObject, false);
}
// 5) now lets add the physical URI to the pixel name URI
String pixelName = Utility.cleanVariableString(propertyCol);
String pixelFullName = pixelName + "/" + Utility.cleanVariableString(tableName);
String pixelUri = BASE_PROPERTY_URI + "/" + pixelFullName;
this.addToBaseEngine(property, PIXEL_RELATION_URI, pixelUri);
// 5) let us add the original table name as the conceptual name
if (conceptual == null) {
conceptual = propertyCol;
}
this.addToBaseEngine(property, CONCEPTUAL_RELATION_URI, conceptual, false);
// lastly, store it in the hash for future use
// NOTE : The hash contains the physical URI
propHash.put(tableName + "%" + propertyCol, property);
}
return propHash.get(tableName + "%" + propertyCol);
}
/**
* This method will add a property onto a concept in the OWL file There are some
* differences based on how the information is used based on if it is a RDF
* engine or a RDBMS engine
*
* @param tableName For RDF: This is the name of the concept For RDBMS: This
* is the name of the table where the concept exists. If the
* concept doesn't exist, it is assumed the column name of
* the concept is the same as the table name
* @param propertyCol This will be the name of the property
* @param dataType The dataType for the property
* @param adtlDataType Additional data type for the property
* @return Returns the physical URI for the node
*/
public String addProp(String tableName, String propertyCol, String dataType, String adtlDataType) {
return addProp(tableName, propertyCol, dataType, adtlDataType, null);
}
public String addProp(String tableName, String propertyCol, String dataType) {
return addProp(tableName, propertyCol, dataType, null, null);
}
/**
* This method will calculate the unique values in each column/property and add
* it to the owl file.
*
* @param queryEngine
*/
public void addUniqueCounts(IDatabaseEngine queryEngine) {
String uniqueCountProp = SEMOSS_URI_PREFIX + DEFAULT_PROP_CLASS + "/UNIQUE";
List pixelConcepts = queryEngine.getPixelConcepts();
for (String pixelConcept : pixelConcepts) {
List pSelectors = queryEngine.getPixelSelectors(pixelConcept);
for (String selectorPixel : pSelectors) {
SelectQueryStruct qs = new SelectQueryStruct();
QueryFunctionSelector newSelector = new QueryFunctionSelector();
newSelector.setFunction(QueryFunctionHelper.UNIQUE_COUNT);
newSelector.setDistinct(true);
QueryColumnSelector innerSelector = new QueryColumnSelector(selectorPixel);
newSelector.addInnerSelector(innerSelector);
qs.addSelector(newSelector);
qs.setQsType(AbstractQueryStruct.QUERY_STRUCT_TYPE.ENGINE);
IRawSelectWrapper it = null;
try {
it = WrapperManager.getInstance().getRawWrapper(queryEngine, qs);
if (!it.hasNext()) {
continue;
}
long uniqueRows = ((Number) it.next().getValues()[0]).longValue();
String propertyPhysicalUri = queryEngine.getPhysicalUriFromPixelSelector(selectorPixel);
this.addToBaseEngine(propertyPhysicalUri, uniqueCountProp, uniqueRows, false);
} catch (Exception e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
if(it != null) {
try {
it.close();
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
}
}
this.commit();
try {
this.export(false);
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
////////////////////////////////// END ADDING PROPERTIES TO CONCEPTS INTO THE OWL //////////////////////////////////
public void addLegacyPrimKey(String tableName, String columnName) {
String physicalUri = conceptHash.get(tableName);
if (physicalUri == null) {
physicalUri = addConcept(tableName, null, null);
}
this.addToBaseEngine(physicalUri, LEGACY_PRIM_KEY_URI, columnName, false);
}
////////////////////////////////// ADDITIONAL METHODS TO INSERT INTO THE OWL //////////////////////////////////
/**
* Have one class a subclass of another class
* This code is really intended for RDF databases...
* not sure what use it will have to utilize this within an RDBMS
*
* @param childType The child concept node
* @param parentType The parent concept node
*/
public void addSubclass(String childType, String parentType) {
String childURI = addConcept(childType);
String parentURI = addConcept(parentType);
this.addToBaseEngine(childURI, RDFS.SUBCLASSOF.stringValue(), parentURI);
}
////////////////////////////////// END ADDITIONAL METHODS TO INSERT INTO THE OWL //////////////////////////////////
/*
* REMOVING FROM THE OWL
*/
////////////////////////////////// REMOVING CONCEPTS FROM THE OWL //////////////////////////////////
/**
* Remove a concept from the OWL If RDF : a concept has a data type (String) If
* RDBMS : this will represent a table and not have a datatype
*
* @param appId id of app
* @param tableName name of concept/table
* @param dataType data type of column values
* @param conceptual
* @return
*/
public NounMetadata removeConcept(String tableName) {
long start = System.currentTimeMillis();
// create the physical uri for the concept
// the base URI for the concept will be the baseNodeURI
String conceptPhysical = this.getPhysicalUriFromPixelSelector(tableName);
// remove relationships to node
List fkRelationships = getPhysicalRelationships(this.baseDataEngine);
classLogger.info("Removing relationships for concept='"+tableName+"'");
for (String[] relations: fkRelationships) {
String instanceName = Utility.getInstanceName(relations[2]);
String[] tablesAndPrimaryKeys = instanceName.split("\\.");
for (int i=0; i < tablesAndPrimaryKeys.length; i+=2) {
String key = tablesAndPrimaryKeys[i];
if (tableName.equalsIgnoreCase(key)) {
this.baseDataEngine.removeStatement(new Object[] { relations[0], relations[2], relations[1], true });
this.baseDataEngine.removeStatement(new Object[] { relations[2], RDFS.SUBPROPERTYOF.toString(), "http://semoss.org/ontologies/Relation", true });
}
}
}
List properties = this.getPropertyUris4PhysicalUri(conceptPhysical);
for(String prop : properties) {
// pixel URI is always column/table
String columnName = Utility.getClassName(prop);
removeProp(tableName, columnName);
}
boolean hasTriple = false;
classLogger.info("Removing downstream triples for concept='"+tableName+"'");
// now repeat for the node itself
// remove everything downstream of the node
{
String query = "select ?s ?p ?o where { bind(<" + conceptPhysical + "> as ?s) {?s ?p ?o} }";
IRawSelectWrapper it = null;
try {
it = WrapperManager.getInstance().getRawWrapper(this.baseDataEngine, query);
while (it.hasNext()) {
hasTriple = true;
IHeadersDataRow headerRows = it.next();
executeRemoveQuery(headerRows, this.baseDataEngine);
}
} catch (Exception e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
if(it != null) {
try {
it.close();
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
}
classLogger.info("Removing upstream triples for concept='"+tableName+"'");
// repeat for upstream of the node
{
String query = "select ?s ?p ?o where { bind(<" + conceptPhysical + "> as ?o) {?s ?p ?o} }";
IRawSelectWrapper it = null;
try {
it = WrapperManager.getInstance().getRawWrapper(this.baseDataEngine, query);
while (it.hasNext()) {
hasTriple = true;
IHeadersDataRow headerRows = it.next();
executeRemoveQuery(headerRows, this.baseDataEngine);
}
} catch (Exception e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
if(it != null) {
try {
it.close();
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
}
if (!hasTriple) {
throw new IllegalArgumentException("Cannot find concept in existing metadata to remove");
}
// remove from hash
conceptHash.remove(tableName);
long end = System.currentTimeMillis();
classLogger.info("Time for property concept = "+(end-start)+"ms");
NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN);
noun.addAdditionalReturn(new NounMetadata("Successfully removed concept and all its dependencies",
PixelDataType.CONST_STRING, PixelOperationType.SUCCESS));
return noun;
}
////////////////////////////////// END REMOVING CONCEPTS FROM THE OWL //////////////////////////////////
////////////////////////////////// REMOVING RELATIONSHIPS FROM THE OWL //////////////////////////////////
/**
* Remove an added predicate joining two tables together
* @param fromTable
* @param toTable
* @param predicate
*/
public void removeRelation(String fromTable, String toTable, String predicate) {
String fromConceptURI = addConcept(fromTable, null, null);
String toConceptURI = addConcept(toTable, null, null);
// create the base relationship uri
String baseRelationURI = SEMOSS_URI_PREFIX + DEFAULT_RELATION_CLASS;
String predicateSubject = baseRelationURI + "/" + predicate;
// now lets start to add the triples
// lets add the triples pertaining to those numbered above
// 1) now add the physical relationship URI
this.removeFromBaseEngine(predicateSubject, RDFS.SUBPROPERTYOF.stringValue(), baseRelationURI);
// 2) now add the relationship between the two nodes
this.removeFromBaseEngine(fromConceptURI, predicateSubject, toConceptURI);
// lastly, store it in the hash for future use
relationHash.remove(fromTable + toTable + predicate);
}
////////////////////////////////// END REMOVING RELATIONSHIPS FROM THE OWL //////////////////////////////////
////////////////////////////////// REMOVING PROPERTIES FROM THE OWL //////////////////////////////////
/**
* This method will remove a property from a concept in the OWL file There are some
* differences based on how the information is used based on if it is a RDF
* engine or a RDBMS engine
*
* @param tableName
* @param propertyCol
* @return
* @return
*/
public NounMetadata removeProp(String tableName, String propertyCol) {
long start = System.currentTimeMillis();
// create the property URI
String property = null;
if (this.dbType == IDatabaseEngine.DATABASE_TYPE.SESAME) {
// THIS IS BECAUSE OF LEGACY QUERIES!!!
property = BASE_PROPERTY_URI + "/" + propertyCol;
} else {
property = BASE_PROPERTY_URI + "/" + propertyCol + "/" + tableName;
}
{
classLogger.info("Removing downstream of property ='"+propertyCol+"/"+tableName+"'");
// remove everything downstream of the property
String downstreamQuery = "select ?s ?p ?o where { bind(<" + property + "> as ?s) " + "{?s ?p ?o} }";
IRawSelectWrapper it = null;
try {
it = WrapperManager.getInstance().getRawWrapper(this.baseDataEngine, downstreamQuery);
while (it.hasNext()) {
IHeadersDataRow headerRows = it.next();
executeRemoveQuery(headerRows, this.baseDataEngine);
}
} catch (Exception e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
if(it != null) {
try {
it.close();
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
}
{
classLogger.info("Removing upstream of property ='"+propertyCol+"/"+tableName+"'");
// repeat for upstream of the property
String upstreamQuery = "select ?s ?p ?o where { bind(<" + property + "> as ?o) {?s ?p ?o} }";
IRawSelectWrapper it = null;
try {
it = WrapperManager.getInstance().getRawWrapper(this.baseDataEngine, upstreamQuery);
while (it.hasNext()) {
IHeadersDataRow headerRows = it.next();
executeRemoveQuery(headerRows, this.baseDataEngine);
}
} catch (Exception e) {
classLogger.error(Constants.STACKTRACE, e);
} finally {
if(it != null) {
try {
it.close();
} catch (IOException e) {
classLogger.error(Constants.STACKTRACE, e);
}
}
}
}
// remove from hash
this.propHash.remove(tableName + "%" + propertyCol);
long end = System.currentTimeMillis();
classLogger.info("Time for property removal = "+(end-start)+"ms");
NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN);
noun.addAdditionalReturn(new NounMetadata("Successfully removed property", PixelDataType.CONST_STRING, PixelOperationType.SUCCESS));
return noun;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// rename things
/**
* Rename an old concept name to a new name
* @param appId
* @param oldConceptName
* @param newConceptName
* @return
*/
public NounMetadata renameConcept(String oldConceptName, String newConceptName, String newConceptualName) {
// we need to take the table name and make the URL
String newConceptPhysicalUri = BASE_NODE_URI + "/" + newConceptName;
// then we need to take the properties of the table and store
Map newProperties = new HashMap();
Map newRelations = new HashMap();
List properties = null;
// then everything downstream needs to be edited
// then everything upstream needs to be edited
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy