I'm trying to query indexes, but I receive an empty array. I can't find what's wrong with my code. I used two methods to create the index: 1) VideoSchema.index() and 2) in the schema itself, both of them don't work. I checked the mongodb and it seems that indexes are created correctly, so I don't know what I do wrong.
const mongoose = require("mongoose");
const VideoSchema = mongoose.Schema(
{
user: {
type: mongoose.ObjectId,
required: true,
ref: "user",
},
title: {
type: String,
maxLength: 100,
text: true,
},
description: {
type: String,
text: true,
},
publishDate: {
type: Date,
},
views: {
type: Number,
default: 0,
},
likes: {
type: Number,
default: 0,
},
dislikes: {
type: Number,
default: 0,
},
comments: [
{
type: mongoose.ObjectId,
ref: "comment",
},
],
urls: {
video_url: {
type: String,
required: true,
},
thumbnail_url: {
type: String,
},
preview_url: {
type: String,
required: true,
},
},
private: {
type: Boolean,
default: 0,
},
category: {
type: String,
default: "",
},
duration: {
type: Number,
required: true,
},
},
{ timestamps: true }
);
// VideoSchema.index({ title: "text", description: "text" });
// export model user with UserSchema
module.exports = mongoose.model("video", VideoSchema);
The query:
const express = require("express");
const router = express.Router();
const Video = require("../model/Video");
router.post("/", (req, res) => {
const query = req.body.query;
Video.find({ $text: { $search: query } }, { score: { $meta: "textScore" } })
.sort({ score: { $meta: "textScore" } })
.exec(function (error, results) {
if (error) return res.status(400).send(error);
res.status(200).json({ results });
});
});
module.exports = router;
As you are fetching data from your Database it´s a good practice and makes the code clearer if you use the 'GET' method. If you do so, there is no need to add the score option to the query since V.4.4
const express = require("express");
const router = express.Router();
const Video = require("../model/Video");
router.get("/", (req, res) => {
const query = req.query.YOUR_QUERY_PARAMETER;
Video.find({ $text: { $search: query }})
.sort({ score: { $meta: "textScore" } })
.exec(function (error, results) {
if (error) return res.status(400).send(error);
res.status(200).json({ results });
});
});
module.exports = router;
If the problem persists:
Try to add the wild card text indexing to see if the problem is within it as follows:
VideoSchema.index({'$**': 'text'});
If so, then drop the collection for a fresh start on the indexing and then append your text indexes like this:
VideoSchema.index({ title: "text", description: "text" });
Create new dummy items and then check again.
Make sure you read the exceptions shown in the MongoDB documentation:
https://docs.mongodb.com/manual/reference/operator/query/text/
It seems that I resolved the problem. I noticed that in the express js the 'query' keyword is used for 'get' request params, so I decided to change this variable to 'search', so now it is like underneath and it is working!
router.get("/", (req, res) => {
const { search } = req.query;
Video.find(
{ $text: { $search: search } },
{ score: { $meta: "textScore" } }
)
.sort({ score: { $meta: "textScore" } })
.exec(function (error, results) {
if (error) return res.status(400).send(error);
res.status(200).json({ results });
});
});
But I've noticed that I'm getting only one video instead of two that contains the 'obs' in the title, so now I will need to deal with that.
Thank you so much for your time and effort!
Related
I´m rather new to this..
If I dont want the user to be able to add duplicated countries to visitedCountry, shoulden unique true work?
Or are there any easy way to block that in the patch?
const User = mongoose.model('User', {
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString('hex')
},
visitedCountries:[ {
country: {
type: Object,
ref: "Country",
unique: true
},
comments: String
}]
})
app.patch('/countries', authenticateUser)
app.patch('/countries', async (req, res) => {
const { username, visitedCountry } = req.body
try {
const countryByAlphaCode = await Country.findOne({ alphaCode: visitedCountry }).lean()
const updatedUser = await User.findOneAndUpdate({ username: username, }, {
$push: {
visitedCountries: { country: countryByAlphaCode, comments: "no comments yet"}
},
}, { new: true })
res.json({ success: true, updatedUser })
} catch (error) {
res.status(400).json({ success: false, message: "Invalid request", error })
}
})
The options unique works for all documents. It prevents two (or more) documents from having the same value for your indexed field. It's often used for the email or username.
For your case, I recommend you to perform a check on the user data before you call findOneAndUpdate.
I am following the docs without luck and am at a standstill while trying to update an object in an object in an array using MongoDB and Mongoose.
Here is my document:
{
fields: [
{ id: 603d63086db2db00ab09f50f, data: [Object] },
{ id: 603d63086db2db00ab09f510, data: [Object] },
{ id: 603d63086db2db00ab09f511, data: [Object] },
{ id: 603d63086db2db00ab09f512, data: [Object] },
{ id: 603d63086db2db00ab09f513, data: [Object] },
{ id: 603d63086db2db00ab09f514, data: [Object] },
{ id: 603d63086db2db00ab09f515, data: [Object] }
],
layouts: [],
_id: 603d631a6db2db00ab09f517,
bandId: '603d63146db2db00ab09f516',
eventType: 'private',
ownerId: '6039354906410800c14934c1',
__v: 0
}
I am trying to updateOne of the fields.data in the fields array. fields.data is an object as well.
I call my Express/Node Backend to this route.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.updateOne(
{ _id: req.params.id, "fields.id": req.body.id },
{
"$set": {
"fields.$.data": req.body.data,
},
}
);
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
Where req.body is:
{ id: '603d63086db2db00ab09f50f', data: { type: 1, rate: '200.30' } }
**Just in case it's helpful, here is what one of the fields objects looks like in the document,
{"id":"603d63086db2db00ab09f50f","data":{"type":1,"rate":300}}
I have even tried changing my route to find this document - which I have confirmed exists - Truly at a loss why it won't find the document.
Here is how I changed the above route to find the document.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.find(
{ _id: req.params.id, "fields.id": req.body.id },
);
console.log(updating) //returns []
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
The Model
//Create Schema - QG
const QuoteGeneratorSchema = new Schema({
bandId: {
type: String,
required: true,
},
ownerId: {
type: String,
required: true,
},
fields: {
type: Array,
default: defaultFields,
required: true,
},
eventType: {
type: String,
required: false,
},
layouts: {
type: Array,
required: false,
},
});
let QuoteGenerator = mongoose.model("QuoteGenerator", QuoteGeneratorSchema);
module.exports = QuoteGenerator;
Any nudge in the right direction to replacing that data object with a new data object would be extremely helpful! Thanks!
Listing Schema:
const mongoose = require('mongoose');
const listingSchema = new mongoose.Schema({
title: String,
name: String,
tel: String,
service: String,
description: String,
location: Object,
isAvailible: Boolean,
canTravel: Boolean,
distance: Number,
isPublic: { type: Boolean, default: true},
pro: { type: mongoose.Types.ObjectId, ref: 'User' }
}, { timestamps: true });
const Listing = mongoose.model('Listing', listingSchema);
module.exports = Listing;
Request to DB:
Listing.find({ 'title': { '$regex' : service, '$options' : 'i' } , isPublic: { $gte: true }}, async (err, listings) => {
if (err) { return next(err); }
await listings[0].populate('pro');
console.log(listings[0].pro);
res.render('search', {
title: 'Search',
listings: listings,
search: {
service: service,
zip: zip
}
});
});
Screenshot of console
I'm also curious what is the best way to populate an array of models, however, I can't even get it to populate one. Any thoughts?
can you please tye execPopulate() method
try below code
Listing.find({ 'title': { '$regex' : service, '$options' : 'i' } , isPublic: { $gte: true }}, async (err, listings) => {
if (err) { return next(err); }
const listing=await listings[0].populate('pro').execPopulate();
console.log(listing.pro);
res.render('search', {
title: 'Search',
listings: listing,
search: {
service: service,
zip: zip
}
});
});
assignment to constant variable before the populate may work like so:
Listing.find({ 'title': { '$regex' : service, '$options' : 'i' } , isPublic: { $gte: true }}, async (err, listings) => {
if (err) { return next(err); }
const listings = await listings[0].populate('pro');
console.log(listings.pro);
res.render('search', {
title: 'Search',
listings: listings,
search: {
service: service,
zip: zip
}
});
});
This question already has answers here:
Node.js Mongoose.js string to ObjectId function
(9 answers)
Closed 4 years ago.
I have an array of ids which is launchIds.
I'm trying to push it on a model field trips with
$addToSet: { trips: { $each: launchIds }. This gives me an error: Cast to [ObjectId] failed for value \"[\"1\",\"2\",\"3\"]\...
if I try to map through launchIds and convert to Mongoose.Shema.Types.ObjectId I get in the database trips: [null,null,null]
lauchIds = ['1','2','3']
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const mongoIds = launchIds.map(l => Mongoose.Schema.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$addToSet: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Here's my model Schema:
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
I'm passing ids via grapql playground. Here's my mutation:
bookTrips: async (_, { launchIds }, { dataSources }) => {
console.log(launchIds);
// logs ['1','2','3']
console.log(typeof launchIds);
//Object
const results = await dataSources.userAPI.bookTrips({ launchIds });
console.log(results);
return { message: "hello" };
}
To convert a string or a number into mongo object use Mongoose.Types.ObjectId,
const mongoIds = launchIds.map(l => Mongoose.Types.ObjectId(l));
I was getting back an array of strings where this should be numbers
The solution:
My model (same as above):
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
crud API:
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const idsToNums = launchIds.map(Number);
const mongoIds = idsToNums.map(l => Mongoose.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$push: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Notice the Mongoose.Schema.Types.ObjectId on model and Mongoose.Types.ObjectId on api. If I remove Schema from model or add Schema to api I'm getting an error. Not sure why, but the above example works. I hope someone will find this helpful or suggests a better solution.
When I save a new "experience" document with the model Experience, the experience _id is not saved into the document of the user. So my "experiences" array in the user document remains empty. Why?
const mongoose = require('mongoose');
const ExperienceSchema = mongoose.Schema({
name: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
reviews: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Review' }],
categories: [{ type: String }],
});
module.exports = mongoose.model('Experience', ExperienceSchema);
==============================================
const mongoose = require('mongoose');
const UserSchema = mongoose.Schema({
name: String,
experiences: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Experience' }],
});
module.exports = mongoose.model('User', UserSchema);
=============================================
// Update experience to database
router.post('/:id', (req, res, next) => {
const idexp = req.params.id;
const newExperience = {
name: req.body.name,
user: req.user._id,
};
Experience.findOneAndUpdate({ _id: idexp }, newExperience, (err, result) => {
if (err) {
return res.render(`/${idexp}/edit`, { errors: newExperience.errors });
}
return res.redirect(`/experiences/${idexp}`);
});
});
The experiences is the sub-document of user schema. So, when you save experiences, the user will not be saved. However, when you save user, the experience should be saved.
Refer this subdocs documentation
Here is the solution... I needed to use $push to update the user document with the experience id before rendering the site.
Experience.findOneAndUpdate({ _id: idexp }, newExperience, (err, result) => {
if (err) {
return res.render('experiences/edit', { errors: newExperience.errors });
}
User.findByIdAndUpdate({ _id: req.session.passport.user._id }, { $push: { experiences: idexp } }, (err) => {
if (err) {
next(err);
} else {
return res.redirect(`/experiences/${idexp}`);
}
});
});