How to match string from collection A with collection B in mongodb - javascript

I have collection A as
-> {"_id": .... , "data":"demon"}
-> {"_id": .... , "data":"god"}
collection B as
-> {"_id": .... , "title": "Book A", "description": "this book is about a demon."}
-> {"_id": .... , "title": "Book B", "description": "this book is about a god from Greek."}
-> {"_id": .... , "title": "Book C", "description": "this book is about a dog."}
I want to extract documents from collection B where description does not contain any text from collection A's "data" field.
in Plain JS, I want the following in Mongo Query
collectionA.filter( x => { return !collectionB.some(y => x.description.includes(y.data)});
How can I achieve this in MongoDB?

this would get the desired result i think but don't know how efficient it would be with a lot of data.
db.A.aggregate([
{
$project: {
_id: 0,
data: 1
}
},
{
$lookup:
{
from: 'B',
let: { word: '$data' },
pipeline: [
{
$match: {
$expr: {
$regexMatch: {
input: '$description',
regex: '$$word'
}
}
}
},
{
$project: { _id: 1 }
}
],
as: 'found'
}
},
{
$group: {
_id: null,
ids: {
$addToSet: {
$arrayElemAt: ['$found', 0]
}
}
}
},
{
$set: {
ids: {
$map: {
input: '$ids',
in: '$$this._id'
}
}
}
},
{
$lookup: {
from: 'B',
let: { found_ids: '$ids' },
pipeline: [
{
$match: {
$expr: {
$not: {
$in: ['$_id', '$$found_ids']
}
}
}
}
],
as: 'docs'
}
},
{
$unwind: '$docs'
},
{
$replaceWith: '$docs'
}
])
https://mongoplayground.net/p/YMrYeiUOQdo

Related

MongoDB - Get data in date sections from an array

I am new to MongoDB, I want to get data in sections from the array.
{
"name":"User name",
"messages":[
{
"message":"Message 1",
"date":"2022-05-12T00:00:00.000Z"
},
{
"message":"Message 2",
"date":"2022-05-12T00:00:00.000Z"
},
{
"message":"Message 3",
"date":"2022-05-13T00:00:00.000Z"
},
{
"message":"Message 4",
"date":"2022-05-13T00:00:00.000Z"
},
{
"message":"Message 5",
"date":"2022-05-13T00:00:00.000Z"
}
]
}
The results I want are
[
{
"date":"2022-05-12T00:00:00.000Z",
"messages":[
{
"message":"Message 1",
"date":"2022-05-12T00:00:00.000Z"
},
{
"message":"Message 2",
"date":"2022-05-12T00:00:00.000Z"
}
]
},
{
"date":"2022-05-13T00:00:00.000Z",
"messages":[
{
"message":"Message 3",
"date":"2022-05-13T00:00:00.000Z"
},
{
"message":"Message 4",
"date":"2022-05-13T00:00:00.000Z"
},
{
"message":"Message 5",
"date":"2022-05-13T00:00:00.000Z"
}
]
}
]
I'm not familiar with MongoDB aggregation, I have looked at many questions on stack overflow but didn't find any that helped me.
Is this possible to do? If yes any help would be greatly appreciated. :)
You can use aggregation to get your desired output.
db.collection.aggregate([
{
$unwind: "$messages"
},
{
$group: {
_id: "$messages.date",
messages: {
$push: "$messages"
}
}
},
{
$project: {
_id: 0,
date: "$_id",
messages: 1
}
}
])
$unwind - Deconstruct messages array to multiple documents.
$group - Group by messages.date and add messages document into messages array.
$project - Decorate output documents.
db.collection.aggregate([
{
$unwind: "$messages"
},
{
$group: {
_id: "$messages.date",
messages: {
$push: "$messages"
}
}
},
{
$project: {
_id: 0,
date: "$_id",
messages: 1
}
}
])
Sample Mongo Playground

Add alias to the collection targeted in lookup

I am a beginner in MongoDB. I will try my best to explain this as easy as possible, so I will take an internet example of collection, instead of the more complicated schema I am working with!
I have two collections as follows:
Users:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE"
}
]
Posts:
[
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8"
}
]
I want to join these two collections using $lookup. So I use the following aggregation on the users collection:
{
'$addFields': {
'userStrId': {
'$toString': '$_id'
}
}
}, {
'$lookup': {
'from': 'posts',
'localField': 'userStrId',
'foreignField': 'user_id',
'as': 'user_posts'
}
},
I used $addFields to add the _id field of the user as a string field, so I can use it in $lookup,
The following result is generated:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE",
"user_posts": [
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8"
}
]
}
]
The question that I have right now is, how can I add a field to each of the documents in the user_posts such that I get the following result:
[
{
"_id":{"$oid":"610bcc467b0c4008346547b8"},
"name":"xyz",
"email":"xyz#gmail.com",
"password":"xyz",
"gender":"MALE",
"user_posts": [
{
"_id": {"$oid":"610bce417b0c4008346547bc"},
"image":"myImage 1",
"caption":"r/Mars",
"user_id":"610bcc467b0c4008346547b8",
"post_id":"610bce417b0c4008346547bc"
},
{
"_id": {"$oid":"610bce417b0c4008346547be"},
"image":"myImage 2",
"caption":"hmm",
"user_id":"610bcc467b0c4008346547b8",
"post_id": "610bce417b0c4008346547be"
},
{
"_id": {"$oid":"610bce417b0c4008346547bd"},
"image":"myImage 3",
"caption":"..",
"user_id":"610bcc467b0c4008346547b8",
"post_id":"610bce417b0c4008346547bd"
}
]
}
]
post_id added to each of the documents, and its value equal to the _id of that document converted to string.
You can add a stage at the end of your pipeline stages,
$map to iterate loop of user_posts
$mergeObjects to merge current object of user_posts and new fields user_id and post_id
{
$addFields: {
user_posts: {
$map: {
input: "$user_posts",
in: {
$mergeObjects: [
"$$this",
{
user_id: "$userStrId",
post_id: { $toString: "$$this._id" }
}
]
}
}
}
}
}
Playground

How to sum item in transaction subdocument mongodb from other document relationship

I'm new in mongodb and try small project and document are design just like below
products
[
{
_id:ObjectId('60d87d6fafd7a1377c03b2e6'),
productName:"noodles instant",
description:"lorem ipsum ... ... ...."
},
{
_id:ObjectId('60d87d6fafd7a1377c03b2e7'),
productName:"Sandal",
description:"lorem ipsum ... ... ...."
}
]
and my transaction document has structure is just like below
transactions
[
{
_id:ObjectId('60d8781031308dbf565eaaa8'),
trasactionSerial:"TRS20210000001",
transactionDate:2021-06-27T13:30:23.369+00:00,
productInTransactions:[
{
_id:ObjectId('60d8781031308dbf565eaab9'),
productId:ObjectId('60d87d6fafd7a1377c03b2e6'),
qty:1500
},
{
_id:ObjectId('60d8781031308dbf565eaa47'),
productId:ObjectId('60d87d6fafd7a1377c03b2e7'),
qty:200
}
]
},
{
_id:ObjectId('60d8781031308dbf565eaab7'),
trasactionSerial:"TRS20210000002",
transactionDate:2021-06-27T13:32:23.369+00:00,
productInTransactions:[
{
_id:ObjectId('60d8781031308dbf565eaab9'),
productId:ObjectId('60d87d6fafd7a1377c03b2e6'),
qty:100
},
{
_id:ObjectId('60d8781031308dbf565eaa47'),
productId:ObjectId('60d87d6fafd7a1377c03b2e7'),
qty:300
}
]
}
]
from the transaction I hope will get some result like below from the same date in transactionDate field
result
[
{
_id:ObjectId('60d87d6fafd7a1377c03b2e6'),
productName:"noodles instant",
qtyInTransaction:1600
},
{
_id:ObjectId('60d87d6fafd7a1377c03b2e7'),
productName:"Sandal",
qtyInTransaction:500
},
]
sorry maybe my English is poor to elaborate it
thanks for any feedback and your help
Use the $lookup stage which has $group in its pipeline option.
db.products.aggregate([
{
"$lookup": {
"from": "transactions",
"let": {
"pId": "$_id"
},
"pipeline": [
{
"$unwind": "$productInTransactions"
},
{
"$match": {
"$expr": {
"$eq": [
"$productInTransactions.productId",
"$$pId"
]
},
},
},
{
"$group": {
"_id": "$$pId",
"qtyInTransaction": {
"$sum": "$productInTransactions.qty"
}
},
},
],
"as": "matchedTransactions"
}
},
{
"$project": {
"_id": 1,
"description": 1,
"qtyInTransaction": {
"$arrayElemAt": [
"$matchedTransactions.qtyInTransaction",
0
]
}
},
},
])
Mongo Playground Sample Execution

MongoDB lookup and map 2 arrays of result

There are 2 array fields after I looked up in MongoDB aggregation pipeline.
the first one
[
{
"colorId": "60828a1b216b0972da695f2a",
"name": "Exellent",
"description": "Great work"
}
]
and the second one
[
{
"_id": "60828a1b216b0972da695f2a",
"colorName": "Green",
"hexColorCodes": "#2D9D78",
"sequence": 1,
"isActivated": true,
"created_at": "2021-04-23T08:49:31.729Z",
"updated_at": "2021-04-23T08:49:31.729Z",
"__v": 0,
"isDefault": true
}
]
the result I want is
[
{
"colorId": "60828a1b216b0972da695f2a",
"name": "Exellent",
"description": "Great work",
"colorName": "Green",
"hexColorCodes": "#2D9D78"
}
]
then I want to map colorName and hexColorCodes to the first array. Here is my aggregate pipeline
db.collection.aggregate([
{
$lookup: {
from: "color_tags",
localField: "colors.colorId",
foreignField: "_id",
as: "tempColors",
},
},
{
$addFields: {
stages3: {
$map: {
input: "$colors",
in: {
$mergeObjects: [
"$$this",
{
$arrayElemAt: [
"$tempColors",
{
$indexOfArray: [
"$tempColors._id",
"$$this.colors.colorId",
],
},
],
},
],
},
},
},
},
}
])
but the result is not what I expected. It mapped with incorrect id. Please suggest.
$map to iterate loop of first array
$filter to iterate loop of second array and match colorId with _id and return matching result
$arrayElemAt to get first matching element
$mergeObjects to merge current object with return result from second array
{
$project: {
first: {
$map: {
input: "$first",
as: "f",
in: {
$mergeObjects: [
"$$f",
{
$arrayElemAt: [
{
$filter: {
input: "$second",
cond: { $eq: ["$$this._id", "$$f.colorId"] }
}
},
0
]
}
]
}
}
}
}
}
If you want to result specific fields then add a $project stage at the end,
{
$project: {
"first.colorId": 1,
"first.name": 1,
"first.description": 1,
"first.colorName": 1,
"first.hexColorCodes": 1
}
}
Playground

Destructure arrays within the MongoDB aggregation pipeline

I was wondering if it was possible to destructure arrays while I am still in the MongoDB aggregation pipeline which would make my code alot neater.
For example, I have the following aggregation pipeline.
await User.aggregate([
{ $match: { _id: userID } },
{
$project: { chatLogs: 1, username: 1, profilePicURL: 1 },
},
{ $unwind: "$chatLogs" },
{
$lookup: {
from: "users",
let: { recipientID: "$chatLogs.recipientID" },
pipeline: [
{
$match: { $expr: { $eq: ["$_id", "$$recipientID"] } },
},
{ $project: { profilePicURL: 1 } },
],
as: "chatLogs.recipientID",
},
},
]);
This gives the following results when queried:
{
"_id": "5f2ffb54eea9c2180a732afa",
"username": "joe",
"profilePicURL": "/images/profile/default_profile.png",
"chatLogs": {
"recipientID": [
{
"_id": "5f2faf5ad18a76073729f475",
"profilePicURL": "/images/profile/default_profile.png"
}
],
"chat": "5f30b6c3d117441c2abda1ba"
}
}
In my case, because "recipientID" represents a default MongoDB id, it will always be unique. Hence I would prefer the following, where the resulting recipientID field is no longer a meaningless array
Desired results:
{
"_id": "5f2ffb54eea9c2180a732afa",
"username": "joe",
"profilePicURL": "/images/profile/default_profile.png",
"chatLogs": {
"recipientID": {
"_id": "5f2faf5ad18a76073729f475",
"profilePicURL": "/images/profile/default_profile.png"
}
"chat": "5f30b6c3d117441c2abda1ba"
}
}
You can deconstruct recipientID array using $unwind in last pipeline,
await User.aggregate([
... // your all pipelines
// add this line
{ $unwind: "$chatLogs.recipientID" }
]);

Categories

Resources