Bookmarklet: Advanced Find Outer Join a.k.a Not-In Condition

I had discussed a technique earlier (https://dreamingincrm.com/2015/05/31/performing-outer-join-in-advanced-find/) on how to use a plugin to intercept the RetrieveMultiple message and modify the fetchxml to facilitate outer join. The guys at Cobalt also have developed a full-fledged solution using the same technique called Intelligent Query

If you don’t want to use a plugin, because you are worried about performance or cannot install either of these solutions into an environment because of organisation policies, you can use this bookmarklet to do the outer join.

Code

var contentPanel = $('#crmContentPanel > iframe');
if (contentPanel && contentPanel.length > 0) {
	var targetFrame = contentPanel[0].contentWindow;
	targetFrame.ExecuteQuery();
	var xml = targetFrame.$get("FetchXml").value;
	var isFetchModified = false;

	var xmlDoc = $.parseXML(xml),
	$xml = $(xmlDoc),
	outer = $xml.find("condition[operator=null]");

	var mainFilter = $xml.has('entity > filter');

	if (mainFilter.length === 0) $xml.find('entity').append('<filter></filter>');
	
	$xml.find('link-entity').each(function (i, d) {
		var entity = $(d).attr('name');
		var entityAlias = $(d).attr('alias');
		var outerJoinCondition = $(d).find("condition[operator=null]");
		if (entity + 'id' === outerJoinCondition.attr('attribute')) {
			$(d).attr('link-type', 'outer');
			$xml.find('entity > filter').append('<condition entityname="' + entityAlias + '" attribute="' + entity + 'id" operator="null" />');
			if (!isFetchModified) isFetchModified = true;
		}
	});
	if (isFetchModified) {
		outer.remove();
		targetFrame.$get("FetchXml", $get("resultRender")).value = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">' + xmlDoc.documentElement.innerHTML + '</fetch>';
	}
	targetFrame.changeArea(targetFrame.ResultsPage);
	targetFrame.$get('resultRender').submit();
} else {
	alert('Cannot locate Advanced Find Frame');
}

Bookmarklet

Select the script below and drag it into your bookmarks bar into Chrome or IE.

javascript:var contentPanel=$('#crmContentPanel > iframe');if(contentPanel&&contentPanel.length>0){var targetFrame=contentPanel[0].contentWindow;targetFrame.ExecuteQuery();var xml=targetFrame.$get("FetchXml").value;var isFetchModified=false;var xmlDoc=$.parseXML(xml),$xml=$(xmlDoc),outer=$xml.find("condition[operator=null]");var mainFilter=$xml.has('entity > filter');if(mainFilter.length===0){$xml.find('entity').append('<filter></filter>');}
$xml.find('link-entity').each(function(i,d){var entity=$(d).attr('name');var entityAlias=$(d).attr('alias');var outerJoinCondition=$(d).find("condition[operator=null]");if(entity+'id'===outerJoinCondition.attr('attribute')){$(d).attr('link-type','outer');$xml.find('entity > filter').append('<condition entityname="'+entityAlias+'" attribute="'+entity+'id" operator="null" />');if(!isFetchModified)
isFetchModified=true;}});if(isFetchModified){outer.remove();targetFrame.$get("FetchXml",$get("resultRender")).value='<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">'+xmlDoc.documentElement.innerHTML+'</fetch>';}
targetFrame.changeArea(targetFrame.ResultsPage);targetFrame.$get('resultRender').submit();}else{alert('Cannot locate Advanced Find Frame');} void 0;

How to use this

The condition to indicate outer join is same as with the previous post i.e. you would specify the primary key of a link entity to be null, to indicate that you would like this link to be a outer join. In the screenshot below you are trying to find organisations without any contacts. When the script sees a link entity primary key, in this case contactid, set to null, it modifies this link as outer and places the appropriate filter criteria in the parent entity, in this case organisation. You basically have to build your conditions in the standard Advanced Find UI and execute the bookmarklet.

Outer

How it works

When you execute the Advanced Find, fetchxml and layoutxml is stored in the DOM. This script modifies this attribute and executes the Advanced Find again with the outer join bits added.

Open Issues

  • Even though the underlying fetchxml has been manipulated, the condition builder UI is not updated to reflect this.
  • Script error after the result page is rendered (Ignore this, as it doesn’t affect the results)

I have tested this only in CRMOnline with Firefox and Chrome.

EDIT (05/11/15): This bookmarklet does not work for Marketing List – specifically from Contact to Marketing List. e.g. You want to know the contacts who are not in a marketing list or any marketing list.