2017-02-13 5 views
1

Nest in einem Nest. Ich habe mein Bedürfnis in das folgende Restaurant Beispiel angepasst:Couchbase N1QL - Nest in einem Nest

gewünschte Ausgabe:

{ 
    "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 
    } 
} 

Beispiel Docs:

// 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.... } 

Der nächstgelegene ich ohne Fehler bekommen kann, ist:

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

Aber:
1. Ob das verschachtelte Nest fehlt
2. Die zurückgegebene Bestellung ist falsch - die Getränke Nest kommt zuerst anstelle der letzten
(3. Da ich Sync-Gateway auch mit - es gibt alle „_sync“ Felder mit jedem doc - kann nicht herausfinden, wie diese auf jedem doc wegzulassen)

UPDATE 1: angepasste Lösung
NB:. Ich sollte oben angegeben haben, dass ein Haupt nicht ingentantIds halten kann.

Basierend auf geraldss' v hilfreich Eingang unten, habe ich eine doc, die Schlüssel pro Restaurant, zB Tracks:

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

ich diese hinzugefügt geraldss' auf die erste Lösung unten als JOIN, um es verfügbar Abfrage, zB:

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' zweite Lösung unten auch sieht gut aus - leider wird es nicht für meinen Fall arbeiten, wie diese Abfrage erfordert, dass Netz über Zutaten zu finden ist; für meine Bedürfnisse kann ein Haupt ohne Bestandteile bestehen. EDIT:> er kam mit einer anderen Lösung. Siehe 2.

UPDATE 2: ENDLöSUNG

Also, noch einmal, mit geraldss' Hilfe, die ich eine Lösung, die keine zusätzlichen doc erfordert Schlüssel zu verfolgen:

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 - Die Zeilen AND meta().id NOT LIKE '_sync:%' sind nur erforderlich, wenn Sync Gateway verwendet wird.

Mit nur einem Schlüssel kann ich alle zugehörigen Dokumente ziehen - auch wenn sie dem unmittelbaren "Eltern" unbekannt sind.
Danke Geraldss.

Antwort

0

Wenn das Netz ingredientIds enthalten:

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 
; 

EDIT: aktualisiert Netz, um nicht durch irgendwelche Zutaten verwiesen.

Wenn das Netz nicht ingredientIds enthalten:

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 
; 
+0

Hallo geraldss, Vielen Dank für Ihre Lösungen! Ich nahm Ihre erste Lösung und fügte ein neues Indizierungsdokument hinzu. Siehe oben. Beim Versuch, Ihre zweite Lösung Zutaten kamen als null zurück. Vielleicht liegt das Problem in der Zeile "WHERE" restaurant1 "IN main.restaurantIds"? Wenn ich diese Zeile durch 'WHERE ingredient.type =" ingredient "ersetze, fängt es an zu arbeiten, obwohl ich denke, dass weitere Bedingungen erforderlich sein könnten. Allerdings, auch wenn diese Abfrage perfektioniert wurde, ist es nicht für meinen Fall geeignet, da diese Abfrage erfordert, dass Hauptleitungen über Bestandteile gefunden werden. Für mich kann ein Main ohne Zutaten existieren. – Giles

+0

Sie können eine UNION zu dieser Unterabfrage hinzufügen, um Hauptleitungen aufzunehmen, die keine Zutaten enthalten. – geraldss

+0

Hallo @geraldss. OK. Basierend auf Ihrem Kommentar habe ich versucht, eine Abfrage mit UNION ohne Erfolg zu erstellen. Kannst du mich in die richtige Richtung lenken? Es wäre viel besser, wenn ich vermeiden könnte, ein zusätzliches Dokument zu verwenden, nur um die Schlüssel zu verfolgen. – Giles