Delete specific value from firebase database using swift - swift

Firebase Database
I tried using this bit of code but it doesn't seem to work. I take the name the user selects and store it in nameList.
Lets say I store Blake Wodruff in nameList[0].
How do I remove only that name?
var nameList = [String](repeating: "", count:100)
func remove() {
print(nameList[countAddNames])
let usernameRef = Database.database().reference().child("Candidate 1").child("alton").child(nameList[countAddNames]);
usernameRef.removeValue();
}

To write to or delete a node, you must specify its entire path. So to delete node 0 from your JSON, you'd do:
let usernameRef = Database.database().reference().child("Candidate 1").child("alton").child("0");
usernameRef.removeValue();
Or a bit shorter:
let usernameRef = Database.database().reference().child("Candidate 1/alton/0");
usernameRef.removeValue();
If you only know the name of the user you want to remove, you'll need to first look up its index/full path before you can remove it. If you have the data in your application already, you can do it in that code. Otherwise you may have to use a database query (specifically .queryOrderedByValue and .queryEqualToValue) to determine where the value exists in the database.
Also see: Delete a specific child node in Firebase swift
Once you remove a value from your JSON structure, Firebase may no longer recognize it as an array. For this reason it is highly recommended to not use arrays for the structure that you have. In fact, I'd model your data as a set, which in JSON would look like:
"alton": {
"Jake Jugg": true,
"Blake Wodruff": true,
"Alissa Sanchez": true
}
This would automatically:
Prevent duplicates, as each name can by definition only appear once.
Make removing a candidate by their name as easy as Database.database().reference().child("Candidate 1/alton/Jake Jugg").removeValue()
For more on this, also see my answer to Firebase query if child of child contains a value

Related

How do I add a new Map entry to a Map without overwriting the entire Map?

In my Firestore database, I have a document with a Map called 'Videos'.
In it, I add another Map into it that includes my video data.
So it looks like this:
Videos (Map) :
2022-Oct-03-14:24 (Map):
Title: Hi
Now what I want to do is, add another Map to the Videos with a different date.
How do I do this in Flutter?
Currently, I am doing:
db.collection('Collection').doc('Document').update({
'Videos': {
'2022-Oct-03-14:55': {
'Title': 'Ok'
}
}
});
But what this does is, it overrides the entire Videos map and assigns this new map I give, so the old Map with the Title Hi gets deleted. How do I insert new maps?
While Alex's answer is correct if you had an array field, you only have map fields. In that case, you can use . notation to updates/write only the nested field:
db.collection('Collection').doc('Document').update({
'Videos.2022-Oct-03-14:55.Title': 'Ok'
});
Also see the Firebase documentation on updating a field in a nested object.
For adding a new object inside the Videos map, you have two options available. The first one would be to use the . (dot) notation, as #FrankvanPuffelen mentioned in his answer, which will work perfectly fine. This means that it will indeed create the Videos field if it doesn't exist, and it will add the 2022-Oct-03-14:55 subfield inside it if that doesn't exist, and then the Title you put in there. However, since it's an update operation, this solution will work only if the document already exists, otherwise, it will fail.
If you need to create the Videos field inside a document that doesn't already exist, then you should consider using the set() function with merge true. However, this solution doesn't work using the . notation. So for that, I recommend you check my answer in the following post:
Problem with the initial creation of a map structure within a document in firestore
The second solution would be to read the document/map first. It's not mandatory but it will help you check the existence of an existing object. Why, because those objects are maps, and when it comes to maps, if you add a record that has the same key, then it replaces the old value with the new one. So I recommend you have to check that first.
So in order to perform the addition operation, you have to:
Read to read the document.
Get the Videos map in memory.
Check if a video already exists.
Add the new map inside the Videos.
Write the document back to Firestore.
If you'll have a map with the exact same key as a previous key, then the data will be overwritten, meaning that you'll don't see any change in the key, but only in the value if it's different.

Prisma not performing update on record

There are a few things going on with Prisma update that I just don't get.
Why is the update (using the ORM way) not performed ?
Why the value of data.address seems to affect the outcome of the update ?
Do I have to provide all of the fields of the entity when updating ? Or in this case, could I just put what I want changed inside of data ?
I am using #prisma/client#3.15.2
Here is what I am currently working with:
const { valid: validFor, expire, state, address, ...safeProperties } = data;
const addressAsUnsigned = address >>> 0; // address is an ip address represented as an integer. It needs to be treated as unsigned
const extendBy = newValidFor - validFor;
const extended = add(expire, { seconds: extendBy });
const payload: Prisma.DataTableUpdateArgs = {
where: { address: addressAsUnsigned },
data: {
...safeProperties,
address: addressAsUnsigned,
expire: extended,
valid: authenticated,
state: {},
},
}
Logger.debug(payload);
// contains the changes I expect
const result = await db.dataTable.update(payload);
Logger.debug(result);
// result contains the same values as before the update.
// And indeed, when I check the database, nothing changed.
// Something like this does what I want, so there is really nothing complicated going on...
await db.$executeRaw`
UPDATE data_table SET
expire = ${extended},
valid = ${authenticated}
WHERE address = ${addressAsUnsigned}
`;
Hopefully, I have not missed something too obvious.
In my experience,
Why is the update (using the ORM way) not performed ?
You might be updating the wrong thing. Is your address an #unique field in your prisma.schema?
Why the value of data.address seems to affect the outcome of the update ?
Prisma might have messed some things up with wrong data. If your data is not unique, you might be updating the first row with that address. If you want to update multiple fields with same address, use updateMany
Do I have to provide all of the fields of the entity when updating ? Or in this case, could I just put what I want changed inside of data ?
No, you only need to put in the data that you need. In your "where" field, add the unique address, and in your data, only the fields that you are changing. In your case, expired and valid. If you want to skip updating some values, use "expired: undefined" and so on
Since you are using typescript, I would advise you to put your object directly inside the prisma update to get the correct types. (At least to fix this problem)
prisma.dataTable.update({where: {...}})
This way you will get the correct types. There is also a command to list all available args inside (control + space on mac)
Note that using the spread operator (...) will remove the listed types, so use it last.
Some other things: double check if your prisma import is correct. Is your data correct, is your returned data correct? Did you refresh your database on update? It might be updated but you just need to refresh for new changes.

how to ignore extra fields when storing prisma data?

I'm loading some data from a CSV file that has some extra notes fields that I don't want in the DB. Is there an option to just ignore extra fields when storing to the DB?
I think mongoose did this by default - which does have a downside that stuff goes missing without warning if your schema is wrong but... thats what i want in this case.
Otherwise what is a way to reflect and get the schema so I can remove extra fields from the data manually?
I'm getting this error on .create
Unknown arg `notes` in data.notes for type WalletCreateInput.
Did you mean `name`?
Available args:
...
It's not allowed to add extra fields while interacting with Prisma Query.
The current behaviour is intentional and it throws the error as an extra validation layer to make sure you're passing the right data.
There is a feature request that discusses allowing extra fields while interacting with Query.
As of now, destructuring the fields which are necessary and passing only the required fields is the only option.
Late to the party, but there is a way around this.
If you use the "fieldReference" preview feature:
generator client {
provider = "prisma-client-js"
previewFeatures = ["fieldReference"]
}
You can then create the following to strip out any extra keys.
function stripPrisma<T extends {}>(input: {fields:{}},data: T) : T {
let validKeys = Object.keys(input.fields);
let dataCopy: any = {...data};
for(let key of Object.keys(data)) {
if(!(validKeys.includes(key))) {
delete dataCopy[key];
}
}
return dataCopy as T;
}
And use it like this
data = stripPrisma(prisma.myTable, data);
prisma.myTable.create({data:data});
It is not perfect, since it will only be able to use "checked input", meaning you can only use the foreign key in your input and not the foreign object.

How to store data, from Firebase, with multiple values for each key?

My structure on Firebase is like this:
picture of structure-layout
(Seems like I'm not allowed to embed pictures yet, so they made it a link instead)
As I'm new to Swift, I'm a bit confused here.
Been walking in circles for this, apparently, easy task.
I'd like to store the data on the users device.
This way, if new data is updated to Firebase, the device will retrieve the new data at logon, not having the user to update the app through App Store.
I've concluded an array of Dictionaries would be the way to go, but how?
My problem is the values..
They seem to be downloaded together, in one SNAP, and not in parts. (Like so: "SNAP1: yada yada", SNAP2: ... " and so on)
I need to be able to access, at any time, only one value belonging to whatever key i'd like.
Here's my code (I know it's not complete yet, but I'm stuck):
func updateData() {
var dictArray: [[String:String]] = []
// Setting the refference and getting the values within a snapshot..
ref.child(REF_CATEGORIES).observeSingleEvent(of: .value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
let categories = snap.key
let questions = snap.children.allObjects
dictArray.append([categories : String(describing: questions)])
}
}
})
}
Somehow I need to iterate through the values too, but then: How do I merge them (index) with the correct category?
I'm feeling it's right under my nose.. To bad my nose is too big, for me to see :)
Thanks in advance!
As the Firebase Database is key-value based, you can achieve roughly what you want by giving each value a key which is simply a number. For example:
"Key1": {
"0":"value1",
"1":"value2",
"2":"value3"
}
You can then easily parse this into an array when you retrieve the data.
Fun fact: if you 'number' (they're not actually numbers, they're strings) your keys in this fashion, the Firebase Database will actually store this as an array under-the-hood, they just always display it to us in key-value form.

Store quotes in parse

So I am really new to parse and I have no idea how to store a famous quote and it's own author, so then I can use that information in my iOS app made with swift.
I have imported all the frameworks in order to make parse work but I don't know to to store that quotes with the authors and then retrieve the information to display the quote in a label and the author in another label. Please don't be rude, I don't get who to make this work.
So if you want to save a text and retrieve to Parse
let's say you want to save some text into a class called Data
Save
var data = "Swift is nice"
var object = PFObject(className:"Data")
object["message"] = data
object.saveInBackground()
So I use the saveInBackground method just for simplicity however if you should other saveInBackground method where you could check if there is no error while you are saving into Parse.
Retrieve
#IBOutlet weak var textlabel:UILabel!
var query = PFQuery(className:"Data")
query.getFirstObjectInBackgroundWithBlock({ (objects:PFObject?, error:NSError?) -> Void in
if error == nil {
let retrieveData = objects?.objectForKey("message") as! String
//so lets say you had a UILAbel
self.textlabel.text = retrieveData
}
})
I used the getFirstObjectInBackgroundWithBlock method because I assume that I have at least one object save in Parse. So if you are retrieve a lot of data you could findObjects method .
Hope that helps :)
To answer your comment on Lamars's fine answer:
Create a class ("Add class") "Quotes" in Parse, if you haven't already. Create a column in that class named "Quote", set it's type to String. Click "Add Row" and you will get a new row in your table. Double Click it's field and you can write your quote there.
This is me editing the usernameField in my Parse Class:
You could have another column named "Author" of type String, and just do the same thing, but if your app gets more advanced and you would like to display all the quotes from a specific author, you should add another class, named "Author". Add a column named "Name", double click and submit your name.
In your "Quotes" class, add a column named "Author", type Pointer, and make it point to your Author Class. Then copy the correct objectId from Pointer (let's sat Steve Jobs has objectId "12345678") and paste it to the "Author" column in Quotes. Now, if there's another quote by Steve Jobs, you can re-use that objectId, not having to store the name "Steve Jobs" more than once.
I understand you're new to Parse.com and maybe databases as well, but this way of creating relations is very good knowledge, if you want to design stuff in the future.
Parse has a great documentation, in Obj-C and Swift, check it out:
https://www.parse.com/docs/ios/guide