Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal of a Menu/menuItems schema? #1288

Open
dkutcher opened this issue Aug 9, 2016 · 57 comments
Open

Proposal of a Menu/menuItems schema? #1288

dkutcher opened this issue Aug 9, 2016 · 57 comments
Labels
no-issue-activity Discuss has gone quiet. Auto-tagging to encourage people to re-engage with the issue (or close it!). schema.org vocab General top level tag for issues on the vocabulary

Comments

@dkutcher
Copy link

dkutcher commented Aug 9, 2016

Currently for Restaurants and FoodEstablishments there is a menu item:

http://schema.org/Restaurant
menu
Text or URL
Either the actual menu or a URL of the menu.

But no schema for the menu(s) and menu items themselves.

For example usage, google a restaurant name + menu such as https://www.google.com/?ion=1&espv=2#q=two%20urban%20licks%20menu

You'll see the data of a menu being used in the rich formatting.

Is anyone developing the menu and menuItem schema? If not, how does one propose?

@jvandriel
Copy link

jvandriel commented Aug 9, 2016

What if we added schema.org/OfferCatalog to schema.org/menu's expected value?

Than we could write something like this:

<script type="application/ld+json">
{
  "@context":"http://schema.org/",
  "@type":"Restaurant",
  "name":"TWO urban licks",
  "description":"TWO serves wood-fired meats and fish served in a high-energy, open kitchen featuring fiery American Food.Executive Chef at TWO, Cameron Thompson gets to let his creativity shine as his passion for innovation and desire to exceed diners’ expectation enliven the “Fiery American Cooking” praised by Atlantans year after year. All of the small plates are ideal for sharing or savoring on your own. A few of the menu’s highlights include the Salmon Chips, loaded with short smoked salmon, chipotle cream cheese, capers, and red onion; Lamb Lollipops served with grape chile jam and goat cheese; entrée options include Pork Shoulder with NY baked cheddar macaroni and pork jus; and the Bronzed Sea Scallops served over smoked gouda grits in a tomato broth.",
  "url":"http://places.singleplatform.com/two-urban-licks/menu",
  "menu":
  [
    {
      "@type":"OfferCatalog",
      "name":"Main menu",
      "itemListElement":
      [
        {
          "@type":"Offer",
          "currency":"USD",
          "price":"10",
          "itemOffered":
          {
            "@type":"Recipe",
            "name":"Salmon Chips",
            "recipeIngredient":["smoked salmon","chipotle cream","cheese","caper","red onion"]
          }
        },
        {
          "@type":"Offer",
          "currency":"USD",
          "price":"12",
          "itemOffered":
          {
            "@type":"Recipe",
            "name":"Little Necks",
            "recipeIngredient":["steamed clams","mezcal","charred jalapeno","smokey bacon"],
            "nutrition":
            {
              "@type":"NutritionInformation",
              "name":"*",
              "description":"these items fall under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness"
            }
          }
        }
      ]
    },
    {
      "@type":"OfferCatalog",
      "name":"Brunch menu",
      "itemListElement":
      [
        {
          "@type":"Offer",
          "currency":"USD",
          "price":"10",
          "itemOffered":
          {
            "@type":"Recipe",
            "name":"Salmon Chips",
            "recipeIngredient":["short smoked salmon","chipotle cream cheese","capers","red onion"]
          }
        },
        {
          "@type":"Offer",
          "currency":"USD",
          "price":"6",
          "itemOffered":
          {
            "@type":"Recipe",
            "name":"Brioche Beignets",
            "recipeIngredient":["confectioner's sugar","coffee cream"]
          }
        }
      ]
    }
  ]
}
</script>

Note:
I used a places.singleplatform.com example as Google uses it for menu links in its Knowledge Graph panels, so it's an example based on something that's already out there, minus the markup.

@dkutcher
Copy link
Author

dkutcher commented Aug 9, 2016

I think that gets very, very close to what I was envisioning.

A few things I'd also consider:

  • days/times when menu is being served
  • dates or date ranges when menu is available (special menus for holidays, as well as seasonality)
  • dairyfree, glutenfree, vegan, etc. for items
  • calorie counts for items
  • add-on items with pricing (add chicken to your salad, +$5)

and p.s. great to connect with you here as well as google+ ;)

@DDeering
Copy link

DDeering commented Aug 9, 2016

I don't love it but I don't hate it, @jvandriel. :-) I've been kicking around some ideas myself lately as I'd love to see a more robust way to mark up restaurant menus. But to be honest, I'm not a fan of the MTE Product-Recipe. I think those are two very different entities in the eyes of search engines. Maybe I'm wrong. But I would much rather push for a few new properties and types such as menu > URL or Menu, MenuItem, FoodItem, DrinkItem (just throwing out some initial ideas off the top of my head). We could then include some of the appropriate Recipe properties and types as well such as recipeIngredient, suitableForDiet, etc.

I know that one of the arguments against creating new properties and types specifically for a certain entity is the desire to keep schema.org streamlined. But when you consider the vast number of restaurants in the world, we could argue that it might be worth exploring and developing a bit more because of the potential use and benefits.

@jvandriel
Copy link

jvandriel commented Aug 9, 2016

I have to say I don't have anything fixed in mind. Just thought I'd throw an example in there as a starting point. Tinker with it as much as you want to illustrate what you like to see yourself, maybe that way we can come to a proper proposal (and wider consensus).

@dkutcher
Copy link
Author

Is there a link to a formatting method that I should use to formulate a proposal?

@danbri danbri added the schema.org vocab General top level tag for issues on the vocabulary label Aug 10, 2016
@dkutcher
Copy link
Author

Along similar lines to above... (sorry for formatting, doing in spreadsheet)

A few notes:

  • 1 restaurant can have multiple menus.
  • Menus should have a start date and end date (optional), for seasonality and whatnot, as well as special events and holiday menus
  • Menus, such as a dinner menu, should have a time range for when that menu is relevant
  • Menu items should have an optional classification for things like gluten-free, paleo, vegan, etc.
  • Menu items should have add-on elements

@type Restaurant
name Dave's Restaurant
description serves wood-fired meats and fish served in a high-energy, open kitchen featuring fiery American Food.
url http://www.davesbarandgrill.com
telephone
menu
@type menuCatalog
name new years dinner menu
menuHours Tu-Sa 16:00-22:00
startDate 2015-12-31
endDate 2016-01-01
currency USD
items
@type menuItem
name Roast Beef
description a big slab of roast beef with chipoltle cream
section main course
itemIngredients beef, chipolte cream, cheese, capers, red onion
itemCalories 650
classification [vegetarian,vegan,gluten-free,paleo]
price 18
additionals
@type menuItemAdditional
name potatoes
description roasted red potatoes
price 3

@gmackenz
Copy link
Contributor

Great to see interest about Menus, I too have been recently working on a
proposal for expanding Menus.

I'd like to propose we expand the current property 'menu' on 'Restaurant'
to be either allow a URL or a new type 'Menu' that inherits from Thing >
CreativeWork

Menu would have two new properties:

hasMenuSection --> MenuSection (aka MenuCatalog from above)

hasMenuItem --> MenuItem

MenuSection inherits from Thing > CreativeWork

Through CreativeWork and Thing you can assert name and from offers:
availability information and much more.

MenuItem inherits from Thing > CreativeWork

add specific properties for suitable for diet (RestrictedDiet), allergens
(allergenPresent & allergenAbsent that point to a new Intangible > Allergen
that has a list of common food allergens) and nutrition
(NutritionInformation)

offers can contain all the pricing, variations with pricing and other
specific data to a dish or drink. I think the previous example of
menuItemAdditional could use the Offer > addOn > Offer instead of new
schema.

Gordon Mackenzie | Schema Wrangler (Ontologist) | gmackenz@google.com |

On Mon, Aug 15, 2016 at 10:31 AM, dkutcher notifications@github.com wrote:

Along similar lines to above... (sorry for formatting, doing in
spreadsheet)

A few notes:

  • 1 restaurant can have multiple menus.
  • Menus should have a start date and end date (optional), for
    seasonality and whatnot, as well as special events and holiday menus
  • Menus, such as a dinner menu, should have a time range for when that
    menu is relevant
  • Menu items should have an optional classification for things like
    gluten-free, paleo, vegan, etc.
  • Menu items should have add-on elements

@type https://github.com/type Restaurant

name Dave's Restaurant

description serves wood-fired meats and fish served in a high-energy, open
kitchen featuring fiery American Food.

url http://www.davesbarandgrill.com

telephone

menu

@type https://github.com/type menuCatalog

name new years dinner menu

menuHours Tu-Sa 16:00-22:00

startDate 2015-12-31

endDate 2016-01-01

currency USD

items

@type https://github.com/type menuItem

name Roast Beef

description a big slab of roast beef with chipoltle cream

section main course

itemIngredients beef, chipolte cream, cheese, capers, red onion

itemCalories 650

classification [vegetarian,vegan,gluten-free,paleo]

price 18

additionals

@type https://github.com/type menuItemAdditional

name potatoes

description roasted red potatoes

price 3


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#1288 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEeZIu9slMN-UDmNGu4HDVeQEP_e-U5Rks5qgKKIgaJpZM4JgUxT
.

@dkutcher
Copy link
Author

Taking a stab... sorry for the formatting

`

{
"@context":"http://schema.org/",
"@type":"Restaurant",
"name":"David's Bar and Grill",
"description":"serves wood-fired meats and fish served in a high-energy, open kitchen featuring fiery American Food.",
"url":"http://www.DavidBarAndGrill.com",
"telephone":"212-222-1122",
"menu":
[
{
"@type":"Menu",
"name":"Dinner menu",
"menuHours":"Tu-Sa 16:00-22:00",
"startDate":"2016-01-02",
"endDate":"2016-05-31",
"currency":"USD",
"hasMenuItem":
[
{
"@type":"MenuItem",
"name":"Spears of Romaine",
"description":"spicy caesar salad with chili croutons and parmesan",
"recipeIngredient":["romaine lettuce","caesar dressing","bread","chili peppers","parmesan cheese"],
"currency":"USD",
"price":"10",
"nutrition":
{
"@type":"NutritionInformation",
"name":"",
"description":"these items fall under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness",
"calories": "350",
"restrictedDiet":['vegetarian','gluten-free']
}
"hasAddOn":
[
{
"@type":"AddOn",
"name":"grilled chicken",
"currency":"USD",
"price":"6"
},
{
"@type":"AddOn",
"name":"poached salmon",
"currency":"USD",
"price":"10",
"nutrition":
{
"@type":"NutritionInformation",
"name":"
",
"description":"these items fall under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness",
"calories": "350"
}
}
]
},
{
"@type":"MenuItem",
"name":"Little Necks",
"description":"jalepeno clams in a bacon broth",
"recipeIngredient":["steamed clams","mezcal","charred jalapeno","smokey bacon"],
"currency":"USD",
"price":"12",
"nutrition":
{
"@type":"NutritionInformation",
"name":"*",
"description":"these items fall under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness",
"calories": "450"
}
}
]
},
{
"@type":"Menu",
"name":"Brunch menu",
"menuHours":"Sa-Su 09:00-14:00",
"startDate":"2016-01-02",
"endDate":"2016-05-31",
"currency":"USD",
"hasMenuItem":
[
{
"@type":"MenuItem",
"name":"Salmon Chips",
"description":"",
"recipeIngredient":["short smoked salmon","chipotle cream cheese","capers","red onion"],
"currency":"USD",
"price":"10"
},
{
"@type":"MenuItem",
"name":"Brioche Beignets",
"description":"",
"recipeIngredient":["confectioner's sugar","coffee cream"],
"currency":"USD",
"price":"6"
}
]
}
]
}
`

@gmackenz
Copy link
Contributor

I like it @dkutcher! I'd like to write up a quick proposal in the next day or so based upon what we've discussed so we can have something concrete to reference in further refining this schema.

Looking at your example, I think we can use the existing CreativeWork.offers.Offer.validFrom instead of menuHours, startDate, and endDate as those three values can be captured in one validFrom.DateTime SO encoded value for duration and time intervals.

@dkutcher
Copy link
Author

I'm glad, and yes, that CreativeWork.offers.Offer.validFrom works, although it seems a bit funny to be thinking of a Menu as a creativework... very philosophical for a chef!

To throw a quick stick in the cog, two situations that might not align neatly:

  • Prixe fix menus / Tasting menus (multiple menu items contained under one price?)
  • Daily specials (menu item would need a date availability that assumes the parent unless specified?)

Thoughts?

@unor
Copy link
Contributor

unor commented Aug 30, 2016

Related:

@dkutcher
Copy link
Author

dkutcher commented Sep 5, 2016

Bump? Anything I can do to assist?

@gmackenz
Copy link
Contributor

Hello, been away working on a complete proposal with @vholland and here's what I've come up with as a document. I think it answers all of the questions raised so far.

@dkutcher:

  • Prixe fix menus / Tasting menus (multiple menu items contained under one price?)

Price would be in Offer under Menu or MenuSection and the MenuItems will not list a price. That's how I would do it.

  • Daily specials (menu item would need a date availability that assumes the parent unless specified?)

You can specify availability on Menu, MenuSection or in this case by MenuItem on the Offer's availability properties. Not sure which is better, some restaurants how unique specials items on a separate menu/menu section. Others might use multiple Offer on a MenuItem, each with a different price and availability time period.

@dkutcher
Copy link
Author

@gmackenz Can you make that document public? Can't see it...?

@ghost
Copy link

ghost commented Sep 16, 2016

On Wed, 10 Aug 2016 at 04:27 dkutcher notifications@github.com wrote:

I think that gets very, very close to what I was envisioning.

A few things I'd also consider:

  • days/times when menu is being served
  • dates or date ranges when menu is available (special menus for
    holidays, as well as seasonality)
  • dairyfree, glutenfree, vegan, etc. for items

seeAlso:schema.org/diet

Properties should include means to say whether the food is 'safe' for
particular allergies; in addition to, 'diet' related semantics.

USE CASE: peanutAllergy https://www.wikidata.org/wiki/Q7157933

USE CASE: https://www.thecandidadiet.com/
SeeAlso: https://www.wikidata.org/wiki/Q273510

Both should also support reputation/review properties... (ie: trusted
information)

  • calorie counts for items
  • add-on items with pricing (add chicken to your salad, +$5)

and p.s. great to connect with you here as well as google+ ;)


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#1288 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AFdABnRbqiHmqjkyCJgBaS3HpP18hh-rks5qeMZ-gaJpZM4JgUxT
.

@dkutcher
Copy link
Author

@mediaprophet agreed, I had put those under the NutritionInformation:

"restrictedDiet":['vegetarian','gluten-free']

@gmackenz
Copy link
Contributor

Sorry @dkutcher, I thought I had faithfully followed all the steps to make the document publicly visible. Here's an externally hosted version. Is this accessible for all interested?

@Dataliberate
Copy link
Contributor

Can see that now- thanks.

~Richard

On 17 Sep 2016, at 10:56, Gordon Mackenzie notifications@github.com wrote:

Sorry @dkutcher, I thought I had faithfully followed all the steps to make the document publicly visible. Here's an externally hosted version. Is this accessible for all interested?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@dkutcher
Copy link
Author

A few comments that I'm not quite sure are addressed:

  • menu should have availability, for example specifying that the "main dinner menu" is available "Mo-Th 17:00-23:00"
  • menu should have date range validity, validFrom and validTo, not tied to the items but to the menu
  • a menu can have an offer, such as "appetizers half-price" with "Mo-Th 16:30-18:00"
  • ability to validate a menu against today's date for things like a "daily special", so the menu is "available" on "2016-09-17"
  • lastUpdated specified (assuming this will be there, but didn't want to assume)
  • I noticed in your example you used OpeningHoursSpecification instead of the other option OpeningHours (both found in schema.org/Restaurant), why are there two methods?
  • just also noticed (perhaps a separate issue) that Restaurant has specification for smoking, but not for liquor/beer/wine serving-license. This likely should be an option?
  • will addOn items also have NutritionInfo?

Trying to think of situations...

@dkutcher
Copy link
Author

Additional question:

Many sites have been using the menu property to supply a URL to the location of the menu. Within a restaurant website, all pages of the website might be pointing to site.com/menu.html

If the menu property is now being extended, how would a restaurant property define the definitive location(s) of the menu(s), where they can then be found?

@gmackenz
Copy link
Contributor

On Tue, Sep 20, 2016 at 8:07 AM, dkutcher notifications@github.com wrote:

Additional question:

Many sites have been using the menu property to supply a URL to the
location of the menu. Within a restaurant website, all pages of the website
might be pointing to site.com/menu.html

If the menu property is now being extended, how would a restaurant
property define the definitive location(s) of the menu(s), where they can
then be found?

I don't quite understand, if you mean by the new extension of a Menu type
on Restaurant > menu? They can still use URL and use the Menu as well for a
marked up version. It's up to the site owner. If you mean storing the URL
of the menu page within the new Menu type? Then I suppose you could store
that in Thing > url.

@gmackenz
Copy link
Contributor

Thanks for the questions and feedback!

On Sat, Sep 17, 2016 at 11:57 AM, dkutcher notifications@github.com wrote:

A few comments that I'm not quite sure are addressed:

menu should have availability, for example specifying that the "main
dinner menu" is available "Mo-Th 17:00-23:00"

It is in menu (name:"Main Dinner Menu")> offers > availabilityStarts &
availabilityEnds or if you chose to have multiple menuSections under a root
FoodEstablishment's menu, then menuSection (name:"Main Dinner Menu") >
offers > availabilityStarts & availabilityEnds

menu should have date range validity, validFrom and validTo, not tied
to the items but to the menu

Yes, using offers you can do that at the menu level, menuSection level or
menuItem

a menu can have an offer, such as "appetizers half-price" with "Mo-Th
16:30-18:00"

ability to validate a menu against today's date for things like a
"daily special", so the menu is "available" on "2016-09-17"

lastUpdated specified (assuming this will be there, but didn't want to
assume)

See CreativeWork's dateModified for capturing that (if provided)

I noticed in your example you used OpeningHoursSpecification instead
of the other option OpeningHours (both found in schema.org/Restaurant),
why are there two methods?

Currently I believe Place's OpeningHoursSpecification is more in use in
the world (that I am aware of within my work). You can use either one but I
think OpeningHours is probably an unnecessary denormalization, a
duplication of the OpeningHoursSpecification but I don't know the the
history of Restaurant's creation.

just also noticed (perhaps a separate issue) that Restaurant has
specification for smoking, but not for liquor/beer/wine serving-license.
This likely should be an option?

Probably, but I think those kind of properties might be better on
FoodEstablishment or even higher up, LocalBusiness? Liquor licenses are
idiosyncratic to local governance and can be non-traditional
alcohol-serving establishments like restaurants, pubs and bars. Maybe on
Place? Anyway that should discussed outside of Menu I think.

will addOn items also have NutritionInfo?

That's an interesting thought, maybe the addOns will require a specific
menuItems listing somewhere in the menu (in an Additional Toppings
menuSection for example?). That said, one could simply use the
NutritionInfo property with the addOn

Trying to think of situations...

Please do so...

@vholland
Copy link
Contributor

I don't know the entire history of openingHours and openingHoursSpecification, but openingHoursSpecification is less ambiguous. It is clearer for both readers and writers of the data what is being said rather than parsing strings.

@dkutcher
Copy link
Author

dkutcher commented Sep 22, 2016 via email

@gmackenz
Copy link
Contributor

FYI I removed the allergen portion of the proposal above as @danbri pointed out to me we should look to solving that elsewhere (issue #1411). Working on creating the examples now.

@jvandriel
Copy link

jvandriel commented Jan 19, 2017

I'm guessing that's probably because of how the Offer model has been put together in Goodrelations years ago already. Just have a look at http://schema.org/Offer and http://schema.org/addOn.

And since Goodrelations has been an integral part of schema.org nearly from day one. I think it would be a bad idea to move away from it. (cc: @mfhepp)

Having said that, I can imagine a purpose for using 'menuAddOn' in those situations where a FoodEstablishment has a separate MenuSection containing a group of additional menu items, eg

{
  "@type":"MenuItem",
  "name":"Spears of Romaine",
  "menuAddOn":
  {
    "@type":"MenuSection",
    "name":"Extra's",
    "hasMenuItem":
    [
      {
        "@type":"MenuItem",
        "name":"French fries"
      },
      {
        "@type":"MenuItem",
        "name":"hot vegatables"
      }
    ]
  }
}

Doing it this way means schema.org can facilitate menu's that don't have a separate 'add on' section on their menu but offer add ons directly from the MenuItem itself, while also being able to facilitate menu's that do have a separate 'add on' section.

@DDeering
Copy link

Hmm, I'm not sure if I'm a fan of using menuAddOn in that way. What about simply using the existing addOn property within the Offer? And really, the "add on" is not an addition to the menu but an addition to the menu item, right?

So since many menu items also include the add-ons immediately below the item description, how about doing something like this (shown in a very simple, incomplete example)?:

{
"@type": "MenuItem",
"name": "Ribeye Steak",
"offers": {
"@type": "Offer",
"price": .....
"addOn": {
"@type": "Offer",
"itemOffered": {
"@type": "Product",
"name": "Grilled Mushrooms"
},
"price": .....

Personally, I think that if a restaurant has an "add-on/extras" section, it might be easier to mark it up as it's own menu or menu section. But again, just my opinion.

@dkutcher
Copy link
Author

The addOn is a menu item being added onto a menu item.

Caesar Salad

  • w/ chicken
  • w/ shrimp

IMO the menu item is being modified here (item + sub-item).

At the same time, the same item and sub-items might have different offers depending on time of day (for example).

So it could be:

Caesar Salad

  • offer (hour set 1)
  • offer (hour set 2)
  • w/ chicken (add on)
    -- offer (hour set 1)
    -- offer (hour set 2)

That seems to me to make much more sense than nesting the chicken twice (once under each offer)

@DDeering
Copy link

Right. But I do think that your second scenario would probably be pretty rare to see. I think most restaurants typically have Lunch and Dinner menus as opposed to listing the item with various prices and times together.

@dkutcher
Copy link
Author

Perhaps, but they might have the regular menu discounted during lunch or happy hour, without having a happy hour menu.

@DDeering
Copy link

True. And honestly, there are several ways to mark it up, including the way you showed.

But my main point is that I'm not sure creating a new menuAddOn property would be necessary. I think using the existing addOn property along with Offer would suffice, unless I'm overlooking something.

@gmackenz
Copy link
Contributor

gmackenz commented Jan 19, 2017 via email

@DDeering
Copy link

I see and I understand where you're coming from, @gmackenz. When marking those up, could we possibly group all of the add-on items together in one add-on menu and reference it once for each menu item? Because the problem I see is that some restaurants will list the add-ons immediately under the item description but then other menus such as for pizza will list the add-ons all in a separate menu. So what about creating a new itemAddOn property that could expect a value of either MenuItem or MenuSection?

@gmackenz
Copy link
Contributor

I believe that was the intent for linking from various MenuItem(s) to a MenuSection of add ons items. Maybe I'm misunderstanding your suggestion but the listing add ons immediately could be done through your earlier suggestion of MenuItem > Offer > addOn > Offer.

@jvandriel
Copy link

jvandriel commented Jan 19, 2017

OK, let's try to make it a bit more concrete.

I've taken the original markup example created by @dkutcher and tried to incorporate most of what's been said:

  • 2x Menu
  • 2x an array of MenuItem
  • 2x addOn for the Offer of an individual MenuItem
  • 2x menuAddOn (1x time referred to via an @id to prevent having to add the same data over and over again)
<script type="application/ld+json">
{
  "@context":"http://schema.org/",
  "@type":"Restaurant",
  "name":"Acme's Bar and Grill",
  "description":"serves wood-fired meats and road runner served in an open kitchen featuring American Food.",
  "url":"http://www.example.com",
  "telephone":"555-123-4567",
  "menu":
  [
    {
      "@type":"Menu",
      "name":"Dinner menu",
      "temporalCoverage":"Tu-Sa 16:00-22:00",
      "hasMenuSection":
      {
        "@type":"MenuSection",
        "name":"Starters",
        "hasMenuItem":
        [
          {
            "@type":"MenuItem",
            "name":"Spears of Romaine",
            "description":"Spicy caesar salad with chili croutons and parmesan",
            "nutrition":
            {
              "@type":"NutritionInformation",
              "name":"*",
              "description":"These items fall under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness",
              "calories":"350"
            },
            "suitableForDiet":
            [
              {
                "@type":"RestrictedDiet",
                "name":"Vgetarian"
              },
              {
                "@type":"RestrictedDiet",
                "name":"Gluten-free"
              }
            ],
            "offers":
            {
              "@type":"Offer",
              "priceCurrency":"USD",
              "price":"10",
              "addOn":
              [
                {
                  "@type":"Offer",
                  "priceCurrency":"USD",
                  "price":"10",
                  "itemOffered":
                  {
                    "@type":["MenuItem","Product"], // The 'itemOffered' has to be an MTE because the expected value for 'itemOffered' is either schema.org/Product or schema.org/Service.
                    "name":"Poached salmon",
                    "nutrition":
                    {
                      "@type":"NutritionInformation",
                      "name":"*",
                      "description":"This item falls under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness",
                      "calories":"350"
                    }
                  }
                },
                {
                  "@type":"Offer",
                  "priceCurrency":"USD",
                  "price":"6",
                  "itemOffered":
                  {
                    "@type":["MenuItem","Product"], 
                    "name":"Grilled chicken"
                  }
                }
              ]
            },
            "menuAddOn":
            {
              "@id":"#extras",
              "@type":"MenuSection",
              "name":"Extra's",
              "hasMenuItem":
              [
                {
                  "@type":"MenuItem",
                  "name":"French fries",
                  "offers":
                  {
                    "@type":"Offer",
                    "priceCurrency":"USD",
                    "price":"1"
                  }
                },
                {
                  "@type":"MenuItem",
                  "name":"Hot vegatables",
                  "offers":
                  {
                    "@type":"Offer",
                    "priceCurrency":"USD",
                    "price":"5"
                  }
                }
              ]
            }
          },
          {
            "@type":"MenuItem",
            "name":"Little Necks",
            "description":"jalepeno clams in a bacon broth",
            "nutrition":
            {
              "@type":"NutritionInformation",
              "name":"*",
              "description":"these items fall under the consumer advisory for raw or undercooked meats or seafood. consuming raw or undercooked meats, poultry, seafood, shellfish, or eggs may increase your risk of food borne illness",
              "calories":"450"
            },
            "offers":
            {
              "@type":"Offer",
              "priceCurrency":"USD",
              "price":"12"
            },
            "menuAddOn":
            {
              "@id":"#extras"
            }
          }
        ]     
      }
    },
    {
      "@type":"Menu",
      "name":"Brunch menu",
      "temporalCoverage":"Sa-Su 09:00-14:00",
      "hasMenuItem":
      [
        {
          "@type":"MenuItem",
          "name":"Salmon Chips",
          "description":"Too good to be true!",
          "offers":
          {
            "@type":"Offer",
            "priceCurrency":"USD",
            "price":"10"
          }
        },
        {
          "@type":"MenuItem",
          "name":"Brioche Beignets",
          "description":"These should be illegal!",
          "offers":
          {
            "@type":"Offer",
            "priceCurrency":"USD",
            "price":"6"
          }
        }
      ]
    }
  ]
}
</script>

Anything that should be different folks?

@DDeering
Copy link

No, you're right, Gordon, it certainly can be done like that. I just didn't know if it would look strange for addOn to have an expected value of Offer or MenuSection. But the way you structured it would work perfectly for single add-on items.

So for add-on menus, do you suggest doing something like this?....

.....
"addOn": {
"@type": "Offer",
"itemOffered": {
"@type": "MenuSection",
"name": "Additional Toppings",
"id": "#toppings",
"hasMenuItem": [
{
"@type": "MenuItem",
"name": "Extra Cheese",
"offers": {
"price": "1.00",
"priceCurrency": "USD"
},
{
"@type": "MenuItem",
"name": "Extra Onions",
"offers": {
"price": "1.00",
"priceCurrency": "USD"
},
.........
</script>

The MenuSection could be nested once or marked up separately.

@DDeering
Copy link

Thanks, Jarno.

Sorry if I seem stubborn but I'm still not a fan of menuAddOn. A menu has menu sections but menu items have add-ons. If we already have Menu > hasMenuSection > MenuSection, why do we need menuAddOn to declare essentially another MenuSection? Can't we just use the same structure?

@jvandriel
Copy link

Be as stubborn as you want, I'm not that crazy about it either. I just noticed it on http://webschemas.org/ earlier and tried to make sense of why it's there.

Couldn't we let it out in favor of using addOn in combination with an multi-type entity?

  "@type":"MenuItem",
  "name":"Pepperoni pizza",
  "additionalProperty":
  {
    "@type":"PropertyValue",
    "name":"size",
    "value":"medium"
  },
  "offers":
  {
    "@type":"Offer",
    "priceCurrency":"USD",
    "price":"6.99",
    "addOn":
    {
      "@type":"Offer",
      "itemOffered":
      {
        "@type":["MenuSection","SomeProducts"],
        "name":"Extra's",
        "hasMenuItem":
        [
          {
            "@type":"MenuItem",
            "name":"Pineapple",
            "offers":
            {
              "@type":"Offer",
              "priceCurrency":"USD",
              "price":"0.50"
            }
          },
          {
            "@type":"MenuItem",
            "name":"Gouda cheese",
            "offers":
            {
              "@type":"Offer",
              "priceCurrency":"USD",
              "price":"2"
            }
          }
        ]
      }
    }
  }

@DDeering
Copy link

Is using an MTE really necessary in this case, Jarno? Aren't menus, menu sections, and menu items by default understood to be "some products"? And MenuItem is going to inherit all of the Product type's properties anyway, so I'm thinking that it might be a little redundant and unnecessary. But perhaps I'm overlooking something.

@mfhepp
Copy link
Contributor

mfhepp commented Jan 20, 2017 via email

@DDeering
Copy link

Good point. Thanks, Martin. We certainly don't want to break from schema.org norms. Do you have any suggestions then on how to handle these things?

@jvandriel
Copy link

Going by Martin's comment I think it might be a good idea to remove Menu from webschemas.org until it becomes clear what to do about not being able to use menuAddOn @danbri.

@danbri
Copy link
Contributor

danbri commented Mar 2, 2017

After looking over this and discussing with @gmackenz and @vholland, I will move menuAddOn to the Pending section of schema.org for the upcoming #1292 release.

@danbri
Copy link
Contributor

danbri commented Mar 3, 2017

Ok, I've added examples (thanks @gmackenz ! :) and also made a change that I wavered on previously. Rather than have yet another case where we have 2 very similarly named terms (a property 'menu' and a type 'Menu') I have added a new preferred property named 'hasMenu'. This property means exactly the same thing, and the old version can of course still be used, but it keeps us on course to having more distinct property names throughout. I expect we will fix all the other situations like this in our next release.

@monecchi
Copy link

monecchi commented Feb 8, 2018

I'm amazed by the new schema.org/Restaurant properties structuring markup possibilities. I've come through a few tutorials such as this one introducing the new Restaurant Menu markup, but I got stuck on how to markup individual Menu Items when viewing their actual single pages.

E.g: For a Pizzeria Menu with different sections for pizza, pasta & beverages I'd markup the actual menu page like the following bellow, which is great, but, what about Restaurant Menus which allow viewing a specific Menu Item on its own single page? Could anyone please guide me on the proper method for marking up single Menu Items which exists alone on their own single page?

    <script type="application/ld+json">
    {
      "@context":"http://schema.org",
      "@type":"Menu",
      "name": "Pizza Menu",
      "url": "http://your-restaurant.com/menu/",
      "mainEntityOfPage": "http://your-restaurant.com/menu/",
      "inLanguage":"English",
      "offers": {
        "@type": "Offer",
        "availabilityStarts": "T18:00",
        "availabilityEnds": "T23:30"
      },
      "hasMenuSection": [
        {
          "@type": "MenuSection",
          "name": "Special Pizzas",
          "hasMenuItem": [
            {
              "@type": "MenuItem",
              "name": "Special Peperoni Pizza",
              "description": "Served with Italian red gravy.",
              "offers": {
                "@type": "Offer",
                "price": "9.90",
                "priceCurrency": "USD",
                "eligibleQuantity": {
                  "@type": "QuantitativeValue",
                  "name": "Small - 4 Slices Pizza"
                }
              }
            }
          ]
        },
        {
          "@type": "MenuSection",
          "name": "Pastas",
          "description": "Entrées served with dinner salad or a cup of soup of the day.",
          "hasMenuItem": [
            {
              "@type": "MenuItem",
              "name": "Veal Parmigiana",
              "description": "Tender cuts of paneed veal crowned with golden fried eggplant, Italian red gravy, mozzarella, and parmesan; served with spaghetti.",
              "offers": {
                "@type": "Offer",
                "price": "17.95",
                "priceCurrency": "USD"
              }
            }
          ]
        }
      ]
    }
    </script>

Regards,

@mishadar
Copy link

@monecchi I would play with mainEntityOfPage the page vs. mainEntity. They are inverse to each other. The idea would be identify relations between entities

{
	"@context":"http://schema.org",
	"@type":"Menu",
	"name": "Pizza Menu",
	"url": "http://your-restaurant.com/menu/",
	"inLanguage":"English",
	"mainEntity": {
		"@type": "MenuItem",
		"name": "Veal Parmigiana",
		"description": "Tender cuts of paneed veal crowned with golden fried eggplant, Italian red gravy, mozzarella, and parmesan; served with spaghetti.",
		"offers": {
			"@type": "Offer",
			"price": "17.95",
			"priceCurrency": "USD"
		}
	}
}

@agcty
Copy link

agcty commented Jan 13, 2020

So just to get this right, multiple menus in "hasMenu" are not possible, instead, nested "hasMenuSection" are used?

@michaelzulimar
Copy link

michaelzulimar commented Jan 13, 2020

So just to get this right, multiple menus in "hasMenu"...
Try it in Google schema validate tool, notice errors, warnings. But it makes sense that "restaurant" entity will have a single menu attribute as @menu type, which in turn will be break up to sections like Breakfast//Launch/Dinner/Drinks/Deserts etc. Also Menu should not be nested too deep. Flatten it (recommend to business owner) if so... this will speed up order place time and/or find what item you interested more easy. Results - happy customer=good for business.
Conceptually it's all about how you want Google to understand the site, it's structure and fetch search results to user. Mind that "hasMenu" is for Restaurant entity type vs. "hasMenuSection" is Menu type. So say if Restaurant site has a landing page along with menu pages and dedicated pages that break this menu down. Then, it would make sense use "hasMenu" at the header as sort of indicator to list available menus like "Breakfast/Launch/Dinner" and then detail each on respective page if there is one with "Menu" type entity. On the other hand if the site doesn't have dedicated pages for menu/s and/or menuItems then it would make sense to pack whole menu details within the header.
Back in time Google SE was giving higher rank to sites with many/more pages. Not sure if this is still part of how SE fetches and positions results. Single-Page application tech. should has impact on it.
Have fun.

@github-actions
Copy link

github-actions bot commented Aug 6, 2020

This issue is being tagged as Stale due to inactivity.

@github-actions github-actions bot added the no-issue-activity Discuss has gone quiet. Auto-tagging to encourage people to re-engage with the issue (or close it!). label Aug 6, 2020
@datner
Copy link

datner commented Sep 5, 2024

With the explosion of food delivery apps since this conversation has started, I think that the ecosystem has matured a lot. No longer does a restaurant exist as a brick-and-mortar location but also as a virtual shop. Wolt, Deliveroo, Takeaway, Grab, Uber Eats, we could draw on their insights on expressing the exact semantics a menu expresses. Its also probably required to somehow express the "location" of the menu (or Items?) to differentiate between items, sections, menus, or offerings that are available only on platform A and not in real-life or some other configuration. Or from a different angle, how do we signify whether the provider is a marketplace for the restaurant or direct?
Last thing that is probably already be covered by something that I just am not familiar with yet is payment methods. Price is nice, but it makes a lot of difference if it's cash only (hmm?), credit, pay-by-wire, virtual payment (I mean apple pay/google pay, idk how they are categorized specifically), etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-issue-activity Discuss has gone quiet. Auto-tagging to encourage people to re-engage with the issue (or close it!). schema.org vocab General top level tag for issues on the vocabulary
Projects
None yet
Development

No branches or pull requests