收集遥测数据以用于分析搜索流量

搜索流量分析是一种用于收集遥测数据的模式,它收集有关用户与 Azure 认知搜索应用程序之间的交互(例如用户发起的单击事件和键盘输入)的遥测数据。 使用此信息,你可以确定搜索解决方案的有效性,包括热门搜索词、点击率以及哪些查询输入产生了零个结果。

此模式依赖于 Application InsightsAzure Monitor 的一项功能)来收集用户数据。 你还需要向客户端代码中添加检测机制,如本文中所述。 最后,你将需要一个报告机制来分析数据。 建议使用 Power BI,但你可以使用应用程序仪表板或可连接到 Application Insights 的任何工具。

注意

本文中所述的模式适用于你添加到客户端的代码生成的高级方案和点击流数据。 相比之下,服务日志易于设置,提供各种指标,且无需编写任何代码即可在门户中操作。 建议对所有方案启用日志记录。 有关详细信息,请参阅收集和分析日志数据

标识相关的搜索数据

若要为搜索流量分析提供有用的指标,必须记录搜索应用程序用户发出的一些信号。 这些信号表示用户感兴趣的内容以及他们认为相关的内容。 对于搜索流量分析,这些信号包括:

  • 用户生成的搜索事件:只有用户发起的搜索查询才是需要关注的。 其他搜索请求(例如用于填充方面或检索内部信息的搜索请求)并不重要。 请确保只检测用户启动的事件,以避免结果中出现扭曲或偏差。

  • 用户生成的单击事件:在搜索结果页上,单击事件通常意味着某个文档是特定搜索查询的相关结果。

通过将搜索和单击事件链接到相关 ID,可以更深入地了解应用程序搜索功能的性能表现。

添加搜索流量分析

在 Azure 认知搜索服务的门户页中,打开“搜索流量分析”页,以访问用于遵循此遥测模式的速查表。 在此页中,可以选择或创建一个 Application Insights 资源,获取检测密钥,复制可以根据你的解决方案改编的代码片段,并下载基于模式中反映的架构生成的 Power BI 报表。

Search Traffic Analytics page in the portal在门户中的门户中

1 - 设置 Application Insights

选择现有的 Application Insights 资源,如果没有 Application Insights 资源,则创建一个资源。 如果使用“搜索流量分析”页,则可以复制应用程序在连接到 Application Insights 时所需的检测密钥。

有了 Application Insights 资源后,可以按照适用于受支持语言和平台的说明来注册应用。 注册只是将 Application Insights 中的检测密钥添加到代码,以设置关联。 选择现有的资源时,可以在门户或“搜索流量分析”页中找到该密钥。

以下步骤反映了适用于某些 Visual Studio 项目类型的快捷方式。 只需单击几下鼠标,即可创建资源并注册应用。

  1. 对于 Visual Studio 和 ASP.NET 开发,请打开解决方案,然后选择 " Project添加 Application Insights 遥测

  2. 单击“开始”。

  3. 通过提供 Microsoft 帐户、Azure 订阅和 Application Insights 资源(某个新资源是默认资源)来注册你的应用。 单击“注册”。

此时,你已经为应用程序设置了应用程序监视,这意味着,将使用默认指标跟踪所有页面加载。 有关上述步骤的详细信息,请参阅启用 Application Insights 服务器端遥测

2 - 添加检测

在此步骤中,将使用以上步骤中创建的 Application Insights 资源检测自己的搜索应用程序。 此过程包括四个步骤,第一个步骤是创建遥测客户端。

步骤 1:创建遥测客户端

创建一个用于将事件发送到 Application Insights 的对象。 可以将检测机制添加到服务器端应用程序代码或在浏览器中运行的客户端代码中,此处的代码以 C# 和 JavaScript 变体表示(对于其他语言,请参阅支持的平台和框架的完整列表)。 选择可提供所需信息深度的方法。

服务器端遥测将捕获应用程序层(例如,在云中作为 Web 服务运行的应用程序,或者在企业网络中作为本地应用运行的应用程序)的指标。 服务器端遥测捕获搜索和单击事件、结果中文档的位置以及查询信息,但数据收集范围将限定为该层上可用的任何信息。

在客户端上,可以使用附加的代码来操作查询输入、添加导航或包含上下文(例如,从主页或产品页发起的查询)。 如果此描述符合你的解决方案,则你可以选择启用客户端检测机制,使遥测数据反映附加的详细信息。 如何收集这些附加详细信息超出了此模式的范围,但你可以查看适用于网页的 Application Insights 来获取更多指导。

使用 C#

对于 C#,如果项目是 ASP.NET,则应在应用程序配置(例如 appsettings.json)中定义 InstrumentationKey。 如果你不确定密钥的位置,请再次参考注册说明。

private static TelemetryClient _telemetryClient;

// Add a constructor that accepts a telemetry client:
public HomeController(TelemetryClient telemetry)
{
    _telemetryClient = telemetry;
}

使用 JavaScript

当前代码片段(下面列出的)为版本“5”,该版本在代码片段中编码为 sv:"#",还可在 GitHub 上找到当前版本

<script type="text/javascript">
!function(T,l,y){var S=T.location,k="script",D="instrumentationKey",C="ingestionendpoint",I="disableExceptionTracking",E="ai.device.",b="toLowerCase",w="crossOrigin",N="POST",e="appInsightsSDK",t=y.name||"appInsights";(y.name||T[e])&&(T[e]=t);var n=T[t]||function(d){var g=!1,f=!1,m={initialize:!0,queue:[],sv:"5",version:2,config:d};function v(e,t){var n={},a="Browser";return n[E+"id"]=a[b](),n[E+"type"]=a,n["ai.operation.name"]=S&&S.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(m.sv||m.version),{time:function(){var e=new Date;function t(e){var t=""+e;return 1===t.length&&(t="0"+t),t}return e.getUTCFullYear()+"-"+t(1+e.getUTCMonth())+"-"+t(e.getUTCDate())+"T"+t(e.getUTCHours())+":"+t(e.getUTCMinutes())+":"+t(e.getUTCSeconds())+"."+((e.getUTCMilliseconds()/1e3).toFixed(3)+"").slice(2,5)+"Z"}(),iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}}}}var h=d.url||y.src;if(h){function a(e){var t,n,a,i,r,o,s,c,u,p,l;g=!0,m.queue=[],f||(f=!0,t=h,s=function(){var e={},t=d.connectionString;if(t)for(var n=t.split(";"),a=0;a<n.length;a++){var i=n[a].split("=");2===i.length&&(e[i[0][b]()]=i[1])}if(!e[C]){var r=e.endpointsuffix,o=r?e.location:null;e[C]="https://"+(o?o+".":"")+"dc."+(r||"services.visualstudio.com")}return e}(),c=s[D]||d[D]||"",u=s[C],p=u?u+"/v2/track":d.endpointUrl,(l=[]).push((n="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",a=t,i=p,(o=(r=v(c,"Exception")).data).baseType="ExceptionData",o.baseData.exceptions=[{typeName:"SDKLoadFailed",message:n.replace(/\./g,"-"),hasFullStack:!1,stack:n+"\nSnippet failed to load ["+a+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(S&&S.pathname||"_unknown_")+"\nEndpoint: "+i,parsedStack:[]}],r)),l.push(function(e,t,n,a){var i=v(c,"Message"),r=i.data;r.baseType="MessageData";var o=r.baseData;return o.message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+n+")").replace(/\"/g,"")+'"',o.properties={endpoint:a},i}(0,0,t,p)),function(e,t){if(JSON){var n=T.fetch;if(n&&!y.useXhr)n(t,{method:N,body:JSON.stringify(e),mode:"cors"});else if(XMLHttpRequest){var a=new XMLHttpRequest;a.open(N,t),a.setRequestHeader("Content-type","application/json"),a.send(JSON.stringify(e))}}}(l,p))}function i(e,t){f||setTimeout(function(){!t&&m.core||a()},500)}var e=function(){var n=l.createElement(k);n.src=h;var e=y[w];return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=i,n.onerror=a,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||i(0,t)},n}();y.ld<0?l.getElementsByTagName("head")[0].appendChild(e):setTimeout(function(){l.getElementsByTagName(k)[0].parentNode.appendChild(e)},y.ld||0)}try{m.cookie=l.cookie}catch(p){}function t(e){for(;e.length;)!function(t){m[t]=function(){var e=arguments;g||m.queue.push(function(){m[t].apply(m,e)})}}(e.pop())}var n="track",r="TrackPage",o="TrackEvent";t([n+"Event",n+"PageView",n+"Exception",n+"Trace",n+"DependencyData",n+"Metric",n+"PageViewPerformance","start"+r,"stop"+r,"start"+o,"stop"+o,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),m.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4};var s=(d.extensionConfig||{}).ApplicationInsightsAnalytics||{};if(!0!==d[I]&&!0!==s[I]){var c="onerror";t(["_"+c]);var u=T[c];T[c]=function(e,t,n,a,i){var r=u&&u(e,t,n,a,i);return!0!==r&&m["_"+c]({message:e,url:t,lineNumber:n,columnNumber:a,error:i}),r},d.autoExceptionInstrumented=!0}return m}(y.cfg);function a(){y.onInit&&y.onInit(n)}(T[t]=n).queue&&0===n.queue.length?(n.queue.push(a),n.trackPageView({})):a()}(window,document,{
src: "https://js.monitor.azure.com/scripts/b/ai.2.min.js", // The SDK URL Source
// name: "appInsights", // Global SDK Instance name defaults to "appInsights" when not supplied
// ld: 0, // Defines the load delay (in ms) before attempting to load the sdk. -1 = block page load and add to head. (default) = 0ms load after timeout,
// useXhr: 1, // Use XHR instead of fetch to report failures (if available),
crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
// onInit: null, // Once the application insights instance has loaded and initialized this callback function will be called with 1 argument -- the sdk instance (DO NOT ADD anything to the sdk.queue -- As they won't get called)
cfg: { // Application Insights Configuration
    instrumentationKey: "<YOUR INSTRUMENTATION KEY>"
}});
</script>

注意

为了提高可读性并减少可能的 JavaScript 错误,在上述代码片段的新行上列出了所有可能的配置选项,如果不希望更改注释行的值,可以将其删除。

步骤 2:请求用于关联的搜索 ID

为了将搜索请求与单击相关联,必须具有一个将这两个不同事件关联起来的相关性 ID。 使用 HTTP 标头请求搜索 ID 时,Azure 认知搜索将提供该 ID。

使用搜索 ID 可将 Azure 认知搜索为请求本身发出的指标关联到在 Application Insights 中记录的自定义指标。

使用 C#(较新的 v11 SDK)

最新的 SDK 要求使用 HTTP 管道设置标头,如本示例中所述。

// Create a custom policy to add the correct headers
public class SearchIdPipelinePolicy : HttpPipelineSynchronousPolicy
{
    public override void OnSendingRequest(HttpMessage message)
    {
        message.Request.Headers.SetValue("x-ms-azs-return-searchid", "true");
    }
}
// This sample uses the .NET SDK https://www.nuget.org/packages/Azure.Search.Documents

SearchClientOptions clientOptions = new SearchClientOptions();
clientOptions.AddPolicy(new SearchIdPipelinePolicy(), HttpPipelinePosition.PerCall);

var client = new SearchClient("<SearchServiceName>", "<IndexName>", new AzureKeyCredential("<QueryKey>"), options: clientOptions);

Response<SearchResults<SearchDocument>> response = await client.SearchAsync<SearchDocument>(searchText: searchText, searchOptions: options);
string searchId = string.Empty;
if (response.GetRawResponse().Headers.TryGetValues("x-ms-azs-searchid", out IEnumerable<string> headerValues))
{
    searchId = headerValues.FirstOrDefault();
}

使用 C#(较旧的 v10 SDK)

// This sample uses the .NET SDK https://www.nuget.org/packages/Microsoft.Azure.Search

var client = new SearchIndexClient(<SearchServiceName>, <IndexName>, new SearchCredentials(<QueryKey>));

// Use HTTP headers so that you can get the search ID from the response
var headers = new Dictionary<string, List<string>>() { { "x-ms-azs-return-searchid", new List<string>() { "true" } } };
var response = await client.Documents.SearchWithHttpMessagesAsync(searchText: searchText, searchParameters: parameters, customHeaders: headers);
string searchId = string.Empty;
if (response.Response.Headers.TryGetValues("x-ms-azs-searchid", out IEnumerable<string> headerValues))
{
    searchId = headerValues.FirstOrDefault();
}

使用 JavaScript(调用 REST API)

request.setRequestHeader("x-ms-azs-return-searchid", "true");
request.setRequestHeader("Access-Control-Expose-Headers", "x-ms-azs-searchid");
var searchId = request.getResponseHeader('x-ms-azs-searchid');

步骤 3:记录搜索事件

每当用户发出搜索请求时,应在 Application Insights 自定义事件上使用以下架构,将该请求记录为一个搜索事件。 请记得仅记录用户生成的搜索查询。

  • SearchServiceName:(字符串)搜索服务名称
  • SearchId:(GUID) 搜索查询的唯一标识符(位于搜索响应中)
  • IndexName:(字符串)要查询的搜索服务索引
  • QueryTerms:(字符串)用户输入的搜索字词
  • ResultCount:(整数)返回的文档数(位于搜索响应中)
  • ScoringProfile:(字符串)使用的评分配置文件的名称(如果有)

注意

通过向搜索查询添加 $count=true 来请求用户生成的查询的计数。 有关详细信息,请参阅搜索文档 (REST)

使用 C#

var properties = new Dictionary <string, string> 
{
    {"SearchServiceName", <service name>},
    {"SearchId", <search Id>},
    {"IndexName", <index name>},
    {"QueryTerms", <search terms>},
    {"ResultCount", <results count>},
    {"ScoringProfile", <scoring profile used>}
};
_telemetryClient.TrackEvent("Search", properties);

使用 JavaScript

appInsights.trackEvent("Search", {
  SearchServiceName: <service name>,
  SearchId: <search id>,
  IndexName: <index name>,
  QueryTerms: <search terms>,
  ResultCount: <results count>,
  ScoringProfile: <scoring profile used>
});

步骤 4:记录单击事件

每次用户单击文档,都是一个必须记录以用于搜索分析的信号。 使用 Application Insights 自定义事件可利用下面的架构来记录这些事件:

  • ServiceName:(字符串)搜索服务名称
  • SearchId:(GUID) 相关搜索查询的唯一标识符
  • DocId:(字符串)文档标识符
  • Position:(整数)文档在搜索结果页中的排名

注意

Position 指的是应用程序中的基数顺序。 可以随意设置此数字以用于比较,只要它始终相同。

使用 C#

var properties = new Dictionary <string, string> 
{
    {"SearchServiceName", <service name>},
    {"SearchId", <search id>},
    {"ClickedDocId", <clicked document id>},
    {"Rank", <clicked document position>}
};
_telemetryClient.TrackEvent("Click", properties);

使用 JavaScript

appInsights.trackEvent("Click", {
    SearchServiceName: <service name>,
    SearchId: <search id>,
    ClickedDocId: <clicked document id>,
    Rank: <clicked document position>
});

3 - 在 Power BI 中进行分析

检测到应用并确认应用程序已正确连接到 Application Insights 后,下载一个预定义的报表模板以在 Power BI Desktop 中分析数据。 该报告包含预定义的图表和表,它们可用于分析为搜索流量分析捕获的其他数据。

  1. 在 Azure 认知搜索仪表板左侧导航窗格中,在“设置”下,单击“搜索流量分析”。

  2. 在“搜索流量分析”页面上,在步骤 3 中,单击“获取 Power BI Desktop”以安装 Power BI。

    获取 Power BI 报表

  3. 在同一页面上,单击“下载 Power BI 报表”。

  4. 该报表将在 Power BI Desktop 中打开,并且会提示你连接到 Application Insights 并提供凭据。 可以在你的 Application Insights 资源的 Azure 门户页面中找到连接信息。 对于凭据,请提供用于门户登录的相同用户名和密码。

    连接 Application Insights

  5. 单击“加载”。

该报表包含图表和表,可帮助你做出更明智的决策来提高搜索性能和相关性。

指标包括以下各项:

  • 搜索量和最常用术语-文档对:导致同一文档被单击的词,按单击次数排序。
  • 无单击的搜索:查询次数最多但未记录任何单击的词

以下屏幕截图显示了使用了所有架构元素的内置报表的外观。

Power BI dashboard for Azure Cognitive Search适用于 azure 的 azure 认知搜索认知搜索

后续步骤

检测搜索应用程序,以获取提供深入见解的有关搜索服务的强大数据。

你可以查找有关 Application Insights 的更多信息并访问定价页面来详细了解其各种服务层级。

了解有关创建出色报告的详细信息。 有关详细信息,请参阅 Power BI Desktop 入门