How to accessing a document with two different keys in Pymongo? - mongodb

collection = database.collection
collection.insert_one({"_id": friendship_code, "test#gmail.com": "user_name", "second#gmail.com": "second_user_name"}).inserted_id
I wanted to do something like this.Then I would have reached this document in 2 ways.
collection.find_one({"test#gmail.com": "user_name"})
or
collection.find_one({"second#gmail.com": "second_user_name"})
But in the key name "." can't be.
bson.errors.InvalidDocument: key 'test#test.com' must not contain '.'
I can't write the code like this.
collection = database.collection
collection.insert_one({"_id": friendship_code, "user1": "test#gmail.com", "user2": "second#gmail.com"}).inserted_id
Because sometimes mails change places. When a mail is saved with another mail, it can be user1 or user2.
collection = database.collection
collection.insert_one({"_id": friendship_code, "user1": "second#gmail.com", "User2": "another#gmail.com"}).inserted_id
I can check this with the if blog in python but this is not a good approach.
if collection.find_one ({"user1": "second#gmail.com"}) or collection.find_one({"user2": "second#gmail.com"}):
I want to retrieve all "second#gmail.com" data from the collection, even though I don't know if it's user2 or user1.

Instead of storing user1, user2, etc. store them as a users array, then your query is simpler:
collection.insert_one({"_id": friendship_code, "users": ["test#gmail.com", "second#gmail.com"]}).inserted_id
Then either of these queries will return the relevant record:
print(collection.find_one({"users": "test#gmail.com"}))
print(collection.find_one({"users": "second#gmail.com"}))

Related

How to user $in query in mongodb?

I want to fetch records when permission = User.
Below is my colletions:
"_id":ObjectId("59177f050c9db20629f4562"),
"profile":
{
"permissionGroups" : [
"Admin",
"Restaurant",
"Salesperson",
"User"
]
}
Fetch only in permission is User.
Thanks in advance,
Well first, access the permissionGroups array inside profile obj and then run a query simply matching the string like this. You don't need to run a $in unless you have multiple strings in array to match with:
db.getCollection('collection name').find({"profile.permissionGroups" : "User"})
I don't think $in is what you're looking for. $in queries an array against a field, so let's say you search permissionsGroup: {$in: ['User', 'Admin']}, and that would return all records that could find either User or Admin.
In this case you can simply search for permissionsGroups: 'User'. Mongo is smart enough to recognize permissionGroups as an array, and test the given value against the fields in that array.

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 }}});

Composition in mongo query (SQL sub query equivalent)

I have a couple of collections, for example;
members
id
name
//other fields we don't care about
emails
memberid
//other fields we don't care about
I want to delete the email for a given member. In SQL I could use a nested query, something like;
delete emails
where memberid in (select id from members where name = "evanmcdonnal")
In mongo I'm trying something like this;
db.emails.remove( {"memberid":db.members.find( {"name":"evanmcdonnal"}, {id:1, _id:0} ) )
But it returns no results. So I took the nested query and ran it on it's own. The issue I believe is that it returns;
{
"id":"myMadeUpId"
}
Which - assuming inner queries execute first - gives me a query of;
db.emails.remove( {"memberid":{ "id""myMadeUpId"} )
When really I just want the value of id. I've tried using dictionary and dot notation to access the value of id with no luck. Is there a way to do this that is similar to my attempted query above?
Let's see how you'd roughly translate
delete emails where memberid in (select id from members where name = "evanmcdonnal")
into a set of mongo shell operations. You can use:
db.members.find({ "name" : "evanmcdonnal" }, { "id" : 1 }).forEach(function(doc) {
db.emails.remove({ "memberid" : doc.id });
});
However, this does one remove query for each result document from members. You could push the members result ids into an array and use $in:
var plzDeleteIds = db.members.find({ "name" : "evanmcdonnal" }, { "id" : 1 }).toArray();
db.emails.remove({ "memberid" : { "$in" : plzDeleteIds } });
but that could be a problem if plzDeleteIds gets very, very large. You could batch. In all cases we need to do multiple requests to the database because we are querying multiple collections, which always requires multiple operations in MongoDB (as of 2.6, anyway).
The more idiomatic way to do this type of thing in MongoDB is to store the member information you need in the email collection on the email documents, possibly as a subdocument. I couldn't say exactly if and how you should do this since you've given only a bit of your data model that has, apparently, been idealized.
As forEach() way didn't work for me i solved this using:
var plzDeleteIds = db.members.find({ "name" : "evanmcdonnal" }, { "id" : 1 }).toArray();
var aux = plzDeleteIds["0"];
var aux2 = aux.map(function(u) { return u.name; } );
db.emails.remove({ "memberid" : { "$in" : aux2 } });
i hope it help!
I do not believe what you are asking for is possible. MongoDB queries talk to just one collection -- there is no syntax to go cross-collection.
However, what about the following:
The name in members does not seem to be unique. If you were to delete emails from the "emails' collection using name as the search attribute, you might have a problem. Why not store the actual email address in the email collection? And store email address again in the members collection. When your user logs in, you will have retrieved his member record -- including the email address. When you want to delete his emails, you already have his email and you can do:
db.emails.remove({emailAddress: theActualAddress))
Does that work?

How do I rename a nested key in mongodb

I want rename to rename my dict key in mongodb.
normally it works like that db.update({'_id':id},{$rename:{'oldfieldname':newfieldname}})
My document structure looks like that
{
'data':'.....',
'field':{'1':{'data':....},'2':{'data'...}},
'more_data':'....',
}
if i want to set
a new field in field 1 i do db.update({'_id':id},{$set:{'field.0.1.name':'peter'}})
for field two it is 'field'.1.2.name'
i thought with the rename it should be similar but it isn't ... (like $rename:{'field'.0.1': 2}
Here's a flexible method for renaming keys in a database
Given a document structure like this...
{
"_id": ObjectId("4ee5e9079b14f74ef14ddd2f"),
"code": "130.4",
"description": "4'' Socket Plug",
"technicalData": {
"Drawing No": "50",
"length": "200mm",
"diameter: "20mm"
},
}
I want to loop through all documents and rename technicalData["Drawing No"] to technicalData["Drawing Number"]
Run the following javascript in the execute panel in (the excellent) RockMongo
function remap(x){
dNo = x.technicalData["Drawing No"];
db.products.update({"_id":x._id}, {
$set: {"technicalData.Drawing Number" : dNo},
$unset: {"technicalData.Drawing No":1}
});
}
db.products.find({"technicalData.Drawing No":{$ne:null}}).forEach(remap);
The code will also run in a mongo shell
Your question is unclear but it seems you'd like to rename a field name within an array.
The short answer is you can't. As stated in the docs, $rename doesn't expand arrays to find a matching name. It only works on top level fields.
What you can do to simulate rename is by copying the field and its data to the new name, and then deleting the original field. You might also need a way to account for potentially concurrent writes if you have a lot of writes to that object/field.