Couchbase N1QL - 嵌套在Nest

时间:2017-02-13 05:47:14

标签: couchbase n1ql

嵌套在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;

但:
显然嵌套的巢遗失了 2.退回的订单不正确 - 首先是饮料巢而不是最后一个 (3.由于我也使用Sync Gateway - 它会返回每个文档的所有&#34; _sync&#34;字段 - 无法弄清楚如何在每个文档中省略此内容。)

更新1:适应性解决方案
注意:我应该在上面指出 main 不能保存 ingredientIds

基于杰拉德斯&#39;在下面的有用输入中,我添加了一个跟踪每家餐厅密钥的文档,例如:

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

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

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&#39;下面的第二个解决方案也看起来不错 - 不幸的是它不适用于我的情况,因为这个查询要求通过配料找到主电源;为了我的需要,主要可以存在,没有任何成分。 编辑:&gt;他提出了另一个解决方案。见2.

更新2:最终解决方案

所以,再次,与杰拉德斯&#39;帮助我有一个解决方案,不需要额外的文档来跟踪密钥:

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
;

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

只需1个密钥,我就可以提取所有相关文档 - 即使他们不知道直接的父母&#39;。 谢谢geraldss。

1 个答案:

答案 0 :(得分:1)

如果主电源包含ingredientIds:

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
;

编辑:已更新,其中包含未被任何成分引用的主电源。

如果主电源不包含ingredientIds:

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
;