Parse query returning objects based on parameter string length (not the parameter I filter by though) - parse-platform

This has me stumped and I'm pulling my hair out here.
The simple query below finds speeches for the current user:
var _debug = function(cb) {
console.log('_debug')
var DebugParseObject = Parse.Object.extend("Speech");
var debugQuery = new Parse.Query(DebugParseObject);
debugQuery.equalTo("user", _getCurrentUser()); // Incorrect results only occurs when I set the user with this line
debugQuery.find({
success: function(results) {
console.log("Successfully retrieved " + results.length + " scores.");
cb(results);
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
};
The speech object class has the following extra columns
title
body
speech_id
user (pointer)
Here is the weird part: The query will only return the speeches whose body is a string less than about 1000 characters.
As in, I can have Speech A, with a 500 character string in the body field. It will be returned as one of the speeches. BUT if I increase Speech A's body string to about 1500 characters, it will NOT be returned any longer.
I can't understand why.
Some further points
It's only when I filter by the user. If I search for all speeches or query by a different parameter (e.g. title), then the correct amount is returned
This used to work fine yesterday and before
I manually deleted a user earlier (removed the row from the table), while their linked speeches still existed
I changed those speeches' users value from the deleted user id to a new users id
The speeches appear to have the correct user
I tried re-saving the user object on the speech's user property and it didn't do anything
Any help will be great! I feel like I've corrupted the user class when I deleted the user row. But I can't prove it.

The query syntax looks solid and you should be well within the storage limitations of Parse. In case you're curious, there's no explicit limit on string length, but Parse Objects are limited to 128k (except for Parse Files of course).
My guess is that something has gone awry when copying over a different user in place of the one you deleted. Manually changing data and pointers within the browser is always risky and prone to errors.

Related

Redirect to Zoho matched Customer Record with Deluge function

I'm trying to embed a Zoho CRM by iframe into an application that knows only a phone number.
(Ramble: Originally I intended to call the Zoho api to lookup the Contact by phone number and redirect to or load the Contact's Zoho page - but the hosting app doesn't seem to support enough features to accommodate Zoho's OAuth2-only authentication - so I think I'm stuck with Zoho Deluge which I'm finding to be an ATROCIOUS language)
I'm hoping to GET navigate to this Zoho Function with the phone number as a parameter, have it find the unique match, and redirect to the customer details.
response = zoho.crm.searchRecords(
"Contacts",
"", // no criteria - I hope the later parameter
// normalizes better than this would?
1, // first page
2, // of two max results - just to verify uniqueness
"{ phone: '" + phoneNumber + "'}"); // Docs are terrible. Is this the format?
// I also tried "phone:equal:..."
//if (1 < response.size()) { // script errors show up on nonsense line
// return "[Ambiguous]"; // numbers, but this seems to work until later
//} // lines are included - then errors point here
return response; // Works, but useless string output
return response.firstName; // "Invalid collection object found" - but not expected to work
return response.get(0); // 'TEXT' can not be cast to '[KEY-VALUE, TEXT, LIST]' for the function 'get'
return response.get('firstName'); // 'TEXT' can not be cast to '[KEY-VALUE, TEXT, LIST]' for the function 'get'
return response.get(0).firstName; // Improper Statement Error might be due to missing ';' at end of the line or incomplete expression
// openUrl( <string>, <window_type> ); // hoping to get here
I've also tried variations on returning from inside a for each element loop, no luck.
I THINK I've successfully found the user by phone number because I think I indeed get one match, but I can't verify it and I don't know how to derive the url of the customer detail page for the openUrl() call. Do you know how to make progress on this?
The criteria is malformed, and function searchRecords returns a list of maps.
To access the first element of al list you must use .get(0) and get an element of a map .get("First_Name")
The fields are malformed, you must get the API name of the field form crm.zoho.com->setup->API->API names->Contacts
You can use info to debug the response (info response;)
Zoho CRM API Search records
toReturn = "";
response = zoho.crm.searchRecords("Contacts", "Phone:equals:" + phoneNumber, 1, 2);
if (1 < response.size()) {
toReturn = "[Ambiguous]";
} else if (0 == response.size()) {//error triggered if use get(0) of emty list
toReturn = "[None]";
}else {
toReturn = reponse.get(0).get("First_Name");
openUrl("https://crm.zoho.com/crm/org[yourOrgID]/tab/Contacts/" + reponse.get(0).get("id"), "new window");
}
return toReturn;

Firestore transaction produces console error: FAILED_PRECONDITION: the stored version does not match the required base version

I have written a bit of code that allows a user to upvote / downvote recipes in a manner similar to Reddit.
Each individual vote is stored in a Firestore collection named votes, with a structure like this:
{username,recipeId,value} (where value is either -1 or 1)
The recipes are stored in the recipes collection, with a structure somewhat like this:
{title,username,ingredients,instructions,score}
Each time a user votes on a recipe, I need to record their vote in the votes collection, and update the score on the recipe. I want to do this as an atomic operation using a transaction, so there is no chance the two values can ever become out of sync.
Following is the code I have so far. I am using Angular 6, however I couldn't find any Typescript examples showing how to handle multiple gets() in a single transaction, so I ended up adapting some Promise-based JavaScript code that I found.
The code seems to work, but there is something happening that is concerning. When I click the upvote/downvote buttons in rapid succession, some console errors occasionally appear. These read POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 (). When I look at the actual response from the server, I see this:
{
"error": {
"code": 400,
"message": "the stored version (1534122723779132) does not match the required base version (0)",
"status": "FAILED_PRECONDITION"
}
}
Note that the errors do not appear when I click the buttons slowly.
Should I worry about this error, or is it just a normal result of the transaction retrying? As noted in the Firestore documentation, a "function calling a transaction (transaction function) might run more than once if a concurrent edit affects a document that the transaction reads."
Note that I have tried wrapping try/catch blocks around every single operation below, and there are no errors thrown. I removed them before posting for the sake of making the code easier to follow.
Very interested in hearing any suggestions for improving my code, regardless of whether they're related to the HTTP 400 error.
async vote(username, recipeId, direction) {
let value;
if ( direction == 'up' ) {
value = 1;
}
if ( direction == 'down' ) {
value = -1;
}
// assemble vote object to be recorded in votes collection
const voteObj: Vote = { username: username, recipeId: recipeId , value: value };
// get references to both vote and recipe documents
const voteDocRef = this.afs.doc(`votes/${username}_${recipeId}`).ref;
const recipeDocRef = this.afs.doc('recipes/' + recipeId).ref;
await this.afs.firestore.runTransaction( async t => {
const voteDoc = await t.get(voteDocRef);
const recipeDoc = await t.get(recipeDocRef);
const currentRecipeScore = await recipeDoc.get('score');
if (!voteDoc.exists) {
// This is a new vote, so add it to the votes collection
// and apply its value to the recipe's score
t.set(voteDocRef, voteObj);
t.update(recipeDocRef, { score: (currentRecipeScore + value) });
} else {
const voteData = voteDoc.data();
if ( voteData.value == value ) {
// existing vote is the same as the button that was pressed, so delete
// the vote document and revert the vote from the recipe's score
t.delete(voteDocRef);
t.update(recipeDocRef, { score: (currentRecipeScore - value) });
} else {
// existing vote is the opposite of the one pressed, so update the
// vote doc, then apply it to the recipe's score by doubling it.
// For example, if the current score is 1 and the user reverses their
// +1 vote by pressing -1, we apply -2 so the score will become -1.
t.set(voteDocRef, voteObj);
t.update(recipeDocRef, { score: (currentRecipeScore + (value*2))});
}
}
return Promise.resolve(true);
});
}
According to Firebase developer Nicolas Garnier, "What you are experiencing here is how Transactions work in Firestore: one of the transactions failed to write because the data has changed in the mean time, in this case Firestore re-runs the transaction again, until it succeeds. In the case of multiple Reviews being written at the same time some of them might need to be ran again after the first transaction because the data has changed. This is expected behavior and these errors should be taken more as warnings."
In other words, this is a normal result of the transaction retrying.
I used RxJS throttleTime to prevent the user from flooding the Firestore server with transactions by clicking the upvote/downvote buttons in rapid succession, and that greatly reduced the occurrences of this 400 error. In my app, there's no legitimate reason someone would need to clip upvote/downvote dozens of times per seconds. It's not a video game.

Dexie.js - table.delete(id) not working for per-row deletion

i'm just starting out with Dexie, and I seem to be coming unstuck.
I have a small database (less than 1000 rows), and i'm trying to delete each row one-by-one once I know that the row has been sent to a remote API.
I can also successfully save to the table (which is defined by an ID and a column storing a serialised object)
here's my code:
if (online) {
//we query the db and send each event
database.open()
let allEvents = database.events.toCollection()
let total = allEvents.count(function (count) {
console.log(count + ' events in total')
//a simple test to ensure we're seeing the right number of records
})
allEvents.each(function(thisEvent){
//push to remote API
console.log('deleting ' + thisEvent.id)
database.events.delete(thisEvent.id) //<= this doesn't seem to be working
})
}
All of this with the exception of the final delete statement.
Any ideas on how I should fix this? the important thing for me is to delete on a per-row basis.
thanks in advance!
I was experiencing the same problem, and the answer from Eugenia Pais wasn't working for me. So after some tests, I saw the trouble was with the type of the variable: I was using a string, but a number is needed, so this is how I solved it:
function removeRow (primaryKey) {
primaryKey = parseInt(primaryKey);
databaseName.tableName.where('primaryKey').equals(primaryKey).delete().then(function(deleteCount) {
console.log ("Deleted " + deleteCount + " rows");
}).catch(function(error) {
console.error ("Error: " + error);
});
So be aware you are using a number as argument.
The correct way to delete each row should be selecting the specific row and delete it:
database.tableName.where(indexId).equals(indexValue).delete();
The data type of the key is not a problem you could verify it in my example here: example
db.example.where('key').equals('one').delete();
Maybe you are trying to delete by a property that not is an index.

HandsOnTable manualRowMove not updating the DOM

The manualRowMove works fine visually but the underlying dom is not getting updated. After manualRowMove the HOT.getData() is showing the old sequence of data and not the latest sequence after row swap.
Please advise what I am doing wrong or is it a bug.
Turning on manualRowMove and moving rows around does not affect the original data source. HOT.getData() returns the original data source, which is not what the current state of the table is.
Plugins like manualRowMove, manualColMove, columnSorting are just abstractions that sit on top of the data layer. They maintain state on the handsontable object (ie the sortIndex attribute) which basically act as lookup tables from what the authors call "logical index" to "physical index". Read more about it here: https://github.com/handsontable/handsontable/wiki/Understanding-column-sorting-plugin
It seems like you are trying to allow users to sort the order of the table then save that order. You'll have to roll your own func to walk the table and use a method like getDataAtRow() to get data in table order.
the way I fixed this was to use a PHP script to push the new order to the database on the server (there needs to be some < / > swap logic, it isn't that easy in PHP land, but it is doable), in theory you could recollect the grid and "refresh" it but I didn't feel it was necessary. Obviously the original json from my database is listed by the order
afterRowMove: function (oldIndex, newIndex){
$.post('../ajax/partsOrder.php?id=212&new_parts_order=' + newIndex + '&old_parts_order=' + oldIndex, function(data) {
var msg = '';
if (data.err!=0 || data.url==0) {
msg = 'Error: ' + data.err;
}
else {
msg = 'Success: ' + data.msg;
}
console.log('partsOrder.php says: '+msg);
},"json");
}
PS: I think this is a major omission from handontable, I was not impressed that the getData() doesn't simply represent the gui. In fact it's borderline criminal that they even worked on the gui at all without this

How do I use FTSearchSorted to filter a view in XPages?

I'm working on my Excel exporting using Apache POI. I want to be able to include a query string to limit the exported contents, but I either end up getting 0 documents or getting a couple of documents.
When I get a couple of document, it's unclear if the view object is actually modified. It still responds to getEntryCount with 13 documents, but exports just 2 and doesn't print the columnValues for those two. If I don't use the search, it exports the entire view flawlessly.
The server-side javascript has these few relevant lines.
function createWorkbookStreamWithLabels(workbookName,sheetName,fieldList,dbName,viewName,colLabels,totalLabels,queryString){
...a bunch of other code to get a handle to the view...
postValidationError(control,"Entries: " + view.getEntryCount());
var docCount = view.FTSearchSorted(queryString);
postValidationError(control,"Query: " + queryString);
postValidationError(control,"Query count: " + docCount);
if ( docCount == 0) return;
...code that walks the view and prints the contents...
}
My 0 hit queries include:
FIELD lastname CONTAINS e
and
[state] CONTAINS V
The one that gets two hits is simple:
VA
The view is sorted and now the database is full-text indexed. The column containing State is now sorted and click to sort, ascending.
Just for reference, here's the function that displays my status messages.
function postValidationError(control, msg) {
if ((typeof msg) != "string")
return;
var msgObj = new javax.faces.application.FacesMessage(javax.faces.application.FacesMessage.SEVERITY_ERROR, msg, msg);
facesContext.addMessage(control.getClientId(facesContext), msgObj);
control.setValid(false);
}
What have I done wrong?
Update: the problem with my search queries is solved, but my code also seems to have a terrific tendency to crash HTTP or the server.

Resources