find the string from mongo sub array - mongodb

I have a collection with documents that look like this:
{
"_id" : ObjectId("56b53b7ddd81cc134fac76a5"),
"name_info" : [
"name c" : [
{
"aliases": ["bill", "william"]
}
],
"name e" : [
{
"aliases": ["robert", "bill"]
}
],
]
}
{
"_id" : ObjectId("87653b745481cc134fac7235"),
"name_info" : [
"name b" : [
{
"aliases": ["stan", "stanley"]
}
],
"name c" : [
{
"aliases": ["robert", "bill"]
}
],
]
}
{
"_id" : ObjectId("65433b7563a1cc134fac7634"),
"name_info" : [
"name b" : [
{
"aliases": ["tom", "tommy"]
}
]
]
}
What query should it be used in a find() command to return all objects that have alias containing 'bill'?

check this
db.collection.aggregate([
{
$project: {
"results": {
"$map": {
"input": "$name_info",
"as": "el",
"in": {
"data": {
$objectToArray: "$$el"
},
}
}
}
}
},
{
$unwind: "$results"
},
{
$unwind: "$results.data"
},
{
$unwind: "$results.data.v"
},
{
$unwind: "$results.data.v.aliases"
},
{
$match: {
"results.data.v.aliases": "bill"
}
},
])
example:
https://mongoplayground.net/p/VTNXMLNNXis
check your collection also

Related

Delete a value in a collection Mongo. Example attached

I have this document inside a collection in Mongo and I would like to delete the "7552" : "RTEST" in de bakendData and "7552" in data. How could do it using commands?
{
"_id" : ObjectId("xxxxxx"),
"contractId" : "55528fxxxxxxxxxxxx",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"5312",
"5310"
]
}
}
]
}
Desired result:
{
"_id" : ObjectId("xxxxxx"),
"contractId" : "55528fxxxxxxxxxxxx",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"3511",
"5312",
"5310"
]
}
}
]
}
I try something like that but it doesn't work:
db.collection.update(
{ contractId: "55528..............." },
{ $pull: {field1: [ { backendData: { map: {7552: "RTEST"} } }] }}
);
Also I would like to add new values. Example:
"backendData": {
"map": {
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS",
"5355" : "IRZTS",
}
},
"data": {
"defaultOrder": [
"3511",
"5312",
"5310",
"5355"
]
}
Can someone help me?
Thank in advance
I attached an image with the document in Robot 3T
I expect someone will provide a much shorter answer, but here's one way to remove and add the items you want with one update.
db.collection.update({
// match the doc by _id
"_id": ObjectId("55528f000000000000000000")
},
[
{ // rebuild field1
"$set": {
"field1": {
"$map": {
"input": "$field1",
"in": {
// merge what's there plus filtered map plus new object
"$mergeObjects": [
"$$this",
{ "backendData": {
"map": {
"$mergeObjects": [
{ "$arrayToObject": {
"$filter": {
"input": {
"$objectToArray": "$$this.backendData.map"
},
"as": "stuff",
"cond": {
// keep everything except object with 7552 key
"$ne": [ "$$stuff.k", "7552" ]
}
}
}
},
{ "5355": "IRZTS" }
]
}
}
},
{ "data": {
"defaultOrder": {
"$concatArrays": [
{ "$filter": {
"input": "$$this.data.defaultOrder",
"as": "someNum",
"cond": {
// keep everything except 7552
"$ne": [ "$$someNum", "7552" ]
}
}
},
[ "5355" ]
]
}
}
}
]
}
}
}
}
}
])
Try it on mongoplayground.net.

How to do a Mongodb $lookup for local and foreign array fields

Trying to do $lookup s for local array fields which is inside an object.
Querying case collection :
{
"no" : "2020921008981",
"sale" : {
"soldItems" : [
{
"itemId" : "5b55ac7f0550de00210a3b24",
},
{
"itemId" : "5b55ac7f0550de00215584re",
}
],
"bills" : [
{
"billNo" : "2020921053467",
"insurancePlanId" : "160",
},
{
"billNo" : "2020921053467",
"insurancePlanId" : "170",
}
]
}
}
Item collection :
{
"_id" : ObjectId("5b55ac7f0550de00210a3b24"),
"code" : "ABCDE"
},
{
"_id" : ObjectId("5b55ac7f0550de00215584re"),
"code" : "PQRST"
}
Insurance collection :
{
"_id" : ObjectId("5b55aca20550de00210a6d25"),
"name" : "HIJKL"
"plans" : [
{
"_id" : "160",
"name" : "UVWZ",
},
{
"_id" : "161",
"name" : "LMNO",
}
]
},
{
"_id" : ObjectId("5b55aca20550de00210a6d25"),
"name" : "WXYZ"
"coveragePlans" : [
{
"_id" : "169",
"name" : "5ABC",
},
{
"_id" : "170",
"name" : "4XYZ",
}
]
}
Desired output :
{
"no" : "2020921008981",
"sale" : {}
"insurances" : "HIJKL \n WXYZ",
"items" : [
{
"_id" : ObjectId("5b55ac7f0550de00210a3b24"),
"code" : "ABCDE"
},
{
"_id" : ObjectId("5b55ac7f0550de00215584re"),
"code" : "PQRST"
}
]
}
The attempt to lookup using the local itemRefId field from the item collection. And to lookup using the local insurancePlanId from the insurance collection and then $reduce the returning array into the desired format for the insurances field:
{
$lookup:
{
from: "item",
let: { iid: "$sale.soldItems.itemId" },
pipeline: [
{
$match: {
$expr: {
$in: ["$_id", {
$map: {
input: "$$iid",
in: { $_id: "$$this" }
}
}
]
}
}
}
],
as: "items"
}
},
{
$lookup:
{
from: "insurance",
let: { iid: "$sale.insurances.insurancePlanId" },
pipeline: [
{
$match: {
$expr: {
$in: ["$insurance.plans._id", {
$map: {
input: "$$iid",
in: { $toObjectId: "$$this" }
}
}
]
}
}
}
],
as: "insurancesList"
}
},
{
$addFields: {
insurances: {
$reduce: {
input: "$insurancesList.name",
initialValue: "",
in: {
$cond: [ { "$eq": [ "$$value", "" ] }, "$$this", { $concat: [ "$$value", "\n", "$$this" ] } ]
}
}
}
}
}
This attempt returns a mongodb error. Any help to get the desired output would be appreciated.
db.case.aggregate([
{
$lookup: {
from: "insurance",
let: { ipids: "$salesOrder.invoices.insurancePlanId" },
pipeline: [
{
$unwind: "$coveragePlans"
},
{
$match: { $expr: { $in: ["$coveragePlans._id", "$$ipids"] } }
},
{
$project: { _id: 0, name: 1 }
}
],
as: "insurances"
}
},
{
$lookup: {
from: "item",
let: { iid: "$salesOrder.purchaseItems.itemRefId" },
pipeline: [
{
$match: {
$expr: {
$in: ["$_id", {
$map: {
input: "$$iid",
in: { $toObjectId: "$$this" }
}
}
]
}
}
}
],
as: "items"
}
},
{
$project: {
_id: 0,
caseNumber: 1,
insurances: {
$reduce: {
input: "$insurances",
initialValue: "",
in: { $concat: ["$$value", "$$this.name", " \n "] }
}
},
items: 1
}
}
])

MongoDB query subdocument for records that don't match criteria

I currently have the following query:
db.getCollection('conversations').aggregate([
{
$lookup: {
foreignField: "c_ID",
from: "messages",
localField: "_id",
as: "messages"
}
},
{
"$unwind": "$messages"
},
{
"$sort": {
"messages.t": -1
}
},
{
"$group": {
"_id": "$_id",
"lastMessage": {
"$first": "$messages"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$allFields",
{
"lastMessage": "$lastMessage"
}
]
}
}
},
{
$project: {
messages: 0
}
},
{
$match: {
"members.uID": "1",
//"lastMessage.t": { $gt: ISODate("2020-02-04 20:38:02.154Z") }
}
},
{
$sort: { "lastMessage.t": 1 }
},
{
$limit: 10
},
{
$project: {
members: {
$slice: [ {
$filter: {
input : "$members", as : "member", cond : {
$ne : ["$$member.uID" , "1"]
}
}
}, 3 ]
}
}
},
])
However, I also have a field for each member, named "l", which contains a timestamp. It means someone has left a conversation and thus represents the leave date. I don't want anyone who left before the current timestamp (e.g. 1582056056) to be included in the members list. How can I do this?
EDIT:
conversations document
{
"_id" : ObjectId("5e35f2c840713a43aeeeb3d9"),
"members" : [
{
"uID" : "1",
"j" : 1580580922
},
{
"uID" : "4",
"j" : 1580580922,
ā€œlā€: 1580581982
},
{
"uID" : "5",
"j" : 1580580922
}
]
}
messages document
{
"_id" : ObjectId("5e35ee5f40713a43aeeeb1c5"),
"c_ID" : ObjectId("5e35f2c840713a43aeeeb3d9"),
"fromID" : "1",
"msg" : "What's up?",
"t" : 1580591922,
"d" : {
"4" : 1580592039
},
"r" : {
"4" : 1580592339
}
}
We can exclude them during $filter stage with $and and $or operators.
member.uID != 1 && (member.l == undefined || lastMessage.t < member.l)
Take a look query below.
db.conversations.aggregate([
{
$lookup: {
from: "messages",
foreignField: "c_ID",
localField: "_id",
as: "messages"
}
},
{
"$unwind": "$messages"
},
{
"$sort": {
"messages.t": -1
}
},
{
"$group": {
"_id": "$_id",
"lastMessage": {
"$first": "$messages"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$allFields",
{
"lastMessage": "$lastMessage"
}
]
}
}
},
{
$project: {
messages: 0
}
},
{
$match: {
"members.uID": "1"
}
},
{
$sort: {
"lastMessage.t": 1
}
},
{
$limit: 10
},
{
$project: {
members: {
$slice: [
{
$filter: {
input: "$members",
as: "member",
cond: {
$and: [
{
$ne: [
"$$member.uID",
"1"
]
},
{
$or: [
{
$eq: [
"$$member.l",
undefined
]
},
{
$lt: [
"$lastMessage.t",
"$$member.l"
]
}
]
}
]
}
}
},
3
]
}
}
}
])
MongoPlayground

Retrieve matched document from nested array [duplicate]

This question already has answers here:
Find in Double Nested Array MongoDB
(2 answers)
Closed 4 years ago.
I'm trying to collect all objects in a nested array where the field spec equals unknown.
The structure per document is similar to this:
{
"_id" :"5b1e73786f11e421956023c3",
"subs" : [
{
"name" : "subrepo1",
"files" : [
{
"name" : ".....",
"spec" : "Unknown"
},
{
"name" : ".....",
"spec" : "Unknown"
}
]
},
{
"name" : "subrepo2",
"files" : [
{
"name" : "file2",
"spec" : "Unknown"
},
{
"name" : ".....",
"spec" : "1234"
}
]
}
]
}
I tried the following but it doesn't work. I'm think this is in the right direction but i'm probably missing something important.
db.col.aggregate([
{$match: {'subs.files.spec': 'Unknown'}},
{$project: {
'subs.files': {$filter: {
input: '$subs.files',
//as: 'subs.files',
cond: {$eq: ['this.spec', 'FunSuite']}
}},
//_id: 0
}}
])
The expected output would be: (so ONLY the files that have spec equals to Unknown (NOT the other ones)
{
"_id" : "5b1e73786f11e421956023c3",
"subs" : [
{
"name" : "subrepo1",
"files" : [
{
"name" : ".....",
"spec" : "Unknown"
},
{
"name" : ".....",
"spec" : "Unknown"
}
]
},
{
"name" : "subrepo2",
"files" : [
{
"name" : "file2",
"spec" : "Unknown"
}
]
}
]
}
You need to use $filter aggregation operator which gives only the matched element from the array and escapes the other elements
db.collection.aggregate([
{
$unwind: "$subs"
},
{
$project: {
"subs.name": "$subs.name",
"subs.files": {
$filter: {
input: "$subs.files",
as: "file",
cond: {
$eq: [
"$$file.spec",
"Unknown"
]
}
}
}
}
},
{
$group: {
_id: "$_id",
subs: {
$push: "$subs"
}
}
}
])
Above will give following output
[
{
"_id": ObjectId("5a934e000102030405000000"),
"subs": [
{
"files": [
{
"name": ".....",
"spec": "Unknown"
},
{
"name": ".....",
"spec": "Unknown"
}
],
"name": "subrepo1"
},
{
"files": [
{
"name": "file2",
"spec": "Unknown"
}
],
"name": "subrepo2"
}
]
}
]
You can check the result here
And if you want to get the fields as in array then remove the $unwind and $replaceRoot stage from the pipeline
db.collection.aggregate([
{
$unwind: "$subs"
},
{
$project: {
"subs.name": "$subs.name",
"subs.files": {
$filter: {
input: "$subs.files",
as: "file",
cond: {
$eq: [
"$$file.spec",
"Unknown"
]
}
}
}
}
},
{
$unwind: "$subs.files"
},
{
$replaceRoot: {
newRoot: "$subs.files"
}
}
])
Above will give following output
[
{
"name": ".....",
"spec": "Unknown"
},
{
"name": ".....",
"spec": "Unknown"
},
{
"name": "file2",
"spec": "Unknown"
}
]
Try this way:
db.col.aggregate([
{
$unwind: '$subs'
},
{
$unwind: '$subs.files'
},
{
$match: {
'subs.files.spec': 'Unknown'
}
}
]);

Mongodb unwind array nested in array

This is my collection:
db.questions.insertMany([
{
"content": [
{
"languageId": "en",
"text": "What are you planning to buy today at the supermarket?"
},
{
"languageId": "nl",
"text": "Wat ben je van plan om vandaag in de supermarkt te kopen?"
},
],
"type": "multipleChoice",
"multipleChoice": {
"numAnswers": { "min": 1, "max": 1 },
"possibleAnswers": [
{
"sequence": 1,
"content": [
{
"languageId": "en",
"text": "apples"
},
{
"languageId": "nl",
"text": "appels"
},
],
},
{
"sequence": 2,
"content": [
{
"languageId": "en",
"text": "peers"
},
{
"languageId": "nl",
"text": "peren"
},
],
},
],
}
},
{
"content": [
{
"languageId": "en",
"text": "How do you feel?"
},
{
"languageId": "nl",
"text": "Hoe voel je je?"
},
],
"type": "ranking1to5",
}
]);
I want to transform into one language that are in two content arrays. So I want to have the output:
{
"_id" : ObjectId("5abe4c09d3831890de28ec8f"),
"content" :
{
"languageId" : "en",
"text" : "What are you planning to buy today at the supermarket?"
},
"type" : "multipleChoice",
"multipleChoice" : {
"numAnswers" : {
"min" : 1.0,
"max" : 1.0
},
"possibleAnswers" : [
{
"sequence" : 1.0,
"content" :
{
"languageId" : "en",
"text" : "apples"
}
},
{
"sequence" : 2.0,
"content" :
{
"languageId" : "en",
"text" : "peers"
}
}
]
}
}
/* 2 */
{
"_id" : ObjectId("5abe4c09d3831890de28ec90"),
"content" :
{
"languageId" : "en",
"text" : "How do you feel?"
},
"type" : "ranking1to5"
}
I tried using $unwind, $match and $group to tackle this problem. I have come pretty far only the last piece does not work:
db.getCollection('questions').aggregate([
{ $unwind: "$content" },
{ $match: { "content.languageId": "en" } },
{ $unwind: { path: '$multipleChoice.possibleAnswers', preserveNullAndEmptyArrays: true } },
{ $unwind: { path: '$multipleChoice.possibleAnswers.content', preserveNullAndEmptyArrays: true } },
{ $match: { "multipleChoice.possibleAnswers.content.languageId": "en" } },
{ $group: { _id: "$_id", content: { $first: "$content" }, type: { $first: "$type" }, multipleChoice: { $addToSet: "$multipleChoice" } } }
])
The problem is multipleChoice gets repeated while this should be possibleAnswers. Also the question that has no multipleChoice object should be included.
Any help is particularly appreciated !!
It looks like a good fit for the $redact. Please try the following aggregation:
db.questions.aggregate(
[
{
$redact: {
$cond: {
if: {
$eq: [
{ $ifNull: [ "$languageId", "en" ] },
"en"
]
},
then: "$$DESCEND",
else: "$$PRUNE"
}
}
},
{
$addFields: {
"content": { $arrayElemAt: [ "$content", 0 ] },
"multipleChoice.possibleAnswers": {
$map: {
input: "$multipleChoice.possibleAnswers",
as: "possibleAnswer",
in: {
"sequence": "$$possibleAnswer.sequence",
"content": { $arrayElemAt: [ "$$possibleAnswer.content", 0 ] }
}
}
}
}
},
{
$redact: {
$cond: {
if: { $eq: [ "$possibleAnswers", null ] },
then: "$$PRUNE",
else: "$$DESCEND"
}
}
}
]
);