使用 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)

不过,我还建议您做一些其他事情来帮助您改进代码。 (是的,我知道,这里不是真正的问题):

  1. 将路由功能与数据处理分开。
  2. 此外,将数据操作功能与检索分开。
  3. 使用 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 的类型

Nested Knex

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 => {});

所以食谱甚至有类型