Monday, March 20, 2017

Problem with outdated value in ModifiedBy search managed property after copying the document

Recently we faced with interesting problem: there is functionality which copies documents from another site collection in Sharepoint Online via SP.RequestExecutor:

   1: var sourceExecutor = new SP.RequestExecutor(sourceSiteUrl);
   2: var targetExecutor = new SP.RequestExecutor(targetSiteUrl);
   3:  
   4: // Get form digest of target site collection
   5: jQuery.ajax({
   6:     url: targetSiteUrl + "/_api/contextinfo",
   7:     type: "POST",
   8:     headers: {
   9:         "Accept": "application/json;odata=verbose"
  10:     },
  11:     success: function (data) {
  12:         try {
  13:             var digest = data.d.GetContextWebInformation.FormDigestValue;
  14:  
  15:             // Build executor action to retrieve the file data.
  16:             var getFileAction = {
  17:                 url: sourceSiteUrl + "/_api/web/GetFileByServerRelativeUrl('" +
  18:                     sourceServerRelativeUrl + "')/$value",
  19:                 method: "GET",
  20:                 binaryStringResponseBody: true,
  21:                 success: function (getFileData) {
  22:                     // Get the binary data.
  23:                     var result = data.body;
  24:                     // Build executor action to copy the file data to the new location.
  25:                     var copyFileAction = {
  26:                         url: targetSiteUrl + "/_api/web/GetFolderByServerRelativeUrl('" +
  27:                             targetFolder.get_serverRelativeUrl() + "')/Files/Add(url='" +
  28:                             fileNameFromInput + "', overwrite=true)",
  29:                         method: "POST",
  30:                         headers: {
  31:                             "Accept": "application/json; odata=verbose",
  32:                             "X-RequestDigest": digest
  33:                         },
  34:                         contentType: "application/json;odata=verbose",
  35:                         binaryStringRequestBody: true,
  36:                         body: getFileData.body,
  37:                         success: function (copyFileData) {
  38:                             ...
  39:                         },
  40:                         error: function (ex) {
  41:                             ...
  42:                         }
  43:                     };
  44:  
  45:                     targetExecutor.executeAsync(copyFileAction);
  46:                 },
  47:                 error: function (ex) {
  48:                     ...
  49:                 }
  50:             };
  51:             sourceExecutor.executeAsync(getFileAction);
  52:         } catch (e) {
  53:             ...
  54:         }
  55:     },
  56:     error: function (ex) {
  57:         ...
  58:     }
  59: });

After file is successfully copied Modified By field shown in list views displays correct value – it shows account of the user which copied the document (i.e. under which account code above was executed). However when we tried to get the value of ModifiedBy managed properly from search index we got value which original file had in the source site collection before it was copied to target site collection (i.e. person which last modified the document in the source site collection). Recrawling of the list didn’t help. Also we tried to explicitly set value of Editor field in document metadata after file has been copied and it didn’t help either.

In order to avoid this issue the following workaround was used: at first as before we get list of the files from search index using javascript object model and then when we get last n results sorted in correct way for displaying in web part we make n requests to content database in order to get correct value of ModifiedBy field (remember that in list views correct account is shown in this field and list views show data from content database):

   1: var documentsItems = [];
   2: var ctx = SP.ClientContext.get_current();
   3: for (var i in documentsFromSearch) {
   4:     var item = documentsFromSearch[i];
   5:     var path = item.Path;
   6:  
   7:     var relativeUrl = _spPageContextInfo.webServerRelativeUrl +
   8:         path.replace(_spPageContextInfo.siteAbsoluteUrl, "");
   9:  
  10:     documentsItems.push({
  11:         RelativeURL: relativeUrl,
  12:         File: null,
  13:         // item["ModifiedBy"] from search contains incorrect  value
  14:         ModifiedBy: item["ModifiedBy"]
  15:     });
  16: }
  17:  
  18: for (var i in documentsItems) {
  19:     var doc = documentsItems[i];
  20:     doc.File = ctx.get_web().getFileByServerRelativeUrl(doc.RelativeURL);
  21:     ctx.load(doc.File, "ListItemAllFields");
  22: }
  23:  
  24: ctx.executeQueryAsync(
  25:     function () {
  26:         for (var i in documentsItems) {
  27:             var doc = documentsItems[i];
  28:             if (typeof (doc.File) == "undefined" || doc.File == null) {
  29:                 continue;
  30:             }
  31:             var item = doc.File.get_listItemAllFields();
  32:             if (typeof (item) == "undefined" || item == null) {
  33:                 continue;
  34:             }
  35:             doc.ModifiedBy = item.get_item("Editor").$5E_1;
  36:         }
  37:     },
  38:     function (sender, args) {
  39:         ...
  40:     });

In this code we at first create array of objects from results returned from search index (lines 3-16) and then for each object we get it’s File object and from it’s list item read Editor value (lines 18-40). Note that for getting editor’s display name here we used undocumented property $5E_1 of SP.FieldUserValue – more proper way is to get user’s id from it’s get_lookupId() method and load it in separate request from web’s user collection, so please be aware of that. Of course this approach is slower than original because it makes n requests to content database, but if data is loaded asynchronously and number of displayed files is not big, performance will remain acceptable and web part will display correct value in ModifiedBy column. Hope that this information will help someone.

No comments:

Post a Comment