{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/potega-podzapytan-w-cosmos-db","result":{"data":{"markdownRemark":{"html":"<p><a href=\"https://www.documentdb.com/sql/demo\">Cosmos DB Query Playground</a> 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:</p>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-json line-numbers\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"food\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"14203\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Coffee, instant, regular, powder, half the caffeine\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"tags\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"coffee\"</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"instant\"</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      ...\n      <span class=\"token comment\">// pomijam resztę</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"foodGroup\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Beverages\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"nutrients\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"204\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Total lipid (fat)\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"nutritionValue\"</span><span class=\"token operator\">:</span> <span class=\"token number\">0.5</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"units\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"g\"</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"205\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"description\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Carbohydrate, by difference\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"nutritionValue\"</span><span class=\"token operator\">:</span> <span class=\"token number\">73.18</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">\"units\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"g\"</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      ...\n      <span class=\"token comment\">// pomijam resztę</span>\n    <span class=\"token punctuation\">]</span>\n    ...\n    <span class=\"token comment\">// pomijam resztę</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>Posłużmy się tym \"typem\" danych jako przykładem dla zobrazowania, jak bardzo mogą się różnić koszty zapytania.</p>\n<p>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 <code class=\"language-text\">nutrients</code> elementu o <code class=\"language-text\">description</code> równym <code class=\"language-text\">\"Caffeine\"</code> oraz <code class=\"language-text\">nutritionValue >= 1000</code>.</p>\n<p>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:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-javascript line-numbers\"><code class=\"language-javascript\"><span class=\"token keyword\">function</span> <span class=\"token function\">hasHighLevelOfCaffeine</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">food</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> food<span class=\"token punctuation\">.</span>nutrients<span class=\"token punctuation\">.</span><span class=\"token function\">some</span><span class=\"token punctuation\">(</span>\n    <span class=\"token parameter\">n</span> <span class=\"token operator\">=></span> n<span class=\"token punctuation\">.</span>description <span class=\"token operator\">==</span> <span class=\"token string\">\"Caffeine\"</span> <span class=\"token operator\">&amp;&amp;</span> n<span class=\"token punctuation\">.</span>nutritionValue <span class=\"token operator\">>=</span> <span class=\"token number\">1000</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>Wówczas główne zaptanie się \"upraszcza\":</p>\n<div class=\"gatsby-highlight\" data-language=\"sql\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-sql line-numbers\"><code class=\"language-sql\"><span class=\"token keyword\">SELECT</span> food <span class=\"token keyword\">FROM</span> food <span class=\"token keyword\">WHERE</span> udf<span class=\"token punctuation\">.</span>hasHighLevelOfCaffeine<span class=\"token punctuation\">(</span>food<span class=\"token punctuation\">)</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span></span></pre></div>\n<p>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.</p>\n<p>Spróbujmy zatem do problemu podejść inaczej. Korzystając z <a href=\"https://docs.microsoft.com/pl-pl/azure/cosmos-db/sql-query-subquery\">dokumentacji</a> 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ą <code class=\"language-text\">EXISTS</code> i podzapytania moglibyśmy to ograć.) W Cosmosie spraw wygląda podobnie, z tą różnicą, że używamy <code class=\"language-text\">JOIN</code> i podzapytania. Finalnie query wygląda tak:</p>\n<div class=\"gatsby-highlight\" data-language=\"sql\"><pre style=\"counter-reset: linenumber NaN\" class=\"language-sql line-numbers\"><code class=\"language-sql\"><span class=\"token keyword\">SELECT</span>\nfood\n<span class=\"token keyword\">FROM</span> food\n<span class=\"token keyword\">JOIN</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">SELECT</span> <span class=\"token keyword\">VALUE</span> n <span class=\"token keyword\">FROM</span> n <span class=\"token operator\">IN</span> food<span class=\"token punctuation\">.</span>nutrients\n<span class=\"token keyword\">WHERE</span>\n\tn<span class=\"token punctuation\">.</span>description <span class=\"token operator\">=</span> <span class=\"token string\">'Caffeine'</span> <span class=\"token operator\">and</span> n<span class=\"token punctuation\">.</span>nutritionValue <span class=\"token operator\">>=</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span></code><span aria-hidden=\"true\" class=\"line-numbers-rows\" style=\"white-space: normal; width: auto; left: 0;\"><span></span><span></span><span></span><span></span><span></span><span></span></span></pre></div>\n<p>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.</p>\n<p>Ciekawe, prawda? :)</p>","excerpt":"Cosmos DB Query Playground to interaktywne środowisko do eksperymentowania z kwerendami do Cosmos DB. Jako dane testowe zawiera ono bazę produktów spożywczych…","frontmatter":{"date":"30 January, 2020","path":"/blog/potega-podzapytan-w-cosmos-db","title":"Potęga podzapytań w Cosmos DB"},"fields":{"readingTime":{"text":"2 min read"}}}},"pageContext":{}},"staticQueryHashes":["3649515864","63159454"]}