Sort multiple array of objects in Mongo Aggregation - database

{
"_id" : 1,
"name" : "Ashwanth Madhav",
"scores" : [
{
"type" : "exam",
"score" : 45
},
{
"type" : "quiz",
"score" : 34
},
{
"type" : "work",
"score" : 99
},
{
"type" : "work",
"score" : 38
}
],
"job_type" : [
{
"type" : "full_time",
"activity" :87
},
{
"type" : "part_time",
"activity" : 78
},
{
"type" : "full_time",
"activity" : 71.76133439165544
}
]
}
When using $addToSet the sorted list is getting unsorted. So I used $push. But it leads to duplication of data.
I need to get SCORES and JOB_TYPE sorted. Now, I have done it with, Used $addToSet first, then "$unwind" score-> "$sort" -> "$group" with $push. After "$unwind" job type -> "$sort" -> "$group" it with $push.
Is there any better way? Thanks in Advance.

Related

MongoDb sort on list of objects

I have some dummy data in my database. I'm trying to perform mongodb aggregation functions in order to sort based on a given title
{ "_id" : 1, "item" : "ABC1", "fields" : [ { "title" : "firstName", "value" : "Trish" }, { "title" : "zipcode", "value" : "01001" } ] }
{ "_id" : 2, "item" : "ABC2", "fields" : [ { "title" : "firstName", "value" : "Peter" }, { "title" : "zipcode", "value" : "00011" } ] }
The query that i'm able to come up with so far is this and the following is what mongodb returns.
db.test.aggregate([ {$unwind: "$fields"}, {$match: {"fields.title" : "firstName"}}, {$sort: {"fields.value" : 1} }])
{ "_id" : 2, "item" : "ABC2", "fields" : { "title" : "firstName", "value" : "Peter" } }
{ "_id" : 1, "item" : "ABC1", "fields" : { "title" : "firstName", "value" : "Trish" } }
However the result I would the returned dataset should include the other object in the fields list like so. The main thing that I am trying to accomplish is being able to sort the documents based on a specific field title
{ "_id" : 2, "item" : "ABC2", "fields" : [ { "title" : "firstName", "value" : "Peter" }, { "title" : "zipcode", "value" : "00011" } ] }
{ "_id" : 1, "item" : "ABC1", "fields" : [ { "title" : "firstName", "value" : "Trish" }, { "title" : "zipcode", "value" : "01001" } ] }
If you can guarantee the position of the fields, firstName always the first, and so on. You can sort by that. like this:
db.test.find().sort({'fields.0.value': -1})
//or
db.test.find().sort({'fields.0.value': 1})

How to group records based on array elements using MongoDB

I have a data collection which contains a set of records in the following format.
{
"_id" : 22,
"title" : "3D User Interfaces with Java 3D",
"isbn" : "1884777902",
"pageCount" : 520,
"publishedDate" : ISODate("2000-08-01T07:00:00Z"),
"thumbnailUrl" : "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/barrilleaux.jpg",
"longDescription" : "Description",
"status" : "PUBLISH",
"authors" : [
"Jon Barrilleaux"
],
"categories" : [
"Java",
"Computer Graphics"
]
},
{
"_id" : 23,
"title" : "Specification by Example",
"isbn" : "1617290084",
"pageCount" : 0,
"publishedDate" : ISODate("2011-06-03T07:00:00Z"),
"thumbnailUrl" : "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/adzic.jpg",
"status" : "PUBLISH",
"authors" : [
"Gojko Adzic"
],
"categories" : [
"Software Engineering"
]
}
Please note that the 'categories' is an array.
I want to count the published books for each category. I tried the following solution, but it treated the entire array as one group.
db.books.aggregate([
{
$group:{_id:"$categories", total:{$sum:1}}
}
])
Instead of so, I want to count the number of records for each individual category value inside 'categories' array.
You should first use $unwind which outputs one document for each element in the array.
db.books.aggregate([
{
$unwind : "$categories"
},
{
$group : { _id : "$categories", total: { $sum: 1 } }
}
])

MongoDB Querying Nested Arrays

I'm having some trouble with querying a Mongo Collection.
I have a Collection like this:
{
"_id" : "555bd34329de3cf232434ef2",
"cars" : [
{
"0" : {
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
},
"1" : {
"parts" : [
{
"name" : "y1",
"price" : 8
},
{
"name" : "y2",
"price" : 12
}
]
}
}
]
}
I'd like to return just the following:
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
In other words, I need to figure out how to query the Collection by two parameters at the same time:
where the ID matches "555bd34329de3cf232434ef2"
where the "name" of the part matches "x1"
Does anyone know how to do this kind of nested query?
Assuming a document structure like this:
{
"_id" : ObjectId("555bd34329de3cf232434ef2"),
"cars" : [
{
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
},
{
"parts" : [
{
"name" : "y1",
"price" : 8
},
{
"name" : "y2",
"price" : 12
}
]
}
]
}
you can run the following query:
db.collection.find({ "_id": ObjectId("555bd34329de3cf232434ef2"), "cars.parts.name" : "x1" }, { "_id": 0, "cars.$": 1 })
which will get you pretty close to where you want to be:
{
"cars" : [
{
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
}
]
}
You could get closer using the aggregation framework if that's not good enough...

Complex query containing array tasks with number position in array in MongoDB [duplicate]

This question already has answers here:
Find in Double Nested Array MongoDB
(2 answers)
MongoDB: count the number of items in an array
(3 answers)
Closed 5 years ago.
I need to know, can I make complex query which will return me result with array board but only with parameters Object inside :
board.name
_id
array with (lists.list and index position in array)
length cards in array
I'm weak in mongodb so I ask if it is possible to do so, because I have order and I do not know whether to do it in the query or on the loop in the angularjs
Here is from db result with query db.getCollection('boards').find({}) (2results)
/* 1 */
{
"_id" : ObjectId("5a0b5c6da0502e2174fd849f"),
"name" : "example 1",
"users" : [
ObjectId("59cd114cea98d9326ca1c421")
],
"lists" : [
{
"list" : "example",
"_id" : ObjectId("5a0b5c7ba0502e2174fd84ae"),
"cards" : [
{
"name" : "1",
"_id" : ObjectId("5a0b5c80a0502e2174fd84b4")
},
{
"name" : "2",
"_id" : ObjectId("5a0b5c80a0502e2174fd84b3")
},
{
"name" : "3",
"_id" : ObjectId("5a0b5c80a0502e2174fd84b2")
}
]
},
{
"list" : "example 1",
"_id" : ObjectId("5a0b5c7ba0502e2174fd84ad"),
"cards" : [
{
"name" : "1",
"_id" : ObjectId("5a0b5c83a0502e2174fd84b5")
}
]
},
{
"list" : "example",
"_id" : ObjectId("5a0b5c7ba0502e2174fd84ac"),
"cards" : [
{
"name" : "2",
"_id" : ObjectId("5a0b5c85a0502e2174fd84b6")
}
]
}
],
"__v" : 0
}
/* 2 */
{
"_id" : ObjectId("5a0b5c71a0502e2174fd84a4"),
"name" : "example 2",
"users" : [
ObjectId("59cd114cea98d9326ca1c421")
],
"lists" : [
{
"list" : "example next 1",
"_id" : ObjectId("5a0b5c93a0502e2174fd84bc"),
"cards" : [
{
"name" : "1",
"_id" : ObjectId("5a0b5c95a0502e2174fd84c2")
},
{
"name" : "2",
"_id" : ObjectId("5a0b5c95a0502e2174fd84c1")
},
{
"name" : "3",
"_id" : ObjectId("5a0b5c95a0502e2174fd84c0")
}
]
},
{
"list" : "example next 2",
"_id" : ObjectId("5a0b5c93a0502e2174fd84bb"),
"cards" : [
{
"name" : "1",
"_id" : ObjectId("5a0b5c98a0502e2174fd84c5")
},
{
"name" : "2",
"_id" : ObjectId("5a0b5c98a0502e2174fd84c4")
}
]
},
{
"list" : "example next 3",
"_id" : ObjectId("5a0b5c93a0502e2174fd84ba"),
"cards" : [
{
"name" : "1",
"_id" : ObjectId("5a0b5c9aa0502e2174fd84c6")
}
]
}
],
"__v" : 0
}
Yes,
You can structure your own response, with aggregation framework .
https://docs.mongodb.com/manual/aggregation/
The most useful stages in that framework I used are:
$project: Structure what I want.
$match: Very similiar to select in sql.
$group: to make groups
$sort: to sort :D
$unwind: to unwind arrays.
There are more I think but depend on how you want structure, An excelent tutorial here:
https://www.tutorialspoint.com/mongodb/mongodb_aggregation.htm

Mongoose Query: Find an element inside an array

Mongoose/Mongo noob here:
My Data
Here is my simplified data, each user has his own document
{ "__v" : 1,
"_id" : ObjectId( "53440e94c02b3cae81eb0065" ),
"email" : "test#test.com",
"firstName" : "testFirstName",
"inventories" : [
{ "_id" : "active",
"tags" : [
"inventory",
"active",
"vehicles" ],
"title" : "activeInventory",
"vehicles" : [
{ "_id" : ObjectId( "53440e94c02b3cae81eb0069" ),
"tags" : [
"vehicle" ],
"details" : [
{ "_id" : ObjectId( "53440e94c02b3cae81eb0066" ),
"year" : 2007,
"transmission" : "Manual",
"price" : 1000,
"model" : "Firecar",
"mileageReading" : 50000,
"make" : "Bentley",
"interiorColor" : "blue",
"history" : "CarProof",
"exteriorColor" : "blue",
"driveTrain" : "SWD",
"description" : "test vehicle",
"cylinders" : 4,
"mileageType" : "kms" } ] } ] },
{ "title" : "soldInventory",
"_id" : "sold",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] },
{ "title" : "deletedInventory",
"_id" : "deleted",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] } ] }
As you can see, each user has an inventories property that is an array that contains 3 inventories (activeInventory, soldInventory and deletedInventory)
My Query
Given an user's email a a vehicle ID, i would like my query to go through find the user's activeInventory and return just the vehicle that matches the ID. Here is what I have so far:
user = api.mongodb.userModel;
ObjectId = require('mongoose').Types.ObjectId;
return user
.findOne({email : params.username})
.select('inventories')
.find({'title': 'activeInventory'})
//also tried
//.where('title')
//.equals('activeInventory')
.exec(function(err, result){
console.log(err);
console.log(result);
});
With this, result comes out as an empty array. I've also tried .find('inventories.title': 'activeInventory') which strangely returns the entire inventories array. If possible, I'd like to keep the chaining query format as I find it much more readable.
My Ideal Query
return user
.findOne({email : params.username})
.select('inventories')
.where('title')
.equals('activeInventory')
.select('vehicles')
.id(vehicleID)
.exec(cb)
Obviously it does not work but it can give you an idea what I'm trying to do.
Using the $ positional operator, you can get the results. However, if you have multiple elements in the vehicles array all of them will be returned in the result, as you can only use one positional operator in the projection and you are working with 2 arrays (one inside another).
I would suggest you take a look at the aggregation framework, as you'll get a lot more flexibility. Here's an example query for your question that runs in the shell. I'm not familiar with mongoose, but I guess this will still help you and you'd be able to translate it:
db.collection.aggregate([
// Get only the documents where "email" equals "test#test.com" -- REPLACE with params.username
{"$match" : {email : "test#test.com"}},
// Unwind the "inventories" array
{"$unwind" : "$inventories"},
// Get only elements where "inventories.title" equals "activeInventory"
{"$match" : {"inventories.title":"activeInventory"}},
// Unwind the "vehicles" array
{"$unwind" : "$inventories.vehicles"},
// Filter by vehicle ID -- REPLACE with vehicleID
{"$match" : {"inventories.vehicles._id":ObjectId("53440e94c02b3cae81eb0069")}},
// Tidy up the output
{"$project" : {_id:0, vehicle:"$inventories.vehicles"}}
])
This is the output you'll get:
{
"result" : [
{
"vehicle" : {
"_id" : ObjectId("53440e94c02b3cae81eb0069"),
"tags" : [
"vehicle"
],
"details" : [
{
"_id" : ObjectId("53440e94c02b3cae81eb0066"),
"year" : 2007,
"transmission" : "Manual",
"price" : 1000,
"model" : "Firecar",
"mileageReading" : 50000,
"make" : "Bentley",
"interiorColor" : "blue",
"history" : "CarProof",
"exteriorColor" : "blue",
"driveTrain" : "SWD",
"description" : "test vehicle",
"cylinders" : 4,
"mileageType" : "kms"
}
]
}
}
],
"ok" : 1
}
getting the chaining query format ... i dont know how to parse it but, what you are searching for is projection, you should take a look to http://docs.mongodb.org/manual/reference/operator/projection/
it would probably look like this :
user.findOne({email: params.username}, {'inventories.title': {$elemMatch: "activeInventory", 'invertories.vehicle.id': $elemMatch: params.vehicleId}, function(err, result) {
console.log(err);
console.log(result);
})

Resources