-
Notifications
You must be signed in to change notification settings - Fork 892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FR: FieldValue arrayUnion/arrayRemove for array with objects #1918
Comments
@emadalam Thanks for filing this issue! Since array-union currently treats all elements as distinct, using a nested key-value object might be a better fit for you. If you can key your data by user ID, you could potentially do the following:
We will keep your feature request in mind as we shape our APIs, but you might be able to adjust your data model to take advantage of our existing features. As a further comment, if you do have to rely on Arrays and need to download, modify and set your data manually, please take a look at our Transaction API. If you perform these operations in a transaction, then you will not encounter issues with stale data. |
@schmidt-sebastian Thanks for your suggestion. Though it was just an example model. I do understand that I can model all my array needs into a hash map with unique keys, but that essentially means I'm not using arrays anymore. Furthermore it would be tricky to remove items from the hash map without performing a complete update/set operation, while in case of I'd keep an eye on this issue and see if the feature request gets implemented in near future 🙏 |
@emadalam You can remove a single key from a map as such:
|
@schmidt-sebastian Thanks for the input, however all the approaches are under the assumption that we model the data as hash map. The original question with array values, still remains. Until the feature request is added, I'm using the below version for now that makes use of transaction api to update the array. I still don't like the fact that I need to fetch data and run a transaction just to perform an update operation, but I guess as of now there's no better alternate that exists 🤷♂If you know any better/faster alternate to perform the same, feel free to mention here 💪 import isPlainObject from 'lodash.isplainobject'
import get from 'lodash.get'
import partialRight from 'lodash.partialright'
import unionBy from 'lodash.unionby'
import reject from 'lodash.reject'
import firebase, { db } from 'configs/firebase'
export async function arrayUnion({ documentRef, path, uniqueBy, data }) {
if (!uniqueBy || !isPlainObject(data)) {
return documentRef.update({
[path]: firebase.firestore.FieldValue.arrayUnion(data),
})
}
const updateFn = partialRight(unionBy, uniqueBy)
return updateArray({ documentRef, path, data, updateFn })
}
export async function arrayRemove({ documentRef, path, uniqueBy, data }) {
if (!uniqueBy || !isPlainObject(data)) {
return documentRef.update({
[path]: firebase.firestore.FieldValue.arrayRemove(data),
})
}
const updateFn = values => reject(values, { [uniqueBy]: data[uniqueBy] })
return updateArray({ documentRef, path, data, updateFn })
}
export async function updateArray({
documentRef,
path,
data,
updateFn = firebase.firestore.FieldValue.arrayUnion,
}) {
if (!documentRef || !path) return
return db.runTransaction(async transaction => {
const doc = await transaction.get(documentRef)
if (!doc.exists) return
const dataArray = Array.isArray(data) ? data : [data]
const existingValues = get(doc.data(), path)
const newValues = updateFn(existingValues, dataArray)
await transaction.update(documentRef, { [path]: newValues })
})
} Usage: arrayUnion({
documentRef: db.doc('myCollection/xxxx'),
path: 'uploadedDocuments',
uniqueBy: 'uid',
data: {
uid: 'yyyy',
name: 'something.png',
url: '....',
},
})
arrayRemove({
documentRef: db.doc('myCollection/xxxx'),
path: 'uploadedDocuments',
uniqueBy: 'uid',
data: {
uid: 'yyyy',
name: 'something.png',
url: '....',
},
}) |
@emadalam Unfortunately, there is no succinct way to manipulate array data based on partial equality. We will keep this issue updated as we evolve our APIs. If you do need to use arrays in your documents, updating them inside a Transaction (as shown in your code sample) is the best way forward at this point. |
I'd like to register a vote for this functionality. |
Very much needed! |
i encounter the same problem but in my case, i already downloaded the data because i'm subscribing to a stream, the problem is that I'm not sure how to combine this with the Transaction API |
@emadalam Did you get this to work? Trying out your code, but i'm a bit unsure if partialRight does what.. I want it do to. Take a simple checklist for example:
Seems to just leave the array in the same state. |
@viktorlarsson The code works just fine for the use case. However from what I see for your specific use case you'd need to change the const updateFn = (values, updates) =>
_(values)
.keyBy(uniqueBy)
.merge(_.keyBy(updates, uniqueBy))
.values()
.value() export async function arrayUnion({ documentRef, path, uniqueBy, data }) {
if (!uniqueBy || !isPlainObject(data)) {
return documentRef.update({
[path]: firebase.firestore.FieldValue.arrayUnion(data),
})
}
const updateFn = (values, updates) =>
_(values)
.keyBy(uniqueBy)
.merge(_.keyBy(updates, uniqueBy))
.values()
.value()
return updateArray({ documentRef, path, data, updateFn })
} |
Works like a charm, thanks @emadalam ! |
Since we are talking about arrayUnion for an array of objects, how is it possible to add an object to an array of objects? For example below is some sample data myTodoList: [
{text: 'wash the dishes', completed: true},
{text: 'clean room', completed: false}
] How would I add to the above array of objects another todo object? arrayUnion simply isn't adding an object to an array and seems to only be able to add basic values. I would appreciate some help. |
@ShadeAJ1 - The
If this isn't working please open a new issue with repro steps so we can look into it. |
@rafikhan I know how to add an object that way but what about with a variable like this.. myObject = {text: 'take out trash', completed: true}
docRef.update({
myTodoList: firebase.firestore.FieldValue.arrayUnion(myObject)
}); I would use this where I do not know all the values of an object or if I have different amounts of values in objects I would like to add. Adding an object like this doesn't work and the object doesn't get added to the array. Let me know if I should open a separate issue and I will do so. Thank you! |
@rafikhan Why is this issue closed? Is the original feature request implemented as part of some PR or release somewhere? It's strange at the very least to just randomly close the issue for an unrelated random comment 🤷 |
@emadalam - You're right. I usually close them for customer support issues and it should have been left open since the FR hasn't been resolved. |
@ShadeAJ1 yes, you should file a new issue with a repro of your issue. |
@rafikhan ok I will do so. |
problem when I use arrayUnion () in firebase it deletes all the array that I have saved in my database |
@SebastianMena-185 - Please open a new issue with a code sample. |
➕ 1 |
This comment has been minimized.
This comment has been minimized.
@emadalam I am trying to do something similar but I can't figure out how your workaround works. I don't think it makes a difference by my app is in react and using react-redux-firebase but its a very similar issue to yours with arrayRemove not working My data looks like this
I want to be able to remove a single task by array index. Right now i have (in my example i am passing 3 which is an example array index, but in reality, I am passing the array key programmatically via react props but that shouldn't make a difference to this). Any ideas thanks!
|
I was pretty surprised to find out Wouldn't removing by index be easy to implement (compared to a deep equality check that scans the entire array)? |
[REQUIRED] Describe your environment
[REQUIRED] Describe the problem
Steps to reproduce:
firebase.firestore.FieldValue.arrayUnion
andfirebase.firestore.FieldValue.arrayRemove
are great if the array is made up of primitive values. However to update a field value which is made up of array of objects, it's impossible to perform the operation without querying the actual data and performing the union/without operation on the client.While this is alright if we already have queried the data, but in cases where we haven't queried the data to begin with, it's an unnecessary overhead to make this request. What's worse is when we haven't subscribed to the document updates and are trying to manipulate the array with the stale values, it's going to cause more harm than good, defeating the purpose of allowing array operations with
arrayUnion
andarrayRemove
.Relevant Code:
What is desirable is an option to set
uniqueBy
parameter or something similar.The text was updated successfully, but these errors were encountered: