Query MongoDB for multiple ObjectIDs in Array - mongodb

I have a collection "users" in my Database. A user can be a member of several teams.
I am using an Array to reference the team IDs.
{ _id: ObjectId("552544fd600135861d9e47d5)",
name : "User1",
teams: [
"5527a9493ebbe2452666c238",
"5527b1be3371e3a827fa602c"
]
}
The teams are nothing more than a collection of:
{ _id: ObjectId("5527a9493ebbe2452666c238"),
name: "Team 1"
}
{ _id: ObjectId("5527b1be3371e3a827fa602c"),
name: "Team 2"
}
Now I want to get the names of all the teams in which the user is a member.
I have only found the way of querying it this way:
db.teams.find(
{_id:{$in:
[ObjectId("5527a9493ebbe2452666c238"),
ObjectId("5527b1be3371e3a827fa602c")]
}})
To do this, I would need to create an array specifically for this query. I would rather try to avoid this, because I already have the IDs available as an array in the string format. Something like this would be great:
db.teams.find(
{_id:{$in:
["5527a9493ebbe2452666c238",
"5527b1be3371e3a827fa602c"] // Strings here, not ObjectIDs
}})
But this does not work. Is there any comfortable way of querying an ObjectID with a set of string IDs?
Thanks & regards
Rolf

You could use a combination of mongodb's findOne() and find() cursor methods together with the native JavaScript map method to first get the team id's for a specific user (which will be a string array), then use the map function to map the teams' string id's array to an ObjectId's array, and finally query the teams collection with the resulting array as the $in operator expression:
var teams = db.users.findOne({"name": "User1"}).teams;
var obj_ids = teams.map(function (item){ return ObjectId(item)});
db.teams.find({ "_id": { "$in": obj_ids } });
Output:
/* 0 */
{
"_id" : ObjectId("5527a9493ebbe2452666c238"),
"name" : "Team 1"
}
/* 1 */
{
"_id" : ObjectId("5527b1be3371e3a827fa602c"),
"name" : "Team 2"
}

Related

How to find objectId as an foreign key in mongo

How can I find the value of ObjectId in whole database for any field in mongo ,
it might be use in some collections for various fields as a reference?
"fourk_runs": [{
"Objectid": "6299b9f00f09ff045cc15d"
}],
"fourk_pass_fail": [{
"Objectid": "6299b9f00f09ff045cc152"
}],
"dr_runs": [{
"Objectid": "6299b9f00f09ff045cc154"
}],
I try this command , but it does not work
db.test.find( { $text: { $search: "4c8a331bda76c559ef04" } } )
In MongoDB, the references are mostly used for the normalization process. The references from the children's table (usually an ObjectID) are then embedded in the parent table. When reading the information, the user has to perform several queries in order to retrieve data from multiple collections.
For example, we will take the next two collections, parts and products. A product will contain many parts that will be referenced in the product collection.
parts Collection:
db.parts.findOne()
{
_id : ObjectID('1111'),
partno : '7624-faef-2615',
name : 'bearing',
price: 20,000
}
products Collection:
db.products.findOne()
{
name : 'wheel',
manufacturer : 'BMW',
catalog_number: 1134,
parts : [ // array of references to Part documents
ObjectID('1111'), // reference to the bearing above
ObjectID('3f3g'), // reference to a different Part
ObjectID('234r'),
// etc
]
}
To receive the parts for a particular product, we will first have to fetch the product document identified by the catalog number:
db.products.findOne({catalog_number: 1134});
Then, fetch all the parts that are linked to this product:
db.parts.find({_id: { $in : product.parts } } ).toArray();

Mongo query - Array of Objects - get field from element 0 only

Trying to query Mongo, and get 1 field of element 0, inside a document, namely emails[0].address:
Here's a sample (truncated) document structure:
{
"_id" : "dfadgfe266reh",
"emails" : [
{
"address" : "email#domain.com",
"verified" : false
}
]
}
And my query (truncated) is like this:
{
fields: {
'emails.0.address': {
address: 1
}
}
}
However, when I run this, I get an empty object array, namely emails:[{}]
But if I change the selector to 'emails.address' it will give me the actual email address -- the problem is I only want emails[0].address
What am I doing wrong?
To get the required document you need a project document with two attributes. First one, as #Veeram mentioned, slices the array and second to specify an attribute from the embedded document in the array. See code:
db.collection.find( {}, { "emails": { $slice: 1 } , "emails.address": 1} );
You can use $ in projection:
.find({"emails.address":{$exists:true}}, {"emails.$.address":1})

How to return the results of a query using find() for a deep search level with MongoDB?

I have the following structure of a collection:
teams: [{
name: String,
address: String,
players: [{
name: String,
age: Integer
}]
}]
I am trying to search by 'name' of the player. I am trying this:
db.teams.find({ players: { $elemMatch: { name: /john/ } } }, { 'players.$':1 })
This returns some results, but not all. And this:
db.teams.find({ players: { $elemMatch: { name: /john/ } } })
This last returns all the records of the collection.
I need to find by a player with its name as parameter.
What is right query?
This is a common misconception about MongoDB. Queries in MongoDB do not match subdocuments; they do not return subdocuments. Queries match and return documents in the collection, with returned fields adjustable via projection. With your document structure, you cannot write a query that matches and returns one of the subdocuments in the array. You can (and have, in your examples), written a query to match documents containing an array element that matches a condition, and then project just the first matching element in the array. It's important to understand the difference. If you want to match and return player documents, your collection should contain player documents:
{
"name" : "John Smalls",
"age" : 29,
"team_name" : "The Flying Sharks",
"team_address" : "123 Fake Street"
}
The player documents reference their team. To find a player by name,
db.players.find({ "name" : /^John"/ })
To find all the players on a team,
db.players.find({ "team_name" : "The Flying Sharks" })
Your life will be much easier if you search for players by querying for player documents instead of trying to match team documents.

Finding which elements in an array do not exist for a field value in MongoDB

I have a collection users as follows:
{ "_id" : ObjectId("51780f796ec4051a536015cf"), "userId" : "John" }
{ "_id" : ObjectId("51780f796ec4051a536015d0"), "userId" : "Sam" }
{ "_id" : ObjectId("51780f796ec4051a536015d1"), "userId" : "John1" }
{ "_id" : ObjectId("51780f796ec4051a536015d2"), "userId" : "john2" }
Now I am trying to write a code which can provides suggestions of a userId to user in case id provided by user already exists in DB. In same routine I just append values from 1 to 5 to the for example in case user have selected userId to be John, suggested user name array that needs to be checked for Id in database will look like this
[John,John1,John2,John3,John4,John5].
Now I just want to execute it against Db and to find out which of the suggested values do not exist in DB. So instead of selecting any document, I want to select values within suggested array which do not exist for users collection.
Any pointers are highly appreciated.
Your general approach here is you want to find the "distinct" values for the "userId's" that already exist in your collection. You then compare these to your input selection to see the difference.
var test = ["John","John1","John2","John3","John4","John5"];
var res = db.collection.aggregate([
{ "$match": { "userId": { "$in": test } }},
{ "$group": { "_id": "$userId" }}
]).toArray();
res.map(function(x){ return x._id });
test.filter(function(x){ return res.indexOf(x) == -1 })
The end result of this is the userId's that do not match in your initial input:
[ "John2", "John3", "John4", "John5" ]
The main operator there is $in which takes an array as the argument and compares those values against the specified field. The .aggregate() method is the best approach, there is a shorthand mapReduce wrapper in the .distinct() method which directly produces just the values in the array, removing the call to a function like .map() to strip out the values for a given key:
var res = db.collection.distinct("userId",{ "userId": { "$in": test } })
It should run notably slower though, especially on large collections.
Also do not forget to index your "userId" and likely you want this to be "unique" anyway so you really just want the $match or .find() result:
var res = db.collection.find({ "$in": test }).toArray()

Inline/combine other collection into one collection

I want to combine two mongodb collections.
Basically I have a collection containing documents that reference one document from another collection. Now I want to have this as a inline / nested field instead of a separate document.
So just to provide an example:
Collection A:
[{
"_id":"90A26C2A-4976-4EDD-850D-2ED8BEA46F9E",
"someValue": "foo"
},
{
"_id":"5F0BB248-E628-4B8F-A2F6-FECD79B78354",
"someValue": "bar"
}]
Collection B:
[{
"_id":"169099A4-5EB9-4D55-8118-53D30B8A2E1A",
"collectionAID":"90A26C2A-4976-4EDD-850D-2ED8BEA46F9E",
"some":"foo",
"andOther":"stuff"
},
{
"_id":"83B14A8B-86A8-49FF-8394-0A7F9E709C13",
"collectionAID":"90A26C2A-4976-4EDD-850D-2ED8BEA46F9E",
"some":"bar",
"andOther":"random"
}]
This should result in Collection A looking like this:
[{
"_id":"90A26C2A-4976-4EDD-850D-2ED8BEA46F9E",
"someValue": "foo",
"collectionB":[{
"some":"foo",
"andOther":"stuff"
},{
"some":"bar",
"andOther":"random"
}]
},
{
"_id":"5F0BB248-E628-4B8F-A2F6-FECD79B78354",
"someValue": "bar"
}]
I'd suggest something simple like this from the console:
db.collB.find().forEach(function(doc) {
var aid = doc.collectionAID;
if (typeof aid === 'undefined') { return; } // nothing
delete doc["_id"]; // remove property
delete doc["collectionAID"]; // remove property
db.collA.update({_id: aid}, /* match the ID from B */
{ $push : { collectionB : doc }});
});
It loops through each document in collectionB and if there is a field collectionAID defined, it removes the unnecessary properties (_id and collectionAID). Finally, it updates a matching document in collectionA by using the $push operator to add the document from B to the field collectionB. If the field doesn't exist, it is automatically created as an array with the newly inserted document. If it does exist as an array, it will be appended. (If it exists, but isn't an array, it will fail). Because the update call isn't using upsert, if the _id in the collectionB document doesn't exist, nothing will happen.
You can extend it to delete other fields as necessary or possibly add more robust error handling if for example a document from B doesn't match anything in A.
Running the code above on your data produces this:
{ "_id" : "5F0BB248-E628-4B8F-A2F6-FECD79B78354", "someValue" : "bar" }
{ "_id" : "90A26C2A-4976-4EDD-850D-2ED8BEA46F9E",
"collectionB" : [
{
"some" : "foo",
"andOther" : "stuff"
},
{
"some" : "bar",
"andOther" : "random"
}
],
"someValue" : "foo"
}
Sadly mapreduce can't produce full documents.
https://jira.mongodb.org/browse/SERVER-2517
No idea why despite all the attention, whining and upvotes they haven't changed it. So you'll have to do this manually in the language of your choice.
Hopefully you've indexed 'collectionAID' which should improve the speed of your queries. Just write something that goes through your A collection one document at a time, loading the _id and then adding the array from Collection B.
There is a much faster way than https://stackoverflow.com/a/22676205/1578508
You can do it the other way round and run through the collection you want to insert your documents in. (Far less executions!)
db.collA.find().forEach(function (x) {
var collBs = db.collB.find({"collectionAID":x._id},{"_id":0,"collectionA":0});
x.collectionB = collBs.toArray();
db.collA.save(x);
})