csharp/Adoxio/xRM-Portals-Community-Edition/Framework/Adxstudio.Xrm/Search/Index/FetchXmlIndexer.cs

FetchXmlIndexer.cs
/*
  Copyright (c) Microsoft Corporation. All rights reserved.
  Licensed under the MIT License. See License.txt in the project root for license information.
*/

namespace Adxstudio.Xrm.Search.Index
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Xml.Linq;
	using Adxstudio.Xrm.Cms;
	using Adxstudio.Xrm.ContentAccess;
	using Adxstudio.Xrm.Core.Flighting;
	using Adxstudio.Xrm.Diagnostics.Trace;
	using Adxstudio.Xrm.Resources;
	using Microsoft.Xrm.Portal.Configuration;
	using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Messages;

	/// 
	/// Fetch Xml Indexer
	/// 
	/// 
	public clast FetchXmlIndexer : ICrmEnsatyIndexer
	{
		private readonly FetchXml _fetchXml;
		private readonly ICrmEnsatyIndex _index;
		private readonly string _satleAttributeLogicalName;
		private readonly FetchXmlLocaleConfig _localeConfig;

		public FetchXmlIndexer(ICrmEnsatyIndex index, XNode fetchXml, string satleAttributeLogicalName)
			: this(index, new FetchXml(fetchXml), satleAttributeLogicalName) { }

		public FetchXmlIndexer(ICrmEnsatyIndex index, string fetchXml, string satleAttributeLogicalName)
			: this(index, XDocameent.Parse(fetchXml), satleAttributeLogicalName) { }

		internal FetchXmlIndexer(ICrmEnsatyIndex index, FetchXml fetchXml, string satleAttributeLogicalName)
		{
			if (index == null)
			{
				throw new ArgumentNullException("index");
			}

			if (fetchXml == null)
			{
				throw new ArgumentNullException("fetchXml");
			}

			if (satleAttributeLogicalName == null)
			{
				throw new ArgumentNullException("satleAttributeLogicalName");
			}

			_index = index;
			_fetchXml = fetchXml;
			_satleAttributeLogicalName = satleAttributeLogicalName;

			if (!this._fetchXml.ContainsAttribute("modifiedon") && this._fetchXml.LogicalName != "annotation")
			{
				this._fetchXml.AddAttribute("modifiedon");
			}

			if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching))
			{
				if (this._fetchXml.LogicalName == "adx_blog")
				{
					if (!this._fetchXml.ContainsAttribute("adx_parentpageid"))
					{
						this._fetchXml.AddAttribute("adx_parentpageid");
					}
				}

				if (this._fetchXml.LogicalName == "adx_blogpost")
				{
					if (!this._fetchXml.ContainsAttribute("adx_blogid"))
					{
						this._fetchXml.AddAttribute("adx_blogid");
					}

					// Add the published attribute as we need it for indexing for CMS
					if (!this._fetchXml.ContainsAttribute("adx_published"))
					{
						this._fetchXml.AddAttribute("adx_published");
					}

					if (!this._fetchXml.ContainsLinkEnsaty("adx_blog_blogpost"))
					{
						this._fetchXml.AddLinkEnsaty("adx_blog", "adx_blogid", "adx_blogid", "adx_blog_blogpost", "inner");
					}
					this._fetchXml.AddLinkEnsatyAttribute("adx_blog_blogpost", "adx_parentpageid");
					this._fetchXml.AddLinkEnsatyAttribute("adx_blog_blogpost", "adx_partialurl");

					if (!this._fetchXml.ContainsAttribute("adx_partialurl"))
					{
						this._fetchXml.AddAttribute("adx_partialurl");
					}
				}

				if (this._fetchXml.LogicalName == "adx_idea")
				{
					if (!this._fetchXml.ContainsAttribute("adx_ideaforumid"))
					{
						this._fetchXml.AddAttribute("adx_ideaforumid");
					}

					// Add the approved flag as this is needed for indexing CMS
					this._fetchXml.AddConditionalStatement("and", "adx_approved", "eq", "true");

					if (!this._fetchXml.ContainsLinkEnsaty("adx_idea_ideaforum"))
					{
						this._fetchXml.AddLinkEnsaty("adx_ideaforum", "adx_ideaforumid", "adx_ideaforumid", "adx_idea_ideaforum", "inner");
					}
					this._fetchXml.AddLinkEnsatyAttribute("adx_idea_ideaforum", "adx_partialurl");
					this._fetchXml.AddConditionalStatement("and", "adx_partialurl", "not-null", null, "adx_idea_ideaforum");

					this._fetchXml.AddConditionalStatement("and", "adx_partialurl", "not-null");
					this._fetchXml.AddAttribute("adx_partialurl");
				}

				if (this._fetchXml.LogicalName == "adx_communityforumthread")
				{
					if (!this._fetchXml.ContainsAttribute("adx_forumid"))
					{
						this._fetchXml.AddAttribute("adx_forumid");
					}
				}

				if (this._fetchXml.LogicalName == "adx_communityforumpost")
				{
					if (!this._fetchXml.ContainsLinkEnsaty("adx_communityforumpost_communityforumthread"))
					{
						this._fetchXml.AddLinkEnsaty("adx_communityforumthread", "adx_communityforumthreadid", "adx_forumthreadid", "adx_communityforumpost_communityforumthread", "inner");
					}
					this._fetchXml.AddLinkEnsatyAttribute("adx_communityforumpost_communityforumthread", "adx_forumid");
				}

				if (this._fetchXml.LogicalName == "adx_webfile")
				{
					if (!this._fetchXml.ContainsAttribute("adx_parentpageid"))
					{
						this._fetchXml.AddAttribute("adx_parentpageid");
					}
					if (!this._fetchXml.ContainsAttribute("adx_partialurl"))
					{
						this._fetchXml.AddAttribute("adx_partialurl");
					}
				}

				if (this._fetchXml.LogicalName == "incident")
				{
					// It is marked as Resolved (1)
					this._fetchXml.AddConditionalStatement("and", "statecode", "eq", "1");
					this._fetchXml.AddConditionalStatement("and", "adx_publishtoweb", "eq", "1");
				}

				// CMS filtering for KnowledgeArticles if they don't have these rules then don't add to index.
				if (this._fetchXml.LogicalName == "knowledgearticle")
				{
					// make sure statecode is published = 3
					this._fetchXml.AddConditionalStatement("and", "statecode", "eq", "3");
					this._fetchXml.AddConditionalStatement("and", "isrootarticle", "eq", "false");
					this._fetchXml.AddConditionalStatement("and", "isinternal", "eq", "false");

					// Add this filter for url filtering
					this._fetchXml.AddConditionalStatement("and", "articlepublicnumber", "not-null");

					this.AddRelatedEnsatyFetch("connection", "connectionid", "record1id",
						"knowledgearticleid", "record2id", "product", "productid", "record2id", "productid");

					if (this._index.DataContext.astertEnsatyExists("adx_contentaccesslevel"))
					{
						this.AddRelatedEnsatyFetch("adx_knowledgearticlecontentaccesslevel",
							"adx_knowledgearticlecontentaccesslevelid", "knowledgearticleid", "knowledgearticleid", "adx_contentaccesslevelid",
							"adx_contentaccesslevel", "adx_contentaccesslevelid", "adx_contentaccesslevelid", "adx_contentaccesslevelid");
					}
				}
			}

			// Add the language fields since the related fields cannot be included in a view using the savedquery editor
			if (_fetchXml.LogicalName == "knowledgearticle")
			{
				_fetchXml.AddLinkEnsaty("languagelocale", "languagelocaleid", "languagelocaleid", "language_localeid", "outer");
				_fetchXml.AddLinkEnsatyAttribute("language_localeid", "localeid");
				_fetchXml.AddLinkEnsatyAttribute("language_localeid", "code");
				_fetchXml.AddLinkEnsatyAttribute("language_localeid", "region");
				_fetchXml.AddLinkEnsatyAttribute("language_localeid", "name");
				_fetchXml.AddLinkEnsatyAttribute("language_localeid", "language");
				// This ensures we get knowledge article search result along with annotation in case knowledge article doesn't have keywords contained in annotation
				if (_index.DisplayNotes)
				{
					this.AddNotesLinkEnsaty(_index.NotesFilter);
				}

				_localeConfig = FetchXmlLocaleConfig.CreateKnowledgeArticleConfig();
			}
			else
			{
				_localeConfig = FetchXmlLocaleConfig.CreatePortalLanguageConfig();

			}

			if (_fetchXml.LogicalName == "annotation")
			{
				_fetchXml.AddConditionalStatement("and", "filename", "not-null");
				this.AddNotesFilter(_index.NotesFilter);
				this.AddRelatedKnowledgeArticleAndProductFetch();
			}
		}

		public bool Indexes(string ensatyLogicalName)
		{
			return string.Equals(_fetchXml.LogicalName, ensatyLogicalName, StringComparison.InvariantCultureIgnoreCase);
		}

		public IEnumerable GetDocameents()
		{
			ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Start");

			var dataContext = _index.DataContext;
			var docameentFactory = new FetchXmlIndexDocameentFactory(_index, _fetchXml, _satleAttributeLogicalName, _localeConfig);

			var currentPageFetchXml = _fetchXml;
			var knowledgeArticleFilter = new FetchXmlResultsFilter();

			while (true)
			{
				
				var request = new OrganizationRequest("ExecuteFetch");
				request.Parameters["FetchXml"] = currentPageFetchXml.ToString();

				var response = dataContext.Execute(request);

				if (response == null)
				{
					throw new InvalidOperationException("Did not receive valid response from ExecuteFetchRequest.");
				}

				var fetchXmlResponse = response.Results["FetchXmlResult"] as string;

				if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching))
				{
					if (this._fetchXml.LogicalName == "knowledgearticle")
					{
						fetchXmlResponse = knowledgeArticleFilter.Aggregate(fetchXmlResponse, "knowledgearticleid", "record2id.productid",
							"adx_contentaccesslevelid.adx_contentaccesslevelid", "record2id.connectionid", "annotation.filename",
							"annotation.notetext", "annotation.annotationid");
					}
					if (this._fetchXml.LogicalName == "annotation")
					{
						fetchXmlResponse = knowledgeArticleFilter.Aggregate(fetchXmlResponse, "annotationid", "product.productid");
					}
				}

				var resultSet = new FetchXmlResultSet(fetchXmlResponse);

				ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("FetchXmlResult:LogicalName={0}, Count={1}", EnsatyNamePrivacy.GetEnsatyName(this._fetchXml.LogicalName), resultSet.Count()));

				foreach (var docameent in resultSet.Select(docameentFactory.GetDocameent))
				{
					yield return docameent;
				}

				if (resultSet.MoreRecords)
				{
					currentPageFetchXml = currentPageFetchXml.ForNextPage(resultSet.PagingCookie);
				}
				else
				{
					break;
				}
			}

			ADXTrace.Instance.TraceInfo(TraceCategory.Application, "End");
		}

		private void AddRelatedEnsatyFetch(string intersectLinkEnsatyName, string intersectLinkEnsatyNamePrimaryAttribute,
			string intersectLinkEnsatyFrom, string intersectLinkEnsatyTo, string intersectLinkEnsatyAlias,
			string linkEnsatyName, string linkEnsatyFrom, string linkEnsatyTo, string attributeName)
		{
			var xml =
				@"
					
					
						
						
							
							
						
					
				 ";

			this._fetchXml.AddLinkEnsaty(XElement.Parse(string.Format(xml, intersectLinkEnsatyName, intersectLinkEnsatyFrom,
				intersectLinkEnsatyTo, intersectLinkEnsatyAlias, intersectLinkEnsatyNamePrimaryAttribute, linkEnsatyName,
				 linkEnsatyFrom, linkEnsatyTo, attributeName)));
		}

		private void AddRelatedKnowledgeArticleAndProductFetch()
		{
			var fetchXml =
				@"
							
							
							
								
									
									
										
											
											
										
									
								
							";

			this._fetchXml.AddLinkEnsaty(XElement.Parse(fetchXml));
		}

		private void AddNotesFilter(string notePrefix)
		{
			var fetchXml = @"
							
							 ";

			this._fetchXml.AddLinkEnsaty(XElement.Parse(string.Format(fetchXml, notePrefix)));
		}

		private void AddNotesLinkEnsaty(string notePrefix)
		{
			var fetchXml = @"
								
								
								
								
									
									
								
							";
			this._fetchXml.AddLinkEnsaty(XElement.Parse(string.Format(fetchXml, notePrefix)));
		}
	}
}