Mongoose text search with diacriticSensitive - node.js

I have the following mongoose query:
User.find(
{
$text: {
$search: req.query.search,
$diacriticSensitive: true
}
},
{
score: {
$meta: "textScore"
}
}, function(err, results) {
if (err) {
next(err);
return;
}
return res.json(results);
});
The query leads to the following error message:
Can't canonicalize query: BadValue extra fields in $text
When I remove the "$diacriticSensitive: true" part from the query I get no error message, but only get exact matches.
What I want is when the user searches for "USERNA", but only the user "USERNAME" is available, this result should also be shown with a low score.
I am using Mongoose 4.3.5 and MongoDB 3.0.3.
MongoDB supports this attribute, doesn't Mongoose support it?

Per doc, $diacriticSensitive option is enable in mongodb v3.2
For articles document
{ "_id" : ObjectId("56d305dcc7ce7117db9b6da4"), "subject" : "Coffee Shopping", "author" : "efg", "view" : 5 }
{ "_id" : ObjectId("56d305e9c7ce7117db9b6da5"), "subject" : "Coffee and cream", "author" : "efg", "view" : 10 }
{ "_id" : ObjectId("56d305fec7ce7117db9b6da6"), "subject" : "Baking a cake", "author" : "abc", "view" : 50 }
Here are my test codes
var ArticleSchema = new mongoose.Schema({
subject: String,
author: String,
view: Number,
score: Number,
});
ArticleSchema.index({subject: 'text'});
var article = mongoose.model('article', ArticleSchema);
function queryText() {
article
.find({$text: {$search: 'Coffee', $diacriticSensitive: true}},
{score: {$meta: 'textScore'}})
.exec(function(err, doc) {
if (err)
console.log(err);
else
console.log(doc);
});
}
Result:
{ "_id" : ObjectId("56d305dcc7ce7117db9b6da4"), "subject" : "Coffee Shopping", "author" : "efg", "view" : 5, "score" : 0.75 }
{ "_id" : ObjectId("56d305e9c7ce7117db9b6da5"), "subject" : "Coffee and cream", "author" : "efg", "view" : 10, "score" : 0.75 }

Related

Mongoose removing a specific comment in an array that is attached to User

--NodeJS, Mongoose, MongoDB, ExpressJS, EJS--
My website is, there is a login user, where they can submit "name" and "image" of what they want, then the author and other people can comment on that picture. On per each image, i added a function where i count the comments using the .length function which is counting the comments on the picture and projects the number of comments to my ejs file.
here is my schema:
var testSchema = new mongoose.Schema({
name: String,
image: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Loginuser"
},
username: String
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
var commentSchema = new mongoose.Schema ({
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Loginuser"
},
username: String
},
text: String,
date: String
});
var loginSchema = new mongoose.Schema ({
username: String,
password: String
});
var TestData = mongoose.model("User", testSchema);
var Comment = mongoose.model("Comment", commentSchema);
var LoginUser = mongoose.model("Loginuser", loginSchema);
I have a function that deletes a comment on User,
app.delete("/index/:id/comments/:comment_id", function(req, res){
Comment.findByIdAndRemove(req.params.comment_id, function(err){
if(err) {
console.log(err);
res.redirect("/index/");
} else {
console.log("Comment successfully deleted!");
res.redirect("back");
}
});
});
Here's some sample data from my mongoDB
commentSchema
{ "_id" : ObjectId("57d316e506d8e9186c168a49"),
"text" : "hey baby! why cry?", "
__v" : 0,
"author" : { "id" : ObjectId("57d148acd0f11325b0dcd3e6"),
"username" :
"nagy" },
"date" : "9/10/2016 , 8:07 AM" }
{ "_id" : ObjectId("57d316f706d8e9186c168a4a"),
"text" : "don't you cry baby!",
"__v" : 0,
"author" : { "id" : ObjectId("57d095d727e6b619383a39d0"),
"username": "doge" },
"date" : "9/10/2016 , 8:07 AM" }
{ "_id" : ObjectId("57d3170306d8e9186c168a4b"),
"text" : "wow so cute!!!!!!", "_
_v" : 0,
"author" : { "id" : ObjectId("57d095d727e6b619383a39d0"),
"username" : "doge" }, "date" : "9/10/2016 , 8:07 AM" }
and here's my data on testSchema
{ "_id" : ObjectId("57d316c506d8e9186c168a47"),
"name" : "Baby crying",
"image": "https://s-media-cache-ak0.pinimg.com/564x/d0/bb/ed/d0bbed614353534df9a3be0abe
5f1d78.jpg",
"comments" : [ ObjectId("57d316e506d8e9186c168a49"), ObjectId("57d3
16f706d8e9186c168a4a") ], "author" : { "id" : ObjectId("57d095d727e6b619383a39d0
"), "username" : "doge" }, "__v" : 2 }
{ "_id" : ObjectId("57d316dc06d8e9186c168a48"),
"name" : "Maria?! OZawa?!",
"image" : "https://dncache-mauganscorp.netdna-ssl.com/thumbseg/1092/1092126-bigthumb
nail.jpg",
"comments" : [ ObjectId("57d3170306d8e9186c168a4b") ], "author" : { "
id" : ObjectId("57d148acd0f11325b0dcd3e6"), "username" : "nagy" }, "__v" : 1 }
It is working fine, it's deleting the comment. The problem here is, it is deleting only on the "Comment" model.
I want to delete that same comment also on "TestData" model, because everytime i delete a comment, the count of comments remains the same.
So basically i want to delete that specific comment on both models.
I tried using this approach:
app.delete("/index/:id/comments/:comment_id", function(req, res){
TestData.findByIdAndRemove(req.params.comment_id)function(err){
if(err) {
console.log(err);
res.redirect("/index");
} else {
res.redirect("back");
}
});
});
but it isn't working.
Can you help me on what specific query should i use?
Try the following code:-
TestData.update(
{},
{$pull: {comments: req.params.comment_id}},
{ multi: true },
function(err, data){
console.log(err, data);
});
Hope this will help you.

Finding average from referenced Mongoose Schema

Basically I am trying to find the average rating out of 5 based on comments for specific movie titles in a small app using Node.js and Mongoose.
Movie Schema:
var mongoose = require("mongoose");
//SCHEMA SET UP
var movieSchema = new mongoose.Schema({
title: String,
director: String,
year: Number,
comments:[
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Movie", movieSchema);
Comment Schema:
var mongoose = require("mongoose");
var commentSchema = mongoose.Schema({
author: String,
text: String,
rating: Number,
movie: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Movie"
}
]
});
module.exports = mongoose.model("Comment", commentSchema);
Trying to find the average movie rating:
Comment.aggregate([
{
$group: {
_id: '$movie',
ratingAvg: {$avg: '$rating'}
}
}
], function(err, results){
if(err){
console.log(err);
}else{
console.log(results);
}
}
);
Based on 3 sample ratings(5, 5, 4) on two separate movies this will return:
[ { _id: 'movie', ratingAvg: 4.666666666666667 } ]
So it is getting me the average of ALL the comments, but I want the average depending on the title of the movie. So for example I have a movie with the title of "Big Trouble in Little China" and it has two comments with the ratings of 5 and 4. I want it to return an average of 4.5. Sorry if this question is confusing, I'm super new to MongoDB and Mongoose and I think its a little over my head I'm afraid :(.
Edit: Here are some sample documents from the movie collection:
{ "_id" : ObjectId("57b135aefaa8fcff66b94e3e"), "title" : "Star Trek", "director" : "Idu No", "year" : 2008, "comments" : [ ObjectId("57b
135b9faa8fcff66b94e3f"), ObjectId("57b135c5faa8fcff66b94e40") ], "__v" : 2 }
{ "_id" : ObjectId("57b137b0a64ba6296d0df2d0"), "title" : "Buntley", "director" : "Lucy Mayfield", "year" : 2016, "comments" : [ ObjectId
("57b137bca64ba6296d0df2d1"), ObjectId("57b137caa64ba6296d0df2d2") ], "__v" : 2 }
and the comment collection:
{ "_id" : ObjectId("57b135b9faa8fcff66b94e3f"), "author" : "Tyler", "text" : "Ok", "rating" : 5, "movie" : [ ], "__v" : 0 }
{ "_id" : ObjectId("57b135c5faa8fcff66b94e40"), "author" : "Jennicaa", "text" : "asdlfjljsf", "rating" : 1, "movie" : [ ], "__v" : 0 }
{ "_id" : ObjectId("57b137bca64ba6296d0df2d1"), "author" : "Bentley", "text" : "Its amazing", "rating" : 5, "movie" : [ ], "__v" : 0 }
{ "_id" : ObjectId("57b137caa64ba6296d0df2d2"), "author" : "Lucy", "text" : "Its pretty good", "rating" : 4, "movie" : [ ], "__v" : 0 }
I'm noticing that the comment collection is not associating the movie id as it is just an empty array. That must be my issue?
I just want the average of the reviews for each movie by title. So if a movie titled Star Trek had four reviews with scores of 3, 3, 4, 5, the average would be 3.75. Normally I would just sum all the numbers in an array and then divide by the length of the array, but I just don't know enough with mongoDB/mongoose to do that :(
Sorry for not providing the proper amount of information straight away.
Since the comment model has an array which holds the movies, you need to unwind the array field first before calculating aggregates based on that group. Your aggregation pipeline would have an $unwind step before the $group
to flatten the array (denormalize it). You can then group the flattened documents by the movie _id and calculate the average.
The following example shows this:
Comment.aggregate([
{ "$unwind": "$movie" },
{
"$group": {
"_id": "$movie",
"ratingAvg": { "$avg": "$rating" }
}
}
], function(err, results) {
if(err) handleError(err);
Movie.populate(results, { "path": "_id" }, function(err, result) {
if(err) handleError(err);
console.log(result);
});
})
Here is the solution I used to actually render the average to a template.
app.get("/movies/:id", function(req, res){
Movie.findById(req.params.id).populate("comments").exec(function(err, showMovie){
if(err){
console.log(err);
} else{
var total = 0;
for(var i = 0; i < showMovie.comments.length; i++) {
total += showMovie.comments[i].rating;
}
var avg = total / showMovie.comments.length;
res.render("show", {movie: showMovie, ratingAverage: avg});
}
});
});

findAndModify usage in node.js/MongoDb

I have a collection which shows a list of users with their favorite movies.
Here is a sample dataset ...
{
"_id" : ObjectId("545c08dcc4d2b8a0243dd4db"),
"mail" : "mickey#mouse.com",
"name" : "Mickey Mouse",
"favorite_movies" : [
{
"name" : "The Green Mile",
"Year" : "1992"
},
{
"name" : "Raging Bull",
"Year" : "1980"
}
],
"watched_movies" : [
{
"name" : "The Green Mile",
"Year" : "1992"
},
{
"name" : "Jaws",
"Year" : "1976"
}
]
}
Now given _id 545c08dcc4d2b8a0243dd4db and a movie
{
"name" : "InterStellar",
"Year" : "2014"
}
and if I need to add this to the watched_movies, in Node.js/MongoDB, I believe the only way is to
Find the document using Find
Do a findAndModify on the found document by appending to the watched_movies array.
Step #2 is based on example here
http://mongodb.github.io/node-mongodb-native/markdown-docs/insert.html
Is there a better way to do this ? Specifically can I avoid the step#1 ?
Here is how I'm doing step#2.
But I'm forced to do step#1 to get the "watched_movies" element and then append the new movie to it to construct the movielist
//try to update
collection.findAndModify(
{'_id':new BSON.ObjectID(id)}, //query
[['_id','asc']], //sort order
{$set: {"watched_movies": movielist}}, //replacement
{new: true}, //options
function(err, newObject){
if(err) {
console.warn(err.message);
} else {
console.log("Updated !! " + JSON.stringify(newObject));
res.send(object);
}
});
I am also open to suggestions on improving this schema design.
You can do this with a single findAnyModify call by using the $push or $addToSet array operator to directly add the new movie to the watched_movies array:
var movie = {
"name" : "InterStellar",
"Year" : "2014"
};
collection.findAndModify(
{'_id':new BSON.ObjectID(id)},
[['_id','asc']],
{$addToSet: {"watched_movies": movie}},
{new: true},
function(err, newObject){
if(err) {
console.warn(err.message);
} else {
console.log("Updated !! " + JSON.stringify(newObject));
res.send(newObject);
}
});

Query sub documents in mongoose

I am new to node.js.
I am having JSON object of the form
{ "_id" : ObjectId("540b03ddf1768fe562fbb715"),
"productid" : "men1",
"comments" : [ { "name" : "shiva", "text" : "Haidddsdcccccc", "_id" : ObjectId("540b03dd0570a6261e20a59e"), "date_entered" : ISODate("2014-09-06T12:53:49.658Z") },
{ "name" : "shiva", "text" : "Haidddsdcccccc", "_id" : ObjectId("540cb2be35f8145a2d296ea0"), "date_entered" : ISODate("2014-09-07T19:32:14.827Z") },
{ "name" : "shiva", "text" : "Haidddsdcccccc", "_id" : ObjectId("540cb2c335f8145a2d296ea1"), "date_entered" : ISODate("2014-09-07T19:32:19.456Z") } ] }
I want to query comments of a product after a specific time
I am using query as
var query=Product.find({"productid":obj.productid,
'comments.date_entered': {$gt: obj.timed}}, {'comments.$': 1})
I am getting only one object after the specific time i.e
{
_id: "540b03ddf1768fe562fbb715"
comments: [1]
0: {
name: "shiva"
text: "Haidddsdcccccc"
_id: "540cb2be35f8145a2d296ea0"
date_entered: "2014-09-07T19:32:14.827Z"
}
}
How to get all the comments of the product after the specified time?
Doesn't seem to be a good way to do this from what I can find out. A work-around could be doing something like this:
Product.findOne({productid: PRODUCT_ID}, {comments: 1}, function(err, product) {
var comments = product.comments.filter(function(comment) {
return comment.date_entered > DATE;
});
...
})

How to use '$match' Aggregation Operators of MongoDB to match with the id of embedded document?

Scenario: Consider the document present in the MongoDB in collection named 'MyCollection'
{
"_id" : ObjectId("512bc95fe835e68f199c8686"),
"author": "dave",
"score" : 80,
"USER" : {
"UserID": "Test1",
"UserName": "ABCD"
}
},
{ "_id" : ObjectId("512bc962e835e68f199c8687"),
"author" : "dave",
"score" : 85,
"USER" : {
"UserID": "Test2",
"UserName": "XYZ"
}
},
...
I know the UserID and want to fetch based on that.
Issue: I tried the following code with Node.js + MongoDB-native driver:
db.Collection('MyCollection', function (err, collection) {
if (err) return console.error(err);
collection.aggregate([
{ $match: { '$USER.UserID': 'Test2'} },
{$group: {
_id: '$_id'
}
},
{
$project: {
_id: 1
}
}
], function (err, doc) {
if (err) return console.error(err);
console.dir(doc);
});
});
But its not working as expected.
Question: Can anyone know how to do the same with $match operator in MongoDB query?
Update: I am not getting any error. But the object will be blank i.e. []
I tried in the shell and your $match statement is wrong - trying in the shell
> db.MyCollection.find()
{ "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "USER" : { "UserID" : "Test1", "UserName" : "ABCD" } }
{ "_id" : ObjectId("512bc962e835e68f199c8687"), "author" : "dave", "score" : 85, "USER" : { "UserID" : "Test2", "UserName" : "XYZ" } }
> db.MyCollection.aggregate([{$match: {"$USER.UserID": "Test2"}}])
{ "result" : [ ], "ok" : 1 }
> db.MyCollection.aggregate([{$match: {"USER.UserID": "Test2"}}])
{
"result" : [
{
"_id" : ObjectId("512bc962e835e68f199c8687"),
"author" : "dave",
"score" : 85,
"USER" : {
"UserID" : "Test2",
"UserName" : "XYZ"
}
}
],
"ok" : 1
}
So the full aggregation would be:
db.MyCollection.aggregate([
{$match: {"USER.UserID": "Test2"}},
{$group: {"_id": "$_id"}},
{$project: {"_id": 1}}
])
(You don't need the extra $project as you only project _id in the $group but equally as _id is unique you should just have the $project and remove the $group)

Resources