在 Exchange 中使用 EWS 执行分组搜索

了解如何在面向 Exchange 的 EWS 托管 API 或 EWS 应用程序中执行分组搜索。

分组搜索非常有用,因为它们使你能够控制搜索结果的组织方式。 有条理的搜索结果可以让应用程序更轻松地处理结果,或者以可管理的方式向最终用户显示结果。

分组的工作原理是将结果集中具有相同特定字段值的所有项放入组中。 例如,可以按发件人对结果进行分组,来自同一人员的所有项目将位于单独的组中,并且每个组中的项目将根据你在视图上指定的顺序进行排序。 组本身根据所选字段按聚合值排序。

表 1. 用于组织搜索结果的 EWS 托管 API 方法和 EWS 操作

若要… 在 EWS 托管 API 中,使用… 在 EWS 中,使用…
将结果中特定属性中具有相同值的项整理到组中
Grouping.GroupOn
FieldURI 元素作为 GroupBy 元素的子元素
按特定属性中的值对每个组中的项目进行排序
ItemView.OrderBy
SortOrder 元素
对组进行排序
Grouping.AggregateOn

Grouping.AggregateType

Grouping.SortDirection
FieldURI 元素作为 AggregateOn 元素的子元素

AggregateOn 元素上的 Aggregate 属性

GroupBy 元素上的 Order 属性

让我们一步一步地进行。

按特定属性对结果进行分组

使用分组的第一步是选择要分组依据的 Exchange 存储中项的属性或属性。 EWS 托管 API 将这些属性公开为相应类的类属性,而 EWS 将它们公开为 XML 元素。 可以选择任何属性,包括自定义或扩展属性,但了解如何根据所选属性的值对项进行分组会很有帮助。

选择分组依据的属性中具有相同值的所有项都将分组在一起。 这看似显而易见,但这是一个重要细节。 如果按日期/时间属性(例如 EWS 托管 API 中的 Item.DateTimeReceived )或 EWS 中的 DateTimeReceived 元素进行分组,请考虑会发生什么情况。 目的可能是将结果组织成组,每个组包含同一天的项。 但是,分组将查看整个值,其中包括时间。

最终结果是将项目分组,以便同时收到的项(到第二个)位于其自己的组中。 结果很可能被排序到大量组中,每个组中的项数较少。

若要获取具有较少组数和每个组中更多项的结果集,请选择一个可能具有较少值的属性,例如 EWS 托管 API 中的 EmailMessage.FromItem.Categories ,或者 EWS 中的 FromCategories 。 下图显示了“收件箱”中显示的电子邮件列表。

图 1. 收件箱中的邮件

用户收件箱中的示例邮件列表。

如果按 EmailMessage.From 属性对图 1 中的项目进行分组,则结果将是两个组,一组用于 Hope Gross 发送的邮件,另一组用于 Sadie Daniels 发送的邮件。

图 2. 基于 From 属性划分为组的消息

此图显示按照发件人属性分为两个列表的邮件。

对组中的项目进行排序

可以使用 EWS 托管 API 中的 ItemView.OrderBy 属性或 EWS 中的 SortOrder 元素来控制每个组中项的排序方式。 相同的排序适用于每个组。 例如,如果按 Item.DateTimeReceived 属性对图 1 中的项目进行排序,则按降序排列,最近从 Hope Gross 收到的项将排在希望总值组中,最近从 Sadie Daniels 收到的项将在 Sadie Daniels 组中排在第一位。 图 2 中的组已按此方式进行了排序,这很方便。

对组进行排序

确定组后,最后一步是对组本身进行排序。 由于组本身没有特定值,因此分组过程必须为每个组分配一个排序值。 这是通过聚合每个组中特定属性的值来完成的,这些值由 EWS 托管 API 中的 Grouping.AggregateOn 属性指定,或者 将 FieldURI 元素指定为 EWS 中 AggregateOn 元素的子元素。 EWS 托管 API (中的 Grouping.AggregateType 属性或 EWS 中 AggregateOn 元素上的 Aggregate 属性) 指定将每个组中项中的哪个值分配给组的排序值(最大值或最小值)。 最后, (降序或升序) 排序顺序由 EWS 托管 API 中的 Grouping.SortDirection 属性或 EWS 中的 GroupBy 元素上的 Order 属性指定。

例如,如果图 2 中的组按 聚合 Item.DateTimeReceived 属性(使用最小值)和降序排序,则按图 3 所示的顺序返回项。

图 3. 使用按 DateTimeReceived 属性排序的组的分组搜索结果

此图显示邮件的已排序列表,按照发件人属性分组,各组按照接收的最小日期/时间排序。

下一部分介绍如何在代码中拉取分组和排序。

示例:使用 EWS 托管 API 执行分组搜索

以下 EWS 托管 API 方法可以使用分组:

以下示例使用 ExchangeService.FindItems 方法;但是,相同的规则和概念适用于 Folder.FindItems 方法。 在此示例中,定义了一个名为 GroupItemsByFrom 的方法。 它采用 ExchangeService 对象和 WellKnownFolderName 对象作为参数。 它请求文件夹中的前 50 个项目,按 EmailMessage.From 属性分组,按 Item.DateTimeReceived 属性降序排序。 组本身按其项上最小的 Item.DateTimeReceived 属性值按降序排序。

此示例假定 ExchangeService 对象已使用凭据的有效值和 Url 属性进行了初始化。

static void GroupItemsByFrom(ExchangeService service, WellKnownFolderName folder)
{
    // Limit the result set to 50 items.
    ItemView view = new ItemView(50);
    view.PropertySet = new PropertySet(ItemSchema.Subject,
                                       ItemSchema.DateTimeReceived,
                                       EmailMessageSchema.From,
                                       ItemSchema.Categories);
    // Item searches do not support Deep traversal.
    view.Traversal = ItemTraversal.Shallow;
    // Specify the sorting done within the groups.
    view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
    // Configure grouping.
    Grouping groupByFrom = new Grouping();
    groupByFrom.GroupOn = EmailMessageSchema.From;
    groupByFrom.AggregateOn = ItemSchema.DateTimeReceived;
    groupByFrom.AggregateType = AggregateType.Minimum;
    groupByFrom.SortDirection = SortDirection.Descending;
    try
    {
        GroupedFindItemsResults<Item> results = service.FindItems(folder,
            view, groupByFrom);
        foreach (ItemGroup<Item> group in results.ItemGroups)
        {
            Console.WriteLine("Group: {0}", group.GroupIndex);
            foreach (Item item in group.Items)
            {
                if (item is EmailMessage)
                {
                    EmailMessage message = item as EmailMessage;
                    Console.WriteLine("From: {0}", message.From);
                    Console.WriteLine("Subject: {0}", message.Subject);
                    Console.WriteLine("Id: {0}\n", message.Id.ToString());
                }
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception while enumerating results: {0}", ex.Message);
    }
}

示例:使用 EWS 执行分组搜索

以下请求示例显示了文件夹中前 50 个项目的 FindItem 操作 请求,该请求按 From 元素分组,按 DateTimeReceived 元素按降序排序。 组本身按其项上的最小 DateTimeReceived 元素值按降序排序。

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" 
    xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" 
    xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2007_SP1" />
    <t:TimeZoneContext>
      <t:TimeZoneDefinition Id="Eastern Standard Time" />
    </t:TimeZoneContext>
  </soap:Header>
  <soap:Body>
    <m:FindItem Traversal="Shallow">
      <m:ItemShape>
        <t:BaseShape>IdOnly</t:BaseShape>
        <t:AdditionalProperties>
          <t:FieldURI FieldURI="item:Subject" />
          <t:FieldURI FieldURI="item:DateTimeReceived" />
          <t:FieldURI FieldURI="message:From" />
          <t:FieldURI FieldURI="item:Categories" />
        </t:AdditionalProperties>
      </m:ItemShape>
      <m:IndexedPageItemView MaxEntriesReturned="50" Offset="0" BasePoint="Beginning" />
      <m:GroupBy Order="Descending">
        <t:FieldURI FieldURI="message:From" />
        <t:AggregateOn Aggregate="Minimum">
          <t:FieldURI FieldURI="item:DateTimeReceived" />
        </t:AggregateOn>
      </m:GroupBy>
      <m:SortOrder>
        <t:FieldOrder Order="Descending">
          <t:FieldURI FieldURI="item:DateTimeReceived" />
        </t:FieldOrder>
      </m:SortOrder>
      <m:ParentFolderIds>
        <t:DistinguishedFolderId Id="inbox" />
      </m:ParentFolderIds>
    </m:FindItem>
  </soap:Body>
</soap:Envelope>

服务器返回以下响应。

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="15" MinorVersion="0" MajorBuildNumber="712" MinorBuildNumber="22" Version="V2_3" 
        xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" 
        xmlns="http://schemas.microsoft.com/exchange/services/2006/types" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:FindItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" 
      xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:FindItemResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:RootFolder IndexedPagingOffset="10" TotalItemsInView="8" IncludesLastItemInRange="true">
            <t:Groups>
              <t:GroupedItems>
                <t:GroupIndex>0</t:GroupIndex>
                <t:Items>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>Planning resources</t:Subject>
                    <t:DateTimeReceived>2013-12-10T17:41:05Z</t:DateTimeReceived>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Sadie Daniels</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=8D84A3F4CBB34D48838A3AECF99795C0-SADIE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>Timeline</t:Subject>
                    <t:DateTimeReceived>2013-12-10T17:40:37Z</t:DateTimeReceived>
                    <t:Categories>
                      <t:String>Project</t:String>
                    </t:Categories>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Sadie Daniels</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=8D84A3F4CBB34D48838A3AECF99795C0-SADIE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>For your perusal</t:Subject>
                    <t:DateTimeReceived>2013-11-20T21:51:16Z</t:DateTimeReceived>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Sadie Daniels</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=8D84A3F4CBB34D48838A3AECF99795C0-SADIE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>meeting notes</t:Subject>
                    <t:DateTimeReceived>2013-11-20T21:18:51Z</t:DateTimeReceived>
                    <t:Categories>
                      <t:String>Blue category</t:String>
                    </t:Categories>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Sadie Daniels</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=8D84A3F4CBB34D48838A3AECF99795C0-SADIE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>Meeting notes</t:Subject>
                    <t:DateTimeReceived>2013-11-20T21:18:51Z</t:DateTimeReceived>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Sadie Daniels</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=8D84A3F4CBB34D48838A3AECF99795C0-SADIE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                </t:Items>
              </t:GroupedItems>
              <t:GroupedItems>
                <t:GroupIndex>1</t:GroupIndex>
                <t:Items>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>Query</t:Subject>
                    <t:DateTimeReceived>2013-12-10T17:43:15Z</t:DateTimeReceived>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Hope Gross</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=9B55E4100C064D9D8C5F72FF36802ED3-HOPE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>Update</t:Subject>
                    <t:DateTimeReceived>2013-12-10T17:42:33Z</t:DateTimeReceived>
                    <t:Categories>
                      <t:String>Project</t:String>
                      <t:String>Blue category</t:String>
                    </t:Categories>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Hope Gross</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=9B55E4100C064D9D8C5F72FF36802ED3-HOPE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                  <t:Message>
                    <t:ItemId Id="AAMkAGM2..." ChangeKey="CQAAABYA..." />
                    <t:Subject>This cat is hilarious!</t:Subject>
                    <t:DateTimeReceived>2013-10-15T20:22:12Z</t:DateTimeReceived>
                    <t:From>
                      <t:Mailbox>
                        <t:Name>Hope Gross</t:Name>
                        <t:EmailAddress>/O=FIRST ORGANIZATION/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=9B55E4100C064D9D8C5F72FF36802ED3-HOPE</t:EmailAddress>
                        <t:RoutingType>EX</t:RoutingType>
                      </t:Mailbox>
                    </t:From>
                  </t:Message>
                </t:Items>
              </t:GroupedItems>
            </t:Groups>
          </m:RootFolder>
        </m:FindItemResponseMessage>
      </m:ResponseMessages>
    </m:FindItemResponse>
  </s:Body>
</s:Envelope>

版本差异

从主版本 15 开始,以内部版本 15.0.775.38 结尾的 Exchange 版本返回GroupedItemsType (GroupedItemsType) 的 Group 元素,以取代 SOAP 响应中的 GroupedItems 元素。 如果使用 EWS 托管 API,这将导致 GroupedFindItemsResults.ItemGroups 集合包含 0 个对象。 如果使用 EWS, Group 元素应作为 GroupedItems 元素处理。

从主要版本 15 开始的 Exchange 版本返回额外的 GroupGroupedItems 元素,并在 SOAP 响应中将 xsi:nil 属性设置为 true 。 如果使用 EWS 托管 API,则这些额外的元素将导致引发 ServiceXmlDeserializationException 。 如果使用 EWS,则应忽略这些额外的元素。

另请参阅