簡體   English   中英

使用 Knex.js 創建嵌套返回模型

[英]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 創建一個返回模型,該模型具有與父級的 id/guid 匹配的嵌套對象,以便我沒有嵌套的承諾?

簡短的回答:沒有。

使用 Knex,您可以像使用 SQL 一樣檢索數據,它是基於記錄的,而不是基於對象的,因此最接近的是使用連接來允許僅執行單個選擇來檢索具有元素的單個數組:食譜、指南、配料。 這將重復每種成分的配方和指南,您可以通過使用嵌套對象來避免這種情況。 (有關此示例,請參閱@Fazal 下面的答案。)

作為另一種選擇,您可以將成分存儲為配方表中的“blob”字段,但我認為 MySQL 不允許您創建數組字段,因此在檢索數據時,您必須進行轉換的字段到數組中。 並在將其更新到表中之前將其從 Array 轉換。 比如: storableData = JSON.stringify(arrayData)arrayData = JSON.parse(storableData)

不過,我建議還有其他一些事情可以幫助您改進代碼。 (是的,我知道,這里不是真正的問題):

  1. 將路由功能與數據處理分開。
  2. 此外,將數據操作功能與檢索分開。
  3. 使用 throw 和 .catch 來創建和處理不成功的響應。

路由、數據檢索、數據操作的分離使測試、調試和未來理解更容易,因為每個功能都有更原子的目的。

拋出/.捕獲不成功的過程條件通過允許您(大多數情況下)將單個 .catch 放入您的路由器響應處理(Hapi.js 甚至可以為您執行此 .catch ? ??)。

另外,請參閱我為記錄錯誤而添加的其他.catch.on('query-error' 。您可能想要使用不同的日志記錄機制而不是控制台。我使用 Winston。並注意.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);
  })

看..只有幾行代碼。

我創建了一個返回嵌套對象的庫,即使它具有打字稿的類型

嵌套膝關節

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

所以食譜甚至有類型

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM