Sign In Register

How can we help you today?

Start a new topic
Answered

Modify runtime collection with a template metada collection

Hi!


I tried to modify a runtime collection from a metadata collection. I explain:


When a player is created, I use the "template" meta collection to build a runtime collection for default values. But, I want to keep the player runtime collection updated with the template meta collection when I add/remove fields. So, I use a field "VERSION" to detect when a new version is available.


The update function:

  

function UpdateCollection(runtimeCol, metaCol)
{
    var playerID =  Spark.getPlayer().getPlayerId();
    var updateObject = {};
    //add new keys
    for(var metaKey in metaCol)
    {
        if(metaKey != "_id")
        {
            var found = false;
            for(var runKey in runtimeCol)
            {
                if(runKey == metaKey)
                {
                    found = true;
                    break;
                }
            }
            if( !found)
            {
                 updateObject[metaKey] = metaCol[metaKey];
            }
        }
    }
    runtimeCol.update({"playerID":  playerID }, {"$set" : updateObject}, true, false);
    Spark.setScriptData(collName, runtimeCol);
}

  But I have a error when I tried to update the runtime collection at line:

runtimeCol.update({"playerID": playerID }, {"$set" : updateObject}, true, false);


The error is: TypeError: Cannot find default value for object. (296145-event-GetRuntimeCollection#69)


I have no experience with NoSQL, I am poor in javascript so, don't be too rude! 

Where is my error ?


Thanks!


Best Answer

Dany,


metaCol and runtimeColl parameters are objects of type SparkMongoCollectionReadWrite that you surely obtain using Sparks.runtimeCollection("TheNameOfYourCollection")). You use them to do your CRUD operations.


In your case you want to query the collection so your have to call find() with search parameters like a SELECT FROM WHERE in sql.

You can have more info here


But here what I would have done:

 

//first get your template document, I presume you dont really have one template per player like your code seems to do, because 
//having a template document for each of the players will defeat the concept of template, may be I'm wrong
//I also presume that you have something uniquely identifying your template, i use a "code" property that has a value of DEFAULT

var template = metaCol.findOne({"code": "DEFAULT");

//Again, I presume that you have a runtime collection that has a document (row) for every players in the system with a property named "playerID"
//maybe you decided to use "setScriptData" on the player in that case you would not do the following:

var playerData = runtimeCol.find({"playerID": playerID});

//now you have 2 javascript objects that you can manipulate like any js objects
//go through all the properties of the template objects and assign the values to playerData
for (var prop in template) {
    if (template.hasOwnProperty(prop)) {
        playerData[prop] = template[prop];
    }
}

//you can use update
runtimeCol.update({"playerID":  playerID }, playerData, false, false);
//or save
runtimeCol.save(playerData);

 




This post is a few months old, but I had to do a similar concept, so wanted to post my final code, in hopes it will help someone in the future.  It uses a similar code to Dany, but also allows you to create a new set of stats, if the user is new.  It also only returns the stats themselves, and not the other Collection info.


 

var playerId = Spark.getPlayer().getPlayerId();
var tId = "TemplateStatsApp";

var userStats = Spark.runtimeCollection("UserStats").findOne({"playerId": playerId});
var template = Spark.metaCollection("Templates").findOne({"_id" : tId});
var rtnStats = {};

if(null === userStats){
    Spark.runtimeCollection("UserStats").insert({"playerId": playerId});
    for(var tKey in template){
        if(template.hasOwnProperty(tKey)){
            if("_id" == tKey){continue;}
            rtnStats[tKey] = template[tKey];
        }
    }
    Spark.runtimeCollection("UserStats").update({"playerId":playerId}, {"$set":rtnStats}, true, false);
}
else{
    var newStats = {};
    var nonStats = {};
    
    var newStatCount = 0;
    var nonStatCount = 0;
        
    for(var tKey in template){
        if(template.hasOwnProperty(tKey)){
            if("_id" == tKey){continue;}
            if(null == userStats[tKey] || undefined == userStats[tKey]){
                newStatCount ++;
                newStats[tKey] = template[tKey];
            }
            rtnStats[tKey] = userStats[tKey];
        }
    }
    
    for(var sKey in userStats)
    {
        if(userStats.hasOwnProperty(sKey)){
            if(sKey == "_id" || sKey == "playerId"){continue;}
            if((null == template[sKey] || undefined == template[sKey])){
                nonStatCount ++;
                nonStats[sKey] = userStats[sKey];
            }
        }
    }
    if(newStatCount > 0){
        Spark.runtimeCollection("UserStats").update({"playerId":playerId}, {"$set":newStats}, true, false);
    }
    
    if(nonStatCount > 0){
        Spark.runtimeCollection("UserStats").update({"playerId":playerId}, {"$set":nonStats}, true, false);
    }
}

Spark.setScriptData("userStats", rtnStats)

 

Ok, I figured out how to do like a "Contains" function with JS array functions.

 

 if( Object.keys(runtimeCol).indexOf(metaKey ) != -1) 

 Thanks.

Just one more question and it is related to Javascript.


It exists a function to know if a JS object has a field other than iterate on all fields and verify if the key is present? By example this (the solution for my first question), the code below. This code works but, you know, a "Contains" function should be sexier!


 

    var runtimeCol = Spark.runtimeCollection(collName).findOne({"playerID": Spark.getPlayer().getPlayerId()});
    var metaCol = Spark.metaCollection(collName).findOne({"_id" : idValue});

    var playerID =  Spark.getPlayer().getPlayerId();
    var newValues = {};
    var removedValues = {};
    
    for(var metaKey in metaCol)
    {
        if(metaKey != "_id")
        {
            var found = false;
            for(var runKey in runtimeCol)
            {
                if(runKey == metaKey)
                {
                    found = true;
                    break;
                }
            }
            if( !found)
            {
                //add new keys in runtime from meta collection
                 newValues[metaKey] = metaCol[metaKey];
            }
        }
    }
    
    for(var runKey in runtimeCol)
    {
        if(runKey != "_id" && runKey != "playerID")
        {
            var found = false;
            for(var metaKey in metaCol)
            {
                if(runKey == metaKey)
                {
                    found = true;
                    break;
                }
            }
            if( !found)
            {
                //remove keys in runtime collection but no more in meta collection
                removedValues[runKey] = runtimeCol[runKey];
            }
        }
    }
    //update version
    newValues["VERSION"] = metaCol["VERSION"];
    runtimeCol = Spark.runtimeCollection(collName);
    if(Object.keys(newValues).length > 0 )
        runtimeCol.update({"playerID":  playerID }, {"$set" : newValues}, true, false);
    if(Object.keys(removedValues).length > 0 )
        runtimeCol.update({"playerID":  playerID }, {"$unset" : removedValues}, true, false);
    Spark.setScriptData(collName, runtimeCol);
}

 


Thanks Christian!

Answer

Dany,


metaCol and runtimeColl parameters are objects of type SparkMongoCollectionReadWrite that you surely obtain using Sparks.runtimeCollection("TheNameOfYourCollection")). You use them to do your CRUD operations.


In your case you want to query the collection so your have to call find() with search parameters like a SELECT FROM WHERE in sql.

You can have more info here


But here what I would have done:

 

//first get your template document, I presume you dont really have one template per player like your code seems to do, because 
//having a template document for each of the players will defeat the concept of template, may be I'm wrong
//I also presume that you have something uniquely identifying your template, i use a "code" property that has a value of DEFAULT

var template = metaCol.findOne({"code": "DEFAULT");

//Again, I presume that you have a runtime collection that has a document (row) for every players in the system with a property named "playerID"
//maybe you decided to use "setScriptData" on the player in that case you would not do the following:

var playerData = runtimeCol.find({"playerID": playerID});

//now you have 2 javascript objects that you can manipulate like any js objects
//go through all the properties of the template objects and assign the values to playerData
for (var prop in template) {
    if (template.hasOwnProperty(prop)) {
        playerData[prop] = template[prop];
    }
}

//you can use update
runtimeCol.update({"playerID":  playerID }, playerData, false, false);
//or save
runtimeCol.save(playerData);

 



Login to post a comment