Mongoose : get content of multiple subdocument in one object - javascript

Hello I've been trying to select all the subdocument of multiple document returned in ONE single object and if it's possible sorted by value.
Here is what I mean :
My collection :
{
userId: 1,
...
items : [
{name: "candle", value:5},
{name: "shoes", value: 200}
]
},
{
userId: 2,
...
items : [
{name: "burger", value:17},
{name: "car", value: 5000}
]
},
{
userId: 3,
...
items : [
{name: "chips", value:2},
]
}
And I want to make a query that return me something like that :
[
{userId:1, name: "candle", value:5},
{userId:1, name: "shoes", value: 200},
{userId:2, name: "burger", value:17},
{userId:2, name: "car", value: 5000},
{userId:3, name: "chips", value:2}
]
I've been trying this :
Mymodel.find({}).select("items"); //It return me all the items but in separate object. [ {items: []} ...]
Also if it is possible I would like to sort them : I want to get the 3 most valuable items in the users collection. I tried to use the aggregate method following some example in the mongoose doc but I didn't succeed to make it work. I also have been thinking to make a new database containing only the users items so I can sort them easily but I feel like there is a better solution to do this.
So my question is do you have an idea how could I make it work ? thank you in advance

I'm pasting just an option
var pipeline = [
{
"$unwind" : "$items"
},
{
"$addFields" : {
"name" : "$items.name",
"value" : "$items.value"
}
},
{
"$unset" : "items"
},
{
"$project" : {
"_id" : 0,
"name" : 1,
"value" : 1,
"userId" : 1
}
}
]
Run db.collname.aggregate(pipeline) or review the syntax for the driver.

Related

How should I do an aggregate?

I have an array with products(objects) inside. It looks like:
[
{_id: 123, name:'first', category:'Vape', property:[
{key:'brand', values:'Eleaf'},
{key:'mechmode', values:'some'},
{key:'color', values:['red', 'blue']},
{key:'batrtype', values:'some2'},
]},
{_id: 1234, name:'second', category:'Vape1', property:[
{key:'brand', values:'Eleaf1'},
{key:'mechmode', values:'some'},
{key:'color', values:['black']},
{key:'batrtype', values:'some2'}
]},
{_id: 12345, name:'third', category:'Vape3', property:[
{key:'brand', values:'Eleaf3'},
{key:'mechmode', values:'some'},
{key:'color', values:['green', 'yellow']},
{key:'batrtype', values:'some2'}
]},
{_id: 123456, name:'fourth', category:'Vape', property:[
{key:'brand', values:'Eleaf'},
{key:'mechmode', values:'some'},
{key:'color', values:['red', 'green']},
{key:'batrtype', values:'some2'}
]}
]
Request from client
{category:'Vape', body:{brand:'Eleaf', color:'red'}}
How should I do an aggregate in mongo to get products which have brand = 'Eleaf'
and color = red in according to request?
Can somebody help please?
bgraham's answer is exactly right if you are using find. To do the same thing in an aggregation pipeline, use $match, such as:
db.aggregate([{$match:{
category: "Vape",
$and: [
{ property: { $elemMatch: { key: "brand", value: "Eleaf" },
{ property: { $elemMatch: { key: "color", value: "red" }
]
}}])
You could then add to the array any additional pipeline stages you need.
I think this should do the trick:
db.products.find({
category: "Vape",
$and: [
{ property: { $elemMatch: { key: "brand", value: "Eleaf1" },
{ property: { $elemMatch: { key: "color", value: "red" }
]})
I'm not sure the structure of your data is best practice. It might be easier if they were fields called "brand" with a value of "color" instead of key/value pairs.

how to convert json data into a list of values (for linechart)

i have json data in following format.
{
"2" : {
"question" : "How did you like our Food?",
"avg_rating" : {
"2016-05-23" : 3.7142857142857,
"2016-05-25" : 4.5
}
},
"3" : {
"question" : "How did you like our drinks?",
"avg_rating" : {
"2016-05-23" : 3.4285714285714,
"2016-05-25" : 4
}
}
}
i want it to be formatted in following format where above data should be inside data variable of plot object.
var plot = [{
label: "",
data: [
[2016-05-23, 3.7142857142857],[2016-05-25, 4.5]
],
points: {fillColor: '#006699'}
},{
label: "",
data: [
[2016-05-23, 3.4285714285714],[2016-05-25, 4]
],
points: {fillColor: '#009933'}
}];
i am not good in english so i may not have been able to explain this problem properly. please help me to sort out this problem.

How to get element from collection in Meteor if it multi-dimensional array or object or both?

I have collection "groups". like this:
{
"_id" : "e9sc7ogDp8pwY2uSX",
"groupName" : "one",
"creator" : "KPi9JwvEohKJsFyL4",
"eventDate" : "",
"isEvent" : true,
"eventStatus" : "Event announced",
"user" : [
{
"id" : "xfaAjgcSpSeGdmBuv",
"username" : "1#gmail.com",
"email" : "1#gmail.com",
"order" : [ ],
"price" : [ ],
"confirm" : false,
"complete" : false,
"emailText" : ""
},
...
],
...
"buyingStatus" : false,
"emailTextConfirmOrder" : " With love, your Pizzaday!! "
}
How can I get a value of specific element? For example i need to get value of "Groups.user.confirm" of specific group and specific user.
I tried to do so in methods.js
'pizzaDay.user.confirm': function(thisGroupeId, thisUser){
return Groups.find({ _id: thisGroupeId },{"user": ""},{"id": thisUser}).confirm
},
but it returns nothing.
Even in mongo console I can get just users array using
db.groups.findOne({ _id: "e9sc7ogDp8pwY2uSX"},{"user": ""})
The whole code is github
http://github.com/sysstas/pizzaday2
Try the following query:-
db.groups.aggregate(
[
{
$match:
{
_id: thisGroupeId,
"user.id": thisUser
}
},
{
$project:
{
groupName : 1,
//Add other fields of `user` level, if want to project those as well.
user:
{
"$setDifference":
[{
"$map":
{
"input": "$user",
"as": "o",
"in":
{
$eq : ["$$o.id" , thisUser] //Updated here
}
}
},[false]
]
}
}
}
]);
The above query will give the object(s) matching the query in $match inside user array. Now you can access any field you want of that particular object.
'pizzaDay.user.confirm': function(){
return Groups.findOne({ _id: thisGroupeId }).user.confirm;
I resolved it using this:
Template.Pizzaday.helpers({
confirm: function(){
let isConfirm = Groups.findOne(
{_id: Session.get("idgroupe")}).user.filter(
function(v){
return v.id === Meteor.userId();
})[0].confirm;
return isConfirm;
},
});
But I still think that there is some much elegant way to do that.

how to update an array inside a MongoDB collection with another array but update only changed values?

Having the following collection named eshops:
{
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"name" : "www.example.com",
"products" : [
{
"name" : "apple", //lets say the name key here is primary key of products
"status" : 0
},
{
"name" : "banana",
"status" : 0
},
{
"name" : "iphone",
"status" : 0
}
]
}
and having this array
var products = [
{name: "apple", status: 1}
{name: "notebook", status: 0}
]
What should the update query look like if I wanted the following result?
{
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"name" : "www.example.com",
"products" : [
{
"name" : "apple",
"status" : 1
},
{
"name" : "banana",
"status" : 0
},
{
"name" : "iphone",
"status" : 0
},
{
"name" : "notebook",
"status" : 0
}
]
}
The full explaination of this is at the end so read on.
That cannot Cannot be done "atomically" in a single operation and the best you will get is "Bulk" operations which is the best way to do it.
var products = [
{name: "apple", status: 1}
{name: "notebook", status: 0}
];
var bulk = db.collection.initializeOrderedBulkOp();
products.forEach(function(product) {
// Try to update
bulk.find({
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"products.name": product.name
})
.updateOne({
"$set": { "products.$.status": product.status }
});
// Try to "push"
bulk.find({
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"products.name": { "$ne": product.name }
})
.updateOne({
"$push": { "products": product }
});
});
bulk.execute();
The other alternative is to retrieve the document via a .findOne() or similar operation, then alter the array content in client code and then .save() the altered content back.
That is what you don't want since there is no guarantee the document has not "changed" since it was read into memory. And if other members were added to the array that sort of action would "overwrite" them.
So loop the items with multiple updates. At least "Bulk" operations send these all at once to the server without waiting for responses from individual writes.
But as you point out. What if the value is still the same? For that you need to look at the "WriteResult" response from the "Bulk" operation on .execute():
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
So there were two (2) actions here depite four (4) operations being sent in total. If the array contained one more item, say "iphone" without changes:
var products = [
{name: "apple", status: 1}
{name: "notebook", status: 0},
{name: "iphone", status: 0 }
];
Then the response would be this:
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 2 })
Since the "Bulk" API is smart enough to see that the value for "status" on the matching "iphone" is not different to the value already present ( assuming nothing else changed that in between ) and does not report this as a modification.
So let the server do the work, because all the smarts you could code up are really already there.

Mongoose distinct condition not working

I'm using mongoose to query a MongoDB using following scheme:
var File = mongoose.model('File',{
size: Number,
type: String,
filename: String,
path: String,
taxonomies:[{
tags:[{
name:String,
'tag':String
}],
name: String
}]
});
Now I want to do a distinct query as follows:
File.distinct('taxonomies.tags.tag', {'taxonomies.name':'AM'},function(error, tags){
if(err)
res.send(err);
console.log("All distinct tags", tags);
})
to get all unique tags from all files where the name of the taxonomy is 'AM'. The problem is that the query above returns all tags, including those with a different taxonomies.name. Without the condition, it just works as it should.
Do I have a syntactic error in here, or am I misunderstanding how distinct works?
Update (More examples)
Each document has a taxonomy with name SM and one with name AM, something like
taxonomies: [{
tags: [
{
name:"Type",
'tag':kind
}, {
name:"Language",
'tag':lang
},
{
name:"Code",
"tag":code
}],
name:'AM'
}, {
name:'SM',
tags:[{
name:"Sales",
'tag':'has to be sold'
},{
name:"Personal filter",
'tag':'temp'
}]
}]
When I execute the query mentioned above, I get as a result:
All distinct tags [ '4007', 'fr', 'has to be sold', 'temp', 'wol', '16104', 'en' ]
while 'temp' and 'has to be sold are from SM, not AM.
I want as a result only the ones wehre taxonomies.name = 'AM', across all documents, without duplicates
The query selection is selecting documents, not elements from the array:
{'taxonomies.name':'AM'}
That criteria will select the entire document.
As a result, all items in both arrays are considered as valid for the distinct operation. If you only want subsets to be considered, you'll either need to use the aggregation framework to filter, or modify your documents so that the two taxonomies are not stored in the same document.
If you used the aggregation framework instead of the distinct command, there are a lot of options you could use to gather the distinct values:
db.test.aggregate({$unwind: "$taxonomies" },
{ $group :
{ _id: "$taxonomies.name",
tags: { $addToSet: "$taxonomies.tags" } }} )
That should group on both SM and AM (and any others):
[
{
"_id" : "SM",
"tags" : [
[
"d1",
"e1"
],
[
"d",
"e"
]
]
},
{
"_id" : "AM",
"tags" : [
[
"a1",
"b1",
"c1"
],
[
"a",
"b",
"c"
]
]
}
]
You could use $match to filter on a particular name as well.

Categories