Couchbase N1QL - 嵌套嵌套

Couchbase N1QL - Nest within a Nest

嵌套在 Nest 中。 我已将我的需要应用到以下餐厅示例中:

期望的输出:

{
  "restaurant": {
    "id": "restaurant1",
    "name": "Foodie",
    "mains": [                          // < main nested in restaurant
      {
        "id": "main1",
        "title": "Steak and Chips",
        "ingredients": [                // < ingredient nested in main (...which is nested in restaurant)
          {
            "id": "ingredient1",
            "title": "steak"
          },
          {
            "id": "ingredient2",
            "title": "chips"
          }
        ]
      },
      {
        "id": "main2",
        "title": "Fish and Chips",
        "ingredients": [
          {
            "id": "ingredient3",
            "title": "fish"
          },
          {
            "id": "ingredient2",
            "title": "chips"
          }
        ]
      }
    ]
    "drinks": [ you get the idea ]     // < drink nested in restaurant
  }
}

示例文档:

// RESTAURANTS
{
  "id": "restaurant1",
  "type": "restaurant",
  "name": "Foodie",
  "drinkIds": [ "drink1", "drink2" ],
  "mainIds: [ "main1", "main2" ]
},
// MAINS
{
  "id": "main1",
  "type": "main",
  "restaurantIds": [ "restaurant1" ],
  "title": "Steak and Chips"
},
{
  "id": "main2",
  "type": "main",
  "restaurantIds": [ "restaurant1" ],
  "title": "Fish and Chips"
},
// INGREDIENTS
{
  "id": "ingredient1",
  "type": "ingredient",
  "title": "steak",
  "mainIds": [ "main1" ]
},
{
  "id": "ingredient2",
  "type": "ingredient",
  "title": "chips",
  "mainIds": [ "main1", "main2" ]
},
{
  "id": "ingredient3",
  "type": "ingredient",
  "title": "fish",
  "mainIds": [ "main2" ]
},
// DRINKS
{ you get the idea.... }

我能准确无误地得到最接近的是:

SELECT restaurant, mains, drinks
FROM default restauant USE KEYS "restaurant1"
NEST default mains ON KEYS restaurant.mainIds
NEST default drinks ON KEYS restaurant.drinkIds;

但是:
1.明显嵌套nest不见了
2. 返回的顺序不正确-饮料巢在前而不是在后
(3. 因为我也在使用 Sync Gateway - 它 returns 每个文档的所有“_sync”字段 - 不知道如何在每个文档上省略它。)

更新 1:适应的解决方案
注意:我应该在上面指定 main 不能容纳 ingredientIds

根据下面 geraldss 的有用输入,我添加了一个跟踪每个餐厅钥匙的文档,例如:

{ 
  "id": "restaurant1-JoeBloggs",
  "dinerId": "JoeBloggs",
  "ingredientIds": [ "ingredient1", "ingredient2" "ingredient3" ],
  "mainOrdered": [ "main1" ],    // < other potential uses...
  "drinkOrdered": [ "drink2" ]
}

我将其作为 JOIN 添加到下面 geraldss 的第一个解决方案中,以使其可用于查询,例如:

SELECT *
FROM
(
SELECT
    r.*,
    (
        SELECT
            drink.*
        FROM default AS drink
        USE KEYS r.drinkIds
    ) AS drinks,
    (
        SELECT
            main.*,
            (
                SELECT
                  ingredient.*
                  FROM default AS ingredient
                  USE KEYS keyIndex.ingredientIds   // < keyIndex
                  WHERE ingredient.mainId=main.id
            ) AS ingredients
        FROM default AS main
        USE KEYS r.mainIds
    ) AS mains
FROM default AS r
USE KEYS "restaurant1"
JOIN default AS keyIndex ON KEYS "restaurant1-JoeBloggs"   // < keyIndex JOINed
) AS restaurant
;

geraldss 下面的第二个解决方案看起来也不错——不幸的是,它不适用于我的情况,因为此查询要求通过配料找到电源;为了我的需要,一个主菜可以没有任何成分存在。 编辑: > 他想出了另一个解决方案。参见 2.

更新 2:最终解决方案

所以,再次,在 geraldss 的帮助下,我有一个不需要额外文档来跟踪密钥的解决方案:

SELECT *
FROM
  (
  SELECT
    restaurant.id, restaurant.name,
    (
      SELECT
      drink.id, drink.title
      FROM default AS drink
      USE KEYS restaurant.drinkIds
    ) 
    AS drinks,
    (
      SELECT
      main.id, main.title,
      ARRAY_AGG({"title":ingredient.title, "id":ingredient.id}) AS ingredients
      FROM default AS ingredient
      JOIN default AS main
      ON KEYS ingredient.mainIds
      WHERE main.restaurantId="restaurant1"
      AND meta().id NOT LIKE '_sync:%'                          // < necessary only if using Sync Gateway
      GROUP BY main

      UNION ALL

      SELECT
      mainWithNoIngredients.id, mainWithNoIngredients.title
      FROM default AS mainWithNoIngredients
      UNNEST mainWithNoIngredients AS foo                       // < since this is being flattened the AS name is irrelevant
      WHERE mainWithNoIngredients.restaurantId="restaurant1" 
      AND mainWithNoIngredients.type="main"
      AND meta().id NOT LIKE '_sync:%'                          // < necessary only if using Sync Gateway
      AND META(mainWithNoIngredients).id NOT IN 
      (
        SELECT RAW mainId
        FROM default AS ingredient
      )
    ) 
    AS mains

  FROM default AS restaurant
  USE KEYS "restaurant1"
  ) 
  AS restaurant
;

NB - AND meta().id NOT LIKE '_sync:%' 行仅在使用 Sync Gateway 时才需要。

只需一个键,我就可以提取所有相关文档 - 即使它们是直接未知的 'parent'。
谢谢 geraldss。

如果主菜包含成分ID:

SELECT *
FROM
(
SELECT
    r.*,
    (
        SELECT
            drink.*
        FROM default AS drink
        USE KEYS r.drinkIds
    ) AS drinks,
    (
        SELECT
            main.*,
            (
                SELECT
                    ingredient.*
                FROM default AS ingredient
                USE KEYS main.ingredientIds
            ) AS ingredients
        FROM default AS main
        USE KEYS r.mainIds
    ) AS mains
FROM default AS r
USE KEYS "restaurant1"
) AS restaurant
;

编辑:更新以包括未被任何成分引用的电源。

如果主菜不包含成分ID:

SELECT *
FROM
(
SELECT
    r.*,
    (
        SELECT
            drink.*
    FROM default AS drink
    USE KEYS r.drinkIds
    ) AS drinks,
    (
        SELECT
            main.*,
            ARRAY_AGG(ingredient) AS ingredients
        FROM default AS ingredient
        JOIN default AS main
    ON KEYS ingredient.mainIds
        WHERE "restaurant1" IN main.restaurantIds
        GROUP BY main
        UNION ALL
        SELECT
            main.*
        FROM default AS main
        WHERE "restaurant1" IN main.restaurantIds
        AND META(main).id NOT IN (
            SELECT RAW mainId
            FROM default AS ingredient
            UNNEST mainIds AS mainId
        )
    ) AS mains
FROM default AS r
USE KEYS "restaurant1"
) AS restaurant
;