prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add keywords for search
  ### Where on docs.platform.sh should be changed?
  
  /configuration/app/app-reference.html
  
  ### What exactly should be updated?
  
  We'd like specific pages to be findable by searching for specific words. For example, the app reference when searching for `.platform.app.yaml` (this may also involve a problem with escaping characters like `.`). Add keywords or other to make these pages findable.
  
  ### Additional context
  
  _No response_
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `search/main.py`
  Content:
  ```
  1 import os
  2 import glob
  3 import json
  4 import meilisearch
  5 from platformshconfig import Config
  6 
  7 class Search:
  8     def __init__(self):
  9         self.default = {
  10             "host": "http://127.0.0.1",
  11             "key": None,
  12             "port": 7700
  13         }
  14 
  15         self.scrape_dir = "output"
  16         self.scrape_config = "config/scrape.json"
  17         self.docs_index = "docs"
  18         self.primaryKey = "documentId"
  19         self.index_name = "Docs"
  20 
  21         # Below are Platform.sh custom settings for how the search engine functions.
  22 
  23         # Data available to the dropdown React app in docs, used to fill out autocomplete results.
  24         self.displayed_attributes = ['title', 'text', 'url', 'site', 'section']
  25         # Data actually searchable by our queries.
  26         self.searchable_attributes = ['title', 'pageUrl', 'section', 'url', 'text']
  27 
  28         # Show results for one query with the listed pages, when they by default would not show up as best results.
  29         # Note: these aren't automatically two-way, which is why they're all defined twice.
  30         self.synonyms = {
  31             "cron": ["crons"],
  32             "crons": ["cron tasks", "cron jobs"],
  33             "e-mail": ["email"],
  34             "routes.yaml": ["routes"],
  35             "routes": ["routes.yaml"],
  36             "services": ["services.yaml"],
  37             "services.yaml": ["services"],
  38             "application": [".platform.app.yaml", "app.yaml", "applications.yaml"],
  39             ".platform.app.yaml": ["application"],
  40             "app.yaml": ["application"],
  41             "applications.yaml": ["application", "multi-app"],
  42             "multi-app": ["applications.yaml"],
  43             "regions": ["public ip addresses"],
  44             "public ip addresses": ["regions"],
  45             "ssl": ["https", "tls"],
  46             "https": ["ssl"],
  47         }
  48 
  49         # Ranking rules:
  50         #
  51         #   - Default order: ["words", "typo", "proximity", "attribute", "sort", "exactness"]
  52         #
  53         #   - words: number of times query is in document (greater number gets priority)
  54         #   - typo: fewer typos > more typos
  55         #   - proximity: smaller distance between multiple occurences of query in same document > larger distances
  56         #   - attribute: sorted according to order of importance of attributes (searchable_attributes). terms in
  57         #       more important attributes first.
  58         #   - sort: queries are sorted at query time
  59         #   - exactness: similarity of matched words in document with query
  60 
  61         self.ranking_rules = ["rank:asc", "attribute", "typo", "words", "proximity", "exactness"]
  62 
  63         self.updated_settings = {
  64             "rankingRules": self.ranking_rules,
  65             "searchableAttributes": self.searchable_attributes,
  66             "displayedAttributes": self.displayed_attributes
  67         }
  68 
  69         # Group results by page
  70         self.distinct_attribute = "pageUrl"
  71 
  72     def getConnectionString(self):
  73         """
  74         Sets the Meilisearch host string, depending on the environment.
  75 
  76         Returns:
  77             string: Meilisearch host string.
  78         """
  79         if os.environ.get('PORT'):
  80             return "{}:{}".format(self.default["host"], os.environ['PORT'])
  81         else:
  82             return "{}:{}".format(self.default["host"], self.default["port"])
  83 
  84     def getMasterKey(self):
  85         """
  86         Retrieves the Meilisearch master key, either from the Platform.sh environment or locally.
  87         """
  88         config = Config()
  89         if config.is_valid_platform():
  90             return config.projectEntropy
  91         elif os.environ.get("MEILI_MASTER_KEY"):
  92             return os.environ["MEILI_MASTER_KEY"]
  93         else:
  94             return self.default["key"]
  95 
  96     def add_documents(self, index):
  97         """
  98         Cycle through the individual site indexes in /outputs so their individual documents can be added to Meilisearch.
  99         """
  100         documents = [f for f in glob.glob("{}/*.json".format(self.scrape_dir))]
  101         for doc in documents:
  102             self.add(doc, index)
  103 
  104     def add(self, doc, index):
  105         """
  106         Add an individual site's index to the Meilisearch service.
  107         """
  108         with open(doc) as scraped_index:
  109             data = json.load(scraped_index)
  110             index.add_documents(data)
  111 
  112     def update(self):
  113         """
  114         Updates the Meilisearch index.
  115         """
  116         # Create a Meilisearch client.
  117         client = meilisearch.Client(self.getConnectionString(), self.getMasterKey())
  118 
  119         # Delete previous index
  120         if len(client.get_indexes()):
  121             client.get_index(self.docs_index).delete()
  122 
  123         # Create a new index
  124         index = client.create_index(uid=self.docs_index, options={'primaryKey': self.primaryKey, 'uid': self.index_name})
  125 
  126         # Add synonyms for the index
  127         index.update_synonyms(self.synonyms)
  128 
  129         # Update its settings: what can be searched, what's displayable, and how results should be ranked.
  130         index.update_settings(self.updated_settings)
  131 
  132         # Update distinct attribute.
  133         index.update_distinct_attribute(self.distinct_attribute)
  134 
  135         # Add documents to the index
  136         self.add_documents(index)
  137 
  138 if __name__ == "__main__":
  139     meili = Search()
  140     meili.update()
  141
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/search/main.py b/search/main.py
  --- a/search/main.py
  +++ b/search/main.py
  @@ -21,9 +21,9 @@
           # Below are Platform.sh custom settings for how the search engine functions.
   
           # Data available to the dropdown React app in docs, used to fill out autocomplete results.
  -        self.displayed_attributes = ['title', 'text', 'url', 'site', 'section']
  +        self.displayed_attributes = ['keywords', 'title', 'text', 'url', 'site', 'section']
           # Data actually searchable by our queries.
  -        self.searchable_attributes = ['title', 'pageUrl', 'section', 'url', 'text']
  +        self.searchable_attributes = ['keywords', 'title', 'pageUrl', 'section', 'text', 'url']
   
           # Show results for one query with the listed pages, when they by default would not show up as best results.
           # Note: these aren't automatically two-way, which is why they're all defined twice.
  
verification_info: {"golden_diff": "diff --git a/search/main.py b/search/main.py\n--- a/search/main.py\n+++ b/search/main.py\n@@ -21,9 +21,9 @@\n         # Below are Platform.sh custom settings for how the search engine functions.\n \n         # Data available to the dropdown React app in docs, used to fill out autocomplete results.\n-        self.displayed_attributes = ['title', 'text', 'url', 'site', 'section']\n+        self.displayed_attributes = ['keywords', 'title', 'text', 'url', 'site', 'section']\n         # Data actually searchable by our queries.\n-        self.searchable_attributes = ['title', 'pageUrl', 'section', 'url', 'text']\n+        self.searchable_attributes = ['keywords', 'title', 'pageUrl', 'section', 'text', 'url']\n \n         # Show results for one query with the listed pages, when they by default would not show up as best results.\n         # Note: these aren't automatically two-way, which is why they're all defined twice.\n", "issue": "Add keywords for search\n### Where on docs.platform.sh should be changed?\n\n/configuration/app/app-reference.html\n\n### What exactly should be updated?\n\nWe'd like specific pages to be findable by searching for specific words. For example, the app reference when searching for `.platform.app.yaml` (this may also involve a problem with escaping characters like `.`). Add keywords or other to make these pages findable.\n\n### Additional context\n\n_No response_\n", "before_files": [{"content": "import os\nimport glob\nimport json\nimport meilisearch\nfrom platformshconfig import Config\n\nclass Search:\n    def __init__(self):\n        self.default = {\n            \"host\": \"http://127.0.0.1\",\n            \"key\": None,\n            \"port\": 7700\n        }\n\n        self.scrape_dir = \"output\"\n        self.scrape_config = \"config/scrape.json\"\n        self.docs_index = \"docs\"\n        self.primaryKey = \"documentId\"\n        self.index_name = \"Docs\"\n\n        # Below are Platform.sh custom settings for how the search engine functions.\n\n        # Data available to the dropdown React app in docs, used to fill out autocomplete results.\n        self.displayed_attributes = ['title', 'text', 'url', 'site', 'section']\n        # Data actually searchable by our queries.\n        self.searchable_attributes = ['title', 'pageUrl', 'section', 'url', 'text']\n\n        # Show results for one query with the listed pages, when they by default would not show up as best results.\n        # Note: these aren't automatically two-way, which is why they're all defined twice.\n        self.synonyms = {\n            \"cron\": [\"crons\"],\n            \"crons\": [\"cron tasks\", \"cron jobs\"],\n            \"e-mail\": [\"email\"],\n            \"routes.yaml\": [\"routes\"],\n            \"routes\": [\"routes.yaml\"],\n            \"services\": [\"services.yaml\"],\n            \"services.yaml\": [\"services\"],\n            \"application\": [\".platform.app.yaml\", \"app.yaml\", \"applications.yaml\"],\n            \".platform.app.yaml\": [\"application\"],\n            \"app.yaml\": [\"application\"],\n            \"applications.yaml\": [\"application\", \"multi-app\"],\n            \"multi-app\": [\"applications.yaml\"],\n            \"regions\": [\"public ip addresses\"],\n            \"public ip addresses\": [\"regions\"],\n            \"ssl\": [\"https\", \"tls\"],\n            \"https\": [\"ssl\"],\n        }\n\n        # Ranking rules:\n        #\n        #   - Default order: [\"words\", \"typo\", \"proximity\", \"attribute\", \"sort\", \"exactness\"]\n        #\n        #   - words: number of times query is in document (greater number gets priority)\n        #   - typo: fewer typos > more typos\n        #   - proximity: smaller distance between multiple occurences of query in same document > larger distances\n        #   - attribute: sorted according to order of importance of attributes (searchable_attributes). terms in\n        #       more important attributes first.\n        #   - sort: queries are sorted at query time\n        #   - exactness: similarity of matched words in document with query\n\n        self.ranking_rules = [\"rank:asc\", \"attribute\", \"typo\", \"words\", \"proximity\", \"exactness\"]\n\n        self.updated_settings = {\n            \"rankingRules\": self.ranking_rules,\n            \"searchableAttributes\": self.searchable_attributes,\n            \"displayedAttributes\": self.displayed_attributes\n        }\n\n        # Group results by page\n        self.distinct_attribute = \"pageUrl\"\n\n    def getConnectionString(self):\n        \"\"\"\n        Sets the Meilisearch host string, depending on the environment.\n\n        Returns:\n            string: Meilisearch host string.\n        \"\"\"\n        if os.environ.get('PORT'):\n            return \"{}:{}\".format(self.default[\"host\"], os.environ['PORT'])\n        else:\n            return \"{}:{}\".format(self.default[\"host\"], self.default[\"port\"])\n\n    def getMasterKey(self):\n        \"\"\"\n        Retrieves the Meilisearch master key, either from the Platform.sh environment or locally.\n        \"\"\"\n        config = Config()\n        if config.is_valid_platform():\n            return config.projectEntropy\n        elif os.environ.get(\"MEILI_MASTER_KEY\"):\n            return os.environ[\"MEILI_MASTER_KEY\"]\n        else:\n            return self.default[\"key\"]\n\n    def add_documents(self, index):\n        \"\"\"\n        Cycle through the individual site indexes in /outputs so their individual documents can be added to Meilisearch.\n        \"\"\"\n        documents = [f for f in glob.glob(\"{}/*.json\".format(self.scrape_dir))]\n        for doc in documents:\n            self.add(doc, index)\n\n    def add(self, doc, index):\n        \"\"\"\n        Add an individual site's index to the Meilisearch service.\n        \"\"\"\n        with open(doc) as scraped_index:\n            data = json.load(scraped_index)\n            index.add_documents(data)\n\n    def update(self):\n        \"\"\"\n        Updates the Meilisearch index.\n        \"\"\"\n        # Create a Meilisearch client.\n        client = meilisearch.Client(self.getConnectionString(), self.getMasterKey())\n\n        # Delete previous index\n        if len(client.get_indexes()):\n            client.get_index(self.docs_index).delete()\n\n        # Create a new index\n        index = client.create_index(uid=self.docs_index, options={'primaryKey': self.primaryKey, 'uid': self.index_name})\n\n        # Add synonyms for the index\n        index.update_synonyms(self.synonyms)\n\n        # Update its settings: what can be searched, what's displayable, and how results should be ranked.\n        index.update_settings(self.updated_settings)\n\n        # Update distinct attribute.\n        index.update_distinct_attribute(self.distinct_attribute)\n\n        # Add documents to the index\n        self.add_documents(index)\n\nif __name__ == \"__main__\":\n    meili = Search()\n    meili.update()\n", "path": "search/main.py"}], "after_files": [{"content": "import os\nimport glob\nimport json\nimport meilisearch\nfrom platformshconfig import Config\n\nclass Search:\n    def __init__(self):\n        self.default = {\n            \"host\": \"http://127.0.0.1\",\n            \"key\": None,\n            \"port\": 7700\n        }\n\n        self.scrape_dir = \"output\"\n        self.scrape_config = \"config/scrape.json\"\n        self.docs_index = \"docs\"\n        self.primaryKey = \"documentId\"\n        self.index_name = \"Docs\"\n\n        # Below are Platform.sh custom settings for how the search engine functions.\n\n        # Data available to the dropdown React app in docs, used to fill out autocomplete results.\n        self.displayed_attributes = ['keywords', 'title', 'text', 'url', 'site', 'section']\n        # Data actually searchable by our queries.\n        self.searchable_attributes = ['keywords', 'title', 'pageUrl', 'section', 'text', 'url']\n\n        # Show results for one query with the listed pages, when they by default would not show up as best results.\n        # Note: these aren't automatically two-way, which is why they're all defined twice.\n        self.synonyms = {\n            \"cron\": [\"crons\"],\n            \"crons\": [\"cron tasks\", \"cron jobs\"],\n            \"e-mail\": [\"email\"],\n            \"routes.yaml\": [\"routes\"],\n            \"routes\": [\"routes.yaml\"],\n            \"services\": [\"services.yaml\"],\n            \"services.yaml\": [\"services\"],\n            \"application\": [\".platform.app.yaml\", \"app.yaml\", \"applications.yaml\"],\n            \".platform.app.yaml\": [\"application\"],\n            \"app.yaml\": [\"application\"],\n            \"applications.yaml\": [\"application\", \"multi-app\"],\n            \"multi-app\": [\"applications.yaml\"],\n            \"regions\": [\"public ip addresses\"],\n            \"public ip addresses\": [\"regions\"],\n            \"ssl\": [\"https\", \"tls\"],\n            \"https\": [\"ssl\"],\n        }\n\n        # Ranking rules:\n        #\n        #   - Default order: [\"words\", \"typo\", \"proximity\", \"attribute\", \"sort\", \"exactness\"]\n        #\n        #   - words: number of times query is in document (greater number gets priority)\n        #   - typo: fewer typos > more typos\n        #   - proximity: smaller distance between multiple occurences of query in same document > larger distances\n        #   - attribute: sorted according to order of importance of attributes (searchable_attributes). terms in\n        #       more important attributes first.\n        #   - sort: queries are sorted at query time\n        #   - exactness: similarity of matched words in document with query\n\n        self.ranking_rules = [\"rank:asc\", \"attribute\", \"typo\", \"words\", \"proximity\", \"exactness\"]\n\n        self.updated_settings = {\n            \"rankingRules\": self.ranking_rules,\n            \"searchableAttributes\": self.searchable_attributes,\n            \"displayedAttributes\": self.displayed_attributes\n        }\n\n        # Group results by page\n        self.distinct_attribute = \"pageUrl\"\n\n    def getConnectionString(self):\n        \"\"\"\n        Sets the Meilisearch host string, depending on the environment.\n\n        Returns:\n            string: Meilisearch host string.\n        \"\"\"\n        if os.environ.get('PORT'):\n            return \"{}:{}\".format(self.default[\"host\"], os.environ['PORT'])\n        else:\n            return \"{}:{}\".format(self.default[\"host\"], self.default[\"port\"])\n\n    def getMasterKey(self):\n        \"\"\"\n        Retrieves the Meilisearch master key, either from the Platform.sh environment or locally.\n        \"\"\"\n        config = Config()\n        if config.is_valid_platform():\n            return config.projectEntropy\n        elif os.environ.get(\"MEILI_MASTER_KEY\"):\n            return os.environ[\"MEILI_MASTER_KEY\"]\n        else:\n            return self.default[\"key\"]\n\n    def add_documents(self, index):\n        \"\"\"\n        Cycle through the individual site indexes in /outputs so their individual documents can be added to Meilisearch.\n        \"\"\"\n        documents = [f for f in glob.glob(\"{}/*.json\".format(self.scrape_dir))]\n        for doc in documents:\n            self.add(doc, index)\n\n    def add(self, doc, index):\n        \"\"\"\n        Add an individual site's index to the Meilisearch service.\n        \"\"\"\n        with open(doc) as scraped_index:\n            data = json.load(scraped_index)\n            index.add_documents(data)\n\n    def update(self):\n        \"\"\"\n        Updates the Meilisearch index.\n        \"\"\"\n        # Create a Meilisearch client.\n        client = meilisearch.Client(self.getConnectionString(), self.getMasterKey())\n\n        # Delete previous index\n        if len(client.get_indexes()):\n            client.get_index(self.docs_index).delete()\n\n        # Create a new index\n        index = client.create_index(uid=self.docs_index, options={'primaryKey': self.primaryKey, 'uid': self.index_name})\n\n        # Add synonyms for the index\n        index.update_synonyms(self.synonyms)\n\n        # Update its settings: what can be searched, what's displayable, and how results should be ranked.\n        index.update_settings(self.updated_settings)\n\n        # Update distinct attribute.\n        index.update_distinct_attribute(self.distinct_attribute)\n\n        # Add documents to the index\n        self.add_documents(index)\n\nif __name__ == \"__main__\":\n    meili = Search()\n    meili.update()\n", "path": "search/main.py"}]}