Skip to content

Commit 777aaa9

Browse files
authored
v1.1.3 - Performance Improvements & Random Count (#38)
* random * 1.1.3 * docs * cleanup
1 parent 7fe7ec0 commit 777aaa9

File tree

14 files changed

+149
-148
lines changed

14 files changed

+149
-148
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jetsetradio-api",
3-
"version": "1.1.2",
3+
"version": "1.1.3",
44
"description": "A Data Provider relating to the JSR/JSRF universe",
55
"type": "module",
66
"main": "src/app.js",

src/app.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dotenv from "dotenv";
44
dotenv.config();
55

66
import LOGGER from "./utils/logger.js";
7+
import {connectToDb} from "./config/db.js";
78
import MiddlewareManager from "./managers/MiddlewareManager.js";
89

910
const middlewareManager = new MiddlewareManager();
@@ -14,12 +15,15 @@ const baseUrl = process.env.BASE_URL;
1415

1516
middlewareManager.setMiddleware(app);
1617

17-
app.listen(PORT || 8080, () => {
18-
LOGGER.info(`JSR-API Listening on port ${PORT}`);
18+
(async () => {
19+
await connectToDb();
20+
app.listen(PORT || 8080, () => {
21+
LOGGER.info(`JSR-API Listening on port ${PORT}`);
1922

20-
// Ping App every 10 minutes
21-
setInterval(async () => {
22-
const res = await axios.get(`${baseUrl}/health`);
23-
console.log(`App Ping - ${baseUrl}. Status: ${res.data.message}`);
24-
}, 600000);
25-
});
23+
// Ping App every 10 minutes
24+
setInterval(async () => {
25+
const res = await axios.get(`${baseUrl}/health`);
26+
console.log(`App Ping - ${baseUrl}. Status: ${res.data.message}`);
27+
}, 600000);
28+
});
29+
})();

src/config/db.js

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,43 @@ import {ObjectId} from "mongodb";
33
import dotenv from "dotenv";
44
dotenv.config();
55

6-
import LOGGER from "../utils/logger.js";
76
import Constants from "../constants/dbConstants.js";
7+
import LOGGER from "../utils/logger.js";
88

99
const {CORE_DB} = Constants;
1010

11-
const buildMongoUri = () => {
12-
const user = process.env.MONGO_USER;
13-
const password = process.env.MONGO_PASS;
14-
const clusterName = process.env.MONGO_CLUSTER;
15-
const domainName = process.env.MONGO_DOMAIN;
16-
if (!user) {
17-
return LOGGER.error(`Invalid admin user found while building mongo uri`);
18-
}
19-
if (!password) {
20-
return LOGGER.error(
21-
`Invalid admin password found while building mongo uri`
22-
);
23-
}
24-
if (!clusterName) {
25-
return LOGGER.error(`Invalid cluster name found while building mongo uri`);
11+
/* Database Connections */
12+
const client = new MongoClient(process.env.MONGO_URI);
13+
let isConnected = false;
14+
15+
export const connectToDb = async () => {
16+
if (isConnected) {
17+
return client; // reuse existing connection
2618
}
27-
if (!domainName) {
28-
return LOGGER.error(`Invalid domain name found while building mongo uri`);
19+
try {
20+
await client.connect();
21+
isConnected = true;
22+
LOGGER.info("✅ Connected to MongoDB");
23+
return client;
24+
} catch (err) {
25+
LOGGER.error("❌ Failed to connect to MongoDB", err);
26+
throw err;
2927
}
30-
return `mongodb+srv://${user}:${password}@${clusterName}.${domainName}?retryWrites=true&w=majority`;
3128
};
3229

33-
const client = new MongoClient(buildMongoUri());
34-
35-
/* Database Connections */
3630
export const performAdminAction = async (action, username) => {
3731
try {
38-
await client.connect();
32+
await connectToDb();
3933
return await action(client, CORE_DB, "Admin", username);
4034
} catch (err) {
4135
console.error(err);
4236
return err;
43-
} finally {
44-
await client.close();
4537
}
4638
};
4739

4840
export const performDBAction = async (action, dbName, collection, id, qps) => {
4941
try {
50-
await client.connect();
42+
await connectToDb();
5143
const queryActions = [getSortQuery(qps), getLimitSize(qps)];
5244
return await action(
5345
client,
@@ -60,20 +52,16 @@ export const performDBAction = async (action, dbName, collection, id, qps) => {
6052
} catch (err) {
6153
console.error(err);
6254
return err;
63-
} finally {
64-
await client.close();
6555
}
6656
};
6757

6858
export const listCollections = async (dbName) => {
6959
try {
70-
await client.connect();
60+
await connectToDb();
7161
return await client.db(dbName).listCollections().toArray();
7262
} catch (err) {
7363
console.error(err);
7464
return err;
75-
} finally {
76-
await client.close();
7765
}
7866
};
7967

src/config/dbActions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ export const Actions = {
66
fetchWithQuery: async (client, dbName, collectionName, id, qps, queryActions) => { return await client.db(dbName).collection(collectionName).find(qps).sort(queryActions[0]).limit(queryActions[1]).toArray() },
77
fetchById: async (client, dbName, collectionName, id) => { return await client.db(dbName).collection(collectionName).findOne({ _id: new ObjectId(id) }) },
88
fetchAdmin: async (client, dbName, collectionName, username) => { return await client.db(dbName).collection(collectionName).findOne({ username: username }) },
9-
fetchRandom: async (client, dbName, collectionName) => { return await client.db(dbName).collection(collectionName).aggregate([{ $sample: { size: 1 } }]).toArray(); }
9+
fetchRandom: async (client, dbName, collectionName, count) => { return await client.db(dbName).collection(collectionName).aggregate([{ $sample: { size: count } }]).toArray(); }
1010
}

src/controllers/characterController.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import Constants from "../constants/dbConstants.js";
22
import {Actions} from "../config/dbActions.js";
33
import {performDBAction} from "../config/db.js";
44
import {sortObjects} from "../utils/utility.js";
5+
import {fetchRandom} from "./utilController.js";
56
import LOGGER from "../utils/logger.js";
67

78
const Character = "Character";
8-
const {JSR_DB, JSRF_DB, BRC_DB, gameMap} = Constants;
9+
const {JSR_DB, JSRF_DB, BRC_DB} = Constants;
910

1011
export const getAllCharacters = async (req, res) => {
1112
try {
@@ -24,13 +25,7 @@ export const getAllCharacters = async (req, res) => {
2425

2526
export const getRandomCharacter = async (req, res) => {
2627
try {
27-
const games = [JSR_DB, JSRF_DB, BRC_DB];
28-
const userSelectedGame = req?.query?.game;
29-
let game =
30-
gameMap[userSelectedGame] ||
31-
games[Math.floor(Math.random() * games.length)];
32-
const randomCharacter = await fetchRandomCharacter(req, game);
33-
res.json(randomCharacter[0]);
28+
res.send(await fetchRandom(req, Character));
3429
} catch (err) {
3530
LOGGER.error(`Could not fetch random character`, err);
3631
res.status(500).json({error: "Failed to fetch random character"});
@@ -130,13 +125,3 @@ export const fetchCharacters = async (req, dbName) => {
130125
req?.query
131126
);
132127
};
133-
134-
export const fetchRandomCharacter = async (req, dbName) => {
135-
return await performDBAction(
136-
Actions.fetchRandom,
137-
dbName,
138-
Character,
139-
null,
140-
req?.query
141-
);
142-
};

src/controllers/collectibleController.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Actions} from "../config/dbActions.js";
33
import {performDBAction} from "../config/db.js";
44
import {sortObjects} from "../utils/utility.js";
55
import LOGGER from "../utils/logger.js";
6+
import {fetchRandom} from "./utilController.js";
67

78
const Collectible = "Collectible";
89
const {BRC_DB} = Constants;
@@ -27,8 +28,7 @@ export const getCollectibles = async (req, res) => {
2728

2829
export const getRandomCollectible = async (req, res) => {
2930
try {
30-
const randomCollectible = await fetchRandomCollectible(req, BRC_DB);
31-
res.json(randomCollectible[0]);
31+
res.send(await fetchRandom(req, Collectible, BRC_DB));
3232
} catch (err) {
3333
LOGGER.error(`Could not fetch random collectible`, err);
3434
res.status(500).json({error: "Failed to fetch random collectible"});
@@ -60,12 +60,6 @@ export const fetchCollectibles = async (req) => {
6060
return await performDBAction(Actions.fetchAll, BRC_DB, Collectible, null);
6161
};
6262

63-
export const fetchRandomCollectible = async (req, dbName) => {
64-
return await performDBAction(
65-
Actions.fetchRandom,
66-
dbName,
67-
Collectible,
68-
null,
69-
req?.query
70-
);
63+
export const fetchRandomCollectible = async (req, dbName, count) => {
64+
return await performDBAction(Actions.fetchRandom, dbName, Collectible, count);
7165
};

src/controllers/locationController.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import Constants from "../constants/dbConstants.js";
22
import {Actions} from "../config/dbActions.js";
33
import {performDBAction} from "../config/db.js";
44
import {sortObjects} from "../utils/utility.js";
5+
import {fetchRandom} from "./utilController.js";
56
import LOGGER from "../utils/logger.js";
67

78
const Location = "Location";
89
const Level = "Level";
9-
const {JSR_DB, JSRF_DB, BRC_DB, gameMap} = Constants;
10+
const {JSR_DB, JSRF_DB, BRC_DB} = Constants;
1011

1112
export const getLocations = async (req, res) => {
1213
try {
@@ -25,13 +26,7 @@ export const getLocations = async (req, res) => {
2526

2627
export const getRandomLocation = async (req, res) => {
2728
try {
28-
const games = [JSR_DB, JSRF_DB, BRC_DB];
29-
const userSelectedGame = req?.query?.game;
30-
let game =
31-
gameMap[userSelectedGame] ||
32-
games[Math.floor(Math.random() * games.length)];
33-
const randomLocation = await fetchRandomLocation(req, game);
34-
res.json(randomLocation[0]);
29+
res.send(await fetchRandom(req, Location));
3530
} catch (err) {
3631
LOGGER.error(`Could not fetch random location`, err);
3732
res.status(500).json({error: "Failed to fetch random location"});
@@ -152,13 +147,3 @@ export const fetchLocations = async (req, dbName) => {
152147
req?.query
153148
);
154149
};
155-
156-
export const fetchRandomLocation = async (req, dbName) => {
157-
return await performDBAction(
158-
Actions.fetchRandom,
159-
dbName,
160-
Location,
161-
null,
162-
req?.query
163-
);
164-
};

src/controllers/songController.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import Constants from "../constants/dbConstants.js";
22
import {Actions} from "../config/dbActions.js";
33
import {performDBAction} from "../config/db.js";
44
import {sortObjects} from "../utils/utility.js";
5+
import {fetchRandom} from "./utilController.js";
56
import LOGGER from "../utils/logger.js";
67

78
const Song = "Song";
8-
const {JSR_DB, JSRF_DB, BRC_DB, gameMap} = Constants;
9+
const {JSR_DB, JSRF_DB, BRC_DB} = Constants;
910

1011
export const getSongs = async (req, res) => {
1112
try {
@@ -24,13 +25,7 @@ export const getSongs = async (req, res) => {
2425

2526
export const getRandomSong = async (req, res) => {
2627
try {
27-
const games = [JSR_DB, JSRF_DB, BRC_DB];
28-
const userSelectedGame = req?.query?.game;
29-
let game =
30-
gameMap[userSelectedGame] ||
31-
games[Math.floor(Math.random() * games.length)];
32-
const randomSong = await fetchRandomSong(req, game);
33-
res.json(randomSong[0]);
28+
res.send(await fetchRandom(req, Song));
3429
} catch (err) {
3530
LOGGER.error(`Could not fetch random song`, err);
3631
res.status(500).json({error: "Failed to fetch random song"});
@@ -152,13 +147,3 @@ export const fetchSongs = async (req, dbName) => {
152147
req?.query
153148
);
154149
};
155-
156-
export const fetchRandomSong = async (req, dbName) => {
157-
return await performDBAction(
158-
Actions.fetchRandom,
159-
dbName,
160-
Song,
161-
null,
162-
req?.query
163-
);
164-
};

src/controllers/utilController.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {performDBAction} from "../config/db.js";
2+
import {Actions} from "../config/dbActions.js";
3+
import Constants from "../constants/dbConstants.js";
4+
import LOGGER from "../utils/logger.js";
5+
6+
const {JSR_DB, JSRF_DB, BRC_DB, gameMap} = Constants;
7+
8+
/* Helper Functions to support all other Controllers */
9+
export const fetchRandom = async (req, resource, game) => {
10+
try {
11+
const games = [JSR_DB, JSRF_DB, BRC_DB];
12+
const selectedGame = req?.query?.game;
13+
const count = Number(req?.query?.count);
14+
const safeCount = Number.isFinite(count) && count > 0 ? count : 1;
15+
16+
/* if a game is provided */
17+
if (game || selectedGame) {
18+
const dbName = game || gameMap[selectedGame];
19+
return await performDBAction(
20+
Actions.fetchRandom,
21+
dbName,
22+
resource,
23+
safeCount
24+
);
25+
}
26+
27+
/* If no game is provided, select a random characters from a random games */
28+
const shuffled = [...games].sort(() => Math.random() - 0.5);
29+
let remaining = safeCount;
30+
const promises = [];
31+
32+
for (const dbName of shuffled) {
33+
if (remaining <= 0) break;
34+
const take = Math.min(
35+
remaining,
36+
Math.floor(Math.random() * remaining) + 1
37+
);
38+
remaining -= take;
39+
promises.push(
40+
performDBAction(Actions.fetchRandom, dbName, resource, take)
41+
);
42+
}
43+
44+
const results = await Promise.all(promises);
45+
return results.flat();
46+
} catch (err) {
47+
LOGGER.error(`Error fetching random ${resource} from game ${game}`, err);
48+
return [];
49+
}
50+
};

0 commit comments

Comments
 (0)