Mongoose/MongoDB: Get second to last document - node.js

How would I be able to get the 2nd to latest or previous day's document collection using Mongoose?
my code to get the latest data goes as follows:
data.findOne()
.sort({"_id": -1})
.exec(function(err, data) {
if (err) {
console.log('Error getting data..');
}
if (data) {
res.json(data);
}
else {
console.log('No data found!');
}
});
This only returns the LATEST document in the collection. I instead need the one prior to the latest one, so one from a day before this one, how would I be able to do this?

Then you'd have to use aggregation:
db.getCollection('data').aggregate([
{ $sort: {"date": -1}}, // sort by latest date (descending)
{ $limit: 2}, // limit to the first two results
{ $sort: {"date": 1}}, // sort these two ascending
{ $limit: 1} // get first document
])
This pipeline is translated in mongoose like that (I think):
data.aggregate([
{ $sort: {"date": -1}},
{ $limit: 2},
{ $sort: {"date": 1}},
{ $limit: 1}
], function (err, result) {
if (err) {
next(err);
} else {
res.json(result);
}
});
Also by sorting without adding a $match pipeline, your first $sort would sort all the documents in your collection. So if your collection is big you might want to restrict them with some query parameters that you can add to the $match pipeline. You could add the match before the first $sort
E.g.
db.getCollection('data').aggregate([
{ $match: {...}},
{ $sort: {"date": -1}},
{ $limit: 2},
{ $sort: {"date": 1}},
{ $limit: 1}
])

To get the 2nd before the last
data.find().sort({ _id: -1 }).limit(1).skip(2).exec(function(err, data) {
if (err) {
console.log('Error getting data..');
}
if (data) {
res.json(data);
}
else {
console.log('No data found!');
}
});

you could do like this:
data
.find()
.limit(2)
.sort({ "_id": -1 })
.exec(function(err, data) {
if (err) return res.json(err);
return res.json(data);
});
this will find on all documents sorted by id: -1, and then limit the result to two, this should give you the result that you want.

This is the fix I used to make it work for what I wanted, hope it helps others:
I decided to import MomentJS, from there it displays the current date, subtracting 1 from it, to display the previous date's value, hence giving me the proper collection from the day before.
Code:
const moment = require('moment');
...
data.find({"updated": moment().subtract(1, 'days').format('L')}) ...

Related

Mongoose aggregate giving no response

I am trying to mongoose aggregate for grouping businesses. I have working MongoDB script. I am attaching the NodeJS with Mongoose script as well as the MongoDB code below:
History.aggregate()
.cursor({ batchSize: 1000 })
.group({
_id: "$businessName",
transactions: {
$push: "$$ROOT"
},
numberOfTransactions: {
$sum: 1
}
})
.exec(function (err, transactions) {
if (err) {
res.json({
"message": err.message
});
} else if (transactions) {
// winston.info(transactions);
res.send(
transactions
);
}
});
Working MongoDb code:
db.getCollection('transactionhistories')
.aggregate([
{$group:
{_id:"$businessName", transactions:{$push:"$$ROOT"},numberOfTransactions: { $sum: 1 }},
},
{$sort:{businessId:1}}
]).toArray()
I am not receiving any response when using the first code. Can someone help? Thanks in advance!
The .cursor({ batchSize: 1000 }) method you are using would make the aggregate call return a cursor so the callback won't even be executed. Source.
You should use the cursor to access the result of the aggregation:
const cursor = Todo.aggregate()
.cursor({ batchSize: 1000 })
.group({
_id: "$name",
transactions: {
$push: "$$ROOT"
},
numberOfTransactions: {
$sum: 1
}
})
.exec();
cursor.eachAsync(function (doc) {
console.log('doc: ', doc);
});
More details on how to use cursors here.

Group data in mongoDB

I am trying to group the data that I get from mongoDB by repo Id
my collection structure is:
{
"id":"8820624457",
"type":"CreateEvent",
"actor":{
"id":27394937,
"login":"Johnson0608",
"display_login":"Johnson0608",
"gravatar_id":"",
"url":"https://api.github.com/users/Johnson0608",
"avatar_url":"https://avatars.githubusercontent.com/u/27394937?"
},
"repo":{
"id":163744671,
"name":"Johnson0608/test",
"url":"https://api.github.com/repos/Johnson0608/test"
},
"payload":{
"ref":"master",
"ref_type":"branch",
"master_branch":"master",
"description":null,
"pusher_type":"user"
},
"public":true,
"created_at":"2019-01-01T15:00:00Z"
}
my code is :
collection.find({}).project({ 'repo.id': 1, 'actor.login': 1, 'type': 1 }).toArray(function (err, docs) {
assert.equal(err, null);
console.log("Found the following records");
res.status(200).json({ docs });
callback(docs);
});
I am trying to group by repo id but i don't know how(I am new to mongoDB)
Go to this MongoPlayAround
db.collection.aggregate([
{
$group: {
_id: "$repo.id",
Actors: {
$push: {
"login": "$actor.login"
}
}
}
}
])
You can use the aggregation function.
Groups input documents by the specified _id expression and for each distinct grouping, outputs a document. The _id field of each output document contains the unique group by value.
collection
.find({})
.aggregate({
$group: {
_id : 'repo.id' // The thing you want to group by
// ... Other arguments
}
})
.project({'repo.id': 1, 'actor.login': 1, 'type': 1})
.toArray(function (err, docs) {
assert.equal(err, null);
console.log("Found the following records");
res.status(200).json({ docs });
callback(docs);
});

How can I sort then limit an array of results in mongoose?

I have a user collection with tons of properties for each user. One property is an array ('guestlist_submitted') and I am trying to return the most recent 3 items in that array according to the date field.
Using Mongoose, I am able to target that specific property on the user object, but I can't seem to limit or sort them.
I've tried using slice and and a date calculation to return the most recent 3 entires but I am struggling to get it right and not sure whats wrong.
I've tried using Sort/Limit on query builder but I've learned that these dont apply to the array inside the property I am targeting despite me saying " select('guestlist_submitted') as part ofthe query builder.
When I return the result after just targeting the property on the user object, it comes as an array with an object inside and another array inside that object. I am having problems traversing down it to target that inner array to use the slice and date sort.
First try
Users.
find({ fbid: req.query.fbid}).
select('guestlist_submitted -_id').
sort({"guestlist_submitted.date": 'desc'}).
limit(3).
exec((err, result) => {
if (err) throw (err));
if (result) {
res.send(result);
}
Second try:
Users.
find({ fbid: req.query.fbid}).
select('guestlist_submitted -_id').
exec((err, result) => {
if (err) res.send(JSON.stringify(err));
if (result) {
const slicedArr = result.guestlist_submitted.slice(0,1);
const sortedResultArr = slicedArr.sort(function(a,b){return new Date(b.date) - new Date(a.date); });
const newResultArr = sortedResultArr.slice(0, 2);
res.send(newResultArr);
}
The best I can get is 'guestlist_submitted' and the entire array, not sorted and its inside an object inside an array.
I expected the output to be the 3 most recent results from the guestlist_submitted array inside a new array that I can display on the client.
Thank you in advance for your help
EDIT - FIGURED OUT A SOLUTION
I ended up figuring out how to get what I wanted using aggregate()
Users.aggregate([
{
$match: {fbid: req.query.fbid}
},
{
$unwind: "$guestlist_submitted"
},
{
$sort: {'guestlist_submitted.date': -1}
},
{
$limit: 2
},
{
$group: {_id: '$_id', guestlist_submitted: {$push: '$guestlist_submitted'}}
},
{
$project: { guestlist_submitted: 1, _id: 0 }
}
]).exec((err, result) => {
if (err) res.send(JSON.stringify(err));
if (result) {
console.log(result);
res.send(result);
}
});
I ended up figuring out how to get what I wanted using aggregate()
Users.aggregate([
{
$match: {fbid: req.query.fbid}
},
{
$unwind: "$guestlist_submitted"
},
{
$sort: {'guestlist_submitted.date': -1}
},
{
$limit: 2
},
{
$group: {_id: '$_id', guestlist_submitted: {$push: '$guestlist_submitted'}}
},
{
$project: { guestlist_submitted: 1, _id: 0 }
}
]).exec((err, result) => {
if (err) res.send(JSON.stringify(err));
if (result) {
console.log(result);
res.send(result);
}
});

Full text search in MongoDB and NodeJS with text score sorting in distinct values

I'm trying to implement a full text search with mongodb 3.4, nodejs and socket.io, with distinct and sorting. So far so good, i have this code that works fine but without the sorting part:
socket.on('searchProductName', function (data) {
MongoClient.connect(config.database.url, function (err, db) {
db.collection(config.database.collection.products).distinct('productName',
{
$text: {$search: data}}, {score: {$meta: "textScore"}
},
function (err, doc) {
socket.emit('searchProductNameResults', doc);
db.close();
});
});
});
I'm trying to find a way to use this based on textScore sorting method, but for distinct values:
db.collection.find(
<query>,
{ score: { $meta: "textScore" } }
).sort( { score: { $meta: "textScore" } } )
Any ideas?
Thank you
Use the aggregate() function in the aggregation framework to take advantage of the text search within the $match, $sort and $group pipeline operators that will help you achieve the
desired result.
Take for instance the following pipeline which uses the $match operator as the initial step and includes the $text operation. The score can be part of a $sort pipeline specification and the preceding pipeline, $group creates the distinct values sorted by the scores, using the $addToSet operator:
socket.on('searchProductName', function (data) {
MongoClient.connect(config.database.url, function (err, db) {
var pipeline = [
{
"$match": {
"$text": {
"$search": data
}
}
},
{ "$sort": { "score": { "$meta": "textScore" } } },
{
"$group": {
"_id": null,
"products": { "$addToSet": "$productName" }
}
}
];
db.collection(config.database.collection.products)
.aggregate(pipeline, function (err, docs) {
socket.emit('searchProductNameResults', docs[0].products);
db.close();
}
);
});
});

Mongoose how to do multiple updates

I'm having a tough time coming up with a solution to update multiple documents with different values.
I have an app that makes sales for every sold item I want to reduce the quantity in my database by one.
var soldItems =
[
{
"_id": "1"
"name": "Foo",
"price": 1.09
},
{
"_id": "2",
"name": "Bar",
"price": 2.00
}
]
var ids = [];
soldItems.forEach(function(item){
ids.push(item._id);
});
I'm collecting all the ids in my soldItems array of objects.
Now I want to know how many items quantity I have in the database and then reduce the number quantity by one.
ModelItem.find({_id: {$in: ids}}, function(err, docs){
if(err) throw err;
docs.forEach(function(doc){
soldItems.forEach(function(item){
if(doc._id === item._id){
doc.quantity += -1;
}
});
});
Item.update({_id: {$in: ids}}, {$set: docs }, function(err){
if(err) throw err;
});
});
Obviously this is wrong because $set is passing in array instead of an object.
I want to know how can I reduce the quantity by one for each item in my database, but I the same time I don't want to go below 0 items in the database.
I'm sure im looking at this from the wrong angle.
Thanks.
Use the $inc operator instead, and the multi options:
Item.update({_id: {$in: ids}, quantity: {$gt: 0}}, // selection
{$inc: {quantity: -1}}, // modifications
{multi: true}); //options

Resources