> ## Documentation Index
> Fetch the complete documentation index at: https://docs.everybite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Search

> Find dishes that match your guest's dietary needs and preferences

The `search` query is where personalization happens. Pass in a guest's dietary restrictions, allergen exclusions, and nutritional preferences—we traverse our entire ingredient hierarchy to find dishes that are safe and satisfying for them.

Results come back grouped by match quality: full matches, almost matches (with explanations), and non-matches. This gives you everything you need to build interfaces that help guests find exactly what they're looking for—while being transparent about dishes that come close but don't quite fit.

<Info>
  This query requires an API key scoped to your chain in the `Authorization` header. Optionally include `X-Session-ID` for analytics and personalization; you do not need to pass chain or session in the GraphQL query itself.
</Info>

## Query

```graphql theme={null}
query SmartMenuSearch(
  $restaurantId: ID
  $preferences: PreferencesInput
  $searchTerm: String
  $category: String
  $pagination: PaginationArgs
) {
  search(
    restaurantId: $restaurantId
    preferences: $preferences
    searchTerm: $searchTerm
    category: $category
    pagination: $pagination
  ) {
    matches {
      dish { ...DishFields }
      matchStatus
    }
    almostMatches {
      dish { ...DishFields }
      matchStatus
      matchReasons
    }
    notMatches {
      dish { ...DishFields }
      matchStatus
      matchReasons
    }
    counts {
      matches
      almostMatches
      notMatches
      total
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
  }
}
```

## Parameters

All parameters are optional. Chain context comes from your `Authorization` header; restaurant and session context are provided via `restaurantId` (argument) and `X-Session-ID` (header).

| Parameter      | Type             | Description                                                                                                       |
| -------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------- |
| `restaurantId` | ID               | Scope search to a single restaurant. If omitted, the search runs in the chain's widget scope (all widget dishes). |
| `preferences`  | PreferencesInput | Dietary filters (see below)                                                                                       |
| `searchTerm`   | String           | Text search (dish name, description)                                                                              |
| `category`     | String           | Filter by menu category                                                                                           |
| `pagination`   | PaginationArgs   | Pagination controls                                                                                               |

### PreferencesInput

```graphql theme={null}
input PreferencesInput {
  diets: [DietType!]
  excludeAllergens: [AllergenType!]
  calorieRange: RangeInput
  nutrientRanges: NutrientRangesInput
}

input RangeInput {
  min: Int
  max: Int
}

input NutrientRangesInput {
  protein: RangeInput
  carbohydrates: RangeInput
  fatTotal: RangeInput
}

enum DietType {
  VEGAN
  VEGETARIAN
  PESCATARIAN
}

enum AllergenType {
  DAIRY
  EGG
  FISH
  SHELLFISH
  TREE_NUT
  PEANUT
  WHEAT
  SOY
  SESAME
}
```

### PaginationArgs

```graphql theme={null}
input PaginationArgs {
  first: Int       # Number of results when paginating forwards
  after: String    # Cursor for next page (forward pagination)
  last: Int        # Number of results when paginating backwards
  before: String   # Cursor for previous page (backward pagination)
}
```

Pagination uses opaque, base64-encoded page-number cursors:

* **Forward pagination (`first` / `after`)**: When `after` is omitted, the resolver returns the first page. When present, `after` is treated as the cursor for the current page and the resolver returns the next page.
* **Backward pagination (`last` / `before`)**: When both are present, `before` is treated as the cursor for the current page and the resolver returns the previous page. When only `last` is provided, the resolver returns the last page.
* Cursors should be treated as opaque strings; clients should not rely on the encoding details.

## Response

### Match Groups

Results are grouped into three categories:

| Group           | Description                        | Use Case                   |
| --------------- | ---------------------------------- | -------------------------- |
| `matches`       | Fully meets all preferences        | Display prominently        |
| `almostMatches` | Partial match with exceptions      | Display with warnings      |
| `notMatches`    | Does not meet critical preferences | Display greyed out or hide |

### MatchStatus Enum

```graphql theme={null}
enum MatchStatus {
  MATCH           # Fully matches all preferences
  ALMOST_MATCH    # Partial match, has exceptions
  NOT_MATCH       # Does not meet preferences
}
```

### Match Reasons

For `almostMatches` and `notMatches`, the `matchReasons` array explains why:

```json theme={null}
{
  "matchReasons": [
    "Contains Dairy",
    "Exceeds calorie limit (650 > 600)"
  ]
}
```

## Example

<CodeGroup>
  ```graphql Query theme={null}
  query SmartMenuSearch {
    search(
      preferences: {
        diets: [VEGETARIAN]
        excludeAllergens: [PEANUT]
        calorieRange: { max: 600 }
      }
    ) {
      matches {
        dish {
          id
          name
          nutrition { calories, protein }
        }
        matchStatus
      }
      almostMatches {
        dish { id, name }
        matchReasons
      }
      counts {
        matches
        almostMatches
        notMatches
        total
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "search": {
        "matches": [
          {
            "dish": {
              "id": "dish_001",
              "name": "Garden Salad",
              "nutrition": { "calories": 320, "protein": 12 }
            },
            "matchStatus": "MATCH"
          },
          {
            "dish": {
              "id": "dish_004",
              "name": "Mediterranean Bowl",
              "nutrition": { "calories": 450, "protein": 18 }
            },
            "matchStatus": "MATCH"
          }
        ],
        "almostMatches": [
          {
            "dish": { "id": "dish_002", "name": "Caesar Salad" },
            "matchReasons": ["Contains Dairy"]
          }
        ],
        "counts": {
          "matches": 8,
          "almostMatches": 4,
          "notMatches": 12,
          "total": 24
        }
      }
    }
  }
  ```
</CodeGroup>

## Examples

### Basic Search (No Filters)

```graphql theme={null}
query {
  search {
    matches {
      dish {
        id
        name
        nutrition { calories }
      }
    }
    counts { total }
  }
}
```

<Accordion title="Example Response">
  ```json theme={null}
  {
    "data": {
      "search": {
        "matches": [
          { "dish": { "id": "dish_001", "name": "Garden Salad", "nutrition": { "calories": 320 } } },
          { "dish": { "id": "dish_002", "name": "Mediterranean Bowl", "nutrition": { "calories": 450 } } }
        ],
        "counts": { "total": 24 }
      }
    }
  }
  ```
</Accordion>

### Search with Dietary Preferences

```graphql theme={null}
query VegetarianSearch {
  search(
    preferences: {
      diets: [VEGETARIAN]
      excludeAllergens: [PEANUT, TREE_NUT]
      calorieRange: { max: 600 }
    }
  ) {
    matches {
      dish {
        id
        name
        category
        nutrition { calories, protein }
        diets { type }
      }
      matchStatus
    }
    almostMatches {
      dish { id, name }
      matchReasons
    }
    counts {
      matches
      almostMatches
      notMatches
    }
  }
}
```

<Accordion title="Example Response">
  ```json theme={null}
  {
    "data": {
      "search": {
        "matches": [
          {
            "dish": { "id": "dish_001", "name": "Garden Salad", "category": "Salads", "nutrition": { "calories": 320, "protein": 12 }, "diets": [{ "type": "VEGETARIAN" }] },
            "matchStatus": "MATCH"
          }
        ],
        "almostMatches": [
          { "dish": { "id": "dish_003", "name": "Caesar Salad" }, "matchReasons": ["Contains Dairy"] }
        ],
        "counts": { "matches": 8, "almostMatches": 4, "notMatches": 12 }
      }
    }
  }
  ```
</Accordion>

### Text Search with Category Filter

```graphql theme={null}
query SearchSalads {
  search(
    searchTerm: "chicken"
    category: "Salads"
    pagination: { first: 10 }
  ) {
    matches {
      dish {
        id
        name
        description
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

<Accordion title="Example Response">
  ```json theme={null}
  {
    "data": {
      "search": {
        "matches": [
          { "dish": { "id": "dish_005", "name": "Grilled Chicken Salad", "description": "Mixed greens with grilled chicken breast" } },
          { "dish": { "id": "dish_006", "name": "Chicken Caesar Salad", "description": "Romaine, parmesan, croutons, grilled chicken" } }
        ],
        "pageInfo": { "hasNextPage": false, "endCursor": "cursor_abc123" }
      }
    }
  }
  ```
</Accordion>

### Paginated Results

```graphql theme={null}
# First page
query Page1 {
  search(pagination: { first: 20 }) {
    matches { dish { id, name } }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

# Next page (use endCursor from previous response)
query Page2 {
  search(pagination: { first: 20, after: "eyJpZCI6MjB9" }) {
    matches { dish { id, name } }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

## Full Example Response

<Accordion title="Complete Response">
  ```json theme={null}
  {
    "data": {
      "search": {
        "matches": [
          {
            "dish": {
              "id": "dish_001",
              "partnerDishId": "olo_12345",
              "chainProductId": "olo_prod_78901",
              "name": "Garden Salad",
              "category": "Salads",
              "imageUrl": "https://cdn.example.com/garden-salad.jpg",
              "nutrition": {
                "calories": 320,
                "protein": 12,
                "carbohydrates": 28,
                "fatTotal": 15
              },
              "allergens": [],
              "diets": [
                { "type": "VEGAN", "displayName": "Vegan" },
                { "type": "VEGETARIAN", "displayName": "Vegetarian" }
              ]
            },
            "matchStatus": "MATCH"
          },
          {
            "dish": {
              "id": "dish_004",
              "partnerDishId": "olo_12348",
              "chainProductId": "olo_prod_78904",
              "name": "Mediterranean Bowl",
              "category": "Bowls",
              "imageUrl": "https://cdn.example.com/med-bowl.jpg",
              "nutrition": {
                "calories": 450,
                "protein": 18,
                "carbohydrates": 52,
                "fatTotal": 20
              },
              "allergens": [],
              "diets": [
                { "type": "VEGETARIAN", "displayName": "Vegetarian" }
              ]
            },
            "matchStatus": "MATCH"
          }
        ],
        "almostMatches": [
          {
            "dish": {
              "id": "dish_002",
              "partnerDishId": "olo_12346",
              "chainProductId": "olo_prod_78902",
              "name": "Caesar Salad",
              "category": "Salads",
              "imageUrl": "https://cdn.example.com/caesar-salad.jpg",
              "nutrition": {
                "calories": 450,
                "protein": 18,
                "carbohydrates": 25,
                "fatTotal": 28
              },
              "allergens": [
                { "type": "DAIRY", "displayName": "Dairy" }
              ],
              "diets": [
                { "type": "VEGETARIAN", "displayName": "Vegetarian" }
              ]
            },
            "matchStatus": "ALMOST_MATCH",
            "matchReasons": ["Contains Dairy"]
          }
        ],
        "notMatches": [
          {
            "dish": {
              "id": "dish_003",
              "partnerDishId": "olo_12347",
              "name": "Grilled Chicken Salad",
              "category": "Salads"
            },
            "matchStatus": "NOT_MATCH",
            "matchReasons": ["Contains Meat"]
          }
        ],
        "counts": {
          "matches": 8,
          "almostMatches": 4,
          "notMatches": 12,
          "total": 24
        },
        "pageInfo": {
          "hasNextPage": true,
          "hasPreviousPage": false,
          "startCursor": "eyJpZCI6MX0=",
          "endCursor": "eyJpZCI6MjR9"
        }
      }
    }
  }
  ```
</Accordion>
