你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

2 - 创建和加载包含 JavaScript 的搜索索引

按照以下步骤继续生成启用搜索的网站:

创建 Azure AI 搜索资源

使用 Azure CLI 或 Azure PowerShell,从命令行创建新的搜索资源。 你还将检索用于对索引进行读取访问的查询密钥,并获取用于添加对象的内置管理密钥。

设备上必须已安装 Azure CLIAzure PowerShell。 如果你不是设备的本地管理员,请选择 Azure PowerShell 并使用参数“Scope”以当前用户身份运行。

注意

此任务不需要 Azure CLI 和 Azure PowerShell 的 Visual Studio Code 扩展。 Visual Studio Code 可识别没有扩展的命令行工具。

  1. 在 Visual Studio Code 中的“终端”下,选择“新建终端”。

  2. 连接到 Azure:

    az login
    
  3. 创建新的搜索服务之前,先列出订阅的现有服务:

    az resource list --resource-type Microsoft.Search/searchServices --output table
    

    如果有希望使用的服务,请记下名称,然后跳到下一部分。

  4. 创建新搜索服务。 将以下命令作为模板,将资源组、服务名称、层、区域、分区和副本替换为有效值。 以下语句使用在上一步中创建的“cognitive-search-demo-rg”资源组,并指定“免费”层。 如果 Azure 订阅已提供免费搜索服务,则指定“基本”等计费层。

    az search service create --name my-cog-search-demo-svc --resource-group cognitive-search-demo-rg --sku free --partition-count 1 --replica-count 1
    
  5. 获取向搜索服务授予读取访问权限的查询密钥。 搜索服务预配有两个管理密钥和一个查询密钥。 替换为资源组和搜索服务的有效名称。 将查询密钥复制到记事本,以便在后面的步骤中将其粘贴到客户端代码中:

    az search query-key list --resource-group cognitive-search-demo-rg --service-name my-cog-search-demo-svc
    
  6. 获取搜索服务管理员 API 密钥。 管理 API 密钥提供对搜索服务的写入访问权限。 将任一管理密钥复制到记事本,以便在创建和加载索引的批量导入步骤中使用它:

    az search admin-key show --resource-group cognitive-search-demo-rg --service-name my-cog-search-demo-svc
    

此 ESM 脚本使用 Azure SDK 进行 Azure AI 搜索:

  1. 在 Visual Studio Code 中,在子目录 search-website-functions-v4/bulk-insert 中打开 bulk_insert_books.js 文件,将以下变量替换为您自己的值,以便使用 Azure 搜索 SDK 进行身份验证:

    • YOUR-SEARCH-RESOURCE-NAME
    • YOUR-SEARCH-ADMIN-KEY
    import fetch from 'node-fetch';
    import Papa from 'papaparse';
    import {
      SearchClient,
      SearchIndexClient,
      AzureKeyCredential
    } from '@azure/search-documents';
    
    // Azure AI Search resource settings
    const SEARCH_ENDPOINT = 'https://YOUR-RESOURCE-NAME.search.windows.net';
    const SEARCH_ADMIN_KEY = 'YOUR-RESOURCE-ADMIN-KEY';
    
    // Azure AI Search index settings
    const SEARCH_INDEX_NAME = 'good-books';
    import SEARCH_INDEX_SCHEMA from './good-books-index.json' assert { type: 'json' };
    
    // Data settings
    const BOOKS_URL =
      'https://raw.githubusercontent.com/Azure-Samples/azure-search-sample-data/main/good-books/books.csv';
    const BATCH_SIZE = 1000;
    
    // Create Search service client
    // used to upload docs into Index
    const client = new SearchClient(
      SEARCH_ENDPOINT,
      SEARCH_INDEX_NAME,
      new AzureKeyCredential(SEARCH_ADMIN_KEY)
    );
    
    // Create Search service Index client
    // used to create new Index
    const clientIndex = new SearchIndexClient(
      SEARCH_ENDPOINT,
      new AzureKeyCredential(SEARCH_ADMIN_KEY)
    );
    
    // Insert docs into Search Index
    // in batch
    const insertData = async (data) => {
      let batch = 0;
      let batchArray = [];
    
      for (let i = 0; i < data.length; i++) {
        const row = data[i];
    
        // Convert string data to typed data
        // Types are defined in schema
        batchArray.push({
          id: row.book_id,
          goodreads_book_id: parseInt(row.goodreads_book_id),
          best_book_id: parseInt(row.best_book_id),
          work_id: parseInt(row.work_id),
          books_count: !row.books_count ? 0 : parseInt(row.books_count),
          isbn: row.isbn,
          isbn13: row.isbn13,
          authors: row.authors.split(',').map((name) => name.trim()),
          original_publication_year: !row.original_publication_year
            ? 0
            : parseInt(row.original_publication_year),
          original_title: row.original_title,
          title: row.title,
          language_code: row.language_code,
          average_rating: !row.average_rating ? 0 : parseFloat(row.average_rating),
          ratings_count: !row.ratings_count ? 0 : parseInt(row.ratings_count),
          work_ratings_count: !row.work_ratings_count
            ? 0
            : parseInt(row.work_ratings_count),
          work_text_reviews_count: !row.work_text_reviews_count
            ? 0
            : parseInt(row.work_text_reviews_count),
          ratings_1: !row.ratings_1 ? 0 : parseInt(row.ratings_1),
          ratings_2: !row.ratings_2 ? 0 : parseInt(row.ratings_2),
          ratings_3: !row.ratings_3 ? 0 : parseInt(row.ratings_3),
          ratings_4: !row.ratings_4 ? 0 : parseInt(row.ratings_4),
          ratings_5: !row.ratings_5 ? 0 : parseInt(row.ratings_5),
          image_url: row.image_url,
          small_image_url: row.small_image_url
        });
    
        console.log(`${i}`);
    
        // Insert batch into Index
        if (batchArray.length % BATCH_SIZE === 0) {
          await client.uploadDocuments(batchArray);
    
          console.log(`BATCH SENT`);
          batchArray = [];
        }
      }
      // Insert any final batch into Index
      if (batchArray.length > 0) {
        await client.uploadDocuments(batchArray);
    
        console.log(`FINAL BATCH SENT`);
        batchArray = [];
      }
    };
    const bulkInsert = async () => {
      // Download CSV Data file
      const response = await fetch(BOOKS_URL, {
        method: 'GET'
      });
      if (response.ok) {
        console.log(`book list fetched`);
        const fileData = await response.text();
        console.log(`book list data received`);
    
        // convert CSV to JSON
        const dataObj = Papa.parse(fileData, {
          header: true,
          encoding: 'utf8',
          skipEmptyLines: true
        });
        console.log(`book list data parsed`);
    
        // Insert JSON into Search Index
        await insertData(dataObj.data);
        console.log(`book list data inserted`);
      } else {
        console.log(`Couldn\t download data`);
      }
    };
    
    // Create Search Index
    async function createIndex() {
      SEARCH_INDEX_SCHEMA.name = SEARCH_INDEX_NAME;
    
      const result = await clientIndex.createIndex(SEARCH_INDEX_SCHEMA);
    }
    
    await createIndex();
    console.log('index created');
    
    await bulkInsert();
    console.log('data inserted into index');
    
  2. 在 Visual Studio 中打开用于项目目录的子目录 search-website-functions-v4/bulk-insert 的集成终端,并运行以下命令以安装依赖项。

    npm install 
    
  1. 继续使用 Visual Studio 中的集成终端作为项目目录的子目录 search-website-functions-v4/bulk-insert,以运行 bulk_insert_books.js 脚本:

    npm start
    
  2. 当代码运行时,控制台将显示进度。

  3. 上传完成后,打印到控制台的最后一个语句将为“已完成”。

查看新的搜索索引

上传完成后,便可以使用搜索索引。 在 Azure 门户中查看新索引。

  1. 在 Azure 门户中,找到在上一步中创建的搜索服务

  2. 在左侧,选择“索引”,然后选择好书索引。

    Expandable screenshot of Azure portal showing the index.

  3. 默认情况下,索引将在“搜索资源管理器”选项卡中打开。选择“搜索”以从索引返回文档。

    Expandable screenshot of Azure portal showing search results

回滚批量导入文件更改

bulk-insert 目录中的 Visual Studio Code 集成终端使用以下 git 命令来回滚更改。 它们不是继续学习教程所必需的,不应将这些机密保存或推送到你的存储库。

git checkout .

复制搜索资源名称

记下搜索资源名称。 将 Azure Function 应用连接到搜索资源时需要用到它。

注意

尽管你可能想要在 Azure 函数中使用搜索管理密钥,但这并不符合最低特权原则。 Azure Function 将使用查询密钥以符合最小特权。

后续步骤

部署静态 Web 应用