Return documents matching field array - javascript

Using Mongoose with MongoDB, I need to return a series of lookups based on a field of an initial lookup. My Schema is as follows:
var PartSchema = new Schema({
partcode: String,
children: [String]
});
And the data looks like the following:
[{"partcode":"A1","children":["B1","B2","B3","B4"]},
{"partcode":"B1","children":["C11","C21","C31","C41"]},
{"partcode":"B2","children":["C12","C22","C32","C42"]},
{"partcode":"B3","children":["C13","C23","C33","C43"]},
{"partcode":"B4","children":["C14","C24","C34","C44"]}]
I can query for A1's children field by using the following static call:
var childrenOnly =
{children: 1, _id: 0};
PartSchema.static('getChildren', function (partcode, callback) {
return this.find({ partcode: partcode }, childrenOnly, callback);
});
This returns (via express)
[{"children":["B1","B2","B3","B4"]}]
What I need it to return is
[{"partcode":"B1","children":["C11","C21","C31","C41"]},
{"partcode":"B2","children":["C12","C22","C32","C42"]},
{"partcode":"B3","children":["C13","C23","C33","C43"]},
{"partcode":"B4","children":["C14","C24","C34","C44"]}]
I'm guessing an iterative call is required with the first query returning the children array, and then iterating over the children array to get each of the child records.

The general idea is to first try getting the array with the children codes using the findOne() method and then use that array as the query with the $in operator in the find() method to return the full result.
var childrenOnly = {children: 1, _id: 0};
PartSchema.static('getChildren', function (partcode, callback) {
var self = this;
this.findOne({ partcode: partcode }, childrenOnly)
.exec(function (err, doc) {
console.log(doc.children); // {"children":["B1","B2","B3","B4"]}
return self.find({"partcode": {"$in": doc.children} }, callback);
});
});
-- EDIT --
Look into using promises. I haven't tested this yet but I believe it should also do the trick:
var childrenOnly = {children: 1, _id: 0};
PartSchema.static('getChildren', function (partcode, callback) {
var self = this,
promise = this.findOne({ partcode: partcode }, childrenOnly).exec();
promise.then(function (doc) {
return self.find({"partcode": {"$in": doc.children} }, callback);
});
});

Related

mongoose check if id exists but that id is nested inside an array

When i fetch new alerts, i want to check if the ID of the new alert was already recorded. The issue is that that ID is nested inside an array. There's the alertsDetails array, which contains objects and those objects have an _ID filed which is what i want to check. I am not sure how to achieve that. I got the code below but then i have to iterate over the result to check the exists value. Im sure there must be a better way.
const mongoose = require('mongoose');
const { Schema } = mongoose;
const G2AlertsSchema = new Schema(
{
status: { type: String, required: true },
openDate: { type: Date, required: true },
alertType: { type: Array, required: true },
severity: { type: Array, required: true },
locationName: { type: Array, required: true },
history: { type: Array, required: true },
alertDetails: { type: Array, required: false },
assignedTo: { type: Schema.Types.ObjectId, ref: 'user' },
},
{
timestamps: true,
},
);
const G2Alerts = mongoose.model('G2Alert', G2AlertsSchema);
module.exports = G2Alerts;
This is the code i found on mongodb's website. I just want to see if the ID exists only. Basically when i fetch the new alerts i get an array and i iterate over it, i want to check each item's ID against what's inside the Database. If it's there, skip and go to the next. If it's new, then create a new alert and save it.
const exists = await G2Alerts.aggregate([
{
$project: {
exists: {
$in: ['5f0b4f508bda3805754ab343', '$alertDetails._id'],
},
},
},
]);
EDIT: Another thing. I am getting a eslint warning saying i should use array iteration instead of a for loop. The issue is, i need to use await when looking up the Alert ID. If i use, reduce or filter, i can't use await. If i use async inside the reduce or filter function, then it will return promises in or just an empty array.
This below works, based on the answer provided by Tom Slabbaert
const newAlertsData = [];
for (let item of alertData.data.items) {
const exists = await G2Alerts.find({ 'alertDetails._id': `${item._id}` });
if (exists.length === 0) {
newAlertsData.push(item);
}
}
if (newAlertsData.length !== 0) {......
But this does not
const filteredAlerts = alertData.data.items.reduce((filtered, item) => {
const exists = await G2Alerts.find({ 'alertDetails._id': `${item._id}` });
if (exists.length === 0) {
filtered.push(item);
}
return filtered;
}, []);
You're not far off, here is an example using the correct syntax:
const exists = await G2Alerts.findOne({"alertDetails._id": '5f0b4f508bda3805754ab343'}});
if (!exists) {
... do something
}
This can also be achieve using aggregate with a $match stage instead of a $project stage or even better countDocuments which just returns the count instead of the entire object if you do not require it.
One more thing I'd like to add is that make sure alertDetails._id is string type as you're using string in you're $in. otherwise you'll need to cast them to ObjectId type in mongoose like so:
new mongoose.Types.ObjectId('5f0b4f508bda3805754ab343')
And for Mongo:
import {ObjectId} from "mongodb"
...
new ObjectId('5f0b4f508bda3805754ab343')
EDIT
Try something like this?
let ids = alertData.data.items.map(item => item._id.toString());
let existing = await G2Alerts.distinct("alertsDetails._id", {"alertsDetails._id": {$in: ids}});
const filteredAlerts = alertData.data.items.reduce((filtered, item) => {
if (!existing.includes(item._id.toString())) {
return [item].concat(filtered)
}
return filtered;
}, []);
This way you only need to call the db once and not multiple times.
Final code based on the provided answer.
const ids = alertData.data.items.map(item => item._id);
const existing = await G2Alerts.find({ 'alertDetails._id': { $in: ids } }).distinct(
'alertDetails._id',
(err, alerts) => {
if (err) {
res.send(err);
}
return alerts;
},
);
const filteredAlerts = alertData.data.items.reduce((filtered, item) => {
if (!existing.includes(item._id.toString()) && item.openDate > dateLimit) {
return [item].concat(filtered);
}
return filtered;
}, []);

How to make custom incremental id or number in MongoDB? [duplicate]

According to this mongodb article it is possible to auto increment a field and I would like the use the counters collection way.
The problem with that example is that I don't have thousands of people typing the data in the database using the mongo console. Instead I am trying to use mongoose.
So my schema looks something like this:
var entitySchema = mongoose.Schema({
testvalue:{type:String,default:function getNextSequence() {
console.log('what is this:',mongoose);//this is mongoose
var ret = db.counters.findAndModify({
query: { _id:'entityId' },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
}
});
I have created the counters collection in the same database and added a page with the _id of 'entityId'. From here I am not sure how to use mongoose to update that page and get the incrementing number.
There is no schema for counters and I would like it to stay that way because this is not really an entity used by the application. It should only be used in the schema(s) to auto increment fields.
Here is an example how you can implement auto-increment field in Mongoose:
var CounterSchema = Schema({
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);
var entitySchema = mongoose.Schema({
testvalue: {type: String}
});
entitySchema.pre('save', function(next) {
var doc = this;
counter.findByIdAndUpdate({_id: 'entityId'}, {$inc: { seq: 1} }, function(error, counter) {
if(error)
return next(error);
doc.testvalue = counter.seq;
next();
});
});
You can use mongoose-auto-increment package as follows:
var mongoose = require('mongoose');
var autoIncrement = require('mongoose-auto-increment');
/* connect to your database here */
/* define your CounterSchema here */
autoIncrement.initialize(mongoose.connection);
CounterSchema.plugin(autoIncrement.plugin, 'Counter');
var Counter = mongoose.model('Counter', CounterSchema);
You only need to initialize the autoIncrement once.
The most voted answer doesn't work. This is the fix:
var CounterSchema = new mongoose.Schema({
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);
var entitySchema = mongoose.Schema({
sort: {type: String}
});
entitySchema.pre('save', function(next) {
var doc = this;
counter.findByIdAndUpdateAsync({_id: 'entityId'}, {$inc: { seq: 1} }, {new: true, upsert: true}).then(function(count) {
console.log("...count: "+JSON.stringify(count));
doc.sort = count.seq;
next();
})
.catch(function(error) {
console.error("counter error-> : "+error);
throw error;
});
});
The options parameters gives you the result of the update and it creates a new document if it doesn't exist.
You can check here the official doc.
And if you need a sorted index check this doc
So combining multiple answers, this is what I ended up using:
counterModel.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const counterSchema = new Schema(
{
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
}
);
counterSchema.index({ _id: 1, seq: 1 }, { unique: true })
const counterModel = mongoose.model('counter', counterSchema);
const autoIncrementModelID = function (modelName, doc, next) {
counterModel.findByIdAndUpdate( // ** Method call begins **
modelName, // The ID to find for in counters model
{ $inc: { seq: 1 } }, // The update
{ new: true, upsert: true }, // The options
function(error, counter) { // The callback
if(error) return next(error);
doc.id = counter.seq;
next();
}
); // ** Method call ends **
}
module.exports = autoIncrementModelID;
myModel.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const autoIncrementModelID = require('./counterModel');
const myModel = new Schema({
id: { type: Number, unique: true, min: 1 },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date },
someOtherField: { type: String }
});
myModel.pre('save', function (next) {
if (!this.isNew) {
next();
return;
}
autoIncrementModelID('activities', this, next);
});
module.exports = mongoose.model('myModel', myModel);
Attention!
As hammerbot and dan-dascalescu pointed out this does not work if you remove documents.
If you insert 3 documents with id 1, 2 and 3 - you remove 2 and insert another a new one it'll get 3 as id which is already used!
In case you don't ever remove documents, here you go:
I know this has already a lot of answers, but I would share my solution which is IMO short and easy understandable:
// Use pre middleware
entitySchema.pre('save', function (next) {
// Only increment when the document is new
if (this.isNew) {
entityModel.count().then(res => {
this._id = res; // Increment count
next();
});
} else {
next();
}
});
Make sure that entitySchema._id has type:Number.
Mongoose version: 5.0.1.
This problem is sufficiently complicated and there are enough pitfalls that it's best to rely on a tested mongoose plugin.
Out of the plethora of "autoincrement" plugins at http://plugins.mongoosejs.io/, the best maintained and documented (and not a fork) is mongoose sequence.
I've combined all the (subjectively and objectively) good parts of the answers, and came up with this code:
const counterSchema = new mongoose.Schema({
_id: {
type: String,
required: true,
},
seq: {
type: Number,
default: 0,
},
});
// Add a static "increment" method to the Model
// It will recieve the collection name for which to increment and return the counter value
counterSchema.static('increment', async function(counterName) {
const count = await this.findByIdAndUpdate(
counterName,
{$inc: {seq: 1}},
// new: return the new value
// upsert: create document if it doesn't exist
{new: true, upsert: true}
);
return count.seq;
});
const CounterModel = mongoose.model('Counter', counterSchema);
entitySchema.pre('save', async function() {
// Don't increment if this is NOT a newly created document
if(!this.isNew) return;
const testvalue = await CounterModel.increment('entity');
this.testvalue = testvalue;
});
One of the benefits of this approach is that all the counter related logic is separate. You can store it in a separate file and use it for multiple models importing the CounterModel.
If you are going to increment the _id field, you should add its definition in your schema:
const entitySchema = new mongoose.Schema({
_id: {
type: Number,
alias: 'id',
required: true,
},
<...>
});
test.pre("save",function(next){
if(this.isNew){
this.constructor.find({}).then((result) => {
console.log(result)
this.id = result.length + 1;
next();
});
}
})
I didn't wan to use any plugin (an extra dependencie, initializing the mongodb connection apart from the one I use in the server.js, etc...) so I did an extra module, I can use it at any schema and even, I'm considering when you remove a document from the DB.
module.exports = async function(model, data, next) {
// Only applies to new documents, so updating with model.save() method won't update id
// We search for the biggest id into the documents (will search in the model, not whole db
// We limit the search to one result, in descendant order.
if(data.isNew) {
let total = await model.find().sort({id: -1}).limit(1);
data.id = total.length === 0 ? 1 : Number(total[0].id) + 1;
next();
};
};
And how to use it:
const autoincremental = require('../modules/auto-incremental');
Work.pre('save', function(next) {
autoincremental(model, this, next);
// Arguments:
// model: The model const here below
// this: The schema, the body of the document you wan to save
// next: next fn to continue
});
const model = mongoose.model('Work', Work);
module.exports = model;
Hope it helps you.
(If this Is wrong, please, tell me. I've been having no issues with this, but, not an expert)
Here is a proposal.
Create a separate collection to holds the max value for a model collection
const autoIncrementSchema = new Schema({
name: String,
seq: { type: Number, default: 0 }
});
const AutoIncrement = mongoose.model('AutoIncrement', autoIncrementSchema);
Now for each needed schema, add a pre-save hook.
For example, let the collection name is Test
schema.pre('save', function preSave(next) {
const doc = this;
if (doc.isNew) {
const nextSeq = AutoIncrement.findOneAndUpdate(
{ name: 'Test' },
{ $inc: { seq: 1 } },
{ new: true, upsert: true }
);
nextSeq
.then(nextValue => doc[autoIncrementableField] = nextValue)
.then(next);
}
else next();
}
As findOneAndUpdate is an atomic operation, no two updates will return same seq value. Thus each of your insertion will get an incremental seq regardless of number of concurrent insertions. Also this can be extended to more complex auto incremental logic and the auto increment sequence is not limited to Number type
This is not a tested code. Test before you use until I make a plugin for mongoose.
Update I found that this plugin implemented related approach.
The answers seem to increment the sequence even if the document already has an _id field (sort, whatever). This would be the case if you 'save' to update an existing document. No?
If I'm right, you'd want to call next() if this._id !== 0
The mongoose docs aren't super clear about this. If it is doing an update type query internally, then pre('save' may not be called.
CLARIFICATION
It appears the 'save' pre method is indeed called on updates.
I don't think you want to increment your sequence needlessly. It costs you a query and wastes the sequence number.
I had an issue using Mongoose Document when assigning value to Schema's field through put(). The count returns an Object itself and I have to access it's property.
I played at #Tigran's answer and here's my output:
// My goal is to auto increment the internalId field
export interface EntityDocument extends mongoose.Document {
internalId: number
}
entitySchema.pre<EntityDocument>('save', async function() {
if(!this.isNew) return;
const count = await counter.findByIdAndUpdate(
{_id: 'entityId'},
{$inc: {seq: 1}},
{new: true, upsert: true}
);
// Since count is returning an array
// I used get() to access its child
this.internalId = Number(count.get('seq'))
});
Version: mongoose#5.11.10
None of above answer works when you have unique fields in your schema
because unique check at db level and increment happen before db level validation, so you may skip lots of numbers in auto increments like above solutions
only in post save can find if data already saved on db or return error
schmea.post('save', function(error, doc, next) {
if (error.name === 'MongoError' && error.code === 11000) {
next(new Error('email must be unique'));
} else {
next(error);
}
});
https://stackoverflow.com/a/41479297/10038067
that is why none of above answers are not like atomic operations auto increment in sql like dbs
I use together #cluny85 and #edtech.
But I don't complete finish this issues.
counterModel.findByIdAndUpdate({_id: 'aid'}, {$inc: { seq: 1} }, function(error,counter){
But in function "pre('save...) then response of update counter finish after save document.
So I don't update counter to document.
Please check again all answer.Thank you.
Sorry. I can't add comment. Because I am newbie.
var CounterSchema = Schema({
_id: { type: String, required: true },
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);
var entitySchema = mongoose.Schema({
testvalue: { type: String }
});
entitySchema.pre('save', function(next) {
if (this.isNew) {
var doc = this;
counter.findByIdAndUpdate({ _id: 'entityId' }, { $inc: { seq: 1 } }, { new: true, upsert: true })
.then(function(count) {
doc.testvalue = count.seq;
next();
})
.catch(function(error) {
throw error;
});
} else {
next();
}
});

Defining mongoose Model Methods Dynamically

I'm in the case of a function being able to search by different fields depending on the situation.
It returns the same dataset, it just searches by different fields: either userId or tagId. Therefore, in my code I have something like this:
var findByMethod;
if (searchBy === 'userId') {
findByMethod = UserArticleModel.findByUser;
}
else {
findByMethod = UserArticleModel.findByTag;
}
findByMethod(idToSearch, function (err, articles) {…});
findByUser and findByTag are static methods defined in the UserArticleModel.js
UserArticleModel.js
var mongoose = require('mongoose');
var userArticleSchema = new mongoose.Schema({
…
}
});
userArticleSchema.statics.findByUser = function (userId, callback) {
this.find({userId: userId}, function () {…});
};
userArticleSchema.statics.findByTag = function (tagId, callback) {…};
module.exports = mongoose.model('UserArticle', userArticleSchema);
Back in my controller, when I do:
UserArticleModel.findByTag(idToSearch, function (err, articles) {…});
All is well and things go right. But when I dynamically call the method via my variable:
findByMethod(idToSearch, function (err, articles) {…});
Things go wrong as node returns an error:
DOMAINE ERROR CAUGHT: TypeError: Object #<Object> has no method 'find'
I suspect this not to be be bound to the correct scope but I don't really understand why as findByMethod === UserArticleModel.findByUser // true
I think you are making this more involved that it need be. Though it is an easy trap to fall into by "too literally" following documented API examples and thinking essentially "this is how I need to hardcode this, because the docs say this is how you do it".
JavaScript objects are, well "Objects", and therefore just assigning "named" static methods which are really only object properties is just a basic process of "looping" the defined "schema paths" from that already defined "Object" and setting up the properties for the "findByFieldName" methods you want.
It's just "assigning named properties" and nothing more obscure or complex or even as "terse" than that.
If that "sounds like a mouthful" then the actual process of iterating object properties and "setting other properties" related to that within an overall object structure is not really as hard as you might think.
As a brief example:
var async = require('async'),
pascal = require('to-pascal-case'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var testSchema = new Schema({
fieldA: String,
fieldB: Number
});
function setStatics(schema) {
Object.keys(schema.paths).filter(function(key) {
return key != '_id';
}).forEach(function(key) {
schema.statics['findBy' + pascal(key)] = function(arg,callback) {
var query = {};
query[key] = arg;
this.findOne(query,callback);
};
});
};
// Set up findByFieldName other than _id
setStatics(testSchema);
var Test = mongoose.model( 'Test', testSchema, "test" );
mongoose.connect('mongodb://localhost/test');
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
Test.create([
{ "fieldA": "a", "fieldB": 1 },
{ "fieldA": "b", "fieldB": 2 }
],callback);
},
function(callback) {
Test.findByFieldA("a",function(err,doc) {
console.log(doc);
callback(err);
});
},
function(callback) {
Test.findByFieldB(2,function(err,doc) {
console.log(doc);
callback(err);
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
Which proves that they work by "testing them" with the output:
{ _id: 55f2ae1b7d8315f40b1a2b77, fieldA: 'a', fieldB: 1, __v: 0 }
{ _id: 55f2ae1b7d8315f40b1a2b78, fieldA: 'b', fieldB: 2, __v: 0 }
And that is all there is to it.
Of course for fields like "Arrays" you want to get a little more involved, but this is the basic premise as a listing you can try out yourself ( or selves for the community in general ).
I could also note that there are already a few things out there such as Bluebird via it's own .promisifyAll() call which interacts with objects to set new up "named methods" on the object in a similar way. Or at least it should be similar in principle, as I have not actually looked at that code.

How to properly pass data from mongodb using callback when looping through multiple db.Phrase.find calls

I'm receiving params from my get request that looks like this:
{ location: 'Venice', weather: 'Dry', what: 'Yoga', who: 'Bob' }
I then query a mongodb database that loops through each of the key and value pairs and queries for their union in the database.
I then save the returned values to outputCaption and then use a callback to pass the outputCaption back.
The problem is the callback gets called as many times as their key-value pairs looped over.
I'm forced to do this because I need the callback inside the db.Phrase.find call but I call that multiple times...
So I've fixed it using the code in app.get (I wait until all the keys have defined values in outputCaption before doing anything)
It works, but I can't imagine it's the best way to do it so I'm hoping there's a less hackish way?
Thanks
server.js
var express = require('express');
var db = require('./modules/db')
var logic = require('./modules/logic')
...
app.get('/phrase', function(req, res){
logic(req.query, function(outputCaption){
var flag = true
for (key in outputCaption){
if (outputCaption[key] === null){
console.log('Incomplete')
var flag = false;
}
}
if (flag === true) {
console.log(outputCaption);
};
});
})
...
logic.js
var db = require('./db')
var logic = function(params, callback){
var outputCaption = {
who: null,
what: null,
location: null,
weather: null
};
for (key in params){
category = key.toLowerCase();
option = params[key].toLowerCase();
db.Phrase.find({$and: [
{category: category},
{option: option}
]}, function(err, phrases){
if (err) return console.error(err);
var options = Object.keys(phrases).length
var idxOfOptionPicked = Math.floor(Math.random() * options)
outputCaption[phrases[idxOfOptionPicked].category] = phrases[idxOfOptionPicked].phrase
callback(outputCaption)
})
}
}
module.exports = logic;
Instead of firing multiple queries and performing a union of the result at the client side, make use of the query operators to allow MongoDB to do the union for you.
That way you:
Avoid multiple hits to the database.
Avoid multiple callback handlers.
Can post process the results in a single callback handler.
You can modify your code to prepare a query object from the request parameter,
var params = {location: 'Venice', weather: 'Dry', what: 'Yoga', who: 'Bob' };
var query = {};
var conditions = [];
Object.keys(params).forEach(function(key){
var $and = {};
$and["category"] = key.toLowerCase();
$and["option"] = params[key];
conditions.push($and);
});
(conditions.length > 1)?(query["$or"] = conditions):(query = conditions[0]);
Now the constructed query looks like:
{ '$or':
[ { category: 'location', option: 'Venice' },
{ category: 'weather', option: 'Dry' },
{ category: 'what', option: 'Yoga' },
{ category: 'who', option: 'Bob' }
]
}
You can pass this object to the find() method, to get the results in a single hit:
db.Phrase.find(query,callback);
This way your code remains cleaner and easier to understand.

How to sort in mongoose?

I find no doc for the sort modifier. The only insight is in the unit tests:
spec.lib.query.js#L12
writer.limit(5).sort(['test', 1]).group('name')
But it doesn't work for me:
Post.find().sort(['updatedAt', 1]);
In Mongoose, a sort can be done in any of the following ways:
Post.find({}).sort('test').exec(function(err, docs) { ... });
Post.find({}).sort([['date', -1]]).exec(function(err, docs) { ... });
Post.find({}).sort({test: 1}).exec(function(err, docs) { ... });
Post.find({}, null, {sort: {date: 1}}, function(err, docs) { ... });
This is how I got sort to work in mongoose 2.3.0 :)
// Find First 10 News Items
News.find({
deal_id:deal._id // Search Filters
},
['type','date_added'], // Columns to Return
{
skip:0, // Starting Row
limit:10, // Ending Row
sort:{
date_added: -1 //Sort by Date Added DESC
}
},
function(err,allNews){
socket.emit('news-load', allNews); // Do something with the array of 10 objects
})
As of Mongoose 3.8.x:
model.find({ ... }).sort({ field : criteria}).exec(function(err, model){ ... });
Where:
criteria can be asc, desc, ascending, descending, 1, or -1
Note: Use quotation marks or double quote
use "asc", "desc", "ascending", "descending", 1, or -1
UPDATE:
Post.find().sort({'updatedAt': -1}).all((posts) => {
// do something with the array of posts
});
Try:
Post.find().sort([['updatedAt', 'descending']]).all((posts) => {
// do something with the array of posts
});
Mongoose v5.x.x
sort by ascending order
Post.find({}).sort('field').exec(function(err, docs) { ... });
Post.find({}).sort({ field: 'asc' }).exec(function(err, docs) { ... });
Post.find({}).sort({ field: 'ascending' }).exec(function(err, docs) { ... });
Post.find({}).sort({ field: 1 }).exec(function(err, docs) { ... });
Post.find({}, null, {sort: { field : 'asc' }}), function(err, docs) { ... });
Post.find({}, null, {sort: { field : 'ascending' }}), function(err, docs) { ... });
Post.find({}, null, {sort: { field : 1 }}), function(err, docs) { ... });
sort by descending order
Post.find({}).sort('-field').exec(function(err, docs) { ... });
Post.find({}).sort({ field: 'desc' }).exec(function(err, docs) { ... });
Post.find({}).sort({ field: 'descending' }).exec(function(err, docs) { ... });
Post.find({}).sort({ field: -1 }).exec(function(err, docs) { ... });
Post.find({}, null, {sort: { field : 'desc' }}), function(err, docs) { ... });
Post.find({}, null, {sort: { field : 'descending' }}), function(err, docs) { ... });
Post.find({}, null, {sort: { field : -1 }}), function(err, docs) { ... });
For Details: https://mongoosejs.com/docs/api.html#query_Query-sort
Update
There is a better write up if this is confusing people; check out finding documents and how queries work in the mongoose manual. If you want to use the fluent api you can get a query object by not providing a callback to the find() method, otherwise you can specify the parameters as I outline below.
Original
Given a model object, per the docs on Model, this is how it can work for 2.4.1:
Post.find({search-spec}, [return field array], {options}, callback)
The search spec expects an object, but you can pass null or an empty object.
The second param is the field list as an array of strings, so you would supply ['field','field2'] or null.
The third param is the options as an object, which includes the ability to sort the result set. You would use { sort: { field: direction } } where field is the string fieldname test (in your case) and direction is a number where 1 is ascending and -1 is desceding.
The final param (callback) is the callback function which receives the collection of docs returned by the query.
The Model.find() implementation (at this version) does a sliding allocation of properties to handle optional params (which is what confused me!):
Model.find = function find (conditions, fields, options, callback) {
if ('function' == typeof conditions) {
callback = conditions;
conditions = {};
fields = null;
options = null;
} else if ('function' == typeof fields) {
callback = fields;
fields = null;
options = null;
} else if ('function' == typeof options) {
callback = options;
options = null;
}
var query = new Query(conditions, options).select(fields).bind(this, 'find');
if ('undefined' === typeof callback)
return query;
this._applyNamedScope(query);
return query.find(callback);
};
HTH
you can sort your query results by
Post.find().sort({createdAt: "descending"});
This is how I got sort to work in mongoose.js 2.0.4
var query = EmailModel.find({domain:"gmail.com"});
query.sort('priority', 1);
query.exec(function(error, docs){
//...
});
Chaining with the query builder interface in Mongoose 4.
// Build up a query using chaining syntax. Since no callback is passed this will create an instance of Query.
var query = Person.
find({ occupation: /host/ }).
where('name.last').equals('Ghost'). // find each Person with a last name matching 'Ghost'
where('age').gt(17).lt(66).
where('likes').in(['vaporizing', 'talking']).
limit(10).
sort('-occupation'). // sort by occupation in decreasing order
select('name occupation'); // selecting the `name` and `occupation` fields
// Excute the query at a later time.
query.exec(function (err, person) {
if (err) return handleError(err);
console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host
})
See the docs for more about queries.
app.get('/getting',function(req,res){
Blog.find({}).limit(4).skip(2).sort({age:-1}).then((resu)=>{
res.send(resu);
console.log(resu)
// console.log(result)
})
})
Output
[ { _id: 5c2eec3b8d6e5c20ed2f040e, name: 'e', age: 5, __v: 0 },
{ _id: 5c2eec0c8d6e5c20ed2f040d, name: 'd', age: 4, __v: 0 },
{ _id: 5c2eec048d6e5c20ed2f040c, name: 'c', age: 3, __v: 0 },
{ _id: 5c2eebf48d6e5c20ed2f040b, name: 'b', age: 2, __v: 0 } ]
with the current version of mongoose (1.6.0) if you only want to sort by one column, you have to drop the array and pass the object directly to the sort() function:
Content.find().sort('created', 'descending').execFind( ... );
took me some time, to get this right :(
This is how I managed to sort and populate:
Model.find()
.sort('date', -1)
.populate('authors')
.exec(function(err, docs) {
// code here
})
Post.find().sort({updatedAt: 1});
As of October 2020, to fix your issue you should add .exec() to the call. don't forget that if you want to use this data outside of the call you should run something like this inside of an async function.
let post = await callQuery();
async function callQuery() {
return Post.find().sort(['updatedAt', 1].exec();
}
Others worked for me, but this did:
Tag.find().sort('name', 1).run(onComplete);
Post.find().sort({updatedAt:1}).exec(function (err, posts){
...
});
Starting from 4.x the sort methods have been changed. If you are using >4.x. Try using any of the following.
Post.find({}).sort('-date').exec(function(err, docs) { ... });
Post.find({}).sort({date: -1}).exec(function(err, docs) { ... });
Post.find({}).sort({date: 'desc'}).exec(function(err, docs) { ... });
Post.find({}).sort({date: 'descending'}).exec(function(err, docs) { ... });
Post.find({}).sort([['date', -1]]).exec(function(err, docs) { ... });
Post.find({}, null, {sort: '-date'}, function(err, docs) { ... });
Post.find({}, null, {sort: {date: -1}}, function(err, docs) { ... });
This is what i did, it works fine.
User.find({name:'Thava'}, null, {sort: { name : 1 }})
// Ascending with updatedAt field
Post.find().sort('updatedAt').exec((err, post) => {...});
// Descending with updatedAt field
Post.find().sort('-updatedAt').exec((err, post) => {...});
Refer here: https://mongoosejs.com/docs/queries.html
you can also use aggregate() for sorting
const sortBy = req.params.sort;
const limitNum = req.params.limit;
const posts = await Post.aggregate([
{ $unset: ['field-1', 'field-2', 'field-3', 'field-4'] },
{ $match: { field-1: value} },
{ $sort: { [sortBy]: -1 } }, //-------------------> sort the result
{ $limit: Number(limitNum) },
]);
Solution :
posts.find().sort({field:1})
// for ascending and for descending order just use -1 instead of 1

Categories