Queries with session token in Parse server 4.x.x look a bit different in mongo

Hello folks!

I am currently running Parse Server 3.6.0 and mongodb 3.6.9. Last week I tried to update Parse server to the latest version (4.2.0) and it was a horrible experience. Some of the queries against a large table (+5m records) started taking too long.

I haven’t really found the root cause of the problem, however, one of the things I noticed differently is that in the latest version, simple queries done with a session token seem to be “converted” to an $or query at the mongodb level. For example:

For a single query, this is how it get translated to the mongodb query in version 3.6

        "command" : {
            "find" : "lineItem",
            "filter" : {
                    "$and" : [
                            {
                                    "_p_user" : "_User$userId"
                            },
                            {
                                    "_p_user" : "_User$userId",
                                    "_p_receipt" : "receipt$receiptId",
                                    "status" : {
                                            "$in" : [
                                                    "status1",
                                                    "status2",
                                                    "status3",
                                                    "status4"
                                            ]
                                    }
                            }
                    ],
                    "_rperm" : {
                            "$in" : [
                                    role1,
                                    "*",
                                    "*",
                                    "role2",
                                    "role3",
                                    "userId"
                            ]
                    }
            },
            "sort" : {
                    
            },
            "projection" : {
                    
            },
            "limit" : 100,
            "returnKey" : false,
            "showRecordId" : false,
            "$db" : "test"
    }

And this is how it gets translated to the mongodb query in version 4.2.0

        "command" : {
            "find" : "lineItem",
            "filter" : {
                    "$or" : [
                            {
                                    "$and" : [
                                            {
                                                    "_p_user" : "_User$userId"
                                            },
                                            {
                                                    "_p_user" : "_User$userId",
                                                    "_p_receipt" : "receipt$receiptId",
                                                    "status" : {
                                                            "$in" : [
                                                                    "status1",
                                                                    "status2",
                                                                    "status3",
                                                                    "status4"
                                                            ]
                                                    }
                                            }
                                    ]
                            },
                            {
                                    "$and" : [
                                            {
                                                    "_p_user" : {
                                                            "$all" : [
                                                                    {
                                                                            "__type" : "Pointer",
                                                                            "className" : "_User",
                                                                            "objectId" : "userid"
                                                                    }
                                                            ]
                                                    }
                                            },
                                            {
                                                    "_p_user" : "_User$userId",
                                                    "_p_receipt" : "receipt$receiptId",
                                                    "status" : {
                                                            "$in" : [
                                                                    "status1",
                                                                    "status2",
                                                                    "status3",
                                                                    "status4"
                                                            ]
                                                    }
                                            }
                                    ]
                            }
                    ],
                    "_rperm" : {
                            "$in" : [
                                    role1,
                                    "*",
                                    "*",
                                    "role2",
                                    "role3",
                                    "userId"
                            ]
                    }
            },
            "sort" : {
                    
            },
            "projection" : {
                    
            },
            "limit" : 100,
            "returnKey" : false,
            "showRecordId" : false,
            "lsid" : {
                    "id" : UUID("15fe77ed-45ad-4625-b486-ba008b3aebb2")
            },
            "$db" : "test"
    },

This leads me to the following questions:

  1. Why does a simple Parse Query with a couple of equalTo and a containedIn gets translated into a complicated $or query in the latest version? I tried it in versions 4.0.0 and above and I get the same behavior.

  2. Why does the query in the latest version adds a clause to match for the user in the following way?

    “_p_user” : {
    “$all” : [
    {
    “__type” : “Pointer”,
    “className” : “_User”,
    “objectId” : “userid”
    }
    ]
    }

    I may be wrong, but it seems to me it is useless to search this way since pointers are not stored this way in the mongodb tables.

  3. Does this means that on average, the queries in the old version of parse server are more performant than in the new version because of the increased complexity in the query?

    I am aware that there are few corner cases in which $or queries can mess up performance in some versions of mongodb, see https://jira.mongodb.org/browse/SERVER-36393.

I hope someone can help me clarify my doubts above.

Best regards,

  • Musa

Hi Musa.

Thanks for sharing the problem.

Could you please also share the query that you are sending to the server?

Best.

Hello @davimacedo!

Sure:

new Parse.Query('lineItem')
        .equalTo('user', userPointer)
        .equalTo('receipt', receiptPointer)
        .containedIn('status', [ "status1", "status2", "status3", "status4" ])
        .find({sessionToken: token});

Just wanted to clarify that this specific query (which corresponds to the mongodb queries I showed in my original post) is not giving me any performance problems. The performance problems are related to a different query that I think I know how to solve.

The purpose of this post was to ask about the three questions/doubts/curiosities I posted in my original post.

Thanks!

It looks you can have found a bug. Do you mind to open an issue in Parse Server GitHub repo? If you can also add a test case, it would be very helpful.

Sure! I don’t know what a “test case” implies, but I will document it as best as I can and will include the query I used to reproduce it as I did here, and if anything else is required I will provide it

@davimacedo Ahh I think I got it now. It seems that the “or” query with the pointer value is introduced whenever you add a CLP giving access to the user that is specified in a column of the same table.

I will document this behavior on the issue I will open.

Done https://github.com/parse-community/parse-server/issues/6708

1 Like

Thanks for the further investigation. I will check it out.