Search MongoDB autocomplete filtered - mongodb

I want to search autocomplete on the following fields :contactfirstname, contactlastname and name
Also, want to filter based on userid(s) first then perform autocomplete search
Issue:
Without filter criteria, autocomplete is working fine
With filter criteria in compound query not working as getting empty array
Can anyone help please?
exports.userNameCitySearchAutocomplete = async function (req, res) {
try {
const { userNameCityQueryparam } = req.query;
console.log("search query param", userNameCityQueryparam);
const agg = [
{
$search: {
index: 'userNameCity',
'compound': {
"filter": [{
"text": {
"query": ["6271f2bb79cd80194c81f631"],
"path": "_id",
}
}],
"should": [
{
//search on user name
autocomplete: {
query: userNameCityQueryparam,
path: 'name',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
}},
//search on user city
{
autocomplete: {
query: userNameCityQueryparam,
path: 'city',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
},
}
,
//search on user contact first name
{
autocomplete: {
query: userNameCityQueryparam,
path: 'contactfirstname',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
},
}
,
//search on user contact last name
{
autocomplete: {
query: userNameCityQueryparam,
path: 'contactlastname',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
},
}
],
"minimumShouldMatch": 1
}
}
}
]
const response = await User.aggregate(agg);
return res.json(response);
// res.send(response);
} catch (error) {
console.log("autocomplete search error", error);
return res.json([]);
}
};
Index details in mongodb:
{
"mappings": {
"dynamic": false,
"fields": {
"_id": {
"type": "string"
},
"city": {
"type": "autocomplete"
},
"contactfirstname": {
"type": "autocomplete"
},
"contactlastname": {
"type": "autocomplete"
},
"name": {
"type": "autocomplete"
}
}
}
}
Image of collection in mongodb
image of empty array

for anyone looking for solution to this,
You can use the MQL where clause in your pipeline stage definition.
{
$match: {
email:"email#domain.com"
}
}
check here for an example

Related

MongoDB update a specific nested field

Hi I am trying to update nested filed , but couldn't able to do so.
Here is the sample data,
[{
"_id": {
"$oid": "632ec4128f567511dcd80ed9"
},
"company_id": 1,
"contact_id": 1001,
"roles_to_be_accepted": {
"ROLE#04": {
"assigned_data": {
"assigned_3HFui": {
"is_idle": false,
"send_for_acceptance_date": 1664009233,
"action_date": ""
},
"assigned_b1J9t": {
"is_idle": false,
"send_for_acceptance_date": 1664009233,
"action_date": ""
}
}
},
"ROLE#02": {
"assigned_data": {
"assigned_uPJI1": {
"is_idle": false,
"send_for_acceptance_date": 1664009233,
"action_date": ""
}
}
}
}
}]
Now I want to update that is_idle field to true. I have tried in this way
let query = { contact_id: 1, company_id: 1001};
const db = this.client.db("dbname");
const col= db.collection("collection_name");
col.update(query, {
'$set': { "roles_to_be_accepted.assigned_data.is_idle": true }
});

Mongoose update only fields available in request body

I am trying to update one document using findOneAndUpdate and $set but I clearly missing something very crucial here because the new request is overwriting old values.
My Device schema looks like this:
{
deviceId: {
type: String,
immutable: true,
required: true,
},
version: {
type: String,
required: true,
},
deviceStatus: {
sensors: [
{
sensorId: {
type: String,
enum: ['value1', 'value2', 'value3'],
},
status: { type: Number, min: -1, max: 2 },
},
],
},
}
And I am trying to update the document using this piece of code:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{ $set: req.body },
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
And when I try to send a request from the postman with the body that contains one or multiple sensors, only the last request is saved in the database.
{
"deviceStatus": {
"sensors": [
{
"sensorId": "test",
"status": 1
}
]
}
}
I would like to be able to update values that are already in the database based on req.body or add new ones if needed. Any help will be appreciated.
The documentation said:
The $set operator replaces the value of a field with the specified
value.
You need the $push operator, it appends a specified value to an array.
Having this documents:
[
{
_id: 1,
"array": [
2,
4,
6
]
},
{
_id: 2,
"array": [
1,
3,
5
]
}
]
Using $set operator:
db.collection.update({
_id: 1
},
{
$set: {
array: 10
}
})
Result:
{
"_id": 1,
"array": 10
}
Using $push operator:
db.collection.update({
_id: 1
},
{
$push: {
array: 10
}
})
Result:
{
"_id": 1,
"array": [
2,
4,
6,
10
]
}
you want to using $push and $set in one findOneAndUpdate, that's impossible, I prefer use findById() and process and save() ,so just try
let result = await Device.findById(deviceId )
//implementation business logic on result
await result.save()
If you want to push new sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{
$push: {
"deviceStatus.sensors": { $each: req.body.sensors }
}
},
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
Update to the old answer:
If you want to update sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ "deviceId": deviceId },
{ "deviceStatus": req.body.sensors },
{ upsert: true },
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);

mongoDB autcomplete returns empty array

I am using mongodb's sample movie database (https://docs.atlas.mongodb.com/sample-data/sample-mflix#std-label-sample-mflix) to experiment with mongodb's autocomplete functionality. The search always returns an empty array. I have set up a Search Index as follows:
{
"mappings": {
"dynamic": false,
"fields": {
"title": [
{
"minGrams": 3,
"tokenization": "edgeGram",
"type": "autocomplete"
}
]
}
}
}
The model and the search query are setup as follows:
// Creating the Movies model
const Movies = mongoose.model("Movies", new mongoose.Schema({}), "movies");
// Impplementing autocomplete search
app.get("/search", async (req, res) => {
try {
let result = await Movies.aggregate([
{
$search: {
autocomplete: {
path: "title",
query: req.query.title,
fuzzy: {
maxEdits: 2,
prefixLength: 3,
},
},
},
},
]);
res.status(200).json({
status: "success",
results: result.length,
data: { result },
});
} catch (error) {
console.log(error);
}
});
I am using postman to run test queries and a sample query is: 127.0.0.1:3030/search?title=black
The model can be queried using .find(), for example, and returns the full collection of documents.
Any and all help is greatly appreciated.

And operator mongoosastic (filter)

How can I add filter to my query to get for example just users where role admin ?
Here is my query:
userModel.search({
"multi_match": {
"fields": ["firstName", 'lastName', 'specialities', 'profession', 'country', 'state', 'city'],
"query": req.query.q,
"fuzziness": "AUTO"
}
},
{
from: (pageNum - 1) * perPage,
size: perPage
},
function (err, results) {
...
I need to do something like this just with mognoosastic:
ElasticSearch - multi-match with filter - returns no result
Try Below Query Format
"query": {
"bool": {
"must": {
"multi_match": {
"query": "values",
"fields": [
"filed_one","field_two"
]
}
},
"filter": {
"range": {
"field_name": {
"gte": 1467311400,
"lte": 1468866599
}
}
}
}
}
Let me know if this format help or not

Finding multiple docs using same id not working, using meteor + react and mongoDB

How do I get the email address of the students in the same class_id, take it as there are more then 2 students in different class in the DB as well?
I have this but it return empty array []
Meteor.users.find({"course_learn_list.$.class_id": {$in: [classId]}},
{field: {"emails.address": 1}}
).fetch()
Collections
{
"_id": "LMZiLKs2MRhZiiwoS",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student1#gmail.com",
"verified": false
}
]
},
{
"_id": "JgfdLKs2MRhZJgfNgk",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student2#gmail.com",
"verified": false
}
]
}
I think you want:
Meteor.users.find({ "course_learn_list.class_id": classId },
{ "course_learn_list.$": 1, "emails.address": 1 }).fetch()
This should find the first instance in each course_learn_list array where the classId is your classId.
In this case you probably don't need to use a projection to get the right answer. Here's an example of extracting the verified email addresses using only the . operator in the selector:
const ids = ['jePhNgEuXLM3ZCt98', 'some-other-id'];
const emails =
Meteor.users.find({ 'course_learn_list.class_id': { $in: ids } })
.fetch()
.map(user => _.findWhere(user.emails, { verified: true }).address);
This works for me!
Meteor.publish("getMyClassStudents", function(classId) {
console.log("Publish getMyClassStudents")
var self = this
if (self.userId) {
var data = Meteor.users.find({
"course_learn_list.class_id": classId
}, {
"fields": {
"emails.address": 1
}
})
return data
}
else {
return self.ready()
}
})