MongoDB $merge with two fields being unique before insert? - mongodb

Say I have a collection of elements with several fields, including userId and questionId. If I'm in an aggregation pipeline and I have a list of documents that all have userId and questionId as fields, but their values might already be in the collection (ie. a document of
{userId:1, questionId:1, score: 1}
but a similar document already exists in the collection
{userId:1, questionId:1, score:0}
How do I do a $merge into the collection while checking both fields? The $merge function does have an 'on: [field]' field to check overlap, but I don't think it can check two.

You can specify multiple fields in the on clause using an array.
db.toBe.aggregate([
{
"$merge": {
"into": "asIs",
"on": [
"userId",
"questionId"
],
"whenMatched": "merge"
}
}
])
Mongo Playground

Related

How does mongodb use an index to count documents?

According to docs, db.collection.countDocuments() wraps this:
db.collection.aggregate([
{ $match: <query> },
{ $group: { _id: null, n: { $sum: 1 } } }
])
Even if there is an index, all of the matched docs will be passed into the $group to be counted, no?
If not, how is mongodb able to count the docs without processing all matching docs?
The MongoDB query planner can make some optimizations.
In that sample aggregation, it can see that no fields are required except for the ones referenced in <query>, so it can add an implicit $project stage to select only those fields.
If those fields and the _id are all included in a single index, there is no need to fetch the documents to execute that query, all the necessary information is available from the index.

How to query certain elements of an array of objects? (mongodb)

say I have a mongo DB collection with records as follows:
{
email: "person1#gmail.com",
plans: [
{planName: "plan1", dataValue = 100},
{planName: "plan2", dataValue = 50}
]
},
{
email: "person2#gmail.com",
plans: [
{planName: "plan3", dataValue = 25},
{planName: "plan4", dataValue = 12.5}
]
}
and I want to query such that only the dataValue returns where the email is "person1#gmail.com" and the planName is "plan1". How would I approach this?
You can accomplish this using the Aggregation Pipeline.
The pipeline may look like this:
db.collection.aggregate([
{ $match: { "email" :"person1#gmail.com", "plans.planName": "plan1" }},
{ $unwind: "$plans" },
{ $match: { "plans.planName": "plan1" }},
{ $project: { "_id": 0, "dataValue": "$plans.dataValue" }}
])
The first $match stage will retrieve documents where the email field is equal to person1#gmail.com and any of the elements in the plans array has a planName equal to plan1.
The second $unwind stage will output one document per element in the plans array. The plans field will now be an object containing a single plan object.
In the third $match stage, the unwound documents are further matched against to only include documents with a plans.planName of plan1. Finally, the $project stage excludes the _id field and projects a single dataValue field with a value of plans.dataValue.
Note that with this approach, if the email field is not unique you may have multiple documents consist with just a dataValue field.

Mongo lookup returning all values

I have 2 collections as shown below:
branches
{
_id: ...,
custId: "abc123",
branchCode: "AA",
...other fields
}
branchHolidays
{
_id: ...,
custId: "abc123",
holidayDate: ISODate("2019-06-01T00:00:00:0000"),
holidayStatus: "PROCESSED",
..other fields
}
Need to get all branchHolidays with the custId available in branches collection along with the branchCode from branches collection. (branches.custId = branchHolidays.custId)
For the first part of join I tried the below query but I'm getting all the fields from branchHolidays collection.
db.getCollection('branchHolidays').aggregate([
{
$lookup: {
localField: "custId",
from: "branches",
foreignField: "custId",
as: "holidays"
}
},
$match: { holidayStatus: "PROCESSED" }
])
The above query returns all the documents from branchHolidays collection.
I'm new to mongo but I'm not able to figure out what the problem is. Have gone through most of the SO queries but haven't found anything which helped.
Note: There are multiple branchCodes mapped to 1 custId in branches collection.
The $lookup stage is similar to a left outer join. The sample aggregation should return all documents from the branchHolidays collection that have holidayStatus: "PROCESSED", and each document will have an added field holidays containing all documents from the branches collection that have the same custId. For those documents that do not match any braches, the holidays field will contain an empty array.
If you want to return only document that have matching branches, match on size, like:
holidays:{$not:{$size:0}}
Also note placing the $match: { holidayStatus: "PROCESSED" } before the $lookup will avoid querying the branches collection for documents that would be eliminated, which may improve performance.

Return fields one level up from subdocument query mongodb

I am trying to return a field one level up in my subdocument query. My schema is:
profile:{
contacts:[{
first_name:'Tom',
last_name:'Smith',
shared:[{
owner_id:'something',
other_data:'something'
}]
},
{
//.... more contacts
}]
}
if I write a query to get a particular contact by matching something in the 'shared' subdocument, I have access to the fields in the subdocument.
My problem is I want to search for contacts based on an owner_id in the shared subdocument, but I want to return the 'first_name' and 'last_name' one level up.
To query the subdocument I would do the following:
db.profile.find({'contacts.shared':{ower_id:'something},{'first_name':1, 'last_name':1}})
Does not return the fields in the parent.
How do you move up a level to get those fields?
try this
db.people.aggregate([
{$unwind:'$profile.contacts'},
{$unwind:'$profile.contacts.shared'},
{$match:{
'profile.contacts.shared.owner_id':"1"}
},{
$group:{
_id:null,
fname:{$first:"$profile.contacts.first_name"},
lname:{$first:"$profile.contacts.last_name"}
}
}
])

MongoDB query, filter using cursor

I have two collections, one that has _id and UserId, and another that has UserId (same unique identifier) and "other data".
I want to filter the latter collection based on a list of _ids from the former collection.
Can someone provide an example query for this scenario?
The only way to 'join' collections in MongoDB is a $lookup aggregation stage (available in version 3.2).
firstCollection.aggregate([
{ $match: { _id: {$in: [1,2,3] }}}, // filter by _ids
{
$lookup:
{
from: "secondCollection",
localField: "UserId",
foreignField: "UserId",
as: "data"
}
}
])
That will add 'data' field to the documents from the first collection which will contain all related documents from second collection. If relation is not 1:1, you can add $unwind stage to flatten results:
{$unwind: "$data"}