Firestore .set not saving data from firebase cloud function - google-cloud-firestore

I am trying to save data from my form in firestore. The collection users already exists and the document 'email' also exists already. It's not saving and nor does it give any error or messages.
Any ideas why that would happen? I am able to save data from another page though. I copied the same const admin = require('firebase-admin');
and
const db = admin.firestore();
to this page as well, exactly same. Not working:
let setDoc = db
.collection('users')
.doc(email)
.set({'abc':'def'},{merge: true});
// .set(Encdata,{merge: true})
return setDoc.then(res => {
console.log('Set: ', res);
})
.catch(error => {
console.log(error);
});
Working from another page:
let setA = docRef.set({
'username': encDec.encrypt(user_name),
'password' : encDec.encrypt(password),
'email': email,
'uid': UserID
});```
Before and After I trigger the function, data remains the same. All instructions in the function execute fine UNTIL the control reaches the "let setDoc = db" line. After that everything just silently quits.

From your description and codes, it seems you intended to overwrite a single document. Based on the Add data to Cloud Firestore documentation set a documentation to overwrite a single one, the first set method fit for the situation.

Finally I figured this one out. Maybe because too much of screen time, but basically I was messing up the init statement
instead of
db = admin.firestore();
I had the statement:
db = admin.database();
==> this was messing up all my references to db.
So db.collection().get() was not working.
Thanks to all who answered.
Hope this helps someone.

Related

Query Firestore documents with role based security via Flutter

I´ve a role based data model on Firestore according to googles suggestion here: https://firebase.google.com/docs/firestore/solutions/role-based-access
Security rules are set up correctly and work fine. But now I´ve the problem on how to query for the roles.
This is my data model (one sample document):
id: "1234-5678-91234",
roles:
userId_1:"owner",
userId_2:"editor
title: "This is a sample document"
And this is my Firestore Query in Flutter which gets all documents for a specific user by its ID if the user has assigned the role "owner" for the document:
return firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "owner")
.snapshots().map((snapshot) {
return snapshot.documents.map((catalog) {
return SomeDocumentObject(...);
}).toList();
});
My problem now is, that I need some kind of "OR" clause - which does not exist as far as I know. The query above only retrieves documents for users with role "owner" but I need a query that also retrieves the document if the userId is associated with the role "editor".
I´ve tried "arrayContains:" which also doesn´t seem to work (cause it´s a map).
I´ve read about solutions with two independent queries which doesn´t sound like a good solution due to a lot of overhead.
Maybe someone of you have a hint for me? :)
Thanks & best,
Michael
Firestore doesn't currently have any logical OR operations. You'll have to perform two queries, one for each condition, and merge the results of both queries in the client app.
This is the final solution using RxDart, Observables and .combineLatest() - maybe it helps someone out there:
#override
Stream<List<Catalog>> catalogs(User user) {
// Retrieve all catalogs where user is owner
Observable<QuerySnapshot> ownerCatalogs = Observable(firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "owner")
.snapshots());
// Retrieve all catalogs where user is editor
Observable<QuerySnapshot> editorCatalogs = Observable(firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "editor")
.snapshots());
// Convert merged stream to list of catalogs
return Observable.combineLatest([ownerCatalogs, editorCatalogs],
(List<QuerySnapshot> snapshotList) {
List<Catalog> catalogs = [];
snapshotList.forEach((snapshot) {
snapshot.documents.forEach((DocumentSnapshot catalog) {
catalogs.add(Catalog(
id: catalog.documentID,
title: catalog.data['title'],
roles: catalog.data['roles'],
));
});
});
return catalogs;
}).asBroadcastStream();
}

Meteor subscription to get only new documents

Just wondering if there is a way to set my meteor subscription to load only new documents from a mongo collection, avoiding to sync deletes and updates (Since they are not relevant in the data that is shown to user).
Why I need that? It seems anytime I do a Meteor.subscribe after an offline period, the WHOLE collection is sent again from server to client, while I only need the new records.
I think this happen to keep local/remote database integrity, but since my app is planned to work online/offline (I'm using also groundDB), it seems to me It will be very inefficient in terms of data usage.
Thanks in advance.
You can create a publish which sends only new documents. Like:
Meteor.publish('newDocumentsOnly', () => {
let initializing = true;
const handle = Collection.find().observeChanges({
added: (id, fields) => {
if (initializing) return;
this.added('Collection', id, fields);
}
});
initializing = false;
this.ready();
this.onStop(() => {
handle.stop();
});
});

create personal collections for logged in users

import {favRestaurants} from '/lib/collections';
import {Meteor} from 'meteor/meteor';
import {check} from 'meteor/check';
export default function () {
Meteor.methods({
'favRestaurants.create' (id, name, rating, priceLevel, type) {
check(id, String);
check(name, String);
check(rating, Number);
check(priceLevel, Number);
check(type, String);
const createdAt = new Date();
const restaurant = {id, name, rating, priceLevel, type, createdAt};
if(check(Meteor.user()) == null){
console.log('onlye logged in users can data');
}else{
FavRestaurants.insert(restaurant);
}
}
});
}
This is my insert method for adding data to the restaurants collections. When i console log the 'check(Meteor.user())' in the console i get null as output. By that logic you shouldn't be able to add data to the collection, although this is still possible.
I would also like to make the FavResaurants collection personal for each user. Iv'e tried to check if there is a user and then adding a collection in the main.js file on the client side.
Meteor.loggingIn(() => {
console.log('check for user method');
var restId = 0;
if(Meteor.user() != null){
console.log('created new collection for the user');
const FavRestaurants = new Mongo.Collection('favRestaurants' + restId);
}
restId++;
});
I dont get any output to console using this method, which i found in the meteor docs. Is the code in the wrong place? Any help is much appriciated.
According to the docs the Accounts.ui.config is the method i should use. But I'm not sure in code i should put it. So far the placement of this method has resulted in my application crashing.
Answering your first question, to allow only logged-in clients to access a method, you should use something like:
if (!Meteor.userId()) {
throw new Meteor.Error('403', 'Forbidden');
}
Now, I see you want a collection to store favorite restaurants for each user in client side. But as I see it, there'd be only one logged in user per client, so you don't need a separate collection for each user (as the collection is in each client), you can just refer the user with it's id, and then fetch a user's favorite restaurants by a query like:
FavRestaurants.find({user: Meteor.userId()});
Moreover, as the docs suggest, Meteor.loggingIn is a method which tells you if some user is in the process of logging in. What you are doing is over-riding it, which doesn't make sense.
You should do something like:
if (Meteor.loggingIn()) {
// Do your stuff
}
Hope it gives you more clarity.
Creating a collection per user is a bad approach.
Define your favRestaurants collection once and add a owner field in the restaurant document before insert.
Create a publish method to publish to client side the userid favrestaurant only.
One more thing, check your userid first in your Meteor method, it will avoid unnecessary proces.
Regs

mongoosejs upsert with on save hook

I'm trying to add data to my mongo database with mongoose, but there is a high probability that most of the data is already in the database, only a small number of fields need to be updated. Creation time for the record and last time updated need to be saved.
My first attempt at solving this problem included using the Model.save function, given that my model is called server, and data is an object coming from an external http service, which specifies the unique _id in data.
var instance = new Server(data);
instance.save(function(err){
if(err)
console.log(err);
});
also my pre-save hook:
ServerSchema.pre('save', function(next) {
this.lastseen = Date.now();
if (!this.isNew)
return next() //if the entry isn't new, lets not change the date registred
this.registered = Date.now();
next() //Don't forget this!
})
The problem here is that on duplicate _id the save chokes, with error E11000 duplicate key error index...
This now makes sense as save only does an update when the document instance is not created using the new operator.
So in my next attempt, I added code to attempt to lookup the document, then used underscore.js's _.extend to merge the new document with the one found in the database, then saved that to the database. The problem with this approach is that it require an extra call to the database for each chunk of data being processed.
My third attempt uses the Model.findByIdAndUpdate with {upsert:true} this works, in terms of stroring the data in the database, but schema defaults and my pre-save hook isn't triggered.
The fourth attempt uses code suggested by #aheckmann in this gist: https://gist.github.com/2764948
var server = new Server();
server.init(ping);
server.save(function(err){
if(err) {
console.log("DB Error: ",err);
return res.send('DB Error')
}
//if server approved, tell the inworld server to sync textures
if(server.approved)
res.send('success')
else
res.send('skip')
user.servers.addToSet(ping._id); //add the server to the user's list
user.save(function(err, usr){
if(err)
console.log("DB Error: ", err);
})
})
Here again, the pre-save hook isn't triggered. Am I to understand that the only to upsert with hooks is to attempt to find the document first with a findById ?
Q:
Is there a way to "upsert" Insert or Update based on the primary unique key without making more than one database call per chunk of data? Is there a method, or obvious fact that I am overlooking?
I don't think that you can do it with less then two calls to DB, unless you'll drop mongoose part and use mongo driver directly. But you can create a static method to do all the job for you:
ServerSchema.statics.findOrCreate(function(doc, next) {
this.findById(doc._id, function(err, res) {
res || (res = new this);
_.extend(res, doc); // add new data to the document
next(err, res); // if (err != null) then something went wrong
});
});
findByIdAndUpdate not triggers presave hook because it calls mongo driver directly.

How to store data inside mongodb

js and mongodb.I have created a model file named models like the one given below
User = new Schema({
username : String
, password : String
, created_at : Date
});
mongoose.model('User', User);
exports.defineModels = defineModels;
In app.js i have called the defineModels like this:
var models = require('./models'),
models.defineModels(mongoose, function() {
app.User = User = mongoose.model('User');
db = mongoose.connect(app.set('db-uri'));
})
I can't call save method directly on User or can I?
i want to save data in User what could be the function for the same.any answer will be appriciated
To do what you want, you should have something like this:
var user = new User({username: 'Name', password: 'unsecure'});
user.save();
There are a few things odd with your code, so I highly suggest going over a tutorial that uses express and mongoose to create a sample site (most likely you can find a blog).
Here is one I made: https://github.com/mathrawka/node-express-starter
Good luck!