Get inner array value based on condition with other values in mongoose - arrays

I have a DB structure as follows:
{
"Title": "AAA",
"Photos": ["/aaa/aaa.png"],
"Loc": "XXX",
"Emp": "SSS",
"Rate": [{
"Rating": 2,
"RateID": "12345654654",
"RatedDate": new Date()
}],
"Fav": [{
"FavValue": 2,
"FavID": "1111",
"FavDate": new Date()
}]
}
Here, I want to fetch all the details by default. But from array, i need to get the object that matches the ID.
In "Rate" array it needs to match "RateID" and in "Fav" array it need to match "FavID".
Both "Rate" and "Fav" wont contains objects always.
I have tried foll mongoose aggregate:
ss.aggregate([
{ $unwind: "$Rate" },
{
$group: {
"_id": '$_id',
"Title": { "$first": "$Title" },
"Loc": { "$first": "$Loc" },
"Emp": { "$first": "$Emp" },
"Photos": { "$first": "$Photos" },
"Rate": { $max: { $cond: [ { $eq: [ "$Rate.RateID", new ObjectId(id) ] }, '$Rate.Rating', null ] } },
"Fav": { $max: { $cond : [ { $eq : [{ "$size": "$Fav" }, 0]}, null, { $cond: [ { $eq: [ "$Fav.FavID", new ObjectId(id) ] }, '$Fav.FavValue', null ] } ]} }
}
}
], function (err, AvgResult) {
res.json({"result": AvgResult});
});
For "Rate", if the array is empty it returns empty result as {}
For "Fav", it returns null always if the array is empty or if it contains object
If I send the ID as '12345654654', the result should be like
{
"Title": "AAA",
"Photos": ["/aaa/aaa.png"],
"Loc": "XXX",
"Emp": "SSS",
"Rate": 2,
"Fav": null
}
If the ID is "1111", the result should be
{
"Title": "AAA",
"Photos": ["/aaa/aaa.png"],
"Loc": "XXX",
"Emp": "SSS",
"Rate": null,
"Fav": 2
}
If "Fav" is empty array in DB like "Fav": [] and ID - is "12345654654", then the result should be as follows
{
"Title": "AAA",
"Photos": ["/aaa/aaa.png"],
"Loc": "XXX",
"Emp": "SSS",
"Rate": 2,
"Fav": null
}
can any one help to get the expected result in all the above scenario..

Related

How to find array's object property(string) by its numeric value in mongodb query?

I have a restaurant collection with its documents formed like this one:
{
"address": {
"building": "1007",
"coord": [
-73.856077,
40.848447
],
"street": "Moris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [
{
"date": {
"$date": 1393804800000
},
"grade": "A",
"score": "81"
},
{
"date": {
"$date": 1378857600000
},
"grade": "A",
"score": "6"
},
{
"date": {
"$date": 1358985600000
},
"grade": "A",
"score": "99"
},
{
"date": {
"$date": 11322006400000
},
"grade": "B",
"score": "14"
},
{
"date": {
"$date": 1288715200000
},
"grade": "B",
"score": "14"
}
],
"name": "Morris Park Bake Shop"
}
My homework asked me to find any restaurants having score from 80 to 100 and I do this
db.restaurants.find({ $expr: {$and: [{$gt: [ { $toInt: "$grades.score" }, 80 ]}, {$lt: [ { $toInt: "$grades.score" }, 100 ]}] } }).pretty()
And received "Executor error during find command :: caused by :: Unsupported conversion from array to int in $convert with no onError value".
I try
db.restaurants.find({ $expr: {$and: [{$gt: [ { $toInt: "$grades.$score" }, 80 ]}, {$lt: [ { $toInt: "$grades.$score" }, 100 ]}] } }).pretty()
And this returned:"FieldPath field names may not start with '$'. Consider using $getField or $setField."
Then i try
db.restaurants.find({$and:[{'grades.score': {$gt: 80}}, {'grade.score':{$lt:100}}]}).collation({locale:'en_US' ,numericOrdering: true})
And that returned nothing. It has to return at least the document i mentioned above, right?.
Perhaps this homework is about learning proper field value types or collation. With collation, numericOrdering can be used as commented by #prasad_. If score is truly numeric, for several reasons it's best to store it as numeric.
Unfortunately there doesn't seem to be a way at this time to specify a collation with mongoplayground.net. Without using a collation, there are many ways to achieve your desired output. Here's one way.
db.collection.aggregate([
{
// make grades.score numeric
"$set": {
"grades": {
"$map": {
"input": "$grades",
"as": "grade",
"in": {
"$mergeObjects": [
"$$grade",
{ "score": { "$toDecimal": "$$grade.score" } }
]
}
}
}
}
},
{
"$match": {
"grades.score": {
"$gt": 80,
"$lt": 100
}
}
},
{
"$project": {
"_id": 0,
"name": 1
}
}
])
Try it on mongoplayground.net.

MongoDB get only selected elements from objects inside an array

What I have is a collection of documents in MongoDB that have the structure something like this
[
{
"userid": "user1",
"addresses": [
{
"type": "abc",
"street": "xyz"
},
{
"type": "def",
"street": "www"
},
{
"type": "hhh",
"street": "mmm"
},
]
},
{
"userid": "user2",
"addresses": [
{
"type": "abc",
"street": "ccc"
},
{
"type": "def",
"street": "zzz"
},
{
"type": "hhh",
"street": "yyy"
},
]
}
]
If I can give the "type" and "userid", how can I get the result as
[
{
"userid": "user2",
"type": "abc",
"street": "ccc",
}
]
It would also be great even if I can get the "street" only as the result. The only constraint is I need to get it in the root element itself and not inside an array
Something like this:
db.collection.aggregate([
{
$match: {
userid: "user1" , "address.type":"abc"
}
},
{
$project: {
userid: 1,
address: {
$filter: {
input: "$addresses",
as: "a",
cond: {
$eq: [
"$$a.type",
"abc"
]
}
}
}
}
},
{
$unwind: "$address"
},
{
$project: {
userid: 1,
street: "$address.street",
_id: 0
}
}
])
explained:
Filter only documents with the userid & addresess.type you need
Project/Filter only the addresses elements with the needed type
unwind the address array
project only the needed elements as requested
For best results create index on the { userid:1 } field or compound index on { userid:1 , address.type:1 } fields
playground
You should be able to use unwind, match and project as shown below:
db.collection.aggregate([
{
"$unwind": "$addresses"
},
{
"$match": {
"addresses.type": "abc",
"userid": "user1"
}
},
{
"$project": {
"_id": 0,
"street": "$addresses.street"
}
}
])
You can also duplicate the match step as the first step to reduce the number of documents to unwind.
Here is the playground link.
There is a similar question/answer here.

Search in Embedded Documents in MongoDB?

I have document as shown
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
},
{
"Users": [
{
"Name": "Witcher Proxima",
"_id": "2",
},
{
"Name": "Saga",
"_id": "4",
}
],
"_id": "13",
}
]
I want to search for those documents whose Users array has that particular ID
For Example if
ID == 1 // should return
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
}
]
ID == 2 // should return
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
},
{
"Users": [
{
"Name": "Witcher Proxima",
"_id": "2",
},
{
"Name": "Saga",
"_id": "4",
}
],
"_id": "13",
}
]
ID == 4 // should return
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Saga",
"_id": "4",
}
],
"_id": "13",
}
]
As you can see from above my query should return only those objects whose "Users" array contains an object with given ID. I tried this but it doesn't work.
const chats = await Chats.find({
Users: { $elemMatch: { _id: "1" } },
});
// this returns an empty array
const chats = await Chats.find({
Users: { $elemMatch: { Name: "Kartikey Vaish" } },
});
// this returns
[
{
"Users": [
{
"Name": "Kartikey Vaish",
"_id": "1",
},
{
"Name": "Witcher Proxima",
"_id": "2",
}
],
"_id": "12",
}
]
What am I doing wrong here?
Is it something related to _id paramter?
EDIT:
My Chats Schema looks like this -
const Chats = mongoose.model(
"Chats",
new mongoose.Schema({
Users: {
type: Array,
required: true,
default: [],
},
})
);
Update your schema as shown below
const Chats = mongoose.model(
"Chats",
new mongoose.Schema({
Users: [{
_id: {
type: String,
required: true, // Include only if needed!
unique: true // Include only if needed!
},
Name: {
type: String,
index: true // Include only if needed!
}
}]
})
);
If you do not explicitly mention _id MongoDB will create _id field as ObjectId.

How to find in specific date interval

I have problem in Mongodb to find wanted documents and fragment of list field. So I have following document:
{
"_id": "5ed38e5d2a6e74567c7a579c",
"id": "AAA",
"events": [
{
"dayTypeId": "5e71e1918ee9a326ebdf1611",
"startDate": {
"$date": "2020-06-01T00:00:00.000Z"
},
"endDate": {
"$date": "2020-06-04T00:00:00.000Z"
},
"stared": false,
"userId": "XXX",
"mixing": "wjk0ra1v7m88p0x5aaxwndkmwmlxx4pn92hzjlgpl34u8ojx855m8e8spp1v7l57omtoc4qxbv0g22nybqubd3hq5skuff8ezbzdum2a92itwco64tbi5y2p5mboznxiuwynv0rb8eqk9d80ib65cve6ab9p1d1divee3wbywc2st1lkjruqvgu42zgx8mjtsnb8gyeqtxycl4ujpllgxpshdu8o97iiw347bjqv4mrv6jgwq4r21zp5rm4dw6a1"
},
{
"dayTypeId": "5e71e1918ee9a326ebdf1611",
"startDate": {
"$date": "2020-06-10T00:00:00.000Z"
},
"endDate": {
"$date": "2020-06-10T00:00:00.000Z"
},
"stared": false,
"userId": "XXX",
"mixing": "wjk0ra1v7m88p0x5aaxwndkmwmlxx4pn92hzjlgpl34u8ojx855m8e8spp1v7l57omtoc4qxbv0g22nybqubd3hq5skuff8ezbzdum2a92itwco64tbi5y2p5mboznxiuwynv0rb8eqk9d80ib65cve6ab9p1d1divee3wbywc2st1lkjruqvgu42zgx8mjtsnb8gyeqtxycl4ujpllgxpshdu8o97iiw347bjqv4mrv6jgwq4r21zp5rm4dw6a1"
},
{
"dayTypeId": "5e71d8628ee9a326e7df160d",
"startDate": {
"$date": "2020-06-05T00:00:00.000Z"
},
"endDate": {
"$date": "2020-06-09T00:00:00.000Z"
},
"stared": false,
"userId": "XXX",
"mixing": "wjk0ra1v7m88p0x5aaxwndkmwmlxx4pn92hzjlgpl34u8ojx855m8e8spp1v7l57omtoc4qxbv0g22nybqubd3hq5skuff8ezbzdum2a92itwco64tbi5y2p5mboznxiuwynv0rb8eqk9d80ib65cve6ab9p1d1divee3wbywc2st1lkjruqvgu42zgx8mjtsnb8gyeqtxycl4ujpllgxpshdu8o97iiw347bjqv4mrv6jgwq4r21zp5rm4dw6a1"
},
{
"dayTypeId": "5e71d8628ee9a326e7df160d",
"startDate": {
"$date": "2020-07-13T00:00:00.000Z"
},
"endDate": {
"$date": "2020-07-21T00:00:00.000Z"
},
"stared": false,
"userId": "XXXX",
"mixing": "wjk0ra1v7m88p0x5aaxwndkmwmlxx4pn92hzjlgpl34u8ojx855m8e8spp1v7l57omtoc4qxbv0g22nybqubd3hq5skuff8ezbzdum2a92itwco64tbi5y2p5mboznxiuwynv0rb8eqk9d80ib65cve6ab9p1d1divee3wbywc2st1lkjruqvgu42zgx8mjtsnb8gyeqtxycl4ujpllgxpshdu8o97iiw347bjqv4mrv6jgwq4r21zp5rm4dw6a1"
}
]
}
I want to get fragment of "events" list where either the startDate or the endDate is in the range of 02.06.2020-11.06.2020.
For example out of my sample data I want all but the last element to be returned.
You can use $filter to filter out any events that do not match your conditions:
db.collection.aggregate([
{
$match: {
$or: [
{
"events.startDate": {$gte: new Date("2020-06-02"), $lte: new Date("2020-06-11")}
},
{
"events.endDate": {$gte: new Date("2020-06-02"), $lte: new Date("2020-06-11")}
}
]
}
},
{
$project: {
events: {
$filter: {
input: "$events",
as: "event",
cond: {
$or: [
{
$and: [
{$gte: ["$$event.startDate", new Date("2020-06-02")]},
{$lte: ["$$event.startDate", new Date("2020-06-11")]},
]
},
{
$and: [
{$gte: ["$$event.endDate", new Date("2020-06-02")]},
{$lte: ["$$event.endDate", new Date("2020-06-11")]},
]
}
]
}
}
}
}
}
])

How can I get unique array from a collection of nested objects with lodash?

I have the following collection:
"items": [{
"id": 1,
"title": "Montrachet",
"imageUrl": "http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"imageUrls": [
"http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"http://media.riepenau.com/wines/17973_b.jpg",
"http://lorempixel.com/400/400/food/3"
],
"properties": [
{"description" : "Kırmızı Şaraplar Desc"},
{"region" :"Bordeaux"},
{"age": "16"},
{"producer" :"Kayra"},
{"grapeType":"Espadeiro"}
],
"priceGlass": "1",
"priceBottle": "2",
"year": "1999"
},
{
"id": 2,
"title": "Montrachet2",
"imageUrl": "http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"imageUrls": [
"http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"http://media.riepenau.com/wines/17973_b.jpg",
"http://lorempixel.com/400/400/food/3"
],
"properties": [
{"description" : "Kırmızı Şaraplar Desc"},
{"region" :"Bordeaux"},
{"age": "16"},
{"producer" :"Kayra"},
{"grapeType":"Chardonnay"}
],
"priceGlass": "1",
"priceBottle": "2",
"year": "1999",
}
]
I want to grab unique grapeTypes from that collection. The returning array shold be ["Chardonnay","Espadeiro"]
What is the best way to do it with lodash?
I think this combination of pluck, map and filter should do it:
var result = _.chain(obj.items).pluck('properties').map(function(obj) {
return _.filter(obj, function(prop) {
return prop.grapeType;
})[0].grapeType;
}).uniq().value();
console.log(result);
Check the demo run below.
// Code goes here
var obj = {
items: [{
"id": 1,
"title": "Montrachet",
"imageUrl": "http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"imageUrls": [
"http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"http://media.riepenau.com/wines/17973_b.jpg",
"http://lorempixel.com/400/400/food/3"
],
"properties": [{
"description": "Kırmızı Şaraplar Desc"
}, {
"region": "Bordeaux"
}, {
"age": "16"
}, {
"producer": "Kayra"
}, {
"grapeType": "Espadeiro"
}
],
"priceGlass": "1",
"priceBottle": "2",
"year": "1999"
},
{
"id": 2,
"title": "Montrachet2",
"imageUrl": "http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"imageUrls": [
"http://winebuff.com.hk/products_image/3376-Ramonet-ChassagneMontrachetBlanc.jpg",
"http://media.riepenau.com/wines/17973_b.jpg",
"http://lorempixel.com/400/400/food/3"
],
"properties": [{
"description": "Kırmızı Şaraplar Desc"
}, {
"region": "Bordeaux"
}, {
"age": "16"
}, {
"producer": "Kayra"
}, {
"grapeType": "Chardonnay"
}
],
"priceGlass": "1",
"priceBottle": "2",
"year": "1999",
}
]
};
var result = _.chain(obj.items).pluck('properties').map(function(obj) {
return _.filter(obj, function(prop) {
return prop.grapeType;
})[0].grapeType;
}).uniq().value();
document.write(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.8.0/lodash.js"></script>
UPD. If grapeType can be missing from properties then the script should be
var result = _.chain(obj.items).pluck('properties').map(function(obj) {
return (_.filter(obj, function(prop) {
return prop.grapeType;
})[0] || {}).grapeType;
}).compact().uniq().value();
Here's one way to do it with lodash:
_(items)
.pluck('properties')
.map(function(item) {
return _.find(item, _.ary(_.partialRight(_.has, 'grapeType'), 1));
})
.pluck('grapeType')
.uniq()
.value();
First, you get the properties arrays using pluck(). Next, you use find() to get the first object in this array that has a grapeType property. This is done using has(), and partially-applying the argument to build the callback function.
Next, you use pluck() again to get the actual property values. Finally, uniq() ensures there are no duplicates.

Resources