Adding new data with identical fields to Firebase without overwriting old data? Flutter - flutter

So, essentially I'm trying to allow my users to click a button and add each individual username to a document on Firebase. However, .set() obviously sets brand new information while .update() overwrites the old information. I've tried using SetOptions(merge: true) when writing to Firebase, but that hasn't proven effective either.
I would love it if someone could point me in the right direction and/or explain what I'm doing wrong. This is how I'm handling it currently:
CollectionReference scatterSignup =
FirebaseFirestore.instance.collection('scatterSignup');
onPressed: () async {
scatterUp.name = username;
scatterUp.code = userCode;
final scatter = scatterSignup.doc(regionUpload).set({
'name': scatterUp.name,
'code': scatterUp.code,
}, SetOptions(merge: true));
}),
I expected new information to be added without overwriting (which I believe is the function of merge unless I'm mistaken?) but that definitely didn't happen.
Essentially, I'm trying to figure out how to have a single document that consists of multiple name and code fields: one for each user trying to sign up for the list.

Related

How to determine newly inserted data upon using upsert? prisma

I have a working code below that inserts a data if it does not exist (but not updates if it exist). In below implementation I am looping upsert, and it just works fine.
My question is, how to get those newly inserted data? (exclude data that is already existing). Do have idea how to achieve this, in a shortest way as possible?
I did some research about it and found this possible github solution, but I don't get the point. Because it also returning data even its already existing.
this.data = await prisma.$transaction(
temp.map((provider) =>
prisma.provider.upsert({
where: {
user_id_api_key: {
user_id: provider.user_id,
api_key: provider.api_key
}
},
create: provider,
update: {}
})
)
)
console.log(this.data) // it still return data even if its already existing

Saving to-do list items to be reviewed later?

I am extremely new to Flutter, so please forgive my ignorance and please explain things to me like I am a toddler.
I would like to implement a to-do list into my app similar to this project: https://github.com/ishrath-raji/todoey-flutter
It's just a basic list where users can add items, cross them out, and delete them. Very simple.
However, I have absolutely no idea how to take the items that users enter into the to-do list and store them in memory so that the user can review them later.
I've tried googling around, but all the answers I've seen are above my understanding and/or written in a way that is difficult to follow.
Any help would be very much appreciated!
I assume you have a view / page, where the user can give all the necessary information for the new ToDo item. This means you have a class representing a ToDoItem.
class ToDoItem {}
You will want to store them in some List.
As you probably want this list of ToDo items to be accessbible everywhere within the map, you should start researching the topic "state management".
As a starting point, just to name the easiest of all solutions, you could use Riverpod and declare one global variable:
final todoListRereference = StateProvider<List<ToDoItem>>((ref) => <ToDoItem>[]);
Now you have a list of ToDoItem which is accessible from everywhere in your app, provided you follow the steps to make Riverpod providers accessible everywhere. For example, in every build method you can use
final todoList = ref.watch(todoListRereference);
and you have access to all the stored ToDoItems.
In the case of your ToDoItem you can create with all the user information, you will have a construction like:
onPressed: () {
final todoItem = ToDoItem(...);
final todoListProvider = ref.read(todoListReference.notifier);
todoListProvider.state = [... todoListProvider.state, todoItem);
}
I just assumed it would happen after the user clicked something and the onPressed method of the according button is triggered.... However, first you create the ToDoItem. Then you access the List we made accessible with the reference. Then we change the "state" of that provider to a new state which is defined as all the old ToDoItems plus the newly created one.
If you have any pages in your app where you can see all the ToDoItems, you will now see one more.
I hope this is okay as a starting point.

E11000 duplicate key error collection {info.subs: null}

My app isn't letting me create more than one profile for some reason. Here's the setup in the service file:
//This finds the profile if it exists
async getProfile(user) {
let profile = await dbContext.Profile.findOne({
email: user.email
});
profile = await createProfileIfNeeded(profile, user);
await mergeSubsIfNeeded(profile, user);
return profile;
}
//This is supposed to create one if one doesn't exist
async function createProfileIfNeeded(profile, user) {
if (!profile) {
profile = await dbContext.Profile.create({
...user,
subs: [user.sub]
});
}
return profile;
}
It works for the first user, but when I make another, I get the error:
{"error":{"message":"MongoError: E11000 duplicate key error collection: TownMiner.profiles index: info.subs_1 dup key: { info.subs: null }","status":400},"url":"/api/profile"}
What's confusing is that subs are set via Auth0. When I look at it with a break-point in the server, it shows all the info there. Also, when I look in my MongoDB collections, nowhere does it say that any of the values are "null". I've used this same setup for a few projects now and they've all worked perfectly (and this new project is cloned from the same template). Also noted to make sure that the sub info is all different and it is.
This is the MongoDB collection:
_id: ObjectId("***")
subs:Array
0:"auth0|***dda6a"
1:"auth0|***aa288
name:"kevin#test.com"
picture:"https://s.gravatar.com/avatar/c6788456e2639d2d10823298cc219aaf?s=480&r..."
email:"kevin#test.com"
createdAt:2020-08-07T21:23:05.867+00:00
updatedAt:2020-08-17T17:24:05.583+00:00
__v:1
I've looked at the other answers for similar questions on here but couldn't quite find where it fit into this project. Any help would be great. Thanks!
There is a unique index in the TownMiner.profiles collection on {info.subs:1}.
That sample document doesn't include an info field, so the value entered in the index for that document would be null.
Since the index is tagged unique, the mongod will not permit you to insert any other document that would also be entered into the info.subs index using null.
Turns out the error was because I went on the mongoDB site and manually added a collection and probably set it up wrong. I deleted it and let my app build the collection itself and it seems to be working fine. Thank you for taking time to help! Always appreciated!

Any way to conditionally recall "FirestoreConnect()"; Or call multiple times for different Collections? (React-Redux)

I'm working on a parent component, with conditionally rendered children components. I am simplifying the parent component a bit to help clarify my issue.
Display 1: Display list of "Courses"
The page starts off displaying a list of the user's "Courses"; a "Course" is just a collection inside Firestore. To access these collections, I call the "FirestoreConnect()" at the export default line of the end of this parent component.
However, once the user clicks on one of the Courses that are displayed, the screen enters Display 2.
Display 2: Display list of "Lectures"
Once the user chooses a course, I want it to display a list of that courses "lectures". "Lectures" are simply Firestore sub-collection within that specific courses collection. However, since I already called the "FirestoreConnect()" once already during the initial rendering of this parent, I don't know how to recall it specifically to read the selected sub-collections material. (Since I didn't know before hand which course the user would choose.)
Is there a way to continue recalling a FirestoreConnect() multiple times, for different collections, inside the same parent component?
Since we don't see your code, I will be guessing how you are passing the parameters for your firestoreConnect(). I'm saying that to clarify that you need to pass the subcollection/subdocument hierarchy information as to firestoreConnect(). So, your code would be something like below:
const enhance = compose(
firestoreConnect(props => {
return [{ collection: "Courses", doc: props.uid, subcollections: [{ collection: "Lectures" }], storeAs: `${props.uid}-lectures` }];
}),
connect(({ firestore }, props) => {
return {
lectures: firestore.ordered[`${props.uid}-lectures`] || []
};
})
);
...
const Lectures = ({ firestore, lectures }) => {
return <YOUR_LECTURES_LIST_UI>
}
While this code is untested, it was based from this tutorial here and I believe it might help you. You just need to know that the subcollection is still referenced in your firestoreConnect(), so the application already has this part of the collection configured.
In case this still doesn't help you, I would recommend you to check this other similar cases below, because they might help you as well.
Choose firestore subcollection when connecting component to redux with react-redux-firebase
How to load firestore subcollection to redux store?
Let me know if the information helped you!

Mongoose ('findOneAndUpdate') Middleware: Need access to original document

I am attempting to use pre('findOneAndUpdate') to update the icon attribute of the Meeting document. The update is based on the pre-existing value of the yearlymeeting attribute (see below).
Because pre and post save() hooks are not executed on update(), I seem to be unable to access the original document at all. Yet this is critical for the operation I'm trying to perform. Is there any way around this?
For example, I am able to accomplish my purpose on pre('save'), like so:
meetingSchema.pre('save', function(next) {
const yearlymeetingSlug = this.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, '');
this.icon = `${yearlymeetingSlug}.png`
next();
});
What I would like to be able to do is something like this:
meetingSchema.pre('findOneAndUpdate', function(next) {
const yearlymeetingSlug = originalDocument.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, '');
this.icon = `${yearlymeetingSlug}.png`
next();
});
I understand that this in pre(findOneAndUpdate) refers to the query, rather than the stored document itself. Is there any way to access the document, so that I can update icon based on the stored value of yearlymeeting?
tl;dr
Not possible via middleware. Query for the doc first, and then separately update a specific version of the doc to prevent race conditions.
Can't do it the way you're trying according to this issue on the Mongoose Github (from the main dev):
By design - the document being updated might not even be in the server's memory. In order to do that, mongoose would have to do a findOne() to load the document before doing the update(), which is not acceptable.
The design is to enable you to manipulate the query object by adding or removing filters, update params, options, etc. For instance, automatically calling .populate() with find() and findOne(), setting the multi: true option by default on certain models, access control, and other possibilities.
findOneAndUpdate() is a bit of a misnomer, it uses the underlying mongodb findAndModify command, it's not the same as findOne() + update(). As a separate operation, it should have its own middleware.
Following this, there are no other suggestions in the issue thread to access the original document inside of the middleware itself.
What I've seen done (and what I've had to do many times myself), is simply have to query for the document before updating it (which, of course, could lead to a race condition depending on who is updating the doc, and when, but you can fix that by also querying for a specific version of the document -- a sort of "optimistic locking"):
let meeting = yield Meeting.findOne({}).exec()
let update = {}
// ... some conditional logic to figure out which icon to set
update.icon = // whatever
yield Meeting.update({ _id: meeting._id, version: meeting.version }, update)
This is of course assuming you have a "version" field in your schema. This sort of locking will prevent you from updating an old version of the doc. If you're gonna use this kind of versioning, you'll also probably want to add some middleware that updates the version of a doc any time the doc is updated/saved.
You can also use a more naïve implementation, where you don't use locking, which may be fine in your specific business case, as long as you're aware of the possibility of a race condition, and the risks.
This may not be the best solution, but I did find a way to make it work. I used the controller rather than schema pre hooks. Here's what my update controller looks like now:
exports.updateMeeting = async (req, res) => {
const _id = req.params.id
let meeting = await Meeting.findOneAndUpdate({ _id }, req.body, {
new: true,
runValidators: true
});
/* New Code: */
const yearlymeetingSlug = meeting.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, '');
meeting.icon = `${yearlymeetingSlug}.png`;
meeting.save();
req.flash('success', 'meeting successfully updated!');
res.redirect(`/meetings/${meeting.slug}`);
};
I welcome your feedback on any problems you see with this solution.