Any ideas ... dynamic query and parameters

In some code I am writing, I build a query dynamically, if I capture theParams which is a Dictionary <string, object> and the query strings and move them to the browser, the query fetches what I need (in the example I am running, it is one record).

In the c# code ... I get nothing (fetched == false), any ideas?

            retVal.AddRange(await session.ExecuteReadAsync(async tx =>
            {
                var theParams = queryParams.Build();
                var cursor = await tx.RunAsync(query, theParams);
                var fetched = await cursor.FetchAsync();
                while (fetched)
                {
... etc

So I have 3 options, if i hardcode the params, <string,object> and <object,object> :

                List<string> aParams = [ "c8849ba9-8c87-4a55-9db4-2c2cebbccab1", "a2594625-077e-423b-b3a0-eef12e89824b", "8bc17789-dc82-49f6-94a3-ce2615e41e83", "31d6967b-fcfb-4818-ba8d-8adb0cabacd1", "52a1e6f1-6299-41c1-8fef-d1a044fe064e", "232f8c3d-2ab5-4b28-94e8-d1757063d6f6", "baa97f28-8d06-4fbd-96fa-30089d8fd5ee" ];
                var testParams = new
                {
                    theTarget = "54:6b65977c-9dd8-4989-80f4-225b96258645:12750",
                    theAsset = asset,
                    var0 = "fe9bb638-c729-4c2b-ba10-6ecba0548aac",
                    var1 = "b8a0feab-355e-4dc0-9117-837f7a206130",
                    var2 = "437b6bba-f503-4b40-9710-08d44cf18f2c",
                    params3 = aParams
                };

If I pass the testParams to the query, I get the expected results; if I pass theParams, I get 0.

Debugging, testParams works, queryParams._parameters which is <string,object> returns empty, theParams (an attempt to do <object, object>) throws an exception

AFAIK, C# has no way of building dynamic objects programmatically, and according to the documentation Neo4J should accept a <string, object>

I have managed to narrow it down to the bug being in a param entry that is an array of strings, in this case $params3 : [ uuid1 ... uuid7 ] is when the query is not returning anything:

MATCH (R3) WHERE R3.uuid IN $params3 WITH COLLECT(R3) as rOps3

OPTIONAL MATCH (RET)-[:language*0..1]->(V3)

MATCH (:Asset {uuid: $theTarget})<-[partOf]-(RET:Asset { uuid: $theAsset })
  WHERE V3 IN rOps3

RETURN DISTINCT RET.uuid AS uuid, RET.publicName AS name

If I execute the query in the browser with the :params set, it works.

The rest of the query are scalar variables, which seem to work (although some of those alone return nothing - which was expected), the $params3 is the key parameter anyway.

More clues on the issue.

When fixed anonymous object:

                List<string> aParams = [ "c8849ba9-8c87-4a55-9db4-2c2cebbccab1", "a2594625-077e-423b-b3a0-eef12e89824b", "8bc17789-dc82-49f6-94a3-ce2615e41e83", "31d6967b-fcfb-4818-ba8d-8adb0cabacd1", "52a1e6f1-6299-41c1-8fef-d1a044fe064e", "232f8c3d-2ab5-4b28-94e8-d1757063d6f6", "baa97f28-8d06-4fbd-96fa-30089d8fd5ee" ];
                var testParams = new
                {
                    theTarget = "54:6b65977c-9dd8-4989-80f4-225b96258645:12750",
                    theAsset = "54:6b65977c-9dd8-4989-80f4-225b96258645:13256",,
                    var0 = "fe9bb638-c729-4c2b-ba10-6ecba0548aac",
                    var1 = "b8a0feab-355e-4dc0-9117-837f7a206130",
                    var2 = "437b6bba-f503-4b40-9710-08d44cf18f2c",
                    params3 = aParams
                };

The type of params3 is List<string>

In a real case when building queries and params dynamically, a parameter is just another object, either a scalar object (e.g. string) which seems to work fine or a list of objects (e.g. list), and the object inside the list could be a scalar type (i assume consistency) or a hierarchy of parameters (e.g. Dictionary<string, object>).

Not sure about the behaviour when one of the params is nesting params as a Dictionary<string,object>, but not currently building one.

More information:

  • testParams is as above and can be "calculated" before execution
  • theParams is created at run time from the Dictionary<list, object>
  • as you can see from the bottom of the screenshot, the debugger "calculates" they are the same type ((<>f AnonymousTvne29<strina, string, string, string, string, list<string>>).
  • if I run a query that uses only scalar params it all works (Qfetched = Qbfetched = true)
  • If I add the the list of strings, bfetched = true, but fetched = false

@dana_canzano any idea who is your dotnet driver guru ? :)

Heylo,

Not a guru, but happy to take a look.

So!

  • What version of the driver are you using?
  • Can you give a full working code snippet that replicates the issue - I mean - I can attempt to recreate - but I suspect you're 90% there already and we can try to see what's happening faster that way.

All the best

Charlotte

Driver 5.28.1

Not sure how to send you all the code ...

// the method used for testing  parameter building from a Dictionary of <string, object>

    private readonly Dictionary<string, object> _parameters = new Dictionary<string, object>();

    public object BuildTmp()
    {
        // todo fix error when new drivers are out, temporary method
        // ---------------------------------------

        List<string> conv(object obj)
        {
            return ((IEnumerable<object>)obj).Select(o => o?.ToString())
                                             .ToList();
        }
        
        var list3 = new List<string>();
        if (_parameters.ContainsKey("params3"))
            list3 = conv(_parameters["params3"]);
        var testParams = new
        {
            theTarget = _parameters.ContainsKey("theTarget") ? _parameters["theTarget"] as string : "",
            theAsset = _parameters.ContainsKey("theAsset") ? _parameters["theAsset"] as string : "",
            var0 = _parameters.ContainsKey("var0") ? _parameters["var0"] as string : "",
            var1 = _parameters.ContainsKey("var1") ? _parameters["var1"] as string : "",
            var2 = _parameters.ContainsKey("var2") ? _parameters["var2"] as string : "",
            params3 = list3,
        };
        return testParams;
    }

// the compiled version
                List<string> aParams = [ "c8849ba9-8c87-4a55-9db4-2c2cebbccab1", "a2594625-077e-423b-b3a0-eef12e89824b", "8bc17789-dc82-49f6-94a3-ce2615e41e83", "31d6967b-fcfb-4818-ba8d-8adb0cabacd1", "52a1e6f1-6299-41c1-8fef-d1a044fe064e", "232f8c3d-2ab5-4b28-94e8-d1757063d6f6", "baa97f28-8d06-4fbd-96fa-30089d8fd5ee" ];
                
                var testParams = new
                {
                    theTarget = asst.Id,
                    theAsset = asset,
                    var0 = "fe9bb638-c729-4c2b-ba10-6ecba0548aac",
                    var1 = "b8a0feab-355e-4dc0-9117-837f7a206130",
                    var2 = "437b6bba-f503-4b40-9710-08d44cf18f2c",
                    params3 = aParams
                };
                
// hardcoded parameter builder 
                var theParams = queryParams.BuildTmp();

// query using only scalar
                query = "MATCH (:Asset {uuid: $theTarget})<-[partOf]-(RET:Asset { uuid:$theAsset })\nRETURN DISTINCT RET.uuid AS uuid, RET.publicName AS name \n";
                var Qcursor = await tx.RunAsync(query, theParams);
                var Qfetched = await Qcursor.FetchAsync();

                var Qtemp = await tx.RunAsync(query, testParams);
                var Qbfetched = await Qtemp.FetchAsync();
                
// query using scalar and list of scalar
                query = "MATCH (R3) WHERE R3.uuid IN $params3 WITH COLLECT(R3) as rOps3\nOPTIONAL MATCH (RET)-[:language*0..1]->(V3)\nMATCH (:Asset {uuid: $theTarget})<-[partOf]-(RET:Asset { uuid: $theAsset })\n\nWHERE V3 IN rOps3\nRETURN DISTINCT RET.uuid AS uuid, RET.publicName AS name ";

                var cursor = await tx.RunAsync(query, theParams);
                var fetched = await cursor.FetchAsync();

                var temp = await tx.RunAsync(query, testParams);
                var bfetched = await temp.FetchAsync();