Meteor mongodb insert document of an array - meteor

This code has the server insert some documents in a collection for the client to find it later.
I need to return the array for a given task
But the page is saying
No data received
Why is that and how to fix it? Thanks
//Both
FooterButtons2 = new Mongo.Collection('footerButtons2');
//Server
Meteor.publish('footerButtons2', function(){
return FooterButtons2.find();
});
FooterButtons2.insert(
{ "task1": ["submit"]},
{ "task2": ["cancel","continue"]}
);
//client
Meteor.subscribe('footerButtons2');
var res = FooterButtons2.findOne("task1");

When you search like this:
var res = FooterButtons2.findOne("task1");
you are searching an object that has the "_id" key equal to "task1", this is not correct.
You want the object that has the key "task1" in it. The correct way would be:
var res = FooterButtons2.findOne({
task1: { $exists: true }
});
But ideally, you should be doing searches based on values and not keys. Something like this:
FooterButtons2.insert({
task: "task1",
buttons: ["submit"]
}, {
task: "task2",
buttons: ["cancel", "continue"]
});
var res = FooterButtons2.findOne({
task: "task1"
});

Related

What is the way to update or insert a record on Firebase with Flutter?

I have a collection users like this:
[{
'uid' : '1',
'favourites' : [
{ // fav1 },
{ // fav2 },
{ // fav3 },
etc
]
},
{
'uid' : '2',
'favourites' : [
{ // fav1 },
{ // fav2 },
{ // fav3 },
etc
]
},
etc
]
In some situations I have to update the favourites collection with a new "fav" and I can do that in this way:
final doc = FirebaseFirestore.instance.collection('users').doc(userId);
doc.update({ 'favourites': FieldValue.arrayUnion([fav.toJson()]) });
however the item might be not there so I have to use doc.set to create a new item. As I am new with Firebase, what is a "best practice" for a problem like this (if the element is not there create it first, otherwise update it)?
You can specify a merge option to set, which does precisely what you want:
doc.set({ 'favourites': FieldValue.arrayUnion([fav.toJson()]) }, SetOptions(merge : true))
You can use a function that can check if there is a doc or not with that specific info. And you can create a if-else statement depends on if there is a doc named like that or not.
An example function for checking the doc:
Future<bool> checkIfDocExists(String stuffID) async {
try {
/// Check If Document Exists
// Get reference to Firestore collection
var collectionRef = FirebaseFirestore.instance
.collection('favorites');
var doc = await collectionRef.doc(userId).get();
return doc.exists;
} catch (e) {
throw e;
}
}

How to order by child value in Firebase

I have data like this:
scores: {
uid1: {
score: 15,
displayName: "Uciska"
},
uid2: {
score: 3,
displayName: "Bob"
},
uid3: {
etc...
}
}
I want to rank them by score and keep only the top 100.
I did that by following the doc. But it does not work. It always returns the same order even if the score changes.
const query = firebase.database().ref('scores')
.orderByChild('score')
.limitToLast(100)
query.on('child_added', snapshot => {
const score = snapshot.val().score
console.log(score)
})
I added that too in the rules to optimize but I'm not sure it's correct:
"scores": {
".indexOn": ["score"]
}
What is the right way to go?
Your code is correct and should show the desired result.
You may encounter difficulties to see the result due to the 'child_added' event since "the listener is passed a snapshot containing the new child's data", as detailed here in the doc.
You could use the once() method, as follows, which will show the result a bit more clearly since it will display the entire set of scores.
const query = firebase.database().ref('scores')
.orderByChild('score')
.limitToLast(100)
query.once('value', function (snapshot) {
snapshot.forEach(function (childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
console.log(childData);
// ...
});
});
Also, your rule can be written as ".indexOn": "score" since there is only one parameter.

How to show documents from multiple remote publication in the template?

I wish to use Meteor to subscribe a few remote publication via DDP. Then show the documents in one template. Here is what I did:
Posts = {};
var lists = [
{server: "localhost:4000"},
{server: "localhost:5000"}
];
var startup = function () {
_.each(lists, function (list) {
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection});
connection.subscribe("allPosts");
});
}
startup();
This file is at client folder. Every startup, in this example, at browser I have two client collections Posts["localhost:4000"] and Posts["localhost:5000"], both are same schema. I know this format (Collection[server]) is ugly, please tell me if there is a better way.
Is there a way to show these client collections in the same template with reactive. Like this:
Template.registerHelper("posts", function () {
return Posts.find({}, {sort: {createdAt: -1}});
});
I think Connected Client is a big part of the Meteor. There should be a best practice to solve this problem, right?
Solved.
Connect to multiple servers via DDP, then observe their collections reactive via cursor.observeChanges.
Posts = {};
PostsHandle = {};
// LocalPosts is a local collection lived at browser.
LocalPosts = new Mongo.Collection(null); // null means local
// userId is generated by another Meteor app.
var lists = [
{server: "localhost:4000", userId: [
"hocm8Cd3SjztwtiBr",
"492WZqeqCxrDqfG5u"
]},
{server: "localhost:5000", userId: [
"X3oicwXho45xzmyc6",
"iZY4CdELFN9eQv5sa"
]}
];
var connect = function () {
_.each(lists, function (list) {
console.log("connect:", list.server, list.userId);
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection}); // 'posts' should be same with remote collection name.
PostsHandle[`${list.server}`] = connection.subscribe("posts", list.userId);
});
};
var observe = function () {
_.each(PostsHandle, function (handle, server) {
Tracker.autorun(function () {
if (handle.ready()) {
console.log(server, handle.ready());
// learn from http://docs.meteor.com/#/full/observe_changes
// thank you cursor.observeChanges
var cursor = Posts[server].find();
var cursorHandle = cursor.observeChanges({
added: function (id, post) {
console.log("added:", id, post);
piece._id = id; // sync post's _id
LocalPosts.insert(post);
},
removed: function (id) {
console.log("removed:", id);
LocalPosts.remove(id);
}
});
}
})
});
}
Template.posts.onCreated(function () {
connect(); // template level subscriptions
});
Template.posts.helpers({
posts: function () {
observe();
return LocalPosts.find({}, {sort: {createdAt: -1}}); // sort reactive
}
});

Client data not being updated in Meteor application

I have a Meteor application with a publish of:
Meteor.publish('my_items', function() {
var selector = {owner_id: this.userId};
var items = ItemOwnership.find(selector, {fields: {item_id: 1}}).fetch();
var itemIds = _.pluck(items, 'item_id');
return Items.find({
_id: {$in: itemIds},
item_archived_ts: { $exists: false }
});
});
and a subscription of this:
Meteor.subscribe('my_items');
The application allows for the user to add items to the 'Items' collection and this is done by calling a server method. The Items collection on the server is updated with the new record, but the client-side equivalent collection is not showing the new record. Is there anything obviously wrong with what I am doing, or some way to debug this?
p.s. there are no client/server-side errors occurring?
I found a way to accomplish this using the reywood:publish-composite Meteor package. Here is the publish that achieves this:
Meteor.publishComposite('my_items', {
find: function () {
var selector = {owner_id: this.userId};
return ItemOwnership.find(selector, {fields: {item_id: 1}});
},
children: [
{
find: function(IOrecord){
return Items.find({
_id: IOrecord.item_id,
item_archived_ts: { $exists: false }
});
}
}
]
});

How do I make a collection reactive based on the contents of another?

In short, I want to do:
Meteor.publish('items', function(){
return Item.find({categoryId: Categories.find({active: true} });
});
The flag 'active' as part of 'Categories' changes regularly.
I also tried unsub/resub to the Items collection by leveraging reactivity on the Categories collections, and it works, unfortunately it re-triggers on ANY modification to the Categories collection, regardless if it affected the 'active' flag or not.
What are my options?
Nothing solved the issue of the items not being 'deleted' locally when the category is flagged as inactive on the server. Solution (ish) is to:
Client:
Categories.find({active: true}).observeChanges({
added: function(){
itemsHandle && itemsHandle.stop();
itemsHandle = Meteor.subscribe("items");
}
});
Server:
Meteor.publish('items', function(){
var category = Categories.findOne({active: true});
return category && Items.find({categoryId: Categories.findOne({active: true}._id);
});
I realize this isn't perfect (still uses client side code), but it works and its the cleanest I could think of. I hope it helps someone!
A possible solution is to create a dependency object, watch for all categories change, and trigger the dep change if the active flag was toggled. Something along these lines:
var activeCount = Categories.find({active: true}).count();
var activeDep = new Deps.Dependency();
Deps.autorun(function() {
var activeCountNow = Categories.find({active: true}).count();
if(activeCountNow !== activeCount) {
activeCount = activeCountNow;
activeDep.changed();
}
});
Meteor.publish('items', function(){
activeDep.depend();
return Item.find({categoryId: Categories.find({active: true} });
});
Note: I'm only verifying whether the number of active categories have changes so that I don't have to keep the active list in the memory. This may or may not be appropriate depending on how your app works.
Edit: Two-sided flavor mentioned in the comments:
Client:
var activeCount = Categories.find({active: true}).count();
var activeDep = new Deps.Dependency();
Deps.autorun(function() {
var activeCountNow = Categories.find({active: true}).count();
if(activeCountNow !== activeCount) {
activeCount = activeCountNow;
activeDep.changed();
}
});
Deps.autorun(function(){
activeDep.depend();
Meteor.subscribe('items', new Date().getTime());
});
Server:
Meteor.publish('items', function(timestamp) {
var t = timestamp;
return Item.find({categoryId: Categories.find({active: true} });
});
Meteor.startup(function() {
Categories.find().observe({
addedAt: function(doc) {
trigger();
},
changedAt: function(doc, oldDoc) {
if(doc.active != oldDoc.active) {
trigger();
}
},
removedAt: function(oldDoc) {
trigger();
}
});
});
Now, the trigger function should cause the publish to rerun. This time it's easy when it's on the client (change subscription param). I'm not sure how to do this on the server - perhaps run publish again.
I use the following publish to solve a similar issue. I think it is only the one line nesting of queries that limits the reactivity. Breaking one query out inside the publish function seems to avoid the issue.
//on server
Meteor.publish( "articles", function(){
var self= this;
var subscriptions = [];
var observer = Feeds.find({ subscribers: self.userId }, {_id: 1}).observeChanges({
added: function (id){
subscriptions.push(id);
},
removed: function (id){
subscriptions.splice( subscriptions.indexOf(id)) , 1);
}
});
self.onStop( function() {
observer.stop();
});
var visibleFields = {_id: 1, title: 1, source: 1, date: 1, summary: 1, link: 1};
return Articles.find({ feed_id: {$in: subscriptions} }, { sort: {date: -1}, limit: articlePubLimit, fields: visibleFields } );
});
//on client anywhere
Meteor.subscribe( "articles" );
Here is another SO example which gets the search criteria from the client through subscribe if you decide that is acceptable.
Update: Since the OP struggled to get this going I made a gist and launched a working version on meteor.com. If you just need the publish function it is as above.

Resources