How can I CORRECTLY append map array to firestore? - flutter

I want to add Map<> data to my firestore database with code :
Map<String, Object> prop = {
'read' : true,
'vote_left' : false,
'vote_right' : false,
};
Map<String, Object> attended_topic =
{
received_id:prop
};
FirebaseFirestore.instance.collection('userinfo').doc(user_now!.uid)
.update({"attended_topic": FieldValue.arrayUnion([attended_topic])});
What I expected is this.
attended_topic
topicId_0
read : true
vote_left : false
vote_right : false
But I got something unexpected.
attended_topic
0
topicId_0
read : true
vote_left : false
vote_right : false
I never expected that new category '0' appearing and donot know why. Since the atabase has other properties so I thought using update() rather than set() is adequate. Please somebody tell my why this happens.

From the docs
FieldValue.arrayUnion adds elements to an array but only elements not already present.
So {"a": FieldValue.arrayUnion([b])} adds b variable to the Array a.
To solve your problem, just remove FieldValue as shown below.
FirebaseFirestore.instance
.collection('userinfo')
.doc(user_now!.uid)
.set({"attended_topic": attended_topic}, SetOptions(merge: true));
// use set method to add new data (not update)
// or
FirebaseFirestore.instance.collection('userinfo').doc(user_now!.uid).set(
{
'attended_topic': {
received_id: {
'read': true,
'vote_left': false,
'vote_right': false,
}
}
},
SetOptions(merge: true),
);

I solved this problem referring Peter's solution and changing it slightly.
FirebaseFirestore.instance.collection('userinfo').doc(user_now!.uid)
.set({"attended_topic": attended_topic}, SetOptions(merge: true));

Related

How to get the length of map values flutter

Map<String, bool> _toDos = {
'case1': true,
'case2': false,
'case3': false,
'case4': false,
'case5' : false
};
Say I have a map object like this and I want to get the length of strings that contains "true" values. How do I do so with flutter?
_toDos.length gives me the length of the map but I want to get the length of items that contain only "true" values.
_todos.values.where((element) => element == true).length

Add element into an existing Map in Cloud Firestore

Through Flutter I want to add a Map element in Firestore.
Instead of fetching the whole map and replacing it with the added element is it possible to directly add a new key and value?
Example:
Map = { '2020-05-21' : true } -- Add --> Map = { '2020-05-21' : true, '2020-05-22': true }
This is how I tried to do this but without any success.
return collection.document('id').updateData({'map.$date' : true});
Use this approach:
final ref = Firestore.instance.document('collection_id/document_id');
await ref.updateData({
'field.key2': 'value2',
});
Since I am not having an idea about how your data looks like on server, I tested this one and it worked.
Edit:
// initial work
var ref = Firestore.instance.document('Employees/54G...Naf');
var initialData = {
'date_1': '1',
};
await ref.setData({
'busy_map': initialData,
});
// updated work
var updatedData = {
'busy_map.date_2': '2',
};
await ref.updateData(updatedData);

Mongoose: atomic FindOne-Or-Insert(), do not update existing instance if found

In Mongoose, I am seeking to perform atomically a way to Model.FindOne-Or-Insert(), similar functionality and signature as currently available Model.FindOneAndUpdate() except if an instance exists (i.e. matches filter) then do not update using provided object but return instance found as is, and if not exists (i.e. no match with filter) then insert object and return new instance.
I could not find a way using Model.FindOneAndUpdate() not to perform an update to an existing instance by trying out variances to its options and not providing fields to object that preferred not to update if instance exists.
So, my current non-atomic workaround is Model.FindOne() and if not found then perform Document.save().
const Foo = DB.model('foo', FooSchema)
async function findOneAndUpdateFoo(jsonFoo, next) {
const filter = {
deletedAt: null
}
if (jsonFoo.dsAccountId) {
filter.dsAccountId = jsonFoo.dsAccountId
}
if (jsonIntegration.dsUserId) {
filter.dsUserId = jsonIntegration.dsUserId
}
if (jsonFoo.providerId) {
filter.providerId = jsonFoo.providerId
}
const fooDoc = {
name: jsonFoo.name,
dsAccountId: jsonFoo.dsAccountId,
dsUserId: jsonFoo.dsUserId,
providerId: jsonFoo.providerId,
providerName: jsonFoo.providerName,
// Most of these fields could be empty
accessToken: jsonFoo.accessToken,
refreshToken: jsonFoo.refreshToken,
scope: jsonFoo.scope,
tokenType: jsonFoo.tokenType,
expiresAt: jsonFoo.expiresAt
}
return await Foo.findOneAndUpdate(
filter, // find a document with that filter
fooDoc, // document to insert when nothing was found
{ upsert: true, new: true, runValidators: true } // options
)
.catch(next)
}
Suggestions? Thank you
You can use $setOnInsert in your update parameter so that it will only apply in the insert case; with the update becoming a no-op in the case where the document already exists:
return await Foo.findOneAndUpdate(
filter, // find a document with that filter
{$setOnInsert: fooDoc}, // document to insert when nothing was found
{ upsert: true, new: true, runValidators: true }
)
Note that you should also create a unique index over the fields included in your filter and then handle the possibility of a duplicate error. See this post for the details why.

What am I doing wrong when manipulating data in Meteor/MongoDB?

I have this helper
myClub: function(){
var currentUserId = Meteor.userId();
var user = Meteor.users.findOne({_id: currentUserId});
return user;
}
I want it to return user.role
Here is my user in MongoDB
{
"_id" : "RdirmrLG3t8qBk4js",
"createdAt" : ISODate("2016-04-17T19:40:56.877Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$cPe92XR9DT238bH/RanYEu.J6K2ImvAEbWOcVq6j9luI0BH08Qdly"
},
"resume" : {
"loginTokens" : [
{
"when" : ISODate("2016-04-17T19:51:49.474Z"),
"hashedToken" : "uVKUj/7JEkkOuizXhjl212Z38E47HXCex+D4zRikQ1k="
}
]
}
},
"username" : "worker",
"role" : "worker",
"club" : "hzSKAJfPXo7hSpTYS"
}
The code above works just fine. So it finds the current user and outputs info about it. But when I change user to user.role I get the following errormessage.
TypeError: Cannot read property 'role' of undefined
at Object.myClub
How can it be undefined? Is my syntax incorrect?
Template helpers are reactive, which means they update themselves as the app state changes or new data appears. In your case, the helper is called immediately when the template is rendered and before the Meteor.users collection is filled. Therefore, the .findOne() method returns undefined. It will be corrected in the second pass after new data arrives.
The simple fix here is to check whether the data is present inside the helper:
myClub: function(){
var currenUserId = Meteor.userId();
var user = Meteor.users.findOne({_id: currenUserId});
if(!user) return 'NO DATA';
return user.role;
},
In real life you'll probably want to wait for the basic data to be loaded before you render the template. That is usually done on the controller level.
Try:
myClub: function(){
return Meteor.user() && Meteor.user().role;
}
This is shorthand for return the role if there's a user.
As far as the role field not showing up, make sure that you are publishing that key from the server and subscribing to it. For example:
Meteor.publish('me',function(){
return Meteor.users.find(this.userId,{fields: {role: 1, username: 1, profile: 1, emails: 1}});
});
And on the client:
var me = Meteor.subscribe('me');
if ( me.ready() ) console.log("Ta-da! The role is: "+Meteor.user().role);
make sure that you subscribed to all data you need.
By the way, you can try following:
role: function(){ return (Meteor.user() || {}).role; }
Cheers

How to update jstree node values without reload

I have a jstree that I created with the following code:
$('#mytree').jstree({"core": { "data" : value
, "themes" : { "dots": false
, "icons": false }
}
}
);
I can rebuild it with new data by this code:
$('#mytree').jstree(true).settings.core.data = new_data;
$('#mytree').jstree(true).refresh();
but it can be expensive when you have a lot of nodes. What I would like to achieve is that I would like update the value of the elements (i.e. the node.text part) without rebuilding the whole tree. I get the new values via websocket in one message (the complete JSON string that will be the new_data) but the structure is not changing. How can I do that? Thank you!
What you need is not refresh() but redraw() thus the code is
$('#mytree').jstree(true).settings.core.data = new_data;
$('#mytree').jstree(true).redraw(true);
You can find the functions in the jstree API.
As per zmirc suggestion, in v3.1 use:
$('#mytree').jstree(true).settings.core.data = new_data;
$('#mytree').jstree(true).refresh();
for deleting the node and reload tree
$('#mytree').jstree(true).refresh();
for those who need to redraw without restart the tree use
jQuery('#data').jstree(true).refresh(true);
worked for me: $('#structureRows').jstree("destroy").empty();
function CreateStructureTree(jsonData)
{
$('#structureRows').jstree("destroy").empty();
$('#structureRows').jstree
({
'core' : {
'data':
[
jsonData,
{
'id' : 'node_2',
'text' : 'Root node with options',
'state' : { 'opened' : true, 'selected' : true },
'children' : [ { 'text' : 'Child 1' }, 'Child 2']
}
]
}
});
}
You can refresh node by this
$('#treeView').jstree(true).refresh_node("node_id_here")
$('#mytree').jstree(true).refresh();
is working, but in my case it causes thread leak.
every refresh adds one more thread
I load data via an url, so my refresh part looks like:
$('#groupTree').jstree(true).settings.core.data.url = get_group_url();
$('#YourJSTREE').jstree("destroy").empty();
Set new data
$('#YourJSTREE').jstree(true).refresh();