---
title: "Document secondary indexes"
description: "Learn how to perform secondary index queries on document records using the Aerospike Go client."
---

# Document secondary indexes

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

This guide provides practical Go examples for executing secondary index queries on document records in Aerospike. It demonstrates two approaches: indexing a dedicated bin during record insertion and using MAPVALUES indexes to query nested values inside document bins.

## Secondary index usage

The following example demonstrates using a [secondary index](https://aerospike.com/docs/database/learn/architecture/data-storage/secondary-index) to find a document record. It does the following:

-   Create a document.
-   Add the document to an Aerospike set.
-   Create a secondary index.
-   Query the document using the secondary index.

The example demonstrates two ways of setting up a secondary index. The first approach stores the index target in a dedicated bin at write time. The second approach indexes values inside the document bin with a `MAPVALUES` index.

### Setup

Import the necessary helpers and create a client connection.

```go
import (

  "fmt"

  "log"

  as "github.com/aerospike/aerospike-client-go/v8"

)

client, err := as.NewClient("127.0.0.1", 3000)

if err != nil {

  log.Fatal(err)

}

defer client.Close()

namespace := "test"

setName := "table1"
```

### Create a JSON document

Prepare the JSON document to send to Aerospike.

```go
transactions := []map[string]interface{}{

  {"txn_id": "1111", "name": "Davis", "item_id": "A1234", "count": 1},

  {"txn_id": "2222", "name": "Johnson", "item_id": "B2345", "count": 2},

  {"txn_id": "3333", "name": "Johnson", "item_id": "C3456", "count": 2},

  {"txn_id": "4444", "name": "Lee", "item_id": "D4567", "count": 3},

}
```

### First approach: write-time indexing

Write each document to Aerospike.

```go
writePolicy := as.NewWritePolicy(0, 0)

writePolicy.SendKey = true

for _, transaction := range transactions {

  key, err := as.NewKey(namespace, setName, transaction["txn_id"])

  if err != nil {

    log.Fatal(err)

  }

  bins := as.BinMap{

    "name":        transaction["name"],

    "transaction": transaction,

  }

  if err := client.Put(writePolicy, key, bins); err != nil {

    log.Fatalf("Create failed: %v", err)

  }

  record, err := client.Get(nil, key)

  if err != nil {

    log.Fatalf("Read failed: %v", err)

  }

  fmt.Printf("Create succeeded\nKey: %v\nRecord: %v\n", key.Value(), record.Bins)

}
```

#### Create a secondary index on a dedicated bin

Create a secondary index on the dedicated `name` bin and execute a query. This approach stores the index target in its own bin during record insertion.

```go
task, err := client.CreateIndex(nil, namespace, setName, "test_name_idx", "name", as.STRING)

if err != nil {

  log.Fatalf("Create index failed: %v", err)

}

<-task.OnComplete()

stmt := as.NewStatement(namespace, setName)

stmt.SetFilter(as.NewEqualFilter("name", "Johnson"))

recordSet, err := client.Query(nil, stmt)

if err != nil {

  log.Fatalf("Query failed: %v", err)

}

for res := range recordSet.Results() {

  if res.Err != nil {

    log.Fatalf("Query result error: %v", res.Err)

  }

  fmt.Printf("Key: %v | Record: %v\n", res.Record.Key.Value(), res.Record.Bins)

}

recordSet.Close()

if err := client.DropIndex(nil, namespace, setName, "test_name_idx"); err != nil {

  log.Fatalf("Drop index failed: %v", err)

}
```

### Second approach: create a MAPVALUES index on the document bin

Create a secondary index on map values in the `transaction` bin and execute a query. This approach indexes nested values inside the document bin directly.

```go
task, err := client.CreateComplexIndex(

  nil, namespace, setName, "test_transaction_idx", "transaction", as.STRING, as.ICT_MAPVALUES)

if err != nil {

  log.Fatalf("Create index failed: %v", err)

}

<-task.OnComplete()

stmt := as.NewStatement(namespace, setName)

stmt.SetFilter(as.NewContainsFilter("transaction", as.ICT_MAPVALUES, "Johnson"))

recordSet, err := client.Query(nil, stmt)

if err != nil {

  log.Fatalf("Query failed: %v", err)

}

for res := range recordSet.Results() {

  if res.Err != nil {

    log.Fatalf("Query result error: %v", res.Err)

  }

  fmt.Printf("Key: %v | Record: %v\n", res.Record.Key.Value(), res.Record.Bins)

}

recordSet.Close()

if err := client.DropIndex(nil, namespace, setName, "test_transaction_idx"); err != nil {

  log.Fatalf("Drop index failed: %v", err)

}
```

## Code block: dedicated bin index

Expand this section for a single code block to run the dedicated bin index example.

```go
package main

import (

  "fmt"

  "log"

  as "github.com/aerospike/aerospike-client-go/v8"

)

func main() {

  client, err := as.NewClient("127.0.0.1", 3000)

  if err != nil {

    log.Fatal(err)

  }

  defer client.Close()

  namespace := "test"

  setName := "table1"

  records := []map[string]interface{}{

    {"record_id": "1111", "name": "Davis", "item_id": "A1234", "count": 1},

    {"record_id": "2222", "name": "Johnson", "item_id": "B2345", "count": 2},

    {"record_id": "3333", "name": "Johnson", "item_id": "C3456", "count": 2},

    {"record_id": "4444", "name": "Lee", "item_id": "D4567", "count": 3},

  }

  writePolicy := as.NewWritePolicy(0, 0)

  writePolicy.SendKey = true

  for _, record := range records {

    key, err := as.NewKey(namespace, setName, record["record_id"])

    if err != nil {

      log.Fatal(err)

    }

    bins := as.BinMap{

      "name":        record["name"],

      "transaction": record,

    }

    if err := client.Put(writePolicy, key, bins); err != nil {

      log.Fatalf("Create failed: %v", err)

    }

  }

  task, err := client.CreateIndex(nil, namespace, setName, "test_name_idx", "name", as.STRING)

  if err != nil {

    log.Fatalf("Create index failed: %v", err)

  }

  <-task.OnComplete()

  stmt := as.NewStatement(namespace, setName)

  stmt.SetFilter(as.NewEqualFilter("name", "Johnson"))

  recordSet, err := client.Query(nil, stmt)

  if err != nil {

    log.Fatalf("Query failed: %v", err)

  }

  for res := range recordSet.Results() {

    if res.Err != nil {

      log.Fatalf("Query result error: %v", res.Err)

    }

    fmt.Printf("Key: %v | Record: %v\n", res.Record.Key.Value(), res.Record.Bins)

  }

  recordSet.Close()

  if err := client.DropIndex(nil, namespace, setName, "test_name_idx"); err != nil {

    log.Fatalf("Drop index failed: %v", err)

  }

}
```

## Code block — MAPVALUES index on document bin

Expand this section for a single code block to run the MAPVALUES index example.

```go
package main

import (

  "fmt"

  "log"

  as "github.com/aerospike/aerospike-client-go/v8"

)

func main() {

  client, err := as.NewClient("127.0.0.1", 3000)

  if err != nil {

    log.Fatal(err)

  }

  defer client.Close()

  namespace := "test"

  setName := "table1"

  transactions := []map[string]interface{}{

    {"txn_id": "1111", "name": "Davis", "item_id": "A1234", "count": 1},

    {"txn_id": "2222", "name": "Johnson", "item_id": "B2345", "count": 2},

    {"txn_id": "3333", "name": "Johnson", "item_id": "C3456", "count": 2},

    {"txn_id": "4444", "name": "Lee", "item_id": "D4567", "count": 3},

  }

  writePolicy := as.NewWritePolicy(0, 0)

  writePolicy.SendKey = true

  for _, transaction := range transactions {

    key, err := as.NewKey(namespace, setName, transaction["txn_id"])

    if err != nil {

      log.Fatal(err)

    }

    bins := as.BinMap{

      "name":        transaction["name"],

      "transaction": transaction,

    }

    if err := client.Put(writePolicy, key, bins); err != nil {

      log.Fatalf("Create failed: %v", err)

    }

  }

  task, err := client.CreateComplexIndex(

    nil, namespace, setName, "test_transaction_idx", "transaction", as.STRING, as.ICT_MAPVALUES)

  if err != nil {

    log.Fatalf("Create index failed: %v", err)

  }

  <-task.OnComplete()

  stmt := as.NewStatement(namespace, setName)

  stmt.SetFilter(as.NewContainsFilter("transaction", as.ICT_MAPVALUES, "Johnson"))

  recordSet, err := client.Query(nil, stmt)

  if err != nil {

    log.Fatalf("Query failed: %v", err)

  }

  for res := range recordSet.Results() {

    if res.Err != nil {

      log.Fatalf("Query result error: %v", res.Err)

    }

    fmt.Printf("Key: %v | Record: %v\n", res.Record.Key.Value(), res.Record.Bins)

  }

  recordSet.Close()

  if err := client.DropIndex(nil, namespace, setName, "test_transaction_idx"); err != nil {

    log.Fatalf("Drop index failed: %v", err)

  }

}
```