$limit number of documents in $lookup - arrays

I encounter a problem with this query that returns this result:
{
"_id" : ObjectId("5bd22f28f77cfb1f6ce503ca"),
"search" : "flarize",
"name" : "flarize",
"color" : 0,
"profil" : "",
"banner" : "",
"desc" : "",
"date" : 1540501286109,
"friend" : [
[
{
"_id" : ObjectId("5bd22f28f77cfb1f6ce503ca"),
"search" : "flarize",
"name" : "flarize",
"email" : "flarize.b73#gmail.com",
"password" : "$2a$10$eYeOtEkEUyD7TFkjKvhZOuSSpvBolkL17TrPHuoHhOT8JrsQR0UKW",
"color" : 0,
"profil" : "",
"banner" : "",
"desc" : "",
"date" : 1540501286109,
"friend" : [
{
"id" : ObjectId("5bd22f28f77cfb1f6ce503ca"),
"date" : 1540572026419
},
{
"id" : ObjectId("5bd19a92da24674fdabd26b6"),
"date" : 1540572026419
}
],
"groupes" : [ ]
}
]
]
}
But it's not really what I want, I'd like these fields to be hidden: friend.password, friend.email and the result of friend.groups and friend.friend is limité to 10. I don't know how i can solve this problem. My request :
db.users.aggregate(
{$match:
{search:"flarize"}
},
{$lookup:
{from:"users",
localField:"friend.id",
foreignField:"_id",
as:"friend"
}},
{$project:
{ search: 1,
name: 1,
profil: 1,
banner: 1,
color: 1,
date: 1,
desc: 1,
friend: [{$slice:["$friend", 0, 10]}]
}
}).pretty();
One document of users:
{
"_id" : ObjectId("5bd22f28f77cfb1f6ce503ca"),
"search" : "flarize",
"name" : "flarize",
"email" : "theo.ba73#gmail.com",
"password" : "$2a$10$eYeOtEkEUyD7TFkjKvhZOuSSpvBolkL17TrPHuoHhOT8JrsQR0UKW",
"color" : 0,
"profil" : "",
"banner" : "",
"desc" : "",
"date" : 1540501286109,
"friend" : [
{
"id" : ObjectId("5bd22f28f77cfb1f6ce503ca"),
"date" : 1540572026419
},
{
"id" : ObjectId("5bd19a92da24674fdabd26b6"),
"date" : 1540572026419
}
],
"groupes" : [ ]
}
Thank you for helping me.

You can try below aggregation
db.users.aggregate([
{ $match: { search: "flarize" } },
{ $lookup: {
from: "users",
let: { friendId: "$friend.id" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$friendId"] }}},
{ $limit: 10 },
{ $project: { email: 0, password: 0 }}
],
as: "friend"
}}
])

Related

How to make new array from another array during aggregation?

I have following document:
{
"subscriptionIds" : [
ObjectId("60c312c6dbb5a49fbbf560ea")
],
"gps" : {
"type" : "Point",
"coordinates" : [
23.942706,
54.932539
]
},
"online" : false,
"suspended" : false,
"hwModel" : "",
"fw" : "",
"lastSeen" : ISODate("2021-06-16T04:43:36.682Z"),
"lastSimRequest" : ISODate("2021-06-16T04:34:59.749Z"),
"lastLocation" : "LT",
"lastLocationType" : "gps",
"createdAt" : ISODate("2021-05-20T10:37:16.025Z"),
"updatedAt" : ISODate("2021-06-11T07:37:56.981Z"),
"notes" : "",
"psk_seed" : "QTAebOeNP4nIs-JJSNNlkAQ78N_VaxOq98-_lQPCyZQ=",
"lastOnline" : ISODate("2021-06-15T08:01:59.886Z"),
"lastOffline" : ISODate("2021-06-16T04:43:36.682Z"),
"onlineReason" : "deviceOnlineStatusFromAC",
"offlineReason" : "deviceOfflineStatusTimeout",
"allocationSettings" : "dataplan",
"subscriptionDataplans" : [
{
"_id" : ObjectId("5fae82fc1224cc8d62b5bf17"),
"organizationId" : ObjectId("5dd63d1c1d042f3018e8374e"),
"organizationName" : "",
"name" : "Naujas plan Telia 75GB",
"enabled" : true,
"contractsId" : [
ObjectId("5e32847ab8013befcc14bb1b"),
ObjectId("5e32847ab8013befcc14bb1b")
],
"simQuota" : 0,
"periodQuota" : NumberLong(0),
"allocationRules" : null,
"createdAt" : ISODate("2020-11-13T12:58:36.650Z"),
"updatedAt" : ISODate("2021-06-14T08:08:28.728Z"),
"notes" : "",
"allowRoaming" : false,
"enablePriorityOrdering" : false,
"priorityOrdering" : ""
},
{
"_id" : ObjectId("5fcf25662b1c7d9bab8c1f7d"),
"organizationId" : ObjectId("5dd63d1c1d042f3018e8374e"),
"organizationName" : "",
"name" : "London test",
"enabled" : true,
"contractsId" : [
ObjectId("5e5dfea1efcf754767408eae")
],
"simQuota" : 0,
"periodQuota" : NumberLong(0),
"createdAt" : ISODate("2020-12-08T07:04:06.255Z"),
"updatedAt" : ISODate("2021-06-15T09:28:07.472Z"),
"notes" : "",
"allowRoaming" : true,
"enablePriorityOrdering" : false,
"priorityOrdering" : ""
}
],
}
Is there a way to make following array using "_id" and "allowRoaming" fields:
"dataplanRoaming": [
{
"_id" : ObjectId("5fae82fc1224cc8d62b5bf17"),
"allowRoaming" : false,
},
{
"_id" : ObjectId("5fcf25662b1c7d9bab8c1f7d"),
"allowRoaming" : true,
}
]
My best result was, I tried using project, addFields etc still can't get structure which I want. Rest of query works just fine just missing this part
"dataplanRoaming" : [
[
false,
true
],
[
ObjectId("5fae82fc1224cc8d62b5bf17"),
ObjectId("5fcf25662b1c7d9bab8c1f7d")
]
],
I hoped that {$addFields:{dataplanRoaming:["$subscriptionDataplans.allowRoaming", "$subscriptionDataplans._id"]}},
would give me wanted result it just made array with _id and allowRoaming as separates fields?
Is there a way to create my wanted result using aggregation etc?
$map to iterate loop of subscriptionDataplans and return needed feidls
db.collection.aggregate([
{
$addFields: {
dataplanRoaming: {
$map: {
input: "$subscriptionDataplans",
in: {
_id: "$$this._id",
allowRoaming: "$$this.allowRoaming"
}
}
}
}
}
])
Playground

MongoDB - Update field in an array object based on nested array's field value

I am trying to update a field inside array of objects, where field in nested array is equal to a value.
My goal here is to set the picture field a new url, where value field in valueList is oldRed
Product schema:
{
variations: [{
id: 1,
picture: 'https://example.picture.com',
valueList: [{
name: 'color',
value: 'oldRed'
}, {
name: 'size',
value: 'M'
}]
}, {
id: 2,
picture: 'https://example.picture.com',
valueList: [{
name: 'color',
value: 'black'
}, {
name: 'size',
value: 'M'
}]
}]
}
The closest I get is thanks to this answer, where I update all my nested array fields that contains :'oldRed' . But my final goal is to update other field picture, based on nested array field.
db.Product.findOneAndUpdate({
_id: '123'
}, {
$set: {
'variations.$[].valueList.$[nameField].value': 'newRed'
}
}, {
arrayFilters: [{
'nameField.value': 'oldRed'
}],
new: true
}
});
Please try this :
db.Product.findOneAndUpdate(
{ _id: 123 },
{
$set: {
'variations.$[item].valueList.$[nameField].value': 'newRed',
'variations.$[item].picture': 'newURL' // item is each object in variations which is being checked in arrayFilters.
}
},
{
arrayFilters: [{ 'item.valueList.value': 'oldRed' }, { 'nameField.value': 'oldRed' }],
new: true
}
)
Colletion Data :
{
"_id" : 123,
"variations" : [
{
"id" : 1,
"picture" : "https://example.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "oldRed"
},
{
"name" : "size",
"value" : "M"
},
{
"name" : "color",
"value" : "oldRed"
}
]
},
{
"id" : 2,
"picture" : "https://example.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "black"
},
{
"name" : "size",
"value" : "M"
}
]
},
{
"id" : 3,
"picture" : "https://example3.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "oldRed"
},
{
"name" : "size",
"value" : "M"
}
]
}
]
}
Result :
/* 1 */
{
"_id" : 123,
"variations" : [
{
"id" : 1,
"picture" : "newURL",
"valueList" : [
{
"name" : "color",
"value" : "newRed"
},
{
"name" : "size",
"value" : "M"
},
{
"name" : "color",
"value" : "newRed"
}
]
},
{
"id" : 2,
"picture" : "https://example.picture.com",
"valueList" : [
{
"name" : "color",
"value" : "black"
},
{
"name" : "size",
"value" : "M"
}
]
},
{
"id" : 3,
"picture" : "newURL",
"valueList" : [
{
"name" : "color",
"value" : "newRed"
},
{
"name" : "size",
"value" : "M"
}
]
}
]
}

Aggregating MongoDB, displaying top results from two separate collections

I have am trying to perform an aggregate function on my collection but I can't seem to fit the right query for the job.
My goal is to display the top 2 fastest laps on all maps and show the associated user first name and last name.
Here is my stats collections:
{
"_id" : ObjectId("5c86674d87e8cd468c850c86"),
"lapTime" : "1:32:29",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:49:01.472Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c8667ec87e8cd468c850c87"),
"lapTime" : "2:32:34",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:51:40.895Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c86674x87e8Sd567c120c86"),
"lapTime" : "1:12:29",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T10:49:01.472Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c8667f887e8cd468c850c88"),
"lapTime" : "1:88:29",
"map" : "Italy",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:51:52.727Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c866970c65910291c6f2000"),
"lapTime" : "1:34:29",
"map" : "Italy",
"driver" : [
ObjectId("5c80f78ca0ecdf26c83dfc8a")
],
"date" : ISODate("2019-03-11T13:58:08.135Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c868532b5c50c17b0917f9e"),
"lapTime" : "1:43:33",
"map" : "Italy",
"driver" : [
ObjectId("5c80f78ca0ecdf26c83dfc8a")
],
"date" : ISODate("2019-03-11T15:56:34.869Z"),
"__v" : 0
}
Since I am passing the driver ID by reference here:
"driver":[ObjectId("5c7c499b555fa13f50c9c248")] , I want to display the driver's attributes from my users collection.
Here is one of my user objects:
{
"_id" : ObjectId("5c7c499b555fa13f50c9c248"),
"password" : "$2a$10$L..Pf44/R7yJfNPdikIObe04aiJaY/e94VSKlFscjgYOe49Y7iwJK",
"email" : "john.smith#yahoo.com",
"firstName" : "John",
"lastName" : "Smith",
"laps" : [],
"__v" : 0,
}
Here is what I tried so far:
db.getCollection('stats').aggregate([
{ $group: {
_id: { map: "$map" }, // replace `name` here twice
laps: { $addToSet: "$lapTime" },
driver:{$addToSet: "$driver"},
count: { $sum: 1 }
} },
{$lookup:
{
from: "users",
localField: "firstName",
foreignField: "lastName",
as: "driver"
}},
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
As a result, I am getting drivers as a empty array.
What I am actually trying to achieve is something like this:
{
"_id" : {
"map" : "France"
},
"laps" : [
"Jonathan Smith":"2:32:34",
"Someone Else":"1:32:29"
],
"count" : 2.0
}
I think this should work:-
db.getCollection('stats').aggregate([
{ $unwind: "$driver" },
{$lookup:
{
from: "users",
localField: "driver",
foreignField: "_id",
as: "driver"
}},
{ $group: {
_id: { map: "$map" }, // replace `name` here twice
laps: { $addToSet:
{
lapTime: "$lapTime",
driverName: "$driver.firstName" + "$driver.lastName"
}
},
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);

count inner array mongoose

I want to do a count on an array. This my model
modelDetail.aggregate([
{
$group: {
_id: '$main_section.dept',
count: {$sum: 'first_array.second_array.main_data'}
}
}
], function (err, result) {
if (err) {
//next(err);
console.log(err);
} else {
console.log(JSON.stringify(result));
res.json(result);
}
});
This is bringing out 0 for all the result. Output is below
[{"_id":"design","count":0},
{"_id":"training","count":0},
{"_id":"forecast","count":0},
{"_id":"internal audit","count":0},
{"_id":"research","count":0}]
I wanted to get the count of all the data
this is my schema
var userSchema = mongoose.Schema({
main_section :{
dept : String
},
first_array :[{//
floor : String,
second_array :[{//3rd level
name : String,//4th level
date : String
}]
}]
});
this is the data in the json
{ "_id" : "5bb39baf40f87f17f01734f8",
"main_section" : {
"dept" : "design"
},
"first_array" : [{ "_id" : "5bb39baf40f87f17f01734fc", "floor" : "2nd floor" ,
"second_array" : [
{ "_id" : "5bc102f6dff41a0e844dd2b7", "name" : "Blake Tyson", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc102fddff41a0e844dd2bb", "name" : "Meagan Shawn", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e7408", "name" : "Stephen Stone", "date" : "2018-10-12T00:00:00Z" }
]
}]
},
{ "_id" : "5bb39baf40f87f17f01734f8",
"main_section" : {
"dept" : "training"
},
"first_array" : [{ "_id" : "5bb39baf40f87f17f01734fc", "floor" : "1st floor",
"second_array" : [
{ "_id" : "5bc102f6dff41a0e844dd2b7", "name" : "Micheal Harrison", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc102fddff41a0e844dd2bb", "name" : "Favour Reality", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e7108", "name" : "Gift Myers", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e71a1", "name" : "Drake Hills", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e74c2", "name" : "Hashtan Priest", "date" : "2018-10-12T00:00:00Z" }
]
}]
},
{ "_id" : "5b98987f08925c0f5cd86780",
"main_section" : {
"dept" : "forecast"
},
"first_array" : [{ "_id" : "5b98987f08925c0f5cd86784", "floor" : "4th floor",
"second_array" : [ ]
}]
},
{ "_id" : "5b98187f08924c0f5ad86790",
"main_section" : {
"dept" : "internal audit"
},
"first_array" : [{ "_id" : "5b98987f08925c0f5cd86784", "floor" : "4th floor",
"second_array" : [ ]
}]
},
{ "_id" : "5b98187f08924c0f5ad86790",
"main_section" : {
"dept" : "research"
},
"first_array" : [{ "_id" : "5b98987f08925c0f5cd86784", "floor" : "4th floor",
"second_array" : [ ]
}]
}
Basically what i was hoping to do is to let each of the array produces the total number of elements
in the array for second alongside with the main section so for example
design would output 3
training would output 5
forecast would output 0
internal audit would output 0
research would output 0
So, any help is greatly appreciated!

Mongodb - moving a field in an array to a new array in another parent

I have the following data scheme in mongodb database.
Due to a user interaction an entry can be moved from CAT_A to CAT_B, and the angularjs model changes appropriately.
[
{
"_id":"537f4407cb8a077d396bd73e",
"cat":"CAT_A",
"ntype":"category",
"entries":[
{
"title":"111",
"content":"Content One",
"ntype":"entry",
"_id":"537f4407cb8a077d396bd741"
},
{
"title":"222",
"content":"Content Two",
"ntype":"entry",
"_id":"537f4407cb8a077d396bd740"
},
{
"title":"333",
"content":"Content Three",
"ntype":"entry",
"_id":"537f4407cb8a077d396bd73f"
}
]
},
{
"_id":"537f4407cb8a077d396bd742",
"cat":"CAT_B",
"ntype":"category",
"entries":[
{
"title":"444",
"content":"Content Four",
"ntype":"entry",
"_id":"537f4407cb8a077d396bd745"
},
{
"title":"555",
"content":"Content Five",
"ntype":"entry",
"_id":"537f4407cb8a077d396bd744"
},
{
"title":"666",
"content":"Content Six",
"ntype":"entry",
"_id":"537f4407cb8a077d396bd743"
}
]
}
]
How do I save this new model to the mongo database, or really what is the best way to handle this?
Things I've though about doing:
Simply remove all the categories involved(there will be more than 2) from the database, and recreate them from the new model. This seems inefficient, also the content field may contain larger amounts of data, which makes http requests expensive.
Same as 1, but leave the 'content' out of the schema, create a new collection for content only, and somehow link that to the entry ID.
Pull an entry from CAT_A and push to CAT_B, have struggled getting this working and what if I wanted to keep the index position as in the model? ie if I wanted to move entry 6 in CAT_B to between entry 1 and 2 in CAT_A?
cheers
EDIT new schemas:
var CatSchema = new Schema({
name : String,
ntype : String,
incentries: {
ntype : String,
entry_id : { type: Schema.Types.ObjectId, ref: 'Entry' },
entry_title : String
}
});
var EntrySchema = new Schema({
cat : { type: Schema.ObjectId, ref: 'Cat' },
title : String,
content : String,
});
and the code:
exports.editCat = function (req, res) {
Cat.update({_id: req.body.old},
{$pull: {'incentries': {'entry_id': req.body.entry}}},
function (err, data) {
});
Cat.update({_id: req.body.new},
{$addToSet: { incentries : {'entry_id': req.body.entry, 'entry_title': req.body.entryTitle, ntype: 'entry' }}},
function (err, data) {
});
};
Use $pull and $push:
>db.elements.findOne()
{
"_id" : ObjectId("537f6ddcd66d3634fe5963f6"),
"arr" : [
{
"_id" : "537f4407cb8a077d396bd73e",
"cat" : "CAT_A",
"ntype" : "category",
"entries" : [
{
"title" : "111",
"content" : "Content One",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd741"
},
{
"title" : "222",
"content" : "Content Two",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd740"
},
{
"title" : "333",
"content" : "Content Three",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd73f"
}
]
},
{
"_id" : "537f4407cb8a077d396bd742",
"cat" : "CAT_B",
"ntype" : "category",
"entries" : [
{
"title" : "444",
"content" : "Content Four",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd745"
},
{
"title" : "555",
"content" : "Content Five",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd744"
},
{
"title" : "666",
"content" : "Content Six",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd743"
}
]
}
]
}
> db.elements.update({"_id" : ObjectId("537f6ddcd66d3634fe5963f6")},{$pull:{"arr.0.entries":entry},$push:{"arr.1.entries":entry}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>db.elements.findOne()
{
"_id" : ObjectId("537f6ddcd66d3634fe5963f6"),
"arr" : [
{
"_id" : "537f4407cb8a077d396bd73e",
"cat" : "CAT_A",
"ntype" : "category",
"entries" : [
{
"title" : "222",
"content" : "Content Two",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd740"
},
{
"title" : "333",
"content" : "Content Three",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd73f"
}
]
},
{
"_id" : "537f4407cb8a077d396bd742",
"cat" : "CAT_B",
"ntype" : "category",
"entries" : [
{
"title" : "444",
"content" : "Content Four",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd745"
},
{
"title" : "555",
"content" : "Content Five",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd744"
},
{
"title" : "666",
"content" : "Content Six",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd743"
},
{
"title" : "111",
"content" : "Content One",
"ntype" : "entry",
"_id" : "537f4407cb8a077d396bd741"
}
]
}
]
}
But you will have to know where CAT_A and CAT_B are within the array (position 0 and position 1)
You could also try a schema like this:
{
"_id" : ObjectId("537f6dddd66d3634fe5963f7"),
"categories" : {
CAT_A: {
"_id" : "537f4407cb8a077d396bd73e",
"cat" : "CAT_A",
"ntype" : "category",
"entries" : [...]
},
CAT_B:{
"_id" : "537f4407cb8a077d396bd742",
"cat" : "CAT_B",
"ntype" : "category",
"entries" : [...]
}
}
}
So you update query would finally be:
> db.elements.update({"_id" : ObjectId("537f6ddcd66d3634fe5963f6")},{$pull:{"categories.CAT_B.entries":entry},$push:{"categories.CAT_A.entries":entry}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
As long as you "cat" field is unique within a single document this other approach seems better to me.

Resources