@@ -41,176 +41,178 @@ export interface PIDComponentDB extends DBSchema {
4141 } ;
4242}
4343
44- /**
45- * Opens the indexedDB database for the PID component and creates the object stores and indexes if they do not exist.
46- * @type {Promise<IDBPDatabase<PIDComponentDB>> }
47- * @const
48- */
49- const dbPromise = openDB < PIDComponentDB > ( dbName , dbVersion , {
50- upgrade ( db ) {
51- const entityStore = db . createObjectStore ( 'entities' , {
52- keyPath : 'value' ,
53- } ) ;
54- entityStore . createIndex ( 'by-context' , 'context' , { unique : false } ) ;
55-
56- const relationStore = db . createObjectStore ( 'relations' , {
57- autoIncrement : true ,
58- } ) ;
59-
60- // Create indexes for the relations
61- relationStore . createIndex ( 'by-start' , 'start' , { unique : false } ) ;
62- relationStore . createIndex ( 'by-description' , 'description' , { unique : false } ) ;
63- relationStore . createIndex ( 'by-end' , 'end' , { unique : false } ) ;
64- } ,
65- } ) ;
66-
67- /**
68- * Adds an entity to the database.
69- * @param {GenericIdentifierType } renderer The renderer to add to the database.
70- */
71- export async function addEntity ( renderer : GenericIdentifierType ) {
72- const context = document . documentURI ;
73- const db = await dbPromise ;
74-
75- // Add the entity to the entities object store
76- await db
77- . add ( 'entities' , {
78- value : renderer . value ,
79- rendererKey : renderer . getSettingsKey ( ) ,
80- context : context ,
81- lastAccess : new Date ( ) ,
82- lastData : renderer . data ,
83- } )
84- . catch ( reason => {
85- if ( reason . name === 'ConstraintError' ) {
86- console . debug ( 'Entity already exists' , reason ) ;
87- } else console . error ( 'Could not add entity' , reason ) ;
88- } ) ;
89- console . debug ( 'added entity' , renderer ) ;
90-
91- // Add the relations to the relations object store
92- // Start a new transaction
93- const tx = db . transaction ( 'relations' , 'readwrite' ) ;
94- const promises = [ ] ;
95-
96- for ( const item of renderer . items ) {
97- // Create a relation object
98- const relation = {
99- start : renderer . value ,
100- description : item . keyTitle ,
101- end : item . value ,
102- } ;
103- // Check if the relation already exists
104- const index = tx . store . index ( 'by-start' ) ;
105- let cursor = await index . openCursor ( ) ;
106- while ( cursor ) {
107- if ( cursor . value . start === relation . start && cursor . value . end === relation . end && cursor . value . description === relation . description ) {
108- // relation already exists
109- return ;
44+ export class Database {
45+ /**
46+ * Opens the indexedDB database for the PID component and creates the object stores and indexes if they do not exist.
47+ * @type {Promise<IDBPDatabase<PIDComponentDB>> }
48+ * @const
49+ */
50+ dbPromise = openDB < PIDComponentDB > ( dbName , dbVersion , {
51+ upgrade ( db ) {
52+ const entityStore = db . createObjectStore ( 'entities' , {
53+ keyPath : 'value' ,
54+ } ) ;
55+ entityStore . createIndex ( 'by-context' , 'context' , { unique : false } ) ;
56+
57+ const relationStore = db . createObjectStore ( 'relations' , {
58+ autoIncrement : true ,
59+ } ) ;
60+
61+ // Create indexes for the relations
62+ relationStore . createIndex ( 'by-start' , 'start' , { unique : false } ) ;
63+ relationStore . createIndex ( 'by-description' , 'description' , { unique : false } ) ;
64+ relationStore . createIndex ( 'by-end' , 'end' , { unique : false } ) ;
65+ } ,
66+ } ) ;
67+
68+ /**
69+ * Adds an entity to the database.
70+ * @param {GenericIdentifierType } renderer The renderer to add to the database.
71+ */
72+ async addEntity ( renderer : GenericIdentifierType ) {
73+ const context = document . documentURI ;
74+ const db = await this . dbPromise ;
75+
76+ // Add the entity to the entities object store
77+ await db
78+ . add ( 'entities' , {
79+ value : renderer . value ,
80+ rendererKey : renderer . getSettingsKey ( ) ,
81+ context : context ,
82+ lastAccess : new Date ( ) ,
83+ lastData : renderer . data ,
84+ } )
85+ . catch ( reason => {
86+ if ( reason . name === 'ConstraintError' ) {
87+ console . debug ( 'Entity already exists' , reason ) ;
88+ } else console . error ( 'Could not add entity' , reason ) ;
89+ } ) ;
90+ console . debug ( 'added entity' , renderer ) ;
91+
92+ // Add the relations to the relations object store
93+ // Start a new transaction
94+ const tx = db . transaction ( 'relations' , 'readwrite' ) ;
95+ const promises = [ ] ;
96+
97+ for ( const item of renderer . items ) {
98+ // Create a relation object
99+ const relation = {
100+ start : renderer . value ,
101+ description : item . keyTitle ,
102+ end : item . value ,
103+ } ;
104+ // Check if the relation already exists
105+ const index = tx . store . index ( 'by-start' ) ;
106+ let cursor = await index . openCursor ( ) ;
107+ while ( cursor ) {
108+ if ( cursor . value . start === relation . start && cursor . value . end === relation . end && cursor . value . description === relation . description ) {
109+ // relation already exists
110+ return ;
111+ }
112+ cursor = await cursor . continue ( ) ;
110113 }
111- cursor = await cursor . continue ( ) ;
114+ // Add the relation to the relations object store if it does not exist
115+ promises . push ( tx . store . add ( relation ) ) ;
112116 }
113- // Add the relation to the relations object store if it does not exist
114- promises . push ( tx . store . add ( relation ) ) ;
117+ promises . push ( tx . done ) ;
118+ await Promise . all ( promises ) ;
119+ console . debug ( 'added relations' , promises ) ;
115120 }
116- promises . push ( tx . done ) ;
117- await Promise . all ( promises ) ;
118- console . debug ( 'added relations' , promises ) ;
119- }
120121
121- /**
122- * Gets an entity from the database. If the entity does not exist, it is created.
123- * @returns {Promise<GenericIdentifierType> } The renderer for the entity.
124- * @param {string } value The stringified value of the entity, e.g. the PID.
125- * @param {{type: string, values: {name: string, value: any}[]}[] } settings The settings for all renderers.
126- */
127- export const getEntity = async function (
128- value : string ,
129- settings : {
130- type : string ;
131- values : {
132- name : string ;
133- value : any ;
134- } [ ] ;
135- } [ ] ,
136- ) : Promise < GenericIdentifierType > {
137- // Try to get the entity from the database
138- try {
139- const db = await dbPromise ;
140- const entity :
141- | {
142- value : string ;
143- rendererKey : string ;
144- context : string ;
145- lastAccess : Date ;
146- lastData : any ;
122+ /**
123+ * Gets an entity from the database. If the entity does not exist, it is created.
124+ * @returns {Promise<GenericIdentifierType> } The renderer for the entity.
125+ * @param {string } value The stringified value of the entity, e.g. the PID.
126+ * @param {{type: string, values: {name: string, value: any}[]}[] } settings The settings for all renderers.
127+ */
128+ async getEntity (
129+ value : string ,
130+ settings : {
131+ type : string ;
132+ values : {
133+ name : string ;
134+ value : any ;
135+ } [ ] ;
136+ } [ ] ,
137+ ) : Promise < GenericIdentifierType > {
138+ // Try to get the entity from the database
139+ try {
140+ const db = await this . dbPromise ;
141+ const entity :
142+ | {
143+ value : string ;
144+ rendererKey : string ;
145+ context : string ;
146+ lastAccess : Date ;
147+ lastData : any ;
148+ }
149+ | undefined = await db . get ( 'entities' , value ) ;
150+
151+ if ( entity !== undefined ) {
152+ // If the entity was found, check if the TTL has expired
153+ console . debug ( 'Found entity for value in db' , entity , value ) ;
154+ const entitySettings = settings . find ( value => value . type === entity . rendererKey ) ?. values ;
155+ const ttl = entitySettings ?. find ( value => value . name === 'ttl' ) ;
156+
157+ if ( ttl != undefined && ttl . value != undefined && ( new Date ( ) . getTime ( ) - entity . lastAccess . getTime ( ) > ttl . value || ttl . value === 0 ) ) {
158+ // If the TTL has expired, delete the entity from the database and move on to creating a new one (down below)
159+ console . log ( 'TTL expired! Deleting entry in db' , ttl . value , new Date ( ) . getTime ( ) - entity . lastAccess . getTime ( ) ) ;
160+ await this . deleteEntity ( value ) ;
161+ } else {
162+ // If the TTL has not expired, get a new renderer and return it
163+ console . log ( 'TTL not expired or undefined' , new Date ( ) . getTime ( ) - entity . lastAccess . getTime ( ) ) ;
164+ const renderer = new ( renderers . find ( renderer => renderer . key === entity . rendererKey ) . constructor ) ( value , entitySettings ) ;
165+ renderer . settings = entitySettings ;
166+ await renderer . init ( entity . lastData ) ;
167+ return renderer ;
147168 }
148- | undefined = await db . get ( 'entities' , value ) ;
149-
150- if ( entity !== undefined ) {
151- // If the entity was found, check if the TTL has expired
152- console . debug ( 'Found entity for value in db' , entity , value ) ;
153- const entitySettings = settings . find ( value => value . type === entity . rendererKey ) ?. values ;
154- const ttl = entitySettings ?. find ( value => value . name === 'ttl' ) ;
155-
156- if ( ttl != undefined && ttl . value != undefined && ( new Date ( ) . getTime ( ) - entity . lastAccess . getTime ( ) > ttl . value || ttl . value === 0 ) ) {
157- // If the TTL has expired, delete the entity from the database and move on to creating a new one (down below)
158- console . log ( 'TTL expired! Deleting entry in db' , ttl . value , new Date ( ) . getTime ( ) - entity . lastAccess . getTime ( ) ) ;
159- await deleteEntity ( value ) ;
160- } else {
161- // If the TTL has not expired, get a new renderer and return it
162- console . log ( 'TTL not expired or undefined' , new Date ( ) . getTime ( ) - entity . lastAccess . getTime ( ) ) ;
163- const renderer = new ( renderers . find ( renderer => renderer . key === entity . rendererKey ) . constructor ) ( value , entitySettings ) ;
164- renderer . settings = entitySettings ;
165- await renderer . init ( entity . lastData ) ;
166- return renderer ;
167169 }
170+ } catch ( error ) {
171+ console . error ( 'Could not get entity from db' , error ) ;
168172 }
169- } catch ( error ) {
170- console . error ( 'Could not get entity from db' , error ) ;
173+
174+ // If no entity was found, create a new one, initialize it and it to the database
175+ console . debug ( 'No valid entity found for value in db' , value ) ;
176+ const renderer = await Parser . getBestFit ( value , settings ) ;
177+ renderer . settings = settings . find ( value => value . type === renderer . getSettingsKey ( ) ) ?. values ;
178+ await renderer . init ( ) ;
179+ await this . addEntity ( renderer ) ;
180+ console . debug ( 'added entity to db' , value , renderer ) ;
181+ return renderer ;
171182 }
172183
173- // If no entity was found, create a new one, initialize it and it to the database
174- console . debug ( 'No valid entity found for value in db' , value ) ;
175- const renderer = await Parser . getBestFit ( value , settings ) ;
176- renderer . settings = settings . find ( value => value . type === renderer . getSettingsKey ( ) ) ?. values ;
177- await renderer . init ( ) ;
178- await addEntity ( renderer ) ;
179- console . debug ( 'added entity to db' , value , renderer ) ;
180- return renderer ;
181- } ;
184+ /**
185+ * Deletes an entity from the database.
186+ * @param value The value of the entity to delete.
187+ */
188+ async deleteEntity ( value : string ) {
189+ const db = await this . dbPromise ;
182190
183- /**
184- * Deletes an entity from the database.
185- * @param value The value of the entity to delete.
186- */
187- export async function deleteEntity ( value : string ) {
188- const db = await dbPromise ;
189-
190- // Delete the entity
191- await db . delete ( 'entities' , value ) ;
192-
193- // Delete all relations for the entity
194- const tx = db . transaction ( 'relations' , 'readwrite' ) ;
195- const index = tx . store . index ( 'by-start' ) ;
196- let cursor = await index . openCursor ( ) ;
197- while ( cursor ) {
198- if ( cursor . value . start === value || cursor . value . end === value ) {
199- await tx . store . delete ( cursor . primaryKey ) ;
191+ // Delete the entity
192+ await db . delete ( 'entities' , value ) ;
193+
194+ // Delete all relations for the entity
195+ const tx = db . transaction ( 'relations' , 'readwrite' ) ;
196+ const index = tx . store . index ( 'by-start' ) ;
197+ let cursor = await index . openCursor ( ) ;
198+ while ( cursor ) {
199+ if ( cursor . value . start === value || cursor . value . end === value ) {
200+ await tx . store . delete ( cursor . primaryKey ) ;
201+ }
202+ cursor = await cursor . continue ( ) ;
200203 }
201- cursor = await cursor . continue ( ) ;
204+ console . log ( 'deleted entity' , value ) ;
205+ await tx . done ;
202206 }
203- console . log ( 'deleted entity' , value ) ;
204- await tx . done ;
205- }
206207
207- /**
208- * Clears all entities from the database.
209- * @returns {Promise<void> } A promise that resolves when all entities have been deleted.
210- */
211- export async function clearEntities ( ) {
212- const db = await dbPromise ;
213- await db . clear ( 'entities' ) ;
214- await db . clear ( 'relations' ) ;
215- console . log ( 'cleared entities' ) ;
208+ /**
209+ * Clears all entities from the database.
210+ * @returns {Promise<void> } A promise that resolves when all entities have been deleted.
211+ */
212+ async clearEntities ( ) {
213+ const db = await this . dbPromise ;
214+ await db . clear ( 'entities' ) ;
215+ await db . clear ( 'relations' ) ;
216+ console . log ( 'cleared entities' ) ;
217+ }
216218}
0 commit comments