3
votes

I'm simply trying to execute the standard example bulkImport sproc for documentDB API and I can't seem to pass it an array of objects. I always get 400 errors despite the documentation giving clear direction to send an array of objects .. very frustrating.

Additional details: Even if I wrap the array in an object with the array under a property of 'items' and include it in my sproc it still errors out saying the same bad request, needs to be an object or JSON-serialized. When I try to do JSON.stringify(docs) before sending it fails to parse on the other side.

Bad Request: The document body must be an object or a string representing a JSON-serialized object.

bulkInsert.js: https://github.com/Azure/azure-documentdb-js-server/blob/master/samples/stored-procedures/BulkImport.js

My Code (using documentdb-util for async):

execProc(docs, insertProc);
async function execProc(docs, insertProc){
    let database = await dbUtil.database('test');
    let collection = await dbUtil.collection(database, 'test');
    let procInstance = await dbUtil.storedProcedure(collection, insertProc);
    try{
        let result = await dbUtil.executeStoredProcedure(procInstance, docs);
        console.log(result);
    } catch(e){
        console.log(e.body)
    }  
}

Header

Object {Cache-Control: "no-cache", x-ms-version: "2017-11-15", User-Agent: "win32/10.0.16299 Nodejs/v8.9.0 documentdb-nodejs-s…", x-ms-date: "Mon, 11 Dec 2017 07:32:29 GMT", Accept:"application/json" authorization: myauth Cache-Control:"no-cache" Content-Type:"application/json" User-Agent:"win32/10.0.16299 Nodejs/v8.9.0 documentdb-nodejs-sdk/1.14.1" x-ms-date:"Mon, 11 Dec 2017 07:32:29 GMT" x-ms-version:"2017-11-15"

Path "/dbs/myDB/colls/myColl/sprocs/myBulkInsert"

Params

Array(3) [Object, Object, Object] length:3

0:Object {id: "0001", type: "donut", name: "Cake", …}

1:Object {id: "0002", type: "donut", name: "Raised", …}

2:Object {id: "0003", type: "donut", name: "Old Fashioned", …}

[{
    "id": "0001",
    "type": "donut",
    "name": "Cake",
    "ppu": 0.55
},
{
    "id": "0002",
    "type": "donut",
    "name": "Raised",
    "ppu": 0.35
},
{
    "id": "0003",
    "type": "donut",
    "name": "Old Fashioned",
    "ppu": 0.25
}]
3

3 Answers

4
votes

The "docs" must be an array of array of params, otherwise, the procedure executor will treat them as multiple params of the procedure, not a single-array-param.

2
votes

the following code works when call storedProcedure to pass argument with array type.

JS:

var docs = [{'id':1},{'id':2}];
executeStoredProcedure(proc, [docs])

C#

var docs = new[] {new MyDoc{id=1, source="abc"}, new MyDoc{id=2, source="abc"}];

dynamic[] args = new dynamic[] {docs};

ExecuteStoredProcedureAsync<int>(
   procLink,
   new RequestOptions {PartitionKey = new PartitionKey("abc")},
   args);

NOTE: you must ensure the 'docs' have the same partition key, and pass partion key in RequestionOptions

1
votes

I had the same problem. I was able to get it to work by Stringify the Array and parse it in the stored procedure. I opened an issue on the github where that code originated as well. Below is what worked for me. Good luck.

---- Stringify Array

var testArr = []
for (var i = 0; i < 50; i++) {
    testArr.push({
        "id": "test" + i
    })
}
var testArrStr = JSON.stringify(testArr)

//pass testArrStr to stored procedure and parse in stored procedure
---- Slightly altered original BulkImport

exports.storedProcedure = {
    id: "bulkImportArray",
    serverScript:function bulkImportArray(docs) {
        var context = getContext();
        var collection = context.getCollection();
        var docsToCreate = JSON.parse(docs)
        var count = 0;
        var docsLength = docsToCreate.length;
        if (docsLength == 0) {
            getContext().getResponse().setBody(0);
        }
        var totals = ""
        function insertDoc(){ 
            var msg = " count=" + count+" docsLength=" +docsLength + " typeof docsToCreate[]=" + typeof docsToCreate+ " length =" + docsToCreate.length
            if(typeof docsToCreate[count] != 'undefined' ) { 

                collection.createDocument(collection.getSelfLink(),
                    docsToCreate[count],
                    function (err, documentCreated) {
                        if (err){
                        // throw new Error('Error' + err.message);
                        getContext().getResponse().setBody(count + " : " + err);
                        }else{ 
                          if (count < docsLength -1) { 
                                count++;    
                                insertDoc();
                                getContext().getResponse().setBody(msg);
                            } else { 
                                getContext().getResponse().setBody(msg);
                            }
                        }
                    });
                 }else{ 
                     getContext().getResponse().setBody(msg);
                 }    

        }
        insertDoc()
    }
}

If you want to test it in the portal Script Explorer I had to create an escaped string i.e.

var testArr = []
  for(var i=200; i<250; i++){
    testArr.push({"id":"test"+i})
  }
  var testArrStr = JSON.stringify(testArr)
   console.log('"'+testArrStr.replace(/\"/g,'\\"') + '"')