Skip to content

Commit

Permalink
Merge pull request #334 from mff-uk/feature/core-v2
Browse files Browse the repository at this point in the history
core-v2
  • Loading branch information
apolicky authored Oct 23, 2023
2 parents 7a576e9 + ec928f7 commit 4149b8e
Show file tree
Hide file tree
Showing 10 changed files with 461 additions and 71 deletions.
42 changes: 16 additions & 26 deletions packages/core-v2/src/entity-model/async-queryable/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,6 @@ export class SimpleAsyncQueryableObservableEntityModel extends InMemoryEntityMod
protected queries: Record<string, any> = {};
protected reverseQueries: Map<string, string[]> = new Map();

protected addToReverseQueries(entityIri: string, queryIri: string): void {
if (!this.reverseQueries.has(entityIri)) {
this.reverseQueries.set(entityIri, []);
}
const queries = this.reverseQueries.get(entityIri)!;
if (!queries.includes(queryIri)) {
queries.push(queryIri);
}
}

constructor(queryableModel: AsyncQueryableEntityModel) {
super();
this.model = queryableModel;
Expand All @@ -34,23 +24,23 @@ export class SimpleAsyncQueryableObservableEntityModel extends InMemoryEntityMod
}

this.queries[queryIri] = result;
const iris = Object.keys(result);
const addedIris: Record<string, Entity> = {};
const ids = Object.keys(result);
const addedIds: Record<string, Entity> = {};

for (const iri of iris) {
if (!this.reverseQueries.has(iri)) {
this.reverseQueries.set(iri, []);
for (const id of ids) {
if (!this.reverseQueries.has(id)) {
this.reverseQueries.set(id, []);
}
const queries = this.reverseQueries.get(iri)!;
const queries = this.reverseQueries.get(id)!;
if (!queries.includes(queryIri)) {
if (queries.length == 0) {
addedIris[iri] = result[iri];
addedIds[id] = result[id];
}
queries.push(queryIri);
}
}

this.change(addedIris, []);
this.change(addedIds, []);
}

async releaseQuery(queryIri: string): Promise<void> {
Expand All @@ -59,22 +49,22 @@ export class SimpleAsyncQueryableObservableEntityModel extends InMemoryEntityMod
return;
}

const iris = Object.keys(this.queries[queryIri]);
const removedIris: string[] = [];
for (const iri of iris) {
let queries = this.reverseQueries.get(iri)!;
const ids = Object.keys(this.queries[queryIri]);
const removedIds: string[] = [];
for (const id of ids) {
let queries = this.reverseQueries.get(id)!;
queries = queries.filter(q => q !== queryIri);
if (queries.length === 0) {
this.reverseQueries.delete(iri);
removedIris.push(iri);
this.reverseQueries.delete(id);
removedIds.push(id);
} else {
this.reverseQueries.set(iri, queries);
this.reverseQueries.set(id, queries);
}
}

delete this.queries[queryIri];

this.change({}, removedIris);
this.change({}, removedIds);
}

}
2 changes: 1 addition & 1 deletion packages/core-v2/src/entity-model/entity.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface Entity {
iri: string;
id: string;
type: string[];
}

Expand Down
75 changes: 55 additions & 20 deletions packages/core-v2/src/semantic-model/aggregator/aggregator.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import {Entity} from "../../entity-model/entity";

import {EntityModel} from "../../entity-model";
import {EntityModel} from "../../entity-model/model";

/**
* Object containing the result of the aggregation of an entity together with additional metadata, such as how the
* aggregation was performed.
*/
interface AggregatedEntityWrapper {
iri: string;
id: string;
aggregatedEntity: Entity | null;
}

type SupportedModels = EntityModel;

type AggregatedModelSubscriber = (updated: AggregatedEntityWrapper[], removed: string[]) => void;

/**
* Aggregates multiple models (the model graph] describing the semantics (concepts and their relations) into one.
*
Expand All @@ -22,34 +24,68 @@ type SupportedModels = EntityModel;
interface SemanticModelAggregator {
/**
* Adds model describing the semantics to the aggregator.
* @todo for now, only accepts one model
* @param model
*/
addModel(model: SupportedModels): void;

/**
* Unregisters the model from the aggregator.
*/
deleteModel(model: SupportedModels): void;

/**
* Returns the specific view.
* @todo option to choose the view
*/
getView(toModel: SupportedModels): SemanticModelAggregatorView;
getView(): SemanticModelAggregatorView;
}

class SemanticModelAggregatorInternal implements SemanticModelAggregator {
model: EntityModel | null = null;
models: Map<SupportedModels, () => void> = new Map();
baseModelEntities: AggregatedEntityWrapper[] = [];
baseModelSubscribers = new Set<AggregatedModelSubscriber>();

addModel(model: SupportedModels) {
if (this.model != null) {
throw new Error('Not implemented yet.');
if (this.models.has(model)) {
throw new Error('Model already added.');
}
const callback = (updated: Record<string, Entity>, removed: string[]) => {
if (!this.models.has(model)) {
return;
}

const entities = Object.values(updated);
const wrappedEntities = entities.map(aggregatedEntity => ({id: aggregatedEntity.id, aggregatedEntity}));

// Suppose there is a model that joins them all. Then we need to inform it
this.notifyBaseModel(model, wrappedEntities, removed);
};
const unsubscribe = model.subscribeToChanges(callback);
this.models.set(model, unsubscribe);

this.model = model;
callback(model.getEntities(), []);
};

getView(toModel: SupportedModels): SemanticModelAggregatorView {
if (this.model == null) {
throw new Error('Not implemented yet. First add model.');
deleteModel(model: SupportedModels) {
const unsubscribe = this.models.get(model);
if (!unsubscribe) {
throw new Error('Model not added.');
}
this.models.delete(model);
this.notifyBaseModel(model, [], Object.keys(model.getEntities()));
}

/**
* Temporary function that notifies base model about changes. We suppose that every model is a dependency of the base model.
*/
private notifyBaseModel(fromModel: SupportedModels, updated: AggregatedEntityWrapper[], removed: string[]) {
// This does only a simple merge
this.baseModelEntities = this.baseModelEntities
.filter(entity => !removed.includes(entity.id) && !updated.find(updatedEntity => updatedEntity.id === entity.id))
.concat(updated);
this.baseModelSubscribers.forEach(subscriber => subscriber(updated, removed));
}

getView(): SemanticModelAggregatorView {
return new SemanticModelAggregatorView(this);
}
}
Expand Down Expand Up @@ -80,22 +116,21 @@ export class SemanticModelAggregatorView {
*/
getEntities(): Record<string, AggregatedEntityWrapper> {
// todo temporary
return Object.fromEntries(Object.values(this.aggregator.model?.getEntities() ?? {})
.map(entity => [entity.iri, {iri: entity.iri, aggregatedEntity: entity} as AggregatedEntityWrapper]));
return Object.fromEntries(this.aggregator.baseModelEntities.map(e => [e.id, e]));
};

/**
* Subscribe to changes.
* @param callback Function that will be called when the entities change with the updated entities
* @returns Function that can be called to unsubscribe from changes
*/
subscribeToChanges(callback: (updated: Record<string, AggregatedEntityWrapper>, removed: string[]) => void): () => void {
subscribeToChanges(callback: AggregatedModelSubscriber): () => void {
// todo temporary
return this.aggregator.model?.subscribeToChanges((updated, removed) => callback(
Object.fromEntries(Object.values(updated)
.map(entity => [entity.iri, {iri: entity.iri, aggregatedEntity: entity} as AggregatedEntityWrapper])),
removed
)) ?? (() => {});
if (this.aggregator.baseModelSubscribers.has(callback)) {
throw new Error('Callback already subscribed.');
}
this.aggregator.baseModelSubscribers.add(callback);
return () => this.aggregator.baseModelSubscribers.delete(callback);
}

changeView(toModel: SupportedModels) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ export function searchQuery(search: string) {
return "search:" + search;
}

export function classSurroundingsQuery(iri: string) {
return "surroundings:" + iri;
export function classSurroundingsQuery(id: string) {
return "surroundings:" + id;
}

export function classQuery(iri: string) {
return "class:" + iri;
export function classQuery(id: string) {
return "class:" + id;
}

export interface SearchQueryEntity extends Entity {
Expand Down
15 changes: 12 additions & 3 deletions packages/core-v2/src/semantic-model/concepts/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import {Entity} from "../../entity-model/entity";
*/
export type LanguageString = { [key: string]: string };

export interface SemanticModelEntity extends Entity {
/**
* Public, usually globally-recognised, identifier of the entity.
* The value may be null indicating that the entity has no public IRI.
* @example http://xmlns.com/foaf/0.1/Person
*/
iri: string | null;
}

export interface NamedThing {
name: LanguageString;
//alias: LanguageString[];
Expand All @@ -16,7 +25,7 @@ export interface NamedThing {
/**
* Represent classes, enumerations and simple data types.
*/
export interface SemanticModelClass extends NamedThing, Entity {
export interface SemanticModelClass extends NamedThing, SemanticModelEntity {
type: [typeof SEMANTIC_MODEL_CLASS];

// todo: is it class, enumeration, datatype, code list, ...
Expand All @@ -31,7 +40,7 @@ export function isSemanticModelClass(resource: Entity | null): resource is Seman
/**
* Represents attributes and associations.
*/
export interface SemanticModelRelationship extends NamedThing, Entity {
export interface SemanticModelRelationship extends NamedThing, SemanticModelEntity {
type: [typeof SEMANTIC_MODEL_RELATIONSHIP];

ends: SemanticModelRelationshipEnd[]
Expand All @@ -55,7 +64,7 @@ export function isSemanticModelRelationship(resource: Entity | null): resource i
/**
* Inheritance hierarchy.
*/
export interface SemanticModelGeneralization extends Entity {
export interface SemanticModelGeneralization extends SemanticModelEntity {
type: [typeof SEMANTIC_MODEL_GENERALIZATION]

/** {@link SemanticModelClass} */
Expand Down
Loading

0 comments on commit 4149b8e

Please sign in to comment.