Skip to content

Commit a925eae

Browse files
committed
internal caching
1 parent 14328bf commit a925eae

10 files changed

+454
-151
lines changed

src/controllers/adoptionController.js

Lines changed: 95 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,125 @@
11
import { firestoreOld } from '../utils/db.js';
22
const firestore = firestoreOld;
33

4-
import { createSuccessResponse } from '../utils/helpers.js';
54
import {
6-
REQUIRED_PARAMS,
7-
validateRequiredParams,
8-
sendValidationError,
9-
applyDateFilters,
10-
applyStandardFilters,
11-
preprocessParams,
12-
handleControllerError
5+
getLatestDate,
6+
generateQueryCacheKey,
7+
getCachedQueryResult,
8+
setCachedQueryResult
139
} from '../utils/controllerHelpers.js';
1410

1511
const TABLE = 'adoption';
1612

1713
/**
18-
* List adoption data with filtering
14+
* List adoption data with filtering - Optimized version
1915
*/
2016
const listAdoptionData = async (req, res) => {
2117
try {
2218
const params = req.query;
2319

24-
// Validate required parameters
25-
const requiredParams = [
26-
REQUIRED_PARAMS.GEO,
27-
REQUIRED_PARAMS.RANK,
28-
REQUIRED_PARAMS.TECHNOLOGY
29-
];
20+
// Validate required parameters inline for speed
21+
if (!params.geo || !params.rank || !params.technology) {
22+
res.statusCode = 400;
23+
res.end(JSON.stringify({
24+
success: false,
25+
errors: [
26+
...(!params.geo ? [{ geo: 'missing geo parameter' }] : []),
27+
...(!params.rank ? [{ rank: 'missing rank parameter' }] : []),
28+
...(!params.technology ? [{ technology: 'missing technology parameter' }] : [])
29+
]
30+
}));
31+
return;
32+
}
33+
34+
// Fast preprocessing - handle 'latest' date and technology array
35+
const techArray = params.technology ? decodeURIComponent(params.technology).split(',') : [];
36+
37+
// Handle 'latest' date with caching
38+
let startDate = params.start;
39+
if (startDate === 'latest') {
40+
startDate = await getLatestDate(firestore, TABLE);
41+
}
42+
43+
// Create cache key for this specific query
44+
const queryFilters = {
45+
geo: params.geo,
46+
rank: params.rank,
47+
technology: techArray,
48+
startDate: startDate,
49+
endDate: params.end
50+
};
51+
const cacheKey = generateQueryCacheKey(TABLE, queryFilters);
3052

31-
const validationErrors = validateRequiredParams(params, requiredParams);
32-
if (validationErrors) {
33-
sendValidationError(res, validationErrors);
53+
// Check cache first
54+
const cachedResult = getCachedQueryResult(cacheKey);
55+
if (cachedResult) {
56+
res.statusCode = 200;
57+
res.end(JSON.stringify(cachedResult));
3458
return;
3559
}
3660

37-
// Preprocess parameters and get technology array
38-
const { params: processedParams, techArray } = await preprocessParams(firestore, params, TABLE);
39-
const data = [];
61+
// Build optimized query
62+
let query = firestore.collection(TABLE);
4063

41-
// Query for each technology
42-
for (const technology of techArray) {
43-
let query = firestore.collection(TABLE);
64+
// Apply required filters
65+
query = query.where('geo', '==', params.geo);
66+
query = query.where('rank', '==', params.rank);
4467

45-
// Apply standard filters including version filter
46-
query = applyStandardFilters(query, processedParams, technology, techArray);
68+
// Apply technology filter efficiently
69+
if (techArray.length <= 30) {
70+
// Use 'in' operator for batch processing (Firestore limit: 30 values)
71+
query = query.where('technology', 'in', techArray);
72+
} else {
73+
// Parallel queries for >30 technologies (rare case)
74+
const queryPromises = techArray.map(async (technology) => {
75+
let individualQuery = firestore.collection(TABLE)
76+
.where('geo', '==', params.geo)
77+
.where('rank', '==', params.rank)
78+
.where('technology', '==', technology);
4779

48-
// Apply date filters
49-
query = applyDateFilters(query, processedParams);
80+
if (startDate) individualQuery = individualQuery.where('date', '>=', startDate);
81+
if (params.end) individualQuery = individualQuery.where('date', '<=', params.end);
5082

51-
// Execute query
52-
const snapshot = await query.get();
53-
snapshot.forEach(doc => {
54-
data.push(doc.data());
83+
const snapshot = await individualQuery.get();
84+
const results = [];
85+
snapshot.forEach(doc => results.push(doc.data()));
86+
return results;
5587
});
88+
89+
const results = await Promise.all(queryPromises);
90+
const data = results.flat();
91+
92+
// Cache the result
93+
setCachedQueryResult(cacheKey, data);
94+
95+
res.statusCode = 200;
96+
res.end(JSON.stringify(data));
97+
return;
5698
}
5799

58-
// Send response
100+
// Apply date filters
101+
if (startDate) query = query.where('date', '>=', startDate);
102+
if (params.end) query = query.where('date', '<=', params.end);
103+
104+
// Execute single optimized query
105+
const snapshot = await query.get();
106+
const data = [];
107+
snapshot.forEach(doc => {
108+
data.push(doc.data());
109+
});
110+
111+
// Cache the result
112+
setCachedQueryResult(cacheKey, data);
113+
114+
// Direct response without wrapper functions
59115
res.statusCode = 200;
60-
res.end(JSON.stringify(createSuccessResponse(data)));
116+
res.end(JSON.stringify(data));
61117
} catch (error) {
62-
handleControllerError(res, error, 'fetching adoption data');
118+
console.error('Error fetching adoption data:', error);
119+
res.statusCode = 500;
120+
res.end(JSON.stringify({
121+
errors: [{ error: 'Failed to fetch adoption data' }]
122+
}));
63123
}
64124
};
65125

src/controllers/categoriesController.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,37 @@
11
import { firestore } from '../utils/db.js';
2-
import { createSuccessResponse } from '../utils/helpers.js';
3-
import { applyArrayFilter, selectFields, handleControllerError } from '../utils/controllerHelpers.js';
2+
import {
3+
applyArrayFilter,
4+
selectFields,
5+
generateQueryCacheKey,
6+
getCachedQueryResult,
7+
setCachedQueryResult
8+
} from '../utils/controllerHelpers.js';
49

510
/**
6-
* List categories with optional filtering and field selection
11+
* List categories with optional filtering and field selection - Optimized version
712
*/
813
const listCategories = async (req, res) => {
914
try {
1015
const params = req.query;
1116
const isOnlyNames = params.onlyname || typeof params.onlyname === 'string';
1217
const hasCustomFields = params.fields && !isOnlyNames;
1318

19+
// Create cache key for this specific query
20+
const queryFilters = {
21+
category: params.category,
22+
onlyname: isOnlyNames,
23+
fields: params.fields
24+
};
25+
const cacheKey = generateQueryCacheKey('categories', queryFilters);
26+
27+
// Check cache first
28+
const cachedResult = getCachedQueryResult(cacheKey);
29+
if (cachedResult) {
30+
res.statusCode = 200;
31+
res.end(JSON.stringify(cachedResult));
32+
return;
33+
}
34+
1435
let query = firestore.collection('categories').orderBy('category', 'asc');
1536

1637
// Apply category filter using shared utility
@@ -34,11 +55,18 @@ const listCategories = async (req, res) => {
3455
}
3556
});
3657

37-
// Send response
58+
// Cache the result
59+
setCachedQueryResult(cacheKey, data);
60+
61+
// Direct response
3862
res.statusCode = 200;
39-
res.end(JSON.stringify(createSuccessResponse(data)));
63+
res.end(JSON.stringify(data));
4064
} catch (error) {
41-
handleControllerError(res, error, 'fetching categories');
65+
console.error('Error fetching categories:', error);
66+
res.statusCode = 500;
67+
res.end(JSON.stringify({
68+
errors: [{ error: 'Failed to fetch categories' }]
69+
}));
4270
}
4371
};
4472

src/controllers/geosController.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
import { firestore } from '../utils/db.js';
2-
import { createSuccessResponse } from '../utils/helpers.js';
3-
import { handleControllerError } from '../utils/controllerHelpers.js';
2+
import { handleControllerError, generateQueryCacheKey, getCachedQueryResult, setCachedQueryResult } from '../utils/controllerHelpers.js';
43

54
/**
65
* List all geographic locations from database
76
*/
87
const listGeos = async (req, res) => {
98
try {
9+
// Generate cache key for this query
10+
const cacheKey = generateQueryCacheKey('geos', { orderBy: 'mobile_origins' });
11+
12+
// Check cache first
13+
const cachedResult = getCachedQueryResult(cacheKey);
14+
if (cachedResult) {
15+
res.statusCode = 200;
16+
res.end(JSON.stringify(cachedResult));
17+
return;
18+
}
19+
1020
const snapshot = await firestore.collection('geos').orderBy('mobile_origins', 'desc').get();
1121
const data = [];
1222

@@ -15,8 +25,11 @@ const listGeos = async (req, res) => {
1525
data.push({ geo: doc.data().geo });
1626
});
1727

28+
// Cache the result
29+
setCachedQueryResult(cacheKey, data);
30+
1831
res.statusCode = 200;
19-
res.end(JSON.stringify(createSuccessResponse(data)));
32+
res.end(JSON.stringify(data));
2033
} catch (error) {
2134
handleControllerError(res, error, 'fetching geographic locations');
2235
}

0 commit comments

Comments
 (0)