快速入门:使用 Jupyter Notebook 通过 Python 创建 Azure 认知搜索索引

使用 Python 和用于 Python 的 Azure SDK 中的 azure-search-documents 库生成可创建、加载和查询 Azure 认知搜索索引的 Jupyter Notebook。 本文介绍如何逐步生成笔记本。 你也可以下载并运行一个已完成的 Jupyter Python 笔记本

如果没有 Azure 订阅,请在开始前创建一个试用订阅

先决条件

本快速入门需要以下服务和工具。

复制密钥和 URL

REST 调用需要在每个请求中使用服务 URL 和访问密钥。 搜索服务是使用这二者创建的,因此,如果向订阅添加了 Azure 认知搜索,则请按以下步骤获取必需信息:

  1. 登录到 Azure 门户,在搜索服务的“概述”页中获取 URL。 示例终结点可能类似于 https://mydemo.search.azure.cn

  2. 在“设置” > “密钥”中,获取有关该服务的完全权限的管理员密钥 。 有两个可交换的管理员密钥,为保证业务连续性而提供,以防需要滚动一个密钥。 可以在请求中使用主要或辅助密钥来添加、修改和删除对象。

    获取 HTTP 终结点和访问密钥

所有请求对发送到服务的每个请求都需要 API 密钥。 具有有效的密钥可以在发送请求的应用程序与处理请求的服务之间建立信任关系,这种信任关系以每个请求为基础。

在此任务中,请启动 Jupyter Notebook 并验证能否连接到 Azure 认知搜索。 为此,你将从服务请求索引列表。 在装有 Anaconda3 的 Windows 上,可以使用 Anaconda Navigator 来启动笔记本。

  1. 创建新的 Python3 笔记本。

  2. 在第一个单元格中,从用于 Python 的 Azure SDK 加载库,包括 azure-search-documents

     !pip install azure-search-documents --pre
     !pip show azure-search-documents
    
     import os
     from azure.core.credentials import AzureKeyCredential
     from azure.search.documents.indexes import SearchIndexClient 
     from azure.search.documents import SearchClient
     from azure.search.documents.indexes.models import (
         ComplexField,
         CorsOptions,
         SearchIndex,
         ScoringProfile,
         SearchFieldDataType,
         SimpleField,
         SearchableField
     )
    
  3. 在第二个单元格中,输入用作每个请求中的常量的请求元素。 输入在上一步中复制的搜索服务名称、管理员 API 密钥和查询 API 密钥。 此单元格还设置了将用于执行特定操作的客户端:用于创建索引的 SearchIndexClient,以及用于查询索引的 SearchClient

     service_name = ["SEARCH_ENDPOINT - do not include search.azure.cn"]
     admin_key = ["Cognitive Search Admin API Key"]
    
     index_name = "hotels-quickstart"
    
     # Create an SDK client
     endpoint = "https://{}.search.azure.cn/".format(service_name)
     admin_client = SearchIndexClient(endpoint=endpoint,
                           index_name=index_name,
                           credential=AzureKeyCredential(admin_key))
    
     search_client = SearchClient(endpoint=endpoint,
                           index_name=index_name,
                           credential=AzureKeyCredential(admin_key))
    
  4. 在第三个单元格中,运行 delete_index 操作以清除所有现有的 hotels-quickstart 索引服务。 通过删除索引,可以创建另一个同名的 hotels-quickstart 索引。

     try:
         result = admin_client.delete_index(index_name)
         print ('Index', index_name, 'Deleted')
     except Exception as ex:
         print (ex)
    
  5. 运行每个步骤。

1 - 创建索引

索引的所需元素包括名称、字段集合和键。 字段集合定义逻辑搜索文档的结构,用于加载数据和返回结果。

每个字段具有一个确定其用法的名称、类型和属性(例如,该字段在搜索结果是否可全文搜索、可筛选或可检索)。 在索引中,必须将一个 Edm.String 类型的字段指定为文档标识的键。

此索引名为“hotels-quickstart”,使用下面所示的字段定义。 它是其他演练中使用的一个更大 Hotels 索引的子集。 为简明起见,本快速入门已对其进行修整。

  1. 在下一个单元格中,将以下示例粘贴到某个单元格以提供架构。

    # Specify the index schema
    name = index_name
    fields = [
            SimpleField(name="HotelId", type=SearchFieldDataType.String, key=True),
            SearchableField(name="HotelName", type=SearchFieldDataType.String, sortable=True),
            SearchableField(name="Description", type=SearchFieldDataType.String, analyzer_name="en.lucene"),
            SearchableField(name="Description_fr", type=SearchFieldDataType.String, analyzer_name="fr.lucene"),
            SearchableField(name="Category", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
    
            SearchableField(name="Tags", collection=True, type=SearchFieldDataType.String, facetable=True, filterable=True),
    
            SimpleField(name="ParkingIncluded", type=SearchFieldDataType.Boolean, facetable=True, filterable=True, sortable=True),
            SimpleField(name="LastRenovationDate", type=SearchFieldDataType.DateTimeOffset, facetable=True, filterable=True, sortable=True),
            SimpleField(name="Rating", type=SearchFieldDataType.Double, facetable=True, filterable=True, sortable=True),
    
            ComplexField(name="Address", fields=[
                SearchableField(name="StreetAddress", type=SearchFieldDataType.String),
                SearchableField(name="City", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
                SearchableField(name="StateProvince", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
                SearchableField(name="PostalCode", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
                SearchableField(name="Country", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
            ])
        ]
    cors_options = CorsOptions(allowed_origins=["*"], max_age_in_seconds=60)
    scoring_profiles = []
    suggester = [{'name': 'sg', 'source_fields': ['Tags', 'Address/City', 'Address/Country']}]
    
  2. 在另一个单元格中构建请求。 此 create_index 请求以搜索服务的索引集合为目标,并基于在上一单元格中提供的索引架构创建 SearchIndex

    index = SearchIndex(
        name=name,
        fields=fields,
        scoring_profiles=scoring_profiles,
        suggesters = suggester,
        cors_options=cors_options)
    
    try:
        result = admin_client.create_index(index)
        print ('Index', result.name, 'created')
    except Exception as ex:
        print (ex)
    
  3. 运行每个步骤。

2 - 加载文档

若要加载文档,请使用操作类型(上传、合并上传等)的索引操作来创建文档集合。 文档源自 GitHub 上的 HotelsData

  1. 在新单元格中,提供符合索引架构的四个文档。 指定每个文档的上传操作。

    documents = [
        {
        "@search.action": "upload",
        "HotelId": "1",
        "HotelName": "Secret Point Motel",
        "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
        "Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
        "Category": "Boutique",
        "Tags": [ "pool", "air conditioning", "concierge" ],
        "ParkingIncluded": "false",
        "LastRenovationDate": "1970-01-18T00:00:00Z",
        "Rating": 3.60,
        "Address": {
            "StreetAddress": "677 5th Ave",
            "City": "New York",
            "StateProvince": "NY",
            "PostalCode": "10022",
            "Country": "USA"
            }
        },
        {
        "@search.action": "upload",
        "HotelId": "2",
        "HotelName": "Twin Dome Motel",
        "Description": "The hotel is situated in a  nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.",
        "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
        "Category": "Boutique",
        "Tags": [ "pool", "free wifi", "concierge" ],
        "ParkingIncluded": "false",
        "LastRenovationDate": "1979-02-18T00:00:00Z",
        "Rating": 3.60,
        "Address": {
            "StreetAddress": "140 University Town Center Dr",
            "City": "Sarasota",
            "StateProvince": "FL",
            "PostalCode": "34243",
            "Country": "USA"
            }
        },
        {
        "@search.action": "upload",
        "HotelId": "3",
        "HotelName": "Triple Landscape Hotel",
        "Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services.",
        "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
        "Category": "Resort and Spa",
        "Tags": [ "air conditioning", "bar", "continental breakfast" ],
        "ParkingIncluded": "true",
        "LastRenovationDate": "2015-09-20T00:00:00Z",
        "Rating": 4.80,
        "Address": {
            "StreetAddress": "3393 Peachtree Rd",
            "City": "Atlanta",
            "StateProvince": "GA",
            "PostalCode": "30326",
            "Country": "USA"
            }
        },
        {
        "@search.action": "upload",
        "HotelId": "4",
        "HotelName": "Sublime Cliff Hotel",
        "Description": "Sublime Cliff Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace.",
        "Description_fr": "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.",
        "Category": "Boutique",
        "Tags": [ "concierge", "view", "24-hour front desk service" ],
        "ParkingIncluded": "true",
        "LastRenovationDate": "1960-02-06T00:00:00Z",
        "Rating": 4.60,
        "Address": {
            "StreetAddress": "7400 San Pedro Ave",
            "City": "San Antonio",
            "StateProvince": "TX",
            "PostalCode": "78216",
            "Country": "USA"
            }
        }
    ]
    
  2. 在另一个单元格中构建请求。 此 upload_documents 请求以 hotels-quickstart 索引的文档集合为目标,将在上一步骤中提供的文档推送到认知搜索索引。

    try:
        result = search_client.upload_documents(documents=documents)
        print("Upload of new document succeeded: {}".format(result[0].succeeded))
    except Exception as ex:
        print (ex.message)
    
  3. 运行每个步骤,将文档推送到搜索服务中的索引。

3 - 搜索索引

此步骤说明如何使用 search.client 类的 search 方法来查询索引。

  1. 下面的步骤执行空搜索 (search=*),返回任意文档的未排名列表(搜索分数 = 1.0)。 由于没有条件,因此所有文档都包含在结果中。 此查询仅输出每个文档中的两个字段。 它还会添加 include_total_count=True 以获取结果中所有文档 (4) 的计数。

    results =  search_client.search(search_text="*", include_total_count=True)
    
    print ('Total Documents Matching Query:', results.get_count())
    for result in results:
        print("{}: {}".format(result["HotelId"], result["HotelName"]))
    
  2. 下一个查询将整个术语添加到搜索表达式 ("wifi")。 此查询指定结果仅包含 select 语句中的那些字段。 限制返回的字段可最大程度地减少通过网络发回的数据量,并降低搜索延迟。

    results =  search_client.search(search_text="wifi", include_total_count=True, select='HotelId,HotelName,Tags')
    
    print ('Total Documents Matching Query:', results.get_count())
    for result in results:
        print("{}: {}: {}".format(result["HotelId"], result["HotelName"], result["Tags"]))
    
  3. 接下来,应用筛选表达式,仅返回评分高于 4 的酒店(按降序排列)。

    results =  search_client.search(search_text="hotels", select='HotelId,HotelName,Rating', filter='Rating gt 4', order_by='Rating desc')
    
    for result in results:
        print("{}: {} - {} rating".format(result["HotelId"], result["HotelName"], result["Rating"]))
    
  4. 添加 search_fields,将查询匹配的范围限制为单一字段。

    results =  search_client.search(search_text="sublime", search_fields='HotelName', select='HotelId,HotelName')
    
    for result in results:
        print("{}: {}".format(result["HotelId"], result["HotelName"]))
    
  5. Facet 是可用于组成 Facet 导航结构的标签。 此查询返回类别的 Facet 和计数。

    results =  search_client.search(search_text="*", facets=["Category"])
    
    facets = results.get_facets()
    
    for facet in facets["Category"]:
        print("    {}".format(facet))
    
  6. 在此示例中,根据文档的键查找特定的文档。 用户单击搜索结果中的文档时,通常你需要返回文档。

    result = search_client.get_document(key="3")
    
    print("Details for hotel '3' are:")
    print("Name: {}".format(result["HotelName"]))
    print("Rating: {}".format(result["Rating"]))
    print("Category: {}".format(result["Category"]))
    
  7. 在此示例中,我们将使用自动完成函数。 用户在搜索框中键入内容时,通常在搜索框中使用该函数来帮助自动完成潜在匹配。

    创建索引时,还会创建名为“sg”的建议器作为请求的一部分。 建议器定义指定哪些字段可用于查找建议器请求的潜在匹配。 在此示例中,这些字段是“标签”、“地址/城市”、“地址/国家/地区”。 若要模拟自动完成,请输入字母“sa”作为字符串的一部分。 SearchClient 的自动完成方法会发回可能的术语匹配。

    search_suggestion = 'sa'
    results = search_client.autocomplete(search_text=search_suggestion, suggester_name="sg", mode='twoTerms')
    
    print("Autocomplete for:", search_suggestion)
    for result in results:
        print (result['text'])
    

清理

使用自己的订阅时,最好在项目结束时确定是否仍然需要所创建的资源。 持续运行资源可能会产生费用。 可以逐个删除资源,也可以删除资源组以删除整个资源集。

可以使用左侧导航窗格中的“所有资源”或“资源组”链接 ,在门户中查找和管理资源。

如果使用的是免费服务,请记住只能设置三个索引、索引器和数据源。 可以在门户中删除单个项目,以不超出此限制。

后续步骤

为简单起见,本快速入门使用了 Hotels 索引的缩写版本。 你可以创建完整的版本,以尝试进行更有意思的查询。 若要获取完整版本和所有 50 个文档,请运行“导入数据”向导,并从内置的示例数据源中选择“hotels-sample”。