I am trying to sort my MongoDB data. I have a rentals Schema shown below which includes a "featured" item. IF the featured item is set to true (users can do this on their end) I want the featured item to be shown above the rest of the Rentals. In other words I need to sort the mongo data that has featured = true to the top.
How would I change my code below to do this?
Here is my rental Schema:
var rentalsSchema = new mongoose.Schema({
featured: {
type: Boolean,
default: false
},
Title: String,
});
Here is what I've tried so far (and is not working.)
router.get('/rentals', function(req, res) {
db.collection('rentals').find().sort({
featured: 1
}).toArray(function(err, items) {
});
});
Change .sort({ featured: 1 }) to .sort({ featured: -1 }). You want those with featured value of true to come before those with false false.
It looks like default sorting by boolean fields goes from false to true (maybe because of 0 and 1 numerical values, and default ascending order). To demonstrate this, open the Chrome Dev Tools console, enter [true, false, false, true].sort(), and the result is [false, false, true, true].
Related
so basically I have this and I am trying to update the STATUS part of an array.
However, everything I try does nothing. I have tried findOneAndUpdate also. I am trying to identify the specific item in the array by the number then update the status part of that specific array
(Sorry for formatting, I have no idea how to do that on the site yet ...) (Full code can be found here: https://sourceb.in/0811b5f805)
Code
const ticketObj = {
number: placeholderNumber,
userID: message.author.id,
message: m.content,
status: 'unresolved'
}
let tnumber = parseInt(args[1])
let statuss = "In Progress"
await Mail.updateOne({
"number": tnumber
}, { $set: { "status": statuss } })
Schema
const mongoose = require('mongoose')
const mailSchema = new mongoose.Schema({
guildID: { type: String, required: true },
ticketCount: { type: Number, required: true },
tickets: { type: Array, default: [] }
}, { timestamps: true });
module.exports = mongoose.model('Mail', mailSchema)
You need to use something like Mail.updateOne({"guildID": message.guild.id}, {$set: {`tickets.${tnumber}.status`: statuss}})
or for all objects in array:
Mail.updateOne({"guildID": message.guild.id}, {$set: {'tickets.$[].status': statuss}})
Also, you need to create a schema for the tickets, as it is described in docs:
one important reason to use subdocuments is to create a path where there would otherwise not be one to allow for validation over a group of fields
I have the following schema defined in my app (node.js w/ express):
const ArtistSchema = new mongoose.Schema({
name: {type: String, required: true},
year: {type: Number, required: true},
genres: {type: [String], required: true},
imageUrl: {type: String, required: false},
trending: {type: Boolean, required: false, default: false},
trendingDate: {type: Date, required: false}
});
and a route that is supposed to retrieve those entries, who have trending set to true:
// GET trending
router.get('/trending', (req, res) => {
artist.Artist.find({trending: true}).exec((err, trendingArtists) => {
if (err) {
console.error(err);
res.status(500).json({message: err.message});
return;
}
res.json(trendingArtists);
});
});
However, it always returns an empty array when i try to filter by trending field, even though there are items in my collection that have trending set to true. I have tried wrapping everything in single and double quotes and using 1 instead of true, but no query returns results. Filtering by other fields works just fine, not filtering at all returns all entries as expected.
The entries in mongo shell look like this:
> db.artists.find({},{name:1,trending:1}).pretty()
{
"_id" : ObjectId("5de942074a486e2c21246fb9"),
"name" : "Unwound",
"trending" : "true"
}
{
"_id" : ObjectId("5de942074a486e2c21246fba"),
"name" : "Ladytron",
"trending" : "true"
}
{
"_id" : ObjectId("5de942074a486e2c21246fbb"),
"name" : "Depeche Mode",
"trending" : "true"
}
console.loging the results in the app produces this:
[
{
genres: [ 'post-hardcore', 'noise rock', 'indie rock' ],
trending: true,
_id: 5de942074a486e2c21246fb9,
name: 'Unwound',
year: 1991,
imageUrl: '2a7f00a1f8e0ab37c647600f6abff67e.jpg',
trendingDate: 2019-12-20T18:48:53.000Z
},
{
genres: [ 'synthpop', 'electroclash', 'dream pop' ],
trending: true,
_id: 5de942074a486e2c21246fba,
name: 'Ladytron',
year: 1999,
imageUrl: 'f26cc1ae1fef371793622bd199b4bb52.jpg',
trendingDate: 2019-12-20T18:49:05.000Z
},
{
genres: [ 'synthpop', 'new wave' ],
trending: true,
_id: 5de942074a486e2c21246fbb,
name: 'Depeche Mode',
year: 1980,
imageUrl: 'e5328919dac971af86dd034781a2da71.jpg',
trendingDate: 2019-12-20T18:49:43.000Z
}
]
I am at my wits' end. What could cause filtering by a boolean field to break the query regardless of what i specify as the value (i have tried true, 'true', "true", 1, '1', as well as the falsy counterparts)?
edit: i tried a few things since:
1) filtering the results after the query was executed works fine (i.e just writing res.json(trendingArtists.filter(a => a.trending === true));), though it's obviously not the way i would like to deal with filtering my queries
2) the collection i'm querying was created and edited manually, and not via api that my app implements. If i insert a new entry using a POST request via api, that entry will be returned by the query, provided trending was set to true
3) editing existing entries with PATCH requests where i set trending to false and then back to true also works, though it messes up the trendingDate field that is set to current date each time trending is changed to true
4) if the query works for an entry, it works no matter what i put as the value for the filter, as long as it's truthy for mongoose. .find({trending: true}) works just as well as .find({trending: '1'})
I guess the problem is mongoose not recognizing my manually inserted values as truthy at all, even though theoretically they should be cast to true. My problem is solved, I guess? I do not plan to insert values manually in the future, this was just for testing, and those that few that are inserted can be fixed fairly easily, but i don't think it should matter for mongoose whether i edit the entries manually or via Model.save. It seems like a bug - should i close the question and open a bug report on their github?
I have the following issue. I have some comments that are soft-deletable. So they have a flag is_deleted and it is set to true when a record is deleted.
My comments aren't an independent model, but they are a nested array in another model (simplified model):
let CommentSchema = new Schema({
text: {
type: String,
required: true
},
modified_at: {
type: Date,
default: null
},
created_at: {
type: Date,
default: Date.now
},
is_deleted: {
type: Boolean,
default: false
},
});
let BookSchema = new Schema({
...
comments: [CommentSchema],
...
});
Now when I get all my Books with Books.find({}, (err, books) => {}) I wanted to filter out the deleted comments in:
BookSchema.pre('find', function() {
this.aggregate(
{},
{ $unwind: '$comments'},
{ $match: {'comments.is_deleted': false}})
});
But it does not work. Any idea how to write the query, so that it only return the non-deleted nested comments without creating an independent Comment collection?
EDIT: I didn't mention it's an endpoint where I access only one book object. The resource url is like this: /books/{bookId}/comments. But also nice to have if it would work when getting all book objects.
You can use the positional $ operator and filter directly from find. As greatly explaned by #Blakes Seven Here
BookSchema.find({
'comments.is_deleted': false,
}, {
'comments.$': 1,
});
EDIT:
As you said the $ operator only return one element, here is what the document says :
The positional $ operator limits the contents of an from the
query results to contain only the first element matching the query
document
There is two solution to your problem:
Make an aggregate, which is usually slow to get executed by database
Get the books and filter the comments using loops
BookSchema.find({
'comments.is_deleted': false,
}).map(x => Object.assign(x, {
comments: x.comments.filter(y => !y.is_deleted),
}));
The find get all books that have a non-deleted comment.
The map loop on each book.
We then remove the comments marked as deleted
I need to update some fields
i am using mongoose driver and express js
schema:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ProfilesSchema = new Schema({
presentRound: {
type: Number,
default: 1
},
scheduleInterviewStatus: {
type: Boolean,
default: false
},
interviewStatus: String,
ratings: [{
round: Number,
rating: Number,
feedback: String,
interviewer: String,
roundStatus: String
}]
});
module.exports = mongoose.model('Profiles', ProfilesSchema);
so in these i need to update by id presentRound scheduleInterviewStatus interviewStatus and roundStatus(in ratings array by matching round number)
Before updating:
presentRound: 1,
scheduleInterviewStatus: true,
interviewStatus: "on-hold",
ratings: [{
round: 1,
rating: 3,
feedback: "good communication skills",
interviewer: "Vishal",
roundStatus: "second opinion"
}]
After Updating:
presentRound: 2,
scheduleInterviewStatus: false,
interviewStatus: "in-process",
ratings: [{
round: 1,
rating: 3,
feedback: "good communicationskills",
interviewer: "Vishal",
roundStatus: "selected"
}]
i have tried to run the query in robomongo first but getting error
Error: Fourth argument must be empty when specifying upsert and multi with an object.
Query:
db.getCollection('profiles').update({
"_id": ObjectId("57a9aa24e93864e02d91283c")
}, {
$set: {
"presentRound": 2,
"interviewStatus":"in process",
"scheduleInterviewStatus": false
}
},{
"ratings.$.round": 1
},{
$set: {
"ratings.roundStatus":"selected"
}
},
{ upsert: true },{multi:true})
I have no idea where i am going wrong
Please help.
Your update statement is incorrect, it has misplaced arguments - you are putting multiple $set operations and options as different parameters to the update method; they should be under separate designated update parameters. The correct Node.js syntax is:
update(selector, document, options, callback)
where selector is an object which is the selector/query for the update operation, document is also an object which is the update document and finally an optionsobject which by default is null and has the optional update settings.
Here you are doing
update(selector, document, selector, document, options, options, callback)
In which mongo is updating the collection using the first two parameters as correct and it naturally throws the error
Error: Fourth argument must be empty when specifying upsert and multi
with an object.
because you have too many incorrect parameters specified.
Also, you have incorrect usage of the positional operator. It should be part of the document to be updated, not in the query.
For the correct implementation, follow this update
db.getCollection('profiles').update(
/* selector */
{
"_id": ObjectId("57a9aa24e93864e02d91283c"),
"ratings.round": 1
},
/* update document */
{
"$set": {
"presentRound": 2,
"interviewStatus": "in process",
"scheduleInterviewStatus": false,
"ratings.$.roundStatus": "selected"
}
},
/* optional settings */
{ upsert: true, multi: true }
)
replace {upsert:true} -> {upsert:true,strict: false}
I'm building a node.js application with Mongoose and have a problem related to sorting embedded documents. Here's the schema I use:
var locationSchema = new Schema({
lat: { type: String, required: true },
lon: { type: String, required: true },
time: { type: Date, required: true },
acc: { type: String }
})
var locationsSchema = new Schema({
userId: { type: ObjectId },
source: { type: ObjectId, required: true },
locations: [ locationSchema ]
});
I'd like to output the locations embedded in the userLocations documented sorted by their time attribute. I currently do the sorting in JavaScript after I retrieved the data from MongoDb like so:
function locationsDescendingTimeOrder(loc1, loc2) {
return loc2.time.getTime() - loc1.time.getTime()
}
LocationsModel.findOne({ userId: theUserId }, function(err, userLocations) {
userLocations.locations.sort(locationsDescendingTimeOrder).forEach(function(location) {
console.log('location: ' + location.time);
}
});
I did read about the sorting API provided by Mongoose but I couldn't figure out if it can be used for sorting arrays of embedded documents and if yes, if it is a sensible approach and how to apply it to this problem. Can anyone help me out here, please?
Thanks in advance and cheers,
Georg
You're doing it the right way, Georg. Your other options are either to sort locations by time upon embedding in the first place, or going the more traditional non-embedded route (or minimally embedded route so that you may be embedding an array of ids or something but you're actually querying the locations separately).
This also can be done using mongoose sort API as well.
LocationsModel.findOne({ userId: theUserId })
// .sort({ "locations.time": "desc" }) // option 1
.sort("-locations.time") // option 2
.exec((err, result) => {
// compute fetched data
})
Sort by field in nested array with Mongoose.js
More methods are mentioned in this answer as well
Sorting Options in mogoose
Mongoose Sort API