Fuzzy Matching to Mapping Table Data

0 Likes
over 12 years ago

Cool Solution: Approximate Matching on a Mapping Table


Recently, I was wrapping up a large project with a large client when they had the classic, “Oh yeah, one more thing…” moment. The client is a municipal government who has 100 autonomous agencies all of which seem to want to interface to the central IT department in their own particular way (or, as we say in Consulteranto, with their own “Use Case”).


The driver had a mapping table with entries for both the eDir and AD contexts for each agency represented in this driver. However, they had asked for the ability to have one agency search in two places in the match rule on the subscriber channel. This was easily done by making the mapping table entry for the AD DN a semi-colon delimited list, passing the list into the token-split, and then iterating through the resulting node-set.



<rule>
<description>[DoITT] Allow for multiple match paths</description>
<comment xml:space="preserve">Added 1/7/06 rhr. This rule enables a semicolonn (";") delimited list of potential match containers in the CSC tree. However, only the first element of the list will be used for placement, so after splitting the list up we will reset the value of DEST-SUBTREE to be the first value in the list.</comment>
<conditions>
<and>
<if-local-variable name="DEST-SUBTREE" op="available"/>
</and>
</conditions>
<actions>
<do-set-local-variable name="DEST-LIST" scope="policy">
<arg-node-set>
<token-split delimiter="|">
<token-local-variable name="DEST-SUBTREE"/>
</token-split>
</arg-node-set>
</do-set-local-variable>
<do-set-local-variable name="DEST-SUBTREE" scope="driver">
<arg-string>
<token-xpath expression="$DEST-LIST[1]"/>
</arg-string>
</do-set-local-variable>
</actions>
</rule>

<rule>
<description>match users based on eMail Address</description>
<comment xml:space="preserve">eMail address is the primary matching criteria and applies irrespective of name mapping</comment>
<conditions>
<and>
<if-class-name mode="case" op="equal">User</if-class-name>
</and>
</conditions>
<actions>
<do-for-each>
<arg-node-set>
<token-local-variable name="DEST-LIST"/>
</arg-node-set>
<arg-actions>
<do-find-matching-object scope="subtree">
<arg-dn>
<token-local-variable name="current-node"/>
<token-text xml:space="preserve">,</token-text>
<token-global-variable name="adDomainLDAP"/>
</arg-dn>
<arg-match-attr name="Internet EMail Address"/>
</do-find-matching-object>
</arg-actions>
</do-for-each>
</actions>
</rule>



The problem was when using the same mapping table on the other channel. Now the source DN that we got from AD didn’t match exactly the value in that column of the mapping table, so we couldn’t successfully do the reverse match any longer. So what I needed was to do a “contains” match for the value in the mapping table, something that wasn’t there as a feature.


The solution here was to read the mapping table into a node-set variable and use an XPATH expression to find the node which contains the string, then return the full value of that entry in the mapping table so you can pass that into the token-map verb.


To directly read in the XML data from eDirectory, whether it’s a mapping table, driver filter, schema map or whatever, typically you need to read the attribute and do a Base64 decode to change it into XML text, and then run it through the XML Parser to turn it into a node-set. This is that area in the past where you would get the notorious “no type may be converted to a node-set” message, now with IDM 3.6 this is a piece of cake.


The core element of this solution is the XPath expression, $HMPT/mapping- table/row/col[contains(text(),$SRC-SUBTREE)]/text(), which uses a predicate with the contains function to look up the row and column where the XML contains the value, and then returns the text of that same row.




<rule>
<description>[DoITT] Get Target DN</description>
<comment xml:space="preserve">Read the mapping table to get the target DN</comment>
<conditions>
<and>
<if-src-dn op="available"/>
</and>
</conditions>
<actions>
<do-set-local-variable name="HMPT" scope="policy">
<arg-node-set>
<token-xml-parse notrace="true">
<token-base64-decode notrace="true">
<token-dest-attr name="DirXML-Data" notrace="true">
<arg-dn>
<token-global-variable name="dirxml.auto.driverdn"/>
<token-text xml:space="preserve">\HMPT</token-text>
</arg-dn>
</token-dest-attr>
</token-base64-decode>
</token-xml-parse>
</arg-node-set>
</do-set-local-variable>
<do-set-local-variable name="SRC-SUBTREE" scope="policy">
<arg-string>
<token-xpath expression="$HMPT/mapping- table/row/col[contains(text(),$SRC-SUBTREE)]/text()"/>
</arg-string>
</do-set-local-variable>
<do-set-local-variable name="DEST-SUBTREE" scope="driver">
<arg-string>
<token-map default-value="**NONE**" dest="Vault Placement" src="AD Location" table="HMPT">
<token-local-variable name="SRC-SUBTREE"/>
</token-map>
</arg-string>
</do-set-local-variable>
</actions>
</rule>



If I was going for extra credit (which I was not), I could have possibly replaced the token-map entirely by going back up one level and taking the next column which is where the result value was. I actually tried that using an XPath axis called following-sibling but was not successful, probably because it doesn’t do what it intuitively seems it would. I’m sure there is a way to do this, but with the problem solved in a way that’s not I/O or processing expensive the engineering is considered done.


In conclusion, this is something that can be useful in many applications, where you might have data similar to but not exactly matching the mapping table, and using this technique many other predicates for searching would be possible.


Labels:

How To-Best Practice
Comment List
Anonymous
Related Discussions
Recommended