I have a one to many relationship in my coredata object graph, typically User<->>Device. Lets say from my API i get a response of something as bellow
[
{
user:"John"
id:1
devices:[
{
id:1
name:"iPhoneX"
},
{
id:2
name:"Samsung Galaxy Plus"
}
]
},
{
user:"Peter"
id:"2"
devices:[
{
id:3
name:"iPhone5s"
},
{
id:4
name:"Samsung Galaxy"
}
]
}
]
Now the objective is to do a simple update/insert to my coredata tables. My Partial code as bellow for updating and inserting Device table
func updateDevice (userID:String, apiDevicesArray:[APIResposeDevice]){
for device in apiDevicesArray {
let request:NSFetchRequest<Device> = Device.fetchRequest()
request.predicate = NSPredicate(format:"id = %#", device.id)
do{
let searchResults = try self.privateMOC.fetch(request)
if searchResults.count > 0 {
//Update
let update_device = searchResults.last
update_device.name = device.name
update_device.id = device.id
}else{
//Stuck with inserting a device to the proper user
request.predicate = NSPredicate(format: "owner.id = %#, userID)
do{
let devicesArrayForUserID = try self.privateMOC.fetch(request)
//How to insert a record to this array now ???
}catch{
}
}
}catch{
}
}
}
First, you need to fetch the User.
Then, you have to choices:
user.devices.add(device)
or if it's two ways relationship, then you can call device.user = user
Related
I am trying to do a simple ordered query request based on epoch timestamps:
guard let user = Auth.auth().currentUser?.uid else {return}
let queryRef = Database.database().reference().child("ordersBackup").child(user).queryOrdered(byChild: "pickupCompleteAfter").queryLimited(toFirst: 1)
queryRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
print(snapshot.value)
})
Structure:
{
"UserId" : {
"UniqueId1" : {
"pickupCompleteAfter" : 1568314979000,
"name" : "Jeff"
},
"UniqueId2" : {
"pickupCompleteAfter" : 1557687779000,
"name" : "Stig"
},
"UniqueId3" : {
"pickupCompleteAfter" : "1578855779000",
"name" : "Ali"
}
}
}
It should return UniqueId2. However, I am always retrieving the same order as the structure i.e UniqueId1, UniqueId2, UniqueId3. It doesn't take the timestamp into consideration at all. What am I doing wrong?
Output:
LQ8nHi
{
orderInfo = {
deliveryCompleteAfter = 1552917600000;
deliveryCompleteBefore = 1552924800000;
pickupCompleteAfter = 156249280000;
pickupCompleteBefore = 1552492800000;
};
}
YQxeKv
{
orderInfo = {
deliveryCompleteAfter = 1552917600000;
deliveryCompleteBefore = 1552924800000;
pickupCompleteAfter = 1557687779000;
pickupCompleteBefore = 1552492800000;
};
}
ibIPO9
{
orderInfo = {
deliveryCompleteAfter = 1553090400000;
deliveryCompleteBefore = 1553097600000;
pickupCompleteAfter = 1578855779000;
pickupCompleteBefore = 1552665600000;
};
}
The pickupCompleteAfter is nested under the orderInfo node of each child. You're current not telling the database about that full path, so it only checks for a pickupCompleteAfter right under each child.
To order/filter on orderInfo/pickupCompleteAfter, use:
let query = Database.database().reference().child("ordersBackup").child(user)
.queryOrdered(byChild: "orderInfo/pickupCompleteAfter").queryLimited(toFirst: 1)
query.observeSingleEvent(of: .childAdded, with: { (snapshot) in
print(snapshot.value)
I am trying to delete multiple rows from database. Currently, I have an array which consists of all the row id's which i want to delete. But i cannot seem to find a suitable code for it
func deleteMultiple(Id:[Int]) -> NSMutableArray {
sharedInstance.databese!.open()
var i:Int = Id.count
print(Id, i)
let resultSet:FMResultSet! = sharedInstance.databese!.executeQuery("DELETE FROM Info WHERE Id = ?", withArgumentsIn: Id)
let itemInfo:NSMutableArray = NSMutableArray ()
if (resultSet != nil)
{
while resultSet.next() {
let item:Tbl_Info = Tbl_Info()
item.Id = Int(resultSet.int(forColumn: "Id"))
item.Name = String(resultSet.string(forColumn: "Name")!)
item.LastName = String(resultSet.string(forColumn: "LastName")!)
itemInfo.add(item)
}
}
sharedInstance.databese!.close()
return itemInfo
}
You can execute your query in a queue it will not freeze your database.
if anything fails during the transaction FMDB Provides the rollback functionality.
Look at the sample code.
sharedInstance.databese.dbQueue.inTransaction { db, rollback in
idArray.forEach {id in
do {
let resultSet:FMResultSet! = try db.executeQuery("DELETE FROM Info WHERE Id = ?", withArgumentsIn: Id)
//Do whatever with your resultSet
} catch {
//Error occured during deleting from database.
rollback.pointee = true
}
}
}
Edited: New information added into the question
When using bulk operation with option journaling false it doesn't return code: 11000 duplicate key error.
When using following options, it doesn't return errors:
{ j:false }
{ w:0, j:false }
When using following options, it returns duplicate key errors:
{ w:"majority", j:false }
{ w:0, j:true }
{ w:"majority", j:true }
I wonder that is this a correct behaviour?
Code for example:
var ObjectId = mongoose.Types.ObjectId,
User = require('./models/user')
let bulk = User.collection.initializeUnorderedBulkOp()
let doc1 = {
"branch" : "DUPLICATE",
"department" : ObjectId("582bf1d8322809041e667777"),
}
let doc2 = {
"branch" : "TEST_SUCCESS",
"department" : ObjectId("582bf1d8322809041e668888"),
}
let doc3 = {
"branch" : "DUPLICATE",
"department" : ObjectId("582bf1d8322809041e669999"),
}
bulk.insert(doc1)
bulk.insert(doc2)
bulk.insert(doc3)
let bulkOptions = {
w:0,
j:false // next run change argument j as true
}
bulk.execute(bulkOptions, (err, result) => {
let writeErrors = result.toJSON().writeErrors
for (let i = 0; i > writeErrors.length - 1; i++) {
let error = writeErrors[i]
debug(error.toString())
}
})
Additionally the schema of User model has an unique compound index as userSchema.index({ branch:1, department:1 }, { unique: true })
App Version
mongoDB v3.4.3 (storageEngine is wiredTiger)
mongoose v4.12.4 (the documentation refers to node-mongodb-native API 2.2)
node.js v8.8.1
My problem is adding new numbers to the array field in users. all the code seems to work but i have the problem with saveAll function. All the names are correct, as i've checked on several different occasions.
func taskAllocation(){
var query = PFUser.query()
var objectQuery:[PFUser] = [PFUser]()
query.whereKey("Year", equalTo: yearTextField.text.toInt())
query.whereKey("Class", equalTo: classTextField.text.toInt())
query.whereKey("Keystage", equalTo: keystageTextField.text.toInt())
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error != nil {
println(error)
} else {
if objects.isEmpty {
println("empty query")
}
else {
for object in objects as [PFUser] {
var test:[Int] = object["taskIDs"] as [Int]
test.append(self.getIndex)
println(test)
object["taskIDs"] = test
object["tasksCompleted"] = "hello"
objectQuery.append(object)
}//For
println(objectQuery.count)
if objectQuery.count != 0{
println("Success!!!")
println(objectQuery)
PFUser.saveAllInBackground(objectQuery)
}
}
}
}
This below is stored in the println(objectQuery) as you can see the taskIDs have been appended and the tasksCompleted contains the string. Which leads me to believe that there is a problem with the saveallinbackground function.
[<PFUser: 0x155f6510, objectId: W6TrPQwCQ7, localId: (null)> {
Class = 2;
Forename = "Joe ";
Keystage = 2;
Surname = Freeston;
Year = 4;
admin = false;
completedTaskIDs = (
);
taskIDs = (
0,
2,
4,
5,
6,
46
);
tasksCompleted = hello;
tasksCorrect = 0;
userType = Student;
username = jfreeston;
}, <PFUser: 0x1569b7a0, objectId: slLd1KBIaM, localId: (null)> {
Class = 2;
Forename = "Camilla ";
Keystage = 2;
Surname = Linhart;
Year = 4;
admin = false;
completedTaskIDs = (
);
taskIDs = (
0,
46
);
tasksCompleted = hello;
tasksCorrect = 0;
userType = Student;
username = clinhart;
}]
Not sure if anyone has more knowledge of the parse api, mainly how the saveall/saveallinbackground functions work maybe.
To save all in background as a user I would first create a PFUser variable, like
var NewUser = PFUser.currentUser
Then to save in background
newUser.saveInBackgroundWithBlock {
(success:Bool, error: NSError!) -> Void in {
}
Then you can put if-statements and stuff within those brackets to do what you need it to when successful. like:
if(success) {
performSegueWithIdentifier("YourSegue", sender: nil)
}
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"(UserId==%#)",[defaluts objectForKey:#"objectId"]];
PFQuery *frndquery=[PFQuery queryWithClassName:#"FriendsDetails" predicate:predicate];
[frndquery orderByDescending:#"lastdate"];
[frndquery whereKey:#"BlockStatus" equalTo:#"No"];
NSArray *arrquery=[frndquery findObjects];
for (PFObject *frndids in arr){
PFRelation *relation=[frndids relationforKey:#"ChatRelation"];
NSArray *arrids=[NSArray arrayWithObjects:[frndids objectForKey:#"UserId"],[frndids objectForKey:#"ConversationID"], nil];
PFQuery *statusQuery = [relation query];
[statusQuery orderByDescending:#"createdAt"];
[statusQuery whereKey:#"Deletechat" notContainedIn:arrids];
statusQuery.limit = [[NSNumber numberWithInt:1] intValue];
NSArray *arrforrelationobjects=[statusQuery findObjects];}
I want to find all objects when we retrieve the objects from the first query itself. Please solve my problem
There is a method you can use to include properties which are pointer values. You cannot use the include method with relations. What I do instead is use a Cloud Code function to aggregate the results I want into a JSON object and return that object.
See the fetchPostDetails function in the following script.
https://github.com/brennanMKE/PostThings/blob/master/Parse/PostThings/cloud/main.js
It fetches items are relation objects such as tags and likes which happen to be User objects which are relations to the Post class. There are also comments which are referenced as a pointer back to the post from each comment. The fetchPostTags and fetchPostLikes methods show how to fetch those relations and populate the JSON object which is holding all of the results. You need to deploy those Cloud Code update and then access it as a function from the iOS side. The results will come back as an NSDictionary with values for posts, tags, likes and comments. The posts are an array of Post objects. The tags, likes and comments are NSDictionary objects which have the postId as the key to access the array of Parse objects.
This way one call to the function will get you want you need.
I've included some of the code below as a reference in case what is on GitHub changes.
// Helper functions in PT namespace
var PT = {
eachItem : function (items, callback) {
var index = 0;
var promise = new Parse.Promise();
var continueWhile = function(nextItemFunction, asyncFunction) {
var item = nextItemFunction();
if (item) {
asyncFunction(item).then(function() {
continueWhile(nextItemFunction, asyncFunction);
});
}
else {
promise.resolve();
}
};
var nextItem = function() {
if (index < items.length) {
var item = items[index];
index++;
return item;
}
else {
return null;
}
};
continueWhile(nextItem, callback);
return promise;
},
arrayContainsItem : function(array, item) {
// True if item is in array
var i = array.length;
while (i--) {
if (array[i] === item) {
return true;
}
}
return false;
},
arrayContainsOtherArray : function(array, otherArray) {
/// True if each item in other array is in array
var i = otherArray.length;
while (i--) {
if (!PT.arrayContainsItem(array, otherArray[i])) {
return false;
}
}
return true;
},
fetchPostTags : function(post) {
return post.relation("tags").query().find();
},
fetchPostLikes : function(post) {
return post.relation("likes").query().find();
},
fetchPostComments : function(post) {
var query = new Parse.Query(Comment);
query.include("owner");
query.equalTo("post", post);
return query.find();
},
fetchPostDetails : function(post, json) {
json.tags[post.id] = [];
json.likes[post.id] = [];
json.comments[post.id] = [];
return PT.fetchPostTags(post).then(function(tags) {
json.tags[post.id] = tags;
return PT.fetchPostLikes(post);
}).then(function(likes) {
json.likes[post.id] = likes;
return PT.fetchPostComments(post);
}).then(function(comments) {
json.comments[post.id] = comments;
json.count++;
return Parse.Promise.as();
});
},
};