Working with Mongodb References inside array next to other content - arrays

I want to use the mongodb aggregation framework on 3 Collections that have to "come together" to one Query. The problem is, when I'm referencing to another collection with $lookup it deletes the other content of the array the reference is in.
Heres the collection my aggregation starts in (users):
{
"_id" : ObjectId("5c9bea89f4fe8c37175ade58"),
"kundennummer" : "000001",
"passwort" : "xxx",
"status" : "1",
"onlinestatus" : true,
"kontakt" : {
"email" : "test#test.net"
},
"thing" : [
{
"thing_id" : 2,
"onlinestatus" : false,
"status" : true,
"site" : [
{
"site_id" : 3,
"status": true
},
{
"site_id" : 4,
"status": true
}
],
"refs" : [
{
"thing_id" : 11,
"status" : true
},
{
"thing_id" : 22,
"status" : true
}
]
}
]
}
when I know want site to be extended by the content given in site collection like this:
{
"_id": 11,
"name": "test"
},
{
"_id": 22,
"name": "test2"
}
I tried to use $lookup and the status: true disappeared.
db.users.aggregate([{
$lookup:
{
from: "sites",
localField: "things.site.site_id",
foreignField: "_id",
as: "things.site"
}
}])
edit:
What I want to achieve is the following:
{
"_id" : ObjectId("5c9bea89f4fe8c37175ade58"),
"kundennummer" : "000001",
"passwort" : "xxx",
"status" : "1",
"onlinestatus" : true,
"kontakt" : {
"email" : "test#test.net"
},
"thing" : [
{
"thing_id" : 2,
"onlinestatus" : false,
"status" : true,
"site" : [
{
"site_id" : 11,
"status": true,
"name": "test"
},
{
"site_id" : 12,
"status": true,
"name": "test2"
}
],
"refs" : [
{
"thing_id" : 11,
"status" : true,
"name": "test"
},
{
"thing_id" : 12,
"status" : true,
"name": "test2"
}
]
}
]
}

To use $lookup on the embedded document in MongoDB aggregation you need to use $unwind. It will output a document for each element and then you can use $lookup. You will get results from sites as well with the help of the below query:
db.user.aggregate([
{
$unwind: "$thing"
},
{
$unwind: "$thing.site"
},
{
$unwind: "$thing.refs"
},
{
$lookup: {
from: 'sites',
let: { 'siteId': '$thing.site.site_id' },
pipeline: [
{
$match: {
$expr: {
$eq: ['$_id', '$$siteId']
}
}
},
],
as: 'siteObject'
}
},
{
$lookup: {
from: 'things',
let: { 'thingId': '$thing.refs.thing_id' },
pipeline: [
{
$match: {
$expr: {
$eq: ['$_id', '$$thingId']
}
}
},
],
as: 'thingObject'
}
},
{
$unwind: "$siteObject"
},
{
$unwind: "$thingObject"
},
{
$project: {
"_id" : 1,
"kundennummer" : 1,
"passwort" : 1,
"status" : 1,
"onlinestatus" : 1,
"kontakt" : 1,
"thing": [{
"thing_id" : "$thing.thing_id",
"onlinestatus" : "$thing.onlinestatus",
"status" : "$thing.status",
"site" : {
"site_id":"$thing.site.site_id",
"status":"$thing.site.status",
"siteName":"$siteObject.siteName",
},
"refs" : {
"thing_id":"$thing.refs.thing_id",
"status":"$thing.refs.status",
"siteName":"$thingObject.thingName",
},
}]
}
},
{
$unwind: "$thing"
},
{
$group: {
_id: {
"_id": "$_id",
"kundennummer": "$kundennummer",
"passwort": "$passwort",
"status":"$status",
"onlinestatus":"$onlinestatus",
"kontakt":"$kontakt"
},
thing: { $push: { thing_id: "$thing.thing_id", onlinestatus: "$thing.onlinestatus", status: "$thing.status",site:"$thing.site",refs: "$thing.refs" } }
}
}
])
This will result as below:
{
"_id" : {
"_id" : ObjectId("5c9bea89f4fe8c37175ade58"),
"kundennummer" : "000001",
"passwort" : "xxx",
"status" : "1",
"onlinestatus" : true,
"kontakt" : {
"email" : "test#test.net"
}
},
"thing" : [
{
"thing_id" : 2,
"onlinestatus" : false,
"status" : true,
"site" : {
"site_id" : ObjectId("5caf395e512ab5123bb4b0af"),
"status" : true,
"siteName" : "Moto"
},
"refs" : {
"thing_id" : ObjectId("5cb042ea512ab5123bb4b0b0"),
"status" : true,
"siteName" : "hi"
}
},
{
"thing_id" : 2,
"onlinestatus" : false,
"status" : true,
"site" : {
"site_id" : ObjectId("5caf395e512ab5123bb4b0af"),
"status" : true,
"siteName" : "Moto"
},
"refs" : {
"thing_id" : ObjectId("5cb042ea512ab5123bb4b0b1"),
"status" : true,
"siteName" : "world"
}
},
{
"thing_id" : 2,
"onlinestatus" : false,
"status" : true,
"site" : {
"site_id" : ObjectId("5caf395e512ab5123bb4b0ae"),
"status" : true,
"siteName" : "hello"
},
"refs" : {
"thing_id" : ObjectId("5cb042ea512ab5123bb4b0b0"),
"status" : true,
"siteName" : "hi"
}
},
{
"thing_id" : 2,
"onlinestatus" : false,
"status" : true,
"site" : {
"site_id" : ObjectId("5caf395e512ab5123bb4b0ae"),
"status" : true,
"siteName" : "hello"
},
"refs" : {
"thing_id" : ObjectId("5cb042ea512ab5123bb4b0b1"),
"status" : true,
"siteName" : "world"
}
}
]
}
Result is near by your desired result and i think you should not follow such schema it will make your every query more complex.

Related

Return Documents with Same Subarray elements

I have the following collection:
{ "_id" : 1, "tags" : [ "school", "home" ] }
{ "_id" : 2, "tags" : [ "school", "storage", "home" ] }
{ "_id" : 3, "tags" : [ "school", "storage", "home", "bones" ] }
{ "_id" : 4, "tags" : [ "school", "storage", "home", "bones", "milk" ] }
{ "_id" : 5, "tags" : [ "homes", "maps", "bones" ] }
{ "_id" : 6, "tags" : [ "xxx", "yyy", "zzz" ] }
I would like to return all documents where "tags" matches documents with exactly the same array elements.
So thus, if I were to query for tags == ["school", "home"] or tags == ["home", "school"], document w/ _id=1 would be returned
I am doing the following:
db.test.find({tags: {$in: ['school', 'home']}}) or
db.test.find({tags: {$in: ['home', 'school']}})
and the following are returned:
{ "_id" : 1, "tags" : [ "school", "home" ] }
{ "_id" : 2, "tags" : [ "school", "storage", "home" ] }
{ "_id" : 3, "tags" : [ "school", "storage", "home", "bones" ] }
{ "_id" : 4, "tags" : [ "school", "storage", "home", "bones", "milk" ] }
How may I achieve only item w/ _id=1 be returned?
Maybe something like this:
Option 1:
db.collection.find({
$and: [
{
tags: "school"
},
{
tags: "home"
},
{
tags: {
$size: 2
}
}
]
})
Explained:
Match if school & home are in the tags array and if array size is 2.
Playground
Option 2:
db.collection.find({
$or: [
{
tags: [
"school",
"home"
]
},
{
tags: [
"home",
"school"
]
}
]
})
Explained:
Match the two possible array options
Playground2

Mongodb get all documents in object array with only same values that is passed

I have the below document in my collection.
{ "_id" : ObjectId("ObjectId"), "MobNo" : "23232", "data" : [ { "status" : "PASS" }, { "status" : "PASS" } ] }
{ "_id" : ObjectId("ObjectId"), "MobNo" : "232323", "data" : [ { "status" : "PASS" }, { "status" : "FAIL" } ] }
{ "_id" : ObjectId("ObjectId"), "MobNo" : "32322", "data" : [ { "status" : "PASS" }, { "status" : "PASS" } ] }
I want a query to return all objects in data array not to have PASS status(All the objects in the array should NOT have the status as "PASS").
My Expected output here is
{ "_id" : ObjectId("ObjectId"), "MobNo" : "232323", "data" : [ { "status" : "PASS" }, { "status" : "FAIL" } ] }
My query for the above result :
db.Collection.aggregate({
$match: {
"data": {
$exists: true, $not: { $size: 0 }, $all: [
{ "$elemMatch": { "status": { $nin: ["PASS"] } } },
]
},
}
})
Now i want to my collection to return documents with data array having all object status as passed .
My expected output :
{ "_id" : ObjectId("ObjectId"), "MobNo" : "23232", "data" : [ { "status" : "PASS" }, { "status" : "PASS" } ] }
{ "_id" : ObjectId("ObjectId"), "MobNo" : "32322", "data" : [ { "status" : "PASS" }, { "status" : "PASS" }
How can i get the above output, my below query doesnt work and it return even the failure data array.
db.Collection.aggregate({
$match: {
"data": {
$exists: true, $not: { $size: 0 }, $all: [
{ "$elemMatch": { "status": { $in: ["PASS"] } } },
]
},
}
})
Output of the above query :
{ "MobNo" : "23232", "data" : [ { "status" : "PASS" }, { "status" : "PASS" } ] },
{ "MobNo" : "232323", "data" : [ { "status" : "PASS" }, { "status" : "FAIL" } ] },
{ "MobNo" : "32322", "data" : [ { "status" : "PASS" }, { "status" : "PASS" } ] }
I do not wanted the below record in my above query output:
{ "MobNo" : "232323", "data" : [ { "status" : "PASS" }, { "status" : "FAIL" } ] },
Any help ?
You can do this in several different ways, here is the most straight forward in my opinion using $size and $filter to see if any "bad" documents exist in the array.
db.collection.aggregate([
{
$match: {
$expr: {
$eq: [
{
$size: {
$filter: {
input: "$data",
cond: {
$ne: [
"$$this.status",
"PASS"
]
}
}
}
},
0
]
},
"data.0": {
$exists: true
}
}
}
])
Mongo Playground
Cause your status values are in the array field (data), so you must to $unwind it, then query in it, see this:
db.Collection.aggregate({
$unwind:{ path: "$data"},
$match: {
"data": {
$exists: true, $not: { $size: 0 }, $all: [
{ "$elemMatch": { "status": { $in: ["PASS"] } } },
]
},
}
})

Merge two objects inside an array, one with a key nested deeper than the other in the same document in Mongo DB

Schema of the document
id is the key to join
{
"foo" : [
{
"properties" : {
"id" : 1
},
},
{
"properties" : {
"id" : 2
},
}],
"bar" : [
{
"id" : 1,
"metadata" : abc
},
{
"id" : 2,
"metadata" : def
}
]
}
Goal
{
"foo" : [
{
"properties" : {
"id" : 1,
"metadata" : abc
},
},
{
"properties" : {
"id" : 2,
"metadata" : def
},
},
}
You can use the $lookup on the schema and with the help of $unwind and $project you will get the desired result.
Query:
db.foo.aggregate({
$lookup: {
from: "bar",
localField: "properties.id",
foreignField: "id",
as: "properties"
},
},
{
$unwind: {
path: "$properties",
}
},
{
$project: {
_id: 0,
properties: 1
}
})
Output:
[
{
"properties": {
"_id": ObjectId("5a934e000102030405000000"),
"id": 1,
"metadata": "abc"
}
},
{
"properties": {
"_id": ObjectId("5a934e000102030405000001"),
"id": 2,
"metadata": "def"
}
}
]

Sorting nested Array in MongoDB using aggregate

I am trying to sort the nested array "subCategoryList on top of sorting by _id on the parent document. Newbie to mongo. Any help will be appreciated
Here is sample documents:
[
{
"_id": 1000,
"name": "Automobiles",
"parentId": "",
"helpText": "Year, Brand, Model, Color, Size"
},
{
"_id": 1004,
"name": "RV / Campers",
"parentId": 1000,
"helpText": ""
},
{
"_id": 1001,
"name": "Car / SUV / Truck",
"parentId": 1000,
"helpText": ""
}
]
Here is what I tried:
db.Category.aggregate([
{
"$match": {
"parentId": ""
}
},
{
"$lookup": {
"from": "Category", "localField": "_id", "foreignField": "parentId", "as": "subCategoryList"
}
},
{
$sort: {
_id: 1
}
}
]).pretty()
But I get:
{
"_id" : 17000,
"name" : "Music",
"parentId" : "",
"helpText" : "Help Text - Brand, Model, Title",
"subCategoryList" : [
{
"_id" : 17001,
"name" : "DVD / Blu-ray",
"parentId" : 17000,
"helpText" : ""
},
{
"_id" : 17002,
"name" : "Player",
"parentId" : 17000,
"helpText" : ""
}
]
}
{
"_id" : 20000,
"name" : "Sports Gear",
"parentId" : "",
"helpText" : "Help Text - Brand, Model, Gear Type, Size, Color,",
"subCategoryList" : [
{
"_id" : 20002,
"name" : "Football",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20007,
"name" : "Tennis",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20008,
"name" : "Cricket",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20004,
"name" : "Hockey",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20003,
"name" : "Golf",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20006,
"name" : "Basketball",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20005,
"name" : "Soccer",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20010,
"name" : "Camping / Hiking",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20009,
"name" : "Cycling",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20001,
"name" : "Baseball",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20012,
"name" : "Skiing",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20011,
"name" : "Swimming",
"parentId" : 20000,
"helpText" : ""
},
{
"_id" : 20099,
"name" : "Other",
"parentId" : 20000,
"helpText" : ""
}
]
}
db.hardwares.aggregate([
{$unwind: "$subCategoryList"},
{$sort: {"subCategoryList._id": 1}},
{$group: {_id:"$_id", subCategoryList: {$push:"$subCategoryList"}}}
]);
It could be helps you, try like this...
May be it didn't work but it gives an idea
Mongo doesn't have a built in function to sort an inner array, However Mongo v4.4 introduces the $function aggregation operator. this allows us to use custom javascript functions within a pipeline.
You could use it like so:
db.Category.aggregate([
{
$addFields: {
"subCategoryList":
{
$function: {
body: function (categories) {
return categories.sort((a, b) => a._id - b._id);
},
args: ["$subCategoryList"],
lang: "js"
}
}
}
}
])
For lesser Mongo versions you will have to first $unwind the array. then $sort it and finally constructing the original structure or in your case because you are $lookuping the inner array you could use the other $lookup syntax introduced at v3.6 to $sort within the $lookup:
db.Category.aggregate([
{
"$match": {
"parentId": ""
}
},
{
"$lookup": {
"from": "Category",
let: {id: "$_id"},
pipeline: [
{
$match: {
$expr: {
$eq: ["$$id", "$parentId"]
}
}
},
{
$sort: {
_id: 1
}
}
],
"as": "subCategoryList"
}
},
{
$sort: {
_id: 1
}
}
]);

update deeply nested array mongodb

I want to push one value in attachments array using mongodb. I want to update query using following criteria.
_id:ObjectId("5b56bd2f3e18580edc85af73") "cardID": ObjectId("5b56c895d0a04836f71aa776") "commentId":"2"
I want to push value in attachments, any help would be appreciated
This is a collection object:
{
"_id" : ObjectId("5b56bd2f3e18580edc85af73"),
"orgId" : "90",
"createdBy" : "test",
"name" : "testname",
"Cards" : [
{
"cardID" : ObjectId("5b56c895d0a04836f71aa776"),
"cardName" : "test Name",
"cardCreated" : "",
"reviewer" : "",
"priority" : "",
"cardPosition" : "",
"membersAssigned" : [
"ggg",
"fff"
],
"labels" : [
"l1",
"l2"
],
"description" : "",
"attachements" : [],
"comments" : [
{
"commentId" : "2",
"commentedBy" : "test",
"date" : "",
"comment" : "Hello world",
"attachements" : [
"1",
"data"
],
"emojis" : [
":smile:",
":thumbsup:"
],
"updatedBy" : "arkadata",
"updatedOn" : "",
"subComments" : {
"commentedBy" : "jaril",
"date" : "",
"comment" : "Hello world inside dark"
}
},
{
"commentId" : "3",
"commentedBy" : "test",
"date" : "",
"comment" : "Hello world",
"attachements" : [
"1",
"raj"
],
"emojis" : [
":smile:",
":thumbsup:"
],
"updatedBy" : "arkadata",
"updatedOn" : "",
"subComments" : {
"commentedBy" : "jaril",
"date" : "",
"comment" : "Hello world inside dark"
}
},
{
"commentId" : 6.0
}
],
"dueDate" : "",
"createdDate" : "",
"lastUpdated" : "",
"checkList" : [],
"position" : "5",
"status" : "active"
},
"timestamp" : ISODate("2018-07-24T05:46:23.890Z")
}
You can try with mongodb 3.6 arrayFilters
db.collection.update(
{ "_id": ObjectId(5b56bd2f3e18580edc85af73) },
{ "$push": { "Cards.$[card].comments.$[comment].attachments": "2" } },
{ "arrayFilters": { "card.cardID": ObjectId("5b56c895d0a04836f71aa776"), "comment.commentId": 2 } }
)
Make sure you cast your ids to ObjectId
Edit:
db.collection.update(
{ "_id": ObjectId(5b56bd2f3e18580edc85af73) },
{ "$push": { "Cards.$[card].comments.$[comment].attachments": "2" } },
{ "arrayFilters": [
{ "card.cardID": ObjectId("5b56c895d0a04836f71aa776")},
{"comment.commentId": 2 }
]
}
)

Resources