Mongoose find unique tags - node.js

Currently saving an array of tags for each user. The issue I'm running into is how to search for unique tags (so I can do a type ahead for adding new users) Right now if I get a match, the function returns the WHOLE tags array that contain the tag fragment rather than just the individual tag of the user that matches.
How do I specify that I only want to return unique title values rather than the whole tag array?
var UserSchema = new Schema({
username: {
type: String,
trim: true
},
tags:[
{
title:String
}
]
});
exports.searchByTag = function(req, res) {
var tag = req.params.tag;
User.find({'tags.title':{ $regex: tag }}).distinct('tags.title', function(error, tags) {
res.json(tags);
});

Kept cracking & figured it out. This solution uses the lodash _.filter method.
searchTerm = 'abc'
//search the database for unique user.tag.title
User.find().distinct('tags.title', function(error, tagTitles) {
var results = _.filter(tagTitles, function(title){
//use lodash to filter out the the tagTitles array built by mongoose
//return = push the item into the results array based on regex match of searchTerm
return title.match(searchTerm);
});
//respond with the results array
res.json(results);
});

Related

How to add object to nested array in mongoose?

In essence what I am trying to do is something along the lines of FindByIdAndCreate, a method which does not exist in mongoose.
I have a schema as so:
const WordSchema = new Schema ({
TargetWord: String,
Translation: String,
ExampleSentences: [{
Number: Number, //increment somehow each time
Sentence: String,
}],
});
I have a form where the user can add example sentences of this target word, the route for which looks like this:
router.put("/word/:id/add", async(req, res) => {
//get the new sentence from the field
var NewSentence = req.body.Sentence;
Now once I have this new sentence saved to the variable NewSentence I want to create a new object within the WordSchema.ExampleSentences array which contains the new sentences itself, and the number which should automatically increment.
I have fiddled around with FindByIdAndUpdate to no avail,this syntax does not work because it throws an error at the use of .
WordSchema.findByIdAndUpdate(req.params.id, {ExampleSentences.Sentence: NewSentence}, ...
The only solution to increment your counter is to retrieve every document with a good old 'find' and create your new entry accordingly, since 'update' has no way to self reference a document during the process.
router.put("/word/:id/add", async(req, res) => {
WordSchema.find({_id: req.body.id}, function(results) {
if (results.length === 0) return res.json();
const word = result[0];
const NewExampleSentence = {
Number: word.ExampleSentences.length, // if your counter start from 1, then add 1
Sentence: req.body.Sentence
};
word.ExampleSentences.push(NewExampleSentence);
word.save(function(err) {
if (err) {
// handle error
}
return res.json();
})
}
})

Mongoose can't search by number field

I have a schema that has an id field that is set to a string. When I use collection.find({id: somenumber}) it returns nothing.
I've tried casting somenumber to a string and to a number. I've tried sending somenumber through as a regex. I've tried putting id in quotes and bare... I have no idea what's going on. Any help and input would be appreciated.
Toys.js
var Schema = mongoose.Schema;
var toySchema = new Schema( {
id: {type: String, required: true, unique: true},
name: {type: String, required: true},
price: Number
} );
My index.js is as such
app.use('/findToy', (req, res) => {
let query = {};
if (req.query.id)
query.id = req.query.id;
console.log(query);
// I've tried using the query variable and explicitly stating the object as below. Neither works.
Toy.find({id: '123'}, (err, toy) => {
if (!err) {
console.log("i'm right here, no errors and nothing in the query");
res.json(toy);
}
else {
console.log(err);
res.json({})
}
})
I know that there is a Toy in my mongoDB instance with id: '123'. If I do Toy.find() it returns:
[{"_id":"5bb7d8e4a620efb05cb407d2","id":"123","name":"Dog chew toy","price":10.99},
{"_id":"5bb7d8f7a620efb05cb407d3","id":"456","name":"Dog pillow","price":25.99}]
I'm at a complete loss, really.
This is what you're looking for. Visit the link for references, but here's a little snippet.
For the sake of this example, let's have a static id, even though Mongo creates a dynamic one [ _id ]. Maybe that what is the problem here. If you already a record in your DB with that id, there's no need for adding it manually, especially not the already existing one. Anyways, Drop your DB collection, and try out this simple example:
// Search by ObjectId
const id = "123";
ToyModel.findById(id, (err, user) => {
if(err) {
// Handle your error here
} else {
// If that 'toy' was found do whatever you want with it :)
}
});
Also, a very similar API is findOne.
ToyModel.findOne({_id: id}, function (err, toy) { ... });

Modifying array directly in mongoose doesn´t works

I have a mongoose schema with a field array.
I need to set the array field directly without get rid off the existent values.
I am using
item.files.concat(myfiles);
in the next code, but it doesn´t work. Only the last item from array is saved
//code
var files=['file1','file2','file3']
var myfiles=[]
files.forEach(function(file){
myfiles.push({title:file});
}
});
//router
FileSchema.findById(id).exec( (err,item) => {
//fill files -- error is here
item.files.concat(myfiles);
item.save(function (err, data) {
if (err) {
return res.status(500).send(err)
}
res.send(data); //send response
})
})
//schema
const mongoose=require('mongoose');
const fileSchema = new mongoose.Schema({
id: {type: Number, unique:true},
...
...
files:[{title:{type: String}}]
});
From MDN web docs:
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
item.files = item.files.concat(myfiles);

How to remove mongo specific fields from result (NodeJS, Mongoose)

I want to remove all Mongo specific fields (like '_id') from query result. Is there a simple method to do this or should I remove fields manually? If yes, then which are that fields and how to do that?
I'm using NodeJS and Mongoose
You can use select() method for remove the field from your query:
Model.find({}).select("-removed_field").then (resp => {
// your code
});
You should specified the "-" before field name, to be remove this field.
If you want remove several fields - you can specified their as array:
Model.find({}).select(["-removed_field1", "-removed_field2" ... ]).then (resp => {
// your code
});
Also you can to select only specified fields, using this method without "-"
Model.find({}).select(["field1", "field2" ... ]).then (resp => {
// your code
});
If you want hide _id property you can use text argument with prefix - which will exclude this or that field from the result, for get sepecifict fields you should pass like this:
Entity.find({ ... }, 'field1 field2', function(err, entity) {
console.log(entity); // { field1: '...', field2: '...' }
});
You can specify a field to be excluded from results by using the optional 2nd parameter projection string of the find method:
Model.find({}, "-a -b").then (res => {
// objects in the res array will all have the
// 'a' and 'b' fields excluded.
});
https://mongoosejs.com/docs/api.html#model_Model.find (see projection)
you can use mongoose instance method two show specific fields from all documents
const userSchema = new mongoose.Schema({
email: {
type: String,
},
name: {
type: String,
maxlength: 128,
index: true,
trim: true,
},
});
userSchema.method({
transform() {
const transformed = {};
const fields = ['name', 'email'];
fields.forEach((field) => {
transformed[field] = this[field];
});
return transformed;
},
});
module.exports = mongoose.model('User', userSchema);
if You want to remove any specific fields like _id, You can try in two ways:
Suppose Here you try to find a user using User Model
User.find({ email: email }, { _id: 0 });
OR
const user = User.find({ email: email });
delete user._doc._id;
OP mentioned "from result", as far as I understood, it means, removing from the query result i.e. query result will contain the field, but will be removed from the query result.
A SO answer here mentions, that to modify a query result (which are immutable), we've to convert the result to Object using toObject() method (making it mutable).
To remove a field from a query result,
let immutableQueryResult = await Col.findById(idToBeSearched)
let mutableQueryResult = immutableQueryResult.toObject()
delete mutableQueryResult.fieldToBeRemoved
console.log(mutableQueryResult)
Another way of getting the mutable result is using the _doc property of the result:
let immutableQueryResult = await Col.findById(idToBeSearched)
let mutableQueryResult = immutableQueryResult._doc // _doc property holds the mutable object
delete mutableQueryResult.fieldToBeRemoved
console.log(mutableQueryResult)

Incorrect Subdocument Being Updated?

I've got a Schema with an array of subdocuments, I need to update just one of them. I do a findOne with the ID of the subdocument then cut down the response to just that subdocument at position 0 in the returned array.
No matter what I do, I can only get the first subdocument in the parent document to update, even when it should be the 2nd, 3rd, etc. Only the first gets updated no matter what. As far as I can tell it should be working, but I'm not a MongoDB or Mongoose expert, so I'm obviously wrong somewhere.
var template = req.params.template;
var page = req.params.page;
console.log('Template ID: ' + template);
db.Template.findOne({'pages._id': page}, {'pages.$': 1}, function (err, tmpl) {
console.log('Matched Template ID: ' + tmpl._id);
var pagePath = tmpl.pages[0].body;
if(req.body.file) {
tmpl.pages[0].background = req.body.filename;
tmpl.save(function (err, updTmpl) {
console.log(updTmpl);
if (err) console.log(err);
});
// db.Template.findOne(tmpl._id, function (err, tpl) {
// console.log('Additional Matched ID: ' + tmpl._id);
// console.log(tpl);
// tpl.pages[tmpl.pages[0].number].background = req.body.filename;
// tpl.save(function (err, updTmpl){
// if (err) console.log(err);
// });
// });
}
In the console, all of the ID's match up properly, and even when I return the updTmpl, it's saying that it's updated the proper record, even though its actually updated the first subdocument and not the one it's saying it has.
The schema just in case:
var envelopeSchema = new Schema({
background: String,
body: String
});
var pageSchema = new Schema({
background: String,
number: Number,
body: String
});
var templateSchema = new Schema({
name: { type: String, required: true, unique: true },
envelope: [envelopeSchema],
pagecount: Number,
pages: [pageSchema]
});
templateSchema.plugin(timestamps);
module.exports = mongoose.model("Template", templateSchema);
First, if you need req.body.file to be set in order for the update to execute I would recommend checking that before you run the query.
Also, is that a typo and req.body.file is supposed to be req.body.filename? I will assume it is for the example.
Additionally, and I have not done serious testing on this, but I believe your call will be more efficient if you specify your Template._id:
var template_id = req.params.template,
page_id = req.params.page;
if(req.body.filename){
db.Template.update({_id: template_id, 'pages._id': page_id},
{ $set: {'pages.$.background': req.body.filename} },
function(err, res){
if(err){
// err
} else {
// success
}
});
} else {
// return error / missing data
}
Mongoose doesn't understand documents returned with the positional projection operator. It always updates an array of subdocuments positionally, not by id. You may be interested in looking at the actual queries that mongoose is building - use mongoose.set('debug', true).
You'll have to either get the entire array, or build your own MongoDB query and go around mongoose. I would suggest the former; if pulling the entire array is going to cause performance issues, you're probably better off making each of the subdocuments a top-level document - documents that grow without bounds become problematic (at the very least because Mongo has a hard document size limit).
I'm not familiar with mongoose but the Mongo update query might be:
db.Template.update( { "pages._id": page }, { $set: { "pages.$.body" : body } } )

Resources