These two lines of code simply output the same result, so what is the difference between them? I know I know, documentation... But I mean in this context. Thank you for your answers!
db.someData.find({$and: [{genre: {$eq: "action"}}, {genre: {$eq: "thriller"}}]}).pretty()
db.someData.find({genre: {$all: ["action", "thriller"]}}).pretty()
This is the collection in my mongodb database.
{
"_id" : ObjectId("5d19fe6080fc4d046d99d42b"),
"title" : "The Last Student Returns",
"meta" : {
"rating" : 9.5,
"aired" : 2018,
"runtime" : 100
},
"visitors" : 1300000,
"expectedVisitors" : 1550000,
"genre" : [
"thriller",
"drama",
"action"
]
}
{
"_id" : ObjectId("5d19fe6080fc4d046d99d42c"),
"title" : "Teach me if you can",
"meta" : {
"rating" : 8.5,
"aired" : 2014,
"runtime" : 90
},
"visitors" : 590378,
"expectedVisitors" : 500000,
"genre" : [
"action",
"thriller"
]
}
{
"_id" : ObjectId("5d19fe6080fc4d046d99d42d"),
"title" : "Supercharged Teaching",
"meta" : {
"rating" : 9.3,
"aired" : 2016,
"runtime" : 60
},
"visitors" : 370000,
"expectedVisitors" : 1000000,
"genre" : [
"thriller",
"action"
]
}
Interesting that you mentioned documentation since your exact question is actually answered there:
Behavior
Equivalent to $and Operation
The $all is equivalent to an $and operation of the specified values;
i.e. the following statement:
{ tags: { $all: [ "ssl" , "security" ] } }
is equivalent to:
{ $and: [ { tags: "ssl" }, { tags: "security" } ] }
But overall there are many ways to get the same result with mongo just like there are many ways to get the same exact result with JS etc.
Related
I'm trying to retrieve just a lesson from a classes collection based on it's id. I put together the following from previous answers, but it doesn't return anything. Can someone point me in the right direction?
My Code
Class.aggregate([
{$match: {'lessons._id': id}},
{$project: {
lessons: {$filter: {
input: '$lessons',
as: 'lesson',
cond: {$eq: ['$$lesson._id', id]}
}},
_id: 0
}}
])
Example
{
"_id" : ObjectId("14354"),
"title" : "Easy Math",
"instructor_id" : "2454",
"lessons" : [
{
"lesson_body" : "2 + 2 = 4",
"lesson_title" : "Addition",
"_id" : ObjectId("3456")
},
{
"lesson_body" : "4 - 2 = 2",
"lesson_title" : "Subtraction",
"_id" : ObjectId("4456")
}
],
"__v" : 0
}
{
"_id" : ObjectId("5345"),
"title" : "Harder Math",
"instructor_id" : "6345",
"lessons" : [
{
"lesson_body" : "2 * 2 = 4",
"lesson_title" : "Multiplication",
"_id" : ObjectId("7454")
}
],
"__v" : 0
}
Shouldn't it be lessons instead of lesson
on this line :
cond: {$eq: ['$$lessons._id', id]}
Hope this will help you:
db.COLLECTION_NAME.find({ lessons: { $elemMatch: { "_id" : ObjectId("3456")} } })
I am not totally sure, but remember having similar issues. Also I think your syntax looks somewhat strange? Try to adapt to this, this is how I have learned it should look atleast:
var objectId = require('mongodb').ObjectID;
And then when you try to get the object by ID:
db.collection("classes").findOne({"_id": new ObjectID(id)}
Feel free to find inspiration here: github repository
I am trying to create a dashboard where I show summaries of order data within the app. In this case I am simply wanting count the number of items in a given category in my Orders collection. My code so far looks like this:
Collection data
{
"_id" : "a6wHiXxyM5DwSAsfq",
"orderNumber" : 1234,
"createdAt" : "11/01/2016, 14:43:49",
"productsInOrder" : [
{
"category" : "ambient",
"item" : 50818,
"desc" : "Tasty Rubber Chicken",
"quantity" : "44",
"price" : "0.92",
"lineprice" : "40.48",
"_id" : "FFNxG8vujs6NGN69r"
},
{
"category" : "frozen",
"item" : 71390,
"desc" : "Generic Granite Fish",
"quantity" : "11",
"price" : "1.00",
"lineprice" : "11.00",
"_id" : "LcRtpyLxkWyh39kkB"
}
]
}
{
"_id" : "PdpywXCvfew7qojmA",
"orderNumber" : 1234,
"createdAt" : "11/01/2016, 14:44:15",
"productsInOrder" : [
{
"category" : "frozen",
"item" : 71390,
"desc" : "Generic Granite Fish",
"quantity" : "44",
"price" : "1.00",
"lineprice" : "44.00",
"_id" : "dAscx4R8pcBgbzoZs"
},
{
"category" : "frozen",
"item" : 66940,
"desc" : "Gorgeous Granite Bike",
"quantity" : "55",
"price" : "4.21",
"lineprice" : "231.55",
"_id" : "xm3mFRmPmmdPxjfP9"
},
{
"category" : "frozen",
"item" : 96029,
"desc" : "Gorgeous Plastic Fish",
"quantity" : "1234",
"price" : "4.39",
"lineprice" : "5417.26",
"_id" : "7u7SHnpTf7PWcrhGA"
}
]
}
{
"_id" : "xcHZ25qwvyDpDJtAZ",
"orderNumber" : 1234,
"createdAt" : "11/01/2016, 14:44:47",
"productsInOrder" : [
{
"category" : "frozen",
"item" : 31104,
"desc" : "Handcrafted Rubber Keyboard",
"quantity" : "11",
"price" : "4.78",
"lineprice" : "52.58",
"_id" : "LMMwbKFEgnCbgCt9c"
},
{
"category" : "frozen",
"item" : 77832,
"desc" : "Practical Rubber Shirt",
"quantity" : "21",
"price" : "0.62",
"lineprice" : "13.02",
"_id" : "63otkkXWGrTJkwEgX"
},
{
"category" : "frozen",
"item" : 66940,
"desc" : "Gorgeous Granite Bike",
"quantity" : "111",
"price" : "4.21",
"lineprice" : "467.31",
"_id" : "rbPSujey8CFeMPjza"
}
]
}
JS
So far I have tried:
Orders.find({ 'productsInOrder': ['ambient']}).count();
Orders.find({ productsInOrder: { category: 'ambient' }}).count();
Orders.find({ productsInOrder: { $all: [ 'frozen' ] }}).count();
I am having a hard time understanding Mongo queries when the data is nested in this manner. Please can you help point me in the right direction? Many thanks in advance.
* SOLUTION *
I have accomplished the desired result thanks to the contributions below. To make this work I created a method on the server as the query cannot be run on the client using an existing collection. This is done as follows:
Meteor.methods({
'byCategory': function() {
var result = Orders.aggregate([
{ "$unwind": "$productsInOrder" },
{
"$group": {
"_id": null,
"ambient_count": {
"$sum": {
"$cond": [ { "$eq": [ "$productsInOrder.category", "ambient" ] }, 1, 0 ]
}
},
"frozen_count": {
"$sum": {
"$cond": [ { "$eq": [ "$productsInOrder.category", "frozen" ] }, 1, 0 ]
}
},
"other_category_count": {
"$sum": {
"$cond": [ { "$eq": [ "$productsInOrder.category", "other_category" ] }, 1, 0 ]
}
}
}
}
]);
return result;
}
})
and then on the client:
Meteor.call('byCategory', function( error, result ) {
if( error ) {
console.log( error.reason );
} else {
console.log( result[0].ambient_count );
console.log( result[0].frozen_count );
etc....
}
});
Thanks and credit to #chridam and #Brett.
An alternative approach is to use the aggregation framework. Consider the following aggregation pipeline which as the first stage of the aggregation pipeline, the $unwind operator denormalizes the productsInOrder array to output for each input document, n documents where n is the number of array elements. The next pipeline stage has the $group operator which groups all the documents into a single document and stores the counts for each category with the help of the $sum and $cond operators.
In Meteor, you can then use meteorhacks:aggregate package to implement the aggregation:
Add to your app with
meteor add meteorhacks:aggregate
Note, this only works on server side and there is no oberserving support or reactivity built in. Then simply use .aggregate function like below.
var coll = new Mongo.Collection('orders');
var pipeline = [
{ "$unwind": "$productsInOrder" },
{
"$group": {
"_id": null,
"ambient_count": {
"$sum": {
"$cond": [ { "$eq": [ "$productsInOrder.category", "ambient" ] }, 1, 0 ]
}
},
"frozen_count": {
"$sum": {
"$cond": [ { "$eq": [ "$productsInOrder.category", "frozen" ] }, 1, 0 ]
}
},
"other_category_count": {
"$sum": {
"$cond": [ { "$eq": [ "$productsInOrder.category", "other_category" ] }, 1, 0 ]
}
}
}
}
];
var result = coll.aggregate(pipeline);
Running the same pipeline in mongo shell using the sample data will yield:
{
"result" : [
{
"_id" : null,
"ambient_count" : 1,
"frozen_count" : 7,
"other_category_count" : 0
}
],
"ok" : 1
}
You can access the native mongo collection and publish the aggregation results to the orders collection on the client side:
Meteor.publish('categoryCounts', function() {
var self = this,
db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
orders = db.collection("orders").aggregate(pipeline, // Need to wrap the callback so it gets called in a Fiber.
Meteor.bindEnvironment(
function(err, result) {
// Add each of the results to the subscription.
_.each(result, function(e) {
self.added("orders", e._id, e);
});
self.ready();
},
function(error) {
Meteor._debug( "Error doing aggregation: " + error);
}
)
);
});
If you don't want to do this within Meteor, you will need to use mongo aggregation. Minimongo doesn't include aggregation though, so you will need this package to accomplish it:
https://docs.mongodb.org/manual/core/aggregation-introduction/
I only tested this in mongo itself, so you will have to adapt it to the way that the aggregation package does it:
db.orders.aggregate([
{
$unwind: "$productsInOrder"
},
{
$match: {
"productsInOrder.category": "frozen"
}
},
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
]);
The first part is unwinding the collection. It will basically make an "order" entry for every instance of $productsInOrder. Once you have the array flattened out, we match on the category you care about; in this case, the "frozen" category. Next we group it up so we can count the number of documents returned. $group is simply constructing the final object that will be output from the query. You can modify this to be whatever you want, or you could group by productsInOrder.category and not even $match on "frozen".
This question already has an answer here:
Updating template with session variable and subscription/publication only updates with previous query and appends information
(1 answer)
Closed 7 years ago.
Description of the problem:
I have two collections videos and specs. videos collection has a key called spec which corresponds to a specs id. Both collections are not empty.
My Template Helper:
Template.List.helpers({
videos: function(){
var vids = Videos.find({ online: false}).fetch();
return vids.map(function(value){
console.log("id: " + value.spec);
console.log(Specs.find({ id: value.spec }).fetch());
//Issue here
value.specName = Specs.find({ id: value.spec }).fetch()[0].name;
return value;
});
}
});
As you can see in my template helper I loop through the video array and add specName to the array.
The main problem comes from this specific line:
value.specName = Specs.find({ id: value.spec }).fetch()[0].name;
and more specifically this line: Specs.find({ id: value.spec }).fetch()
which will return null every time.
What I've tried:
Naturally, my first thought would be to check what value.spec returns. And it returns an int between 1 and 15 (included) which is right. If value.spec is right, then how come the find() doesn't return anything?
I then decided to hard set it and tried this:
Specs.find({ id: 2}).fetch()
And this worked. Which is weird because on multiple occasions value.spec returns 2...
I've also tried intParse(value.spec) just in case, however that didn't work either.
Question
Why does Specs.find({ id: value.spec }).fetch() return null knowing that value.spec is set correctly and that a hard coded number works?
Requested json data: (from meteor mongo)
specs:
{ "_id" : "XKXHtQuiFsAew3dDy", "id" : 1, "name" : "Endocrine surgery" }
{ "_id" : "68jFidAMXTXpQtQye", "id" : 2, "name" : "General and digestive" }
{ "_id" : "GZSXToRXMfJgnH3CY", "id" : 3, "name" : "Pediatric surgery" }
{ "_id" : "T2mBz2gsXEqQaybmq", "id" : 4, "name" : "Thoracic surgery" }
{ "_id" : "hnuQzZiPKvYYDZhc8", "id" : 5, "name" : "Equipment" }
{ "_id" : "byE3A6HchvfhKdmR8", "id" : 6, "name" : "Gynecology" }
{ "_id" : "u5rrPB7asGW3NC6B2", "id" : 7, "name" : "Urology" }
{ "_id" : "umxKvR66oEx5dRppf", "id" : 8, "name" : "Cardiovascular surgery" }
{ "_id" : "bPcBTZn3t5ubRRcrQ", "id" : 9, "name" : "Endoscopic surgery" }
{ "_id" : "yNyAqQPoreNtdRZ34", "id" : 10, "name" : "NOTES" }
{ "_id" : "KG794eakRaztEqehG", "id" : 11, "name" : "Robotic surgery" }
{ "_id" : "QBrtvTg4GT7Tf7cAJ", "id" : 12, "name" : "Skull base surgery" }
{ "_id" : "HEhq6oBjuuMnrxE5a", "id" : 13, "name" : "Arthroscopy and upper limb surgery" }
{ "_id" : "xwpgHqZpBQP7WAnd5", "id" : 14, "name" : "Single port surgery" }
{ "_id" : "K4BgFupwNdDGD3449", "id" : 15, "name" : "Telemicrosurgery" }
videos:
{ "_id" : "L5Qi7YRRhn6Sfcjk8", "id" : "vd01en1065e", "title" : "Right inguinal hernia: open plug technique", "authors" : [ "E Pelissier" ], "date_published" : "2004-09-27", "abstract" : "", "tags" : [ "" ], "spec" : 2, "private" : true, "online" : false }
{ "_id" : "M8cuLW6KNCqKeP9vF", "id" : "vd01en1074e", "title" : "Laparoscopic splenectomy, posterior approach", "authors" : [ "D Mutter", " F Rubino" ], "date_published" : "2004-09-27", "abstract" : "", "tags" : [ "" ], "spec" : 2, "private" : true, "online" : false }
{ "_id" : "Ptzrxw8GifeMvQk9k", "id" : "vd01en1090e", "title" : "Intussusception of the intestine in the newborn", "authors" : [ "F Becmeur", " D Christmann", " I Kauffmann" ], "date_published" : "2004-09-27", "abstract" : "", "tags" : [ "" ], "spec" : 3, "private" : true, "online" : false }
{ "_id" : "oHWcX3vCBHuZQM9hR", "id" : "vd01en1103e_2", "title" : "Appendicular peritonitis: laparoscopic conversion", "authors" : [ "B Navez" ], "date_published" : "2001-11-05", "abstract" : "", "tags" : [ "" ], "spec" : 2, "private" : true, "online" : false }
{ "_id" : "6uzmxYxhd5DDuS2gG", "id" : "vd01en1108e", "title" : "Diaphragmatic hernias", "authors" : [ "F Becmeur" ], "date_published" : "2001-11-28", "abstract" : "", "tags" : [ "" ], "spec" : 3, "private" : true, "online" : false }
{ "_id" : "yHqruiQYeeQ9SDHpH", "id" : "vd01en1112e", "title" : "Laparoscopic excision of the cystic stump", "authors" : [ "J Leroy" ], "date_published" : "2004-09-27", "abstract" : "", "tags" : [ "" ], "spec" : 2, "private" : true, "online" : false}
{ "_id" : "fmjtk5WAEKitMxyGj", "id" : "vd01en1114e", "title" : "Laparoscopic gastric banding in a patient with a BMI of 40", "authors" : [ "JM Zimmermann", " D Fölscher" ], "date_published" : "2004-09-27", "abstract" : "", "tags" : [ "" ], "spec" : 2, "private" : true, "online" : false}
I've been stuck on this problem for a couple hours, I didn't want to post this on SO since I do believe it's a simple problem. However, it is mind boggling.
The problem here is the publication/subscription issue. You did not mention it, how do you handle publications and subscriptions but that is most likely the issue. What is happening is that when you browse your videos collection, which subscription is ready (since you hae any data in it) here: Videos.find({ online: false}) that not necessarly means that (in that precise moment) subscription handling Spec collection is ready as well. So even if on the server the query is working, on the client it's null, because the data are not synced between client and server YET. so you have to wait until both subscriptions are ready somehow. You can use template subscriptions, or waitOn function in your router.
First of all check on server side that your publication and subscription is ready and returning you back data check this like as
Meteor.publish('Videso', function () {
var result= Videso.find({});
console.log(result);
return result
});
if console.log returning you records on server side i have a solution then your
problem
the solution of your problem is that
Template.List.helpers({
videos: function(){
var vids = Videos.find({ online: false}).map(function (value) {
console.log("id: " + value.spec);
var idValue = Specs.findOne({ "id": value.spec })
console.log("idValue===",idValue)
return _.extend(value, idValue);
});
console.log(vids); //here merged data of both table having
// relvent record will returned
return vids;
}
});
Just implement it and check data on your template it will be ready for you and vote me then :)
How I can convert an array to an object in MongoDB?
For example, I want to convert this document:
{
"_id" : NumberLong(279),
"userAddressList" : [
{
"street" : "Street",
"house" : "House",
"building" : "Building",
"flat" : NumberLong(0),
"entrance" : NumberLong(0),
"floor" : NumberLong(0),
"intercom" : "Intercome"
}
],
}
to this:
{
"_id" : NumberLong(279),
"userAddressList" :
{
"street" : "Street",
"house" : "House",
"building" : "Building",
"flat" : NumberLong(0),
"entrance" : NumberLong(0),
"floor" : NumberLong(0),
"intercom" : "Intercome"
},
}
So I need to convert ""userAddressList" : [{..}]" to the ""userAddressList" : {..}".
For MongoDB 4.2 and newer
You could try the following query which uses the aggregation pipeline in the update:
db.collection.updateMany(
{},
[
{ '$addFields': {
'userAddressList': {
'$arrayElemAt': ['$userAddressList', 0]
}
} }
]
)
For older MongoDB versions:
db.collection.find().forEach(function(doc){
userAddressList = doc.userAddressList[0];
doc.userAddressList = userAddressList;
db.collection.save(doc);
})
or use the aggregation framework where you run the following pipeline
db.collection.aggregate([
{ "$addFields": {
"userAddressList": {
"$arrayElemAt": ["$userAddressList", 0]
}
} },
{ "$out": "collection" }
])
Note that this does not update your collection but replaces the existing one and does not change any indexes that existed on the previous collection. If the aggregation fails, the $out operation makes no changes to the pre-existing collection.
I am using aggregate to paginate data from subdocuments. The subdocuments don't have a strict schema so they can be different for each document, this is making it difficult for me to structure my output as I don't know the field names for the subdocuments.
What I am using
Mongo 3.0.0
Node 0.10.33
Mongoose 3.9.7
My models
var BodySchema = new Schema({random: String},{strict:false});
var FeedSchema = new Schema({
name: String,
body:[BodySchema]
});
My data looks like this
[{
_id:"...",
name:"Power Rangers feed",
body:[
{
"_id":"...",
"name" : "Jason",
"colour" : "Red",
"animal" : "T-rex"
},
{
"_id":"...",
"name" : "Billy",
"colour" : "Blue",
"animal" : "Triceratops"
},
{
"_id":"...",
"name" : "Zach",
"colour" : "Black",
"animal" : "Mastadon"
}
]
},
{
_id:"...",
name:"Transformers feed",
body:[
{
"_id":"...",
"name" : "Optimus Prime",
"team" : "Autobots",
"class" : "leader"
"alt-mode" : "truck"
},
{
"_id":"...",
"name" : "Bumblebee",
"team" : "Autobots",
"class" : "scout"
"alt-mode" : "VW Beetle"
},
{
"_id":"...",
"name" : "Blaster",
"team" : "Autobots",
"class" : "Commmunicator"
"alt-mode" : "Sterio"
},
{
"_id":"...",
"name" : "Hotrod",
"team" : "Autobots",
"class" : "Warrior"
"alt-mode" : "Car"
}
]
}]
My current aggregate code looks like this
feed.aggregate([
{'$match':{_id:id('550234d3d06039d507d238d8')}},
{'$unwind':'$body'},
{'$skip':2},
{'$limit':2},
{
'$project': {
'_id':"$body._id",
'body': "$body"
}
},
], function(err, result){
if(err){return(res.send(500, err))}
res.send(result);
});
My current result looks like this
[{
_id:"...",
body:{
"_id":"...",
"name" : "Blaster",
"team" : "Autobots",
"class" : "Commmunicator"
"alt-mode" : "Sterio"
}
},
{
_id:"...",
body:{
"_id":"...",
"name" : "Hotrod",
"team" : "Autobots",
"class" : "Warrior"
"alt-mode" : "Car"
}
}]
My desired result looks like this
[
{
"_id":"...",
"name" : "Blaster",
"team" : "Autobots",
"class" : "Commmunicator"
"alt-mode" : "Sterio"
},
{
"_id":"...",
"name" : "Hotrod",
"team" : "Autobots",
"class" : "Warrior"
"alt-mode" : "Car"
}
]
My Question
How can I achieve my desired result structure.
So, you are going to hate this, but this is just how the $project and the $group stages in the aggregation pipeline work. You need to specify all of the fields that you want in the output "explicitly". Therefore:
feed.aggregate([
{'$match':{_id:id('550234d3d06039d507d238d8')}},
{'$unwind':'$body'},
{'$skip':2},
{'$limit':2},
{
'$project': {
'_id':"$body._id",
'name': '$body.name',
'team': '$body.team',
'class': '$body.alt-mode'
}
},
], function(err, result){
That really is the only way to do it.
There really is a strong reasoning behind that though, and most of the principles are backed up in SQL SELECT, with the exception of the * "wildcard" where applicable.
The general premise is that this is analogous to unix pipe in general operation so if you think like:
grep | awk | sed
Then the operations seem more logical in structure.