Doing partial search with mongoose - node.js

I'm trying to get Mongoose to return results in a query when I only give a partial query. For example: I have a 'Company' schema that lists a bunch of companies. A document example:
{
"_id" : ObjectId("57aabeb80057405968de1539"),
"companyName" : "Vandelay Industries",
"owner" : "Ary Vandelay",
"inception" : 2012,
"__v" : 1
}
So if I do a search query like this:
Company.findOne(
{ companyName: Vandelay Industries }, function (err, company) {
if (company) {
//do stuff
}
});
This will produce the document. But If I do the following, I won't get a result:
Company.findOne(
{ companyName: Vandelay }, function (err, company) {
if (company) {
//do stuff
}
});
I would like to be able to do these sorts of partial searches and still get a result. Is there any way to do that with Mongoose?

In order to achieve this you can use a regex search to get the required result.
var searchKey = new RegExp('Vandelay', 'i')
Company.findOne({ companyName: searchKey }, function (err, company) {
if (company) {
//do stuff
}
});
Refer this stackoverflow post.

You can use this query to get result on specific value
Company.findOne({"companyName": /Vandelay/},function(err,company){
if(!err){
console.log(company);
}
});

To get result faster you should use indexing ref https://docs.mongodb.com/manual/text-search/.
db.Company.createIndex( { companyName: "text" } )
then you can search
db.Company.find( { $text: { $search: "company name" } } )
But this only support full word search not partial, so adding an extra line to this will help
db.Company.find({ $or: [
{ $text: { $search: query } },
{ companyName: { $regex: '^' + 'copmany name'} }
]}
This will help you search the results faster than normal

Are you doing a fulltext search? If you do that:
TagGroup.find({
$text: {
$search: text
}
}, {
score: {
$meta: "textScore"
}
}).sort({
score: {
$meta: 'textScore'
}
})
Try that code below. Also you need create a index on that schema
TagGroupSchema.index({
"$**": "text"
});
Here is the document
You can use elasticsearch to do that either, when the documents grows, you should consider this way.

Related

Mongoose full text search not filtering correctly

So basically i have model with a bunch of string fields like so:
const Schema: Schema = new Schema(
{
title: {
type: String,
trim: true
},
description: {
type: String,
trim: true
},
...
}
);
Schema.index({ '$**': 'text' });
export default mongoose.model('Watch', Schema);
where I index all of them.
Now when I search being that this schema is used as a ref for another model I do a search like this where user is an instance of the other model
const { search, limit = 5 } = req.query;
const query = search && { match: { $text: { $search: new RegExp(search, 'i') } } };
const { schemaRes } = await user
.populate({
path: 'schema',
...query,
options: {
limit
}
})
.execPopulate();
and the searching itself seems to work ok, the problem is when search fields starts to be more specific it seems to me the it does not regard it well.
Example
db
{ title: 'Rolex', name: 'Submariner', description: 'Nice' }
{ title: 'Rolex', name: 'Air-King', description: 'Nice' }
When the search param is Rolex I get both items which is ok but when the search param becomes Rolex Air-King i keep on getting both items which to me is not ok because I would rather get only one.
Is there something I could do to achieve this?
Returning both items is correct, since both items match your search params, but with different similarity score.
You can output the similarity score to help sorting the result.
user.aggregate([
{ $match: { $text: { $search: "Rolex Air-King" } } },
{ $set: { score: { $meta: "textScore" } } }
])
// new RegExp("Rolex Air-King", 'i') is not necessary and even invalid,
// as $search accepts string and is already case-insensitive by default
The query will return
[{
"_id": "...",
"title": "Rolex",
"name": "Air-King",
"description": "Nice",
"score": 2.6
},
{
"_id": "....",
"title": "Rolex",
"name": "Submariner",
"description": "Nice",
"score": 1.1
}]
Since the second result item matches your search query (even partially), MongoDB returns it.
You could use the score to help sort the items. But determining the right threshold to filter the result is complex, as the score depends on the word count as well.
On a side note: You can assign different weights to the fields if they are not equally important
https://docs.mongodb.com/manual/tutorial/control-results-of-text-search/

search mongoDB by 2 fields

I have been looking at some other answers on stack overflow and got as far as I could with that. I learnt that I need to create a text index where I define my Schema which I did like this:
productSchema.index({'title': 'text', 'address.city': 'text'});
If I search by just 1 field, ie: title then I get results as I expect.
Product.find( { $text: { $search: searchTerm } } )
But there is something wrong with my query when trying to search by title and city together.
Product.find( { $text: { $search: searchTerm }, $text: { $search: city } } )
I see no error but I get no results even though I know there should be results for my query. I am not sure if it is because address is an object (according to what I see in Compass)
This is how I defined it in my schema using Mongoose
address: {
city: {type: String, required: true }
},
If I do this:
Product.find().and([{ title: searchTerm }, { 'address.city': city }])
it almost works. But I have to type in the exact title of the product. If the product is called "a rubber duck" and I type in "duck" I get no results. If I type in "a rubber duck" and select the city it is listed in I get back a result.
I have just also tried this:
Product.find( { $and: [ {$text: { $search: searchTerm }}, { address: {city : city } } ] } )
Which seems to work but could probably be improved upon!
Have you looked into the link below? https://docs.mongodb.com/manual/reference/operator/query/or/
There you can find how to add multiple expressions in same query.
If you want to search for titles where title might be equal to A or to B use a query like this: db.inventory.find( { $or: [ { title: "A" }, { title: "B"} ] } ).
Edit If you need data from db to match both expressions in query then use something like this:
Product.find( { $and: [{ address:{city: "CityName"} }, { address: {country : "UK" } } ] } )

Mongoose: update an element of an array of a specific document

I am having a collection of documents called 'company'.
company 1 -
{
_id: '1',
data:[
{_id:'11', value: 'emp11'},
{_id:'12', value: 'emp12'}
]
}
company 2-
{
_id: '2',
data:[
{_id:'21', value: 'emp21'},
{_id:'22', value: 'emp22'}
]
}
Now I want to update value 'emp11' to 'emp99'.
I'm following this approach-
companyModel.findById('1', function(err, company) {
return company.data.update(
{_id: '11'},
{$set: {value: 'emp99'}}
);
});
I'm able to get the company but after that it's showing an error-
company.data.update is not a function
Please suggest a possible solution.
companyModel.update(
{ "_id" : :"1", "data._id": "11" },
{ "$set": { "data.$.value": "emp99" }},
function(err, company) {
console.log(company)
})
There is no need to use findById, you can use this directly.
In mongo update you need to search for both the document and the field you want to update.
The field you want to update is essential as it is used to locate the position of the array when you use $.
However you can do it as following also :
companyModel.findById('1', function(err, company) {
for(var i =0; i< company.data.length; i++)
if(company.data._id === "11")
break;
if(i<company.data.length)
company.data[i].value = 'emp99'
company.save()
});
db.company.update({
_id: "1",
data: {
$elemMatch: {
value: 'emp11'
}
}
}, {
$set: {
'data.$.value': 'emp99'
}
})

How to $match multiple values for MongoDB Mongoose query

I am trying to find specific fields for more than one value. For example, I have a database with different countries and I am trying to retrieve their name, year, and nominalGDP (renamed to y in the result for some other important reason). It works perfect for this example, where I am only retrieving from USA, but how would I add another country like China or whatever?
Country.aggregate([
{
$match: {
name: "USA"
}
},
{
$project: {
_id: 0,
name: 1,
year: 1,
'y' : '$nominalGDP'
}
}
], function(err, recs){
if(err){
console.log(err);
} else {
console.log(recs);
}
});
This is probably really simple but I have not been able to find out how.
Use $in operator to specify more than one matching option. For example:
{
$match: {
name: { $in: [ "USA", "China" ] }
}
}
`const results = await SchemaName.find([{$match:{name:{$in:["USA","China"]}}}])
res.status(200).json(results);`
but if you are getting the country names from frontend through the body or something then:
` const allCountries = req.body.allCountries;
var result;
for(let i=0; i < allCountries.length; i++){
result = await SchemaName.find([{$match:{name:{$in:allCountries[i]}}}])
}
`
assuming you are building asynchronous functions...

How to use $regex in mongodb aggregation query within $match

I am trying to use the $regex within $match, its not returning the matching documents.
db.collection('MyCollection', function (err, collection) {
collection.aggregate([
{ $match: { 'Code': 'Value_01', 'Field2': { $regex: '/Value_2/g' } } },
{ $project: {
_id: 1,
CodeNumber: '$Code',
FieldName2: '$Field2'
}
}
], function (err, Result_doc) {
console.log(Result_doc);
}
});
Can anyone tell me where its going wrong or the correct syntax?
I even tried with replacing the
'Field2': { $regex: /Value_2/g }
As it says in the $regex docs you linked to, the two ways to do this are:
Field2: /Value_2/g
OR
Field2: { $regex: 'Value_2', $options: 'g' }
But I also tried your second attempt of 'Field2': { $regex: /Value_2/g } and that worked as well.
BTW, the g regex option doesn't make sense in this context as you just need one match anyway. Note that it isn't even listed in the $regex docs.
I got it working with the following code:
var Value_match = new RegExp('Value_2');
db.collection('MyCollection', function (err, collection) {
collection.aggregate([
{ $match: { Code: 'Value_01', Field2: { $regex: Value_match } } },
{ $project: {
_id: 1,
CodeNumber: '$Code',
FieldName2: '$Field2'
}
}
], function (err, Result_doc) {
console.log(Result_doc);
}
});
On pushing the object content to console using console.dir(Value_match) it prints out '/Value_2/'

Resources