Azure Cosmos DB'de saklı yordamlar, tetikleyiciler ve kullanıcı tanımlı işlevler yazma

UYGULANANLAR: NOSQL

Azure Cosmos DB saklı yordamlar, tetikleyiciler ve kullanıcı tanımlı işlevler (UDF) yazmanıza olanak tanıyan javascript'in dille tümleşik, işlemsel yürütmesini sağlar. Azure Cosmos DB'de NoSQL api'sini kullandığınızda JavaScript kullanarak saklı yordamları, tetikleyicileri ve UDF'leri tanımlayabilirsiniz. Mantığınızı JavaScript'te yazabilir ve veritabanı altyapısının içinde yürütebilirsiniz. Azure portal, Azure Cosmos DB'deki JavaScript sorgu API'sini ve NoSQL SDK'ları için Azure Cosmos DB'yi kullanarak tetikleyiciler, saklı yordamlar ve UDF'ler oluşturabilir ve yürütebilirsiniz.

Saklı yordamı, tetikleyiciyi veya UDF'yi çağırmak için kaydetmeniz gerekir. Daha fazla bilgi için bkz. Azure Cosmos DB'de saklı yordamlar, tetikleyiciler ve kullanıcı tanımlı işlevlerle çalışma.

Not

Bölümlenmiş kapsayıcılar için, saklı yordam yürütülürken istek seçeneklerinde bir bölüm anahtarı değeri sağlanmalıdır. Saklı yordamların kapsamı her zaman bir bölüm anahtarı olarak belirlenmiştir. Farklı bir bölüm anahtarı değerine sahip öğeler saklı yordama görünmez. Bu durum tetikleyiciler için de geçerlidir.

Not

Saklı yordamlar, tetikleyiciler ve UDF'ler de dahil olmak üzere sunucu tarafı JavaScript özellikleri modülleri içeri aktarmayı desteklemez.

İpucu

Azure Cosmos DB saklı yordamlar, tetikleyiciler ve UDF'ler ile kapsayıcı dağıtmayı destekler. Daha fazla bilgi için bkz . Sunucu tarafı işlevselliğiyle Azure Cosmos DB kapsayıcısı oluşturma.

Saklı yordamları yazma

Saklı yordamlar JavaScript kullanılarak yazılır ve Azure Cosmos DB kapsayıcısı içindeki öğeleri oluşturabilir, güncelleştirebilir, okuyabilir, sorgulayabilir ve silebilir. Saklı yordamlar koleksiyon başına kaydedilir ve bu koleksiyonda bulunan herhangi bir belge veya ek üzerinde çalışabilir.

Not

Azure Cosmos DB,saklı yordamlar için farklı bir ücretlendirme ilkesine sahiptir. Saklı yordamlar kod yürütebileceğinden ve herhangi bir sayıda istek birimi (RU) tüketebileceğinden, her yürütme için ön ücret gerekir. Bu, saklı yordam betiklerinin arka uç hizmetlerini etkilememesini sağlar. Önceden ücretlendirilen tutar, önceki çağrılarda betik tarafından tüketilen ortalama ücrete eşittir. İşlem başına ortalama RU'lar yürütmeden önce ayrılmıştır. Çağrıların RU'larda çok fazla varyansı varsa bütçe kullanımınız etkilenebilir. Alternatif olarak, RU ücretlerinin değişkenliğini önlemek için saklı yordamlar yerine toplu veya toplu istekler kullanmanız gerekir.

"Merhaba Dünya" yanıtı döndüren basit bir saklı yordam aşağıdadır.

var helloWorldStoredProc = {
    id: "helloWorld",
    serverScript: function () {
        var context = getContext();
        var response = context.getResponse();

        response.setBody("Hello, World");
    }
}

Bağlam nesnesi, Azure Cosmos DB'de gerçekleştirilebilecek tüm işlemlere erişimin yanı sıra istek ve yanıt nesnelerine de erişim sağlar. Bu durumda, yanıtın gövdesini istemciye geri gönderilecek şekilde ayarlamak için yanıt nesnesini kullanırsınız.

Yazıldıktan sonra saklı yordamın bir koleksiyona kaydedilmesi gerekir. Daha fazla bilgi edinmek için bkz. Azure Cosmos DB'de saklı yordamları kullanma.

Saklı yordamları kullanarak öğe oluşturma

Saklı yordam kullanarak öğe oluşturduğunuzda, öğe Azure Cosmos DB kapsayıcısına eklenir ve yeni oluşturulan öğenin kimliği döndürülür. Öğe oluşturmak zaman uyumsuz bir işlemdir ve JavaScript geri çağırma işlevlerine bağlıdır. Geri çağırma işlevinin iki parametresi vardır: biri işlemin başarısız olması durumunda hata nesnesi için, diğeri ise bir dönüş değeri için, bu durumda oluşturulan nesne. Geri çağırma içinde özel durumu işleyebilir veya hata oluşturabilirsiniz. Geri arama sağlanmazsa ve bir hata varsa Azure Cosmos DB çalışma zamanı bir hata oluşturur.

Saklı yordam, açıklamayı boole değeri olarak ayarlamak için bir parametre de içerir. parametre true olarak ayarlandığında ve açıklama eksikse saklı yordam bir özel durum oluşturur. Aksi takdirde saklı yordamın geri kalanı çalışmaya devam eder.

Aşağıdaki saklı yordam örneği, giriş olarak bir dizi yeni Azure Cosmos DB öğesini alır, Bunu Azure Cosmos DB kapsayıcısına ekler ve eklenen öğelerin sayısını döndürür. Bu örnekte, NoSQL için Hızlı Başlangıç .NET API'sinden ToDoList örneğini kullanıyoruz.

function createToDoItems(items) {
    var collection = getContext().getCollection();
    var collectionLink = collection.getSelfLink();
    var count = 0;

    if (!items) throw new Error("The array is undefined or null.");

    var numItems = items.length;

    if (numItems == 0) {
        getContext().getResponse().setBody(0);
        return;
    }

    tryCreate(items[count], callback);

    function tryCreate(item, callback) {
        var options = { disableAutomaticIdGeneration: false };

        var isAccepted = collection.createDocument(collectionLink, item, options, callback);

        if (!isAccepted) getContext().getResponse().setBody(count);
    }

    function callback(err, item, options) {
        if (err) throw err;
        count++;
        if (count >= numItems) {
            getContext().getResponse().setBody(count);
        } else {
            tryCreate(items[count], callback);
        }
    }
}

Saklı yordamlar için giriş parametreleri olarak diziler

Azure portal'de bir saklı yordam tanımladığınızda, giriş parametreleri her zaman saklı yordama bir dize olarak gönderilir. Bir dize dizisini giriş olarak geçirseniz bile, dizi bir dizeye dönüştürülür ve saklı yordama gönderilir. Bu sorunu geçici olarak çözmek için saklı yordamınızda dizeyi dizi olarak ayrıştıracak bir işlev tanımlayabilirsiniz. Aşağıdaki kod, bir dize giriş parametresinin dizi olarak nasıl ayrıştırılmış olduğunu gösterir:

function sample(arr) {
    if (typeof arr === "string") arr = JSON.parse(arr);

    arr.forEach(function(a) {
        // do something here
        console.log(a);
    });
}

Saklı yordamlar içindeki işlemler

Saklı yordam kullanarak kapsayıcı içindeki öğelere işlem uygulayabilirsiniz. Aşağıdaki örnek, tek bir işlemde iki takım arasında oyuncu takası yapmak için fantezi futbol oyun uygulamasındaki işlemleri kullanır. Saklı yordam, her biri bağımsız değişken olarak geçirilen oynatıcı kimliklerine karşılık gelen iki Azure Cosmos DB öğesini okumaya çalışır. Her iki oyuncu da bulunursa saklı yordam, takımlarını değiştirerek öğeleri güncelleştirir. Yol boyunca herhangi bir hatayla karşılaşılırsa saklı yordam, işlemi örtük olarak durduran bir JavaScript özel durumu oluşturur.

// JavaScript source code
function tradePlayers(playerId1, playerId2) {
    var context = getContext();
    var container = context.getCollection();
    var response = context.getResponse();

    var player1Document, player2Document;

    // query for players
    var filterQuery =
    {
        'query' : 'SELECT * FROM Players p where p.id = @playerId1',
        'parameters' : [{'name':'@playerId1', 'value':playerId1}] 
    };

    var accept = container.queryDocuments(container.getSelfLink(), filterQuery, {},
        function (err, items, responseOptions) {
            if (err) throw new Error("Error" + err.message);

            if (items.length != 1) throw "Unable to find both names";
            player1Item = items[0];

            var filterQuery2 =
            {
                'query' : 'SELECT * FROM Players p where p.id = @playerId2',
                'parameters' : [{'name':'@playerId2', 'value':playerId2}]
            };
            var accept2 = container.queryDocuments(container.getSelfLink(), filterQuery2, {},
                function (err2, items2, responseOptions2) {
                    if (err2) throw new Error("Error" + err2.message);
                    if (items2.length != 1) throw "Unable to find both names";
                    player2Item = items2[0];
                    swapTeams(player1Item, player2Item);
                    return;
                });
            if (!accept2) throw "Unable to read player details, abort ";
        });

    if (!accept) throw "Unable to read player details, abort ";

    // swap the two players’ teams
    function swapTeams(player1, player2) {
        var player2NewTeam = player1.team;
        player1.team = player2.team;
        player2.team = player2NewTeam;

        var accept = container.replaceDocument(player1._self, player1,
            function (err, itemReplaced) {
                if (err) throw "Unable to update player 1, abort ";

                var accept2 = container.replaceDocument(player2._self, player2,
                    function (err2, itemReplaced2) {
                        if (err) throw "Unable to update player 2, abort"
                    });

                if (!accept2) throw "Unable to update player 2, abort";
            });

        if (!accept) throw "Unable to update player 1, abort";
    }
}

Saklı yordamlar içinde sınırlanmış yürütme

Aşağıda, öğeleri Azure Cosmos DB kapsayıcısına toplu olarak içeri aktaran bir saklı yordam örneği verilmiştir. Saklı yordam, içinden boole dönüş değerini createDocumentdenetleyerek sınırlanmış yürütmeyi işler ve ardından toplu işlemler arasında ilerleme durumunu izlemek ve sürdürmek için saklı yordamın her çağrısına eklenen öğelerin sayısını kullanır.

function bulkImport(items) {
    var container = getContext().getCollection();
    var containerLink = container.getSelfLink();

    // The count of imported items, also used as current item index.
    var count = 0;

    // Validate input.
    if (!items) throw new Error("The array is undefined or null.");

    var itemsLength = items.length;
    if (itemsLength == 0) {
        getContext().getResponse().setBody(0);
    }

    // Call the create API to create an item.
    tryCreate(items[count], callback);

    // Note that there are 2 exit conditions:
    // 1) The createDocument request was not accepted.
    //    In this case the callback will not be called, we just call setBody and we are done.
    // 2) The callback was called items.length times.
    //    In this case all items were created and we don’t need to call tryCreate anymore. Just call setBody and we are done.
    function tryCreate(item, callback) {
        var isAccepted = container.createDocument(containerLink, item, callback);

        // If the request was accepted, callback will be called.
        // Otherwise report current count back to the client,
        // which will call the script again with remaining set of items.
        if (!isAccepted) getContext().getResponse().setBody(count);
    }

    // This is called when container.createDocument is done in order to process the result.
    function callback(err, item, options) {
        if (err) throw err;

        // One more item has been inserted, increment the count.
        count++;

        if (count >= itemsLength) {
            // If we created all items, we are done. Just set the response.
            getContext().getResponse().setBody(count);
        } else {
            // Create next document.
            tryCreate(items[count], callback);
        }
    }
}

Saklı yordamlarla zaman uyumsuz/await

Aşağıdaki saklı yordam örneği, bir yardımcı işlevi kullanarak Promises ile kullanırasync/await. Saklı yordam bir öğeyi sorgular ve onu değiştirir.

function async_sample() {
    const ERROR_CODE = {
        NotAccepted: 429
    };

    const asyncHelper = {
        queryDocuments(sqlQuery, options) {
            return new Promise((resolve, reject) => {
                const isAccepted = __.queryDocuments(__.getSelfLink(), sqlQuery, options, (err, feed, options) => {
                    if (err) reject(err);
                    resolve({ feed, options });
                });
                if (!isAccepted) reject(new Error(ERROR_CODE.NotAccepted, "queryDocuments was not accepted."));
            });
        },

        replaceDocument(doc) {
            return new Promise((resolve, reject) => {
                const isAccepted = __.replaceDocument(doc._self, doc, (err, result, options) => {
                    if (err) reject(err);
                    resolve({ result, options });
                });
                if (!isAccepted) reject(new Error(ERROR_CODE.NotAccepted, "replaceDocument was not accepted."));
            });
        }
    };

    async function main() {
        let continuation;
        do {
            let { feed, options } = await asyncHelper.queryDocuments("SELECT * from c", { continuation });

            for (let doc of feed) {
                doc.newProp = 1;
                await asyncHelper.replaceDocument(doc);
            }

            continuation = options.continuation;
        } while (continuation);
    }

    main().catch(err => getContext().abort(err));
}

Tetikleyicileri yazma

Azure Cosmos DB ön tetikleyicileri ve son tetikleyicileri destekler. Ön tetikleyiciler bir veritabanı öğesi değiştirilmeden önce yürütülür ve bir veritabanı öğesi değiştirildikten sonra son tetikleyiciler yürütülür. Tetikleyiciler otomatik olarak yürütülmüyor. Yürütülmesini istediğiniz her veritabanı işlemi için belirtilmelidir. Bir tetikleyici tanımladıktan sonra Azure Cosmos DB SDK'larını kullanarak bir ön tetikleyici kaydedip çağırmanız gerekir.

Ön tetikleyiciler

Aşağıdaki örnekte, oluşturulan Azure Cosmos DB öğesinin özelliklerini doğrulamak için ön tetikleyicinin nasıl kullanıldığı gösterilmektedir. Bu örnekte, yeni eklenen bir öğeye zaman damgası özelliği eklemek için NoSQL için Hızlı Başlangıç .NET API'sindeki ToDoList örneği kullanılmıştır.

function validateToDoItemTimestamp() {
    var context = getContext();
    var request = context.getRequest();

    // item to be created in the current operation
    var itemToCreate = request.getBody();

    // validate properties
    if (!("timestamp" in itemToCreate)) {
        var ts = new Date();
        itemToCreate["timestamp"] = ts.getTime();
    }

    // update the item that will be created
    request.setBody(itemToCreate);
}

Ön tetikleyicilerde giriş parametresi olamaz. Tetikleyicideki istek nesnesi, işlemle ilişkili istek iletisini işlemek için kullanılır. Önceki örnekte, bir Azure Cosmos DB öğesi oluşturulurken ön tetikleyici çalıştırılır ve istek iletisi gövdesi JSON biçiminde oluşturulacak öğeyi içerir.

Tetikleyiciler kaydedildiğinde, birlikte çalıştırabileceği işlemleri belirtebilirsiniz. Bu tetikleyici değeriyle TriggerOperation.Createoluşturulmalıdır, başka bir TriggerOperation deyişle tetikleyicinin değiştirme işleminde kullanılmasına izin verilmez.

Ön tetikleyici kaydetme ve çağırma örnekleri için bkz. ön tetikleyiciler ve son tetikleyiciler.

Son tetikleyiciler

Aşağıdaki örnekte bir son tetikleyici gösterilmektedir. Bu tetikleyici meta veri öğesini sorgular ve yeni oluşturulan öğeyle ilgili ayrıntılarla güncelleştirir.

function updateMetadata() {
    var context = getContext();
    var container = context.getCollection();
    var response = context.getResponse();

    // item that was created
    var createdItem = response.getBody();

    // query for metadata document
    var filterQuery = 'SELECT * FROM root r WHERE r.id = "_metadata"';
    var accept = container.queryDocuments(container.getSelfLink(), filterQuery,
        updateMetadataCallback);
    if(!accept) throw "Unable to update metadata, abort";

    function updateMetadataCallback(err, items, responseOptions) {
        if(err) throw new Error("Error" + err.message);

        if(items.length != 1) throw 'Unable to find metadata document';

        var metadataItem = items[0];

        // update metadata
        metadataItem.createdItems += 1;
        metadataItem.createdNames += " " + createdItem.id;
        var accept = container.replaceDocument(metadataItem._self,
            metadataItem, function(err, itemReplaced) {
                    if(err) throw "Unable to update metadata, abort";
            });

        if(!accept) throw "Unable to update metadata, abort";
        return;
    }
}

Dikkat edilmesi gereken önemli noktalardan biri, Azure Cosmos DB'de tetikleyicilerin işlemsel olarak yürütülmesidir. Son tetikleyici, temel alınan öğenin kendisi için aynı işlemin bir parçası olarak çalışır. Tetikleyici sonrası yürütme sırasında bir özel durum tüm işlemi başarısız olur. İşlenen her şey geri alınır ve bir özel durum döndürülür.

Ön tetikleyici kaydetme ve çağırma örnekleri için bkz. ön tetikleyiciler ve son tetikleyiciler.

Kullanıcı tanımlı işlevler yazma

Aşağıdaki örnek, çeşitli gelir ayraçları için gelir vergisini hesaplamak için bir UDF oluşturur. Bu kullanıcı tanımlı işlev daha sonra sorgunun içinde kullanılır. Bu örneğin amaçları doğrultusunda, aşağıdaki özelliklere sahip Gelirler adlı bir kapsayıcı olduğunu varsayalım:

{
   "name": "Daniel Elfyn",
   "country": "USA",
   "income": 70000
}

Aşağıdaki işlev tanımı, çeşitli gelir ayraçları için gelir vergisini hesaplar:

function tax(income) {
    if (income == undefined)
        throw 'no input';

    if (income < 1000)
        return income * 0.1;
    else if (income < 10000)
        return income * 0.2;
    else
        return income * 0.4;
}

UDF'yi kaydetme ve kullanma örnekleri için bkz. Azure Cosmos DB'de kullanıcı tanımlı işlevlerle çalışma.

Günlüğe Kaydetme

Saklı yordam, tetikleyiciler veya UDF'leri kullanırken, betik günlüğünü etkinleştirerek adımları günlüğe kaydedebilirsiniz. Aşağıdaki örneklerde gösterildiği gibi true olarak ayarlandığında hata ayıklama için bir dize oluşturulurEnableScriptLogging:

let requestOptions = { enableScriptLogging: true };
const { resource: result, headers: responseHeaders} await container.scripts
      .storedProcedure(Sproc.id)
      .execute(undefined, [], requestOptions);
console.log(responseHeaders[Constants.HttpHeaders.ScriptLogResults]);

Sonraki adımlar

Azure Cosmos DB'de saklı yordamları, tetikleyicileri ve UDF'leri yazma veya kullanma hakkında daha fazla bilgi edinin: