Cosmos DB Query Playground to interaktywne środowisko do eksperymentowania z kwerendami do Cosmos DB. Jako dane testowe zawiera ono bazę produktów spożywczych wraz z informacjami o ich wartościach odżywczych. Przykładowy dokument wygląda mniej-więcej tak:
{
"food": {
"id": "14203",
"description": "Coffee, instant, regular, powder, half the caffeine",
"tags": [
{
"name": "coffee"
},
{
"name": "instant"
},
...
// pomijam resztę
],
"foodGroup": "Beverages",
"nutrients": [
{
"id": "204",
"description": "Total lipid (fat)",
"nutritionValue": 0.5,
"units": "g"
},
{
"id": "205",
"description": "Carbohydrate, by difference",
"nutritionValue": 73.18,
"units": "g"
},
...
// pomijam resztę
]
...
// pomijam resztę
}
}Posłużmy się tym “typem” danych jako przykładem dla zobrazowania, jak bardzo mogą się różnić koszty zapytania.
Załóżmy, że chcemy wybrać wszystkie produkty, które zawierają 1g lub więcej kofeiny. A zatem musimy dla każdego produkty wyszukać w liście nutrients elementu o description równym "Caffeine" oraz nutritionValue >= 1000.
Podejście “łopatologiczno-proceduralne” może nas zaprowadzić do pomysłu, żeby dodać sobie user-defined-function, która “opakuje” te warunki, powiedzmy coś takiego:
function hasHighLevelOfCaffeine(food) {
return food.nutrients.some(
n => n.description == "Caffeine" && n.nutritionValue >= 1000
)
}Wówczas główne zaptanie się “upraszcza”:
SELECT food FROM food WHERE udf.hasHighLevelOfCaffeine(food)Problem z tym zapytaniem jest jednak taki, że zżera ono sporo RU. Na potrzeby obliczeń zrobiłem sobie przykładową bazę, gdzie miałem niecałe 2000 produktów i tylko jeden z “wysokim poziomem kofeiny”. Dla takich dancyh zwykły select zeżarł ponad 600 RUs. Co gorsza - im więcej dokumentów w kolekcji, tym koszt będzie jeszcze większy. Wynika to prawdopodobnie z tego, że Cosmos nie potrafi sobie “zoptymalizować” zapytania z użyciem UDF, operującej na podkolekcji.
Spróbujmy zatem do problemu podejść inaczej. Korzystając z dokumentacji możemy spróbować użyć podzapytania - tak byśmy przecież postąpili w “zwykłym SQL-u”. (Mamy tu relację jeden-do-wielu, więc za pomocą EXISTS i podzapytania moglibyśmy to ograć.) W Cosmosie spraw wygląda podobnie, z tą różnicą, że używamy JOIN i podzapytania. Finalnie query wygląda tak:
SELECT
food
FROM food
JOIN (SELECT VALUE n FROM n IN food.nutrients
WHERE
n.description = 'Caffeine' and n.nutritionValue >= 1000)Dla wspomnianej wyżej “benchmarkowej” bazy z 2000 produktów - to zapytanie “kosztowało” tylko 2 RU. I to w zasadzie niezależnie od ilości dokumentów.
Ciekawe, prawda? :)