{"id":99,"date":"2008-03-18T15:50:54","date_gmt":"2008-03-18T15:50:54","guid":{"rendered":"https:\/\/www.wapshere.com\/missmiis\/?p=99"},"modified":"2023-01-16T20:12:35","modified_gmt":"2023-01-16T20:12:35","slug":"advanced-joining","status":"publish","type":"post","link":"https:\/\/www.wapshere.com\/missmiis\/advanced-joining","title":{"rendered":"Advanced Joining"},"content":{"rendered":"<p>Thought I&#8217;d already covered joins sufficiently in <a href=\"https:\/\/www.wapshere.com\/missmiis\/?p=94\">this post<\/a>? Heck no, I got plenty more to say!<\/p>\n<h4>Multiple Join Rules<\/h4>\n<p>You can have multiple join rules, which you may (or may not) combine into Mapping Groups.<\/p>\n<p><!--more--><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.wapshere.com\/images\/ma-join-mappinggroups.gif\" alt=\"\" \/><\/p>\n<ul>\n<li>The components <em>within <\/em>a mapping group have AND relationships: eg., where &#8220;sn = sn&#8221; AND &#8220;department = department&#8221;.<\/li>\n<li>Between the mapping groups we have an OR relationship &#8211; so we are looking for an object that satisfies <em>one of them<\/em>.<\/li>\n<li>The mapping groups are evaluated top-down. If you are not resolving the search (more on that below) ILM will join the first match, so the order you put them in is significant.<\/li>\n<\/ul>\n<h4>Advanced Join Rules<\/h4>\n<p>As well as Direct join rules, we can use .NET to write Advanced rules for when we want to match a single metaverse attribute to a calculated value based on a number of CDS attributes.<\/p>\n<p>For example: Take the first inital and surname from the CDS and try and match it to the uid in the metaverse, using a couple of different capitalisations.<\/p>\n<p>Configuring an Advanced join rule in the MA is very similar to an Advanced flow rule &#8211; select the input and output attributes, and give the join rule a name (you do not have to accept the default one).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.wapshere.com\/images\/ma-join-advanced.gif\" alt=\"\" width=\"402\" height=\"85\" \/><\/p>\n<p>You now need to write the code for the join rule. And you do that in the <a href=\"https:\/\/www.wapshere.com\/missmiis\/?p=102\">MA Extension<\/a>, under the <strong>Sub MapAttributesForJoin<\/strong>.<\/p>\n<p>One of the arguments of this sub is a collection called <em>values<\/em>. You put your calculated values into here &#8211; one of which will hopefully match to a metaverse object.<\/p>\n<p>Continuing on with our uid example, the code might look like this:<\/p>\n<pre>Public Sub MapAttributesForJoin(ByVal FlowRuleName As String, ByVal csentry As CSEntry, ByRef values As ValueCollection) Implements IMASynchronization.MapAttributesForJoin\n\n\u00a0 Select Case FlowRuleName\n\n\u00a0   Case \"joinUid\"\n\n\u00a0     Dim initial as String = csentry(\"givenName\").Value.Substring(0,1)\n\n\u00a0     values.Add(initial.ToUpper &amp; csentry(\"sn\").Value)\n\n\u00a0     values.Add(initial.ToUpper &amp; csentry(\"sn\").Value.ToUpper)\n\n\u00a0   Case Else\n\n\u00a0     Throw New EntryPointNotImplementedException\n\n\u00a0 End Select\n\nEnd Sub<\/pre>\n<p>So all we&#8217;ve done is derive a string, returned it to ILM in the <em>values<\/em> collection, so that ILM can try to match it against the uid&#8217;s it has in the metaverse.<\/p>\n<p>Note that it&#8217;s a good idea to leave that &#8220;Case Else&#8221; in there, just in case someone configures a join rule name without also writing the code.<\/p>\n<h4>Resolving Multiple Matches<\/h4>\n<p>There are some situations where you want to check through a group of possible matches, and select the most likely one.<\/p>\n<p>I had an experience with trying to match student home directories against usernames. The usernames included the course code, but the home directories just used the first past of the username &#8211; ie without the course code. The only way you could tell for sure who&#8217;s directory it was was by looking at the folder path, which did include the course code somewhere in the parent hierarchy. Exactly where was not predictable as it differed from department to department.<\/p>\n<p>The solution was as follows:<\/p>\n<ol>\n<li>Create a new metaverse attribute called &#8220;uidSimple&#8221;,<\/li>\n<li>Add a separate flow rule on the importing MA that cropped the course code from student IDs and populated uidSimple,<\/li>\n<li>Add a Direct join rule for the home folders: &#8220;folderName = uidSimple&#8221;,<\/li>\n<li>Use a Resolve rule to figure out which folder is the right one.<\/li>\n<\/ol>\n<h4>Configuring a Resolve Rule<\/h4>\n<p>In your join rule, tick the box saying you wish to use a rules extension to resolve multiple matches, and then enter a name. Once again you are under no obligation to accept the default (I never do) &#8211; just make sure you name check it correctly in your code.<\/p>\n<p>Then modify the <strong>Function ResolveJoinSearch<\/strong> in the MA Extension.<\/p>\n<p>This function passes you <em>rgmventry<\/em> &#8211; a collection of metaverse objects which have satisfied your join rule &#8211; and a single <em>csentry<\/em>. Your job is to return a TRUE (match found) or FALSE (match not found), and the index of the found object as <em>imventry<\/em>.<\/p>\n<pre>Public Function ResolveJoinSearch(ByVal joinCriteriaName As String, ByVal csentry As CSEntry, ByVal rgmventry() As MVEntry, ByRef imventry As Integer, ByRef MVObjectType As String) As Boolean Implements IMASynchronization.ResolveJoinSearch\n\n\u00a0 ResolveJoinSearch = False\n\u00a0 imventry = -1\n  index = 0\n\n\u00a0 Dim mventry As MVEntry\n\n\u00a0 Select Case joinCriteriaName\n\n\u00a0   Case \"resolveFolder\"\n\u00a0     For Each mventry In rgmventry\n\u00a0       If mventry.ConnectedMAs(<em>MAName<\/em>).Connectors.Count = 0 Then\n\n\u00a0       'Only considering metaverse obejcts that are not yet connected in this MA\n\u00a0         If csentry(\"Path\").Value.IndexOf(mventry(\"CourseCode\").Value) &gt; 0 Then\n\u00a0           ResolveJoinSearch = True\n\u00a0           imventry = index\n\u00a0           Exit Function\n\u00a0         End If\n\n\u00a0       End If\n\n\u00a0       index = index + 1\n\n\u00a0     Next\n\n\u00a0   Case Else\n\n\u00a0     Throw New EntryPointNotImplementedException()\n\n\u00a0  End Select\n\nEnd Function<\/pre>\n<h4>Once you&#8217;ve got these hard-won joins in place, make sure you keep &#8217;em!<\/h4>\n<p>There&#8217;s lots of creative stuff you can do to help you get a messy CDS joined up. It would, however, be nice if all these complex join rules can be retired once the CDS is being properly managed.<\/p>\n<p>If at all possible export a &#8220;breadcrumb&#8221; value out to some attribute in the CDS that will enable a simple, 100% certain join to take place in the future if, for any reason, you have to re-import the entire connector space. If you plan for it it won&#8217;t hurt a bit when it happens!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Thought I&#8217;d already covered joins sufficiently in this post? Heck no, I got plenty more to say! Multiple Join Rules You can have multiple join rules, which you may (or may not) combine into Mapping Groups.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[34,28,19],"tags":[],"class_list":["post-99","post","type-post","status-publish","format-standard","hentry","category-ilm2007","category-miis2003","category-newbie"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pkp1o-1B","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/99","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/comments?post=99"}],"version-history":[{"count":4,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/99\/revisions"}],"predecessor-version":[{"id":3387,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/99\/revisions\/3387"}],"wp:attachment":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/media?parent=99"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/categories?post=99"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/tags?post=99"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}