I am trying to save a list of parseobjects using save all in background. To avoid duplices in the table, I am querying for the already existing rows and deleting them if any and save the new copy.
The ParseObject.saveAllInBackground gets execued, the callbacks are getting called, but no rows are getting saved.
I am sure the list I am passing to the saveAllInBackground has parseObjects.
I debugged the methods, the flow runs as intended.
Update 1:
When the number of rows to delete is less than the number of rows added, the newer rows get persisted. Meaning, the rows that are not present in the list passed to deleteallinbackground method get persisted.
This is my code
ParseQuery query = new ParseQuery("PostChoice");
query.fromPin();
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(final List<ParseObject> localList, ParseException e) {
if (localList != null && !localList.isEmpty()) {
List<ParseObject> postList = new ArrayList<ParseObject>();
for (ParseObject object : localList) {
postList.add(object.getParseObject("post"));
}
ParseQuery query = new ParseQuery("PostChoice");
query.whereContainedIn("post", postList);
query.whereEqualTo("user", ParseUser.getCurrentUser());
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> parseCloudList, ParseException e) {
if (parseCloudList != null && !parseCloudList.isEmpty()) {
ParseObject.deleteAllInBackground(parseCloudList, new DeleteCallback() {
#Override
public void done(ParseException e) {
// this gets executed and rows are accordingly deleted
ParseObject.saveAllInBackground(localList, new SaveCallback() {
#Override
public void done(ParseException e) {
// this gets executed but the rows are not uploaded.
//the locallist is not empty. it contains the right data.
editor.putLong(Four.LAST_CHOICE_SYNC_TIME, System.currentTimeMillis());
editor.commit();
Log.i("SyncChoiceService", "Synced Choices");
}
});
}
});
}
else{
ParseObject.saveAllInBackground(localList, new SaveCallback() {
//This method works fine and uploads. The issue arises only when saveAllInBackground is called inside deleteAllInBackground's call back
#Override
public void done(ParseException e) {
Log.i("SyncChoiceService", "Synced Choices");
editor.putLong(Four.LAST_CHOICE_SYNC_TIME,System.currentTimeMillis());
editor.commit();
}
});
}
}
});
}
}
});
query.fromPin(); - this might be using your local datastore, not sure
ParseObject.saveAllInBackground(localList... make sure that localList is not empty. I have a suspicion it is when you call the save method.
First two queries dublicate themselves, just make this query instead of those two:
ParseQuery query = new ParseQuery("PostChoice");
query.whereEqualTo("user",ParseUser.getCurrentUser());
im not sure, but there might be a limit to nesting bacground callbacks. You might try to call a method in your activity class from one of the callbacks, that continues the chain.
Related
Unfortunately, the official Parse documentation regarding the matter is very unclear. It tells that "When an object is pinned, every time you update it by fetching or saving new data, the copy in the local datastore will be updated automatically. You don't need to worry about it at all.", so from what I can understand it means that if I do this
List<GameScore> list;
// ...
ParseObject.pinAllInBackground(list);
GameScore.saveAllInBackground(list);
and then I do this
ParseQuery.getQuery(GameScore.class).findInBackground(new FindCallback<GameScore>() {
#Override
public void done(final List<GameScore> objects, ParseException e) {
// show them ...
}
);
my locally stored pinned data should update itself.
However, just two paragraphs below, the Parse team showed a method that was something like this:
public void getDataFromParse() {
ParseQuery.getQuery(GameScore.class)
.findInBackground(new FindCallback<GameScore>() {
#Override
public void done(final List<GameScore> objects, ParseException e) {
ParseObject.unpinAllInBackground(oldPinnedData, new DeleteCallback() {
#Override
public void done(ParseException e) {
ParseObject.pinAllInBackground(objects,
new SaveCallback() {
#Override
public void done(ParseException e) {
// Show data
}
}
);
}
});
}
}
So, basically: First, query the updated Parse data on the cloud. Then, unpin the old data that you have. Finally, pin the new one.
In another one, they don't even unpin the old, local data, but they do pin the new one just taken from the cloud.
private void loadFromParse() {
ParseQuery<Todo> query = Todo.getQuery();
query.findInBackground(new FindCallback<Todo>() {
#Override
public void done(List<Todo> todos, ParseException e) {
if (e == null) {
ParseObject.pinAllInBackground((List<Todo>) todos,
new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
// show them
}
}
});
}
}
});
}
I am very confused, and I don't understand the relationship between local datastore and the Parse cloud.
Personally, I'm in a situation where the cloud always wins over local data.
SaveAllInBackground doesn't work inside deleteAllInBackground as desired.
I am trying to save a list of parseobjects using save all in background. To avoid duplicates in the table, I am querying for the already existing rows and deleting them if any and then save the new copy. Therefore I am calling the saveAllInBackground inside the deleteAllInBackground's callback.
The problem is this :
For ex: if the list to delete contains [a,b,c,d] and the list to upload has [a,b,c,d,e,f] only [e,f] get persised to parse. I am passing [a,b,c,d,e,f] to the saveAllInBackground but only [e,f] get persisted.
Is there something I am missing? How to solve this?
Can I use a different approach?
Is there a better way to avoid duplicates? I dont want to add a
beforeSave hook. The whole purpose of calling the saveAll is to reduce the number of API calls. I guess if I use beforeSave, I will have to run some queries in the cloud code anyway.
This is my code
ParseQuery query = new ParseQuery("PostChoice");
query.fromPin();
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(final List<ParseObject> localList, ParseException e) {
if (localList != null && !localList.isEmpty()) {
List<ParseObject> postList = new ArrayList<ParseObject>();
for (ParseObject object : localList) {
postList.add(object.getParseObject("post"));
}
ParseQuery query = new ParseQuery("PostChoice");
query.whereContainedIn("post", postList);
query.whereEqualTo("user", ParseUser.getCurrentUser());
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> parseCloudList, ParseException e) {
if (parseCloudList != null && !parseCloudList.isEmpty()) {
ParseObject.deleteAllInBackground(parseCloudList, new DeleteCallback() {
#Override
public void done(ParseException e) {
// this gets executed and rows are accordingly deleted
ParseObject.saveAllInBackground(localList, new SaveCallback() {
#Override
public void done(ParseException e) {
// this gets executed but the rows are not uploaded.
//the locallist is not empty. it contains the right data.
editor.putLong(Four.LAST_CHOICE_SYNC_TIME, System.currentTimeMillis());
editor.commit();
Log.i("SyncChoiceService", "Synced Choices");
}
});
}
});
}
else{
ParseObject.saveAllInBackground(localList, new SaveCallback() {
#Override
public void done(ParseException e) {
Log.i("SyncChoiceService", "Synced Choices");
editor.putLong(Four.LAST_CHOICE_SYNC_TIME,System.currentTimeMillis());
editor.commit();
}
});
}
}
});
}
}
});
I have come up with a solution like this. and it meets my requirement. I use the updatedValue and delete the old ones and the remaining get updated as a whole list.
ParseQuery query = new ParseQuery("PostChoice");
query.fromPin();
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(final List<ParseObject> localList, ParseException e) {
if (localList != null && !localList.isEmpty()) {
List<ParseObject> postList = new ArrayList<ParseObject>();
for (ParseObject object : localList) {
postList.add(object.getParseObject("post"));
}
ParseQuery query = new ParseQuery("PostChoice");
query.whereContainedIn("post", postList);
query.whereLessThan("updatedAt",System.currentTimeMillis());
query.whereEqualTo("user", ParseUser.getCurrentUser());
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(final List<ParseObject> parseCloudList, ParseException e) {
if (parseCloudList != null && !parseCloudList.isEmpty()) {
ParseObject.deleteAllInBackground(parseCloudList, new DeleteCallback() {
#Override
public void done(ParseException e) {
Log.i("SyncChoiceService", "Deleted old Choices");
}
});
}
}
});
ParseObject.saveAllInBackground(localList, new SaveCallback() {
#Override
public void done(ParseException e) {
Log.i("SyncChoiceService", "Synced Choices");
}
});
}
}
});
Yes, if i understand you correctly you want to save only new data from localdb to parse backend.
Best and less request solution would be to have anther field in your table called "Draft" or "isUpdated" (name as you want). Role of this flag is to identify whether this field is saved in backend or not. if its a new field "isUpdated" is false else its true. Then in query you can query only the isUpdated is false. Then save them in backend. Then
You don't want to delete any data.
Reduce requests
Less unnecessary logic in your code.
it's clean
Hope this helps
I am using ParseUser for user management. Having the username (unique), I planned to query the User class to identify the user and then put a new score to the "score" field, just like a Leaderboard. Coded as follows:
Code:
public void update_user_score(String username, int original_score, int add)
{
final int new_score = original_score + add;
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereEqualTo("username", username);
query.setLimit(1);
query.findInBackground(new FindCallback()
{
public void done(final ParseObject object, ParseException e)
{
if (e == null)
{
object.saveInBackground(new SaveCallback()
{
public void done(ParseException e)
{
object.put("score", new_score);
object.saveInBackground();
onBackPressed();
}
});
}
}
});
}
Situation:
It states that The type new FindCallback(){} must implement the inherited abstract method FindCallback.done(List, ParseException) . I have tried that FindCallback.done and found it unsucessful.
Question:
I would like to ask how the other fields of the user be updated with the username given? Thanks!
As per the parse documentation said,
The ParseUser class is secured by default. Data stored in a ParseUser can only be modified by that user. By default, the data can still be read by any client. Thus, some ParseUser objects are authenticated and can be modified, whereas others are read-only.
Specifically, you are not able to invoke any of the save or delete type methods unless the ParseUser was obtained using an authenticated method, like logIn or signUp. This ensures that only the user can alter their own data.
The following illustrates this security policy:
ParseUser user = ParseUser.logIn("my_username", "my_password");
user.setUsername("my_new_username"); // attempt to change username
user.saveInBackground(); // This succeeds, since the user was authenticated on the device
// Get the user from a non-authenticated manner
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.getInBackground(user.getObjectId(), new GetCallback<ParseUser>() {
public void done(ParseUser object, ParseException e) {
object.setUsername("another_username");
// This will throw an exception, since the ParseUser is not authenticated
object.saveInBackground();
}
});
please go through this link Reference
public void update_user_score(String username, int original_score, int add)
{
final int new_score = original_score + add;
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereEqualTo("username", username);
query.setLimit(1);
query.findInBackground(new FindCallback()
{
public void done(final ParseUser object, ParseException e)
{
if (e == null)
{
object.put("score", new_score);
object.saveInBackground(new SaveCallback()
{
public void done(ParseException e)
{
if(e==null)
onBackPressed();
}
});
}
}
});
}
As far as I understand from your question is; you want to find the user entry in user table via using the specified user id and update the score field. First suggestion, instead of findInBackground use getFirstInBackground. Following that check the returned user object if not null perfrom the user update operation. Then call saveInBackground. saveInBackground is used for perfroming actions when the save is succesful. In your code, I see that you try to save the object in done method again. Also, one suggestion is that control the ACL.
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.getFirstInBackground(someUserId, new GetCallback<ParseUser>() {
public void done(ParseUser user, ParseException e) {
if (e == null) {
// No error e is null
// check user object
if (user == null) {
// no user , error
} else {
// perfrom save operation
// put score field
// call save in background
// in done method update UI for example.
}
} else {
// Something went wrong.
}
}
});
I have done storing into database and but when I wish to retrieve the database elements, I always need to specify the objectID which needs to be checked in database. I want a way by which I can get objectID from the element or better get the object id while storing.
"o1l5gCCPB4" is the objectID
Here is code I used for retrieval:
query.getInBackground("o1l5gCCPB4", new GetCallback<ParseObject>() {
#Override
public void done(ParseObject object, ParseException e) {
// TODO Auto-generated method stub
if (e == null) {
String playerName = object.getString("foo");
tv.setText(playerName);
} else {
// something went wrong
tv.setText("Something went wrong!!");
}
}
});
You can get an element, this way:
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>(PARSE_CLASS_DRAGS);
query.whereEqualTo("objectId", objectId);
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> list, ParseException e) {
//convert the parseObjects to your objects
}
});
If you want to get all of the elements, you only have to ignore the query.whereEqualTo() row.
I am currently working on the User Profile part of my app and I would like to run a query on a specific user object using the objectId of that user. With that query, I hope to get the users info(email, username, hometwown, etc.) from parse and display it on the page.
The parse documentation recommends the getInBackground method like so:
ParseQuery<ParseObject> query = ParseQuery.getQuery("MyClass");
query.getInBackground(myId, new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
objectWasRetrievedSuccessfully(object);
} else {
objectRetrievalFailed();
}
}
}
Using this I would think I would replace "MyClass" with "User" since I will be searching the user class for the specific Id entered. However, I am getting an exception every time and the query is failing. A copy of my code is below where, as an example, I attempt to extract the "hometown" attribute from the specefic user and set it to display as a textview. mId is equal to the specific object ID of the user passed over from another activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_friends_profile);
mHometownField = (TextView) findViewById(R.id.textView);
mId = getIntent().getStringExtra(ParseConstants.KEY_ID);
ParseQuery<ParseObject> query = ParseQuery.getQuery("User");
query.getInBackground(mId, new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, com.parse.ParseException e) {
if (e == null) {
mHometown = parseObject.get(ParseConstants.KEY_HOMETOWN).toString();
}
else {
Log.e(TAG, e.getMessage());
AlertDialog.Builder builder = new AlertDialog.Builder(FriendsProfileActivity.this);
builder.setTitle(R.string.error_title)
.setMessage(e.getMessage())
.setPositiveButton(android.R.string.ok, null);
AlertDialog dialog = builder.create();
dialog.show();
}
}
});
mHometownField.setText(mHometown);
}
A very common mistake. The "User" class is an internal class so you have to query it differently:
ParseQuery<ParseUser> query = ParseUser.getQuery();
Same applies for the "Role" and "Installation" classes.