Insert or update record in HighScore table - azure-mobile-services

I have a table that receives high score entries. However, if the user already has an entry in the table (tracked through a GUID field, not the user parameter) I want to update it if the new entry has a better time otherwise don't change the existing record. However, if the user doesn't have a record in the high score table then add a new record. I also have two query parameters to pass to the query.
I want the insert operation to handle this for the table. I have this so far but I get an exception raised when I call InsertAsync(...) on the highscore table
function insert(item, user, request) {
var sql ="select Id from HighScore where PlayerGUID=? AND PlayerBadge=?";
mssql.query(sql, [user.PlayerGUID], [user.PlayerBadge], {
success: function(results) {
if(results.length > 0) {
// leader board record exists so update the current record
// Check the existing record and update it is the new time is better
console.log("Found existing entry");
} else {
// no record exists for this user to insert one
request.execute();
console.log("Found existing entry");
}
}
});
}
Can anyone offer me any assistance with achieving my goal?
Many thanks,
J.

It took some time and some help but here's where I ended up. It works just as I intended it to.
function insert(item, user, request) {
// Store the passed in item object for us when inserting or updating
resultsItem = item;
// Store the request object to allow calld functions to send respond commands
thisRequest = request;
// Retrieve the HighScore table so we can check it for an existing record
hsTable = tables.getTable('HighScore');
// Update the leaderboard
updateLeaderboard(item);
}
// Global variables
var resultsItem, hsTable, thisRequest;
function updateLeaderboard(item){
//Filter the table using the where operator to only include those
// records for the current PlayerGUID and PlayerBadge fields
hsTable.where({
PlayerGUID: item.PlayerGUID,
PlayerBadge: item.PlayerBadge
}).read({
success:updateScore,
error: errorHandler
})
}
function updateScore(results){
if(results.length > 0) {
// If a record already exists then check the PlayerTime
if(results[0].PlayerTime > resultsItem.PlayerTime)
{
// Update the PlayerTime if it is less than the currently saved value
hsTable.update({
id: results[0].id,
PlayerTime: resultsItem.PlayerTime
}, {
success: logSuccess,
error: errorHandler
})
} else {
// Send them OK. Could change this and use the returned code/text to display a custom
// message that tells the user that a previous time is faster.
thisRequest.respond(statusCodes.OK);
}
} else {
// The record for this PlayerGUID and PlayerBadge exists so write one
hsTable.insert({
PlayerName: resultsItem.PlayerName,
PlayerCountry: resultsItem.PlayerCountry,
PlayerTime: resultsItem.PlayerTime,
PlayerBadge: resultsItem.PlayerBadge,
PlayerGender: resultsItem.PlayerGender,
PlayerDOB: resultsItem.PlayerDOB,
PlayerGUID: resultsItem.PlayerGUID
}, {
success: logSuccess,
error: errorHandler
})
}
}
// Called if there is an error
function errorHandler(error){
console.error
("An error occurred trying to update leaderboard infor for player" +
resultsItem.PlayerName);
thisRequest.respond(statusCodes.BAD_REQUEST);
}
//Called if things work out ok.
function logSuccess()
{
thisRequest.respond(statusCodes.OK);
}

Related

Flutter Parse Server Sdk not saving the second object in the table (class)

this function takes a ServicePoint object as argument, which has the following attributes:
adminId (String)
name (String)
serviceType (enum)
I want this function to create a new Table with name: "name+adminId". This is achieved.
Also I want this function to create a new Table (if it is not there already) by the name ServicePoints.
ServicePoints stores the relationship between user (with objectId = adminId) and the new Table.
To achieve this, I set "serviceTable" attribute with value as the new Table created, acting as a pointer.
When I run the code first time, I achieve the required tables. But, when I run the function second time, it doesn't add the new row/record to ServicePoints table.
I don't know why.
UPDATE I found that set ParseObject operation is the culprit. But, to my surprize, it executes successfully for the very first time. But fails every next time. This is really absurd behaviour from parse_server_sdk_flutter.
Future<bool> createServicePoint(ServicePoint servicePoint) async {
String newServicePointName = servicePoint.name + servicePoint.adminId;
var newServiceTable = ParseObject(newServicePointName);
var response = await newServiceTable.save();
if (response.success) {
print('Now adding new row to ServicePoints table');
var servicePointsTable = ParseObject('ServicePoints')
..set<String>("serviceName", servicePoint.name)
..set<String>("adminId", servicePoint.adminId)
..set<String>("serviceType", _typeToLabel[servicePoint.serviceType])
..set<ParseObject>("serviceTable", newServiceTable);
var recentResponse = await servicePointsTable.save();
return recentResponse.success;
} else {
return false;
}
}
If anyone runs into this problem, you need to check the result after saving the ParseObject. If there is error like "Can't save into non-existing class/table", then just go to the dashboard and create the table first.

High frequency calls leads to duplicates with findOrCreate in Waterline & Sails

How to handle high frequency updateOrCreate requests with Waterline in Sails for a Postgresql database ?
I tried to use findOrCreate and then update the item, I tried findOne and then update or create the item, I tried to put a beforeCreate, a beforeValidation hook method to check if the item exists but without any success.
Should I add an error handler to get errors from the unique index and try again?
In the Waterline docs, there is a warning about it but no direction to solve this problem.
Thank you for any tips.
Should I add an error handler to get errors from the unique index and try again?
That's going to be the only option until such time as Waterline implements transactions. Something like:
// This will hold the found or created user
var user;
// Keep repeating until we find or create a user, or get an error we dont expect
async.doUntil(
function findOrCreate(cb) {
// Try findOrCreate
User.findOrCreate(criteria, values).exec(function(err, _user) {
// If we get an error that is not a uniqueness error on the
// attribute we expect collisions on, bail out of the doUntil
if (err &&
(
!err.invalidAttributes["myUniqueAttribute"] ||
!_.find(err.invalidAttributes["myUniqueAttribute"], {rule: 'unique'})
)
) {
return cb(err);
}
// Otherwise set the user var
// It may still be undefined if a uniqueness error occurred;
// this will just cause doUntil to run this function again
else {
user = _user;
return cb();
}
},
// If we have a user, we are done. Otherwise go again.
function test() {return user},
// We are done!
function done(err) {
if (err) {return res.serverError(err);}
// "user" now contains the found or created user
}
});
Not the prettiest, but it should do the trick.

Azure Mobile Services Node.js update column field count during read query

I would like to update a column in a specific row in Azure Mobile Services using server side code (node.js).
The idea is that the column A (that stores a number) will increase its count by 1 (i++) everytime a user runs a read query from my mobile apps.
Please, how can I accomplish that from the read script in Azure Mobile Services.
Thanks in advance,
Check out the examples in the online reference. In the table Read script for the table you're tracking you will need to do something like this. It's not clear whether you're tracking in the same table the user is reading, or in a separate counts table, but the flow is the same.
Note that if you really want to track this you should log read requests to another table and tally them after the fact, or use an external analytics system (Google Analytics, Flurry, MixPanel, Azure Mobile Engagement, etc.). This way of updating a single count field in a record will not be accurate if multiple phones read from the table at the same time -- they will both read the same value x from the tracking table, increment it, and update the record with the same value x+1.
function read(query, user, request) {
var myTable = tables.getTable('counting');
myTable.where({
tableName: 'verses'
}).read({
success: updateCount
});
function updateCount(results) {
if (results.length > 0) {
// tracking record was found. update and continue normal execution.
var trackingRecord = results[0];
trackingRecord.count = trackingRecord.count + 1;
myTable.update(trackingRecord, { success: function () {
request.execute();
});
} else {
console.log('error updating count');
request.respond(500, 'unable to update read count');
}
}
};
Hope this helps.
Edit: fixed function signature and table names above, adding another example below
If you want to track which verses were read (if your app can request one at a time) you need to do the "counting" request and update after the "verses" request, because the script doesn't tell you up front which verse records the user requested.
function read(query, user, request) {
request.execute( { success: function(verseResults) {
request.respond();
if (verseResults.length === 1) {
var countTable = tables.getTable('counting');
countTable.where({
verseId: verseResults[0].id
}).read({
success: updateCount
});
function updateCount(results) {
if (results.length > 0) {
// tracking record was found. update and continue normal execution.
var trackingRecord = results[0];
trackingRecord.count = trackingRecord.count + 1;
countTable.update(trackingRecord);
} else {
console.log('error updating count');
}
}
}
});
};
Another note: make sure your counting table has an index on the column you're selecting by (tableName in the first example, verseId in the second).

How to update the tables in dataclass IBM?

I am trying to update a dataclass table with extra values after inserting some fields in the columns,again if i want insert the details in the same column,its not working,it gets in the second row,could anyone please help me,i want this answer as soon as possible
This is how I updated the row in bluemix by getting a particular column value position. Here is the code.
This is the code for getting the entire values from the bluemix table:
IBMQuery<Company> query = IBMQuery.queryForClass(Company.CLASS_NAME);
query.find().continueWith(new Continuation<List<Company>, Void>() {
public Void then(Task<List<Company>> task) throws Exception {
final List<Company> objects = task.getResult();
// Log if the find was cancelled.
if (task.isCancelled()) {
Log.e(CLASS_NAME, "Exception : Task " + task.toString()
+ " was cancelled.");
}
// Log error message, if the find task fails.
else if (task.isFaulted()) {
Log.e(CLASS_NAME, "Exception : "
+ task.getError().getMessage());
}
// If the result succeeds, load the list.
else {
companyList.clear();
for (IBMDataObject storeObject : objects) {
companyList.add((Company) storeObject);
}
if (task.isCompleted()) {
handler.sendEmptyMessage(2);
}
}
Where Company is the name of the Table and companyList is the company class array list.
After this code is executed the companylist will get all the rows and columns values stored in bluemix from which we can get the required row by using the query
query.whereKeyEqualsTo(User.RegisterName, userName);
query.whereKeyEqualsTo(User.Password, password);
Where User is the table name
RegisterName and Password are static variable defined in the User Class
userName and password is the user defined given inputs.
By getting the position of the required row retrieved in companyList, I do the update in the following way:
Company companyObject=Company.getPosition(position);
companyObject.setName("Something");
companyObject.save() query......
Now the problem is I'm able to do the update properly, but I'm not able to retrieve the table values from bluemix using the code which I mentioned in the top.
// Find a set of objects by class
IBMQuery<Item> queryByClass = IBMQuery.queryForClass(Item.class);
// Find a specific object
IBMQuery<Item> queryForObject = myItem.getQuery();
query.find().continueWith(new Continuation<List<Item>, Void>() {
#Override
public Void then(Task<List<Item>> task) throws Exception {
if (task.isFaulted()) {
// Handle errors
} else {
// do more work
List<Item> objects = task.getResult();
}
return null;
}
});
This code came from http://mbaas-gettingstarted.ng.bluemix.net/android

Updating in Entity Framework - The following objects have not been refreshed because they were not found in the store

I'm saving an Order object using the following code:
public void SaveOrder (string orderNo)
{
using (var se = new StoreEntities)
{
var order = new Order { OrderNumber = orderNo }
try
{
//// Update
if (se.Orders.Any(e => e.OrderNumber == orderNo))
{
se.Orders.Attach(order);
se.ObjectStateManager.ChangeObjectState(order, EntityState.Modified);
}
//// Create
else
{
se.Orders.AddObject(order);
}
se.SaveChanges();
}
catch (OptimisticConcurrencyException){
se.Refresh(RefreshMode.ClientWins, order);
se.SaveChanges();
}
}
}
This works fine when it's a new order and I'm just inserting into the DB.
However, if I'm trying to update an existing order, I get his error:
The following objects have not been refreshed because they were not
found in the store: 'EntitySet=Orders;OrderID=0'.
In the database, the Order table looks like
OrderID | OrderNumber
13 567-87
15 567-93
where OrderID is an Identity key. There are no other rows besides these two as they have been deleted.
What am I doing wrong that I can't update a record?
Looks like you're getting the error because you're attaching the new Order object you've created as if it's an Order which already exists - that's why the OrderID in the error is zero.
Try this:
try
{
//// Update
var existingOrder = se.Orders.FirstOrDefault(e => e.OrderNumber == orderNo);
if (existingOrder != default(Order))
{
existingOrder.DateLastUpdated = DateTime.Now;
se.ObjectStateManager.ChangeObjectState(existingOrder, EntityState.Modified);
}
//// Create
else
{
se.Orders.AddObject(order);
}
se.SaveChanges();
}
Edit
You can update the order details property-by-property, or if you have an Order (or an OrderViewModel, perhaps) with the details you want to update, you could use something like AutoMapper to copy the values for you.