{"id":1811,"date":"2014-12-16T17:51:04","date_gmt":"2014-12-16T17:51:03","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=1811"},"modified":"2025-06-13T21:11:09","modified_gmt":"2025-06-14T04:11:09","slug":"understanding-grouplevel-view-queries-compound-keys","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/","title":{"rendered":"Understanding group_level in View Queries with Compound Keys"},"content":{"rendered":"<p><span style=\"font-family: ff-meta-serif-web-pro-1, ff-meta-serif-web-pro-2, Georgia, 'Times New Roman', Times, serif;font-size: 16px;font-style: normal\">Querying Views is what views are all about. Our documentation is great and can be found here:\u00a0<\/span><a style=\"font-style: normal;font-size: 16px;font-family: ff-meta-serif-web-pro-1, ff-meta-serif-web-pro-2, Georgia, 'Times New Roman', Times, serif\" href=\"https:\/\/www.couchbase.com\/docs\/couchbase-manual-2.0\/couchbase-views.html\">https:\/\/www.couchbase.com\/docs\/couchbase-manual-2.0\/couchbase-views.html<\/a><\/p>\n<h3>Compound Keys<\/h3>\n<p>Just to illustrate how group level works, let&#8217;s use a Map function that sorts users by the latest login timestamp, converting that timestamp to an array [yyyy,MM,dd,hh,mm,ss] in what we call a &#8220;compound key&#8221; (or Array Key) as the output of the Map function:<\/p>\n<h5>Map Function<\/h5>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">function(doc, meta) {<br \/>\nif (doc.login_timestamp) {<br \/>\nemit(dateToArray(doc.login_timestamp), doc.username);<br \/>\n}<br \/>\n}<\/div>\n<\/div>\n<h5>Reduce Function<\/h5>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">_count<\/div>\n<\/div>\n<h5>Resulting Rows (with reduce=false)<\/h5>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2011,10,10,10,10,09]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,12,24,17,03,59]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2013,01,01,08,22,23]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2013,01,25,10,38,01]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;tgrall&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2013,01,25,11,02,32]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;jzablocki&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2013,02,01,11,02,32]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>First thing of note and *very* important, even though this is an array output that seems like integers from the javascript Map function, they are not, each of those Index Keys are strings, and are ordered character by character as strings, including the brackets and commas, notice that all single digits are padded with zeros in front, and that is why the order is maintained. It&#8217;s more like this, so we&#8217;ll go ahead and keep the quote characters:<\/p>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2011&#8243;,&#8221;10&#8243;,&#8221;10&#8243;,&#8221;10&#8243;,&#8221;10&#8243;,&#8221;09&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2012&#8243;,&#8221;12&#8243;,&#8221;24&#8243;,&#8221;17&#8243;,&#8221;03&#8243;,&#8221;59&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;01&#8243;,&#8221;08&#8243;,&#8221;22&#8243;,&#8221;23&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;25&#8243;,&#8221;10&#8243;,&#8221;38&#8243;,&#8221;01&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;tgrall&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;25&#8243;,&#8221;11&#8243;,&#8221;02&#8243;,&#8221;32&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;jzablocki&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;02&#8243;,&#8221;01&#8243;,&#8221;11&#8243;,&#8221;02&#8243;,&#8221;32&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">&#8220;scalabl3&#8221;<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Quick Illustrative Counter-Example for Key Ordering<\/h3>\n<p>If you had the following Map output, notice that it is sorted differently than it would be if the Int parameters were actually Int&#8217;s, in fact Index Keys are always strings.<\/p>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,&#8221;beer&#8221;,1]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">null<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,&#8221;beer&#8221;,10]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">null<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,&#8221;beer&#8221;,2]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">null<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Notice that the second &#8220;element&#8221; of the Index Key is ordered to be before the 3rd because of string compare, these are not integers. Back to the scheduled program\u2026<\/p>\n<h3>Continuing with Group Level&#8230;<\/h3>\n<p>So now, back to the list of login_timestamps in the view above, if we want to see how many people logged in during each year, I add the query parameter &#8220;group_level=1&#8221; and it splits the string on the first comma and groups the left elements together by string match, and then reduces on the group, producing the count.<\/p>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2011&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2012&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">4<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now, if we want to have a finer grain view (year, month), we add 1 to the group_level, group_level=2, which splits on the second comma and takes the left element and groups them, followed by the reduce. This results in the following with the same data:<\/p>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2011&#8243;,&#8221;10&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2012&#8243;,&#8221;12&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">3<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;02&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Moving along to the next group_level, group_level=3, will group by (yyyy,MM,dd), which is the third element of the compound key, this splits on the 3rd comma, and groups together the left element and reduces:<\/p>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2011&#8243;,&#8221;10&#8243;,&#8221;10&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2012&#8243;,&#8221;12&#8243;,&#8221;24&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;01&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;25&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">2<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;02&#8243;,&#8221;01&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In this tiny dataset, if we go to group_level=4, which in this case is by hour (yyyy,MM,dd,hh), each element is it&#8217;s own row result with being able to be grouped and reduced because they are all unique. In a larger and more frequently updated dataset you could probably group by minute and see higher _count numbers:<\/p>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2011&#8243;,&#8221;10&#8243;,&#8221;10&#8243;,&#8221;10&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2012&#8243;,&#8221;12&#8243;,&#8221;24&#8243;,&#8221;17&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;01&#8243;,&#8221;08&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;25&#8243;,&#8221;10&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;01&#8243;,&#8221;25&#8243;,&#8221;11&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[&#8220;2013&#8243;,&#8221;02&#8243;,&#8221;01&#8243;,&#8221;11&#8221;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Understanding Compound Keys<\/h3>\n<p>As you can see, compound keys are very useful, but some level of understanding helps you use it correctly. Remembering that this is a string compare will help you avoid creating keys that won&#8217;t sort the way you expect. In addition it&#8217;s good to left pad numbers with 0&#8217;s to ensure they are ordered correctly as well since it is string compare and not by integer &#8220;value&#8221;:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">function (doc, meta) {<br \/>\nif (doc.year &amp;&amp; doc.type == &#8220;beer&#8221; &amp;&amp; doc.int_value) {<br \/>\nvar val = doc.int_value.toString();<br \/>\nvar max =&#8221;000000000&#8243;;<br \/>\nvar pval = max.substring(0, max.length &#8211; val.length) + val;<br \/>\nemit([doc.year, doc.type, pval], null);<br \/>\n}<br \/>\n}<\/div>\n<\/div>\n<h5>Resulting Rows (with reduce=false)<\/h5>\n<table style=\"width: 400px\" border=\"1\" cellspacing=\"1\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,&#8221;beer&#8221;,&#8221;000000001&#8243;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,&#8221;beer&#8221;,&#8221;000000002&#8243;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">[2012,&#8221;beer&#8221;,&#8221;000000010&#8243;]<\/div>\n<\/div>\n<\/td>\n<td>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">1<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Of course you can put anything in the array, not just dateToArray! It&#8217;s a useful tactic for being able to group items together for reductions. Last thing to remember is that if you pad with zero&#8217;s these are strings, so if you want to do a Range Query on the resulting Index Key, you must also pad that range with 0&#8217;s as well if you are looking for a specific range, for example:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family: monospace\">startkey=[2012,&#8221;beer&#8221;,&#8221;000000002&#8243;]<br \/>\n&amp;endkey=[2012,&#8221;beer&#8221;,&#8221;000000007&#8243;]<\/div>\n<\/div>\n<h3>Understanding Unicode Collation<\/h3>\n<p>For more information about string character ordering, please read my post on Unicode Collation in Views, as it isn&#8217;t in byte order and applies to compound keys as well: <a href=\"https:\/\/www.couchbase.com\/blog\/understanding-letter-ordering-view-queries\/\">Understanding Letter Ordering in View Queries<\/a><\/p>\n<p><a href=\"https:\/\/twitter.com\/scalabl3\" target=\"_blank\" rel=\"noopener noreferrer\">@scalabl3<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Querying Views is what views are all about. Our documentation is great and can be found here:\u00a0https:\/\/www.couchbase.com\/docs\/couchbase-manual-2.0\/couchbase-views.html Compound Keys Just to illustrate how group level works, let&#8217;s use a Map function that sorts users by the latest login timestamp, converting [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[9407,1812,2201],"tags":[1241],"ppma_author":[8968],"class_list":["post-1811","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruby","category-n1ql-query","category-tools-sdks","tag-views"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.0 (Yoast SEO v26.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Undertsnading compound keys - The Couchbase Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Understanding group_level in View Queries with Compound Keys\" \/>\n<meta property=\"og:description\" content=\"Querying Views is what views are all about. Our documentation is great and can be found here:\u00a0https:\/\/www.couchbase.com\/docs\/couchbase-manual-2.0\/couchbase-views.html Compound Keys Just to illustrate how group level works, let&#8217;s use a Map function that sorts users by the latest login timestamp, converting [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2014-12-16T17:51:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T04:11:09+00:00\" \/>\n<meta name=\"author\" content=\"The Couchbase Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"The Couchbase Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\"},\"author\":{\"name\":\"The Couchbase Team\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/764f4a6771ee19bc7af70b70a326fb93\"},\"headline\":\"Understanding group_level in View Queries with Compound Keys\",\"datePublished\":\"2014-12-16T17:51:03+00:00\",\"dateModified\":\"2025-06-14T04:11:09+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\"},\"wordCount\":726,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"keywords\":[\"views\"],\"articleSection\":[\"Ruby\",\"SQL++ \/ N1QL Query\",\"Tools &amp; SDKs\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\",\"name\":\"Undertsnading compound keys - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2014-12-16T17:51:03+00:00\",\"dateModified\":\"2025-06-14T04:11:09+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Understanding group_level in View Queries with Compound Keys\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/764f4a6771ee19bc7af70b70a326fb93\",\"name\":\"The Couchbase Team\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/7befc37d02226b59499817eafdec60c3\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b4c18c758421903398e84d6c9560f319f39c665798d7d23e6a6f9dff8a8f984e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b4c18c758421903398e84d6c9560f319f39c665798d7d23e6a6f9dff8a8f984e?s=96&d=mm&r=g\",\"caption\":\"The Couchbase Team\"},\"description\":\"Jennifer Garcia is a Senior Web Manager at Couchbase Inc. As the website manager, Jennifer has overall responsibility for the website properties including design, implementation, content, and performance.\",\"sameAs\":[\"https:\/\/www.couchbase.com\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/jennifer-garcia\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Undertsnading compound keys - The Couchbase Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/","og_locale":"en_US","og_type":"article","og_title":"Understanding group_level in View Queries with Compound Keys","og_description":"Querying Views is what views are all about. Our documentation is great and can be found here:\u00a0https:\/\/www.couchbase.com\/docs\/couchbase-manual-2.0\/couchbase-views.html Compound Keys Just to illustrate how group level works, let&#8217;s use a Map function that sorts users by the latest login timestamp, converting [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/","og_site_name":"The Couchbase Blog","article_published_time":"2014-12-16T17:51:03+00:00","article_modified_time":"2025-06-14T04:11:09+00:00","author":"The Couchbase Team","twitter_card":"summary_large_image","twitter_misc":{"Written by":"The Couchbase Team","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/"},"author":{"name":"The Couchbase Team","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/764f4a6771ee19bc7af70b70a326fb93"},"headline":"Understanding group_level in View Queries with Compound Keys","datePublished":"2014-12-16T17:51:03+00:00","dateModified":"2025-06-14T04:11:09+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/"},"wordCount":726,"commentCount":2,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","keywords":["views"],"articleSection":["Ruby","SQL++ \/ N1QL Query","Tools &amp; SDKs"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/","url":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/","name":"Undertsnading compound keys - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2014-12-16T17:51:03+00:00","dateModified":"2025-06-14T04:11:09+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/understanding-grouplevel-view-queries-compound-keys\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Understanding group_level in View Queries with Compound Keys"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/764f4a6771ee19bc7af70b70a326fb93","name":"The Couchbase Team","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/7befc37d02226b59499817eafdec60c3","url":"https:\/\/secure.gravatar.com\/avatar\/b4c18c758421903398e84d6c9560f319f39c665798d7d23e6a6f9dff8a8f984e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b4c18c758421903398e84d6c9560f319f39c665798d7d23e6a6f9dff8a8f984e?s=96&d=mm&r=g","caption":"The Couchbase Team"},"description":"Jennifer Garcia is a Senior Web Manager at Couchbase Inc. As the website manager, Jennifer has overall responsibility for the website properties including design, implementation, content, and performance.","sameAs":["https:\/\/www.couchbase.com"],"url":"https:\/\/www.couchbase.com\/blog\/author\/jennifer-garcia\/"}]}},"authors":[{"term_id":8968,"user_id":2,"is_guest":0,"slug":"jennifer-garcia","display_name":"The Couchbase Team","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/b4c18c758421903398e84d6c9560f319f39c665798d7d23e6a6f9dff8a8f984e?s=96&d=mm&r=g","author_category":"","last_name":"Garcia","first_name":"Jennifer","job_title":"","user_url":"https:\/\/www.couchbase.com","description":"Jennifer Garcia is a Senior Web Manager at Couchbase Inc. As the website manager, Jennifer has overall responsibility for the website properties including design, implementation, content, and performance."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/1811","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=1811"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/1811\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=1811"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=1811"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=1811"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=1811"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}