Querying a multi value array to retrieve specific value in mongodb - mongodb

I have an array element in my document db with multiple parameters.This is how a single document looks like. I can search based on name which is unique. Is there a way to list all technologies associated with the name.
"name" : "Sam",
"date" : ISODate("2020-02-05T06:34:28.453Z"),
"technology" : [
{
"technologyId" : "1",
"technologyName" : "tech1"
},
{
"technologyId" : "2",
"technologyName" : "tech2"
},
{
"technologyId" : "3",
"technologyName" : "tech3"
},
{
"technologyId" : "4",
"technologyName" : "tech4"
}
],
"sector" : [
{
"sectorId" : "1",
"sectorName" : "sector1"
},
{
"sectorId" : "2",
"sectorName" : "sector2"
},
{
"sectorId" : "3",
"sectorName" : "sector3"
},
{
"sectorId" : "4",
"sectorName" : "sector4"
}
]
This is my simple query
db.getCollection('myCollection').find({'name':'Sam'})
Is there a way to retrieve all technologies for a name in a single query.
My output should have only tech1,tech2,tech3,tech4.

A two stage aggregation using $match, $project and $map.
Query:
db.collection.aggregate([
{
$match: {
name: "Sam"
}
},
{
$project: {
"name": "$name",
"technologies": {
$map: {
input: "$technology",
as: "t",
in: "$$t.technologyName"
}
}
}
}
]);
Result:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"name": "Sam",
"technologies": [
"tech1",
"tech2",
"tech3",
"tech4"
]
}
]
In case you don't want the name in the final O/P remove it from project stage.

I'm considering that you don't have duplicate tech under a single name. You can project only the tech names then map:
db.getCollection('myCollection')
.find({ name: 'Sam' }, { 'technology.technologyName': 1 })
.map(function(doc) { return doc['technology.technologyName'] })

Related

Get paginated records and total records in one mongodb aggregate query

I have my monogdb departments data structure like as shown below
[{
category: "ABC",
sections: [
{
section_hod: "x111",
section_name: "SECTION A",
section_staff_count: "v11111",
section_id: "a1111",
:
},
{
section_hod: "x2222",
section_name: "SECTION B",
section_staff_count: "v2222",
section_id: "a2222",
:
}
]
}
:
:
]
I wrote a mongodb query like as shown below
db.getSiblingDB("departments").getCollection("DepartmentDetails").aggregate([
{ $unwind : "$sections"},
{ $match : { $and : [{ "sections.section_name" : "SECTION A"},
{ $or : [{ "category" : "ABC"}]}]}},
{$project : { "name" : "$sections.section_name", "hod" : "$sections.section_hod", "staff_count" : "$sections.section_staff_count", "id" : "$sections.section_id"}},
{$skip: 0}, {$limit: 10}
]);
which gives me a list of section details as shown below which contains name, hod, staff_count, id etc
[
{
"name": "xxxxx",
"hod": "xxxxx",
"staff_count": "xxxxx",
"id": "xxxxx"
},
{
"name": "yyyyy",
"hod": "yyyyy",
"staff_count": "yyyyy",
"id": "yyyyy"
}
:
:
:
]
Everything looks good, but the problem is I have so many records in the list with which I am trying to build a pagination. For implementing pagination I know I can use the skip and limit function for iterating the pages, but for doing that I need to know the total counts of all the records.
I can do this in two ways, First way is I can execute two queries one which will be a count and then the aggregate query passing the skip and limit, second way is execute one query which return me the total counts and the documents in the order of first paginated page.
I am trying to implement the second way and bring the expected result is as shown below
{
"documents": [
{
"name": "xxxxx",
"hod": "xxxxx",
"staff_count": "xxxxx",
"id": "xxxxx"
},
{
"name": "yyyyy",
"hod": "yyyyy",
"staff_count": "yyyyy",
"id": "yyyyy"
}
:
:
:
],
"totalCount": 5444
}
Not sure if this is achievable. Can someone please help me on this. My default limit is 10
You can do it like this, it will give you total records and paginated results in one go,
db.getSiblingDB("departments").getCollection("DepartmentDetails")
.aggregate([
{ $unwind : "$sections"},
{ $match : { $and : [{ "sections.section_name" : "SECTION A"},
{ $or : [{ "category" : "ABC"}]}]}},
{
$project : {
"name" : "$sections.section_name",
"hod" : "$sections.section_hod",
"staff_count" : "$sections.section_staff_count",
"id" : "$sections.section_id"
}
},
{
$facet: {
metaData: [{
$count: 'total'
}],
records: [
{$skip: 0},
{$limit: 10}
]
}
},
{
$project: {
records: 1,
total: {
$let: {
vars: {
totalObj: {
$arrayElemAt: ['$metaData', 0]
}
},
in: '$$totalObj.total'
}
},
}
}
]);

Mongo DB - How to get group products on the base of supplier?

I need your help. Given the structure below I need to query the
document in a way to get all 'products' for a given supplier.
{
"supplier" : [
{"name" : "supplier 1",
"products" : [
{ "name" : "product 1" }
{ "name" : "product 2" }
]
},
{
"name" : "supplier 1",
"products" : [
{ "name" : "product 3" }
]
},
{
"name" : "supplier 2",
"products" : [
{ "name" : "product 3" }
]
},
]
}
The resulting document for 'supplier 1' should
be the following (or similar).
{
"products" : [
{ "name" : "product 1" },
{ "name" : "product 2" },
{ "name" : "product 3" }
]
}
The result should only contain unique values for the products.
What is a good server side solution for this? Thank you in advance.
$unwind deconstruct supplier array
$group by supplier.name and make array of products
$project to show required fields, $reduce to iterate loop of products and concat arrays or product using $concatArrays
db.collection.aggregate([
{ $unwind: "$supplier" },
{
$group: {
_id: "$supplier.name",
products: { $push: "$supplier.products" }
}
},
{
$project: {
_id: 0,
name: "$_id",
products: {
$reduce: {
input: "$products",
initialValue: [],
in: { $concatArrays: ["$$this", "$$value"] }
}
}
}
}
])
Playground

MongoDB concat two fields using aggregation where actual field is in an array

I have a mongo document like below;
{
"_id" : "123",
"info" : {
"batch" : "Batch1-Minor"
},
"batchElements" : {
"elements" : [
{
"_id" : "elementId1",
"type": "ABC"
},
{
"_id" : "elementId2",
"type": "ABC"
}
]
}
}
How can generate an aggregated output by changing the _id field inside elements by concatenating $info.batch and $batchElements.elements._id
Expected Output:
{
"_id" : "123",
"info" : {
"batch" : "Batch1-Minor"
},
"batchElements" : {
"elements" : [
{
"_id" : "Batch1-Minor-elementId1",
"type": "ABC"
},
{
"_id" : "Batch1-Minor-elementId2",
"type": "ABC"
}
]
}
}
With below query we're iterating through batchElements.elements & forming objects with type & _id & finally $map would push an array of objects back to batchElements.elements where $addFields would add the field to actual document, Try this :
db.yourCollectionName.aggregate([{
$addFields: {
'batchElements.elements': {
$map:
{
input: "$batchElements.elements",
as: "each",
in: { type: '$$each.type', '_id': { $concat: ["$info.batch", "-", "$$each._id"] } }
/** So if you've multiple fields in each object then instead of listing all, You need to use
in: { $mergeObjects: ["$$each", { '_id': { $concat: ["$info.batch", "-", "$$each._id"] } }] } */
}
}
}
}])

Find in All subkey of Object MongoDB

I have this Document in mongodb
{
"name": "test2",
"_id": "1502609098801598ffeca615f5d3dd09087c6",
"events": {
"0": {
"delay": "0",
"actionid": "2"
},
"1": {
"delay": "0",
"actionid": "3"
}
}
}
I want to find documents that contain event with specific actionid
i tried something like these but i can't find what i want
db.mycollection.find({ "events.$.actionid":"2" })
db.mycollection.find({ "events.$**.actionid":"2" })
db.mycollection.find({ "events.$": { $elemMatch: { "actionid":"2"} })
attention please: i can't change document structure and mongodb version is 3.0.6
We can take use of the aggregation feature within mongodb and project the object to an array
db.test.aggregate( [ { "$project" : { "events" : { "$objectToArray" : "$events" } } } ] )
after this we can just use the normal array filtering with a $match
db.test.aggregate([
{ "$project":
{ "events": {"$objectToArray": "$events"} } },
{ "$match":
{ "events.v.actionid" : "2" } } ] )
This will output the following:
{
"_id" : "1502609098801598ffeca615f5d3dd09087c6",
"events" : [
{
"k" : "0",
"v" : {
"delay" : "0",
"actionid" : "2"
}
},
{
"k" : "1",
"v" : {
"delay" : "0",
"actionid" : "3"
}
}
]
}
>
So you might want to project the document back to its orignial structure

Project an array with MongoDB

I'm using MongoDB's aggregation pipeline, to get my documents in the form that I want. As the last step of aggregation, I use $project to put the documents into their final form.
But I'm having trouble projecting and array of sub-documents. Here is what I currently get from aggrgation:
{
"_id": "581c8c3df1325f68ffd23386",
"count": 14,
"authors": [
{
"author": {
"author": "57f246b9e01e6c6f08e1d99a",
"post": "581c8c3df1325f68ffd23386"
},
"count": 13
},
{
"author": {
"author": "5824382511f16d0f3fd5aaf2",
"post": "581c8c3df1325f68ffd23386"
},
"count": 1
}
]
}
I want to $project the authors array so that the return would be this:
{
"_id": "581c8c3df1325f68ffd23386",
"count": 14,
"authors": [
{
"_id": "57f246b9e01e6c6f08e1d99a",
"count": 13
},
{
"_id": "5824382511f16d0f3fd5aaf2",
"count": 1
}
]
}
How would I go about achieving that?
You can unwind the array and wind it u again after projecting.
Something like this:
db.collectionName.aggregate([
{$unwind:'$authors'},
{$project:{_id:1,count:1,'author.id':'$authors.author.author','author.count':'$authors.count'}},
{$group:{_id:{_id:'$_id',count:'$count'},author:{$push:{id:'$author.id',count:'$author.count'}}}},
{$project:{_id:0,_id:'$_id._id',count:'$_id.count',author:1}}
])
the output for above will be:
{
"_id" : "581c8c3df1325f68ffd23386",
"author" : [
{
"id" : "57f246b9e01e6c6f08e1d99a",
"count" : 13.0
},
{
"id" : "5824382511f16d0f3fd5aaf2",
"count" : 1.0
}
],
"count" : 14.0
}
I have been having the same problem and just now found a simple and elegant solution that has not been mentioned anywhere, so i thought I'd share it here:
You can iterate the array using $map and project each author. With the given structure, the aggregation should look somewhat like this
db.collectionName.aggregate([
$project: {
_id: 1,
count:1,
authors: {
$map: {
input: "$authors",
as: "author",
in: {
id: "$$author.author.author",
count: $$author.author.count
}
}
}
}
])
Hope this helps anyone who is looking, like me :)
Question:
"customFields" : [
{
"index" : "1",
"value" : "true",
"label" : "isOffline",
"dataType" : "check_box",
"placeholder" : "cf_isoffline",
"valueFormatted" : "true"
},
{
"index" : "2",
"value" : "false",
"label" : "tenure_extended",
"dataType" : "check_box",
"placeholder" : "cf_tenure_extended",
"valueFormatted" : "false"
}
],
Answer:
db.subscription.aggregate([
{$match:{"autoCollect" : false,"remainingBillingCycles" : -1,"customFields.value":"false", "customFields.label" : "isOffline"}},
{$project: {first: { $arrayElemAt: [ "$customFields", 1 ] }}}
])