{"id":2659,"date":"2023-06-23T13:15:52","date_gmt":"2023-06-23T20:15:52","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/"},"modified":"2023-06-23T13:15:52","modified_gmt":"2023-06-23T20:15:52","slug":"recursive-query-processing-in-sql-n1ql","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/recursive-query-processing-in-sql-n1ql\/","title":{"rendered":"Recursive Query Processing in SQL++ (N1QL)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><span>It is extremely likely that you have come across issues with <\/span><b>hierarchical lookups or graph traversal<\/b><span> in your application as a developer who handles real-world use cases. And, for obvious reasons, you prefer solving them at the <\/span><b>database layer and not at the client-side<\/b><span>.<\/span><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span>What are hierarchical lookups?<\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Hierarchical lookup refers to the process of searching and retrieving data from a hierarchical structure, such as a <\/span><b>tree or a parent-child relationship<\/b><span>. It involves navigating through the levels or layers of the hierarchy to locate specific data elements or related information, for example, organizational charts, file systems, and category trees.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Graphs are basically trees that may have cycles, so additionally, we could also cover use cases like path finding, etc provided we have additional configurations to deal with cycles.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-14566\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_124236084.png\" alt=\"hierachical lookups\" width=\"1019\" height=\"679\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Consider an example using an employee collection with a hierarchical structure. Employees are organized in a manager-subordinate relationship. We&#8217;ll perform operations on this employee hierarchy using Couchbase and JavaScript UDFs to demonstrate the solution.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Going from the <\/span><i><span>Employee Collection <\/span><\/i><span>we can retrieve:<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>i) generic employee-level:<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14567\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_124344227-1024x555-1.png\" alt=\"\" width=\"900\" height=\"488\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>ii) hierarchy for each employee:<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14568\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_124413697-1024x379-1.png\" alt=\"\" width=\"900\" height=\"333\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span>What this blog covers<\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><span>You really like the <\/span><b>flexibility<\/b><span> of NoSQL databases, and your underlying data model is <\/span><b>JSON, <\/b><span>which is why you have chosen Couchbase for your database needs (great choice, btw :-) ).<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Now that you have Couchbase as your database, you want to solve the above mentioned problem. After doing some research in the <\/span><a href=\"https:\/\/www.couchbase.com\/sqlplusplus\/\"><span>SQL++<\/span><\/a><span> Language Reference docs<\/span><span> you have come to the conclusion that there is no <\/span><i><span>Statement <\/span><\/i><span>or <\/span><i><span>Function <\/span><\/i><span>support in SQL++ to solve our problem, like the <\/span><a href=\"https:\/\/www.mongodb.com\/docs\/manual\/reference\/operator\/aggregation\/graphLookup\/?_ga=2.234064094.1723037650.1675387315-1783168968.1675058199#-graphlookup--aggregation-\"><span>graphLookup<\/span><\/a><span> API in MongoDB or <\/span><a href=\"https:\/\/docs.snowflake.com\/en\/user-guide\/queries-cte#recursive-ctes-and-hierarchical-data\"><span>recursive CTE<\/span><\/a><span> in SQL databases.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>But <\/span><b>with existing infrastructure, users can emulate recursive CTE using JavaScript UDFs. <\/b><span>The following sections will demonstrate just how to do that and more!<\/span><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span>Solution to recursive queries<\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><span>To implement this solution, we need to create a JavaScript UDF that utilizes a breadth-first search algorithm to traverse the employee hierarchy. The UDF takes an <\/span><b>anchor query<\/b><span>, a <\/span><b>recursive query<\/b><span>, and <\/span><b>configuration options as parameters<\/b><span>. The anchor query retrieves the initial set of employees, while the recursive query references the results of the previous iteration using the <\/span><i><span>$1<\/span><\/i><span> parameter. Configuration options allow customization, including early exit criteria, arguments for inner queries, cycle detection, explain mode for query planning, and logging options.<\/span><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How to use inner queries<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><span>The anchor query gets the <\/span><b>root level documents<\/b><span> from the target collection.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>The recursive query can use a <\/span><i><span>JOIN <\/span><\/i><span>clause with one side as <\/span><i><span>$1<\/span><\/i><span> and the other as the target collection, to exploit the parent-child relationship and perform the traversal from one level to another.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>In our function, <\/span><i><span>$1<\/span><\/i><span> does what the CTE alias does in recursive CTE.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>To better understand how we use SQL++ (N1QL) within JavaScript UDFs, take a look at<\/span> <a href=\"https:\/\/www.couchbase.com\/blog\/from-n1ql-to-javascript-and-back-part-1-introduction\/\"><span>From N1QL to JavaScript and back<\/span><\/a><span>.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Now let&#8217;s look at the process step by step. First of all, <\/span><b>does recursive query processing really need to be a recursive function?<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>No, this would be a bad choice considering time-complexity (the same functionality in recursive might be exponential, but linear in an iterative approach) and all the other issues like frames, etc., that come along with recursion. If interested, check out this <\/span><a href=\"https:\/\/www.baeldung.com\/cs\/convert-recursion-to-iteration\"><span>article<\/span><\/a><span> on converting tail recursion functions to iterative. Also, JS UDFs have a preset <\/span><b>max recursion depth of 128;<\/b><span> this is not preferable for what we are trying to do. <\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>To put it simply, any iterative task does the following:<\/span><span><br>\n<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14569\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_124659049-1024x439-1.png\" alt=\"\" width=\"900\" height=\"386\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>We would like to acknowledge this <\/span><a href=\"https:\/\/www.postgresql.org\/docs\/current\/queries-with.html\"><span>article<\/span><\/a><span> for its approach.<\/span><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span>Points to ponder<\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><span>How do we hold the <\/span><b>state expression<\/b><span> and pass it as a parameter to the query (recursive part here)?<\/span><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span>We use the SQL++ ability to pass <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-intro\/queriesandresults.html#named-placeholders\"><span>dynamic query parameters<\/span><\/a><span> and use parameterized queries to hold state value across levels.<\/span><\/li>\n\n\n<li><span>We use <\/span><em>$1<\/em> in the recursive clause to refer previous iterations results, i.e., state expression<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>How can we optimize?<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span>Prepare both anchor and recursive queries initially so we can reuse the query plans at execution time.<\/span><\/li>\n\n\n<li><span>Find a way to look at the query plan( similar to an <\/span><i><span>EXPLAIN<\/span><\/i><span> statement) so we can generate appropriate indexes for inner statements (anchor and recursive).<\/span><\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The High-level Overview of the implementation is as follows:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14570\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_124838894-1024x659-1.png\" alt=\"\" width=\"900\" height=\"579\"><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span>Code for recursive CTE using JS UDFs\u00a0<\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><span>This is a very naive implementation to get started and running. We set up the <em>Breadth-First-Search<\/em> algorithm, establishing the flow of order as per the overview above. Here is the code, from <\/span><a href=\"https:\/\/gist.github.com\/GauravJayaraj\/2d290f5398efe6edb8dc1b53e87556ae\"><span>this Gist<\/span><\/a><span> link:<\/span><\/p>\n\n\n<p>[crayon height-set=&#8221;true&#8221; nums=&#8221;false&#8221; lang=&#8221;js&#8221; decode=&#8221;true&#8221;]function recursive_cte(anchor , recursive, config) {<br \/>\n    let isLog = false;<br \/>\n    let anchorArgs=[];<br \/>\n    let recursiveArgs=[];<br \/>\n    let cycleFields=[];<br \/>\n    let hash;<br \/>\n    let levelLimit = -1;<br \/>\n    let isExplain = false;<\/p>\n<p>    let res = {&#8220;res&#8221;:[], &#8220;log&#8221;:[]}<\/p>\n<p>    if(config!=undefined) {<br \/>\n        if(config.log!=undefined &amp;&amp; config.log==true) {<br \/>\n            isLog = true;<br \/>\n        }<\/p>\n<p>        if(config.anchorArgs!=undefined) {<br \/>\n            anchorArgs = config.anchorArgs;<br \/>\n        }<\/p>\n<p>        if(config.recursiveArgs!=undefined) {<br \/>\n            recursiveArgs = config.recursiveArgs;<br \/>\n        }<\/p>\n<p>        if(config.levelLimit!=undefined &amp;&amp; config.levelLimit&gt;0) {<br \/>\n            levelLimit = config.levelLimit;<br \/>\n        }<\/p>\n<p>        if(config.cycleFields!=undefined &amp;&amp; config.cycleFields.length&gt;0) {<br \/>\n            res[&#8216;log&#8217;].push(&#8220;Got cycle fields &#8220;+ config.cycleFields)<br \/>\n            \/\/ for(const field of config.cycleFields) {<br \/>\n            \/\/     cycleFields.push(field);<br \/>\n            \/\/ }<br \/>\n            cycleFields = config.cycleFields;<br \/>\n            res[&#8216;log&#8217;].push(cycleFields);<br \/>\n            \/\/ init hash<br \/>\n            hash = createhash();<\/p>\n<p>        }<\/p>\n<p>        if(config.explain!=undefined) {<br \/>\n            isExplain = true;<br \/>\n        }<\/p>\n<p>    }<\/p>\n<p>    \/\/ init state<br \/>\n    recursiveArgs.push(0);<\/p>\n<p>    \/\/ Prepare anchor statement<br \/>\n    let anchorPname;<br \/>\n    let anchorPlan;<br \/>\n    try{<br \/>\n        const anchor_prep = N1QL(&#8220;PREPARE FORCE &#8220;+anchor);<\/p>\n<p>        for(const ap of anchor_prep) {<br \/>\n            anchorPname = ap[&#8220;name&#8221;];<br \/>\n            anchorPlan = ap[&#8220;operator&#8221;];<br \/>\n        }<\/p>\n<p>        res[&#8216;log&#8217;].push(&#8220;prepared anchor&#8221;);<br \/>\n    }<br \/>\n    catch(err) {<br \/>\n        res[&#8216;log&#8217;].push(&#8220;couldn&#8217;t prepare anchor&#8221;);<br \/>\n        throw err;<br \/>\n    }<\/p>\n<p>     \/\/ prepare recursive statement<\/p>\n<p>     let recursivePname;<br \/>\n     let recursivePlan;<br \/>\n     try{<br \/>\n         const recursive_prep = N1QL(&#8220;PREPARE FORCE &#8220;+recursive);<\/p>\n<p>         for(const rp of recursive_prep) {<br \/>\n             recursivePname = rp[&#8220;name&#8221;];<br \/>\n             recursivePlan = rp[&#8220;operator&#8221;];<br \/>\n         }<br \/>\n         res[&#8216;log&#8217;].push(&#8220;prepared recursive&#8221;);<br \/>\n     }<br \/>\n     catch(err) {<br \/>\n         res[&#8216;log&#8217;].push(&#8220;couldn&#8217;t prepare recursive&#8221;);<br \/>\n         throw err;<br \/>\n     }<\/p>\n<p>    \/\/ state expression<br \/>\n    let workSet = []<\/p>\n<p>    \/\/ execute anchor<br \/>\n    try{<br \/>\n        const anchorExec = N1QL(&#8220;EXECUTE `&#8221;+anchorPname+&#8221;`&#8221;,anchorArgs);<br \/>\n        for(const doc of anchorExec) {<br \/>\n            workSet.push(doc);<br \/>\n        }<br \/>\n    }<br \/>\n    catch(err) {<br \/>\n        res[&#8216;log&#8217;].push(&#8220;failed to execute anchor&#8221;);<br \/>\n        throw err;<br \/>\n    }<\/p>\n<p>    \/\/ cycle check<br \/>\n    if(cycleFields.length&gt;0) {<br \/>\n        res[&#8216;log&#8217;].push(&#8220;cycle check on fields &#8220;+cycleFields)<br \/>\n        workSet = cycleCheck(cycleFields, hash, workSet);<br \/>\n    }<\/p>\n<p>    \/\/ populate root level( level 0 )<br \/>\n    res[&#8216;res&#8217;].push(&#8230;workSet);<\/p>\n<p>    let level = 0;<\/p>\n<p>    while(workSet.length!=0) {<\/p>\n<p>        \/\/ exit on level condition<br \/>\n        if(levelLimit&gt;0 &amp;&amp; level&gt;=levelLimit) {<br \/>\n            res[&#8216;log&#8217;].push(&#8220;Exit on level condition: levelLimit=&#8221;+levelLimit.toString())<br \/>\n            break;<br \/>\n        }<\/p>\n<p>        \/\/ execute recursive query<br \/>\n        let newWorkSet = []<\/p>\n<p>        \/\/ set state $1<br \/>\n        recursiveArgs[0] = workSet;<\/p>\n<p>        try{<br \/>\n            const recursiveExec = N1QL(&#8220;EXECUTE `&#8221;+recursivePname+&#8221;`&#8221;, recursiveArgs)<\/p>\n<p>            \/\/ empty workSet to populate again<br \/>\n            for(const doc of recursiveExec) {<br \/>\n                newWorkSet.push(doc)<br \/>\n            }<br \/>\n        }<br \/>\n        catch(err){<br \/>\n            res[&#8216;log&#8217;].push(&#8220;failed execute recursive&#8221;);<br \/>\n            throw err;<br \/>\n        }<\/p>\n<p>        \/\/ cycle check<br \/>\n        if(cycleFields.length&gt;0) {<br \/>\n            newWorkSet = cycleCheck(cycleFields, hash, newWorkSet);<br \/>\n        }<\/p>\n<p>        if(newWorkSet.length==0)<br \/>\n            break;<\/p>\n<p>        res[&#8220;res&#8221;].push(&#8230;newWorkSet);<\/p>\n<p>        \/\/ update state expression<br \/>\n        workSet = newWorkSet;<\/p>\n<p>        level++;<br \/>\n    }<\/p>\n<p>    if(isExplain){<br \/>\n        res[&#8216;log&#8217;].push(&#8220;Anchor Plan:&#8221;);<br \/>\n        res[&#8216;log&#8217;].push(anchorPlan);<br \/>\n        res[&#8216;log&#8217;].push(&#8220;Recursive Plan&#8221;);<br \/>\n        res[&#8220;log&#8221;].push(recursivePlan);<br \/>\n        return res;<br \/>\n    }<\/p>\n<p>    return isLog?res:res[&#8216;res&#8217;];<br \/>\n}<\/p>\n<p>function createhash() {<br \/>\n    let h = {};<\/p>\n<p>    const getVal = function(key) {<br \/>\n        return h[key]==undefined?false:true;<br \/>\n    }<\/p>\n<p>    const setVal = function(key) {<br \/>\n        h[key] = true;<br \/>\n    }<\/p>\n<p>    return {setVal, getVal};<br \/>\n}<\/p>\n<p>function cycleCheck(cycleFields, hash, workSet) {<br \/>\n    let cycleTrim = [];<br \/>\n    for(const doc of workSet) {<br \/>\n        let key = [];<br \/>\n        for(const field of cycleFields) {<br \/>\n            if(doc[field]!=undefined){<br \/>\n                key.push(String(doc[field]));<br \/>\n            }<br \/>\n        }<\/p>\n<p>        \/\/ create hashKey<br \/>\n        hashKey = key.join(String.fromCharCode(30));<\/p>\n<p>        if(hash.getVal(hashKey)==true){<br \/>\n            continue;<br \/>\n        }<br \/>\n        else{<br \/>\n            hash.setVal(hashKey);<br \/>\n            cycleTrim.push(doc);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    return cycleTrim;<br \/>\n}<br \/>\n[\/crayon]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How to add the function<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Here&#8217;s a <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-javascript-library.html\"><span>link<\/span><\/a><span> to add this to a JavaScript library (say <\/span><em>&#8220;mylibrary&#8221;<\/em><span>) within your query service, and a <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-user-defined-function.html\"><span>link<\/span><\/a><span> to create the UDF from the added library.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Create the UDF from the library using SQL++: <\/span><span><br>\n<\/span><\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]CREATE FUNCTION recursiveCte(anchor, recursive, config)<br \/>\nLANGUAGE JAVASCRIPT as &#8220;recursive_cte&#8221; AT &#8220;mylibrary&#8221;;<br \/>\n[\/crayon]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Usage<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Argument 1: <em>Anchor<\/em> query, string<\/span><span><br>\n<\/span><span>Argument 2: <em>Recursive<\/em> query, string <\/span><span>(uses <\/span><i><span>$1(state expression)<\/span><\/i><span> to refer to the results of previous iteration )<\/span><span><br>\n<\/span><span>Argument 3: Config <em>options<\/em>, object<\/span><span><br>\n<\/span><span><br>\n<\/span><span>All are <\/span><b>mandatory<\/b><span>, but you can pass an <\/span><i><span>empty object<\/span><\/i><span>, i.e., <\/span><i><span>{}<\/span><\/i><span> if you aren\u2019t using any config options.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><br>\n<\/span><b>Config Options<\/b><b><\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Early exit<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><em>levelLimit<\/em>(1 to N) &#8211; Specify the level at which we can stop. Level count starts at <\/span><i><span>0<\/span><\/i><span> for anchor results, <\/span><i><span>1<\/span><\/i><span> for first iteration\/level, <\/span><i><span>2<\/span><\/i><span> for second, etc.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Arguments to inner query<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><em>anchorArgs<\/em> &#8211; as named, e.g.,<\/span><i><span>{\u201carg:1}<\/span><\/i><span> or positional arguments, e.g., <\/span><i><span>[1]<\/span><\/i><span> to use in anchor query.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><em>recursiveArgs<\/em> &#8211; Can only be positional arguments, and <\/span><i><span>0<\/span><\/i><span> index must be reserved for state expression. For example: <\/span><i><span>[0, 1] <\/span><\/i><span>&#8211; always set 0th index (<\/span><i><span>$1<\/span><\/i><span> arg) to <\/span><i><span>0<\/span><\/i><span>, so we can use it in the recursive clause as state expression.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Cycle detection<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><em>cycleFields<\/em> &#8211; Array of field names, e.g., <\/span><i><span>[&#8220;_from&#8221;, &#8220;_to&#8221;]<\/span><\/i><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Explain<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><em>explain<\/em> &#8211; Check query plan in logs (sets <\/span><i><span>log <\/span><\/i><span>by default)<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Logs<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span><em>log<\/em> &#8211; Display logs that help when you want to log while developing<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00a0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span>Sample Queries<\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><span><br>\n<\/span><b>1.<\/b><span> <b>Count numbers 1 to N<\/b> &#8211; simplest example of a query that shows how recursive CTE works.<\/span><span><br>\n<\/span><span>\u00a0 \u00a0\u00a0<\/span><span><br>\n<\/span> <span>Take a brief pause to think about how you could do this in N1QL without the function <\/span><span>utility we developed now. Is it possible? (Or did we just make<\/span><b> N1QL Turing complete!<\/b><span>)<br>\n<\/span><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14571\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_125658889-1024x444-1.png\" alt=\"\" width=\"900\" height=\"390\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>2. Employees example<\/b> &#8211; refer to the employee collection we saw earlier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>A. Find the organizational level of an employee<\/span><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14572\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_125805870-1024x424-1.png\" alt=\"\" width=\"900\" height=\"373\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">B. <span>Per employee reports to hierarchy<\/span><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14573\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_125914592-1024x580-1.png\" alt=\"\" width=\"900\" height=\"510\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Anchor query: <\/span><\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]SELECT e1.*, 0 as hlevel<br \/>\nFROM `employees` e1<br \/>\nWHERE e1.manager_id=&#8221; || to_str(e.employee_id)[\/crayon]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This can look too verbose when adding a <em>WHERE<\/em> predicate from outer query.\u00a0 W<span>e can use the <\/span><em>anchorArgs<\/em> option<span> to mitigate this!<\/span><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span>Arguments (anchorArgs\/recursiveArgs)<\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14574\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_130029549-1024x191-1.png\" alt=\"\" width=\"900\" height=\"168\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Explain<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Provide <\/span><span>{&#8220;explain&#8221;: true} &#8211; <\/span><span>you can see the query plan in the log field\u00a0<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Early Exit<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Set <\/span><i><span>levelLimit <\/span><\/i><span>like <\/span><i><span>{\u201clevelLimit\u201d:2}<\/span><\/i><span> &#8211; if you only want 2 levels of recursion<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u00a0<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Cycle Detection<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14575\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_130205451-1024x334-1.png\" alt=\"\" width=\"900\" height=\"294\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><b>Without cycle detection <\/b><span>the query to find employee level hierarchy will go into an <\/span><b>infinite loop <\/b><i><span>and crash on function timeout, wasting CPU resources.<\/span><\/i><span><br>\n<\/span><span><br>\n<\/span><span>Considering that when we work on graph data it is highly likely that we have cycles present and it is the <\/span><b>responsibility of the caller of the function to to set cycle detection of appropriate fields.<\/b><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><span>Here, the <\/span><i><span>&#8220;employee_id&#8221;<\/span><\/i><span>, <\/span><i><span>&#8220;manager_id&#8221;<\/span><\/i><span>\u00a0 pair is sufficient to exit on cycle.<\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14576\" src=\"https:\/\/www.couchbase.com\/wp-content\/uploads\/sites\/5\/2026\/05\/image_2023-06-23_130306815-1024x312-1.png\" alt=\"\" width=\"900\" height=\"274\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thank you for following on this challenging topic, we hope it reveals more options for some of your recursive querying challenges!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span>References<\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.baeldung.com\/cs\/convert-recursion-to-iteration\"><span>Convert Recursive to Iterative Functions (Baeldung)<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/index.html\"><span>SQL++ Language Reference docs<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/www.mongodb.com\/docs\/manual\/reference\/operator\/aggregation\/graphLookup\/?_ga=2.234064094.1723037650.1675387315-1783168968.1675058199#-graphlookup--aggregation-\"><span>MongoDB graphLookup reference<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/docs.snowflake.com\/en\/user-guide\/queries-cte#recursive-ctes-and-hierarchical-data\"><span>Snowflake recursive CTE reference<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/www.couchbase.com\/blog\/from-n1ql-to-javascript-and-back-part-1-introduction\/\"><span>Blog: From N1QL to JavaScript and Back &#8211; Part 1<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/www.postgresql.org\/docs\/current\/queries-with.html\"><span>PostgreSQL query reference, WITH clause<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-intro\/queriesandresults.html#named-placeholders\"><span>SQL++ Queries and Results docs<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-javascript-library.html\"><span>Creating a JavaScript library docs<\/span><\/a><\/li>\n\n\n<li><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-user-defined-function.html\"><span>Creating User Defined Functions (UDF) in Couchbase<\/span><\/a><\/li>\n\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>It is extremely likely that you have come across issues with hierarchical lookups or graph traversal in your application as a developer who handles real-world use cases. And, for obvious reasons, you prefer solving them at the database layer and not at the client-side. What are hierarchical lookups? Hierarchical lookup refers to the process of [&hellip;]<\/p>\n","protected":false},"author":84423,"featured_media":2656,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"_acf":"","footnotes":""},"categories":[54,189,163,18],"tags":[681,682],"ppma_author":[683],"class_list":["post-2659","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-couchbase-server","category-data-modeling","category-javascript","category-n1ql-query","tag-recursive-queries","tag-udf"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.6 (Yoast SEO v27.6) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Recursive Query Processing in SQL++ (N1QL) Function<\/title>\n<meta name=\"description\" content=\"Users can emulate recursive CTE using JavaScript UDFs with existing infrastructure. This Couchbase blog post demonstrates just how to do that and more!\" \/>\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\/pt\/recursive-query-processing-in-sql-n1ql\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Recursive Query Processing in SQL++ (N1QL)\" \/>\n<meta property=\"og:description\" content=\"Users can emulate recursive CTE using JavaScript UDFs with existing infrastructure. This Couchbase blog post demonstrates just how to do that and more!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/recursive-query-processing-in-sql-n1ql\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-06-23T20:15:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"2560\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Gaurav Jayaraj - Software Engineer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Gaurav Jayaraj - Software Engineer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/\"},\"author\":{\"name\":\"Gaurav Jayaraj - Software Engineer\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/546cec92f77cbb0b09f9b973fd1c8d42\"},\"headline\":\"Recursive Query Processing in SQL++ (N1QL)\",\"datePublished\":\"2023-06-23T20:15:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/\"},\"wordCount\":1738,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg\",\"keywords\":[\"recursive queries\",\"User Defined Function (UDF)\"],\"articleSection\":[\"Couchbase Server\",\"Data Modeling\",\"JavaScript\",\"SQL++ \\\/ N1QL Query\"],\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/\",\"name\":\"Recursive Query Processing in SQL++ (N1QL) Function\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg\",\"datePublished\":\"2023-06-23T20:15:52+00:00\",\"description\":\"Users can emulate recursive CTE using JavaScript UDFs with existing infrastructure. This Couchbase blog post demonstrates just how to do that and more!\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg\",\"width\":1920,\"height\":2560},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/recursive-query-processing-in-sql-n1ql\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Recursive Query Processing in SQL++ (N1QL)\"}]},{\"@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\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/06\\\/logo.svg\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/06\\\/logo.svg\",\"width\":\"1024\",\"height\":\"1024\",\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/546cec92f77cbb0b09f9b973fd1c8d42\",\"name\":\"Gaurav Jayaraj - Software Engineer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bbb46a14616fc13176b2496886166e68b3348a81653303d78f7df462db2eb5a1?s=96&d=mm&r=g9f655fda3d24fd49bccf33982dd15670\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bbb46a14616fc13176b2496886166e68b3348a81653303d78f7df462db2eb5a1?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bbb46a14616fc13176b2496886166e68b3348a81653303d78f7df462db2eb5a1?s=96&d=mm&r=g\",\"caption\":\"Gaurav Jayaraj - Software Engineer\"},\"description\":\"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore.\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/pt\\\/author\\\/gauravjayaraj\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Recursive Query Processing in SQL++ (N1QL) Function","description":"Users can emulate recursive CTE using JavaScript UDFs with existing infrastructure. This Couchbase blog post demonstrates just how to do that and more!","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\/pt\/recursive-query-processing-in-sql-n1ql\/","og_locale":"pt_BR","og_type":"article","og_title":"Recursive Query Processing in SQL++ (N1QL)","og_description":"Users can emulate recursive CTE using JavaScript UDFs with existing infrastructure. This Couchbase blog post demonstrates just how to do that and more!","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/recursive-query-processing-in-sql-n1ql\/","og_site_name":"The Couchbase Blog","article_published_time":"2023-06-23T20:15:52+00:00","og_image":[{"width":1920,"height":2560,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg","type":"image\/jpeg"}],"author":"Gaurav Jayaraj - Software Engineer","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Gaurav Jayaraj - Software Engineer","Est. reading time":"8 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/"},"author":{"name":"Gaurav Jayaraj - Software Engineer","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42"},"headline":"Recursive Query Processing in SQL++ (N1QL)","datePublished":"2023-06-23T20:15:52+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/"},"wordCount":1738,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg","keywords":["recursive queries","User Defined Function (UDF)"],"articleSection":["Couchbase Server","Data Modeling","JavaScript","SQL++ \/ N1QL Query"],"inLanguage":"pt-BR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/","url":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/","name":"Recursive Query Processing in SQL++ (N1QL) Function","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg","datePublished":"2023-06-23T20:15:52+00:00","description":"Users can emulate recursive CTE using JavaScript UDFs with existing infrastructure. This Couchbase blog post demonstrates just how to do that and more!","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/remy-penet-zaM9LhySx_0-unsplash-scaled-1.jpg","width":1920,"height":2560},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Recursive Query Processing in SQL++ (N1QL)"}]},{"@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":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/06\/logo.svg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/06\/logo.svg","width":"1024","height":"1024","caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/546cec92f77cbb0b09f9b973fd1c8d42","name":"Gaurav Jayaraj - Software Engineer","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/secure.gravatar.com\/avatar\/bbb46a14616fc13176b2496886166e68b3348a81653303d78f7df462db2eb5a1?s=96&d=mm&r=g9f655fda3d24fd49bccf33982dd15670","url":"https:\/\/secure.gravatar.com\/avatar\/bbb46a14616fc13176b2496886166e68b3348a81653303d78f7df462db2eb5a1?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bbb46a14616fc13176b2496886166e68b3348a81653303d78f7df462db2eb5a1?s=96&d=mm&r=g","caption":"Gaurav Jayaraj - Software Engineer"},"description":"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore.","url":"https:\/\/www.couchbase.com\/blog\/pt\/author\/gauravjayaraj\/"}]}},"acf":[],"authors":[{"term_id":683,"user_id":84423,"is_guest":0,"slug":"gauravjayaraj","display_name":"Gaurav Jayaraj - Software Engineer","avatar_url":{"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/unnamed-2-5.jpg","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/unnamed-2-5.jpg"},"author_category":"","first_name":"Gaurav","last_name":"Jayaraj - Software Engineer","user_url":"","job_title":"","description":"Gaurav Jayaraj is an intern in the Query team at Couchbase R&amp;D. Gaurav is pursuing his Bachelors in Computer Science from PES University, Bangalore."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2659","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/users\/84423"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=2659"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2659\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/2656"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=2659"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=2659"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=2659"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=2659"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}