Full-Text Search in Node JS with Mongoose - javascript

I'm trying to perform a full-text search on an array of strings in Mongoose and I am getting this error:
{ [MongoError: text index required for $text query]
name: 'MongoError',
message: 'text index required for $text query',
waitedMS: 0,
ok: 0,
errmsg: 'text index required for $text query',
code: 27 }
However, I do have a text index declared on the field on the User Schema and I confirmed that the text index has been created because I am using mLab.
I am trying to perform a full-text search on fields
Here Is My User Schema:
var userSchema = mongoose.Schema({
local: {
firstName: String,
lastName: String,
username: String,
password: String,
fields: {type: [String], index: true}
}
});
Here is My Code for the Full-Text Search:
User.find({$text: {$search: search}}, function (err, results) {
if (err) {
console.log(err);
} else {
console.log(results);
}
});

For $text queries to work, MongoDB needs to index the field with a text index. To create this index by mongoose use
fields: {type: [String], text: true}
See here for the MongoDB documentation of text indexes.

You need to add a text index to your schema like below:
userSchema.index({fields: 'text'});
Or use userSchema.index({'$**': 'text'}); if you want to include all string fields

If for some reason adding a test index is not an option, you could also use the regex operator in your aggregation pipeline to match strings.
$regex
Provides regular expression capabilities for pattern matching strings
in queries.
MongoDB uses Perl compatible regular expressions (i.e.
“PCRE” ) version 8.42 with UTF-8 support.
To use $regex, use one of
the following syntaxes:
{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
{ <field>: { $regex: /pattern/<options> } }
In MongoDB, you can also use regular expression objects (i.e.
/pattern/) to specify regular expressions:
{ <field>: /pattern/<options> }

Related

Mongoose - Deleting documents is unresponsive

I'm trying to use Mongoose (MongoDB JS library) to create a basic database, but I can't figure out how to delete the documents / items, I'm not sure what the technical term for them is.
Everything seems to work fine, when I use Item.findById(result[i].id), it returns a valid id of the item, but when I use Item.findByIdAndDelete(result[i].id), the function doesn't seem to start at all.
This is a snippet the code that I have: (Sorry in advance for bad indentation)
const testSchema = new schema({
item: {
type: String,
required: true
},
detail: {
type: String,
required: true
},
quantity: {
type: String,
required: true
}
})
const Item = mongoose.model("testitems", testSchema)
Item.find()
.then((result) => {
for (i in result) {
Item.findByIdAndDelete(result[i].id), function(err, result) {
if (err) {
console.log(err)
}
else {
console.log("Deleted " + result)
}
}
}
mongoose.connection.close()
})
.catch((err) => {
console.log(err)
})
I'm not sure what I'm doing wrong, and I haven't been able to find anything on the internet.
Any help is appreciated, thanks.
_id is a special field on MongoDB documents that by default is the type ObjectId. Mongoose creates this field for you automatically. So a sample document in your testitems collection might look like:
{
_id: ObjectId("..."),
item: "xxx",
detail: "yyy",
quantity: "zzz"
}
However, you retrieve this value with id. The reason you get a value back even though the field is called _id is because Mongoose creates a virtual getter for id:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
The key takeaway is that when you get this value with id it is a string, not an ObjectId. Because the types don't match, MongoDB will not delete anything.
To make sure the values and types match, you should use result[i]._id.

Filtering a query using Node JS and MongoDB

I am new to MangoDB and Node JS. I have always worked on SQL databases and I do not know the syntax of MongoDB well. I wanna try to filter the array that I receive from a MongoDB database. I know that JavaScript has a .filter() function to filter just the results that contain a string. Is it best practice to get all the objects from MongoDB and filter in Node or do I let MongoDB do the filtering?
My Node.JS project is a back-end project using Node.JS and Express to do CRUD operations on a MongoDB database. In the request I send a parameter called 'equalTo' that contains the value that should be filtered on.
var router = express.Router();
var Plot = require("./models/plot");
...
router.get("/plots", (req, res) => {
let query = "" + req.query.equalTo;
Plot.find((err, plots) => {
if (err) {
res.send(err);
}
res.json(plots);
});
});
The filtering should be an OR filter where all results where either the name or the cropName should CONTAIN the value of the string. If it is possible I would also like the comparison to ignore uppercase's. Here is a schema for the Plot object:
const plotSchema = mongoose.Schema({
area: {
type: String,
required: true
},
comments: {
type: String,
required: true
},
cropGroupName: {
type: String,
required: true
},
cropName: {
type: String,
required: true
},
name: {
type: String,
required: true
},
plotId: {
type: Number,
required: true
},
coords: {
type: [],
required: true
},
}, {
collection: "plots"
});
The format is the following:
Plot.find({$or:[{name: "anyname"},{cropName:"othername"}]})
For further information you can read here https://docs.mongodb.com/manual/reference/operator/query/or/
You may replace the strings above in your case with equalTo.

Mongoose/Mongodb: Exclude fields from populated query data

I use the following mongoose query in a MEAN-environment to find and output a particular author and his corresponding books.
Author
.findOne({personcode: code})
.select('-_id')
.select('-__v')
.populate('bookids') //referencing to book documents in another collection (->array of bookids)
.select('-_id') //this doens't affect the data coming from the bookids-documents
.select('-__v') //this doens't affect the data coming from the bookids-documents
.exec(function (err, data) {
//foo
});
I would also like to exclude the "_id" and "__v" fields from the populated data coming from the external documents. How can that be achieved?
The second parameter of populate is a field selection string, so you can do this as:
Author
.findOne({personcode: code})
.select('-_id -__v')
.populate('bookids', '-_id -__v')
.exec(function (err, data) {
//foo
});
Note that you should combine your field selections into a single string.
Thanks JohnnyHK, and for object parameter this works:
Entity.populate({
path: 'bookids',
// some other properties
match: {
active: true
},
// some other properties
select: '-_id -__v' // <-- this is the way
}).then(...) // etc
To exclude individually
User.findOne({_id: userId}).select("-password")
To exclude using the schema
var userSchema = mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
select: false,
},
});
or this will also work
db.collection.find({},{"field_req" : 1,"field_exclude":0});
I came searching for something slightly different. just in case someone needs same as me.
you can specify specific fields to auto-populate during creation of schema as shown below
const randomSchema = mongoose.Schema({
name: {type: String,trim: true},
username: {type: String,trim: true},
enemies: {
type: ObjectId,
ref: randomMongooseModel,
autopopulate:{
select: '-password -randonSensitiveField' // remove listed fields from selection
}
},
friends: {
type: ObjectId,
ref: randomMongooseModel,
autopopulate:{
select: '_id name email username' // select only listed fields
}
}
});
I am using mongoose-autopopulate plugin for this example

Query with OR conditions, mongodb/mognoose

This is (a part) of my model:
var materialSchema = new Schema({
ownerType: { type: String, required: true},
organization: {
type: Schema.ObjectId,
ref: 'organization'
},
user: {
type: Schema.ObjectId,
ref: 'users'
},
});
I want to make a query that returns:
ownerType = 'public'
organization = 321
The condition are 'OR'. So the material should be either ownerType 'public' or organization 321.
Can not find this in the docs. Do I need to make nested queries with "find" to do this? Or can it be done with a single query?
Some pseudo code:
mongoose.model('material').find({ownerType:'public' || organization:321}, function(err,materials){
...
}
Well presuming that your actual "Model" is named Material then you would come out to something like this in MongoDB parlance:
Material.find(
{
"$or": [
{ "ownerType": "public" },
{ "orginization._id": 123 }
]
},
function(err,docs) {
// results in here
}
);
So MongoDB has an $or operator, which makes sense since the query operands are represented in BSON ( from JSON conversion in the JavaScript case ). The purpose presents an "array" of possible arguments which are evaluated in a "short circuit" manner to determine if either case results in a true condition to match your query criteria.

Recreate models using Mongoose

I want to recreate the models in database after dropping everything in it.
Mongoose (or Mongo itself )actually recreates the documents but not the indices. So is there a way to reset Mongoose so that it can recreate indices as if running the first time?
The reason why I'm using dropDatabase is because it seems easier while testing. Otherwise I would have to remove all collections one by one.
While not recommended for production use, depending on your scenario, you can add the index property to a field definition to specify you want an index created:
var animalSchema = new Schema({
name: String,
type: String,
tags: { type: [String], index: true } // field level
});
animalSchema.index({ name: 1, type: -1 }); // schema level
Or,
var s = new Schema({ name: { type: String, sparse: true })
Schema.path('name').index({ sparse: true });
Or, you can call ensureIndex on the Model (docs):
Animal.ensureIndexes(function (err) {
if (err) return handleError(err);
});

Categories