{"id":8849,"date":"2020-07-07T07:00:08","date_gmt":"2020-07-07T14:00:08","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=8849"},"modified":"2025-06-13T23:42:34","modified_gmt":"2025-06-14T06:42:34","slug":"first-class-sql-full-text-search-couchbase","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/","title":{"rendered":"FIRST CLASS SQL for FULL-TEXT SEARCH"},"content":{"rendered":"<p>Over time, the database industry has realized full-text search and SQL are two sides of the same coin.\u00a0 Text search needs further query processing, query processing needs text search to efficiently filter for text patterns.\u00a0 \u00a0The SQL databases have added full-text search within them, albeit for a single node SMP systems.<\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li>SQL Server supports <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/search\/full-text-search?view=sql-server-ver15\">CONTAINS()\u00a0 for text search<\/a><\/li>\n<li>Oracle supports <a href=\"https:\/\/docs.oracle.com\/cd\/B28359_01\/text.111\/b28303\/query.htm#BABGAIHJ\">CONTAINS() for text search<\/a><\/li>\n<li>MySQL added f<a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/fulltext-natural-language.html\">ull text support\u00a0<\/a><\/li>\n<li>PostgreSQL has <a href=\"https:\/\/www.postgresql.org\/docs\/12\/functions-textsearch.html\">supported text search<\/a> for a long time.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">Couchbase Full-Text Search (FTS) is created with three main motivations:<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Transparently search across multiple fields within a document<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Go beyond the exact matching of values by providing <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/fts\/fts-query-types.html\"><span style=\"font-weight: 400\">language based stemming<\/span><\/a><span style=\"font-weight: 400\">, <a href=\"https:\/\/www.couchbase.com\/blog\/fuzzy-matching\/\">fuzzy matching<\/a>, etc.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Provide the search results based on relevance<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">FTS achieves this on an inverted index and a rich set of <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/fts\/fts-query-types.html\"><span style=\"font-weight: 400\">query predicates<\/span><\/a><span style=\"font-weight: 400\">: from simple word search to pattern matching to complex range predicates.\u00a0 \u00a0 In addition to the search, it supports aggregation via <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/fts\/fts-response-object-schema.html#search-facets\"><span style=\"font-weight: 400\">search facets<\/span><\/a><span style=\"font-weight: 400\">.<\/span><\/p>\n<p>In the NoSQL world, Lucene is a popular search index and so are the search servers based on Lucene: <a href=\"https:\/\/lucene.apache.org\/solr\/\">Solr<\/a> and <a href=\"https:\/\/elastic.co\">Elasticsearch<\/a>.\u00a0 Following their RDBMS cousins, <a href=\"https:\/\/www.elastic.co\/\"><span style=\"font-weight: 400\">Elasticsearch<\/span><\/a><span style=\"font-weight: 400\">, <\/span><a href=\"https:\/\/opendistro.github.io\/for-elasticsearch\/\"><span style=\"font-weight: 400\">Opendistro for Elasticsearch<\/span><\/a><span style=\"font-weight: 400\"> all have added SQL for their search.\u00a0 Couchbase introduced the full text service,\u00a0<a href=\"https:\/\/docs.couchbase.com\/server\/current\/fts\/full-text-intro.html\">FTS<\/a> and has followed up with support for search within N1QL.<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/www.couchbase.com\/blog\/introducing-fts-with-n1ql\/\"><span style=\"font-weight: 400\">FTS with N1QL<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/www.elastic.co\/what-is\/elasticsearch-sql\"><span style=\"font-weight: 400\">Elasticsearch with SQL<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/opendistro.github.io\/for-elasticsearch-docs\/docs\/sql\/\"><span style=\"font-weight: 400\">Opendistro for Elasticsearch with SQL<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">And the recent entrant to search, MongoDB has added <\/span><a href=\"https:\/\/docs.atlas.mongodb.com\/atlas-search\/\"><span style=\"font-weight: 400\">search to MQL<\/span><\/a><span style=\"font-weight: 400\"> using Lucene in its Atlas offering. <\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>The SQL Implementations of <a href=\"https:\/\/www.elastic.co\/what-is\/elasticsearch-sql\">Elasticsearch with SQL<\/a> and <a href=\"https:\/\/docs.atlas.mongodb.com\/atlas-search\/\">MongoDB&#8217;s MQL<\/a> comes with a long list of limitations.<\/strong><\/p>\n<p><span style=\"font-weight: 400\"><a href=\"https:\/\/www.elastic.co\/what-is\/elasticsearch-sql\">Elasticsearch with SQL<\/a><\/span> has listed its limitations here:<\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li>Full list: <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/sql-limitations.html\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/sql-limitations.html<\/a><\/li>\n<li>In addition, the language is understandably limited due to its nascent implementation.\n<ul>\n<li>No support for set operations joins, etc, etc.<\/li>\n<li>No window functions.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\"><a href=\"https:\/\/docs.atlas.mongodb.com\/atlas-search\/\">MongoDB&#8217;s MQL&#8217;s<\/a> search integration comes with a long list of limitations.<\/span><\/p>\n<ul>\n<li style=\"list-style-type: none\">\n<ul>\n<li>Available only on the Atlas search service, not on the on-prem product.<\/li>\n<li>Search can only be the FIRST operation within the aggregate() pipeline.<\/li>\n<li>Available only within the aggregation pipeline (aggregate()) and not in find(), insert(), update(), remove() other other operations.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">The integration with its aggregate() API, comes with some limitations: It can only be the first operation in the pipeline unavailable on its on-prem database. <\/span>The features we discuss in this article are in Couchbase 6.5 and above.<\/p>\n<p>Here\u2019s an example from N1QL:<\/p>\n<pre class=\"theme:familiar font-size:17 line-height:20 whitespace-before:2 whitespace-after:2 lang:mysql decode:true\">SELECT country, \r\n               city, \r\n               name, \r\n               ROW_NUMBER() OVER(ORDER BY country DESC, city DESC) rownum\r\nFROM `travel-sample` AS t1\r\nWHERE t1.type = \"hotel\" AND SEARCH(t1.description, \"garden\") \r\nAND ANY r in reviews satisfies r.ratings.Service &gt; 3 END;\r\n<\/pre>\n<p><span style=\"font-weight: 400\">This includes the following in addition to SEARCH():<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Projection of fields from the documents: country, city, name<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Row number generation via the window function ROW_NUMBER()<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Additional scalar predicate\u00a0 t1.type = \u201chotel\u201d<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Array predicate on reviews (ANY)<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400\">You get the FULL benefit of first-class query processing in addition to efficient search.\u00a0 That\u2019s not all &#8212; there\u2019s even more with N1QL. <\/span><span style=\"font-weight: 400\">The benefits and <\/span><a href=\"https:\/\/www.couchbase.com\/blog\/unreasonable-effectiveness-of-sql\/\"><span style=\"font-weight: 400\">effectiveness of SQL<\/span><\/a><span style=\"font-weight: 400\"> are well known.\u00a0 N1QL is SQL for JSON. The goal of N1QL is to give developers and enterprises an expressive, powerful, and complete language for querying, transforming, and manipulating JSON data.<\/span><\/p>\n<p><span style=\"font-weight: 400\">The benefits of using <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/index.html\"><span style=\"font-weight: 400\">N1QL<\/span><\/a><span style=\"font-weight: 400\"> with the search are the following:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Predicates:<\/span>\n<ul>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">FTS is great with searching based on relevance. SQL is great with additional complex query processing: complex predicates, array predicates, additional scalar\u00a0<\/span><\/li>\n<\/ul>\n<\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/arithmetic.html\"><span style=\"font-weight: 400\">Operators<\/span><\/a><span style=\"font-weight: 400\"> and <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/aggregatefun.html\"><span style=\"font-weight: 400\">functions<\/span><\/a><span style=\"font-weight: 400\">:<\/span>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Predicate processing (filter processing)<\/span>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Additional scalar and array predicates<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Scalar and array functions too be used within the predicates<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Subqueries<\/span>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Correlated subqueries<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Uncorrelated subqueries\u00a0<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/aggregatefun.html\"><span style=\"font-weight: 400\">Aggregates<\/span><\/a><\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/windowfun.html\"><span style=\"font-weight: 400\">Window functions<\/span><\/a><\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">JOIN processing<\/span>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">N1QL can do INNER JOIN, LEFT OUTER JOIN, (limited) RIGHT OUTER JOIN, NEST, UNNEST<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">JOINS between buckets, collections and results of subqueries.<\/span><\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/union.html\"><span style=\"font-weight: 400\">SET operations<\/span><\/a>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">UNION<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">UNION ALL<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">EXCEPT<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">EXCEPT ALL<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">INTERSECT<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">INTERSECT ALL<\/span><\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400\"><a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/n1ql\/n1ql-language-reference\/with.html\"><span style=\"font-weight: 400\">CTE<\/span><\/a><span style=\"font-weight: 400\"> (Common Table Expression) and <\/span><a href=\"https:\/\/docs.couchbase.com\/server\/current\/n1ql\/n1ql-language-reference\/let.html\"><span style=\"font-weight: 400\">LET clause<\/span><\/a><span style=\"font-weight: 400\"> for improved query writing<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">More than SEARCH()<\/span>\n<ol>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">In addition to SELECT, you can use SEARCH() predicate in the WHERE clauses of INSERT, UPDATE, DELETE, MERGE statements.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">You can PREPARE these statements and EXECUTE them repeatedly and efficiently.<\/span><\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">You get the usual security via the RBAC roles via GRANT &amp; REVOKE.<\/span><\/li>\n<\/ol>\n<\/li>\n<li style=\"font-weight: 400\"><span style=\"font-weight: 400\">Developer productivity: Write the query in SQL, the language they already know.<\/span><\/li>\n<\/ol>\n<p>Let&#8217;s Look at how the N1QL engine executes this.\u00a0 Abhinav Dangeti from the Couchbase FTS engineering has already written a <a href=\"https:\/\/www.couchbase.com\/blog\/introducing-fts-with-n1ql\/\">great blog detailing the decision making and examples<\/a>.\u00a0 This article is to explain this visually with additional examples in the categories mentioned above.<\/p>\n<h5><strong>1.\u00a0 ARCHITECTURE for QUERY EXECUTION<\/strong><\/h5>\n<p>We&#8217;ve added three important steps to query execution the query uses SEARCH() :<\/p>\n<ol>\n<li>The planner considers the FTS search index one of the valid access paths if search() predicate exists in the query.\n<ul>\n<li>If the search index is selected, then it creates the plan by pushing down the search predicate to the FTS index.<\/li>\n<\/ul>\n<\/li>\n<li>When the search index is selected, executor issues the search request to one of the FTS nodes (instead of the scan request to the index service)<\/li>\n<li>Before the results from the search are finalized, the query service re-verifies the search qualification of the document to the data.<\/li>\n<\/ol>\n<div id=\"attachment_8852\" style=\"width: 855px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8852\" class=\" wp-image-8852\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-300x164.png\" alt=\"N1QL query execution with FTS\" width=\"845\" height=\"462\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-300x164.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-1024x559.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-768x420.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-1536x839.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-2048x1119.png 2048w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-20x11.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.02.40-PM-1320x721.png 1320w\" sizes=\"auto, (max-width: 845px) 100vw, 845px\" \/><\/a><p id=\"caption-attachment-8852\" class=\"wp-caption-text\">N1QL query execution with FTS<\/p><\/div>\n<div id=\"attachment_8853\" style=\"width: 849px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8853\" class=\" wp-image-8853\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-300x127.png\" alt=\"Inside the Query Service\" width=\"839\" height=\"355\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-300x127.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-1024x433.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-768x325.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-1536x649.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-2048x865.png 2048w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-20x8.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-01-at-7.11.26-PM-1320x558.png 1320w\" sizes=\"auto, (max-width: 839px) 100vw, 839px\" \/><\/a><p id=\"caption-attachment-8853\" class=\"wp-caption-text\">Inside a Query Service<\/p><\/div>\n<h5><\/h5>\n<h5><strong>2. PREDICATE PROCESSING<\/strong><\/h5>\n<p>In the following query, the SEARCH() predicate (predicate-2) is pushed to the FTS search request.\u00a0 Every other predicate is processed by the query engine post search in the &#8220;Filter&#8221; phase\u00a0 &#8212; as shown in the &#8220;Inside a Query Service&#8221; figure above.\u00a0 This is one exception to this.\u00a0 When the FTS index has created an index with <strong>JSON type field (doc_config.type_field in the index definition document)<\/strong>\u00a0is defined (in this case type = &#8220;hotel&#8221;) to create the index on the subset of the document,\u00a0 both index selection and search pushdown exploits this predicate.\u00a0 Even in this case, the predicate is re-applied after the document fetch.<\/p>\n<pre class=\"theme:familiar font-size:17 line-height:20 whitespace-before:2 whitespace-after:2 lang:mysql decode:true\">SELECT country, \r\n               city, \r\n               name, \r\n               ROW_NUMBER() OVER(ORDER BY country DESC, city DESC) rownum\r\nFROM `travel-sample` AS t1\r\nWHERE \r\nt1.type = \"hotel\"   \/* predicate-1 *\/\r\nAND SEARCH(t1.description, \"garden\") \/* predicate-2 *\/\r\nAND ANY r in reviews satisfies r.ratings.Service &gt; 3 END; \/* predicate-2 *\/\r\n<\/pre>\n<h5><\/h5>\n<h5><strong>3. <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/n1ql\/n1ql-language-reference\/operators.html\">OPERATORS<\/a> and <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/n1ql\/n1ql-language-reference\/functions.html\">FUNCTIONS<\/a><\/strong><\/h5>\n<p>Here&#8217;s an example of a query exploiting the operators and functions.<\/p>\n<pre class=\"theme:familiar font-size:17 line-height:20 whitespace-before:2 whitespace-after:2 lang:mysql decode:true\">SELECT LOWER(country),   \/* scalar function *\/\r\n       city, \r\n       lastname || \" \" || firstname AS fullname   \/* string operator *\/\r\n       ROW_NUMBER() OVER(ORDER BY country DESC, city DESC) rownum   \/* window function *\/\r\nFROM `travel-sample` AS t1\r\nWHERE \r\nLOWER(t1.type) = \"hotel\"   \/* scalar function *\/\r\nAND SEARCH(t1.description, \"garden\") \r\nAND ARRAY_CONTAINS(public_likes, \"Joe Black\")  \/* Array function *\/\r\n<\/pre>\n<p>Here&#8217;s the query plan for this query. IndexSearch does the FTS search request and this is layered into the query execution pipeline.\u00a0 Hence the query gets the benefit of all the other capabilities of N1QL.\u00a0 This reflects the pipeline stages in the figure above.<\/p>\n<div id=\"attachment_8856\" style=\"width: 1044px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8856\" class=\" wp-image-8856\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-300x190.png\" alt=\"Query plan with SEARCH()\" width=\"1034\" height=\"654\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-300x190.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-1024x647.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-768x485.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-1536x970.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-20x13.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM-1320x834.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-12.18.16-PM.png 1722w\" sizes=\"auto, (max-width: 1034px) 100vw, 1034px\" \/><\/a><p id=\"caption-attachment-8856\" class=\"wp-caption-text\">Query plan with SEARCH()<\/p><\/div>\n<h5>4. JOIN processing<\/h5>\n<p>.The SEARCH() can also be used as part of the join processing.\u00a0 In this case, the FTS is used to find all the cities which have hotels with gardens and then join with airports.<\/p>\n<pre class=\"theme:familiar font-size:17 line-height:20 whitespace-before:2 whitespace-after:2 lang:mysql decode:true\">SELECT hotel.name hname,\r\n       airport.city\r\nFROM `travel-sample` hotel \r\nLEFT OUTER JOIN `travel-sample` airport ON hotel.city = airport.city\r\nWHERE hotel.type = 'hotel'\r\n    AND SEARCH(hotel.description, \"garden\")\r\n    AND airport.type = 'airport' ;\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"attachment_8857\" style=\"width: 777px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8857\" class=\" wp-image-8857\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-300x200.png\" alt=\"JOIN with SEARCH\" width=\"767\" height=\"511\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-300x200.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-1024x683.png 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-768x512.png 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-1536x1024.png 1536w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-400x267.png 400w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-450x300.png 450w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-20x13.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM-1320x880.png 1320w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-1.52.41-PM.png 1764w\" sizes=\"auto, (max-width: 767px) 100vw, 767px\" \/><\/a><p id=\"caption-attachment-8857\" class=\"wp-caption-text\">JOIN with SEARCH<\/p><\/div>\n<h5>5. <a href=\"https:\/\/docs.couchbase.com\/server\/6.5\/n1ql\/n1ql-language-reference\/with.html\">Common Table Expressions (CTEs).<\/a><\/h5>\n<p>N1QL in query service supports non-recursive CTEs.\u00a0 \u00a0You can use SEARCH() within each expression. The derived table from that expression (hotel and airport) are used as keyspaces within the query.<\/p>\n<pre class=\"theme:familiar font-size:17 line-height:20 whitespace-before:2 whitespace-after:2 lang:mysql decode:true\">WITH hotel AS (\r\n    SELECT name,\r\n           city\r\n    FROM `travel-sample`\r\n    WHERE type = 'hotel'\r\n        AND search(description, \"garden\")),\r\nairport AS (\r\n    SELECT name,\r\n           city\r\n    FROM `travel-sample`\r\n    WHERE type = 'airport'\r\n        AND SEARCH(city, \"angeles\"))\r\nSELECT hotel.name hname,\r\n       airport.city\r\nFROM hotel\r\nINNER JOIN airport ON hotel.city = airport.city\r\nORDER BY airport.city,\r\n         hotel.name;\r\n<\/pre>\n<p><a href=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-02-at-3.00.13-PM.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-8858\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2020\/07\/Screen-Shot-2020-07-02-at-3.00.13-PM-198x300.png\" alt=\"\" width=\"536\" height=\"812\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-3.00.13-PM-198x300.png 198w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-3.00.13-PM-300x454.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-3.00.13-PM-13x20.png 13w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/Screen-Shot-2020-07-02-at-3.00.13-PM.png 518w\" sizes=\"auto, (max-width: 536px) 100vw, 536px\" \/><\/a><\/p>\n<h5><strong>5. Use in UPDATEs<\/strong><\/h5>\n<p>SEARCH() can be used anywhere a predicate is allowed within other DML statements.<\/p>\n<pre class=\"theme:familiar font-size:17 line-height:20 whitespace-before:2 whitespace-after:2 lang:mysql decode:true\">\/* INSERT INTO... SELECT statement. *\/\r\nINSERT INTO mybucket (KEY id, VALUES v)\r\n          SELECT meta().id  id, v \r\n          FROM `travel-sample` v\r\n          WHERE SEARCH(v, \"+type:hotel  +description:clean\");\r\n\r\n\/* DELETE statement *\/\r\nDELETE FROM `travel-sample` WHERE SEARCH(v, \"+type:hotel +description:clean\");\r\n\r\n\/* UPDATE statement *\/\r\nUPDATE `travel-sample` SET new_field = \"search n update!\" WHERE SEARCH(v, \"+type:hotel +description:clean\"); \r\n\r\n\r\n<\/pre>\n<h5>The examples can flow a long time.\u00a0 I&#8217;ve shown common sample examples. You use this in various SQL statements (DMLs)<\/h5>\n<h5><strong>Conclusion<\/strong>:<\/h5>\n<p>Couchbase FTS provides a scalable, distributed text search engine.\u00a0 We&#8217;ve seamlessly layered it into N1QL in Couchbase Query service so you get the full power of queries with the full power of search.\u00a0 \u00a0There&#8217;s more innovation on this in the pipeline. Stay tuned!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Over time, the database industry has realized full-text search and SQL are two sides of the same coin.\u00a0 Text search needs further query processing, query processing needs text search to efficiently filter for text patterns.\u00a0 \u00a0The SQL databases have added [&hellip;]<\/p>\n","protected":false},"author":55,"featured_media":8850,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,1815,1821,2165,1812],"tags":[1584,1292,1261],"ppma_author":[8929],"class_list":["post-8849","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-best-practices-and-tutorials","category-couchbase-architecture","category-full-text-search","category-n1ql-query","tag-bleve","tag-elasticsearch","tag-json"],"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>FIRST CLASS SQL for FULL-TEXT SEARCH - The Couchbase Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"FIRST CLASS SQL for FULL-TEXT SEARCH\" \/>\n<meta property=\"og:description\" content=\"Over time, the database industry has realized full-text search and SQL are two sides of the same coin.\u00a0 Text search needs further query processing, query processing needs text search to efficiently filter for text patterns.\u00a0 \u00a0The SQL databases have added [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2020-07-07T14:00:08+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T06:42:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1300\" \/>\n\t<meta property=\"og:image:height\" content=\"1244\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Keshav Murthy\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@rkeshavmurthy\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Keshav Murthy\" \/>\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\/first-class-sql-full-text-search-couchbase\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\"},\"author\":{\"name\":\"Keshav Murthy\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c261644262bf98e146372fe647682636\"},\"headline\":\"FIRST CLASS SQL for FULL-TEXT SEARCH\",\"datePublished\":\"2020-07-07T14:00:08+00:00\",\"dateModified\":\"2025-06-14T06:42:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\"},\"wordCount\":1197,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg\",\"keywords\":[\"bleve\",\"elasticsearch\",\"JSON\"],\"articleSection\":[\"Application Design\",\"Best Practices and Tutorials\",\"Couchbase Architecture\",\"Full-Text Search\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\",\"name\":\"FIRST CLASS SQL for FULL-TEXT SEARCH - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg\",\"datePublished\":\"2020-07-07T14:00:08+00:00\",\"dateModified\":\"2025-06-14T06:42:34+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg\",\"width\":1300,\"height\":1244},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"FIRST CLASS SQL for FULL-TEXT SEARCH\"}]},{\"@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\/c261644262bf98e146372fe647682636\",\"name\":\"Keshav Murthy\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/4e51d72fc07c662aa791316deafffac4\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g\",\"caption\":\"Keshav Murthy\"},\"description\":\"Keshav Murthy is a Vice President at Couchbase R&amp;D. Previously, he was at MapR, IBM, Informix, Sybase, with more than 20 years of experience in database design &amp; development. He lead the SQL and NoSQL R&amp;D team at IBM Informix. He has received two President's Club awards at Couchbase, two Outstanding Technical Achievement Awards at IBM. Keshav has a bachelor's degree in Computer Science and Engineering from the University of Mysore, India, holds eleven US patents and has four US patents pending.\",\"sameAs\":[\"https:\/\/blog.planetnosql.com\/\",\"https:\/\/x.com\/rkeshavmurthy\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/keshav-murthy\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"FIRST CLASS SQL for FULL-TEXT SEARCH - The Couchbase Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/","og_locale":"en_US","og_type":"article","og_title":"FIRST CLASS SQL for FULL-TEXT SEARCH","og_description":"Over time, the database industry has realized full-text search and SQL are two sides of the same coin.\u00a0 Text search needs further query processing, query processing needs text search to efficiently filter for text patterns.\u00a0 \u00a0The SQL databases have added [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/","og_site_name":"The Couchbase Blog","article_published_time":"2020-07-07T14:00:08+00:00","article_modified_time":"2025-06-14T06:42:34+00:00","og_image":[{"width":1300,"height":1244,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg","type":"image\/jpeg"}],"author":"Keshav Murthy","twitter_card":"summary_large_image","twitter_creator":"@rkeshavmurthy","twitter_misc":{"Written by":"Keshav Murthy","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/"},"author":{"name":"Keshav Murthy","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c261644262bf98e146372fe647682636"},"headline":"FIRST CLASS SQL for FULL-TEXT SEARCH","datePublished":"2020-07-07T14:00:08+00:00","dateModified":"2025-06-14T06:42:34+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/"},"wordCount":1197,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg","keywords":["bleve","elasticsearch","JSON"],"articleSection":["Application Design","Best Practices and Tutorials","Couchbase Architecture","Full-Text Search","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/","url":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/","name":"FIRST CLASS SQL for FULL-TEXT SEARCH - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg","datePublished":"2020-07-07T14:00:08+00:00","dateModified":"2025-06-14T06:42:34+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2020\/07\/triangle1-e1593651195823.jpg","width":1300,"height":1244},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/first-class-sql-full-text-search-couchbase\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"FIRST CLASS SQL for FULL-TEXT SEARCH"}]},{"@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\/c261644262bf98e146372fe647682636","name":"Keshav Murthy","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/4e51d72fc07c662aa791316deafffac4","url":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g","caption":"Keshav Murthy"},"description":"Keshav Murthy is a Vice President at Couchbase R&amp;D. Previously, he was at MapR, IBM, Informix, Sybase, with more than 20 years of experience in database design &amp; development. He lead the SQL and NoSQL R&amp;D team at IBM Informix. He has received two President's Club awards at Couchbase, two Outstanding Technical Achievement Awards at IBM. Keshav has a bachelor's degree in Computer Science and Engineering from the University of Mysore, India, holds eleven US patents and has four US patents pending.","sameAs":["https:\/\/blog.planetnosql.com\/","https:\/\/x.com\/rkeshavmurthy"],"url":"https:\/\/www.couchbase.com\/blog\/author\/keshav-murthy\/"}]}},"authors":[{"term_id":8929,"user_id":55,"is_guest":0,"slug":"keshav-murthy","display_name":"Keshav Murthy","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g","author_category":"","last_name":"Murthy","first_name":"Keshav","job_title":"","user_url":"https:\/\/blog.planetnosql.com\/","description":"Keshav Murthy is a Vice President at Couchbase R&amp;D. Previously, he was at MapR, IBM, Informix, Sybase, with more than 20 years of experience in database design &amp; development. He lead the SQL and NoSQL R&amp;D team at IBM Informix. He has received two President's Club awards at Couchbase, two Outstanding Technical Achievement Awards at IBM. Keshav has a bachelor's degree in Computer Science and Engineering from the University of Mysore, India,  holds ten US patents and has three US patents pending."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/8849","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\/55"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=8849"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/8849\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/8850"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=8849"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=8849"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=8849"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=8849"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}