Adding new documents to Cosmos DB with null as a PartitionKey value

I recently had to create a Logic App, which is responsible for copying data from one Cosmos DB database to another.
To do this, you can use the standard building blocks available in a Logic App workflow.

While doing so, I did stumble across one issue. When you supply a Partition Key for a specific collection, and some entries have null as a value for this Partition Key, you’ll get into a bit of trouble.

For my regular flow, for documents WITH an actual Partition Key, the step I used looks pretty much like the following piece of code

"The_name_of_my_step": {
    "type": "ApiConnection",
    "inputs": {
        "host": {
            "connection": {
                "name": "@parameters('$connections')['documentdb']['connectionId']"
            }
        },
        "method": "post",
        "body": "@items('For_each_document')",
        "headers": {
            "x-ms-documentdb-is-upsert": true,
            "x-ms-documentdb-raw-partitionkey": "@{items('For_each_document')['MyPrimaryKeyValue']}"
        },
        "path": "/dbs/@{encodeURIComponent('myDatabase')}/colls/@{encodeURIComponent(items('For_each_collection'))}/docs"
    },
    "runAfter": {}
}

Over here, I create a POST request to the endpoint of my Cosmos DB collection. I’m specifying the Partition Key and telling the engine it should be an Upsert in the header. Relatively straightforward and something you’ll get out of the box.

However, when the value of @{items('For_each_document')['MyPrimaryKeyValue']} is a null value, you’ll receive an error message stating the following:

The partition key supplied in x-ms-partitionkey header has fewer components than defined in the collection

You can get this error for other reasons also, but all are related to not specifying a correct partition key for the document.
Also note, the error states x-ms-partitionkey header isn’t correct, but it’s the x-ms-documentdb-raw-partitionkey header value.

It has taken me quite some time to figure out what the problem was over here. After having tried lots of possible solutions, I finally found the answer.

If the Partition Key is null, you should specify this also in the header. This shouldn’t be specified as "x-ms-documentdb-raw-partitionkey": null, as I suspected at first, but as "x-ms-documentdb-raw-partitionkey": "null".
Writing a null value as "null" looks a bit strange at first, but it appears this is a special value you can use for these types of situations.

To do this, you still need to validate if the partition key is null or not. The solution I came up with is the following excerpt:

"Has_MyPrimaryKeyValue_or_not": {
    "type": "If",
    "expression": {
        "and": [
            {
                "equals": [
                    "@items('For_each_document')['MyPrimaryKeyValue']",
                    null
                ]
            }
        ]
    },
    "actions": {
        "Create_or_update_document_without_MyPrimaryKeyValue": {
            "type": "ApiConnection",
            "inputs": {
                "host": {
                    "connection": {
                        "name": "@parameters('$connections')['documentdb']['connectionId']"
                    }
                },
                "method": "post",
                "body": "@items('For_each_document')",
                "headers": {
                    "x-ms-documentdb-is-upsert": true,
                    "x-ms-documentdb-raw-partitionkey": "null"
                },
                "path": "/dbs/@{encodeURIComponent('processdata')}/colls/@{encodeURIComponent(items('For_each_collection'))}/docs"
            },
            "runAfter": {}
        }
    },
    "runAfter": {},
    "else": {
        "actions": {
            "Create_or_Update_document": {
                "type": "ApiConnection",
                "inputs": {
                    "host": {
                        "connection": {
                            "name": "@parameters('$connections')['documentdb']['connectionId']"
                        }
                    },
                    "method": "post",
                    "body": "@items('For_each_document')",
                    "headers": {
                        "x-ms-documentdb-raw-partitionkey": "@{items('For_each_document')['MyPrimaryKeyValue']}",
                        "x-ms-documentdb-is-upsert": true
                    },
                    "path": "/dbs/@{encodeURIComponent('processdata')}/colls/@{encodeURIComponent(items('For_each_collection'))}/docs"
                },
                "runAfter": {
                    "For_each_document_with_PartitionKey_in_target_location": [
                        "Succeeded",
                        "Failed"
                    ]
                }
            }
        }
    }
}

Figuring out I needed to add quotes to a null value has taken me too much time. I do hope this post will help you solve an issue faster.


Share

comments powered by Disqus