Skip to content

Commit 046b3e2

Browse files
committed
Merge branch 'master' of github.com:mmb-irb/MDDB-REST-API
2 parents b7104a3 + 9de470b commit 046b3e2

1 file changed

Lines changed: 46 additions & 18 deletions

File tree

‎src/routes/stats/index.js‎

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ router.route('/').get(
2222
const config = getConfig(request);
2323
const isGlobal = config && config.global;
2424
// Global stats may not be asked to the global API
25-
if (isGlobal) return {
25+
const query = request.query.query;
26+
if (isGlobal && !query) return {
2627
headerError: BAD_REQUEST,
2728
error: 'This is the global database and its stats may be missleading.'
2829
+ ' In order to get the global stats all federated nodes must be asked.'
@@ -34,34 +35,61 @@ router.route('/').get(
3435
// Compute the real stored size of GridFS files for the queried projects
3536
// Without a query filter this would require aggregating all files in the DB,
3637
// so we skip it and fall back to the dbStats figure instead
37-
const query = request.query.query;
3838
let realDataSizeInTB;
3939
if (!query) {
4040
realDataSizeInTB = +(dbStats.dataSize / 1e6);
4141
} else {
42-
// This is expensive so we cache it with a TTL, keyed by query string
42+
// This is expensive so we cache it with a TTL (time-to-live), keyed by query string
4343
if (!gridFsSizeCache.has(query)
4444
|| Date.now() - gridFsSizeCache.get(query).timestamp > CACHE_TTL_MS) {
4545
const projectsFinder = database.getBaseFilter();
4646
const processedQuery = await database.processProjectsQuery(query);
4747
if (processedQuery.error) return processedQuery;
4848
if (!projectsFinder.$and) projectsFinder.$and = processedQuery;
4949
else projectsFinder.$and = projectsFinder.$and.concat(processedQuery);
50-
// Get all file IDs, including those in MDs
51-
const projectsCursor = await database.projects.find(
52-
projectsFinder,
53-
{ projection: { files: 1, 'mds.files': 1 } },
54-
);
55-
const allProjects = await projectsCursor.toArray();
56-
// Flatten all file IDs
57-
const fileIds = allProjects.flatMap(project => [
58-
...(project.files ?? []).map(f => f.id),
59-
...(project.mds ?? []).flatMap(md => (md.files ?? []).map(f => f.id)),
60-
]);
61-
// Sum 'length' from fs.files for all those IDs
62-
const sizeAgg = await database.db.collection('fs.files').aggregate([
63-
{ $match: { _id: { $in: fileIds } } },
64-
{ $group: { _id: null, totalBytes: { $sum: '$length' } } },
50+
// Single aggregation pipeline to do it all
51+
const sizeAgg = await database.projects.aggregate([
52+
// 1. Filter projects
53+
{ $match: projectsFinder },
54+
// 2. Extract and flatten file IDs entirely on the DB side
55+
{
56+
$project: {
57+
fileIds: {
58+
$concatArrays: [
59+
{ $ifNull: [ "$files.id", [] ] },
60+
{
61+
$reduce: {
62+
input: { $ifNull: [ "$mds", [] ] },
63+
initialValue: [],
64+
in: { $concatArrays: [ "$$value", { $ifNull: [ "$$this.files.id", [] ] } ] }
65+
}
66+
}
67+
]
68+
}
69+
}
70+
},
71+
// 3. Flatten the arrays into individual document rows
72+
{ $unwind: "$fileIds" },
73+
// 4. Deduplicate IDs across all projects to avoid double-counting shared files
74+
{ $group: { _id: "$fileIds" } },
75+
// 5. Join with the fs.files collection using the indexed _id primary key
76+
{
77+
$lookup: {
78+
from: 'fs.files',
79+
localField: '_id',
80+
foreignField: '_id',
81+
as: 'gridFsFile'
82+
}
83+
},
84+
// 6. Unwind the joined file array (will be 1 item max per row)
85+
{ $unwind: "$gridFsFile" },
86+
// 7. Sum up the lengths
87+
{
88+
$group: {
89+
_id: null,
90+
totalBytes: { $sum: "$gridFsFile.length" }
91+
}
92+
}
6593
]).toArray();
6694
const totalBytes = sizeAgg.length > 0 ? sizeAgg[0].totalBytes : 0;
6795
gridFsSizeCache.set(query, { totalBytes, timestamp: Date.now() });

0 commit comments

Comments
 (0)