{"id":2373,"date":"2021-02-16T14:22:37","date_gmt":"2021-02-16T22:22:37","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cosmosdb\/?p=2373"},"modified":"2021-02-16T14:22:37","modified_gmt":"2021-02-16T22:22:37","slug":"new-ways-to-use-composite-indexes","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cosmosdb\/new-ways-to-use-composite-indexes\/","title":{"rendered":"New ways to use composite indexes in Azure Cosmos DB"},"content":{"rendered":"<p>You can now use\u00a0<a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/index-policy#composite-indexes\"><span data-ccp-charstyle=\"Hyperlink\">composite indexes<\/span><\/a><span data-ccp-parastyle=\"Normal (Web)\"> in Azure Cosmos DB to optimize additional cases of the most common inefficient queries! Anytime you have a slow or high request unit (RU) query, you should consider optimizing it with a composite index.<\/span><\/p>\n<h3>Current use cases for composite indexes:<\/h3>\n<table style=\"width: 96.859%; height: 344px;\">\n<tbody>\n<tr style=\"height: 28px;\">\n<td style=\"height: 28px;\" width=\"312\"><strong>Use case<\/strong><\/td>\n<td style=\"height: 28px;\" width=\"312\"><strong>Example<\/strong><\/td>\n<\/tr>\n<tr style=\"height: 53px;\">\n<td style=\"height: 53px;\" width=\"312\">Queries that ORDER BY multiple properties<\/td>\n<td style=\"height: 53px;\" width=\"312\">\n<pre>SELECT * \r\nFROM c\r\nORDER BY c.name, c.age<\/pre>\n<\/td>\n<\/tr>\n<tr style=\"height: 53px;\">\n<td style=\"height: 53px;\" width=\"312\">Queries with an equality filter and ORDER BY<\/td>\n<td style=\"height: 53px;\" width=\"312\">\n<pre>SELECT *\r\nFROM c\r\nWHERE c.name = \u201cTim\u201d\r\nORDER BY c.age<\/pre>\n<\/td>\n<\/tr>\n<tr style=\"height: 53px;\">\n<td style=\"height: 53px;\" width=\"312\"><strong>(NEW)<\/strong> Queries with multiple equality and\/or range filters<\/td>\n<td style=\"height: 53px;\" width=\"312\">\n<pre>SELECT *\r\nFROM c\r\nWHERE c.name = \u201cTim\u201d AND c.age &gt; 18 and c._ts &gt; 100<\/pre>\n<\/td>\n<\/tr>\n<tr style=\"height: 53px;\">\n<td style=\"height: 53px;\" width=\"312\"><strong>(NEW)<\/strong> Queries with aggregates and equality filters<\/td>\n<td style=\"height: 53px;\" width=\"312\">\n<pre>SELECT SUM(c.total)\r\nFROM c\r\nWHERE c.name = \u201cTim\u201d<\/pre>\n<\/td>\n<\/tr>\n<tr style=\"height: 104px;\">\n<td style=\"height: 104px;\" width=\"312\"><strong>(NEW)<\/strong> Queries with system functions<\/td>\n<td style=\"height: 104px;\" width=\"312\">\n<pre>SELECT *\r\nFROM c\r\nWHERE c.name = \u201cTim\u201d AND CONTAINS(c.biography, \u201cdeveloper\u201d)\r\nORDER BY c.name, c.biography<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>This post looks at examples of the new use cases for composite indexes. For these tests, we\u2019ve ingested 9 million items into an Azure Cosmos DB container. Here is an example item:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/CosmosDBSampleDoc-1.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-2381\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/CosmosDBSampleDoc-1.png\" alt=\"Cosmos DB sample item\" width=\"624\" height=\"263\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/CosmosDBSampleDoc-1.png 624w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/CosmosDBSampleDoc-1-300x126.png 300w\" sizes=\"(max-width: 624px) 100vw, 624px\" \/><\/a><\/p>\n<p>Each item is a different product in an online clothing store. Multiple items can belong to the same <strong>CartId<\/strong> but each item has its own unique <strong>id<\/strong> value.<\/p>\n<p>&nbsp;<\/p>\n<h4><strong>Queries with multiple equality and range filters:<\/strong><\/h4>\n<p>Queries can now use multiple composite indexes in the same filter expression. This allows you to optimize queries that have multiple equality and range filters in the same filter expression. Remember, a single composite index can only apply to one range filter. Therefore, using multiple composite indexes can help optimize queries with multiple range filters.<\/p>\n<p><strong>Query: <\/strong><\/p>\n<pre>SELECT TOP 10 *\r\nFROM c\r\nWHERE c.BuyerState = \"TN\" and c._ts &gt; 1611947901\u00a0\r\nand c.id &gt; \"9c5e4e08-e1d2-4091-8062-78d1da832523\"<\/pre>\n<p><strong>Composite indexes:<\/strong><\/p>\n<ol>\n<li>(BuyerState ASC, _ts ASC)<\/li>\n<li>(BuyerState ASC, id ASC)<\/li>\n<\/ol>\n<p><strong>Query charge with default indexing policy: <\/strong>533.47 RUs<\/p>\n<p><strong>Query charge with both composite indexes: <\/strong>72.23 RUs<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<p>This optimization tends to have a higher impact for multiple range filters than queries with only equality filters. Composite indexes will have a bigger impact on high cardinality properties, such as <strong>_ts<\/strong> or <strong>id<\/strong>.<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<h5><strong>Queries with aggregates and an equality filters:<\/strong><\/h5>\n<p>Aggregate queries (SUM, \u00a0AVG, MIN, or MAX) can now benefit from composite indexes. Previously, composite indexes could only optimize queries with a <strong>COUNT(1)<\/strong> aggregate.<\/p>\n<pre><strong>Query: \r\n<\/strong>SELECT AVG(c._ts)\r\nFROM c \r\nWHERE c.BuyerState = \"TN\"<\/pre>\n<p><strong>\u00a0<\/strong><\/p>\n<p><strong>Composite index:\u00a0<\/strong>(BuyerState ASC, _ts ASC)<\/p>\n<p><strong>Query charge with default indexing policy:<\/strong> 186.25 RUs<\/p>\n<p><strong>Query charge with composite index: <\/strong>39.8 RUs<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<p>Queries with higher cardinality properties typically benefit more from composite indexes. For example, if the property in the <strong>AVG() <\/strong> system function was\u00a0<strong>price<\/strong>, instead of <strong>_ts<\/strong>, you might not need a composite index in the first place since price has fewer possible values than <strong>_ts<\/strong>.<\/p>\n<p>Here\u2019s an example:<\/p>\n<p><strong>Query: <\/strong><\/p>\n<pre>SELECT AVG(c.price)\r\nFROM c\r\nWHERE c.BuyerState = \"TN\"<\/pre>\n<p><strong>\u00a0<\/strong><\/p>\n<p><strong>Composite index:\u00a0<\/strong>(BuyerState ASC, price ASC)<\/p>\n<p><strong>Query charge with default indexing policy: <\/strong>5.08 RUs<\/p>\n<p><strong>Query charge with composite index: <\/strong>2.9 RUs<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<h5><strong>Queries with ORDER BY and system functions:<\/strong><\/h5>\n<p>Queries with ORDER BY and system functions can now benefit from composite indexes. When you use a high cardinality property in the system function, composite indexes may have a big impact. In fact, the impact can be so significant, that it can be worthwhile to rewrite some queries with system functions to use ORDER BY so that they can leverage composite indexes.<\/p>\n<p>&nbsp;<\/p>\n<p>Here\u2019s an example:<\/p>\n<pre><strong>Original Query: \r\n<\/strong>SELECT *\r\nFROM c\r\nWHERE c.BuyerState = \"NJ\" AND Contains (c.id, \"abc\", true)<\/pre>\n<pre><strong>Query with added ORDER BY:\r\n<\/strong>SELECT *\r\nFROM c\r\nWHERE c.BuyerState = \"NJ\" AND Contains (c.id, \"abc\", true)\r\nORDER BY c.BuyerState, c.id<\/pre>\n<p><strong>\u00a0<\/strong><\/p>\n<p><strong>Composite index: <\/strong>(BuyerState ASC, id ASC)<\/p>\n<p><strong>Query charge with default indexing policy: <\/strong>4,138.66 RUs<\/p>\n<p><strong>Query charge with composite index: <\/strong>51.3 RUs<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<p>Adding an <strong>ORDER BY<\/strong> clause for the property in a system function improves system function execution. This is broader than just composite indexes and can often help in any query with a system function on a high cardinality property. Here\u2019s an example that doesn\u2019t involve composite indexes:<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<p>Here\u2019s an example:<\/p>\n<p><strong>Original Query: <\/strong><\/p>\n<pre>SELECT *\r\nFROM c\r\nWHERE RegexMatch (c.id, \"abc\")<\/pre>\n<p><strong>Query with added ORDER BY: <\/strong><\/p>\n<pre>SELECT *\r\nFROM c\r\nWHERE RegexMatch (c.id, \"abc\")\r\nORDER BY c.id<\/pre>\n<p><strong>Query charge without ORDER BY: <\/strong>8,845.61\u00a0RUs<\/p>\n<p><strong>Query charge with ORDER BY: <\/strong>92.71 RUs<\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<p>Queries with the following system functions are most likely to see big improvements since their efficiency depends on cardinality of the property in the system function:<\/p>\n<ul>\n<li>Contains<\/li>\n<li>RegexMatch<\/li>\n<li>EndsWith<\/li>\n<\/ul>\n<p>Having query results sorted can improve the way the query engine utilizes the index for high cardinality properties. Even if a query with a system function doesn\u2019t require ORDER BY, you may be able to significantly improve by adding it, along with the relevant composite indexes.<\/p>\n<p>&nbsp;<\/p>\n<h4><strong>Monitoring index policy transformations:<\/strong><\/h4>\n<p>When you modify your indexing policy, the existing policy is automatically transformed to include the new additional indexed properties. When modifying indexing policy, there are some important points to keep in mind:<\/p>\n<ul>\n<li>Indexing policy changes do not affect read or write availability<\/li>\n<li>When you add a new index, the Cosmos DB query engine will not use the new index until the index transformation is complete<\/li>\n<li>It is safe to modify indexing policy at any time, including when in production. It is common to add composite indexes while in production.<\/li>\n<\/ul>\n<p>Additionally, it\u2019s good to keep in mind that the following factors affect index transformation time:<\/p>\n<ul>\n<li>Amount of data \u2013 The more data, the more time it takes for indexing policy transformations.<\/li>\n<li>Number of added or removed indexes \u2013 A more significant change to the indexing policy will take longer than simple change (for example, adding a single composite index).<\/li>\n<li>Available provisioned throughput \u2013 Indexing policy transformations will use RUs that are leftover from all other database operations.<\/li>\n<\/ul>\n<p>You can track indexing policy transformation progress in the Azure Portal. Once the loading bar disappears, the indexing policy transformation has been successfully applied:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/TrackIndexProgress.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-2379\" src=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/TrackIndexProgress.png\" alt=\"Tracking index progress\" width=\"851\" height=\"140\" srcset=\"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/TrackIndexProgress.png 851w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/TrackIndexProgress-300x49.png 300w, https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-content\/uploads\/sites\/52\/2021\/02\/TrackIndexProgress-768x126.png 768w\" sizes=\"(max-width: 851px) 100vw, 851px\" \/><\/a><\/p>\n<p>We look forward to using composite indexes for many additional query optimizations in the future! Stay up-to-date by reading our query troubleshooting guide when optimizing queries.<\/p>\n<h4>Learn more:<\/h4>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/index-policy#composite-indexes\" target=\"_blank\" rel=\"noopener\">Composite indexes technical documentation<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/azure\/cosmos-db\/how-to-manage-indexing-policy?tabs=dotnetv2%2Cpythonv3#composite-index\" target=\"_blank\" rel=\"noopener\">Indexing policy examples<\/a><\/li>\n<li><a href=\"https:\/\/www.azurecosmosdb.com\" target=\"_blank\" rel=\"noopener\">Get started free with Azure Cosmos DB<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>You can now use\u00a0composite indexes in Azure Cosmos DB to optimize additional cases of the most common inefficient queries! Anytime you have a slow or high RU query, you should first consider whether the query can be optimized with a composite index.<\/p>\n","protected":false},"author":12128,"featured_media":61,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[14,818,19],"tags":[],"class_list":["post-2373","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-core-sql-api","category-query","category-tips-and-tricks"],"acf":[],"blog_post_summary":"<p>You can now use\u00a0composite indexes in Azure Cosmos DB to optimize additional cases of the most common inefficient queries! Anytime you have a slow or high RU query, you should first consider whether the query can be optimized with a composite index.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/2373","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/users\/12128"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/comments?post=2373"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/2373\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media\/61"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media?parent=2373"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/categories?post=2373"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/tags?post=2373"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}