Every [record](/guides/core-concepts-key-terms#record) in an index must contain an ID and a dense or sparse vector. In addition, you can include [metadata key-value pairs](/guides/index-data-indexing-overview#metadata) to store related information or context. When you search the index, you can then include a metadata filter to limit the search to records matching the filter expression. Metadata filtering works the same way on [indexes with a document schema](/guides/index-data-data-modeling#documents), which also support [text-match filters](#text-match-filters) on full-text fields.

## Search with a metadata filter

The following code searches for the 3 records that are most semantically similar to a query and that have a `category` metadata field with the value `digestive system`.

:::::tabs
::::tab{title="Search with text"}
:::callout{intent="note"}
Searching with text is supported only for [indexes with integrated embedding](/guides/index-data-indexing-overview#integrated-embedding).
:::

:::code-group
```python Python theme={null}
from pinecone import Pinecone

pc = Pinecone(api_key="YOUR_API_KEY")

# To get the unique host for an index, 
# see https://docs.pinecone.io/guides/manage-data/target-an-index
index = pc.Index(host="INDEX_HOST")

filtered_results = index.search(
    namespace="example-namespace", 
    query={
        "inputs": {"text": "Disease prevention"}, 
        "top_k": 3,
        "filter": {"category": "digestive system"},
    },
    fields=["category", "chunk_text"]
)

print(filtered_results)
```

```javascript JavaScript theme={null}
import { Pinecone } from '@pinecone-database/pinecone'

const pc = new Pinecone({ apiKey: "YOUR_API_KEY" })

// To get the unique host for an index, 
// see https://docs.pinecone.io/guides/manage-data/target-an-index
const namespace = pc.index("INDEX_NAME", "INDEX_HOST").namespace("example-namespace");

const response = await namespace.searchRecords({
  query: {
    topK: 3,
    inputs: { text: "Disease prevention" },
    filter: { category: "digestive system" }
  },
  fields: ['chunk_text', 'category']
});

console.log(response);
```

```java Java theme={null}
import io.pinecone.clients.Index;
import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import org.openapitools.db_data.client.ApiException;
import org.openapitools.db_data.client.model.SearchRecordsResponse;

import java.util.*;

public class SearchText {
    public static void main(String[] args) throws ApiException {
        PineconeConfig config = new PineconeConfig("YOUR_API_KEY");
        // To get the unique host for an index, 
        // see https://docs.pinecone.io/guides/manage-data/target-an-index
        config.setHost("INDEX_HOST");
        PineconeConnection connection = new PineconeConnection(config);

        Index index = new Index(config, connection, "integrated-dense-java");

        String query = "Disease prevention";
        List<String> fields = new ArrayList<>();
        fields.add("category");
        fields.add("chunk_text");

        Map<String, Object> filter = new HashMap<>();
        filter.put("category", "digestive system");

        // Search the index
        SearchRecordsResponse recordsResponse = index.searchRecordsByText(query,  "example-namespace", fields, 3, filter, null);

        // Print the results
        System.out.println(recordsResponse);
    }
}
```

```go Go theme={null}
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func prettifyStruct(obj interface{}) string {
  	bytes, _ := json.MarshalIndent(obj, "", "  ")
    return string(bytes)
}

func main() {
    ctx := context.Background()

    pc, err := pinecone.NewClient(pinecone.NewClientParams{
        ApiKey: "YOUR_API_KEY",
    })
    if err != nil {
        log.Fatalf("Failed to create Client: %v", err)
    }

    // To get the unique host for an index, 
    // see https://docs.pinecone.io/guides/manage-data/target-an-index
    idxConnection, err := pc.Index(pinecone.NewIndexConnParams{Host: "INDEX_HOST", Namespace: "example-namespace"})
    if err != nil {
        log.Fatalf("Failed to create IndexConnection for Host: %v", err)
    } 

    metadataMap := map[string]interface{}{
        "category": map[string]interface{}{
            "$eq": "digestive system",
        },
    }
    res, err := idxConnection.SearchRecords(ctx, &pinecone.SearchRecordsRequest{
        Query: pinecone.SearchRecordsQuery{
            TopK: 3,
            Inputs: &map[string]interface{}{
                "text": "Disease prevention",
            },
            Filter: &metadataMap,
        },
        Fields: &[]string{"chunk_text", "category"},
    })
    if err != nil {
        log.Fatalf("Failed to search records: %v", err)
    }
    fmt.Printf(prettifyStruct(res))
}
```

```shell curl theme={null}
INDEX_HOST="INDEX_HOST"
NAMESPACE="YOUR_NAMESPACE"
PINECONE_API_KEY="YOUR_API_KEY"

curl "https://$INDEX_HOST/records/namespaces/$NAMESPACE/search" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Api-Key: $PINECONE_API_KEY" \
  -H "X-Pinecone-Api-Version: 2026-07" \
  -d '{
        "query": {
            "inputs": {"text": "Disease prevention"},
            "top_k": 3,
            "filter": {"category": "digestive system"}
        },
        "fields": ["category", "chunk_text"]
     }'
```
:::
::::

::::tab{title="Search with a vector"}
:::code-group
```Python Python theme={null}
from pinecone.grpc import PineconeGRPC as Pinecone

pc = Pinecone(api_key="YOUR_API_KEY")

# To get the unique host for an index, 
# see https://docs.pinecone.io/guides/manage-data/target-an-index
index = pc.Index(host="INDEX_HOST")

index.query(
    namespace="example-namespace",
    vector=[0.0236663818359375,-0.032989501953125, ..., -0.01041412353515625,0.0086669921875], 
    top_k=3,
    filter={
        "category": {"$eq": "digestive system"}
    },
    include_metadata=True,
    include_values=False
)
```

```JavaScript JavaScript theme={null}
import { Pinecone } from '@pinecone-database/pinecone'

const pc = new Pinecone({ apiKey: "YOUR_API_KEY" })

// To get the unique host for an index, 
// see https://docs.pinecone.io/guides/manage-data/target-an-index
const index = pc.index("INDEX_NAME", "INDEX_HOST")

const queryResponse = await index.namespace('example-namespace').query({
    vector: [0.0236663818359375,-0.032989501953125,...,-0.01041412353515625,0.0086669921875],
    topK: 3,
    filter: {
        "category": { "$eq": "digestive system" }
    },
    includeValues: false,
    includeMetadata: true,
});
```

```java Java theme={null}
import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import io.pinecone.clients.Index;
import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import io.pinecone.unsigned_indices_model.QueryResponseWithUnsignedIndices;

import java.util.Arrays;
import java.util.List;

public class QueryExample {
    public static void main(String[] args) {
        PineconeConfig config = new PineconeConfig("YOUR_API_KEY");
        // To get the unique host for an index, 
        // see https://docs.pinecone.io/guides/manage-data/target-an-index
        config.setHost("INDEX_HOST");
        PineconeConnection connection = new PineconeConnection(config);
        Index index = new Index(connection, "INDEX_NAME");
        List<Float> query = Arrays.asList(0.0236663818359375f, -0.032989501953125f, ..., -0.01041412353515625f, 0.0086669921875f);
        Struct filter = Struct.newBuilder()
                .putFields("category", Value.newBuilder()
                        .setStructValue(Struct.newBuilder()
                                .putFields("$eq", Value.newBuilder()
                                        .setStringValue("digestive system")
                                        .build()))
                        .build())
                .build();

        QueryResponseWithUnsignedIndices queryResponse = index.query(1, query, null, null, null, "example-namespace", filter, false, true);
        System.out.println(queryResponse);
    }
}
```

```go Go theme={null}
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/pinecone-io/go-pinecone/v4/pinecone"
)

func prettifyStruct(obj interface{}) string {
	bytes, _ := json.MarshalIndent(obj, "", "  ")
	return string(bytes)
}

func main() {
    ctx := context.Background()

    pc, err := pinecone.NewClient(pinecone.NewClientParams{
        ApiKey: "YOUR_API_KEY",
    })
    if err != nil {
        log.Fatalf("Failed to create Client: %v", err)
    }

    // To get the unique host for an index, 
    // see https://docs.pinecone.io/guides/manage-data/target-an-index
    idxConnection, err := pc.Index(pinecone.NewIndexConnParams{Host: "INDEX_HOST", Namespace: "example-namespace"})
    if err != nil {
        log.Fatalf("Failed to create IndexConnection for Host: %v", err)
  	}

    queryVector := []float32{0.0236663818359375,-0.032989501953125,...,-0.01041412353515625,0.0086669921875}

    metadataMap := map[string]interface{}{
        "category": map[string]interface{}{
            "$eq": "digestive system",
        },
    }

    metadataFilter, err := structpb.NewStruct(metadataMap)
    if err != nil {
        log.Fatalf("Failed to create metadata map: %v", err)
    }

    res, err := idxConnection.QueryByVectorValues(ctx, &pinecone.QueryByVectorValuesRequest{
        Vector:          queryVector,
        TopK:            3,
        MetadataFilter: metadataFilter,
        IncludeValues:   false,
        IncludeMetadata: true,
    })
    if err != nil {
        log.Fatalf("Error encountered when querying by vector: %v", err)
    } else {
        fmt.Printf(prettifyStruct(res))
    }
}
```

```bash curl theme={null}
# To get the unique host for an index,
# see https://docs.pinecone.io/guides/manage-data/target-an-index
PINECONE_API_KEY="YOUR_API_KEY"
INDEX_HOST="INDEX_HOST"

curl "https://$INDEX_HOST/query" \
  -H "Api-Key: $PINECONE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "X-Pinecone-Api-Version: 2026-07" \
  -d '{
        "vector": [0.0236663818359375,-0.032989501953125,...,-0.01041412353515625,0.0086669921875],
        "namespace": "example-namespace",
        "topK": 3,
        "filter": {"category": {"$eq": "digestive system"}},
        "includeMetadata": true,
        "includeValues": false
    }'
```
:::
::::
:::::

## Metadata filter expressions

Pinecone's filtering language supports the following operators:

| Operator  | Function                                                                                                                         | Supported types         |
| :-------- | :------------------------------------------------------------------------------------------------------------------------------- | :---------------------- |
| `$eq`     | Matches vectors with metadata values that are equal to a specified value. Example: `{"genre": {"$eq": "documentary"}}`           | Number, string, boolean |
| `$ne`     | Matches vectors with metadata values that aren't equal to a specified value. Example: `{"genre": {"$ne": "drama"}}`              | Number, string, boolean |
| `$gt`     | Matches vectors with metadata values that are greater than a specified value. Example: `{"year": {"$gt": 2019}}`                 | Number                  |
| `$gte`    | Matches vectors with metadata values that are greater than or equal to a specified value. Example:`{"year": {"$gte": 2020}}`     | Number                  |
| `$lt`     | Matches vectors with metadata values that are less than a specified value. Example: `{"year": {"$lt": 2020}}`                    | Number                  |
| `$lte`    | Matches vectors with metadata values that are less than or equal to a specified value. Example: `{"year": {"$lte": 2020}}`       | Number                  |
| `$in`     | Matches vectors with metadata values that are in a specified array. Example: `{"genre": {"$in": ["comedy", "documentary"]}}`     | String, number          |
| `$nin`    | Matches vectors with metadata values that aren't in a specified array. Example: `{"genre": {"$nin": ["comedy", "documentary"]}}` | String, number          |
| `$exists` | Matches vectors with the specified metadata field. Example: `{"genre": {"$exists": true}}`                                       | Number, string, boolean |
| `$and`    | Joins query clauses with a logical `AND`. Example: `{"$and": [{"genre": {"$eq": "drama"}}, {"year": {"$gte": 2020}}]}`           | -                       |
| `$or`     | Joins query clauses with a logical `OR`. Example: `{"$or": [{"genre": {"$eq": "drama"}}, {"year": {"$gte": 2020}}]}`             | -                       |
| `$not`    | Matches vectors that don't match the wrapped clause. Example: `{"genre": {"$not": {"$eq": "drama"}}}`                            | -                       |

:::callout{intent="note"}
At the top level, list one or more fields (combined with implicit AND) or combine clauses with the logical operators `$and` and `$or`. Use `$not` to negate a clause, as shown in the table. A bare comparison operator (like `$gt`) can't appear at the top level; nest it under a field.
:::

:::callout{intent="note"}
Each `$in` or `$nin` operator accepts a maximum of 10,000 values. Exceeding this limit will cause the request to fail. For more information, see [Metadata filter limits](/guides/apis-database-limits-operation-limits#metadata-filter-limits).
:::

For example, the following has a `"genre"` metadata field with a list of strings:

```JSON JSON theme={null}
{ "genre": ["comedy", "documentary"] }
```

This means `"genre"` takes on both values, and requests with the following filters will match:

```JSON JSON theme={null}
{"genre":"comedy"}

{"genre": {"$in":["documentary","action"]}}

{"$and": [{"genre": "comedy"}, {"genre":"documentary"}]}
```

However, requests with the following filter will **not** match:

```JSON JSON theme={null}
{ "$and": [{ "genre": "comedy" }, { "genre": "drama" }] }
```

Additionally, requests with the following filters will **not** match because they're invalid. They will result in a compilation error:

```json JSON theme={null}
# INVALID QUERY:
{"genre": ["comedy", "documentary"]}
```

```json JSON theme={null}
# INVALID QUERY:
{"genre": {"$eq": ["comedy", "documentary"]}}
```

## Text-match filters

On [indexes with a document schema](/guides/index-data-data-modeling#documents), three additional operators match text on `string` fields that have `full_text_search` enabled. They narrow the candidate set before scoring, the same way metadata operators do.

| Operator        | Example                                           | Description                                                        |
| --------------- | ------------------------------------------------- | ------------------------------------------------------------------ |
| `$match_phrase` | `{"body": {"$match_phrase": "machine learning"}}` | Exact phrase match (contiguous tokens) on a text-searchable field. |
| `$match_all`    | `{"body": {"$match_all": "machine learning"}}`    | All tokens present, in any order.                                  |
| `$match_any`    | `{"body": {"$match_any": "AI robotics"}}`         | At least one token present.                                        |

These operators share a few rules:

- **Where they apply.** Fields declared with a `full_text_search` config object.
- **Tokenization.** They reuse the field's configured tokenizer and stemmer, so a token that matches in BM25 scoring will match in a text-match filter.
- **Lucene-style operators.** Phrase slop (`"phrase"~N`), term boosting (`^N`), and phrase prefix (`"phrase pre"*`) aren't parsed. Values are literal text and match semantics come from the operator name. To use those operators, score with `query_string` instead.
- **Composition.** They compose freely with metadata operators under `$and`, `$or`, and `$not` at any nesting level:

```json theme={null}
{
  "$and": [
    { "body": { "$match_all": "federal reserve" } },
    { "category": { "$eq": "finance" } },
    { "year": { "$gte": 2024 } }
  ]
}
```

:::callout{intent="warning"}
Text-match operators (`$match_phrase`, `$match_all`, `$match_any`) are supported on `documents/search` and on [`documents/fetch`](/guides/manage-data-fetch-data#fetch-documents). A filtered [`documents/update`](/guides/manage-data-update-data#update-documents) or [`documents/delete`](/guides/manage-data-delete-data#delete-documents) rejects them with a `400` and takes plain metadata filters only. To update or delete by text match, search or fetch to retrieve the matching IDs first, then act on those IDs.
:::

## Related pages

- [Account management](./account-management-index.md)
- [Admin](./admin-2-index.md)
- [Admin](./admin-index.md)
- [APIs](./apis-index.md)
- [Architecture](./architecture-index.md)
- [Assistants](./assistants-index.md)
- [Bring Your Own Cloud](./bring-your-own-cloud-index.md)
- [Build an assistant](./build-an-assistant-index.md)
- [Build an integration](./build-an-integration-index.md)
- [Changelog](./changelog-index.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
