Need Help on Mongo DB query - mongodb

There is an existing person collection in the system which is like:
{
"_id" : ObjectId("536378bcc9ecd7046700001f"),
"engagements":{
"5407357013875b9727000111" : {
"role" : "ADMINISTRATOR",
},
"5407357013875b9727000222" : {
"role" : "DEVELOPER",
}
}
}
So that multiple user objects can have the same engagement with a specific role, I need to fire a query in this hierarchy where I can get all the persons which have a specific engagement in the engagements property of person collection.
I want to get all the persons which have
5407357013875b9727000222 in the engagements.
I know $in operator could be used but the problem is that I need to compare the keys of the sub Json engagements.

I think it's as simple as this:
db.users.find({'engagements.5407357013875b9727000222': {$exists: true}})
If you want to match against multiple engagement ids, then you'll have to use $or. Sorry, no $in for you here.
Note, however, that you need to restructure your data, as this one can't be indexed to help this concrete query. Here I assume you care about performance and this query is used often enough to have impact on the database.

Related

Mongodb: What is the best way to search multiple collections for the same field in one query

I inherited a bunch of collections and each of them have fields in common.
Is there a way to query the "EMAIL" field in collection A and Collection B ....or Am I stuck with iterating through the collections programmatically ?
db.colA.find({"EMAIL":"joe#doe.com"})
Collection A :
{ "_id" : ObjectId("58197fc91b69ba68721d4148"), "UUID" : "0b6827f2-9384-11e0-8f4a-b8ac6f949be6", "EMAIL" : "JOHNDOE#YAHEE.COM", "FNAME" : "JOHN", "LNAME" : "DOE"}
Collection B:
{ "_id" : ObjectId("4ed234423654fea654a654f2"), "SOURCE" : "65488451522", "EMAIL" : "JOHNDOE#YAHEE.COM", "FN" : "JOHN", "DOB":"05/13/1967"}
Expected behavior: the search query would returned both records.
Well, why not query Col A and Col B .. I have 17 collections in the DB, one query would be great.
You can use the aggregation lookup to achieve this:
db.colA.aggregate([
{
$lookup:
{
from: "colB",
localField: "EMAIL",
foreignField: "EMAIL",
as: "connected_query"
}
}
])
You will then have a field called connected_query in your results that refer to colB results that have the same email as colA.
You can't do this in a single query with your current collections.
You may be able to achieve this with DBRefs, but it will soon be deprecated (if not already), so don't use it unless it's absolutely necessary.
Even if running the same query on 17 distincts collections is not very elegant, I guess this is still the best option
If you are talking about joins, $lookup has been enabled in mongo latest version. Java doc: https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
There's no way to do that. You're stuck to having to do that programmatically. Although MongoDB has embedded documents and an 'entity' might be scatted in many collections, there should be one collection which represents the complete entity
For example, you might have a UserProfile collection but snippets of user profile might be embedded in other collections. In that case when you need the complete user's profile, you query it straight from UserProfile collection.

Fetch all the documents has reference to a certain document in MongoDB

I need all the documents from different collections that have reference to a certain "parent" document. Any easy workaround?
My naive approach would be to loop all the collections and loop all the documents and check the condition. Any easier / more efficient way?
My naive approach would be to loop all the collections and loop all the documents and check the condition. Any easier / more efficient way?
Well, without the data model that is hard to answer, but the reference could have an index, right? I mean you know which field is the reference? For example,
Users (parent) {
Id : ObjectId("123"),
...
}
Comments {
Id : ObjectId("abc"),
UserId : ObjectId("123"),
...
}
Questions {
Id : ObjectId("efc"),
UserId : ObjectId("123"),
...
}
Then, knowing that UserId is the reference, you can call db.Comments.find({ UserId : ObjectId("123") }) and find all references. That way, you don't have to loop through all documents. The only thing you need is a lookup table collection name -> name of the reference field

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?

Reference an _id in a Subdocument in another Collection Mongodb

I am developing an application with mongodb and nodejs
I should also mention that I am new to both so please help me through this question
my database has a collection categories and then in each category I am storing products in subdocument
just like below :
{
_id : ObjectId(),
name: String,
type: String,
products : [{
_id : ObjectId(),
name : String,
description : String,
price : String
}]
});
When it comes to store the orders in database the orders collection will be like this:
{
receiver : String,
status : String,
subOrders : [
{
products :[{
productId : String,
name : String,
price : String,
status : String
}],
tax : String,
total : String,
status : String,
orderNote : String
}
]
}
As you can see we are storing _id of products which is a subdocument of categories in orders
when storing there is no issue obviously, when it comes to fetch these data if we just need the limited field like name or price there will be no issue as well, but if later on we need some extra fields from products like description,... they are not stored in orders.
My question is this:
Is there any easy way to access other fields of products apart from loop through the whole categories in mongodb, namely I need a sample code for querying the description of a product by only having its _id in mongodb?
or our design and implementation was wrong and I have to re-design it from scratch and separate the products from categories into another collection?
please don't put links to websites or weblogs that generally talks about mongodb and its collections implementations unless they focus on a very similar issue to mine
thanks in advance
I'd assume that you'd want to return as many product descriptions as matched the current list of products, so first, there isn't a query to return only matching array elements. Using $elemMatch you can return a specific element or the first match, but not only matching array elements. However, $elemMatch can also be used as a projection operator.
db.categories({ "products._id" : "PID1" },
{ $elemMatch : { "products._id" : "PID1" },
"products._id" : 1,
"products.description" : 1})
You'd definitely want to index the "products._id" field to achieve reasonable performance.
You might consider instead creating a products collection where each document contains a category identifier, much like you would in a relational database. This is a common pattern in MongoDb when embedding doesn't make sense, or complicates queries and aggregations.
Assuming that is true:
You'll need to load the data from the second collection manually. There are no joins in MognoDb. You might consider using $in which takes a list of values for a field and loads all matching documents.
Depending on the driver you're using to access MongoDb, you should be able to use the projection feature of find, which can limit the fields returned for a document to just those you've specified.
As product descriptions ardently likely to change frequently, you might also consider caching the values for a period on the client (like a web server for example).
db.products.find({ _id: { $in : [ 'PID1', 'PID2'] } }, { description : 1 })

Can you do group by as map reduce in Mongo?

I have two collections. "people" are connected with a "location" like so:
location_id = ObjectId()
db.people.insert(
{
_id : ObjectId(),
name : "Nick",
location : location_id
});
db.locations.insert(
{
_id : location_id,
city : "Cape Town"
});
I would like to create a histogram of locations giving the count of people in each city. But I can't seem to do it with the Mongo group command because they are different collections. Is the correct way to do it with map/reduce?
map-reduce also won't help you here. It also operates on a single collection.
It seems to me that you're trying to apply your relational design skills here. Don't do that. MongoDB requires a fresh approach.
If I were you, I'd probably denormalize location into people collection.
db.people.insert(
{
_id : ObjectId(),
name : "Nick",
location : {city: "Cape Town"}
});
This way it's possible to analyze this data with map-reduce, because everything is in the same collection.