Mongo DB $lookup with no common value to match - mongodb

I am a novice to MongoDb and am trying to join two collections where there is no common value.
I have two collections.
collection 1 : Role
Fields : Role, UserName
collection 2 :mysite
Fields : userName ,userEmail
In collection 1:
eg :
{
'Role' :"admin"
'UserName' : "abc.efg"
}
In collection 2:
eg:
{
'userName' : "abc Mr, efg"
'userEmail' : "abc.efg#company.com"
}
The value of username is different in format so I am looking for a way to join these two collections.
Is there any way to merge these two collections please.
Kindly help on this.

To perform uncorrelated subqueries between two collections as well as allow other join conditions besides a single equality match, the $lookup stage has the following syntax:
{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the collection to join> ],
as: <output array field>
}
}
You can use the following aggregation query using the above $lookup syntax:
db.Role.aggregate([
{
"$lookup": {
"from": "mysite",
let: {
"userName": "$UserName"
},
"pipeline": [
{
$match: {
"$expr": {
"$ne": [
{
"$indexOfCP": [
"$userEmail",
"$$userName"
]
},
-1
]
}
}
}
],
"as": "mysite"
}
},
{
"$unwind": "$mysite"
},
])
MongoDB Playground
$indexOfCP searches a string for an occurrence of a substring and returns the index of the first occurrence. If the substring is not found, it returns -1.
So, in the following stage, it checks if UserName substring is present in userEmail, if not it returns -1, if present it returns the index at which the substring is located.
Hence using the expr $ne -1 , it matches all documents that have UserNamesubstring present in userEmail, ignoring the documents where given substring is not present.
{
$match: {
"$expr": {
"$ne": [
{
"$indexOfCP": [
"$userEmail",
"$$userName"
]
},
-1
]
}
}
}

Related

Playing around with mongodb $expr $gt and $and together doesn't work

I am playing around with mongodb but facing trouble with using $expr $gt and $and operator.
created playground for better understanding..
Mongo Playground
What I want here if history collection's date is greater than main collection date and and check history's user_id not in with my input id. then it will not display any data otherwise display all data how it possible please guide
try this
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "_id",
as: "History"
}
},
{
$unwind: "$History"
},
{
"$match": {
$expr: {
$cond: {
if: {
$in: [
"5e4a8d2d3952132a08ae5763",
"$History.user_id"
]
},
then: {
$gt: [
"$date",
"$History.date"
]
},
else: {}
}
}
}
}
])
we can use $cond to match our requirements
this will return the following
1- if the specified userId exists in the user_id array, then it will get all the documents which have date > history date
2- if the specified userId does not exist in the user_id array, it will return all the documents
check this Mongo Playground

MongoDB aggregate query return document based on match query and priority values

In a mongodb collection , i have following documents :
{"id":"1234","name":"John","stateCode":"CA"}
{"id":"1234","name":"Smith","stateCode":"CA"}
{"id":"1234","name":"Tony","stateCode":"GA"}
{"id":"3323", "name":"Neo","stateCode":"OH"}
{"id":"3323", "name":"Sam","stateCode":"US"}
{"id":"4343","name":"Bruce","stateCode":"NV"}
I am trying to write a mongo aggregate query which do following things:
match based on id field
Give more priority to document having values other than "NV" or "GA" in stateCode field.
If all the document have values either "NV" or "GA" then ignore the priority.
If any of the document have stateCode other than "NV" or "GA" , then return those document.
Example 1:
id = "1234"
then return
{"id":"1234","name":"John","stateCode":"CA"}
{"id":"1234","name":"Smith","stateCode":"CA"}
Example 2:
id = "4343"
then return
{"id":"4343","name":"Bruce","stateCode":"NV"}
Could you please help with a query to achieve this.
I tried with a query , but i am stuck with error:
Failed to execute script.
Error: command failed: {
"ok" : 0,
"errmsg" : "input to $filter must be an array not string",
"code" : 28651,
"codeName" : "Location28651"
} : aggregate failed
Query :
db.getCollection('emp').aggregate([{$match:{
'id': "1234"
}
},
{
$project: {
"data": {
$filter: {
input: "$stateCode",
as: "data",
cond: { $ne: [ "$data", "GA" ],$ne: [ "$data", "NV" ] }
}
}
}
}
])
I actually recommend you split this into 2 queries, first try to find documents with a different status code and if that fails then retrieve the rest.
With that said here is a working pipeline that does it in one go, Due to the fact we cant know in advance whether the condition is true or not we need to iterate all the documents who match the id, this fact makes it VERY inefficient in the case the id is shared by many documents, if this is not possible then using this pipeline is fine.
db.getCollection('emp').aggregate([
{
$match: {
'id': "1234"
}
},
{ //we have to group so we can check
$group: {
_id: null,
docs: {$push: "$$ROOT"}
}
},
{
$addFields: {
highPriorityDocs: {
$filter: {
input: "$docs",
as: "doc",
cond: {$and: [{$ne: ["$$doc.stateCode", "NV"]}, {$ne: ["$$doc.stateCode", "GA"]}]}
}
}
}
},
{
$project: {
finalDocs: {
$cond: [ // if size of high priority docs gt 0 return them.
{$gt: [{$ize: "$highPriorityDocs"}, 0]},
"$highPriorityDocs",
"$docs"
]
}
}
},
{
$unwind: "$finalDocs"
},
{
$replaceRoot: {newRoot: "$finalDocs"}
}
])
The last two stages are just to restore the original structure, you can drop them if you don't care about it.

pipeline function in MongoDB, how to make the join

I am looking at a query in MongoDB.
Essentially, I want to join records, but only when the records in collection mongo2 meet certain conditions (those in the and statement).
I have 2 questions about this
Where can I put the local and foreign field setting. It says I cannot define them when using pipeline.
Its says that my GT and LT statements are wrong. They work in single find statements, but I am getting the error
Expression $gt takes exactly 2 arguments. 1 were passed in.
Any help will be massivel appreciated :)
Thanks guys
db.mongo.aggregate([
{ $lookup:
{
from: "mongo2",
pipeline: [
{ $match:
{ $expr:
{
$and:[{Age : {$gt:50}}, {Age : {$lt:100}}]
}
}
}
],
as: "filters"
}
}
])
The only way to access fields coming from mongo collection inside of pipeline is to define them as variables using let statement. For instance:
db.mongo.aggregate([
{
$lookup: {
from: "mongo2",
let: { "mongo_collection_id": "$_id" },
pipeline: [
{
$match: { $expr: { $eq: [ "$$mongo_collection_id", "$_id" ] } }
}
],
as: "filters"
}
}
])
Please note that you need double dollar sign ($$) to refer to that variable within pipeline. Single dollar references fields from mongo2 collection documents.
Answering second question: there are two $gt and $lt pairs in MongoDB (which might be confusing). Since you probably have to use $expr the only way is to use $gt (aggregation) so the syntax is a bit different:
{ $expr:
{
$and:[{ $gt: [ "$Age", 50 ] }, { $lt: [ "$Age", 100 ] }]
}
}

MongoDB aggregate $lookup to return unmatched items from the main collection

I would like unmatched data from the USERS collection. Here I have two collections 1) USERS, 2) COMPANY.I am able to get the matched data from both USERS using aggregate function. but in this case I want data from USERS table which are not assigned to a company.
USERS table
{
_id: "AAA",
fullName:"John Papa"
},
{
_id: "BBB",
fullName:"Robin Son"
}
COMPANY table
{
_id: "1sd1s",
Name:"Lumbar Company"
User:"AAA"
},
{
_id: "23s1dfs3",
Name:"Patricia"
User:"AAA"
}
$lookup works like LEFT OUTER JOIN so it will remove empty array when there's no match. Then you can use $size to get only empty arrays:
db.users.aggregate([
{
$lookup: {
from: "company",
localField: "_id",
foreignField: "User",
as: "companies"
}
},
{
$match: {
$expr: {
$eq: [ { "$size": "$companies" }, 0 ]
}
}
}
])
Mongo Playground

MongoDB. Select fields existing in other cillection

I have two collections. I need to select documents from the first one by the criteria that field value exists in the second collection. For example:
User_Item collection has documents like below
{
'_id' ...,
'uid' : 123,
'iid' : 'a123',
'quantity' : 10
}
The second collection contains some item's like below
{
'_id' ...,
'iid' : 'a456',
'name' : 'someItem'
}
I need to obtain the sample by the item id's ('iid') which coincide in both collections. The expected result is presented.
{
'_id' : ...,
'uid' : 123,
'iid': 'a123',
'name' : 'item123'
}
I've used $lookup in the user_items from the items but it returns EVERY documents in the first collection and there're a lot of empty arrays. I want to avoid it.
In case of $lookup in items from user_items it will return an array of users. It's not the result desired too.
Is there some options in the lookup, or may be another solution of this issue?
You can use below aggregation
db.User_Item.aggregate([
{ "$lookup": {
"from": "second",
"localField": "iid",
"foreignField": "iid",
"as": "second"
}},
{ "$match": { "second": { "$ne": [] }}},
{ "$addFields": {
"name": { "$arrayElemAt": ["$second.name", 0] }
}},
{ "$project": { "second": 0 }}
])