Skip to main content

Notifications

Microsoft Dynamics 365 | Integration, Dataverse...
Answered

FetchXML to filter lookup based on related entities

Posted on by 355

I'm writing a JavaScript function which will be associated to the form of the entity "Collection".

"Collection" has a lookup field to the entity "Director" which shows all directors in the system by default.

"Collection" is also connected to "Movie", which is a related entity.

"Movie" has a lookup field to "director".

So let's say that I'm on the form of "Collection One", which the movies "Taxi Driver" and "Apocalypse now" are related to, and when I click on "Directors" I see all directors saved on my CRM.



What I want to achieve:

Filter the query which retrieves all the directors to see only those which have directed any of the books related to this collection ( "Martin Scorsese" and "Francis Ford Coppola").


What I did so far:

function filterLookup() {

    var associatedMovies;
    var associatedDirectors;
    var targetId = Xrm.Page.getAttribute("collectionid").GetValue();


    // retrieved movies associated to this collection    
    Xrm.WebApi.retrieveMultipleRecords("movie", "?$select=movieid, directorid,&$filter=collectionid eq"   targetId).then(

        function success(result) {  
            for (var i = 0; i < result.entities.length; i  ) {        
                associatedMovies.push(result.entities[i]);                
            };
            function () {
                console.log("error");
            }
        }

    );

    // retrieve directors associated to each m in associatedMovies
    foreach(m in associatedMovies){

        Xrm.WebApi.retrieveMultipleRecords("director", "?$select=directorid, name,&$filter=directorid eq"   m.getAttribute("directorid").getValue).then(

            function success(result) {  
                for (var i = 0; i < result.entities.length; i  ) {
                    associatedDirectors.push(result.entities[i]);
                };
                function () {
                    console.log("error");
                }
            }

        );

    }

    // filter lookup
    // fetchXML to retrieve director connected to the associatedMovies?


}

Can I complete the function and filter the results by using a fetchXML?

I wonder whether I really need those two variables I've been used so far (associatedMovies and associatedAuthors) or if there is a easier way to achieve the task by using a single fecthXML. 

  • Joel D Profile Picture
    Joel D 355 on at
    RE: FetchXML to filter lookup based on related entities

    Hi,

    I finally applied successfully both of the approaches that you suggested and wrote, thank you very much Jason and Leah. I couldn't find a way to make the filter work even when no movies are retrieved, anyhow it works properly when result.entities is populated.
    The main issue I was experiencing in the second version was due to the fact that in the retrieveMultipleRecords I was using the lookup field parentid (collectionid ) instead than the _prefix_parentEntityid_value (_prefix_collection_value) therefore I could not compare it with the GUID (previously stored in the variable collectionID).

    I'm retrieving the target GUID without deleting the curly braces (without formatting), it works as well:

    var collectionid  = formContext.data.entity.getId();
    



    Full code:

    * * *  Version 1 - with FetchXML passed as option parameter in the retrieveMultipleRecords * * *

    // account is the logical name for director
    
    function filterLookup(executionContext) {
        debugger;
        var formContext = executionContext.getFormContext();
        var collectionid = formContext.data.entity.getId();
        var fetchXmlFilter = ""  
            "";
    
        var fetchmovies = "";
    
    
        Xrm.WebApi.online.retrieveMultipleRecords("prefix_movie", "?fetchXml="   fetchmovies).then(
            function success(results) {
                debugger;
                for (var i = 0; i < results.entities.length; i  ) {
                    var directorid_value = results.entities[i]["_prefix_accountid_value"];
                    var directorID = directorid_value.toUpperCase();
                    var directorID2 = "{"   directorID   "}";
                    fetchXmlFilter  = ""   directorID2   "";
                }
                fetchXmlFilter  = "";
                formContext.getControl("prefix_defaultmoviedirector").addPreSearch(function () {
    
                    // name (not schema name)
                    formContext.getControl("prefix_defaultmoviedirector").addCustomFilter(fetchXmlFilter, "account");
                });
            },
            function (error) {
                debugger;
                Xrm.Navigation.openAlertDialog(error.message);
            }
        );
    }
    



    * * *  Version 2 - retrieveMultipleRecords without fetchXML, and PreSerach event for the lookup defined outside (I'm not using the namespace for the function though)* * *

    I edited some things, including the following:
    I declared the var formContext outside the getCollectionsMovies function, immediately after the declaration of  var fetchDirectorFilter = "".
    Rather than using the _prefix_director_value  in the $select  I'm using the schema name of the lookup field (prefix_accountID), as on my CRM Movie and Author/account have a N:1 relationship and Author is a related entity.


    // account is the logical name for director
    
    var formContext;
    var fetchdirectorFilter = "";
    
    Filtermoviesdirectors = function () {
        formContext.getControl("prefix_defaultmoviedirector").addCustomFilter(fetchdirectorFilter, "account");
    }
    
    
    getCollectionsMovies = function (executionContext) {
    
        formContext = executionContext.getFormContext();
        var collectionid = formContext.data.entity.getId();
    
        // prefix_accountID is the schema name of the lookup field, in movie (when I used the logical name I got errors) ->
        Xrm.WebApi.retrieveMultipleRecords("prefix_movie", "?$select=prefix_accountID,_prefix_accountid_value&$filter=(_prefix_collectionid_value eq "   collectionid   ")").then(
            function success(result) {
                debugger;
                if (result.entities.length > 0) {
                    fetchdirectorFilter = "";
                    
                    for (var i = 0; i < result.entities.length; i  ) {
                    var directorid_value = result.entities[i]["_prefix_accountid_value"];
                    var directorID = directorid_value.toUpperCase();
                    var directorID2 = "{"   directorID   "}";
                    fetchdirectorFilter  = ""   directorID2   "";
                    }
                }
                
                fetchdirectorFilter  = "";
                formContext.getControl("prefix_defaultmoviedirector").addPreSearch(Filtermoviesdirectors);
            },
            function (error) {
                debugger;
                Xrm.Navigation.openAlertDialog(error.message);
            }
        );
    }

    Thank you for helping me to look at the same problem from different approaches, it was really useful.

  • Jason M. Cosman Profile Picture
    Jason M. Cosman 5,234 on at
    RE: FetchXML to filter lookup based on related entities

    Nice glad Leah was able to answer your question!

  • Joel D Profile Picture
    Joel D 355 on at
    RE: FetchXML to filter lookup based on related entities

    Hello,

    thank you for your reply. I'm using double quotes now.

    I noted that in your code, in the retrieveMultipleRecords, you used _jcmvp_collection_value (instead than collectionid) to make the comparison with the variable collectionid which stores the id of the current entity:

    var collectionID = formContext.data.entity.getId().replace("{", "").replace("}", "");
    
    Xrm.WebApi.retrieveMultipleRecords("jcmvp_movie", "?$select=_jcmvp_director_value&$filter=(_jcmvp_collection_value eq "   collectionID   ")")
    

    Does this _jcmvp_collection_value  correspond to the lookup field inside the retrieved entity or is it a different one? If it's different, where can I find its name? I'm inside the solution -> Components -> Entities -> Movie --> Fields, but I can't see it.

    Just to be sure: the attributes that you're passing as parameters in the retrieveMultipleRecords are the field names (not schema names), is it correct?

  • Jason M. Cosman Profile Picture
    Jason M. Cosman 5,234 on at
    RE: FetchXML to filter lookup based on related entities

    Hey Joel;

    I'm definitely get to answering each of your questions but to your last statement it's worth noting there are some differences in your code vs mine around getting the entity ID from the form.

    //My collection ID
    var collectionID = formContext.data.entity.getId().replace("{", "").replace("}", "");
    
    //Your collection ID
    var collectionid = formContext.data.entity.getId().replace('{', '').replace('}', '');

    I wanted to make sure I highlight that your using single quotes where I'm using double in this scenario. I did simplify your replace down to a single line as well for comparison. I'll dig in and respond to your other questions in a bit as well! 

    Thanks for following back up with questions.

  • Joel D Profile Picture
    Joel D 355 on at
    RE: FetchXML to filter lookup based on related entities

    Hi, I followed your tips, but I'm experiencing another issue, the WebApi call is not successful and this is the error message in the errorCallBack:

    A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collection' and 'Edm.Guid' for operator kind 'Equal'."

    I assume that it's related to the incompatibility between the collectionid inside the filter option (it's the lookup field in "Movie" related to the entity "Collection") and the collectionidFormatted previously defined, which contains the id of the current collection, but I don't know how to overcome the issue:

    var collectionid = formContext.data.entity.getId();
        var collectionidFormatted = collectionid.replace('{', '').replace('}', '');//remove {}

    Xrm.WebApi.retrieveMultipleRecords("movie", "?$select= directorID, name&$filter=collectionID eq "   collectionidFormatted)



    The complete error message looks like:

    code: 2147951872
    errorCode: 2147951872
    message: "A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collection' and 'Edm.Guid' for operator kind 'Equal'."
    raw: "{"errorCode":2147951872,"message":"A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collection' and 'Edm.Guid' for operator kind 'Equal'.","code":2147951872,"title":"","raw":"{\"_errorCode\":-2147015424,\"_errorFault\":{\"_responseXml\":null,\"_errorCode\":0,\"_innerFault\":null,\"_callStack\":null,\"_responseText\":\"{\\\"error\\\":{\\\"code\\\":\\\"0x0\\\",\\\"message\\\":\\\"A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collection' and 'Edm.Guid' for operator kind 'Equal'.\\\"}}\",\"_annotations\":{},\"_messages\":[\"A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collection' and 'Edm.Guid' for operator kind 'Equal'.\"},\"_message\":\"A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collection' and 'Edm.Guid' for operator kind 'Equal'.\",\"_exception\":null,\"_innerError\":null,\"_errorSource\":0,\"_blockErrorReporting\":false,\"_stack\":null,\"_faultedRequestIndex\":-1,\"_clientRequestId\":\"5d829a44-c34f-413e-a55b-e04d32a5dde8\",\"_serverResponseId\":\"34f9bc1f-99d1-4e54-9f91-dae7f9a6fad1, 56b00625-a9a8-47a7-83ef-a7eedbdacdd6\",\"_httpStatusCode\":400,\"_retryAfter\":null,\"_date\":\"2021-02-06T21:29:22.000Z\",\"_isRequestTooLong\":false,\"message\":\"A binary operator with incompatible types was detected. Found operand types 'Microsoft.Dynamics.CRM.collectiona' and 'Edm.Guid' for operator kind 'Equal'.\",\"name\":\"Error\",\"stack\":\"No stack available.\",\"__action\":{\"source\":{\"actionType\":\"oData.retrieveMultiple.async\",\"actionStack\":\"oData.retrieveMultiple.async\",\"contextId\":\"d8c6ae26-5137-4ce5-940a-1ae44589f58c\"},\"child\":{\"actionType\":\"oData.retrieveMultiple.async\",\"actionStack\":\"oData.retrieveMultiple.async\",\"contextId\":\"d8c6ae26-5137-4ce5-940a-1ae44589f58c\"}}}"}"
    title: ""



  • Joel D Profile Picture
    Joel D 355 on at
    RE: FetchXML to filter lookup based on related entities

    Hi, thank you all for your replies.

    @Jason, thank you very much for your blog and code, I looked at it and. As I'm new to JavaScript and to debugging client-side, I have few questions:

    - it's not clear to me whether  the PreSearch for the lookup field is optional or required, why do I need it? Could I filter the lookup just by using addCustomFilter instead?

    - I tried to test your code by using a simpler structure, but how can I handle the asynchronous call? More specifically, I wrote the following, which is based on your code, but the structure is slightly different (I declared the global variable and defined the main function and the function which is being called then):

    var FetchDirectorFilter = "";
    
    
    GetCollectionMovies = function (executionContext) {
    
        debugger;
        var formContext = executionContext.getFormContext();
        var collectionID = formContext.data.entity.getId().replace("{", "").replace("}", "");
    
        Xrm.WebApi.retrieveMultipleRecords("movie", "?$select=directorID&$filter=(collectionid eq "   collectionID   ")").then(
            function success(result) {
                debugger;
    
                if (result.entities.length > 0) {
                    FetchDirectorFilter = "filter type='and'>< result.entities.length; i  ) {
                    FetchDirectorFilter  = ""   result.entities[i].getAttribute("directorid").getValue   "";                
                }
                FetchDirectorFilter  = "";
                // do I need preSearch?
                formContext.getControl("director").addPreSearch(filterMovieDirectors);
            },
            function (error) {
                Xrm.Navigation.openAlertDialog(error.message);
            }
        );
    }
    
    
    filterMovieDirectors = function () {
    
        formContext.getControl("director").addCustomFilter(FetchDirectorFilter, "director");
    }
    

    I'm debugging it runtime: once I jump outside the asynchronous WebApi call (after the last } of the GetCollectionMovies function), if I press "Resume script execution" I'm redirected to another JS file associated to the same form, in other words the block of instructions inside the callback function (lines 10-25) never runs.

  • Verified answer
    Jason M. Cosman Profile Picture
    Jason M. Cosman 5,234 on at
    RE: FetchXML to filter lookup based on related entities

    Hello Joel; 

    I rebuilt your scenario and created the JavaScript needed for this to work. I wrote a blog at: http://www.cosmansdynamics.com/2021/01/28/addpresearch/

    The order of what needs to be done is essentially:

    1. Execute a Xrm.WebApi.retrieveMultipleRecords allowing us to build the filter statement with the proper directors.
    2. Once the filter statement has been completed we need to call the addCustomerFilter function.
    3. We need to have our function that calls the addPreSearch when the lookup event is changed using the filter statement.

    The javascript file I created can be found and reviewed at: https://github.com/jasoncosman/Collections/blob/master/jcmvp_collection.js

    Please take a read and post any follow-up questions or concerns and I'll be happy to answer. If I misunderstood the scenario please let me know as well. Thank you and have a great day!

  • Verified answer
    Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: FetchXML to filter lookup based on related entities

    Hi Joel,

    you can refer following link to know how to add dynamic value in fetchXml.

    ( ) How to pass dynamic variable value as array for a attribute in fetchxml? - Microsoft Dynamics CRM Forum Community Forum 

    And i try to use your example to test, it can achieve by using following code:

    I found that two variables (associatedMovies and associatedAuthors) both don't need, and you can just retrieve movie entity to get directorID, there is no need  to retrieve  director entity again, which is redundant.

    you can use Rest Builder tool that i mentioned above to build web api to retrieve movie entity:

    pastedimage1611800725482v2.png

    Copy the code and replace hardcode with dynamic value.

    function filterDirector(executionContext) {
        var formContext = executionContext.getFormContext();
        var collectionId = formContext.data.entity.getId().replace('{', '').replace('}', '');//current collection id
        var fetchXml = ""  
            ""
        // retrieved movies associated to this collection to get  Corresponding director id 
        Xrm.WebApi.online.retrieveMultipleRecords("new_movie", "?$select=_new_directorid_value,new_name&$filter=_new_collectionid_value eq "   collectionId).then(
            function success(results) {
                for (var i = 0; i < results.entities.length; i  ) {
                    var _new_directorid_value = results.entities[i]["_new_directorid_value"];//director id
                    var _new_directorid_value_formatted = results.entities[i]["_new_directorid_value@OData.Community.Display.V1.FormattedValue"];
                    var _new_directorid_value_lookuplogicalname = results.entities[i]["_new_directorid_value@Microsoft.Dynamics.CRM.lookuplogicalname"];
                    var new_name = results.entities[i]["new_name"];
                    var directotID = _new_directorid_value.toUpperCase();
                    var directotID2 =  "{" directotID "}";//Add {} to guid and Letter capitalization
    
                    fetchXml  = ""   directotID2   "";
                }
                fetchXml  = "";
                formContext.getControl("new_directorid").addPreSearch(function () {
    
                    formContext.getControl("new_directorid").addCustomFilter(fetchXml);
    
                });
    
            },
            function (error) {
                Xrm.Utility.alertDialog(error.message);
            }
        );
    }

    You can copy code i provided above, just replace filed and entity logic name.

    Test Result:

    Only Two directors related to two movies can show in the lookup list.

    pastedimage1611799988932v1.png

    Regards,

    Leah Ju

  • Joel D Profile Picture
    Joel D 355 on at
    RE: FetchXML to filter lookup based on related entities

    Hi Leah,

    thanks for replying.

    How can I make movies ID dynamic inside the fetchXML?

    In your fetchXML you compare the director ID to the lookup directorID in the movies I already have in the crm.

    But the author ID of the movies are not "static", as some movies can be added or deleted from the crm system, but me I want that the filter is valid no matter which the books are.

  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: FetchXML to filter lookup based on related entities

    Hi Joel,

    As you mentioned, there are multiple movie related records, so you need add multiple director id as condition value in fetchxml, like following sample:

    (1)use ‘in’ operator with single condition.

    var fetchXml =
    ""  
            ""  
              "{DBBB8E56-B9D9-E311-9410-001CC4EECDD6}"  
              "{DDBB8E56-B9D9-E311-9410-001CC4EECDD6}"  
              "{DFBB8E56-B9D9-E311-9410-001CC4EECDD6}"  
            ""  
    "";
    Xrm.Page.getControl("lookup").addCustomFilter(fetchXml);
    

    (2) use ‘eq’ operatoe with multiple condition.

           fetchXml = " "
           "" 
           "";
           Xrm.Page.getControl("lookup").addCustomFilter(fetchXml);

    And as code shown, you can use addCustomFilter () function to add filter to the lookup field(director).

    Finally, I have doubt about your original code(line 5),

    The js run on the collection record form, right? There is no collectionid field shown in the form, you need use following code to get current record’s id:

    var id = Xrm.Page.data.entity.getId();
    
    var id = id.replace('{', '').replace('}', '');//remove {}

    For more detailed information, you can refer following link:

    http://missdynamicscrm.blogspot.com/2014/08/crm-2013-using-addcustomfilter-to-get-filtered-lookup-field-based-on-linked-entity.html

    And you can download & install Rest Builder tool to build the web api query to retrieve records.

    Refer this blog: https://carldesouza.com/dynamics-crm-rest-builder/

    You can download the tool here: https://github.com/jlattimer/CRMRESTBuilder/releases

    Regards,

    Leah Ju

    Please mark as verified if the answer is helpful. Welcome to join hot discussions in Dynamics 365 Forums.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

December Spotlight Star - Muhammad Affan

Congratulations to a top community star!

Top 10 leaders for November!

Congratulations to our November super stars!

Community AMA December 12th

Join us as we continue to demystify the Dynamics 365 Contact Center

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 291,240 Super User 2024 Season 2

#2
Martin Dráb Profile Picture

Martin Dráb 230,149 Most Valuable Professional

#3
nmaenpaa Profile Picture

nmaenpaa 101,156

Leaderboard

Product updates

Dynamics 365 release plans