Publishing data with information from another collection in Meteor - mongodb

I have two collections: Meta and the normal users collection. The Meta collection has the following structure:
{
userId: ObjectId,
contacts: [Array],
conversations: [Array]
}
Each user has a corresponding, distinct document in the Meta collection. Now I want to publish the users that belong in the current user's contacts or conversations array.
{
userId: 123,
contacts: ['a', 'b', 'c'],
conversations: ['a', 'e']
}
For user 123, I want to publish user profile data for users a, b, c and e. Basically all the distinct values of the contacts and conversations array.
I tried merging the array and using `_.uniq' on the new array inside the publish function but I need this to be reactive so when a user adds a new contact, the contact list can update reactively. Something tells me Mongo's aggregation framework can help but I'm not sure how to use it with Meteor. Any thoughts?

You can use the reactive-publish package (I am one of authors):
Meteor.publish('profiles', function (userId) {
this.autorun(function (computation) {
var meta = Meta.findOne({userId: userId}, {fields: {contacts: 1, conversations: 1}});
return Meteor.users.find({_id: {$in: meta.contacts.concat(meta.conversations)}}, {fields: {profile: 1}});
});
});
The important detail is that you have to limit the first query only to contacts and conversations fields, otherwise autorun would be rerun every time any field in the Meta document changes.
You should probably also add some checks, like what if corresponding Meta document does not exist. Or if fields (arrays) do not exist.

Related

How to search from list ObjectID in MongoDB

I'm having a "ClubFollower" schema like below:
const ClubFollower = new Schema({
id_club:{
required:true,
type:String,
trim:true,
unique:true
},
followers:[
{
_id:false,
id_follower:ObjectId("xxxxxxxxxxxx"),
follower_type:String // 'club' | 'user'
}
]
});
each follower has many information such as:name, phone, email , etc... the question is how do I search a follower by name, phone, email ... with this schema. I dont think loop all item in follower array then findById with each id_follower and compare search key with email, phone... is good solution
You can use Aggregation Pipeline. First you $match on id_club, then you $unwind followers and $lookup followers from their collection based on id_follower. Finally, you can use other stages to filter based on followers' attributes.
You could also search from the other side. Have your followers collection store the clubs they are part of and match against that collection instead.

How can I query whether each element inside an array match a collection field

I am using mongodb to save user information. There is a userId field in that collection. I get many userIds in my application and saved as an array. I need to query whether all the userIds in that array exist in the collection. If not, find out all the missing ones. Is there one query command does the work? I don't want to query the userId one by one. So what is the better way to achieve this?
The user collection is very simple as below and there is no nested data.
userId: String
name: String
gender: String
phone: String
For example, I have an array of ids [1, 2, 3]. I have to run query three times to check whether these are users to match the three ids.
This can be done with the $in operator.
Example:
db.users.find( {userId: { $in: [ 1, 2, 3 ] } } );
Once you have the users pulled back from that you can determine in the application layer which users did not come back.

Pushing into sub document array inside by element

I have mongo db collection that looks like this :
({
_id:id ,
createdAt: new Date(),
name:name,
friends : [{name:"tommy",children:[{name:"sarah",age:12}]}],
dogs : [{}]
});
I would like to be able to insert a new element in the friends array if the name doesnt exist and a children element to that new array.
If i'm adding a new friend named john with a child nathaly , i'd like the output to be
({
_id:id ,
createdAt: new Date(),
name:name,
friends : [{name:"tommy",children:[{name:"sarah",age:12}]},{name:"john",children:[{name:"natahly",age:20}]}],
dogs : [{}]
});
If friend tommy already exist i would like just the children to be added to the children array and the output to now be
({
_id:id ,
createdAt: new Date(),
name:name,
friends : [{name:"tommy",children:[{name:"sarah",age:12},{name:"newchild",age:99}]},{name:"john",children:[{name:"natahly",age:20}]}],
dogs : [{}]
});
I've tried so many things already it's impossible to list.
currently i'm trying a mix of everything
// if `friends` is `[]`, push the empty children firstly through addToSet
var bulk = Directory.rawCollection().initializeUnorderedBulkOp();
bulk.find({_id: id, 'friends.name': {$exists: false}}).updateOne(
{$addToSet: {friends: {name: name_var, children: []}});
Meteor.wrapAsync(bulk.execute)();
// then Since previous step create the children array for said friend only if it doesn't already exist i'm just trying to update said friend with the new child
Directory.update(
{ _id: id,"friends.name": name_var },
{ $push: { "friends.$.children": {name:"sarah",age:12}
}}})
(i've also tried with "friends.children" instead of "friends.$.children" but i just get a different error)
And this should cover my three case but i'm having issues and i'm wondering if i'm going the right way. Any of you guys have any idea because this should at least work to add the children but it doesn't..
Errors : MongoError: The field 'friends.0.children' must be an array but is of type Object in document {_id: "0"} # when friends.$.children"
MongoError: cannot use the part (Friends of Friends.childrens) to traverse the element when friends.children"
One way of doing it would be to,
Initialize the object you want to insert/update
The object can have multiple children inserted at once.
var obj = {name:"tommy",children:[{name:"ds",age:12},{name:"two",age:12}]};
Initialize the Bulk API.
The order of operations does not matter here.
var bulk = db.t.initializeUnorderedBulkOp();
Find and update only the children records that already have a sub document for the name.
use the $addToSet operator to maintain unique records, and $each to add more than one children at a time.
bulk.find({"friends.name":obj.name})
.update({$addToSet:{"friends.$.children":{$each:obj.children}}});
Find and update those which do not have a sub document.
$push the entire object if a document doesn't have one.
bulk.find({"friends.name":{$ne:obj.name}})
.update({$push:{"friends":obj}});
bulk.execute();

Meteor: How do you populate a field from one collection into another collection with _id field?

In mongo I have a document that stores pending userId's in a collaborators object array and looks like this:
researchThread {
_id: 4374583575745756
pending: {
collaborators: [
{
userId: '13745845754745753'
},
{
userId: '23755845854745731'
},
{
userId: '33755845653741736'
}]
}
}
The userId is the _id field for the user from the users collection. Each user also has a name and an email field.
How can I populate the name and email fields from the user collection, into this document for each user in the researchThread.pending.collaborators object array? And also, will the populated data be reactive when used in the templates?
Loop through each collaborator, find the relevant user document by searching the users collection for the id, and update the researchThread document with that information.
The data will be reactive if the researchThread collection is a Meteor.Collection which you're drawing from in your templates.
However, why do you want to copy the user data? Why not just have Meteor query the users collection based on the researchThread userId when you need the data in the template?

Document References query example

If I choose to use Document References with a structure of Materialized Paths instead of the simple Embedded Documents how can I display the same results?
For example if I had Embedded docs I simply :
db.col.find({'user' : 'foo'})
and return:
{'user' : 'foo',
'posts' : [ {},
{},
{}
]
}
Which command should I use to display posts as an embedded array of that user? Or this can only happen client-side?
If it's document references,
users collection will contain:
{
_id : "foo",
// users details
}
and posts collection:
{
_id: "postid",
author: "foo"
// other fields
}
In this case,
1) First make query to get the user id from users collection.
2) Then send the user id to the posts collection to get all posts
var user = db.users.find({_id : "foo"});
// this is used to get user details or validate user and only after validation if you need to fetch the posts
var posts = db.posts.find({author: user._id });
As the documents are referenced, there will be a roundtrip to the server which is obvious.
I am not sure how you have used materialized path for this scenario, let me know the data structure of it and i would be able to mention the query based on that.