Using "$and" and "$in" together - mongodb

I have a collection friends which i am using to store all the friends and has the following fields
id , userid, friendid
I have a collection notes which stores user notes and has the following fields
id, userid , ownerid , privateorpublic
The rule is a user can only see notes he/she has created or notes created by his/her friends or notes that are public
To fetch that information i am writing my query this way. First, get all the ids of my friends separated by a comma. I dont know how to write the query to return comma delimited values but this query below I am hoping will return all the firneds of the user by the given id:
db.friends.find({ { friendid: { userid: 'jhdshh37838gsjj' } }
then get the notes where are either created by a friend or are public notes or are notes belonging to user
db.notes.find({ownerid:'jhdshh37838gsjj', privateorpublic:'public',{ $in: [ "3ghd8e833", "78hhyaysgd" ] }
I am new to MongoDB. Are my queries right?

Find query syntax : db.collection.find({queryFieldName: queryFieldValue}, {requiredFieldName: 1, NotRequiredFieldName: 0})
Thus, translating first query, you'd get "get me all documents from collection friends where friendId equals a string {'userId':'Id'}" which is not desired.
Query for "Get me all friend Ids of userid jhdshh37838gsjj and don't print anything else in results" : db.collection.find({userId:'jhdshh37838gsjj'}, {friendId:1, _id:0})
However, this'll not give output as comma separated values. You'll get output as follows (because every document is an independent object):
{friendId: 1}
{friendId: 2}
$in requires array as input and not comma separated values. To make an array of field values for a $in query, you need to some extra work like:
var friendIdsArr = [] //Initiate an array to store friend Ids
db.friends.find({'userId':'jhdshh37838gsjj'},{'friendId':1,'_id':0}).forEach(function(x) {friendIdsArr.push(x.friendId);} ); //Foreach result in query, push value to array.
The friendIdsArr will be [1,2]
db.notes.find({'ownerId':'jhdshh37838gsjj', 'privateOrPublic':'public',{$in: ["friendId": friendIdsArr]})
will give results, json documents, matching the given conditions.
Interestingly, your notes collection doesn't have a note field.
I'd recommend you to read mongodb official documentation and make a single query instead of two different queries.

Related

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.

How to find and return a specific field from a Mongo collection?

Although I think it is a general question, I could not find a solution that matches my needs.
I have 2 Mongo collections. The 'users' collection and the second one 'dbInfos'.
Now, I have a template called 'Infos' and want the already existing fields in the Mongo collections to be presented to the user in input fields in case there is data in the collection. When no data is provided in the database yet, it should be empty.
So here is my code, which works fine until I want to capture the fields from the second collection.
Template.Infos.onRendered(function() {
$('#txtName').val(Meteor.user().profile.name);
$('#txtEmail').val(Meteor.user().emails[0].address);
});
These 2 work great.
But I don´t know how to query the infos from the collection 'dbInfos', which is not the 'users' collection. Obviously Meteor.user().country does not work, because it is not in the 'users' collection. Maybe a find({}) query? However, I don´t know how to write it.
$('#txtCountry').val( ***query function***);
Regarding the structure of 'dbInfos': Every object has an _id which is equal to the userId plus more fields like country, city etc...
{
"_id": "12345",
"country": "countryX",
"city": "cityY"
}
Additionally, how can I guarantee that nothing is presented, when the field in the collection is empty? Or is this automatic, because it will just return an empty field?
Edit
I now tried this:
dbInfos.find({},{'country': 1, '_id': 0})
I think this is the correct syntax to retrieve the country field and suppress the output of the _id field. But I only get [object Object] as a return.
you're missing the idea of a foreign key. each item in a collection needs a unique key, assigned by mongo (usually). so the key of your country info being the same as the userId is not correct, but you're close. instead, you can reference the userId like this:
{
"_id": "abc123",
"userId": "12345",
"country": "countryX",
"city": "cityY"
}
here, "abc123" is unique to that collection and assigned by mongo, and "12345" is the _id of some record in Meteor.users.
so you can find it like this (this would be on the client, and you would have already subscribed to DBInfos collection):
let userId = Meteor.userId();
let matchingInfos = DBInfos.find({userId: userId});
the first userId is the name of the field in the collection, the second is the local variable that came from the logged in user.
update:
ok, i think i see where you're getting tripped it. there's a difference between find() and findOne().
find() returns a cursor, and that might be where you're getting your [object object]. findOne() returns an actual object.
for both, the first argument is a filter, and the second argument is an options field. e.g.
let cursor = DBInfos.find({
userId: Meteor.userId()
},
{
fields: {
country: 1
}
});
this is going to:
find all records that belong to the logged in user
make only the country and _id fields available
make that data available in the form of a cursor
the cursor allows you to iterate over the results, but it is not a JSON object of your results. a cursor is handy if you want to use "{{#each}}" in the HTML, for example.
if you simply change the find() to a findOne():
let result = DBInfos.findOne({ /** and the rest **/
... now you actually have a JSON result object.
you can also do a combination of find/fetch, which works like a findOne():
let result = DBInfos.find({
userId: Meteor.userId()
},
{
fields: {
country: 1
}
}).fetch();
with that result, you can now get country:
let country = result.country;
btw, you don't need to use the options to get country. i've been assuming all this code is on the client (might be a bad assumption). so this will work to get the country as well:
let result = DBInfos.findOne({userId: Meteor.userId()});
let country = result.country;
what's going on here? it's just like above, but the result JSON might have more fields in it than just country and _id. (it depends on what was published).
i'll typically use the options field when doing a find() on the server, to limit what's being published to the client. on the client, if you just need to grab the country field, you don't really need to specify the options in that way.
in that options, you can also do things like sort the results. that can be handy on the client when you're going to iterate on a cursor and you want the results displayed in a certain order.
does all that make sense? is that what was tripping you up?

How can I query for a subdocument full of objects in Mongo?

So I have a document with an unknown number of objects in it, each with 2 properties. It's a collection of friend lists, and I'm trying to confirm if someone has a friend with a certain username before I allow a user to send a request. I'm keeping the list of friends in a subdocument, like this:
>>all the _id and other properties<<, "ownerFriends":[{"friendId":"an id goes here", "friendUsername": "username"}, {"friendId":"another id", "friendUsername":"username2"}]
I'm trying to do a query that will return username2 if given that as input, but I don't know how to do that with dot notation because I think you need to know the specific property to look for, and these are heterodox amounts of friend objects in the ownerFriends property.
If you want to select the ownerFriend object that has username as the friendUserName you can use the following selector (assuming your collection is called Friends):
Friends.find({
"ownerFriends.friendUsername": "username2"
}, {
fields: { "ownerFriends.$": 1}
});
You can find a detailed explanation of how to query an array of objects based on a property here:
http://www.curtismlarson.com/blog/2015/08/08/meteor-mongodb-array-property-selector/
In summary you have an object that contains keys, one of whose values is an array of objects. You can perform queries on the arrays using $elemMatch In your case:
MyCollection.find({ ownerFriends: { $elemMatch: { friendUsername: searchString }}});
Although I think you'll need to also query on the current user's _id. Not knowing the details of your collection, I can only speculate with:
MyCollection.find({ userId: Meteor.userId(), ownerFriends: { $elemMatch: { friendUsername: searchString }}});

How to retrieve array of specific field of sub document- mongodb

This is my first mongodb project,I have this document structure in mongodb, I am trying to retrieve a particular user account (each user account has an array of contacts), from this user account, I will then obtain an array of the ID fields of the users contacts and then pass this array as a parameter to another query, I am doing this to avoid having to loop through the users contacts array in order to obtain the ID fields, here is the document structure, the query I tried is below it
{
name,
id,
contacts:[{
contactId, //I need an array of this field
dateAdded
},
contactId,
dateAdded
},
{}..]
}
//
var findByIdAll = function(accountId, callback) {
var self=this;
//Get the user account
Account.findOne({_id:accountId}, function(err,doc) {
/ After the user account has been obtained, the function below will
// use an array of the users contactsId's to fetch the contact's accounts
//please how do I obtain the array of contact Id's before reaching here
self.Account.find({_id:{$in:[/array of contact Ids]}},function(err,results){
callback(results);
});
});
};
EDIT
//I have now been able to obtain an array of contactID fields using the following query
var r=db.accounts.aggregate({$match:{email:'m#live.com'}},{$unwind:"$contacts"},
{$project:{_id:0,contacts:1}},{$group:{_id:'$_id',
list:{$push:'$contacts.accountId'}}});
The result I get from the query is
r
{
"result" : [
{
"_id" : null,
"list" : [
ObjectId("51c59a31c398c40c22000004"),
ObjectId("51c59a31c398c40c22000004")
]
}
],
"ok" : 1
}
A normal MongoDB query will always give you the entire document with the same structure.
If you want to get just part of the document or make a transformation to it you need to use the Aggregation Framework (is not as hard to understand as it looks, give it a try).
In your case you might have to use $unwind in contacts to explode the array, $match to get only the account you want, and $project to present the data as you want.

Mongo, find through list of ids

I have a process that returns a list of String MongoDB ids,
[512d5793abb900bf3e20d012, 512d5793abb900bf3e20d011]
And I want to fire a single query to Mongo and get the matching documents back in the same order as the list.
What is the shell notation to do this?
After converting the strings into ObjectIds, you can use the $in operator to get the docs in the list. There isn't any query notation to get the docs back in the order of your list, but see here for some ways to handle that.
var ids = ['512d5793abb900bf3e20d012', '512d5793abb900bf3e20d011'];
var obj_ids = ids.map(function(id) { return ObjectId(id); });
db.test.find({_id: {$in: obj_ids}});
This works fine for me in Robo 3T. No need to create any object and just use the list of ids.
db.getCollection('my_collection').find({'_id':{$in:['aa37ba96']}})
// categoryId comma separated "5c875c27d131b755d7abed86,5c875b0ad131b755d7abed81" in request
var ids= req.body.categoryId.split(',');
db.test.find({ categoryId: { $in: ids } });
If your final purpose is to get the document with the order by your pre-get ids list, you can just convert the query result into mapping(id as key, doc as value) , and then traverse the ids list to get the doc.