import { RDFStore , RDFGraph , createStore , createGraph , Node , Literal , NamedNode , Triple , } from 'rdf-ext' ; import { Dictionary } from '../model' ; import { RDFCompositeParser } from './rdfCompositeParser' ; import { uniqueId } from 'lodash' ; const DEFAULT_STORAGE_TYPE = 'text/turtle' ; const DEFAULT_STORAGE_URI = 'http://graph-explorer.org/defaultGraph' ; export function prefixFactory ( prefix ) { const lastSymbol = prefix [ prefix . length - 1 ] ; const _prefix = lastSymbol === '/' || lastSymbol === '#' ? prefix : prefix + '/' ; return ( id ) => { return _prefix + id ; } ; } export function isLiteral ( el ) { return el . interfaceName === 'Literal' ; } export function isNamedNode ( el ) { return el . interfaceName === 'NamedNode' ; } export interface MatchStatement { subject ? : string ; predicate ? : string ; object ? : string ; iri ? : string ; callback ? : ( ... args : any [ ] ) => void ; limit ? : number ; } export const LABEL_URIS = [ 'http://www.w3.org/2004/02/skos/core#prefLabel' , 'http://www.w3.org/2004/02/skos/core#label' , 'http://www.w3.org/2004/02/skos/core#altLabel' , 'http://www.w3.org/2000/01/rdf-schema#prefLabel' , 'http://www.w3.org/2000/01/rdf-schema#label' , 'http://xmlns.com/foaf/0.1/name' , 'http://schema.org/name' , ] ; export const LABEL_POSTFIXES = [ 'prefLabel' , 'prefName' , 'label' , 'name' , 'title' , ] ; export function isLabelType ( predicateIri ) { const isLabelIri = LABEL_URIS . indexOf ( predicateIri ) !== - 1 ; const type = predicateIri . toLocaleLowerCase ( ) ; const looksLikeLabel = Boolean ( LABEL_POSTFIXES . find ( ( value , index , array ) => { const postfix = value . toLocaleLowerCase ( ) ; return type . indexOf ( postfix ) !== - 1 ; } ) ) ; return isLabelIri || looksLikeLabel ; } export interface RDFStoreOptions { parser : RDFCompositeParser ; acceptBlankNodes ? : boolean ; } export class RDFCacheableStore { private readonly parser : RDFCompositeParser ; private readonly acceptBlankNodes : boolean ; private readonly rdfStorage : RDFStore ; private readonly prefs : { [ id ] : ( id : string ) => string ; } ; private checkingElementMap : Dictionary < Promise < boolean > > = { } ; private labelsMap : Dictionary < Triple [ ] > = { } ; private countMap : Dictionary < number > = { } ; private elementTypes : Dictionary < Triple [ ] > = { } ; constructor ( options ) { this . parser = options . parser ; this . acceptBlankNodes = Boolean ( options . acceptBlankNodes ) ; this . rdfStorage = createStore ( ) ; this . prefs = { RDF : prefixFactory ( 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' ) , RDFS : prefixFactory ( 'http://www.w3.org/2000/01/rdf-schema#' ) , FOAF : prefixFactory ( 'http://xmlns.com/foaf/0.1/' ) , XSD : prefixFactory ( 'http://www.w3.org/2001/XMLSchema#' ) , OWL : prefixFactory ( 'http://www.w3.org/2002/07/owl#' ) , } ; } add ( rdfGraph , prefix ? ) { this . rdfStorage . add ( uniqueId ( prefix ) || uniqueId ( DEFAULT_STORAGE_URI ) , rdfGraph ) ; this . enrichMaps ( rdfGraph ) ; } match ( subject ? , predicate ? , object ? , iri ? , callback ? , limit ? ) { if ( subject && LABEL_URIS . indexOf ( predicate ) !== - 1 && ! object ) { return Promise . resolve ( this . _getLabels ( subject ) ) ; } else if ( subject && predicate === this . prefs . RDF ( 'type' ) && ! object ) { return Promise . resolve ( this . getTypes ( subject ) ) ; } else { return this . rdfStorage . match ( subject , predicate , object , iri , callback , limit ) ; } } matchAll ( statements ) { const slowQueries = [ ] ; const queries = [ ] ; statements . forEach ( ( statement ) => { if ( statement . subject && LABEL_URIS . indexOf ( statement . predicate ) !== - 1 && ! statement . object ) { queries . push ( this . _getLabels ( statement . subject ) ) ; } else if ( statement . subject && statement . predicate === this . prefs . RDF ( 'type' ) && ! statement . object ) { queries . push ( this . getTypes ( statement . subject ) ) ; } else { slowQueries . push ( statement ) ; } } ) ; queries . push ( this . multipleMatch ( slowQueries ) ) ; return Promise . resolve ( this . combineGraphs ( queries ) ) ; } getLabels ( id ) { return Promise . resolve ( createGraph ( this . labelsMap [ id ] ) ) ; } checkElement ( id ) { if ( this . labelsMap [ id ] ) { return Promise . resolve ( true ) ; } else { if ( ! this . checkingElementMap [ id ] ) { this . checkingElementMap [ id ] = this . rdfStorage . match ( id , null , null ) . then ( ( result ) => { const resultArray = result . toArray ( ) ; return resultArray . length !== 0 ; } ) ; } return this . checkingElementMap [ id ] ; } } getTypeCount ( id ) { return this . countMap [ id ] || 0 ; } private enrichMaps ( newGraph ) { const triples = newGraph . toArray ( ) ; for ( const triple of triples ) { const element = triple . subject . nominalValue ; const predicate = triple . predicate . nominalValue ; if ( isLabelType ( predicate ) ) { if ( ! this . labelsMap [ element ] ) { this . labelsMap [ element ] = [ ] ; } if ( isLiteral ( triple . object ) ) { this . labelsMap [ element ] . push ( triple ) ; this . labelsMap [ element ] . sort ( ( a , b ) => { const index1 = LABEL_URIS . indexOf ( a . predicate . nominalValue ) ; const index2 = LABEL_URIS . indexOf ( b . predicate . nominalValue ) ; if ( index1 > index2 ) { return 1 ; } else if ( index1 < index2 ) { return - 1 ; } else { return 0 ; } } ) ; } } } const typeInstances = newGraph . match ( null , this . prefs . RDF ( 'type' ) , null ) . toArray ( ) ; const typeInstMap = { } ; for ( const instTriple of typeInstances ) { const type = instTriple . object . nominalValue ; const inst = instTriple . subject . nominalValue ; if ( ! typeInstMap [ type ] ) { typeInstMap [ type ] = [ ] ; } if ( this . acceptBlankNodes || isNamedNode ( instTriple . subject ) ) { if ( ! this . elementTypes [ inst ] ) { this . elementTypes [ inst ] = [ ] ; } if ( typeInstMap [ type ] . indexOf ( inst ) === - 1 ) { typeInstMap [ type ] . push ( inst ) ; } this . elementTypes [ inst ] . push ( instTriple ) ; } } Object . keys ( typeInstMap ) . forEach ( ( key ) => ( this . countMap [ key ] = typeInstMap [ key ] . length ) ) ; return true ; } private _getLabels ( id ) { return createGraph ( this . labelsMap [ id ] ) ; } private getTypes ( id ) { return createGraph ( this . elementTypes [ id ] ) ; } private combineGraphs ( graphs ) { const triples = [ ] ; for ( const graph of graphs ) { for ( const triple of graph . toArray ( ) ) { triples . push ( triple ) ; } } return createGraph ( triples ) ; } private multipleMatch ( statements ) { const triples = [ ] ; const graphs = Object . keys ( this . rdfStorage . graphs ) . map ( ( id ) => this . rdfStorage . graphs [ id ] ) ; for ( const graph of graphs ) { for ( const triple of graph . _graph ) { for ( const statement of statements ) { if ( ( ! statement . object || statement . object === triple . object . nominalValue ) && ( ! statement . predicate || statement . predicate === triple . predicate . nominalValue ) && ( ! statement . subject || statement . subject === triple . subject . nominalValue ) ) { triples . push ( triple ) ; continue ; } } } } return createGraph ( triples ) ; } }