I'm trying to run a procedure which queries documents and adjusts some properties according to some rules which are decided by the parameters that I pass to the query.
function downsample(ageInDays, downsampleFactor) {
var collection = getContext().getCollection();
var responseBody = {
deleted: 0,
message: ""
};
var downsampledDocuments = [];
var count = 0;
collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM root r ' +
'WHERE (DATEDIFF(day, r.EventProcessedUtcTime, GETDATE()) > ' + ageInDays+ 'AND r.downsamplingFactor < ' + downsampleFactor + ')' +
'OR' +
'((DATEDIFF(day, r.EventProcessedUtcTime, GETDATE()) > ' + ageInDays + ' AND r.downsamplingFactor = null )' +
'ORDER BY r.did, r.resourceType ASC',
function (err, documents, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found',
// else perform the downsampling
if (!documents || !documents.length) {
var response = getContext().getResponse();
responseBody.message = "No documents found";
response.setBody(responseBody);
} else {
// We need to take into consideration that in a lot of cases, the data will already be downsampled so we
// example: previous downsampling factor of documents: 4, if a new downsampling is performed on these docs with factor 8 then we need to
var adjustedDownSamplingFactor;
if (documents[0].downsamplingFactor == null) {
adjustedDownSamplingFactor = downsampleFactor;
} else {
adjustedDownSamplingFactor = downsampleFactor / documents[0].downsamplingFactor;
}
var aggregatedDocument = documents[0];
var documentValueSum = 0;
var documentCount = 0;
var aggregatedDocumentValue = 0;
for(doc in documents){
if(!aggregatedDocument){
aggregatedDocument = doc;
}
if(documentCount >= adjustedDownSamplingFactor || aggregatedDocument.did !== doc.did || aggregatedDocument.resourceType !== doc.resourceType){
// preparing aggregated document
aggregatedDocumentValue = documentValueSum / documentCount;
aggregatedDocument.value = aggregatedDocumentValue;
aggregatedDocument.downsamplingFactor = downsampleFactor;
//Adding the downsampled data to the Array which will be uploaded to the cosmosdb
downsampledDocuments.push(aggregatedDocument);
aggregatedDocument = null;
documentCount = 0;
documentValueSum = 0;
continue;
}
documentValueSum += doc.value;
documentCount++;
}
var response = getContext().getResponse();
tryDelete(documents);
// Call the CRUD API to create a document.
tryCreate(downsampledDocuments[count], callback);
responseBody.message = "Downsampling was succesful"
response.setBody(responseBody);
}
});
So I'm not passing any documents to the query so I don't know which partition key I would have to supply to the stored procedure. Is there any way in which I can avoid having to supply a partition key? I'm calling this stored procedure from an API but keep getting a message that I should supply a partition key.
Is there any way in which I can avoid having to supply a partition
key?
Unfortunately no. You have to supply a partition key value.
Related
I'm trying to get some for Loops running inside a google cloud functions everytime I delete my /users node.
This is the code I'm using
exports.deleteUserAssets = functions.database.ref('/users/{userId}').onWrite((change, context) => {
const beforeData = change.before.val();
const afterData = change.after.val();
const userBuildings = Object.keys(beforeData.isAdmin); // get the buildings of the user stored in the user/userId/isAdmin node .. so far so good
const userId = beforeData.userIDforCloudFunctions; // I'm getting this from a /users/userid/userIDforCloudFucntions node ...so far so good (i've been logging it to confirm)
// making sure it was a delete operation ... so far so good
if (afterData !== null) {
return 0;
}
else {
// on each building
for (var i = 0; i < userBuildings.length; i++) {
let eachBuilding = [userBuildings[i]]
// HERE IS WERE THE PROBLEM IS: Trying to delete all depts + rooms + doors
admin.database().ref('/buildings/' + eachBuilding)
.child("hasDepts")
.once("value")
.then(function(snapshot) { // This is where it goes south – snapshot is returning null
snapshot.forEach(function(childSnapshot) {
var deptKeyString = childSnapshot.key; // will try to get the keys of the departments stored under this space
var deptsOnNode = admin.database().ref('/depts/' + deptKeyString);
deptsOnNode.remove(); // and use the keys to delete each of the depts on depts
});
});
admin.database().ref('/buildings/' + eachBuilding).set({}); // this is working
admin.database().ref('/buildingsUserUid/' + userId + '/' + eachBuilding).remove(); // this is working
}
}
return 0;
});
The snapshot of admin.database().ref('/buildings/' + eachBuilding).child("hasDepts") is returning null.
How can I get to it? Besides admin.database().ref() I've tried to reach it with firebase.database().ref() which is the command/object i use to get this running on frontend functions. I've also tried functions.database() with no result.
Taking in consideration what Doug Stevenson mentioned in his second comment:
exports.deleteUserAssets = functions.database.ref('/users/{userId}').onDelete((change, context, event) => {
const beforeData = change.before.val(); // data before the write (data of all the doors child nodes)
const afterData = change.after.val(); // data before the write (data of all the doors child nodes)
const userBuildings = Object.keys(beforeData.isAdmin); // get the buildings of the user
const userId = beforeData.userIDforCloudFunctions;
// make sure user was deleted
if (afterData !== null) {
return 0;
}
else {
// on each building
for (var i = 0; i < userBuildings.length; i++) {
let eachBuilding = [userBuildings[i]]
// Need to RETURN the whole chain of promises
return admin.database().ref('/buildings/' + eachBuilding)
.child("hasDepts")
.once("value")
.then(function(snapshot) {
console.log(snapshot.val()) // this now works
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val()) // this works as well
var deptKeyString = childSnapshot.key; // get the keys of the departments stored under this space
var deptsOnNode = admin.database().ref('/depts/' + deptKeyString);
// and you can keep on going deeper if you return promises
return deptsOnNode
.child('hasRooms')
.once('value')
.then(function(grandchildSnapshot){
console.log(grandchildSnapshot.val())
grandchildSnapshot.forEach(function(grandGrandchildSnapshot){
var roomKeyString = grandGrandchildSnapshot.key;
var roomsOnDepts = admin.database().ref('/rooms/' + roomKeyString);
admin.database().ref('/roomOwners/' + userId + '/' + roomKeyString).remove();
// and return again here...
return roomsOnDepts
.child('hasDoors')
.once('value')
.then(function(grandgrandGrandchildSnapshot){
grandgrandGrandchildSnapshot.forEach(function(grandgrandGrandchildSnapshot){
var doorKeyString = grandgrandGrandchildSnapshot.key;
var doorsOnRooms = admin.database().ref('/doors/' + doorKeyString);
doorsOnRooms.remove();
let clipOwners = admin.database().ref('/clipOwners/' + doorKeyString);
clipOwners.remove();
})
roomsOnDepts.remove();
})
})
deptsOnNode.remove(); // use the keys to delete the depts on depts main Node
})
});
admin.database().ref('/buildings/' + eachBuilding).set({});
admin.database().ref('/buildingsUserUid/' + userId + '/' + eachBuilding).remove();
});
}
}
return 0;
});
I have a callable function that should return a value, but the only thing ever returned is null. Below is the current version of the function. I have also tried having a return on the first promise (the original once call), and at the end in another then returning the GUID. It actually returned data in that case, but it returned immediately and the GUID was empty.
How can I accomplish my goal and still return the GUID? I don't know when the function is called if I will use a new GUID that I generate, or one that already exists in the database.
There is a similar question here: Receiving returned data from firebase callable functions , but in that case it was because he never returned a promise from the function. I am returning a promise on all code paths. Unless I have to return the initial promise from the once call? In which case, how can I return the GUID when I don't know it yet?
I am also trying to throw an error in a couple of places and the error shows up in the logs for the function, but is never sent to the client that called the function.
I am going off of the examples here: https://firebase.google.com/docs/functions/callable
Sorry for the code bomb.
Calling the function:
var newGame = firebase.functions().httpsCallable('findCreateGame');
newGame({}).then(function(result) {
// Read result of the Cloud Function.
//var sGameID = result.data.guid;
console.log(result);
}).catch(function(error) {
console.log(error);
});
Function:
exports.findCreateGame = functions.https.onCall((data, context) => {
console.log("findCurrentGame Called.")
/**
* WHAT NEEDS DONE
*
*
* Pull in user's information
* Determine their win/loss ratio and search for a game using transactions in either low medium or high queue
* If there are no open games in their bracket, search the one above, then below
* If no open games anywhere, create a new game in their bracket
* If an open game is found, write the UID to the game and add the game's ID to the user's profile
*
*/
var uid = context.auth.uid;
var section = "";
var sUsername = "";
var sProfilePic = "";
var currentGames = null;
var sGUID = "";
//Get the user's info
var userref = admin.database().ref('users/' + uid);
userref.once("value", function(data) {
var ratio = 0;
var wins = parseInt(data.val().wins);
var losses = parseInt(data.val().losses);
var lives = parseInt(data.val().lives);
if (lives < 1){
//This user is out of lives, should not have been able to get here
//Throw an exception so that we can see why it failed
throw new functions.https.HttpsError('permission-denied', 'You do not have enough lives to start a new game.');
}
sUsername = data.val().username;
sProfilePic = data.val().profilepicture;
//Handle if they have no losses
if (losses == 0){
ratio = 100;
} else {
ratio = (wins / losses) * 100;
}
//If they have played less than 5 games, put them in noob tier
if (wins + losses < 5){
ratio = 0;
}
if (ratio <= 33){
section = "noob";
} else if (ratio > 33 && ratio <= 66){
section = "average";
} else {
section = "expert";
}
}).then(() => {
//Get all of the games this user is currently in
admin.database().ref('games').orderByChild(uid).once('value', function(data) {
currentGames = data.val();
}).then(() => {
//Generate a new GUID in case we need to set up a new game
sGUID = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
var queueref = admin.database().ref('gamequeue/' + section);
queueref.transaction(function(currentGUID) {
if (currentGUID == null){
//Write our GUID in the queue
return sGUID;
} else {
//Get the id of the game we just got
sGUID = currentGUID
return null;
}
}).then((res) => {
if (res.snapshot.val() != null){
//This means we are creating the game lobby
//Generate a new answer
var newAnswer = "";
while (newAnswer.length < 4){
var temp = Math.floor(Math.random() * 9) + 1;
temp = temp.toString();
if (!newAnswer.includes(temp)){
newAnswer += temp;
}
}
var obj = {username: sUsername, score: 0, profilepicture: sProfilePic};
return admin.database().ref('games/' + sGUID).set({id: sGUID, requestor: uid, [uid]: obj, answer: newAnswer, turn: uid, status: 'pending'}).then(() => {
return {guid: sGUID};
});
} else {
//We found a game to join
//If we are in a duplicate request situation, make sure the GUID is a string
if (typeof(sGUID) != 'string'){
sGUID = Object.keys(sGUID)[0];
}
//Make sure we didn't find our own game request
if (currentGames[sGUID] != null){
//Add this GUID back to the queue, we shouldn't have removed it
return admin.database().ref('gamequeue/' + section + '/' + sGUID).set('');
//Throw an exception that says you can only have one open game at a time
throw new functions.https.HttpsError('already-exists', 'We are still finding a match for your last request. You are only allowed one open request at a time.');
} else {
//Get the current game info
admin.database().ref('games/' + sGUID).once('value', function(data) {
var sRequestor = data.val().requestor;
var sOpponentUsername = data.val()[sRequestor].username;
var sOpponentProfilePic = data.val()[sRequestor].profilepicture;
//Write all of our info to the game
return admin.database().ref('games/' + sGUID).update({[sRequestor]: {opponentusername: sUsername, opponentprofilepicture: sProfilePic}, [uid]: {username: sUsername, score: 0, opponentusername: sOpponentUsername, opponentprofilepicture: sOpponentProfilePic}, status: 'active'}).then(() => {
return {guid: sGUID};
});
});
}
}
});
});
})
});
The documentation for callable functions explains:
To return data after an asynchronous operation, return a promise. The
data returned by the promise is sent back to the client.
You have many asynchronous operations that must be chained together. A return needs to be added to each of these statements (as shown):
return userref.once("value", function(data) {...
return admin.database().ref('games').orderByChild(uid).once('value', function(data) {...
return queueref.transaction(function(currentGUID) {...
return admin.database().ref('games/' + sGUID).once('value', function(data) {...
I'm trying to make a REST connection to Amazon S3 and I'm struggling to implement the signature V4 process. I know, Amazon also provides a JavasScript an sdk, but I need to get it to work by myself.
To test my code, I'm using the AWS testsuite and my code successfully returns the expected results. However, all my real life attempts fail because of signature does not match error.
The issue for me is, the examples don't include any headers except host and date which is kind of straightforward. I'm having to pass the "full monty" including access tokens and I'm sure there is a problem with the headers.
Here is what I'm doing on click of a button:
function simpleS3Scenario(my_storage) {
return new RSVP.Queue()
.push(function () {
return my_storage.allDocs();
})
.push(function (my_alldocs_response) {
console.log("initial ALLDOCS")
console.log(my_alldocs_response);
})
...
};
allDocs will eventually run into this:
S3Storage.prototype.buildQuery = function (param, options) {
var context = this,
url = this._base_url,
url_path = this._bucket + "/",
headers,
requestText;
return new RSVP.Queue()
.push(function () {
return SigV4_custom.makeSignedRequest(
"GET",
url + url_path,
'',
{"max-key": 100, "prefix": "sample_folder"},
{},
context._region,
"s3",
"AWS4-HMAC-SHA256",
context._secret_access_key,
context._access_key,
context._session_token
);
})
.push(function (e) {
console.log("YUPI");
console.log(e);
})
...
which enters my request signature handler, which is based on this (not working with Signature v4) Amazon example | code: sigv4.js:
(sorry, not shortened yet, scroll down to ENTRY POINT, then work yourself up)
var SigV4_custom = new function(){
this.createCanonicalRequest = function(){};
this.createStringToSign = function(){};
this.generateSignatureAndSign = function(){};
this.makeSignedRequest = function(){};
}
// trim polyfill
String.prototype.trim = String.prototype.trim || function () {
var start = -1,
end = this.length;
while( this.charCodeAt(--end) < 33 );
while( this.charCodeAt(++start) < 33 );
return this.slice( start, end + 1 );
};
// =============== generator funtions =================
// generate query parameter string for canonical request
function generateCanonicalQueryString(my_parameter_dict) {
var canonical_query_string = '',
encoded_parameter_dict = {},
key_list = [],
key,
value,
i,
i_len;
// set and encode
for (key in my_parameter_dict) {
if (my_parameter_dict.hasOwnProperty(key)) {
value = my_parameter_dict[key];
if (typeof value === "object") {
encoded_parameter_dict[encodeURIComponent(key)] =
encodeURIComponent(value[0]) + '&';
// Append each additional value to the query parameter
for (i = 0, i_len = value.length; i < i_len; i += 1) {
encoded_parameter_dict[encodeURIComponent(key)] +=
encodeURIComponent(key) + '=' + encodeURIComponent(value[i]) +'&';
}
encoded_parameter_dict[encodeURIComponent(key)] =
encoded_parameter_dict[encodeURIComponent(key)].slice(0,-1);
} else {
encoded_parameter_dict[encodeURIComponent(key)] =
encodeURIComponent(value);
}
}
}
// then fill key_list
for (key in encoded_parameter_dict) {
if (encoded_parameter_dict.hasOwnProperty(key)) {
key_list.push(key);
}
}
key_list.sort();
for (i = 0, i_len = key_list.length; i < i_len; i += 1) {
canonical_query_string +=
key_list[i] + "=" + encoded_parameter_dict[key_list[i]] + "&";
}
return canonical_query_string.slice(0, -1);
}
// generate canonical header string
function generateCanonicalHeaderString(my_header_dict, is_signature) {
var canonical_header_string = '',
encoded_header_dict = {},
header_list = [],
header,
value,
trimmed_value,
header_lowercase,
i,
i_len,
separator,
connector,
format,
cutoff;
if (is_signature) {
cutoff = -1;
separator = ":";
connector = "\n";
format = function (my_value) {
return my_value.toLowerCase();
}
} else {
cutoff = -1;
separator = "=";
connector = "&";
format = function (my_value) {
return my_value;
}
}
// Take header keys and put in array, then sort and build
for (header in my_header_dict) {
if (my_header_dict.hasOwnProperty(header)) {
header_lowercase = format(header);
value = my_header_dict[header];
trimmed_value = undefined;
//XXX there are not only strings in the headers...
if (value.trim) {
trimmed_value = value.trim();
}
if (encoded_header_dict[header_lowercase] === undefined) {
encoded_header_dict[header_lowercase] = trimmed_value || value;
header_list.push(header_lowercase);
} else {
encoded_header_dict[header_lowercase] += "," + trimmed_value || value;
}
}
}
header_list.sort();
for (i = 0, i_len = header_list.length; i < i_len; i += 1) {
canonical_header_string +=
header_list[i] +
separator +
encoded_header_dict[header_list[i]] +
connector;
}
canonical_header_string = canonical_header_string.slice(0, cutoff);
return canonical_header_string;
}
// generate the signed header string
function generateSignedHeaderString(my_header_dict) {
var signed_header_string = '',
header_list = [],
header,
header_lowercase,
i,
i_len;
for (header in my_header_dict) {
if (my_header_dict.hasOwnProperty(header)) {
header_list.push(header.toLowerCase());
}
}
header_list.sort();
for (i = 0, i_len = header_list.length; i < i_len; i += 1) {
signed_header_string += header_list[i] + ';';
}
return signed_header_string.slice(0, -1);
}
// returns a timestamp in the YYYYMMDD'T'HHmmSS'Z' format, for SigV4 calls
function generateTimestamp() {
var date = new Date();
function bump(my_time_parameter) {
if (my_time_parameter.length === 1) {
return '0' + my_time_parameter;
}
return my_time_parameter;
}
// Assemble date, bump single digits to doubles
// validation: return "20130524T000000Z";
return date.getUTCFullYear() +
bump((date.getUTCMonth()+1).toString()) + // month
bump(date.getUTCDate().toString()) + // day
'T' +
bump(date.getUTCHours().toString()) + // hour
bump(date.getUTCMinutes().toString()) + // minute
bump(date.getUTCSeconds().toString()) + // second
'Z';
}
// generate the credential scope
function generateCredentialScope(my_timestamp, my_region, my_service) {
return my_timestamp.substring(0, 8) + "/" +
my_region + "/" +
my_service + "/" +
"aws4_request";
}
// ================== CORE methods ==================
SigV4_custom.createStringToSign = function (request_time, credential_scope,
canonical_request, signing_algorithm) {
// Step 1: designate the algorithm (for SigV4 it is SHA256)
// Step 2: designate the RequestDate (already done, passed to function)
// Step 3: create credentialScope (already done, passed to funtion)
return signing_algorithm + '\n' +
request_time + '\n' +
credential_scope + '\n' +
canonical_request;
}
SigV4_custom.generateSignatureAndSign = function(secret, request_time, region,
service, string_to_sign) {
var datestamp = request_time.substring(0, 8),
hash_date,
hash_region,
hash_service,
hash_signing,
request = 'aws4_request';
hash_date = CryptoJS.HmacSHA256(datestamp, 'AWS4' + secret, {asBytes: true});
hash_region = CryptoJS.HmacSHA256(region, hash_date, {asBytes: true});
hash_service = CryptoJS.HmacSHA256(service, hash_region, {asBytes: true});
hash_signing = CryptoJS.HmacSHA256(request, hash_service, {asBytes: true});
// sign and return
return CryptoJS.HmacSHA256(string_to_sign, hash_signing)
.toString(CryptoJS.enc.Hex);
}
SigV4_custom.createCanonicalRequest = function(method, pathname,
parameter_dict, header_dict, hashed_payload) {
var canonical_request = "",
http_request_method = "",
canonical_url = "",
canonical_query_string = "",
canonical_header_string = "",
signed_header_string = "",
payload;
// Step 1: Start with the HTTP request method
http_request_method = method;
// Step 2: Add the canonicalUrl parameter
canonical_url = pathname;
// Step 3: Add the canonicalQueryString parameter
canonical_query_string = generateCanonicalQueryString(parameter_dict);
// Step 4: Add the canonicalHeaders parameter
canonical_header_string = generateCanonicalHeaderString(header_dict, true);
// Step 5: Add the signedHeaders parameter
signed_header_string = generateSignedHeaderString(header_dict);
// Step 6: Add the hashed payload
payload = hashed_payload;
// Step 7: string together the canonicalRequest from steps 1 through 6
canonical_request += http_request_method + '\n' +
canonical_url + '\n' +
canonical_query_string + '\n' +
canonical_header_string + '\n\n' +
signed_header_string + '\n' +
payload;
return canonical_request;
};
// =================== ENTRY POINT ===================
// Call this function to make a SigV4 REST API call.
SigV4_custom.makeSignedRequest = function (method, path, payload,
parameter_dict, header_dict, region, service, signing_algorithm,
secret_access_key, access_key_id, security_token) {
var link = document.createElement("a"),
request_time = generateTimestamp(),
scope = generateCredentialScope(request_time, region, service),
authorization_string,
canonical_request,
string_to_sign,
request_uri,
signature;
// link is a location object, set its href to use its properties
link.href = path;
// set all headers, then generate a canonical_request
// payload must be hashed here, as this may vary and the hash is required
// for canonization and as a header. Empty payloads will hash an empty "".
header_dict['X-Amz-Content-Sha256'] = CryptoJS.SHA256(payload)
.toString(CryptoJS.enc.Hex);
header_dict['host'] = link.hostname;
header_dict['X-Amz-Date'] = request_time;
header_dict['X-Amz-Credential'] = access_key_id + "/" + scope;
header_dict['X-Amz-Security-Token'] = security_token;
header_dict['X-Amz-Algorithm'] = signing_algorithm;
header_dict['X-Amz-Expires'] = 86400;
// Task 1: Compute the canonical request
canonical_request = CryptoJS.SHA256(
this.createCanonicalRequest(
method,
link.pathname,
parameter_dict,
header_dict,
header_dict['X-Amz-Content-Sha256']
)
)
.toString(CryptoJS.enc.Hex);
// delete host because it is also in the headers (needs to be in both
// initially to create the canonicalization)
delete parameter_dict['host'];
// Task 2: Create the string to sign using the encoded canonical request
string_to_sign = this.createStringToSign(
request_time,
scope,
canonical_request,
signing_algorithm
);
// Task 3: Create the signature using the signing key and the string to sign
signature = this.generateSignatureAndSign(
secret_access_key,
request_time,
region,
service,
string_to_sign
);
// Task 4: Build the authorization header
header_dict['Authorization'] = header_dict['X-Amz-Algorithm'] +
" Credential=" + header_dict['X-Amz-Credential'] + "," +
"SignedHeaders=" + generateSignedHeaderString(header_dict) + "," +
"Signature=" + signature;
// Task 5: Make the request (through jIO)
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
"method": method,
"url": path,
"headers": header_dict
});
});
}
So when I run the Amazon samples with just two headers through this, I get correct signature and authorization header at the end. I believe I'm doing something wrong with the headers, especially which headers should go into the signature, but I can't find any good help online and am stuck on this for a few days now.
Question:
Maybe someone has an idea from looking at how I'm handling the headers, why my requests to AWS fail due to non-matching signature?
THANKS!
Edit:
I have used this Amazon Example page for deriving signatures. If I run the sample inputs through my createSignatureAndSign method, I can produce the same outputs. So my signature is correct an secret are correct.
I am trying to collect the unique json data, I mean if the key exists the update its value. But not succeed to update the value of existing key.
var fpr_data = [{"rfr_id":"7","user_id":"5","fp_id":"10","raw_id":"3","raw_qty":"20.00","raw_wastage":"2","raw_name":"Buttons"},
{"rfr_id":"9","user_id":"5","fp_id":"10","raw_id":"4","raw_qty":"500.00","raw_wastage":"0","raw_name":"Yarn"},
{"rfr_id":"8","user_id":"5","fp_id":"10","raw_id":"5","raw_qty":"2.00","raw_wastage":"1","raw_name":"Needle"},
{"rfr_id":"7","user_id":"5","fp_id":"10","raw_id":"3","raw_qty":"20.00","raw_wastage":"2","raw_name":"Buttons"}];
var qty = 2, coll={}, _qty=0.00,_wastage=0.00;
// Filter and modify JSON data
$.each(fpr_data, function(i, data) {
_qty = data.raw_qty * qty;
_wastage = data.raw_wastage * qty;
// Next time add on existing keys
if( coll[data.raw_id] == data.raw_id ) {
var q = coll[data.raw_id].qty + _qty;
var w = coll[data.raw_id].wastage + _wastage;
coll[data.raw_id] = {"qty":q, "wastage":w};
}
else {
coll[data.raw_id] = {"qty":_qty, "wastage":_wastage};
}
});
console.log(coll);
In fpr_data there is raw_id that i want to collect unique ids and if the raw_id found in object then update its qty and wastage with raw_qty and raw_wastage. I got Unique JSON data but quantity and wastage are not getting update. What wrong i have done? You can find the same codes in fiddle and check the result in console.
Expected: The value of qty in 3 should be 80
JSFIDDLE
Below condition will not give you correct comparison, when object already exists in array.
if( coll[data.raw_id] == data.raw_id ) {
I think you should just do:
if(coll[data.raw_id]) {
If I understand you correctly try this example
if(coll[data.raw_id]) {
var q = coll[data.raw_id].qty + _qty;
var w = coll[data.raw_id].wastage + _wastage;
coll[data.raw_id] = {"qty":q, "wastage":w};
}
else {
coll[data.raw_id] = {"qty":_qty, "wastage":_wastage};
}
You use jQuery, so enjoy the jQuery.extend() function :
var fpr_data = [{"rfr_id":"7","user_id":"5","fp_id":"10","raw_id":"3","raw_qty":"20.00","raw_wastage":"2","raw_name":"Buttons"},{"rfr_id":"9","user_id":"5","fp_id":"10","raw_id":"4","raw_qty":"500.00","raw_wastage":"0","raw_name":"Yarn"},{"rfr_id":"8","user_id":"5","fp_id":"10","raw_id":"5","raw_qty":"2.00","raw_wastage":"1","raw_name":"Needle"}, {"rfr_id":"7","user_id":"5","fp_id":"10","raw_id":"3","raw_qty":"20.00","raw_wastage":"2","raw_name":"Buttons"}];
console.log(fpr_data);
var qty = 2, coll={}, _qty=0.00,_wastage=0.00;
// Filter and modify JSON data
$.each(fpr_data, function(i, data) {
_qty = data.raw_qty * qty;
_wastage = data.raw_wastage * qty;
// Next time add on existing keys
var currentObj = coll[data.raw_id]; // Try not to repeat yourself ;-)
if( currentObj == data.raw_id ) {
var q = currentObj.qty + _qty;
var w = currentObj.wastage + _wastage;
console.log(data);
coll[data.raw_id] = $.extend(data, {"qty":q, "wastage":w});
}
else {
coll[data.raw_id] = $.extend(data, {"qty":_qty, "wastage":_wastage});
}
});
console.log(coll);
I hope this is what you were looking for.
I write some simple app for windows 8 Metro UI with javascript. Because natural method from microsoft to use Sqlite with Javascript in Metro UI. I use 'doo' wrapper:
dooWrapper SQLite (github)
I create a method :
function addSomething(name) {
var dbPath = Windows.Storage.ApplicationData.current.localFolder.path + '\\a_db.sqlite';
SQLite3JS.openAsync(dbPath).then(function (db) {
return db.runAsync("INSERT INTO STH (nazwa) VALUES (:name)", { name: name }).
done(function () {
console.log('Add sth : ' + name);
db.close();
}, function (error) {
if (db) {
db.close();
}
console.log('ERROR Adding sth' + error.message);
})
});
}
I get error 'database is locked' I read about this error in documentation. But I have one question is other solution to add more rows without create 'insert' function with collections argument something like that : insert (array) ? I just want to use that function n-times without this error. That's possible?
Yes,it possible...i also got this error before....For that you just need to establish the database connection once...i have used this in my app and its working fine.
If there is no need of closing your db then then open database once like..
Add this code to default.js file
var myDatabase; //Global Variable
var dbPath = Windows.Storage.ApplicationData.current.localFolder.path + '\\db.sqlite';
//Create Table
SQLite3JS.openAsync(dbPath).then(function(db) {
myDatabase=db;
return db.runAsync('CREATE TABLE Item (name TEXT, price REAL, id INT PRIMARY KEY)');
});
Then you just need to use below code
// For Insert
return myDatabase.runAsync('INSERT INTO Item (name, price, id) VALUES ("'+ array[i].name+'", "48484", 1);
For array
var dbPromises = [];
var testArray = [];
//only for test purpose
//You can pass your array here directly
for (var a = 0; a < 300; a++) {
var obj = {
name: "Mango"+a,
price: 100+a,
id: a
};
testArray.push(obj);
}
for (var i = 0; i < testArray.length; i++) {
var query = 'INSERT OR REPLACE INTO Item (name, price, id) VALUES ("' + testArray[i].name + '",' + testArray[i].price + ',' + testArray[i].id + ')';
dbPromises.push(globalDatabase.allAsync(query));
}
WinJS.Promise.join(dbPromises).then(function () {
debugger;
}, function(err) {
debugger;
});
Above code is used only for less array size...bcz its taking too much time for insertion...
For fasst execution you should replace just below code
for (var i = 0; i < testArray.length; i++) {
var val = '("' + testArray[i].name + '",' + testArray[i].price + ',' + testArray[i].id + '),';
query = query + val;
if ((i + 1) % 300 == 0 || (i + 1) == testArray.length) {
query = query.replace(/,$/, "");
dbPromises.push(globalDatabase.allAsync(query));
query = 'INSERT OR REPLACE INTO Item (name, price, id) VALUES ';
}
}