Monday, January 13, 2014

Paging eDiscovery results with the EWS Managed API in Exchange 2013

eDiscovery is one of the new features in Exchange 2013 aimed at both improving the search experience and also helping deal with Big Data in a mailbox or mailboxes (which is kind of like the Sun in that its just keeps getting bigger and will one day consume us all).

With eDiscovery in EWS you can perform two types of searches a

  • Estimate Query - Which will return information about the number of hits for a particular KQL query.
  • Preview Query - Will return your query hits as PreviewItems which you can then use to show more information about each hit.

If your doing a Preview Query with a very generic search predicate that is going to be returning many preview Items and because of the size of these results they will returned as separate paged results sets. This will mean you will need to make multiple search requests to navigate thought the result set pages.

To tell the server you want the next Page in the Results set you need to use the PageItemReference  . The PageItemReference value needs to be set to the SortValue of the Last Preview-item returned by the previous page.

I've put together a couple of Managed API samples for this one is a C# example and the other is a Template Powershell script you can use to do a eDiscovery on one mailbox. These sample pages items back in lots of 100, you can adjust this value but I wouldn't go much over 1000.  I've put a download of the code here, the script looks like.

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4. $KQL = "Subject:test";  
  5. $SearchableMailboxString = $MailboxName;  
  6.   
  7. ## Load Managed API dll    
  8. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"    
  9.     
  10. ## Set Exchange Version    
  11. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013    
  12.     
  13. ## Create Exchange Service Object    
  14. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  15.     
  16. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  17.     
  18. #Credentials Option 1 using UPN for the windows Account    
  19. $psCred = Get-Credential    
  20. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  21. $service.Credentials = $creds        
  22.     
  23. #Credentials Option 2    
  24. #service.UseDefaultCredentials = $true    
  25.     
  26. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  27.     
  28. ## Code From http://poshcode.org/624  
  29. ## Create a compilation environment  
  30. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  31. $Compiler=$Provider.CreateCompiler()  
  32. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  33. $Params.GenerateExecutable=$False  
  34. $Params.GenerateInMemory=$True  
  35. $Params.IncludeDebugInformation=$False  
  36. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  37.   
  38. $TASource=@' 
  39.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  40.     public class TrustAll : System.Net.ICertificatePolicy { 
  41.       public TrustAll() {  
  42.       } 
  43.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  44.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  45.         System.Net.WebRequest req, int problem) { 
  46.         return true; 
  47.       } 
  48.     } 
  49.   } 
  50. '@   
  51. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  52. $TAAssembly=$TAResults.CompiledAssembly  
  53.   
  54. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  55. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  56. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  57.   
  58. ## end code from http://poshcode.org/624  
  59.     
  60. ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use    
  61.     
  62. #CAS URL Option 1 Autodiscover    
  63. $service.AutodiscoverUrl($MailboxName,{$true})    
  64. "Using CAS Server : " + $Service.url     
  65.      
  66. #CAS URL Option 2 Hardcoded    
  67.     
  68. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  69. #$service.Url = $uri      
  70.     
  71. ## Optional section for Exchange Impersonation    
  72.     
  73. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  74.   
  75.   
  76. $gsMBResponse = $service.GetSearchableMailboxes($SearchableMailboxString$false);  
  77. $gsMBResponse  
  78. $msbScope = New-Object  Microsoft.Exchange.WebServices.Data.MailboxSearchScope[] $gsMBResponse.SearchableMailboxes.Length  
  79. $mbCount = 0;  
  80. foreach ($sbMailbox in $gsMBResponse.SearchableMailboxes)  
  81. {  
  82.     $msbScope[$mbCount] = New-Object Microsoft.Exchange.WebServices.Data.MailboxSearchScope($sbMailbox.ReferenceId, [Microsoft.Exchange.WebServices.Data.MailboxSearchLocation]::All);  
  83.     $mbCount++;  
  84. }  
  85. $smSearchMailbox = New-Object Microsoft.Exchange.WebServices.Data.SearchMailboxesParameters  
  86. $mbq =  New-Object Microsoft.Exchange.WebServices.Data.MailboxQuery($KQL$msbScope);  
  87. $mbqa = New-Object Microsoft.Exchange.WebServices.Data.MailboxQuery[] 1  
  88. $mbqa[0] = $mbq  
  89. $smSearchMailbox.SearchQueries = $mbqa;  
  90. $smSearchMailbox.PageSize = 100;  
  91. $smSearchMailbox.PageDirection = [Microsoft.Exchange.WebServices.Data.SearchPageDirection]::Next;  
  92. $smSearchMailbox.PerformDeduplication = $false;             
  93. $smSearchMailbox.ResultType = [Microsoft.Exchange.WebServices.Data.SearchResultType]::PreviewOnly;  
  94. $srCol = $service.SearchMailboxes($smSearchMailbox);  
  95.   
  96. if ($srCol[0].Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success)  
  97. {  
  98.     if ($srCol[0].SearchResult.ItemCount -gt 0)  
  99.     {                    
  100.         do  
  101.         {  
  102.             $smSearchMailbox.PageItemReference = $srCol[0].SearchResult.PreviewItems[$srCol[0].SearchResult.PreviewItems.Length - 1].SortValue;  
  103.             foreach ($PvItem in $srCol[0].SearchResult.PreviewItems) {  
  104.                 Write-Host ($PvItem.Subject);  
  105.             }                          
  106.             $srCol = $service.SearchMailboxes($smSearchMailbox);  
  107.             Write-Host("Items Remaining : " + $srCol[0].SearchResult.ItemCount);  
  108.         } while ($srCol[0].SearchResult.ItemCount-gt 0 );  
  109.           
  110.     }  
  111.       
  112. }  


1 comment:

Anonymous said...

How do you specify a page item reference that will take you to the last page?

I'm asking because SortBy doesn't work on the full result set -- only per page. And I don't want to start with the first page, I want to start with the last page, and then go back (using PageDirection 'Previous').