Full Text Search on mLab with mongoose: MongoError: text index required for $text query - mongodb

I'm trying to perform a full text search on a database on mLab. However, apparently the text indexes are not being created.
Mongoose version is 5.4.16
Schema:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var mySchema = new Schema({
title: {
type: String,
trim: true,
required: true
},
description: {
type: String,
trim: true
}
});
mySchema.index({
title: "text",
description: "text",
});
mongoose.model("Model", mySchema);
module.exports = mongoose.model("Model");
Use following to perform the search:
Model.find({
$text: { $search: query }
}).exec(function(err, codes) {...}```
I get the following error:
MongoError: text index required for $text query

I don't know why but I had the same problem with Mongoose and Mlab.
Manually I did the following to solve it.
1) Connect to mlab using the mongo shell
mongo ds245387.mlab.com:45387/<dbname> -u <dbuser> -p <dbpassword>
2) Run db.products.createIndex( { title: "text", "description": "text" } )
3) db.products.getIndexes() should return something like:
{
"v" : 2,
"key" : {
"_fts" : "text",
"_ftsx" : 1
},
"name" : "title_text_description_text",
"ns" : "database-name.products",
"weights" : {
"description" : 1,
"title" : 1
},
"default_language" : "english",
"language_override" : "language",
"textIndexVersion" : 3
}
Note: products is your collection name
4) Try your query again!
MongoDB text-search reference

Related

mongoose find collection by _id in a list of array

Here's my Schema
var PositiveSchema = new mongoose.Schema({
schoolID: {
type: mongoose.Schema.Types.ObjectId, ref: 'School'
},
name: String,
restrictAwardTo: Object
})
Now restrictAwardTo saves the data in this format
"restrictAwardTo" : [
{
"_id" : "5c31907d908eb8404657cbf0",
"firstName" : "Admin 2a"
},
{
"_id" : "5c1a7677c98da061141475a8",
"firstName" : "Justice"
},
{
"_id" : "5c1a7677c98da061141475a9",
"firstName" : "Itik"
}
],
How can I search inside my document using one of the _id listed under restrictAwardTo? I tried the solutions given below
mongooseJS find docs with IDs in an array
mongoose query: find an object by id in an array but it returns empty.
in Robo3t db.getCollection('positives').find({ 'restrictAwardTo._id' : ObjectId('5c31907d908eb8404657cbf0') })
Update: In Robo3t, this query db.getCollection('positives').find({ 'restrictAwardTo._id' : {$in: ['5c1a7677c98da061141475a7']} }) works. Now I'm making it work for mongoose too.
Here's the mongoose that works for me:
Positive.find({ schoolID: mongoose.mongo.ObjectId(schoolID), "restrictAwardTo._id": { $in: [userID]} })
But I'm not entirely sure of the performance for large records.
You could go through this way.
Positive.findOne({'restrictAwardTo': {$elemMatch: {_id: userID}}},
(err,schoolInfo) => { });

how to update one table with the _id from another table in Mongodb

I exported data from a MySQL database into JSON and imported it into MongoDB. The problem:
When I imported clients, MongoDB created its own _id field (I know this is built in functionality, but MySQL used a clientID, autoincrementing integer).
SO, when I imported my appointments collection, the clientID was renamed oldClientID. I'd like the clientID field to be the ObjectID of the corresponding client.
My schemas:
const apptSchema = new mongoose.Schema({
ID: Number,
clientID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Client'
},
oldClientID: Number,
...other field data
});
const clientSchema = new mongoose.Schema({
ID: Number,
familyID: Number,
first: String,
last: String,
});
Sample Patient Data:
{
"_id" : ObjectId("5d82240f7c8ddd03b62aee6a"),
"ID" : 18,
"familyID" : 6,
"first" : "Jane",
"last" : "Doe"
}
Sample Appointment Data
{
"_id" : ObjectId("5d82c8b95627367d122087f9"),
"ID" : 1885,
"oldPatientID" : 18,
"scheduled" : ISODate("2015-08-05T11:20:00Z"),
"note" : ""
},
{
"_id" : ObjectId("5d82c8b95627367d122088a8"),
"ID" : 2066,
"oldPatientID" : 18,
"scheduled" : ISODate("2015-09-17T16:00:00Z"),
"note" : ""
}
What appointments should look like:
{
"_id" : ObjectId("5d82c8b95627367d122087f9"),
"ID" : 1885,
"clientID": ObjectId("5d82240f7c8ddd03b62aee6a"),
"oldPatientID" : 18,
"scheduled" : ISODate("2015-08-05T11:20:00Z"),
"note" : ""
},
{
"_id" : ObjectId("5d82c8b95627367d122088a8"),
"ID" : 2066,
"clientID" : ObjectId("5d82240f7c8ddd03b62aee6a"),
"oldPatientID" : 18,
"scheduled" : ISODate("2015-09-17T16:00:00Z"),
"note" : ""
}
I am open to learning how to achieve this in the mongo shell or using mongoose in express (or if there is another cool way, like in Robo3T).
MongoDB will always use _id as the primary key, this behavior cannot be overwritten, though you can use the _id with values from your custom id. Though this might be confusing, it is better to use indexing on your custom id, and you don't need to use ObjectId for the custom index field, but can use your own custom id schema, like UUID or an incrementing integer value etc. though it has to be generated/incremented by you or some framework, like JPA
Check Indexes
For Mongoose, you can do;
new mongoose.Schema({
customId: { type: Number, index: true }
...other field data
});
with index: true
Ok, this worked out for me, although I'm sure there has to be an easier way:
db.getCollection("appts").aggregate(
[
{
"$lookup" : {
"from" : "clients",
"localField" : "clientID",
"foreignField" : "ID",
"as" : "CLIENT"
}
},
{
"$lookup" : {
"from" : "appttypes",
"localField" : "type",
"foreignField" : "ID",
"as" : "TYPE"
}
},
{
"$lookup" : {
"from" : "apptstatuses",
"localField" : "status",
"foreignField" : "ID",
"as" : "STATUS"
}
},
{
"$project" : {
"_id" : "$_id",
"clientID" : "$CLIENT._id",
"scheduled" : "$scheduled",
"note" : "$note",
}
},
{
"$out" : "apptslinked"
}
]
);
Then I exported that as JSON, dropped the appts table, and did a mongoimport using that file.

Cannot query nested document's _id (other fields work)

I want to find all documents where vendor._id has a certain value. Below is the code, I tried, but it returns nothing.
let name = sampleData.name, _id = sampleData._id
Product.find({"vendor._id":ObjectID(_id)}).then((products) => {
//returns empty array
})
With the same method I tried to query a different field and it works. But I want to query with _id because other fields could vary with time.
Product.find({"vendor.name":name}).then((products) => {
//returns all documents that satisfy the condition.
})
Below is a sample document which I want to find
{
"status" : "active",
"connectedFarms" : [
{
"_id" : "5c412c62bf8a6602f04ae0bf",
"status" : "inActive",
"margin" : 10,
"price" : 55
},
{
"_id" : "5c4567bcb3845b0536a4d92e",
"status" : "inActive",
"margin" : 20,
"price" : 60
},
{
"_id" : "5c4567c4b3845b0536a4d931",
"status" : "active",
"margin" : 7,
"price" : 53.5
}
],
"vendor" : {
"_id" : ObjectId("5c3fcc0c7657ee02ac24bc21"),
"name" : "manna"
}
}
And here is the schema for this document.
let ProductSchema = new mongoose.Schema({
vendor:{_id:String, name:String},
connectedFarms:[{_id:String, name:String, status:String, price:Number, margin:Number}],
status:{
type:String,
trim: true,
minlength:1
}
});
Let's take a different approach on this, and make vendor its own schema. Mongoose does not allow you to nest schemas, so you cannot make the vendor._id a true ObjectID.
Vendor Schema
const VendorSchema = new mongoose.Schema({
name: string
});
module.exports = mongoose.model('Vendor', VendorSchema);
Product Schema
const ProductSchema = new mongoose.Schema({
vendor: {
type: mongoose.Types.ObjectID,
ref: 'Vendor'
},
connectedFarms: [{
_id: String,
name: String,
status: String,
price: Number,
margin: Number
}],
status: {
type: String,
trim: true,
minlength: 1
}
});
module.exports = mongoose.model('Product', ProductSchema);
Now when you want to query a product based on the vendors _id, it's very simple! All you need to do is supply the _id of the vendor in the query. NOTE: There is no reason to convert the _id to an ObjectID in the query, as mongoose accepts strings and converts them later on.
Query
const vendorID = myVendor._id;
Product.find({ vendor: vendorID })
.then((products) => {
// Do something with the found products
});
That's it! Much simpler to do, and much cleaner in the database. The vendor field is now easier to reference. You also have the ability to get the full vendor object in a query if desired by populating in the query. The difference is, the population will return the vendor name and _id, rather than just the _id. To do this, run the following:
Product.find({ vendor: vendorID })
.populate('vendor')
.then((products) => {
// Do something with the populated found products
});

Mongoose Mongo 2dsphere geoWithin

After reading many questions that are SO close to mine, and reading the MongoDB docs and Mongoose docs, I still cannot answer my question.
Using express 4.13.4, mongoose 4.4.10, mongodb 2.1.14 on Node 4.4.0
My Mongoose Location schema:
var schema = new Schema({
type: {type: String},
coordinates: []
},{_id:false});
var model = mongoose.model('LocationModel',schema);
module.exports = {
model : model,
schema : schema
};
My CatalogModel schema (what I write to Mongo):
var locationSchema = require('./locationModel').schema;
var schema = new Schema({
title : String,
format: {type: String, maxlength: 4},
location: {type: locationSchema, required:true},
otherStuff: String
});
schema.index({location: '2dsphere'}); // Ensures 2dsphere index for location
model = mongoose.model('CatalogModel',schema);
I create a concrete example and write to MongoDB (this works fine... in that I can query it in Mongo)
var polyEntry = new CatalogModel({
title:"I am just a Polygon",
otherStuff: "More stuff here",
location:{
type:'Polygon',
coordinates:[[[0,1],[0,2],[1,2],[0,1]]]
}
});
In Mongo, I asked the collection for the indexes:
db.catalogmodels.getIndexes()
And this is what it says (not entirely sure what this means)
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.catalogmodels"
},
{
"v" : 1,
"key" : {
"location" : "2dsphere"
},
"name" : "location_2dsphere",
"ns" : "test.catalogmodels",
"background" : true,
"2dsphereIndexVersion" : 3
}
]
I can do a db.catalogmodels.find() and get my document back.
{
"_id" : ObjectId("12345678901234566778"),
"title" : "I am just a Polygon",
"location" : {
"type" : "Polygon",
"coordinates" : [ [ [ 0, 1 ], [ 0, 2 ], [ 1, 2 ], [ 0, 1 ] ] ]
},
"__v" : 0
}
I can even do a $geoWithin call in Mongo:
db.catalogmodels.find(
{
location:{
$geoWithin:{
$geometry:{
type:"Polygon",
"coordinates":[[[-1,0],[-1,3],[4,3],[4,0],[-1,0]]]
}
}
}
})
But here's the actual question:
Mongoose keeps telling me [Error: Can't use $geoWithin]
var geoJson = {
"type" : "Polygon",
"coordinates" : [[[-1,0],[-1,3],[4,3],[4,0],[-1,0]]]
};
CatalogModel
.find()
.where('location').within(geoJson)
.exec(function(err,data){
if ( err ) { console.log(err); }
else {console.log("Data: " + data);}
db.close()
});
I also replaced the .find().where().within() call to:
CatalogEntryModel.find({
location:{
$geoWithin:{
$geometry:{
type:"Polygon",
"coordinates":[[[-1,0],[-1,3],[4,3],[4,0],[-1,0]]]
}
}
}
})
.exec(function(err,data){
if ( err ) { console.log(err); }
else {console.log("Data: " + data);}
db.close();
});
Is there a reason Mongoose does not like the $geoWithin call? The latest API says this should work.
I wrote this up as an issue on Mongoose: https://github.com/Automattic/mongoose/issues/4044#
And it has been closed.

How to update a subdocument in mongodb

I know the question have been asked many times, but I can't figure out how to update a subdocument in mongo.
Here's my Schema:
// Schemas
var ContactSchema = new mongoose.Schema({
first: String,
last: String,
mobile: String,
home: String,
office: String,
email: String,
company: String,
description: String,
keywords: []
});
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
contacts: [ContactSchema]
});
My collection looks like this:
db.users.find({}).pretty()
{
"_id" : ObjectId("5500b5b8908520754a8c2420"),
"email" : "test#random.org",
"password" : "$2a$08$iqSTgtW27TLeBSUkqIV1SeyMyXlnbj/qavRWhIKn3O2qfHOybN9uu",
"__v" : 8,
"contacts" : [
{
"first" : "Jessica",
"last" : "Vento",
"_id" : ObjectId("550199b1fe544adf50bc291d"),
"keywords" : [ ]
},
{
"first" : "Tintin",
"last" : "Milou",
"_id" : ObjectId("550199c6fe544adf50bc291e"),
"keywords" : [ ]
}
]
}
Say I want to update subdocument of id 550199c6fe544adf50bc291e by doing:
db.users.update({_id: ObjectId("5500b5b8908520754a8c2420"), "contacts._id": ObjectId("550199c6fe544adf50bc291e")}, myNewDocument)
with myNewDocument like:
{ "_id" : ObjectId("550199b1fe544adf50bc291d"), "first" : "test" }
It returns an error:
db.users.update({_id: ObjectId("5500b5b8908520754a8c2420"), "contacts._id": ObjectId("550199c6fe544adf50bc291e")}, myNewdocument)
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 16837,
"errmsg" : "The _id field cannot be changed from {_id: ObjectId('5500b5b8908520754a8c2420')} to {_id: ObjectId('550199b1fe544adf50bc291d')}."
}
})
I understand that mongo tries to replace the parent document and not the subdocument, but in the end, I don't know how to update my subdocument.
You need to use the $ operator to update a subdocument in an array
Using contacts.$ will point mongoDB to update the relevant subdocument.
db.users.update({_id: ObjectId("5500b5b8908520754a8c2420"),
"contacts._id": ObjectId("550199c6fe544adf50bc291e")},
{"$set":{"contacts.$":myNewDocument}})
I am not sure why you are changing the _id of the subdocument. That is not advisable.
If you want to change a particular field of the subdocument use the contacts.$.<field_name> to update the particular field of the subdocument.