UNPKG

usda-food-data-api-server

Version:

USDA Food Data API server for self-hosted API access to the USDA Food Data database

143 lines (133 loc) 4 kB
import mongoose from "mongoose"; import { populateAll, sanitize } from "./dbhelper.js"; /** * Key are USDA API `dataType` values, values are the corresponding Schema */ const translation: { [key: string]: string } = { Foundation: "FoundationFoodItem", "SR Legacy": "SRLegacyFoodItem", Branded: "BrandedFoodItem", Survey: "SurveyFoodItem" }; const rootFoodTypes = Object.values(translation); /** * Given an nutrients api query argument, filters the nutrients returned * to the given nutrient IDs. * @param doc The document to be filtered * @param nutrients * @returns */ function cleanNutrients(doc: any, nutrients: number[]) { if (doc.foodNutrients && nutrients.length > 0) { const set = new Set(nutrients); const filtered = []; for (let n in doc.foodNutrients) { let fn = doc.foodNutrients[n]; if (set.has(fn.nutrient.id)) { filtered.push(doc.foodNutrients[n]); } } doc.foodNutrients = filtered; return doc; } else { return doc; } } /** * Given a dataType api query argument, returns the Schemas to use for search. * @param dataType dataType api query argument * @returns A list of Schema to use for this query */ function getFoodTypes(dataType: string[]) { dataType = dataType.filter((str) => str.trim().length > 0); if (dataType.length === 0) { return rootFoodTypes; } const foodTypes: string[] = []; dataType.forEach((str) => { if (translation[str]) { foodTypes.push(translation[str]); } }); return foodTypes; } /** * FoodQuery which allows fields to be missing */ export interface FoodQuery { format?: string; dataType?: string[]; nutrients?: number[]; sortBy?: string; sortOrder?: string; pageSize?: number; pageNumber?: number; } /** * FoodQuery that promises everything is defined */ export interface DefinedFoodQuery { format: string; dataType: string[]; nutrients: number[]; sortBy: string; sortOrder: string; pageSize: number; pageNumber: number; } /** * A DefinedFoodQuery defining the API query defaults */ export const defaultFoodQuery: DefinedFoodQuery = { format: "json", dataType: Object.keys(translation), nutrients: [], sortBy: "_id", sortOrder: "desc", pageSize: 50, pageNumber: 0 }; /** * Given a MongoDB filter and API query arguments, finds the items requested. * * @param filter A valid MongoDB filter * @param query The API Query arguments * @returns */ export async function getFood( filter: { [key: string]: any }, query: FoodQuery = {} ) { const results: mongoose.Document[] = []; const filterNutrients = query.nutrients; const foodTypes = getFoodTypes(query.dataType || []); const options: DefinedFoodQuery = { ...defaultFoodQuery, ...query }; for (let f in foodTypes) { const model = mongoose.model(foodTypes[f]); const sort = options.sortOrder.toLowerCase() === "asc"; const docs = await model.find( filter, {}, { sort: { [options.sortBy]: sort ? 1 : -1 }, limit: options.pageSize, skip: Math.max(0, options.pageNumber) * options.pageSize } ); let tmp = await Promise.all( docs.map(async (doc) => { await populateAll(doc, model); let json: any = sanitize(doc.toJSON()); if (filterNutrients) { json = cleanNutrients(json, query.nutrients || []); } json.type = foodTypes[f]; return json; }) ); tmp.forEach((t) => results.push(t)); } return results; }