Select lists of suggestions or ngrams with /api or /api/ngrams respectively, JSONify ngrams response

peterrauscher/oap-69
Peter Rauscher 2023-04-18 14:50:43 -04:00
parent c9c03d3104
commit b9d5b78a2e
4 changed files with 146 additions and 10 deletions

View File

@ -19,8 +19,9 @@ if (
) throw new DatabaseConnectionError(
"Some Postgres environment variables weren't found. Please configure them in the .env file."
);
let sslmode = process.env.POSTGRES_SSLMODE === "require";
const connection_string = `postgresql://${process.env.POSTGRES_USERNAME}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB_NAME}?ssl=${sslmode}`;
let connection_string = `postgresql://${process.env.POSTGRES_USERNAME}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB_NAME}`;
if (process.env.POSTGRES_SSLMODE === "require")
connection_string += "?ssl=true";
const db = pgp(connection_string);
module.exports = db;

View File

@ -1,6 +1,9 @@
const validate = require("../validate.js");
const { ParameterizedQuery: PQ } = require("pg-promise");
const DEFAULT_ITEM_LIMIT = 25;
const MAX_ITEM_LIMIT = 100;
const db = require("./connection.js");
async function querySuggestions(handle, threshold = 0) {
@ -18,16 +21,15 @@ async function querySuggestions(handle, threshold = 0) {
return { error: { name: error.name, message: error.message } };
});
if (result?.["error"])
return result;
if (result?.["error"]) return result;
console.log(result);
const data = {
"handle": handle,
"suggestions": result
handle: handle,
suggestions: result,
};
return data;
}
@ -35,18 +37,83 @@ async function queryNgrams(handle) {
await validate.checkHandle(handle);
const query = new PQ({
text: "SELECT * FROM oapen_suggestions.ngrams WHERE handle = $1",
text: `SELECT handle, "name", thumbnail, created_at, updated_at,
array_agg(
JSON_BUILD_OBJECT(
'ngram', ngram.ngram,
'count', ngram.count
)
) as ngrams
FROM oapen_suggestions.ngrams, UNNEST(ngrams) as ngram
WHERE handle = $1
GROUP BY handle;`,
values: [handle],
});
return db.one(query).catch((error) => {
return { error: { name: error.name, message: error.message } };
});
}
// return await db.any(query);
async function queryManySuggestions(
threshold = 0,
limit = DEFAULT_ITEM_LIMIT,
offset = 0
) {
if (threshold < 0) threshold = 0;
if (limit > validate.MAX_ITEM_LIMIT) {
limit = validate.MAX_ITEM_LIMIT;
} else if (limit < 1) {
limit = 1;
}
if (offset < 0) offset = 0;
const query = new PQ({
text: `SELECT suggestion AS handle, score
FROM oapen_suggestions.suggestions
WHERE score >= $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;`,
values: [threshold, limit, offset],
});
return db.query(query).catch((error) => {
return { error: { name: error.name, message: error.message } };
});
}
async function queryManyNgrams(limit = DEFAULT_ITEM_LIMIT, offset = 0) {
if (limit > validate.MAX_ITEM_LIMIT) {
limit = validate.MAX_ITEM_LIMIT;
} else if (limit < 1) {
limit = 1;
}
if (offset < 0) offset = 0;
const query = new PQ({
text: `SELECT handle, "name", thumbnail, created_at, updated_at,
array_agg(
JSON_BUILD_OBJECT(
'ngram', ngram.ngram,
'count', ngram.count
)
) as ngrams
FROM oapen_suggestions.ngrams, UNNEST(ngrams) as ngram
GROUP BY handle
ORDER BY created_at
LIMIT $1 OFFSET $2;
`,
values: [limit, offset],
});
return db.query(query).catch((error) => {
return { error: { name: error.name, message: error.message } };
});
}
module.exports = {
querySuggestions,
queryNgrams,
queryManySuggestions,
queryManyNgrams,
};

View File

@ -59,4 +59,67 @@ router.get("/:handle([0-9]+.[0-9]+.[0-9]+/[0-9]+)/ngrams", async (req, res) => {
}
});
router.get("/", async (req, res) => {
try {
let threshold = parseInt(req.query.threshold) || 0;
if (threshold < 0) threshold = 0;
let limit = parseInt(req.query.limit) || validate.DEFAULT_ITEM_LIMIT;
let offset = parseInt(req.query.offset) || 0;
if (limit > validate.MAX_ITEM_LIMIT) {
limit = validate.MAX_ITEM_LIMIT;
} else if (limit < 1) {
limit = 1;
}
if (offset < 0) offset = 0;
let responseData = await data.queryManySuggestions(threshold, limit, offset);
if (
responseData.error &&
responseData.error.name === pgp.errors.QueryResultError.name
) {
return res.status(404).json({ error: responseData.error.message });
} else if (responseData.error) {
return res.status(500).json(responseData);
}
res.header("Access-Control-Allow-Origin", "*");
return res.status(200).json(responseData);
} catch (e) {
return res.status(500).json({ error: "Internal server error" });
}
})
router.get("/ngrams", async (req, res) => {
try {
let limit = parseInt(req.query.limit) || validate.DEFAULT_ITEM_LIMIT;
let offset = parseInt(req.query.offset) || 0;
if (limit > validate.MAX_ITEM_LIMIT) {
limit = validate.MAX_ITEM_LIMIT;
} else if (limit < 1) {
limit = 1;
}
if (offset < 0) offset = 0;
let responseData = await data.queryManyNgrams(limit, offset);
if (
responseData.error &&
responseData.error.name === pgp.errors.QueryResultError.name
) {
return res.status(404).json({ error: responseData.error.message });
} else if (responseData.error) {
return res.status(500).json(responseData);
}
res.header("Access-Control-Allow-Origin", "*");
return res.status(200).json(responseData);
} catch (e) {
return res.status(500).json({ error: "Internal server error" });
}
})
module.exports = router;

View File

@ -7,6 +7,9 @@ class UserError extends Error {
}
}
const MAX_ITEM_LIMIT = 100;
const DEFAULT_ITEM_LIMIT = 25;
// RegEx to match formatting of handle
const handleRegExpression = new RegExp("([0-9]+.[0-9]+.[0-9]+/[0-9]+)");
@ -24,4 +27,6 @@ let checkHandle = async (handle) => {
module.exports = {
checkHandle,
MAX_ITEM_LIMIT,
DEFAULT_ITEM_LIMIT
};