@@ -13,6 +13,12 @@ const {
1313 STANDARD_STRUCTURE_FILENAME ,
1414} = require ( './utils/constants' ) ;
1515
16+ // Import auxiliar functions
17+ const { areObjectsIdentical } = require ( './utils/auxiliar' ) ;
18+
19+ // Import additional functions
20+ const countOptions = require ( './utils/count-options' ) ;
21+
1622// GridFSBucket manages the saving of files bigger than 16 Mb, splitting them into 4 Mb fragments (chunks)
1723const { GridFSBucket } = require ( 'mongodb' ) ;
1824
@@ -52,28 +58,102 @@ class Database {
5258 return this . _bucket ;
5359 }
5460
61+ // Setup the database by creating and indexing the configured collections
62+ // This function is shared by the loader and the monitor
63+ setup = async ( ) => {
64+ // Check the collections already existing in the database
65+ const currentCollections = await this . db . listCollections ( ) . toArray ( )
66+ const currentCollectionNames = currentCollections . map ( collection => collection . name ) ;
67+ // Iterate over the configured collections
68+ for await ( const [ collectionKey , collectionConfig ] of Object . entries ( this . COLLECTIONS ) ) {
69+ // If the collection already exists then do nothing
70+ if ( currentCollectionNames . includes ( collectionConfig . name ) ) {
71+ // Get the configuration indexes for this collection
72+ const configIndexes = collectionConfig . indexes ;
73+ // If there are no configuration indexes at all then we are done
74+ if ( ! configIndexes ) continue ;
75+ // Get the current collection indexes
76+ const currentIndexesData = await this [ collectionKey ] . indexes ( ) ;
77+ const currentIndexes = currentIndexesData . map ( indexData => indexData . key ) ;
78+ // Iterate the expected indexes
79+ for await ( const configIndex of configIndexes ) {
80+ // Make sure the index exists among the current indexes
81+ let found = false ;
82+ for ( const collectionIndex of currentIndexes ) {
83+ // Compare indices
84+ if ( areObjectsIdentical ( collectionIndex , configIndex ) ) {
85+ found = true ;
86+ break ;
87+ }
88+ }
89+ // If the index does not exist then we create it
90+ if ( ! found ) {
91+ console . log ( `🛠️ Setting a missing index in "${ collectionKey } " collection: ${ JSON . stringify ( configIndex ) } ` ) ;
92+ await this [ collectionKey ] . createIndex ( configIndex ) ;
93+ }
94+ }
95+ // Proceed to the next collection
96+ continue ;
97+ }
98+ console . log ( `🛠️ Setting up ${ collectionKey } collection` ) ;
99+ // Create the collection
100+ await this . db . createCollection ( collectionConfig . name ) ;
101+ // Set some indices if specified to accelerate specific queries
102+ if ( collectionConfig . indexes ) {
103+ for await ( const index of collectionConfig . indexes ) {
104+ await this [ collectionKey ] . createIndex ( index ) ;
105+ }
106+ }
107+ }
108+ } ;
109+
110+ // Set some filters in the project query depending on the context in a single query object
111+ getProjectsFilter = ( isGlobal , isProduction , hostCollection , withAccession ) => {
112+ // Set the published filter according to the host configuration
113+ // If the environment is tagged as "production" only published projects are returned from mongo
114+ // Note that in the global API we target projects flagged as 'posited' instead of 'published'
115+ const productionTargetFlag = isGlobal ? 'posited' : 'published' ;
116+ const publishedFilter = Object . seal ( isProduction ? { [ productionTargetFlag ] : true } : { } ) ;
117+ // Set a filter for the global API to not return unposited projects
118+ // Note that a non global API is not expected to have this field so it makes not sense applying the filter
119+ const positedFilter = Object . seal ( isGlobal ? { unposited : { $exists : false } } : { } ) ;
120+ // Set the collection filter according to the request URL
121+ // This filter is applied over the project metadata 'collections', nothing to do with mongo collections
122+ // Note that unknown hosts (e.g. 'localhost:8000') will get all simulations, with no filter
123+ const collectionFilter = Object . seal ( hostCollection ? { 'metadata.COLLECTIONS' : hostCollection } : { } ) ;
124+ // Set the starting base filter
125+ // This is a strict filter and it is applied even when a specific project is requested
126+ const baseFilter = { ...publishedFilter , ...positedFilter , ...collectionFilter } ;
127+ // If a specific project was requested then the base filter is returned as it is
128+ // Note that the filter above are mandatory for "privacy" reasons
129+ // In the sense that they are not to be skipped even if the requests asks for a specific accession
130+ // The filters below, in the other hand, are to discard projects which are not important
131+ // And they could make the query fail if the user asks for a booked/deleted entry
132+ if ( withAccession ) return baseFilter ;
133+ // If no specific project was requested then the filter is extended
134+ // We hide booked and deleted projects
135+ // Note that these are not hidden when asking specifically for them
136+ // Set the booked filter to remove booked projects from the query
137+ // These are projects which are not yet uploaded
138+ const bookedFilter = Object . seal ( { booked : { $ne : true } } ) ;
139+ // Set the deleted filter to remove deleted projects from the query
140+ // These are projects which were deleted but the entry is kept to preserve the persistent id
141+ const deletedFilter = Object . seal ( { deleted : { $ne : true } } ) ;
142+ // Return all filters together, including also the publsihed filter
143+ return { ...baseFilter , ...bookedFilter , ...deletedFilter } ;
144+ } ;
145+
146+ // Add additional functions
147+ countOptions = ( query , fields , shouldCountMds , useSavedCounts ) =>
148+ countOptions ( this , query , fields , shouldCountMds , useSavedCounts ) ;
149+
55150 // Close the connection to mongo and delete this handler
56151 close = ( ) => {
57152 this . client . close ( ) ;
58153 delete this ;
59154 }
60155}
61156
62- // Connect to the database
63- // Then construct and return the database handler
64- const connectToDatabase = async isGlobal => {
65- // Save the mongo database connection
66- const client = await dbConnection ;
67- // Instantiate the database handler
68- return new Database ( client , isGlobal ) ;
69- } ;
70-
71- // Get the database client
72- // Note that this function is set separatedly and not integrated in the handler since it is async
73- const getDatabaseClient = async ( ) => {
74- const client = await dbConnection ;
75- }
76-
77157module . exports = {
78158 databaseConnection,
79159 Database,
0 commit comments