Text search on mongodb - mongodb

I have a users collection in Mongo DB having 55L users. The collection is shared across 10 nodes.
There is a field "email" in the collection which stores the email address of the users.
I want to perform a text-based search on this field. I want to search for "mail" and the result should list all documents which have the substring "mail".
I tried with regex but it impacts performance due to collection size.
I tried creating a text index on "email" but it does not support partial search on tokens.
Collection example mentioned below
{ "_id" : ObjectId("5b90e5ffe34b5891eb4c32f6"), "email" : "anand#gmail.com" }
{ "_id" : ObjectId("5b90e64ee34b5891eb4c32f7"), "email" : "kumar.anand#hotmail.com" }
{ "_id" : ObjectId("5b90e65ae34b5891eb4c32f8"), "email" : "ankit#gmail.com" }
{ "_id" : ObjectId("5b90e661e34b5891eb4c32f9"), "email" : "rahul#rediff.com" }
{ "_id" : ObjectId("5b90e67de34b5891eb4c32fa"), "email" : "prachi#gmail.com" }
{ "_id" : ObjectId("5b90f0ab46ef1951e6afb822"), "email" : "bb#yahoo.com", "name" : "ankit" }
{ "_id" : ObjectId("5b965d4ad5bc80bda9885181"), "email" : "amit#gmail.com", "rating" : [ 5, 6 ] }
{ "_id" : ObjectId("5b965d56d5bc80bda9885182"), "email" : "amit33#gmail.com", "rating" : [ 2, 4 ] }
{ "_id" : ObjectId("5b965d60d5bc80bda9885183"), "email" : "amit11#gmail.com", "rating" : [ 12, 14 ] }
{ "_id" : ObjectId("5b966cf1f12e2344dc5942e5"), "email" : "avin11#gmail.com", "new_rating" : { "id" : [ 5, 6 ] } }
{ "_id" : ObjectId("5b966cfdf12e2344dc5942e6"), "email" : "avin22#gmail.com", "new_rating" : { "id" : [ 2, 4 ] } }
{ "_id" : ObjectId("5b966d06f12e2344dc5942e7"), "email" : "avin33#gmail.com", "new_rating" : { "id" : [ 12, 14 ] } }
{ "_id" : ObjectId("5b986afaf12e2344dc5942e8"), "email" : "Sachin#hotmail.com", "name" : "Sachin" }
Kindly suggest how to perform the search query

Hi and welcome to Stack overflow community..
I think you can still use a regex, but you should have an index on the email field to have better performance.
Read more about regex and indexes on mongo DB here: https://docs.mongodb.com/manual/reference/operator/query/regex/#index-use
I suggest using Explain Results to make sure your query uses the index you added.
Hope this helps.

Related

MongoDB addToSet in nested array

I'm struggling to insert data inside a nested array in MongoDB.
My schema looks like this:
{
"_id" : ObjectId("5c0c55642440311ff0353846"),
"name" : "Test",
"email" : "test#gmail.com",
"username" : "test",
"password" : "$2a$10$RftzGtgM.DqIiaSvH4LqOO6RnLgQfLY3nk7UIAH4OAvvxo0ZMSaHu",
"created" : ISODate("2018-12-08T23:36:04.464Z"),
"classes" : [
{
"_id" : ObjectId("5c0c556e2440311ff0353847"),
"cName" : "1A",
"student" : [
{
"grades" : [ ],
"_id" : ObjectId("5c0c55812440311ff0353848"),
"name" : "StudentName",
"lname" : "StudenteLastName",
"gender" : "M"
}
insert }
],
"__v" : 0
}.
What I want to do is inserting a grade for the student inside "grades" array.
Expected result is:
{
"_id" : ObjectId("5c0c55642440311ff0353846"),
"name" : "Test",
"email" : "test#gmail.com",
"username" : "test",
"password" : "$2a$10$RftzGtgM.DqIiaSvH4LqOO6RnLgQfLY3nk7UIAH4OAvvxo0ZMSaHu",
"created" : ISODate("2018-12-08T23:36:04.464Z"),
"classes" : [
{
"_id" : ObjectId("5c0c556e2440311ff0353847"),
"cName" : "1A",
"student" : [
{
"grades" : [6],
"_id" : ObjectId("5c0c55812440311ff0353848"),
"name" : "StudentName",
"lname" : "StudenteLastName",
"gender" : "M"
}
]
}
],
"__v" : 0
}.
I tried some queries but none of them helped me, even searching a lot.
db.teachers.update({"_id": ObjectId("5c0c55642440311ff0353846"), "classes._id": ObjectId("5c0c556e2440311ff0353847"), "classes.student._id": ObjectId("5c0c55812440311ff0353848")},{$addToSet: {"classes.$.student.grades":6}})
Basically, I searched for the student with the first curly bracket (if I do "db.teachers.find(the three conditions) the result is correct) and then add to the grades array (of Integer) the value 6. But at this point I get errors, I think I'm making a mistake on the "adding" part.
I need also to do the same thing in Mongoose.
Any help is appreciated, thanks in advance!
Edit: I solved. I post my solution hoping it'll be useful to other:
For pushing inside a triple nested array do:
db.teachers.update({"_id":ObjectId("5c0c59985ae5981c58937e12"),"classes":{ $elemMatch : { _id : ObjectId("5c0c59a35ae5981c58937e13") }},"classes.student": { $elemMatch : { _id : ObjectId("5c0c59aa5ae5981c58937e14")} }},{$addToSet:{"classes.$.student.0.grades":3}})
https://docs.mongodb.com/manual/tutorial/query-array-of-documents/
Try using $elemMatch
"classes":{ $elemMatch : { _id : ObjectId("5c0c556e2440311ff0353847") }},
"classes.student": { $elemMatch : { _id : ObjectId("5c0c55812440311ff0353848")} }

MongoDB unique index across multiple subdocuments

I have this document structure, the property "Name" must be unique across all documents, as shown above:
{
"_id" : ObjectId("56686341d6389c004c689d5d"),
"Bosses" : {
"B1" : {
"_id" : NumberInt(1),
"Name" : "John"
},
"B2" : {
"_id" : NumberInt(1),
"Name" : "Mary"
}
}
}
{
"_id" : ObjectId("56686341d6389c004c689d6f"),
"Bosses" : {
"B1" : {
"_id" : NumberInt(1),
"Name" : "Mary" // should throw an error
}
}
}
It's possible to create a unique index with this structure?
Yes, you have to create a unique index on Bosses.Name. But then, You'll have to change a little bit your schema. Bosses should be an array:
{
"_id" : ObjectId("56686341d6389c004c689d6f"),
"Bosses" : [
{
"_id" : NumberInt(1),
"Name" : "Mary" // should throw an error
}
}
}
If you really need the B1, I suggest you add it into Bosses objects: "Code": "B1". But this might not be necessary, as you can access the index in your array by ...find({ "Bosses.0.Name" })

Error in Projection using find() method

I am total newbie to MongoDB. I was trying out basic stuff in mongo, when I encountered a problem. I searched for it, but not able to find any satisfactory answer.
I have a very simple collection named "users" having names and age of some persons. Below is output of db.users.find()
{ "_id" : ObjectId("566acc0442fea953b8d94a7e"), "name" : "gabriel", "age" : 22 }
{ "_id" : ObjectId("566acc0442fea953b8d94a7f"), "name" : "andy", "age" : 10 }
{ "_id" : ObjectId("566acc1342fea953b8d94a80"), "name" : "henry", "age" : 27 }
{ "_id" : ObjectId("566acc1342fea953b8d94a81"), "name" : "william", "age" : 19 }
{ "_id" : ObjectId("566acc3242fea953b8d94a82"), "name" : "sandra", "age" : 20 }
{ "_id" : ObjectId("566acc3242fea953b8d94a83"), "name" : "tom", "age" : 24 }
Now, I am trying to apply different projection on it. First two are:
db.users.find({}, {"name":1, "age":1})
{ "_id" : ObjectId("566acc0442fea953b8d94a7e"), "name" : "gabriel", "age" : 22 }
{ "_id" : ObjectId("566acc0442fea953b8d94a7f"), "name" : "andy", "age" : 10 }
{ "_id" : ObjectId("566acc1342fea953b8d94a80"), "name" : "henry", "age" : 27 }
{ "_id" : ObjectId("566acc1342fea953b8d94a81"), "name" : "william", "age" : 19 }
{ "_id" : ObjectId("566acc3242fea953b8d94a82"), "name" : "sandra", "age" : 20 }
{ "_id" : ObjectId("566acc3242fea953b8d94a83"), "name" : "tom", "age" : 24 }
db.users.find({}, {"name":0, "age":0})
{ "_id" : ObjectId("566acc0442fea953b8d94a7e") }
{ "_id" : ObjectId("566acc0442fea953b8d94a7f") }
{ "_id" : ObjectId("566acc1342fea953b8d94a80") }
{ "_id" : ObjectId("566acc1342fea953b8d94a81") }
{ "_id" : ObjectId("566acc3242fea953b8d94a82") }
{ "_id" : ObjectId("566acc3242fea953b8d94a83") }
are working just fine, but
db.users.find({}, {"name":0, "age":1})
Error: error: {
"$err" : "Can't canonicalize query: BadValue Projection cannot have a mix of inclusion and exclusion.",
"code" : 17287
}
is failing and giving error as shown above. I searched that problem may arise if there is conflicting conditions in projection, but I don't think there is something like that in my method call. Is there some rule like value of fields in find method should be either all ZERO or all ONE, but cannot be both. Please help.
If you want your query to return only the age, your projection must include only the fields you want to have. Not the one you don't want:
db.projection.find({}, {_id:0, age:1})
Exception for _id, you can specify to not include it.
Result
{
"age": 22
}
From the documentation:
A projection cannot contain both include and exclude specifications, except for the exclusion of the _id field. In projections that explicitly include fields, the _id field is the only field that you can explicitly exclude.

view of query in mongodb

I have a collection ,this is one of it's docs :
{
"_id" : 1 ,
"first_name" : "john",
"phone" : [
{
"type" : "mobile",
"number" : "9151112233"
},
{
"type" : "home",
"city_code" : 51,
"number" : "55251544"
},
{
"type" : "mobile",
"number" : "9152425125"
}
]
}
I'm searching for "phones" that contain type "mobile" and show them.
I need something like this :
{
"number" : "9151112233",
"number" : "9152425125"
}
I write this query for that :
db.main.find({ _id : 1 , 'phone.type' : "mobile" },{'phone.number' : true , _id : false}).forEach(printjson)
I want to show only numbers that their types are mobile but this query show all to numbers because this single doc contain others too.
How can I fix it?
I'd use the aggregation framework along with the $unwind, $match and $project commands. This:
db.main.aggregate({$unwind:"$phone"},{$match:{"phone.type":"mobile"}},{$project:{"phone.number":1,"_id":0}})
produces this output:
{ "phone" : { "number" : "9151112233" } }
{ "phone" : { "number" : "9152425125" } }
which only matches the mobile numbers.
http://docs.mongodb.org/manual/aggregation/

mongodb Embedded document search on parent and child field

I have a nested embedded document CompanyProduct below is structure
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"Name" : Company1,
"CompanyDepartment" : [
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca4")
"DepartmentID" : 287,
"DepartmentName" : "Stores",
"DepartmentInventory" : [
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 1,
"ProductName" : "abc",
"Quantity" : 100
},
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
],
}
],
}
There can be N no of companies and each company can have N number of departments and each department can have N number of products.
I want to do a search to find out a particular product quantity under a particular company
I tried below query but it does not work. It returns all the products for the specific company, the less than 20 condition doesn't work.
db.CompanyProduct.find({$and : [{"CompanyDepartment.DepartmentInventory.Quantity":{$lt :20}},{"CompanyID":90449}]})
How should the query be?
You are searching from companyProduct's sub documents. So it will return you companyProduct whole document, it is NoSQL database , some how we do not need to normalize the collection , but your case it has to be normalize , like if you want to EDIT/DELETE any sub document and if there are thousand or millions of sub document then what will you do ... You need to make other collection with the name on CompanyDepartment and companyProduct collection should be
productCompany
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"Name" : Company1,
"CompanyDepartment" : ['53d213c5ddbb1912343a8ca4'],
}
and other collection companyDepartment
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca4")
"DepartmentID" : 287,
"DepartmentName" : "Stores",
"DepartmentInventory" : [
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 1,
"ProductName" : "abc",
"Quantity" : 100
},
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
],
}
after this you got array of companyDeparment' ID and only push and pull query will be used on productCompany
A Solution can be
db.YourCollection.aggregate([
{
$project:{
"CompanyDepartment.DepartmentInventory":1,
"CompanyID" : 1
}
},{
$unwind: "$CompanyDepartment"
},{
$unwind: "$CompanyDepartment.DepartmentInventory"
},{
$match:{$and : [{"CompanyDepartment.DepartmentInventory.Quantity":{$lt :20}},{"CompanyID":90449}]}
}
])
the result is
{
"result" : [
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"CompanyDepartment" : {
"DepartmentInventory" : {
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
}
}
],
"ok" : 1
}