2017-02-17 13 views
0

У меня есть два типа документов, листинг и продукты. Листингового объект содержит список продуктов для определенных стран, например:Извлечение нескольких документов в хранимой процедуре (Azure DocumentDB)

Листинг:

{ 
    "Name": "Default", 
    "Countries": { 
    "_default": [ 
     "4QlxAPFcCAAPAAAAAAAAAA==", 
     "4QlxAPFcCAAHAAAAAAAAAA==" 
    ], 
    "US": [ 
     "4QlxAPFcCAAIAAAAAAAAAA==", 
     "4QlxAPFcCAAHAAAAAAAAAA==" 
    ] 
    }, 
    "Type": "Listing", 
    "id": "dfed1839-07c5-482b-81c5-669b1dbcd0b6", 
    "_rid": "4QlxAPFcCAAEAAAAAAAAAA==" 
} 

продукта:

{ 
    "Name": "Widget", 
    "Price": 3.45, 
    "Type": "Product", 
    "_rid": "4QlxAPFcCAAHAAAAAAAAAA==" 
} 

Моя цель состояла в том, чтобы создать хранимую процедуру в коллекции Azure DocumentDB с двумя параметрами, rid и country, которые по существу доставит листинговый документ и документы для этой страны наиболее эффективным образом. Моя презумпция заключается в том, что загрузка документа по его идентификатору ресурса с использованием getContext().getCollection().readDocument(...) будет самым быстрым способом, поэтому попытка создать для этого хранимую процедуру.

Мои попытки были связаны с последовательными вызовами (callback hell?), Используя генератор/итераторы с выходом, а затем с использованием чистого метода Promise. Все попытки дали тот же результат:

Он доставит первый документ, но закончится довольно внезапно после того, как документ был получен.

Для справки, вот моя последняя попытка:

function test(rid, country) { 
    var collection = getContext().getCollection(); 
    var collectionSelfLink = collection.getSelfLink(); 
    var docsLink = collectionSelfLink + "docs/"; 
    var body = getContext().getResponse().setBody; 

    function getDocument(rid) { 
     return new Promise(function(resolve, reject) { 
      var accepted = collection.readDocument(docsLink + rid, (err, doc, opts) => { 
       resolve(doc); 
      }); 

      if (!accepted) 
       reject("Not accepted");    
     }); 
    } 

    getDocument(rid) 
     .then(doc => { 
      body("0. First step"); // set test body 

      // Countries is a Dictionary<string, string[]> with resource ids 
      return doc.Countries[country] || doc.Countries["_default"]; 
     }) 
     // This is how far it gets, resulting in response "1. Documents to fetch: 2" 
     .then(a => body("1. Documents to fetch: " + a.length)) 
     .then(a => a.map(function(productId) { return getDoument(productId); })) 
     .then(a => body("2. It should come this far, right?")) 
     .then(a => Promise.all(a)) 
     .then(a => body(a)) 
     .catch(function(e) { throw new Error(JSON.stringify(e)); }); 
} 
+0

Я не эксперт по обещаниям. Я склонен передавать код своим обратным вызовам или использовать async.js (да, вы можете загрузить его как зависимость внутри sproc), но мне интересно, ваша проблема в строке, которая вызывает 'a.map'. Обертка '. Then' вернет обещание, которое представляет собой набор обещаний с этой точки вперед. Кроме того, вы предпочитаете однолинейные функции в ваших вызовах '.then()', но некоторые из них (вызовы на тело и даже вызов .map могут выполняться в строю. Единственное, что вам действительно нужно для обещания for is async call to readDocument. С другой стороны, это может быть просто стиль программирования обещаний. –

+0

Я пробовал указанную выше цепочку вызовов в REPL с издетой функцией 'getDocument', и это работает. Я также, как уже отмечалось, попытался сделать цикл внутри исходного 'collection.readDocument (...)', и при использовании for-of/for-in/for он завершит половину пути (взяв 2 из 4 элементов, которые у меня были в список) .При использовании '.forEach', он прошел через весь массив, но не дождался, когда вызовы будут выполнены. Мне интересно, как вызывающий определяет, когда« завершать »... –

+0

Насколько велика массив? Как обещает цепочка обещаний ('a.map()') решить, насколько распараллеливается имп lement? Моя новая теория заключается в том, что с циклом for или вашим подходом a.map, вы слишком широко размахиваете и превышаете некоторый ресурс. Возможно, вам придется ограничить распараллеливание подобно методам async.js '... Limit()'. –

ответ

0

Оказывается, что гнездятся на звонки делать фактически работают, если вы изменяете тело ответа часто

Следующая процедура (?) работал как ожидалось:

function test(rid, country) { 
    var collection = getContext().getCollection(); 
    var collectionSelfLink = collection.getSelfLink(); 
    var docsLink = collectionSelfLink + "docs/"; 
    var body = getContext().getResponse().setBody; 

    var accepted = collection.readDocument(docsLink + rid, (err, doc, opts) => { 
     if (err) throw new Error(err.message); 

     // Countries is a Dictionary<string, string[]> with resource ids 
     var offerIds = doc.Countries[country] || doc.Countries["_default"]; 
     var result = []; 

     for (var docId of offerIds) { 
      var subAccepted = 
       collection.readDocument(docsLink + docId, (err, doc, opts) => { 
        if (err) throw new Error(err.message); 

        result.push(doc); 
       }); 

      if (!subAccepted) 
       throw new Error("A subsequent request was not accepted"); 

      body(result); // <-- Note, setting body in each iteration. 
     } 
    }); 

    if (!accepted) 
     throw new Error("The request was not accepted"); 
} 
+0

Следует отметить, что это было не так хорошо, как я ожидал. Выполнение подобных запросов у клиента было даже быстрее *! –