Remove _Id from mongoose Aggregate response - node.js

I'm trying to remove the _Id from the returned documents, this is my code:
module.exports = function(app) {
// Module dependencies.
var mongoose = require('mongoose'),
Contacts = mongoose.models.Contacts,
api = {},
limit = 10;
api.contacts = function(req, res) {
Contacts.aggregate([{
$group: {
"_id": {
name: "$name",
city: "$city",
state: "$state"
}
}
}, {
$sort: {
AgencyTranslation: 1
}
}, {
$limit: req.query.limit | limit
}],
function(err, contacts) {
if (err) {
res.json(500, err);
} else {
res.json({
contacts: contacts
})
}
})
};
app.get('/api/contacts', api.contacts);
};
the current result-set looks like this:
{
"contacts":[
{"_id":{"name":"Joe","city":"ankorage","state":"AL"}},
{"_id":{"name":"Mark","city":"washington","state":"DC"}}
...
]
}
I tried to replace "_Id" with "$project", or $project, and adding "_Id": 0 to the object, as some have suggested elsewhere, but was not successful.
I also tried res.send(contacts), but that only stripped the super-object ('contacts').
Any suggestions are appreciated.

Like this
Contacts.aggregate( [
{ $group: { "_id": { name: "$name", city: "$city", state: "$state" } } },
{ $project: {_id: 0, name: '$_id.name', city: '$_id.city', state: '$_id.state'} },
{ $sort: { AgencyTranslation: 1 } },
{ $limit: req.query.limit | limit }
], function () {
});

Bunch of time but, here is the answer:
After making $group or $project, do this:
{ $unset: ["_id"] }

Related

mongoose divide two fields in put request

Can I update a field of a document with a division of two fields? Using Node and MongoDB, I'm trying to create a rating function, and I have to make a division, but nothing seems to work. I want the new value of rating to be, the current one divided by the number of votes.
router.put("/:id/:rating", async (req, res) => {
const movie_rating = parseInt(req.params.rating);
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id,
{
$inc: { noVotes: 1 },
$inc: { rating: movie_rating },
$divide: { rating: [rating, noVotes] },
// rating: { $divide: [rating, noVotes] }
},
{ new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
});
You need to change few things
Sample
db.collection.update({},
[
{
"$set": {
"key2": {
$add: [
"$key2",
1
]
},
key3: {
"$divide": [
{
$add: [
"$key2",
1
]
},
"$key"
]
},
}
}
],
{
"multi": true,
"upsert": false
})
You need aggregate update as you need divide
You cannot use the updated value in the same operation
You cannot combine $inc, $set in aggregate update
Alternatively, you can use $add instead $inc
you can reperform the operation for the divide operation than making another update call
This can be done with $set,
It will look like this:
router.put("/:id/:rating", async (req, res) => {
const movie_rating = parseInt(req.params.rating);
try {
const updatedMovie = await Movie.findByIdAndUpdate(
req.params.id,
[
{
$set: {
noVotes: { $sum: ["$noVotes", 1] },
rating: { $sum: ["$rating", movie_rating] },
averageRating: { $divide: ["$rating", "$noVotes"] },
},
},
],
{ new: true }
);
res.status(200).json(updatedMovie);
} catch (err) {
res.status(500).json(err);
}
});

Remove one field from mongoDB collection

I have a mongoDB collection which I use with a mongoose Schema :
const balanceSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId, ref: 'user'
},
incomes: { Number },
fees: { Number },
},
{ strict: false })
I use the strict mode to false, so I can push any 'key' I want with its value.
I would like to delete just one of the "incomes" category, but I can't specify the line because there is no 'defined key'.
Here is an exemple of the data inside :
{
"_id": {
"$oid": "60c763df3d260204865d2069"
},
"incomes": {
"income1": 1300,
"anyKeyNameIWant": 400
},
"fees": {
"charge1": 29,
"charge2": 29,
"chargetest": 29,
"charge7": 29
},
"__v": 0,
}
I tried this, but no success :
module.exports.deleteOneBalance = (req, res) => {
let data = req.body
if (!ObjectID.isValid(req.params.id))
return res.status(400).send('ID unknown : ' + req.params.id);
BalanceModel.update(
{ _id: req.params.id },
{
$unset: { "incomes.salairetest": "400" }
}), (err, docs) => {
if (!err) res.send('Deleted. ' + data)
else console.log('Error : ' + err)
}
}
Any idea ?
There are several ways to delete fields with dynamic field names.
One solution is this one:
var unset = {};
unset["incomes." + "anyKeyNameIWant"] = null;
db.balanceModel.updateOne({ _id: req.params.id }, { $unset: unset })
Or you can use an aggregation pipelinie like this:
db.balanceModel.updateOne(
{ _id: req.params.id },
[
{ $set: { incomes: { $objectToArray: "$incomes" } } },
{ $set: { incomes: { $filter: { input: "$incomes", cond: { $ne: ["$$this.k", "anyKeyNameIWant"] } } } } },
{ $set: { incomes: { $arrayToObject: "$incomes" } } }
]
)
If you want to remove/unset specific value/(s) from the documents then you have to provide the complete path of that key.
Let's take an example if you want to remove anyKeyNameIWant then your path will be incomes.anyKeyNameIWant and the update query will be like this
db.sample.update(
{
_id: ObjectId("60c763df3d260204865d2069")},
{
$unset: {"incomes.anyKeyNameIWant":""}
})
In your code, you are passing an object having the key incomes in $unset which will remove the complete incomes key from the document
Here is the link to the official document in case you want more details $unset

Why am I getting a AggregationCursor as a result and not an average?

I'm querying my MongoDB database and don't understand why I am getting an aggregator cursor as a result when I expect to be returned a single number. Maybe I need to get something from the cursor object? Just can't figure out what.
module.exports = CalculateAvg = async collection => {
try {
// const count = await collection.countDocuments({ word: "Hello" });
// console.log(count) // logs 140, which shows that it is accessing the db correctly
const cursor = await collection.aggregate([
{ $match: { word: "Hello" } },
{
$group: {
_id: null,
mean: {
$avg: "$value" // in the dataset, each doc has a value field which equals a number
}
}
}
]);
console.log(cursor) // logs a large AggregationCursor object, rather than a number
} catch (err) {
console.log(err);
}
};
It's because aggregate return value is aggregateCursor, I recommend checking the Mongo's Nodejs driver types file whenever you're not sure whats the return value or the parameter value for any of these functions is.
You want to use cursor toArray like so:
const cursor = await collection.aggregate([
{ $match: { word: "Hello" } },
{
$group: {
_id: null,
mean: {
$avg: "$value" // in the dataset, each doc has a value field which equals a number
}
}
}
]).toArray();
You should use next() method... For Example
const pipeline = [{
$facet: {
total: [{
$count: 'createdAt'
}],
data: [{
$addFields: {
_id: '$_id'
}
}],
},
},
{
$unwind: '$total'
},
{
$project: {
data: {
$slice: ['$data', skip, {$ifNull: [limit,'$total.createdAt']} ]
},
meta: {
total: '$total.createdAt',
limit: {
$literal: limit
},
page: {
$literal: ((skip/limit) + 1)
},
pages: {
$ceil: {
$divide: ['$total.createdAt', limit]
}
}
}
}
}];
const document = await collection.aggregate(pipeline);
const yourData = await document.next();

MongoDB, Node and Express return all collections that don't exist in a separate collection that has a large amount of data

I've been pulling my hair out for weeks over this one.
I have a collection (this is a cut down version):
const SubscriberSchema = new Schema({
publication: { type: Schema.Types.ObjectId, ref: "publicationcollection" },
buyer: { type: Schema.Types.ObjectId, ref: "buyercollection" },
postCode: { type: String },
modifiedBy: { type: String },
modified: { type: Date }
});
I also have a collection containing the 1.75 million UK Postcodes
const PostcodeSchema = new Schema({
postcode: { type: String }
});
What I want to do is to return any record in the Subscriber collection which doesn't exist within the Postcode collection.
When I try a very simple aggregation using Mongoose on anything >100 records in the Subscriber collection, I'm getting either a timeout or a >16MB return error.
Here's what I've tried so far:
router.get(
"/badpostcodes/:id",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const errors = {};
Subscriber.aggregate([
{
$match: {
publication: mongoose.Types.ObjectId(req.params.id),
postCode: { "$ne": null, $exists: true }
}
},
{
$lookup: {
'from': 'postcodescollections',
'localField': 'postCode',
'foreignField': 'postcode',
'as': 'founditem'
}
},
// {
// $unwind: '$founditem'
// },
{
$match: {
'founditem': { $eq: [] }
}
}
], function (err, result) {
if (err) {
console.log(err);
} else {
if (result.length > 0) {
res.json(result);
} else {
res.json("0");
}
}
})
}
);
The unwind didn't seem to do anything but it's commented out to show I tried to use it.
I've also tried using a pipeline on the lookup instead but that didn't work, similar to the following (sorry, I don't have my original code attempt so this is from memory only):
$lookup: {
'from': 'postcodescollections',
'let': { 'postcode': "$postCode" },
'pipeline': [
{
'$match': {
'postcode': { $exists: false }
}
},
{
'$unwind': "$postCode"
}
],
'as': 'founditem'
}
Thanks in advance so I can hopefully retain some hair!
You are doing a match on all postcodes that don't match and then unwinding those - that will be a 1.75m documents for each subscriber! The syntax in $lookup is also incorrect I think.
I think you can try something like the following - adjust accordingly for your data:
Do a $lookup to find a matching postcode in postcodes, then do a match to filter those subscribers that that don't have any founditem elements: "founditem.0": {$exists: false}
See an example:
db.getCollection("subscribers").aggregate(
[
// Stage 1
{
$match: {
postCode: { "$ne": null, $exists: true }
}
},
// Stage 2
{
$project: {
_id: 1,
postCode: 1
}
},
// Stage 3
{
$lookup: {
from: "postcodescollections",
let: { p: "$postCode" },
pipeline: [
{
$match: {
$expr:
{
$eq: ["$$p","$postcode"] }
}
},
{ $project: { _id: 1 } }
],
as: "founditem"
}
},
// Stage 4
{
$match: {
"founditem.0": {$exists: false}
}
},
]
);

Query with variables not working using nodejs ,express, mongojs and mongodb

My application needs to get the result of this query (this is already giving the correct result in mongoDb):
var denominator = db.getCollection('Transactions').aggregate({
$group: {
"_id": null,
"Alltotal": {$sum:"$transAmount"}
}
};
db.getCollection('Transactions').aggregate([{
$group: {
_id: '$merchantCode',
total: {
$sum: { $multiply: ['$transAmount', 100 ]}
}}},
{ $project: {
percentage: {
$divide: [
"$total",
denominator.toArray()[0].Alltotal
]
}
}
}
])
Now here is how I am trying to execute it:
var express = require('express');
var app = express();
var mongojs = require('mongojs');
var dbTransaction = mongojs('dataAnalysisDb',['Transactions']);
app.get('/Transactions',function(req,res){
var denominator = dbTransaction.Transactions.aggregate([{
$group: {
_id: 'null',
total: { $sum: '$transAmount' }
}
}]);
dbTransaction.Transactions.aggregate([{
$group: {
_id: '$mtype',
total: {
$sum: { $multiply: ['$transAmount', 100 ]}
}}},
{ $project: {
percentage: {
$divide: [
"$total",
denominator.toArray()[0].total
]
}
}
},
{ $sort : { _id : 1, posts: 1 } }
], function(err,docs){
console.log(docs); //this is for testing
res.json(docs);
});
});
I think this is not working because I am not sending the variable in the correct way to the server and when I use it on the operation it is not defined. I will appreciate any suggestion on how to fix it.
Thank you
I found a way to solve it and it might not be the best but it worked, I leave it here for whoever else needs it
var denominator = dbTransaction.Transactions.aggregate([{
$group: {
"_id": null,
"Alltotal": {$sum:"$transAmount"}
}}]).toArray(function(err,items){
console.log(items[0].Alltotal); //this is for testiong
dbTransaction.Transactions.aggregate([{
$group: {
_id: '$mtype',
total: {
$sum: { $multiply: ['$transAmount', 100 ]}
}}},
{ $project: {
percentage: {
$divide: [
"$total",
items[0].Alltotal
]
}
}
},
{ $sort : { _id : 1, posts: 1 } }
],function(err,docs){
console.log(docs); //this is for testing
res.json(docs);
});
});

Resources