4

I'm trying to build an Azure function in C# that creates a new document object in Azure cosmos DB using SQL API if an id doesn't already exist and updates a document object if it already exists.

The context behind this is logging chatbot conversation history to unique user sessions.

Input:
HTTP GET Request with parameters (id (string), chatHistory(string) and chatDateTime(string))

Output:
If document object with same id already exists - then update document with input chatHisotry and chatDateTime.

If no document object exists with same id then create a new document object with id, chatHistory and chatDateTime equal to input.

Any help much appreciated! Been struggling with this one for days.

Example of document object:

{
    "id": "ESCRfAKwlTbH8W5aVRLxgA",
    "chatHistory": "Hi, Hello",
    "chatDateTime": "Fri Sep 21 2018 05:34:35 GMT+0000 (Coordinated Universal Time)",
    "_rid": "RwYSAIqaSVg2AAAAAAAAAA==",
    "_self": "dbs/RwYSAA==/colls/RwYSAIqaSVg=/docs/RwYSAIqaSVg2AAAAAAAAAA==/",
    "_etag": "\"00007400-0000-0000-0000-5ba482ed0000\"",
    "_attachments": "attachments/",
    "_ts": 1537508077
}
caridina
  • 131
  • 1
  • 7
  • You are developing this in direct azure portal or in Visual Studio.? – SH7 Sep 21 '18 at 06:41
  • Can you know us what you've done until now? What you've tries and what you're struggling with? – Nick Chapsas Sep 21 '18 at 06:53
  • Hi! I'm developing this on the direct Azure portal. – caridina Sep 24 '18 at 04:10
  • @NickChapsas I've been messing around with this below (random tutorial I've found online) which lets me insert documents into cosmos document db. https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/azure-functions/functions-integrate-store-unstructured-data-cosmosdb.md but no luck building conditional logic into it – caridina Sep 24 '18 at 04:13
  • @NickChapsas I've managed to build a function that inputs data into the cosmos document db upon http get request however I'm struggling to make the function replace/upsert(?) data if the document object already exists – caridina Sep 24 '18 at 04:49

2 Answers2

7

You can use the Azure Functions' Cosmos DB Output Binding. The Output binding does an Upsert operation.

[FunctionName("HttpTriggerWithSingleDocument")]
    public static async Task<HttpResponseMessage> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
        [DocumentDB(databaseName: "your-db",
            collectionName: "your-collection",
            ConnectionStringSetting = "CosmosDBConnectionString")] out dynamic documentToSave)
    {
        dynamic data = await req.Content.ReadAsAsync<object>();

        if (data == null)
        {
            documentToSave = null;
            return req.CreateResponse(HttpStatusCode.BadRequest);
        }

        documentToSave = data;

        return req.CreateResponse(HttpStatusCode.Created);
}

Azure Portal version:

using System.Net;

public static async Task<HttpResponseMessage> Run(
            HttpRequestMessage req,
            IAsyncCollector<dynamic> documentsToStore)
        {
            dynamic data = await req.Content.ReadAsAsync<object>();

            if (data == null)
            {
                return req.CreateResponse(HttpStatusCode.BadRequest);
            }

            await documentsToStore.AddAsync(data);

            return req.CreateResponse(HttpStatusCode.Created);
    }

And you also need the function.json updated to something like:

{
  "bindings": [
    {
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "type": "documentDB",
      "name": "documentsToStore",
      "databaseName": "<your-database-name>",
      "collectionName": "<your-collection-name>",
      "createIfNotExists": false,
      "connection": "<your-connection-setting-name>",
      "direction": "out"
    }
  ]
}

More samples available here: https://github.com/ealsur/serverless-recipes/tree/master/cosmosdboutputbindings

Matias Quaranta
  • 13,907
  • 1
  • 22
  • 47
  • Hi Matias, I've copy and pasted your code into a new azure function named "HttpTriggerWithSingleDocument" and I've added my cosmos db as an output but I run into this error (https://textuploader.com/dvyxe). I am building this within the azure portal. Do you have any advice? Sorry if my questions are very rookie! C# and API building is still new to me. – caridina Sep 24 '18 at 04:46
  • @Michael when working with the Azure Portal, the syntax differs slightly. I edited my answer to include it. – Matias Quaranta Sep 24 '18 at 10:23
  • thank you Matias! This works! For others testing this - the only change I had to make was add my db configurations and change "classes" to "req" in my functions.json. – caridina Sep 25 '18 at 01:40
-1

Here is an example how to do it. You just need to adjust it to you needs.

    [FunctionName("Function1")]
    public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
    TraceWriter log)
    {
        dynamic data = await req.Content.ReadAsAsync<object>();

        var connectionString = "DbUri";
        var key = "DbKey";

        using (var client = new DocumentClient(new Uri(connectionString), key))
        {
            var collectionLink = UriFactory.CreateDocumentCollectionUri("DbName", "CollectionName");
            await client.UpsertDocumentAsync(collectionLink, data);
        }

        return req.CreateResponse(HttpStatusCode.OK);
    }
majewka
  • 111
  • 1
  • 10
  • 1
    Do not suggest to create DocumentClients within the Function's code, this can lead to Socket Exhaustion, see https://github.com/Azure/azure-functions-host/wiki/Managing-Connections – Matias Quaranta Sep 21 '18 at 16:45
  • 1
    Good point and good to know. I forget about it. It also depends on app plan. But in that link is nice example how to use DocumentClient so add it to my example and we have an answer. – majewka Sep 21 '18 at 18:53