{"id":14562,"date":"2023-06-23T13:15:52","date_gmt":"2023-06-23T20:15:52","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=14562"},"modified":"2024-03-01T09:01:33","modified_gmt":"2024-03-01T17:01:33","slug":"recursive-query-processing-in-sql-n1ql","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/","title":{"rendered":"Recursive Query Processing in SQL++ (N1QL)"},"content":{"rendered":"<p><span style=\"font-weight: 400\">It is extremely likely that you have come across issues with <\/span><b>hierarchical lookups or graph traversal<\/b><span style=\"font-weight: 400\"> 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 style=\"font-weight: 400\">.<\/span><\/p>\n<h2><span style=\"font-weight: 400\">What are hierarchical lookups?<\/span><\/h2>\n<p><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">. 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<p><span style=\"font-weight: 400\">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<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-14566\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_124236084.png\" alt=\"hierachical lookups\" width=\"1019\" height=\"679\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124236084.png 1019w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124236084-300x200.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124236084-768x512.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124236084-400x267.png 400w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124236084-450x300.png 450w\" sizes=\"auto, (max-width: 1019px) 100vw, 1019px\" \/><\/p>\n<p><span style=\"font-weight: 400\">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<p><span style=\"font-weight: 400\">Going from the <\/span><i><span style=\"font-weight: 400\">Employee Collection <\/span><\/i><span style=\"font-weight: 400\">we can retrieve:<\/span><\/p>\n<p><span style=\"font-weight: 400\">i) generic employee-level:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14567\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_124344227-1024x555.png\" alt=\"\" width=\"900\" height=\"488\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124344227-1024x555.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124344227-300x163.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124344227-768x416.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124344227.png 1238w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400\">ii) hierarchy for each employee:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14568\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_124413697-1024x379.png\" alt=\"\" width=\"900\" height=\"333\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124413697-1024x379.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124413697-300x111.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124413697-768x284.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124413697.png 1296w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<h2><span style=\"font-weight: 400\">What this blog covers<\/span><\/h2>\n<p><span style=\"font-weight: 400\">You really like the <\/span><b>flexibility<\/b><span style=\"font-weight: 400\"> of NoSQL databases, and your underlying data model is <\/span><b>JSON, <\/b><span style=\"font-weight: 400\">which is why you have chosen Couchbase for your database needs (great choice, btw :-) ).<\/span><\/p>\n<p><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">SQL++<\/span><\/a><span style=\"font-weight: 400\"> Language Reference docs<\/span><span style=\"font-weight: 400\"> you have come to the conclusion that there is no <\/span><i><span style=\"font-weight: 400\">Statement <\/span><\/i><span style=\"font-weight: 400\">or <\/span><i><span style=\"font-weight: 400\">Function <\/span><\/i><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">graphLookup<\/span><\/a><span style=\"font-weight: 400\"> API in MongoDB or <\/span><a href=\"https:\/\/docs.snowflake.com\/en\/user-guide\/queries-cte#recursive-ctes-and-hierarchical-data\"><span style=\"font-weight: 400\">recursive CTE<\/span><\/a><span style=\"font-weight: 400\"> in SQL databases.<\/span><\/p>\n<p><span style=\"font-weight: 400\">But <\/span><b>with existing infrastructure, users can emulate recursive CTE using JavaScript UDFs. <\/b><span style=\"font-weight: 400\">The following sections will demonstrate just how to do that and more!<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Solution to recursive queries<\/span><\/h2>\n<p><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">, a <\/span><b>recursive query<\/b><span style=\"font-weight: 400\">, and <\/span><b>configuration options as parameters<\/b><span style=\"font-weight: 400\">. 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 style=\"font-weight: 400\">$1<\/span><\/i><span style=\"font-weight: 400\"> 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<h3>How to use inner queries<\/h3>\n<p><span style=\"font-weight: 400\">The anchor query gets the <\/span><b>root level documents<\/b><span style=\"font-weight: 400\"> from the target collection.<\/span><\/p>\n<p><span style=\"font-weight: 400\">The recursive query can use a <\/span><i><span style=\"font-weight: 400\">JOIN <\/span><\/i><span style=\"font-weight: 400\">clause with one side as <\/span><i><span style=\"font-weight: 400\">$1<\/span><\/i><span style=\"font-weight: 400\"> 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<p><span style=\"font-weight: 400\">In our function, <\/span><i><span style=\"font-weight: 400\">$1<\/span><\/i><span style=\"font-weight: 400\"> does what the CTE alias does in recursive CTE.<\/span><\/p>\n<p><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">From N1QL to JavaScript and back<\/span><\/a><span style=\"font-weight: 400\">.<\/span><\/p>\n<p><span style=\"font-weight: 400\">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<p><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">article<\/span><\/a><span style=\"font-weight: 400\"> on converting tail recursion functions to iterative. Also, JS UDFs have a preset <\/span><b>max recursion depth of 128;<\/b><span style=\"font-weight: 400\"> this is not preferable for what we are trying to do. <\/span><\/p>\n<p><span style=\"font-weight: 400\">To put it simply, any iterative task does the following:<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14569\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_124659049-1024x439.png\" alt=\"\" width=\"900\" height=\"386\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124659049-1024x439.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124659049-300x129.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124659049-768x330.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124659049.png 1235w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400\">We would like to acknowledge this <\/span><a href=\"https:\/\/www.postgresql.org\/docs\/current\/queries-with.html\"><span style=\"font-weight: 400\">article<\/span><\/a><span style=\"font-weight: 400\"> for its approach.<\/span><\/p>\n<h2><span style=\"font-weight: 400\">Points to ponder<\/span><\/h2>\n<p><span style=\"font-weight: 400\">How do we hold the <\/span><b>state expression<\/b><span style=\"font-weight: 400\"> and pass it as a parameter to the query (recursive part here)?<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">We use the SQL++ ability to pass <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-intro\/queriesandresults.html#named-placeholders\"><span style=\"font-weight: 400\">dynamic query parameters<\/span><\/a><span style=\"font-weight: 400\"> and use parameterized queries to hold state value across levels.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">We use <\/span><em>$1<\/em> in the recursive clause to refer previous iterations results, i.e., state expression<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>How can we optimize?<\/strong><\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Prepare both anchor and recursive queries initially so we can reuse the query plans at execution time.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Find a way to look at the query plan( similar to an <\/span><i><span style=\"font-weight: 400\">EXPLAIN<\/span><\/i><span style=\"font-weight: 400\"> statement) so we can generate appropriate indexes for inner statements (anchor and recursive).<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The High-level Overview of the implementation is as follows:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14570\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_124838894-1024x659.png\" alt=\"\" width=\"900\" height=\"579\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124838894-1024x659.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124838894-300x193.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124838894-768x494.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_124838894.png 1294w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<h3><span style=\"font-weight: 400\">Code for recursive CTE using JS UDFs\u00a0<\/span><\/h3>\n<p><span style=\"font-weight: 400\">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 style=\"font-weight: 400\">this Gist<\/span><\/a><span style=\"font-weight: 400\"> link:<\/span><\/p>\n<pre class=\"height-set:true nums:false lang:js decode:true\">function recursive_cte(anchor , recursive, config) {\r\n    let isLog = false;\r\n    let anchorArgs=[];\r\n    let recursiveArgs=[];\r\n    let cycleFields=[];\r\n    let hash;    \r\n    let levelLimit = -1;\r\n    let isExplain = false;\r\n\r\n    let res = {\"res\":[], \"log\":[]}\r\n\r\n\r\n    if(config!=undefined) {\r\n        if(config.log!=undefined &amp;&amp; config.log==true) {\r\n            isLog = true;\r\n        }\r\n\r\n        if(config.anchorArgs!=undefined) {\r\n            anchorArgs = config.anchorArgs;\r\n        }\r\n\r\n        if(config.recursiveArgs!=undefined) {\r\n            recursiveArgs = config.recursiveArgs;\r\n        }\r\n\r\n        if(config.levelLimit!=undefined &amp;&amp; config.levelLimit&gt;0) {\r\n            levelLimit = config.levelLimit;\r\n        }\r\n\r\n        if(config.cycleFields!=undefined &amp;&amp; config.cycleFields.length&gt;0) {\r\n            res['log'].push(\"Got cycle fields \"+ config.cycleFields)\r\n            \/\/ for(const field of config.cycleFields) {\r\n            \/\/     cycleFields.push(field);\r\n            \/\/ }\r\n            cycleFields = config.cycleFields;\r\n            res['log'].push(cycleFields);\r\n            \/\/ init hash\r\n            hash = createhash();\r\n\r\n        }\r\n\r\n        if(config.explain!=undefined) {\r\n            isExplain = true;\r\n        }\r\n\r\n    }\r\n    \r\n\r\n    \/\/ init state\r\n    recursiveArgs.push(0);\r\n    \r\n    \/\/ Prepare anchor statement\r\n    let anchorPname;\r\n    let anchorPlan;\r\n    try{\r\n        const anchor_prep = N1QL(\"PREPARE FORCE \"+anchor);\r\n        \r\n        for(const ap of anchor_prep) {\r\n            anchorPname = ap[\"name\"];\r\n            anchorPlan = ap[\"operator\"];\r\n        }\r\n\r\n        res['log'].push(\"prepared anchor\");\r\n    }\r\n    catch(err) {\r\n        res['log'].push(\"couldn't prepare anchor\");\r\n        throw err;\r\n    }\r\n\r\n     \/\/ prepare recursive statement\r\n\r\n     let recursivePname;\r\n     let recursivePlan;\r\n     try{\r\n         const recursive_prep = N1QL(\"PREPARE FORCE \"+recursive);\r\n \r\n         for(const rp of recursive_prep) {\r\n             recursivePname = rp[\"name\"];\r\n             recursivePlan = rp[\"operator\"];\r\n         }\r\n         res['log'].push(\"prepared recursive\");\r\n     }\r\n     catch(err) {\r\n         res['log'].push(\"couldn't prepare recursive\");\r\n         throw err;\r\n     }\r\n\r\n\r\n    \/\/ state expression \r\n    let workSet = []\r\n\r\n    \/\/ execute anchor \r\n    try{\r\n        const anchorExec = N1QL(\"EXECUTE `\"+anchorPname+\"`\",anchorArgs);\r\n        for(const doc of anchorExec) {\r\n            workSet.push(doc);\r\n        }\r\n    }\r\n    catch(err) {\r\n        res['log'].push(\"failed to execute anchor\");\r\n        throw err;\r\n    }\r\n\r\n    \/\/ cycle check\r\n    if(cycleFields.length&gt;0) {\r\n        res['log'].push(\"cycle check on fields \"+cycleFields)\r\n        workSet = cycleCheck(cycleFields, hash, workSet);\r\n    }\r\n\r\n    \/\/ populate root level( level 0 )\r\n    res['res'].push(...workSet);\r\n\r\n\r\n    let level = 0;\r\n\r\n    while(workSet.length!=0) {\r\n\r\n        \/\/ exit on level condition\r\n        if(levelLimit&gt;0 &amp;&amp; level&gt;=levelLimit) {\r\n            res['log'].push(\"Exit on level condition: levelLimit=\"+levelLimit.toString())\r\n            break;\r\n        }\r\n\r\n        \/\/ execute recursive query\r\n        let newWorkSet = []\r\n\r\n        \/\/ set state $1\r\n        recursiveArgs[0] = workSet;\r\n\r\n        try{\r\n            const recursiveExec = N1QL(\"EXECUTE `\"+recursivePname+\"`\", recursiveArgs)\r\n\r\n            \/\/ empty workSet to populate again\r\n            for(const doc of recursiveExec) {\r\n                newWorkSet.push(doc)\r\n            }\r\n        }\r\n        catch(err){\r\n            res['log'].push(\"failed execute recursive\");\r\n            throw err;\r\n        }\r\n\r\n        \/\/ cycle check\r\n        if(cycleFields.length&gt;0) {\r\n            newWorkSet = cycleCheck(cycleFields, hash, newWorkSet);\r\n        }\r\n\r\n        if(newWorkSet.length==0)\r\n            break;\r\n\r\n        \r\n        res[\"res\"].push(...newWorkSet);\r\n\r\n        \/\/ update state expression\r\n        workSet = newWorkSet;\r\n\r\n        level++;\r\n    }\r\n\r\n    if(isExplain){\r\n        res['log'].push(\"Anchor Plan:\");\r\n        res['log'].push(anchorPlan);\r\n        res['log'].push(\"Recursive Plan\");\r\n        res[\"log\"].push(recursivePlan);\r\n        return res;\r\n    }\r\n\r\n    return isLog?res:res['res'];\r\n}\r\n\r\n\r\nfunction createhash() {\r\n    let h = {};\r\n\r\n    const getVal = function(key) {\r\n        return h[key]==undefined?false:true;\r\n    }\r\n\r\n    const setVal = function(key) {\r\n        h[key] = true;\r\n    }\r\n\r\n    return {setVal, getVal};\r\n}\r\n\r\nfunction cycleCheck(cycleFields, hash, workSet) {\r\n    let cycleTrim = [];\r\n    for(const doc of workSet) {\r\n        let key = [];\r\n        for(const field of cycleFields) {\r\n            if(doc[field]!=undefined){\r\n                key.push(String(doc[field]));\r\n            }\r\n        }\r\n\r\n        \/\/ create hashKey\r\n        hashKey = key.join(String.fromCharCode(30));\r\n\r\n\r\n        if(hash.getVal(hashKey)==true){\r\n            continue;\r\n        }\r\n        else{\r\n            hash.setVal(hashKey);\r\n            cycleTrim.push(doc);\r\n        }\r\n    }\r\n\r\n    return cycleTrim;\r\n}\r\n<\/pre>\n<h3>How to add the function<\/h3>\n<p><span style=\"font-weight: 400\">Here&#8217;s a <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-javascript-library.html\"><span style=\"font-weight: 400\">link<\/span><\/a><span style=\"font-weight: 400\"> to add this to a JavaScript library (say <\/span><em>&#8220;mylibrary&#8221;<\/em><span style=\"font-weight: 400\">) within your query service, and a <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-user-defined-function.html\"><span style=\"font-weight: 400\">link<\/span><\/a><span style=\"font-weight: 400\"> to create the UDF from the added library.<\/span><\/p>\n<p><span style=\"font-weight: 400\">Create the UDF from the library using SQL++: <\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><\/p>\n<pre class=\"lang:default decode:true\">CREATE FUNCTION recursiveCte(anchor, recursive, config) \r\nLANGUAGE JAVASCRIPT as \"recursive_cte\" AT \"mylibrary\";\r\n<\/pre>\n<p><b>Usage<\/b><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\">Argument 1: <em>Anchor<\/em> query, string<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\">Argument 2: <em>Recursive<\/em> query, string <\/span><span style=\"font-weight: 400\">(uses <\/span><i><span style=\"font-weight: 400\">$1(state expression)<\/span><\/i><span style=\"font-weight: 400\"> to refer to the results of previous iteration )<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\">Argument 3: Config <em>options<\/em>, object<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\">All are <\/span><b>mandatory<\/b><span style=\"font-weight: 400\">, but you can pass an <\/span><i><span style=\"font-weight: 400\">empty object<\/span><\/i><span style=\"font-weight: 400\">, i.e., <\/span><i><span style=\"font-weight: 400\">{}<\/span><\/i><span style=\"font-weight: 400\"> if you aren\u2019t using any config options.<\/span><\/p>\n<p><span style=\"font-weight: 400\"><br \/>\n<\/span><b>Config Options<\/b><b><\/b><\/p>\n<p style=\"padding-left: 40px\"><b>Early exit<\/b><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\"><em>levelLimit<\/em>(1 to N) &#8211; Specify the level at which we can stop. Level count starts at <\/span><i><span style=\"font-weight: 400\">0<\/span><\/i><span style=\"font-weight: 400\"> for anchor results, <\/span><i><span style=\"font-weight: 400\">1<\/span><\/i><span style=\"font-weight: 400\"> for first iteration\/level, <\/span><i><span style=\"font-weight: 400\">2<\/span><\/i><span style=\"font-weight: 400\"> for second, etc.<\/span><\/p>\n<p style=\"padding-left: 40px\"><b>Arguments to inner query<\/b><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\"><em>anchorArgs<\/em> &#8211; as named, e.g.,<\/span><i><span style=\"font-weight: 400\">{\u201carg:1}<\/span><\/i><span style=\"font-weight: 400\"> or positional arguments, e.g., <\/span><i><span style=\"font-weight: 400\">[1]<\/span><\/i><span style=\"font-weight: 400\"> to use in anchor query.<\/span><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\"><em>recursiveArgs<\/em> &#8211; Can only be positional arguments, and <\/span><i><span style=\"font-weight: 400\">0<\/span><\/i><span style=\"font-weight: 400\"> index must be reserved for state expression. For example: <\/span><i><span style=\"font-weight: 400\">[0, 1] <\/span><\/i><span style=\"font-weight: 400\">&#8211; always set 0th index (<\/span><i><span style=\"font-weight: 400\">$1<\/span><\/i><span style=\"font-weight: 400\"> arg) to <\/span><i><span style=\"font-weight: 400\">0<\/span><\/i><span style=\"font-weight: 400\">, so we can use it in the recursive clause as state expression.<\/span><\/p>\n<p style=\"padding-left: 40px\"><b>Cycle detection<\/b><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\"><em>cycleFields<\/em> &#8211; Array of field names, e.g., <\/span><i><span style=\"font-weight: 400\">[&#8220;_from&#8221;, &#8220;_to&#8221;]<\/span><\/i><\/p>\n<p style=\"padding-left: 40px\"><b>Explain<\/b><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\"><em>explain<\/em> &#8211; Check query plan in logs (sets <\/span><i><span style=\"font-weight: 400\">log <\/span><\/i><span style=\"font-weight: 400\">by default)<\/span><\/p>\n<p style=\"padding-left: 40px\"><b>Logs<\/b><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\"><em>log<\/em> &#8211; Display logs that help when you want to log while developing<\/span><\/p>\n<p>&nbsp;<\/p>\n<h2><span style=\"font-weight: 400\">Sample Queries<\/span><\/h2>\n<p><span style=\"font-weight: 400\"><br \/>\n<\/span><b>1.<\/b><span style=\"font-weight: 400\"> <b>Count numbers 1 to N<\/b> &#8211; simplest example of a query that shows how recursive CTE works.<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\">\u00a0 \u00a0\u00a0<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span> <span style=\"font-weight: 400\">Take a brief pause to think about how you could do this in N1QL without the function <\/span><span style=\"font-weight: 400\">utility we developed now. Is it possible? (Or did we just make<\/span><b> N1QL Turing complete!<\/b><span style=\"font-weight: 400\">)<br \/>\n<\/span><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14571\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_125658889-1024x444.png\" alt=\"\" width=\"900\" height=\"390\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125658889-1024x444.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125658889-300x130.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125658889-768x333.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125658889.png 1269w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><b>2. Employees example<\/b> &#8211; refer to the employee collection we saw earlier.<\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\">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\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_125805870-1024x424.png\" alt=\"\" width=\"900\" height=\"373\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125805870-1024x424.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125805870-300x124.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125805870-768x318.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125805870.png 1251w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p style=\"padding-left: 40px\">B. <span style=\"font-weight: 400\">Per employee reports to hierarchy<\/span><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14573\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_125914592-1024x580.png\" alt=\"\" width=\"900\" height=\"510\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125914592-1024x580.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125914592-300x170.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125914592-768x435.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_125914592.png 1225w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p><span style=\"font-weight: 400\">Anchor query: <\/span><\/p>\n<pre class=\"lang:default decode:true\">SELECT e1.*, 0 as hlevel \r\nFROM `employees` e1 \r\nWHERE e1.manager_id=\" || to_str(e.employee_id)<\/pre>\n<p>This can look too verbose when adding a <em>WHERE<\/em> predicate from outer query.\u00a0 W<span style=\"font-weight: 400\">e can use the <\/span><em>anchorArgs<\/em> option<span style=\"font-weight: 400\"> to mitigate this!<\/span><\/p>\n<h3><span style=\"font-weight: 400\">Arguments (anchorArgs\/recursiveArgs)<\/span><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14574\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_130029549-1024x191.png\" alt=\"\" width=\"900\" height=\"168\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130029549-1024x191.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130029549-300x56.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130029549-768x143.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130029549.png 1253w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p style=\"padding-left: 40px\"><strong>Explain<\/strong><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\">Provide <\/span><span style=\"font-weight: 400\">{&#8220;explain&#8221;: true} &#8211; <\/span><span style=\"font-weight: 400\">you can see the query plan in the log field\u00a0<\/span><\/p>\n<p style=\"padding-left: 40px\"><strong>Early Exit<\/strong><\/p>\n<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\">Set <\/span><i><span style=\"font-weight: 400\">levelLimit <\/span><\/i><span style=\"font-weight: 400\">like <\/span><i><span style=\"font-weight: 400\">{\u201clevelLimit\u201d:2}<\/span><\/i><span style=\"font-weight: 400\"> &#8211; if you only want 2 levels of recursion<\/span><\/p>\n<p>&nbsp;<\/p>\n<p style=\"padding-left: 40px\"><strong>Cycle Detection<\/strong><\/p>\n<p style=\"padding-left: 40px\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14575\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_130205451-1024x334.png\" alt=\"\" width=\"900\" height=\"294\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130205451-1024x334.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130205451-300x98.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130205451-768x250.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130205451.png 1271w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p style=\"padding-left: 40px\"><b>Without cycle detection <\/b><span style=\"font-weight: 400\">the query to find employee level hierarchy will go into an <\/span><b>infinite loop <\/b><i><span style=\"font-weight: 400\">and crash on function timeout, wasting CPU resources.<\/span><\/i><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\"><br \/>\n<\/span><span style=\"font-weight: 400\">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<p style=\"padding-left: 40px\"><span style=\"font-weight: 400\">Here, the <\/span><i><span style=\"font-weight: 400\">&#8220;employee_id&#8221;<\/span><\/i><span style=\"font-weight: 400\">, <\/span><i><span style=\"font-weight: 400\">&#8220;manager_id&#8221;<\/span><\/i><span style=\"font-weight: 400\">\u00a0 pair is sufficient to exit on cycle.<\/span><\/p>\n<p style=\"padding-left: 40px\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-14576\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/image_2023-06-23_130306815-1024x312.png\" alt=\"\" width=\"900\" height=\"274\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130306815-1024x312.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130306815-300x92.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130306815-768x234.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/image_2023-06-23_130306815.png 1259w\" sizes=\"auto, (max-width: 900px) 100vw, 900px\" \/><\/p>\n<p>Thank you for following on this challenging topic, we hope it reveals more options for some of your recursive querying challenges!<\/p>\n<h2><span style=\"font-weight: 400\">References<\/span><\/h2>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/www.baeldung.com\/cs\/convert-recursion-to-iteration\"><span style=\"font-weight: 400\">Convert Recursive to Iterative Functions (Baeldung)<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/index.html\"><span style=\"font-weight: 400\">SQL++ Language Reference docs<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/www.mongodb.com\/docs\/manual\/reference\/operator\/aggregation\/graphLookup\/?_ga=2.234064094.1723037650.1675387315-1783168968.1675058199#-graphlookup--aggregation-\"><span style=\"font-weight: 400\">MongoDB graphLookup reference<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.snowflake.com\/en\/user-guide\/queries-cte#recursive-ctes-and-hierarchical-data\"><span style=\"font-weight: 400\">Snowflake recursive CTE reference<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/www.couchbase.com\/blog\/from-n1ql-to-javascript-and-back-part-1-introduction\/\"><span style=\"font-weight: 400\">Blog: From N1QL to JavaScript and Back &#8211; Part 1<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/www.postgresql.org\/docs\/current\/queries-with.html\"><span style=\"font-weight: 400\">PostgreSQL query reference, WITH clause<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-intro\/queriesandresults.html#named-placeholders\"><span style=\"font-weight: 400\">SQL++ Queries and Results docs<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-javascript-library.html\"><span style=\"font-weight: 400\">Creating a JavaScript library docs<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/guides\/create-user-defined-function.html\"><span style=\"font-weight: 400\">Creating User Defined Functions (UDF) in Couchbase<\/span><\/a><\/li>\n<\/ul>\n<\/li>\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 [&hellip;]<\/p>\n","protected":false},"author":84423,"featured_media":14577,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1816,1819,9327,1812],"tags":[9836,8911],"ppma_author":[9835],"class_list":["post-14562","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"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\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\/recursive-query-processing-in-sql-n1ql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\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\/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=\"article:modified_time\" content=\"2024-03-01T17:01:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.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 minutes\" \/>\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\",\"dateModified\":\"2024-03-01T17:01:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/\"},\"wordCount\":1232,\"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\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg\",\"keywords\":[\"recursive queries\",\"User Defined Function (UDF)\"],\"articleSection\":[\"Couchbase Server\",\"Data Modeling\",\"JavaScript\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"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\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg\",\"datePublished\":\"2023-06-23T20:15:52+00:00\",\"dateModified\":\"2024-03-01T17:01:33+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\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg\",\"width\":1920,\"height\":2560,\"caption\":\"Fibonacci design of staircase by Remy Penet on Unsplash\"},{\"@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\":\"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\/546cec92f77cbb0b09f9b973fd1c8d42\",\"name\":\"Gaurav Jayaraj - Software Engineer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/a332e5d7f47865015367ca88af3e5891\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg\",\"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\/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\/recursive-query-processing-in-sql-n1ql\/","og_locale":"en_US","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\/recursive-query-processing-in-sql-n1ql\/","og_site_name":"The Couchbase Blog","article_published_time":"2023-06-23T20:15:52+00:00","article_modified_time":"2024-03-01T17:01:33+00:00","og_image":[{"width":1920,"height":2560,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.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 minutes"},"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","dateModified":"2024-03-01T17:01:33+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/"},"wordCount":1232,"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\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg","keywords":["recursive queries","User Defined Function (UDF)"],"articleSection":["Couchbase Server","Data Modeling","JavaScript","SQL++ \/ N1QL Query"],"inLanguage":"en-US","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\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg","datePublished":"2023-06-23T20:15:52+00:00","dateModified":"2024-03-01T17:01:33+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":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2023\/06\/remy-penet-zaM9LhySx_0-unsplash-scaled.jpg","width":1920,"height":2560,"caption":"Fibonacci design of staircase by Remy Penet on Unsplash"},{"@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":"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\/546cec92f77cbb0b09f9b973fd1c8d42","name":"Gaurav Jayaraj - Software Engineer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/a332e5d7f47865015367ca88af3e5891","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg","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\/author\/gauravjayaraj\/"}]}},"authors":[{"term_id":9835,"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\/1\/2025\/02\/unnamed-2.jpg","url2x":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2025\/02\/unnamed-2.jpg"},"author_category":"","last_name":"Jayaraj - Software Engineer","first_name":"Gaurav","job_title":"","user_url":"","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\/wp-json\/wp\/v2\/posts\/14562","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\/84423"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=14562"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/14562\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/14577"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=14562"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=14562"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=14562"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=14562"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}