# Search a Recipe

## Recipe Search

<mark style="color:green;">`POST`</mark> `https://api.whisk.com/recipe/v2/search`

#### Request Body

| Name           | Type   | Description |
| -------------- | ------ | ----------- |
| Search Request | object |             |

{% tabs %}
{% tab title="200 This will return a collection of recipes objects (see Recipe Data section for more information)." %}

```javascript
{
  "data": [
    {
      "content": {
        "id": "97f77cceca5d11e7ae7e42010a9a0035",
        "name": "Quick coronation chicken sandwich",
        "description": "Use leftover roast chicken to make a delicious coronation chicken sandwich ready to pack in your lunchbox.",
        "images": [
          {
            "url": "http://cdnwp.audiencemedia.com/wp-content/uploads/2015/01/478091-1-eng-GB_coronation-chick-sandwich-470x540.jpg",
            "responsive": {
              "url": "https://lh3.googleusercontent.com/crjY_vyxK8kdBYYBd6VVtlGwIXuG3pn9DuCSWP4-_VtURbrYfpKPrYDMmrlCwc8kqSAsgCBtjhqU2C7PEjU0wMDh4FSK",
              "width": 470,
              "height": 540
            }
          },
          ...
        ],
        "source": {
          "name": "deliciousmagazine.co.uk",
          "displayName": "delicious. magazine",
          "sourceRecipeUrl": "http://www.deliciousmagazine.co.uk/recipes/quick-coronation-chicken-sandwich/",
          "license": "Fairuse",
          "image": {
            "url": "https://res.cloudinary.com/whisk/image/upload/v1401879186/content/publisher_logos/delicious-magazine-logo.png",
            "responsive": {
              "url": "https://res.cloudinary.com/whisk/image/upload/v1401879186/content/publisher_logos/delicious-magazine-logo.png",
              "width": 200,
              "height": 200
            }
          }
        },
        "author": {
          "name": "Author name",
          "image": {
            "url": "https://image-cdn.whisk.com/image/upload/v1523894700/custom_upload/ba4d7363cd46c736675d2cc08754f5bc.png",
            "responsive": {
              "url": "https://image-cdn.whisk.com/image/upload/v1523894700/custom_upload/ba4d7363cd46c736675d2cc08754f5bc.png",
              "width": 800,
              "height": 800
            }
          }
        },
        "numberOfServings": 1,
        "labels": {
          "mealType": [],
          "cuisine": [],
          "category": [
            {
              "name": "quick-and-easy",
              "displayName": "Quick and easy"
            }
          ]
        }
      },
      "matchedIngredients": [
        {
          "name": "meat"
        },
        {
          "name": "bread"
        }
      ]
    },
    {
      "content": {
        ...
      }
    }
  ],
  "paging": {
    "cursors": {
      "after": "eyJpZCI6ImNhZjVlOWY3Y2YxNzFkYjBmZTdkYjJmOTM4M2M0ZDIzIiwiaW5kZXgiOjF9"
    },
    "total": 1300
  }
```

{% endtab %}
{% endtabs %}

{% hint style="info" %} <mark style="background-color:orange;">**Logic for A/B was temporarily disabled. It could be enabled again by request**</mark>

Internal logic on whisk back-end was updated for this endpoint. Implicit profile functionality and A/B test were implemented.

For ST Cooking `user_id` for which embeddings were uploaded, search results will be generated by elastic Search according to corresponded embedding.

A/B test was added on the BE. Users splitted by `murmurHash2 (64)` into 2 groups:

1. Provide results with implicit profile functionality
2. Provide results without implicit profile functionality

So for first group embeddings should be added for only first group while call `method = 'whisk.api.recipe.v2.RecipeAPI/SearchRecipes'`
{% endhint %}

The Search API accepts a query object that looks similar to this:

```javascript
{
  "query": "string",
  "language": "string",
  "country": "string",
  "min_ingredients_should_match": 0,
  "max_time_in_minutes": 0,
  "min_health_score": 0,
  "ordering": "ORDERING_INVALID",
  "has_instructions": true,
  "paging": {
    ...
  },
  "labels": [
    ...
  ],
  "exclude_ingredients": {
    ...
  },
  "include_ingredients": {
    ...
  },
  
  "glycemic_filter": {
    ...
  },
  "nutrition": [
    ...
  ],
  "fields": [
    ...
  ],
  "custom_labels": {
    ...
  },
  "apply_implicit_preferences": true
}
```

It is made up of the following attributes:

<table data-header-hidden><thead><tr><th width="326.3333333333333">ATTRIBUTE</th><th>TYPE</th><th>DESCRIPTION</th></tr></thead><tbody><tr><td>ATTRIBUTE</td><td>TYPE</td><td>DESCRIPTION</td></tr><tr><td><code>query</code></td><td>string</td><td>Search phrase</td></tr><tr><td><code>language</code></td><td>string</td><td>recipes should be in this language, it allows only ISO 639-1 language codes Default value: en</td></tr><tr><td><code>minIngredientsShouldMatch</code></td><td>int</td><td>if includeIngredients specified, number of minimum ingredients, which should be matched</td></tr><tr><td><code>maxTimeInMinutes</code></td><td>int</td><td>recipes should take this total time at maximum</td></tr><tr><td><code>minHealthScore</code></td><td>double</td><td>filter recipes with healthScore more than specified value</td></tr><tr><td><code>ordering</code></td><td></td><td></td></tr><tr><td><code>has_instructions</code></td><td></td><td></td></tr><tr><td><code>paging</code></td><td><a href="#paging">object</a></td><td></td></tr><tr><td><code>labels</code></td><td><a href="#custom-labels">array</a></td><td></td></tr><tr><td><code>exclude_ingredients</code></td><td><a href="#exclude-ingredients">object</a></td><td></td></tr><tr><td><code>include_ingredients</code></td><td><a href="#include-ingredients">object</a></td><td></td></tr><tr><td><code>glycemic_filter</code></td><td><a href="#glycemic-filter">object</a></td><td></td></tr><tr><td><code>nutrition</code></td><td><a href="#nutrition">array</a></td><td></td></tr><tr><td><code>fields</code></td><td>array</td><td></td></tr><tr><td><code>custom_labels</code></td><td>object</td><td></td></tr><tr><td><pre><code>apply_implicit_preferences
</code></pre></td><td>boolean</td><td>Flag to switch search results between search with embeddings and regular search</td></tr></tbody></table>

In addition to the base attributes, there are additional objects that contain more detailed information on the search parameters.

### Paging

```bash
"paging": {
  "limit": 0,
  "cursors": {
    "after": "string",
    "before": "string"
  }
},
```

### Labels

```bash
"labels": [
  {
    "label": {
      "diet": "DIET_INVALID",
      "category": "CATEGORY_INVALID",
      "avoidance": "AVOIDANCE_INVALID",
      "cuisine": "CUISINE_INVALID",
      "meal_type": "MEAL_TYPE_INVALID",
      "nutrition": "NUTRITION_INVALID",
      "feature": "string",
      "holiday": "HOLIDAY_INVALID",
      "seasonality": "SEASONALITY_INVALID"
    },
    "boost": true
  }
],
```

### Exclude Ingredients

```bash
"exclude_ingredients": {
  "list": [
    "string"
  ]
},
```

### Include Ingredients

```bash
"include_ingredients": {
  "list": [
    "string"
  ]
},

```

### Glycemic Filter

```bash
"glycemic_filter": {
  "glycemic_index": 0,
  "glycemic_load_total": 0,
  "glycemic_load_serving": 0
},
```

### Nutrition

Allow to use numeric values of

* Energy - `NUTRITION_ENERGY`
* Fat - `NUTRITION_FAT`
* Proteine - `NUTRITION_PROTEINE`
* Carbohydrate - `NUTRITION_CARBOHYDRATE`

Values can set by using `gt`,`lt`,`gte`,`lte` expressions, for example:

```json
    "nutrition": [
        {
            "nutrition": "NUTRITION_ENERGY",
            "condition": "CONDITION_GTE",
            "value": 50.0000000000001
        },
        {
            "nutrition": "NUTRITION_ENERGY",
            "condition": "CONDITION_LTE",
            "value": 100.1
        }
    ]
```

### Ordering

```bash
"ordering": "ORDERING_INVALID",
```

### Fields

```bash
"fields": [
  "RECIPE_FIELD_INVALID"
],
```

### Custom Labels

```bash
"custom_labels": {
  "everywhere": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  },
  "in_recipe": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  },
  "in_ingredients": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  },
  "in_instruction_steps": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  }
}
```

### Recipes with Full license

```
"whisk_origin_recipes": true
```

### Apply Implicit Preferences

Field manages search resultes. Two options available: Search results with user's embedding, for users who accepted this type of search in ST app; And regular search results.

Default value: `true`

Fallback if embedding is not available: regular search results

```
"apply_implicit_preferences": true
```

## Sample Request

```bash
curl -X POST "https://api.whisk.com/recipe/v2/search" \
  -H "accept: application/json" \
  -H "Authorization: Token <Access-Token>" \
  -H "Content-Type: application/json" \
  -d "{ \"language\": \"en\", \"paging\" : {\"limit\": 2}}"
```

### Use-case request examples

#### Boost recipes by certain device, technique, ingredient

Using recipe labels to search or boost recipes will now also take intents set for this recipe into account. I.e. if a recipe has intents for an oven and search is performed with `"device": "DEVICE_OVEN"`, the recipe will be boosted.

```bash
curl -X POST "https://api.whisk.com/recipe/v2/search" \
  -H "accept: application/json" \
  -H "Authorization: Token <Access-Token>" \
  -H "Content-Type: application/json" \
  -d '{
  "language": "ko",
  "country": "KR",
  "labels": [
    {
      "label": {
        "device": "DEVICE_OVEN"
      },
      "boost": true
    },
    {
      "label": {
        "technique": "TECHNIQUE_GRILLING"
      },
      "boost": true
    }
  ],
  "include_ingredients": {
    "list": [
      "Beef Tenderloin"
    ]
  },
  "min_ingredients_should_match": 0
}'
```

#### Search recipe with certain device intents

**Note:** recipes AI identified to be suitable for cooking with a device will also be returned, currently there's no way to search recipes strictly with intents defined

You may find the complete list of devices supported in search on [API definition page](https://api.whisk.com/spec/#/RecipeAPI/RecipeAPI_SearchRecipes) under the section `request body` -> `model` -> `labels` -> `device` or in `whisk.api.shared.v1.Device` model

```
curl -X POST "https://api.whisk.com/recipe/v2/search" \
  -H "accept: application/json" \
  -H "Authorization: Token <Access-Token>" \
  -H "Content-Type: application/json" \
  -d '{
  "language": "ko",
  "country": "KR",
  "labels": [
    {
      "label": {
        "device": "DEVICE_PRESSURE_COOKER"
      },
      "boost": false
    }
  ],
  "fields": [
    " RECIPE_FIELD_INSTRUCTIONS", "RECIPE_FIELD_INSTRUCTION_INTENTS"
  ],
}'
```

**Note:** the `instructions.steps.intents` field is deprecated and will not be extended.

You will find intents in `instructions.steps.instruction_intents.attributes` field. Each intent comes as an object with following fields

* `equipment_action` - describes what equipment and in what mode is going to be used (utensils like frying pan does not have modes or attributes at all)
* `attributes` - the conditions to be used when cooking, the list may be empty which usually means until user stops the program on her own

The complete list of supported equipment, modes and attributes can be found inside [`whisk.api.recipe.v2.Intents`](https://api.whisk.com/spec/#/) model

e.g.

```
...
    "instructions": {
      "steps": [
        {
          "text": "After chicken is 'well-boiled' (the oil should be floating on top of the frying pan liquid), add the garam masala and turmeric and turn off the heat. Season with salt to taste. Stir all together and serve.",
          "intents": [
            {
              "name": "AirFryer.PreHeating",
              "options": [
                {
                  "key": "Temperature",
                  "value": 200,
                  "unit": "celsius"
                },
                {
                  "key": "Duration",
                  "value": 10,
                  "unit": "minute"
                }
              ]
            }
          ],
          "instruction_intents": {
            "intents": [
              {
                "attributes": [
                  {
                    "temperature": {
                      "value": {
                        "plain_value": 200
                      },
                      "unit": "UNIT_CELSIUS"
                    }
                  },
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "air_fryer": {
                    "mode": "MODE_HEAT"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  },
                  {
                    "gas": {
                      "value": {
                        "plain_value": 3
                      },
                      "unit": "UNIT_LEVEL"
                    }
                  }
                ],
                "equipment_action": {
                  "oven": {
                    "mode": "MODE_GAS"
                  }
                }
              },
              {
                "equipment_action": {
                  "microwave": {
                    "mode": "MODE_DEFROST"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "speed": {
                      "value": "LEVEL_MEDIUM",
                      "unit": "UNIT_LEVEL"
                    }
                  },
                  {
                    "duration": {
                      "value": {
                        "plain_value": 900
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "food_processor": {
                    "mode": "MODE_PREPARE"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "pressure_cooker": {
                    "mode": "MODE_MULTIGRAIN"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "slow_cooker": {
                    "mode": "MODE_KEEPWARM"
                  }
                }
              }
            ]
          }
        }
      ]
    },
...
```
