使用 Knex.js 创建嵌套 return 模型
Create a nested return model with Knex.js
我正在使用 Knex.js 在 Hapi.js 路径中查询 MySQL 数据库。以下代码有效,但需要嵌套查询:
{
path: '/recipes',
method: 'GET',
handler: (req, res) => {
const getOperation = Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.then((recipes) => {
if (!recipes || recipes.length === 0) {
res({
error: true,
errMessage: 'no recipes found'
});
}
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
const getOperation2 = Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
res({
count: recipes.length,
data: recipes
});
});
});
}
}
有没有办法用 Knex.js 创建一个 return 模型,它的嵌套对象与父对象的 id/guid 相匹配,这样我就没有嵌套的承诺?
简答:否
使用 Knex,您可以像使用 SQL 一样检索数据,它是基于记录的,而不是基于对象的,所以最接近的方法是使用连接来允许只做一个select 检索包含以下元素的单个数组:食谱、指南、配料。这将重复每种成分的配方和指南,您可以通过使用嵌套对象来避免这种情况。 (有关此示例,请参阅下面@Fazal 的回答。)
作为另一种选择,您可以将成分存储为食谱 table 中的 'blob' 字段,但我认为 MySQL 不允许您创建数组字段,因此在检索数据时,您必须将字段转换为数组。并在将其更新为 table 之前将其从 Array 转换。喜欢:storableData = JSON.stringify(arrayData)
和 arrayData = JSON.parse(storableData)
不过,我还建议您做一些其他事情来帮助您改进代码。 (是的,我知道,这里不是真正的问题):
- 将路由功能与数据处理分开。
- 此外,将数据操作功能与检索分开。
- 使用 throw & .catch 创建和处理不成功的响应。
路由、数据检索、数据操作的分离使得测试、调试和未来的理解更容易,因为每个函数都有更原子的目的。
Throwing/.catching 不成功的进程条件使您可以(大部分时间)在路由器响应处理中放置单个 .catch(Hapi.js 甚至可以更简单地进行更全面的错误处理为你做这个 .catch???)。
另外,请参阅我为记录错误而添加的其他 .catch
和 .on('query-error'
。您可能想要使用不同的日志记录机制而不是控制台。我用温斯顿。请注意 .on('query-error'
不是 .catch。仍然会有一个 Error() 被抛出,并且必须在某处处理,这只会为您提供有关故障源的良好信息。
(抱歉,以下代码未经测试)
path: '/recipes',
method: 'GET',
handler: (req, res) => {
return getRecipeNIngredients()
.then((recipes) => {
res({
count: recipes.length,
data: recipes
});
})
.catch((ex) => {
res({
error: true,
errMessage: ex.message
});
});
};
function getRecipeNIngredients() {
let recipes = null;
return getRecipes()
.then((recipeList) => {
recipes = recipeList;
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
return getIngredients(recipeGuids);
})
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
return recipes;
})
.catch((ex) => {
console.log(".getRecipeNIngredients ERROR ex:",ex); // log and rethrow error.
throw ex;
});
};
function getRecipes() {
return Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.on('query-error', function(ex, obj) {
console.log("KNEX getRecipes query-error ex:", ex, "obj:", obj);
})
.then((recipes) => {
if (!recipes || recipes.length === 0) {
throw new Error('no recipes found')
}
})
};
function getIngredients(recipeGuids) {
return Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.on('query-error', function(ex, obj) {
console.log("KNEX getIngredients query-error ex:", ex, "obj:", obj);
})
};
我希望这是有用的!
加里
您可以轻松避免嵌套查询。只需将子查询用作-
knex.select('*')
.from(function () {
this.select('*').from('recipes').limit(10).as('recipes'); // limit here
})
.leftJoin('ingredients', 'ingredients.recipe_id', 'recipes.guid')
.then((rec) => {
console.log(rec);
})
看.. 几行代码。
我创建了一个 return 嵌套对象的库,即使它具有 typescript 的类型
import * as n from 'nested-knex';
n.array(
n.type({
id: n.number("recipe.id", { id: true }),
title: n.string("recipe.title"),
ingredients: n.array(
n.type({
id: n.number("ingredients.id", { id: true }),
title: n.string("ingredients.title")
})
)
})
)
.withQuery(
knex
.from("recipes")
.innerJoin("ingredients", "recipes.guid", "ingredients.recipe")
.select()
.orderBy("rating", "desc")
.limit(10)
)
.then(recipes => {});
所以食谱甚至有类型
我正在使用 Knex.js 在 Hapi.js 路径中查询 MySQL 数据库。以下代码有效,但需要嵌套查询:
{
path: '/recipes',
method: 'GET',
handler: (req, res) => {
const getOperation = Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.then((recipes) => {
if (!recipes || recipes.length === 0) {
res({
error: true,
errMessage: 'no recipes found'
});
}
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
const getOperation2 = Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
res({
count: recipes.length,
data: recipes
});
});
});
}
}
有没有办法用 Knex.js 创建一个 return 模型,它的嵌套对象与父对象的 id/guid 相匹配,这样我就没有嵌套的承诺?
简答:否
使用 Knex,您可以像使用 SQL 一样检索数据,它是基于记录的,而不是基于对象的,所以最接近的方法是使用连接来允许只做一个select 检索包含以下元素的单个数组:食谱、指南、配料。这将重复每种成分的配方和指南,您可以通过使用嵌套对象来避免这种情况。 (有关此示例,请参阅下面@Fazal 的回答。)
作为另一种选择,您可以将成分存储为食谱 table 中的 'blob' 字段,但我认为 MySQL 不允许您创建数组字段,因此在检索数据时,您必须将字段转换为数组。并在将其更新为 table 之前将其从 Array 转换。喜欢:storableData = JSON.stringify(arrayData)
和 arrayData = JSON.parse(storableData)
不过,我还建议您做一些其他事情来帮助您改进代码。 (是的,我知道,这里不是真正的问题):
- 将路由功能与数据处理分开。
- 此外,将数据操作功能与检索分开。
- 使用 throw & .catch 创建和处理不成功的响应。
路由、数据检索、数据操作的分离使得测试、调试和未来的理解更容易,因为每个函数都有更原子的目的。
Throwing/.catching 不成功的进程条件使您可以(大部分时间)在路由器响应处理中放置单个 .catch(Hapi.js 甚至可以更简单地进行更全面的错误处理为你做这个 .catch???)。
另外,请参阅我为记录错误而添加的其他 .catch
和 .on('query-error'
。您可能想要使用不同的日志记录机制而不是控制台。我用温斯顿。请注意 .on('query-error'
不是 .catch。仍然会有一个 Error() 被抛出,并且必须在某处处理,这只会为您提供有关故障源的良好信息。
(抱歉,以下代码未经测试)
path: '/recipes',
method: 'GET',
handler: (req, res) => {
return getRecipeNIngredients()
.then((recipes) => {
res({
count: recipes.length,
data: recipes
});
})
.catch((ex) => {
res({
error: true,
errMessage: ex.message
});
});
};
function getRecipeNIngredients() {
let recipes = null;
return getRecipes()
.then((recipeList) => {
recipes = recipeList;
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
return getIngredients(recipeGuids);
})
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
return recipes;
})
.catch((ex) => {
console.log(".getRecipeNIngredients ERROR ex:",ex); // log and rethrow error.
throw ex;
});
};
function getRecipes() {
return Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.on('query-error', function(ex, obj) {
console.log("KNEX getRecipes query-error ex:", ex, "obj:", obj);
})
.then((recipes) => {
if (!recipes || recipes.length === 0) {
throw new Error('no recipes found')
}
})
};
function getIngredients(recipeGuids) {
return Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.on('query-error', function(ex, obj) {
console.log("KNEX getIngredients query-error ex:", ex, "obj:", obj);
})
};
我希望这是有用的! 加里
您可以轻松避免嵌套查询。只需将子查询用作-
knex.select('*')
.from(function () {
this.select('*').from('recipes').limit(10).as('recipes'); // limit here
})
.leftJoin('ingredients', 'ingredients.recipe_id', 'recipes.guid')
.then((rec) => {
console.log(rec);
})
看.. 几行代码。
我创建了一个 return 嵌套对象的库,即使它具有 typescript 的类型
import * as n from 'nested-knex';
n.array(
n.type({
id: n.number("recipe.id", { id: true }),
title: n.string("recipe.title"),
ingredients: n.array(
n.type({
id: n.number("ingredients.id", { id: true }),
title: n.string("ingredients.title")
})
)
})
)
.withQuery(
knex
.from("recipes")
.innerJoin("ingredients", "recipes.guid", "ingredients.recipe")
.select()
.orderBy("rating", "desc")
.limit(10)
)
.then(recipes => {});
所以食谱甚至有类型