Skip to main content
Generalized inverted indexes, or GIN indexes, store mappings from values within a container column (such as a document) to the row that holds that value. They are used to speed up containment searches, e.g., “show me all of the rows from this table which have a JSON column that contains the key-value pair {"location":"NYC"}”. GIN indexes are commonly used in document retrieval systems. CockroachDB stores the contents of the following data types in GIN indexes:
For a hands-on demonstration of using GIN indexes to improve query performance on a JSONB column, see the .

How do GIN indexes work?

Standard work well for searches based on prefixes of sorted data. However, data types like or cannot be queried without a full table scan, since they do not adhere to ordinary value prefix comparison operators. JSONB in particular needs to be indexed in a more detailed way than what a standard index provides. This is where GIN indexes prove useful. GIN indexes filter on components of tokenizable data. The JSONB data type is built on two structures that can be tokenized:
  • Objects - Collections of key-value pairs where each key-value pair is a token.
  • Arrays - Ordered lists of values where every value in the array is a token.
For example, take the following JSONB value in column person:
A GIN index for this object would have an entry per component, mapping it back to the original object:
This lets you search based on subcomponents.

Creation

You can use GIN indexes to improve the performance of queries using , , columns (for ), or (for ). You can create them:
  • Using the PostgreSQL-compatible syntax :
    Also specify an opclass when :
You can also use the preceding syntax to specify the jsonb_ops or array_ops opclass (for JSONB and ARRAY columns, respectively).
  • While creating the table, using the syntax :
    Also specify an opclass when :

Selection

If a query contains a filter against an indexed JSONB or ARRAY column that uses any of the supported operators, the GIN index is added to the set of index candidates. In most cases CockroachDB selects the index it calculates will scan the fewest rows (i.e., the fastest). Cases where CockroachDB will use multiple indexes include certain queries that use disjunctions (i.e., predicates with OR), as well as for some other queries. To learn how to use the statement for your query to see which index is being used, see Index Selection in CockroachDB. To override CockroachDB’s index selection, you can use an index hint to force a query to use , including and ; or use .

Storage

CockroachDB stores indexes directly in your key-value store. You can find more information in our blog post Mapping Table Data to Key-Value Storage.

Locking

Tables are not locked during index creation due to CockroachDB’s schema change procedure.

Performance

Indexes create a trade-off: they greatly improve the speed of queries, but slightly slow down writes (because new values have to be copied and sorted). The first index you create has the largest impact, but additional indexes introduce only marginal overhead.

Comparisons

This section describes how to perform comparisons on JSONB and ARRAY columns.

JSONB

GIN indexes on JSONB columns support the following comparison operators:
  • is contained by:
  • contains:
  • equals: . To use =, you must also reach into the JSON document with the operator. For example:
    This is equivalent to using @>:
If you require comparisons using , , etc., you can create an index on a using your JSON payload, and then create a standard index on that. To write a query where the value of foo is greater than three:
  1. Create a table with a computed column:
  2. Create an index on the computed column:
  3. Execute the query with the comparison:

Arrays

GIN indexes on columns support the following comparison operators:
  • is contained by:
  • contains:

Partial GIN indexes

You can create a GIN index, a GIN index on a subset of JSON, ARRAY, or geospatial container column data. Just like partial indexes that use non-container data types, you create a partial GIN index by including a clause, like a WHERE clause, that evaluates to true on a boolean predicate.

GIN indexes on REGIONAL BY ROW tables in multi-region databases

In , most users should use instead of explicit index . When you add an index to a REGIONAL BY ROW table, it is automatically partitioned on the . Explicit index partitioning is not required. While CockroachDB process an or statement on a particular database, creating or modifying an index will throw an error. Similarly, all and statements will be blocked while an index is being modified on a REGIONAL BY ROW table within the same database. For an example that uses unique indexes but applies to all indexes on REGIONAL BY ROW tables, see .

Multi-column GIN indexes

You can create a GIN index with multiple columns. The last indexed column must be one of the inverted types such as JSON, ARRAY, GEOMETRY, and GEOGRAPHY. All preceding columns must have types that are indexable. These indexes may be used for queries that constrain all index columns.

Examples

Create a table with GIN index on a JSONB column

In this example, let’s create a table with a JSONB column and a GIN index:
Then, insert a few rows of data:
Now, run a query that filters on the JSONB column:

Add a GIN index to a table with an ARRAY column

In this example, let’s create a table with an ARRAY column first, and add the GIN index later:
Insert a few rows of data:
Now, let’s add a GIN index to the table and run a query that filters on the ARRAY:

Create a table with a partial GIN index on a JSONB column

In the same users table from Create a table with GIN index on a JSONB column, create a partial GIN index for online users.
Now, use index hinting with the partial GIN index.

Create a trigram index on a STRING column

For an example showing how to create a trigram index on a column, see .

Create a full-text index on a TSVECTOR column

For an example showing how to create a full-text index on a column, see .

Inverted join examples

To run these examples, initialize a demo cluster with the MovR workload. Start the on a 3-node CockroachDB demo cluster with a larger data set.
Create a GIN index on the vehicles table’s ext column.
Check the statement plan for a SELECT statement that uses an inner inverted join.
You can omit the INNER INVERTED JOIN statement by putting v1.ext on the left side of a @> join condition in a WHERE clause and using an for the GIN index.
Use the LEFT INVERTED JOIN hint to perform a left inverted join.

Known limitations

  • CockroachDB does not allow inverted indexes with a .
  • CockroachDB cannot index-accelerate queries with @@ predicates when both sides of the operator are variables for tsvector and tsquery types.
  • and anti joins involving , , or columns with a multi-column or will not take advantage of the index if the prefix columns of the index are unconstrained, or if they are constrained to multiple, constant values. To work around this limitation, make sure that the prefix columns of the index are either constrained to single constant values, or are part of an equality condition with an input column.

See also