Stay organized with collections
Save and categorize content based on your preferences.
Search indexes can index
multiple tokenized columns, making queries on these columns more efficient. This
page describes how to perform a search on multiple columns, which is a type of
full-text search.
Perform a multi-column search
The structure of the search
index
ensures that queries don't need a distributed join, ensuring predictable
performance of the queries. The distributed join is avoided due to the
colocation of all tokens that correspond to a base table row on the same split.
Spanner supports multi-column search queries in conjunction,
disjunction, and negation operators in the WHERE clause. You can use all of
the following types of queries with a search index:
Conjunction: Find documents where Title has the term "car" and
Studio has the term "sun".
Both forms filter documents where Title is NULL. Tokenization and
search functions are defined to return NULL on NULL input. SQL defines NOT
NULL as NULL.
Additionally, you can reference the same TOKENLIST column multiple times.
Use either the rquery language
or SQL to search for multiple terms in the same column. rquery is recommended
due to its efficient query caching
for parameterized queries. Aside from the better query cache hit rate, the
rquery and SQL languages have the same latency and performance rates.
GoogleSQL
SELECTAlbumIdFROMAlbumsWHERESEARCH(Title_Tokens,'car OR guy')SELECTAlbumIdFROMAlbumsWHERESEARCH(Title_Tokens,'car')ORSEARCH(Title_Tokens,'guy')
PostgreSQL
SELECTalbumidFROMalbumsWHEREspanner.search(title_tokens,'car OR guy')SELECTalbumidFROMalbumsWHEREspanner.search(title_tokens,'car')ORspanner.search(title_tokens,'guy')
You can also use non-text conditions accelerated with search indexes in the same
query with full-text search functions.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Hard to understand","hardToUnderstand","thumb-down"],["Incorrect information or sample code","incorrectInformationOrSampleCode","thumb-down"],["Missing the information/samples I need","missingTheInformationSamplesINeed","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-08-28 UTC."],[],[],null,["# Search multiple columns in search indexes\n\n| **Note:** This feature is available with the Spanner Enterprise edition and Enterprise Plus edition. For more information, see the [Spanner editions overview](/spanner/docs/editions-overview).\n\n\u003cbr /\u003e\n\n[Search indexes](/spanner/docs/full-text-search/search-indexes) can index\nmultiple tokenized columns, making queries on these columns more efficient. This\npage describes how to perform a search on multiple columns, which is a type of\n[full-text search](/spanner/docs/full-text-search).\n\nPerform a multi-column search\n-----------------------------\n\nThe [structure of the search\nindex](/spanner/docs/full-text-search/search-indexes#search-indexes)\nensures that queries don't need a distributed join, ensuring predictable\nperformance of the queries. The distributed join is avoided due to the\ncolocation of all tokens that correspond to a base table row on the same split.\n\nFor example, consider the following schema: \n\n### GoogleSQL\n\n CREATE TABLE Albums (\n AlbumId STRING(MAX) NOT NULL,\n Title STRING(MAX),\n Studio STRING(MAX),\n Title_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Title)) HIDDEN,\n Studio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(Studio)) HIDDEN\n ) PRIMARY KEY(AlbumId);\n\n CREATE SEARCH INDEX AlbumsIndex ON Albums(Title_Tokens, Studio_Tokens);\n\n### PostgreSQL\n\n CREATE TABLE albums (\n albumid character varying NOT NULL,\n title character varying,\n studio character varying,\n title_tokens spanner.tokenlist\n GENERATED ALWAYS AS (TOKENIZE_FULLTEXT(title)) VIRTUAL HIDDEN,\n studio_tokens spanner.tokenlist\n GENERATED ALWAYS AS (TOKENIZE_FULLTEXT(studio)) VIRTUAL HIDDEN,\n ) PRIMARY KEY(albumid);\n\n CREATE SEARCH INDEX albumsindex ON albums(title_tokens, studio_tokens);\n\nA query can now search two fields: `Title_Tokens` and `Studio_Tokens`. \n\n### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(Title_Tokens, \"fifth symphony\")\n AND SEARCH(Studio_Tokens, \"Blue Note Studio\")\n\n### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE spanner.search(title_tokens, 'fifth symphony')\n AND spanner.search(studio_tokens, 'Blue Note Studio')\n\nSpanner supports multi-column search queries in conjunction,\ndisjunction, and negation operators in the `WHERE` clause. You can use all of\nthe following types of queries with a search index:\n\n- **Conjunction** : Find documents where `Title` has the term \"car\" and\n `Studio` has the term \"sun\".\n\n ### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(Title_Tokens, 'car') AND SEARCH(Studio_Tokens, 'sun')\n\n ### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE spanner.search(title_tokens, 'car') AND spanner.search(studio_tokens, 'sun')\n\n- **Disjunction** : Find documents where either `Title` has the term \"car\" or\n `Studio` has the term \"sun\"\n\n ### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(Title_Tokens, 'car') OR SEARCH(Studio_Tokens, 'sun')\n\n ### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE spanner.search(title_tokens, 'car') OR spanner.search(studio_tokens, 'sun')\n\n- **Negation** : Find all documents where `Title` doesn't contain the term\n \"car\".\n\n ### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE NOT SEARCH(Title_Tokens, 'car')\n\n ### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE NOT spanner.search(title_tokens, 'car')\n\n The [rquery language](/spanner/docs/full-text-search/query-overview#rquery)\n can perform the same type of searches: \n\n ### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(Title_Tokens, '-car')\n\n ### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE spanner.search(title_tokens, '-car')\n\n Both forms filter documents where `Title` is NULL. Tokenization and\n search functions are defined to return NULL on NULL input. SQL defines NOT\n NULL as NULL.\n\nAdditionally, you can reference the same `TOKENLIST` column multiple times. \n\n### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE (SEARCH(Title_Tokens, 'car') OR SEARCH(Studio_Tokens, 'sun'))\n AND (SEARCH(Title_Tokens, 'guy') OR SEARCH(Studio_Tokens, electric))\n\n### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE (spanner.search(title_tokens, 'car') OR spanner.search(studio_tokens, 'sun'))\n AND (spanner.search(title_tokens, 'guy') OR spanner.search(studio_tokens, 'electric'))\n\nUse either the [rquery language](/spanner/docs/full-text-search/query-overview#rquery)\nor SQL to search for multiple terms in the same column. rquery is recommended\ndue to its efficient query [caching](/spanner/docs/whitepapers/life-of-query#caching)\nfor parameterized queries. Aside from the better query cache hit rate, the\nrquery and SQL languages have the same latency and performance rates. \n\n### GoogleSQL\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(Title_Tokens, 'car OR guy')\n\n SELECT AlbumId\n FROM Albums\n WHERE SEARCH(Title_Tokens, 'car') OR SEARCH(Title_Tokens, 'guy')\n\n### PostgreSQL\n\n SELECT albumid\n FROM albums\n WHERE spanner.search(title_tokens, 'car OR guy')\n\n SELECT albumid\n FROM albums\n WHERE spanner.search(title_tokens, 'car') OR spanner.search(title_tokens, 'guy')\n\nYou can also use non-text conditions accelerated with search indexes in the same\nquery with full-text search functions.\n\nWhat's next\n-----------\n\n- Learn about [full-text search queries](/spanner/docs/full-text-search/query-overview).\n- Learn how to [rank search results](/spanner/docs/full-text-search/ranked-search).\n- Learn how to [perform a substring search](/spanner/docs/full-text-search/substring-search).\n- Learn how to [paginate search results](/spanner/docs/full-text-search/paginate-search-results).\n- Learn how to [mix full-text and non-text queries](/spanner/docs/full-text-search/mix-full-text-and-non-text-queries)."]]