Support Topics Documentation Slack YouTube Blog

Reset a relation field to empty


(Daniel Schuring) #1

We’re having trouble resetting a record’s column to no relations.
We have a table called Beacon, and it has a column named “messageUsersDelivered” that has a one-to-many relationship to Users.
We are saving that Beacon, and what to clear all the users in that column.
But are experiencing cases where sometimes when saving, the Users is not reset, and it still has the user relationships in it.
In iOS, what is the best way to save the beacon record to reset the column messageUsersDelivered to empty again.
Here is our current code :
//Set new message to beacon
beacon.message = _beaconMessage;
beacon.messageUsers = self.selectedRecipients;
beacon.messageUsersDelivered = [NSMutableArray new];
beacon.messageDate = [NSDate date];
beacon.messageIsFacebookPublish = self.beaconFbIsSelected;
beacon.messageIsPublic = self.beaconPublicIsSelected;

Beacon *updatedBeacon = [beacon save];
And then the “save” method on the beacon class:
-(Beacon *)save {
//Synchronous method
@try {
id<IDataStore> dataStore = [backendless.persistenceService of:[Beacon class]];
return [dataStore save:self];
}
@catch (Fault *fault) {
NSLog(@“beacon save FAULT: %@”, fault.message);
return nil;
}
}


(Mark Piller) #2

Is the “beacon” object you’re saving received from the server or do you construct it in the app?


(Daniel Schuring) #3

Its an object from the server.

From the “beacon” table.


(Mark Piller) #4

What is the difference between “self” and “beacon” then here? Does “self” point to a object you got from the server too?

beacon.messageUsers = self.selectedRecipients;
beacon.messageUsersDelivered = [NSMutableArray new];
beacon.messageDate = [NSDate date];
beacon.messageIsFacebookPublish = self.beaconFbIsSelected;
beacon.messageIsPublic = self.beaconPublicIsSelected


(Daniel Schuring) #5

ie, retrieved with the backendless datastore:

id <IDataStore> beaconStore = [backendless.persistenceService of:[Beacon class]];


(Daniel Schuring) #6

Those are just properties on the current view.


(Daniel Schuring) #7

ie, the updated data that is to be changed when saving the beacon record.

And our issue is that:
beacon.messageUsersDelivered is not being reset to no users when we save (in all cases).
It appears to work sometimes, others not.


(Mark Piller) #8

Is there anything different between the requests when it works and when it does not? Perhaps requests that do not work result in a server error?


(Daniel Schuring) #9

I’ve watched the responses.

And the strange thing is that the “save” method returns the updated object, as its a Synchronous method.
See " -(Beacon *)save " above.

And the result of that will show that the “messageUsersDelivered” is empty.
However, when I immediately open my browser, browse to that record, I can see that it in fact is not empty on the record. I refresh the data, and see its still not empty.


(Mark Piller) #10

“messageUsersDelivered” is a one to many collection. When an object is being saved, the returned value will not contain the related objects, unless they are marked as “auto-load”. I would not use your observation as a way to determine if the relation had been broken or not. I will assign this to a developer to investigate the issue further.


(Daniel Schuring) #11

Ok, thanks.

btw, it is set to “autoload”.


(Mark Piller) #12

Internal ticket #: BKNDLSS-12464


(Daniel Schuring) #13

Where can I see updates to this ticket?


(Sergey Kukurudzyak) #14

it is still in progress, we will notify you when will be any update


(Alexandr Navara) #15

Hi, Daniel!

I’ve tried to reproduce your issue, but without any success. Please try to find stable steps to reproduce this issue.
Here is a code sample I’ve tested with ( class Foo has one-to-many relation “users” with Users table ). Try to execute it:









    id&lt;IDataStore&gt; fooDataStore = [backendless.persistenceService of:[Foo class]];

    id&lt;IDataStore&gt; userDataStore = [backendless.persistenceService of:[BackendlessUser class]];

    

    NSMutableArray *initialArray = [ NSMutableArray new ];

    [initialArray addObject: [userDataStore findFirst]];

    [initialArray addObject: [userDataStore findLast]];

    

    NSMutableArray *emptyArray = [ NSMutableArray new ];




    Foo *foo = [ fooDataStore findFirst ];

    foo.users = initialArray;

    [fooDataStore save:foo];

    

    Foo *foundFoo = [fooDataStore findFirst];

    foundFoo.users = emptyArray;

    [ fooDataStore save:foundFoo ];

best regards,
Alex


(Daniel Schuring) #16

Here you go, I’ve been able to confirm the bug using a modified version of your test:









-(void)testBackendlessSaveBug {

    

    id&lt;IDataStore&gt; fooDataStore = [backendless.persistenceService of:[Foo class]];

    id&lt;IDataStore&gt; secondFooDataStore = [backendless.persistenceService of:[Foo class]];

    id&lt;IDataStore&gt; userDataStore = [backendless.persistenceService of:[BackendlessUser class]];

    

    NSMutableArray *initialArray = [ NSMutableArray new ];

    [initialArray addObject: [userDataStore findFirst]];

    [initialArray addObject: [userDataStore findLast]];

    

    NSMutableArray *emptyArray = [ NSMutableArray new ];

    

    Foo *foo = [ fooDataStore findFirst ];

    foo.users = emptyArray;

    [fooDataStore save:foo];

    

    NSLog(@"1) Starting - foo usercount: %lu ", [foo.users count]);

    

    [NSThread sleepForTimeInterval: 1];




    //Simulate data changed by other device (by changing a second instance of the same Foo record)

    

    NSMutableArray *changeUsersArray = [ NSMutableArray new ];

    [changeUsersArray addObject: [userDataStore findFirst]];




    Foo *secondFoo = [ secondFooDataStore findFirst ];

    secondFoo.users = changeUsersArray;

    [ secondFooDataStore save:secondFoo ];

    

    NSLog(@"2) Simulating foo users being changed elsewhere (other device) - setting foo user count: %lu ", [secondFoo.users count]);

    

    [NSThread sleepForTimeInterval: 1];

    

    //Now save our first instance with an empty array

    foo.users = emptyArray;

    [ fooDataStore save:foo ];

    

    NSLog(@"3) Save and reset foo user count to 0 - foo usercount: %lu ", [foo.users count]);

    

    //Check our user count

    Foo *finalFoo = [fooDataStore findFirst];

    

    if ( [finalFoo.users count] > 0) {

        NSLog(@"Test result: Incorrect count - Foo user is not 0, count: %lu ", [finalFoo.users count]);

    } else {

        NSLog(@"Test result: Correct count - Foo user count is 0, count: %lu ", [finalFoo.users count]);

        NSLog(@"If correct, then if we run this test method one more time, usually it is incorrect the second time. Running again....");

        

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            [self testBackendlessSaveBug];

        });

    }

}


(Alexandr Navara) #17

Daniel, thank you for the code.

It works as expected. Let me explain.
This test would pass if and only if initial “foo” object has empty “users” property. Thats why this test fails every second run - after first run “foo” doesn’t have related users! Look what you have here:

  1. “foo” is retrieved, “users” property is empty
  2. You assign emptyArray as “users” for “foo” and save…
  3. …but server doesn’t change “foo” object, because it has been retrieved with empty “users”, and it has come back with empty “users”, and it doesn’t matter that it has been already changed.
    In order to prevent it you should retrieve the latest version of “foo” object right before changing it. I’ve added a line into your test. Now it should work well.








    NSLog(@"2) Simulating foo users being changed elsewhere (other device) - setting foo user count: %lu ", [secondFoo.users count]);

    

    [NSThread sleepForTimeInterval: 1];

    

    //Now save our first instance with an empty array

    

    foo = [ fooDataStore findFirst ]; // added line

    

    foo.users = emptyArray;

    

    [ fooDataStore save:foo ];

hope it helps,
Alex Navara


(Marcio Prudencio) #18

Hi Gus, I’m also need to clear (reset) my relation field.

My scenario is, I have a relation 1:n where Product has many ‘Ingredients’, these ingredients can be easily added or removed to/from a product. When a user has some ingredients and decides to remove all active ingredients I got an error, because now I have no ID to Set or Add. There is a way to just empty/reset the field?


(Stanislaw Grin) #19

Hi @MARCIO_PRUDENCIO,

have you checked this part of documentation?
Delete Objects from relation
I think it’s what you need here. Let me know if this does not work.

Regards,
Stanislaw


(Marcio Prudencio) #20

I did @stanislaw.grin and the documentation requests a child ID. When the user unselects all ingredients I have no child ID to set/add or remove. So this field can contain 5 related objects or none that is an option.

How can I reset a relation field back to none without passing any child IDs?

using the documentation examples I got an error:

“Child objects list not specified. Use either a whe…rray of childs with Content-Type application/json”

Also try to update as data field using an empty object and didn’t work either.