MongoDB unique index across multiple subdocuments - mongodb

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" })

Related

Remove array element from document matching a field in array element

I have a document looking like this:
{
"_id" : ObjectId("5f60ffc5aefd067a9ff9345c"),
"_class" : "com.kalsym.smart.sms.data.MSASMS",
"number1" : NumberLong(923211105469),
"numbers2" : [
{
"field1" : "20200915532E888",
"number2" : NumberLong(923018565627),
"field2" : "abcd",
"datefield" : ISODate("2020-10-10T17:54:09.886Z")
},
{
"field1" : "2020092570A6948",
"number2" : NumberLong(923018565627),
"field2" : "efgh",
"datefield" : ISODate("2020-10-06T15:23:04.891Z")
},
{
"field1" : "2020092570A6948",
"number2" : NumberLong(923018565627),
"field2" : "ijkl",
"datefield" : ISODate("2020-10-03T15:23:04.891Z")
}
],
"optInCount" : 0
}
I want to delete array indices containing datefield value greater than 2020-10-04
After executing the command document must be updated like this:
{
"_id" : ObjectId("5f60ffc5aefd067a9ff9345c"),
"_class" : "com.kalsym.smart.sms.data.MSASMS",
"number1" : NumberLong(923211105469),
"numbers2" : [
{
"field1" : "2020092570A6948",
"number2" : NumberLong(923018565627),
"field2" : "ijkl",
"datefield" : ISODate("2020-10-03T15:23:04.891Z")
}
],
"optInCount" : 0
}
You can use updateMany() or update(), and $pull to remove matching record form array,
db.collection.updateMany({},
{
$pull: {
numbers2: { datefield: { $gt: ISODate("2020-10-04T00:00:00.000Z") } }
}
})
Playground

Text search on 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.

Mongoose Join List

I am new to mongoose and I have two schemas as below
contentschema
{ "_id" : autogenerated
"title": "string",
"description": "string",
}
viewedschema
{ "_id" : autogenerated
"contentid": "ref content",
"viewedby": "string",
}
All the users who have viewed the content will be stored in the viewedschema collection which has the contentid reference.
Note :As the number of viewed records will be huge, i dont want to have the viewed within the content as embedded document.
In Mongoose, Is there a way to get all the contents (array of content schema) viewed. [Similar to inner join in SQL].
Thanks in Advance.
We can use $lookup to merge the documents in two different collections of same database and it performs a left outer join on the collections.
Let us the below documents in content collection
{
"_id" : ObjectId("59ef51f106b0505f997f84c8"),
"title" : "myfavoritesong",
"description" : "A wonderful composition using string instruments"
}
{
"_id" : ObjectId("59ef52ad06b0505f997f84ca"),
"title" : "myfavoritestory",
"description" : "An interesting short story with a twisted ending"
}
Documents in viewed collection
{
"_id" : ObjectId("59ef523706b0505f997f84c9"),
"contentid" : ObjectId("59ef51f106b0505f997f84c8"),
"viewedby" : "user1"
}
{
"_id" : ObjectId("59ef52f406b0505f997f84cb"),
"contentid" : ObjectId("59ef52ad06b0505f997f84ca"),
"viewedby" : "user2"
}
{
"_id" : ObjectId("59ef53c706b0505f997f84cc"),
"contentid" : ObjectId("59ef52ad06b0505f997f84ca"),
"viewedby" : "user3"
}
Final aggregate query using $lookup by combining the two collections is
db.viewed.aggregate({
$lookup:{
from : "content",
localField: "contentid",
foreignField:"_id",
as:"viewed_contents"
}
})
Result of the aggregate query for our sample data is
{
"_id" : ObjectId("59ef523706b0505f997f84c9"),
"contentid" : ObjectId("59ef51f106b0505f997f84c8"),
"viewedby" : "user1",
"viewed_contents" : [
{
"_id" : ObjectId("59ef51f106b0505f997f84c8"),
"title" : "myfavoritesong",
"description" : "A wonderful composition using string in
struments"
}
]
}
{
"_id" : ObjectId("59ef52f406b0505f997f84cb"),
"contentid" : ObjectId("59ef52ad06b0505f997f84ca"),
"viewedby" : "user2",
"viewed_contents" : [
{
"_id" : ObjectId("59ef52ad06b0505f997f84ca"),
"title" : "myfavoritestory",
"description" : "An interesting short story with a twist
ed ending"
}
]
}
{
"_id" : ObjectId("59ef53c706b0505f997f84cc"),
"contentid" : ObjectId("59ef52ad06b0505f997f84ca"),
"viewedby" : "user3",
"viewed_contents" : [
{
"_id" : ObjectId("59ef52ad06b0505f997f84ca"),
"title" : "myfavoritestory",
"description" : "An interesting short story with a twist
ed ending"
}
]
}
Please note you can also swap the collections from viewed as foreign and content as local
db.content.aggregate({
$lookup:{
from : "viewed",
localField: "_id",
foreignField:"contentid",
as:"contents_viewed_by"
}
})
Result of this aggregate query is as follows
{
"_id" : ObjectId("59ef51f106b0505f997f84c8"),
"title" : "myfavoritesong",
"description" : "A wonderful composition using string instruments",
"contents_viewed_by" : [
{
"_id" : ObjectId("59ef523706b0505f997f84c9"),
"contentid" : ObjectId("59ef51f106b0505f997f84c8"),
"viewedby" : "user1"
}
]
}
{
"_id" : ObjectId("59ef52ad06b0505f997f84ca"),
"title" : "myfavoritestory",
"description" : "An interesting short story with a twisted ending",
"contents_viewed_by" : [
{
"_id" : ObjectId("59ef52f406b0505f997f84cb"),
"contentid" : ObjectId("59ef52ad06b0505f997f84ca"),
"viewedby" : "user2"
},
{
"_id" : ObjectId("59ef53c706b0505f997f84cc"),
"contentid" : ObjectId("59ef52ad06b0505f997f84ca"),
"viewedby" : "user3"
}
]
}

Is it possible to return the documents of two collections?

I want to store the employees appeals in different collections based on their type. But am afraid that I want be able to return the content of those different collections at once, as I used to return the content of a collection as shown in the code below:
pendingAppeals = function() {
return Al.find({status: "Pending"});
}
So my concern is, if I have another collection called Ml, will I be able to return the content of both Al and Ml in the same time?
I am not sure which version of mongo you are using and meteor has support for it or not. But mongo 3.2 has a aggregate type join which fetches data from multiple collections depending on your condition. Refer: https://docs.mongodb.com/v3.2/reference/operator/aggregation/lookup/
Not really too sure about your data structure, if you could provide an example it would be easier to work with, however take the following example where we have a students and a teachers collection:
> db.students.find()
{ "_id" : ObjectId("584020b6410ebb5a4ea03393"), "name" : "bob" }
{ "_id" : ObjectId("584020b6410ebb5a4ea03394"), "name" : "foo" }
{ "_id" : ObjectId("584020b7410ebb5a4ea03395"), "name" : "bill" }
> db.teachers.find().pretty()
{
"_id" : ObjectId("584020e7410ebb5a4ea03396"),
"name" : "t 1",
"studentIds" : [
ObjectId("584020b6410ebb5a4ea03393"),
ObjectId("584020b7410ebb5a4ea03395")
]
}
{
"_id" : ObjectId("584020ff410ebb5a4ea03397"),
"name" : "t 1",
"studentIds" : [
ObjectId("584020b6410ebb5a4ea03394"),
ObjectId("584020b7410ebb5a4ea03395")
]
}
Then we can use the aggregation framework with $unwind and $lookup stages:
db.teachers.aggregate([
{$unwind: "$studentIds"},
{$lookup: {
from: "students",
localField: "studentIds",
foreignField: "_id",
as: "students"
}
}
])
and we'll get the following output:
{
"_id" : ObjectId("584020e7410ebb5a4ea03396"),
"name" : "t 1",
"studentIds" : ObjectId("584020b6410ebb5a4ea03393"),
"students" : [
{
"_id" : ObjectId("584020b6410ebb5a4ea03393"),
"name" : "bob"
}
]
}
{
"_id" : ObjectId("584020e7410ebb5a4ea03396"),
"name" : "t 1",
"studentIds" : ObjectId("584020b7410ebb5a4ea03395"),
"students" : [
{
"_id" : ObjectId("584020b7410ebb5a4ea03395"),
"name" : "bill"
}
]
}
{
"_id" : ObjectId("584020ff410ebb5a4ea03397"),
"name" : "t 1",
"studentIds" : ObjectId("584020b6410ebb5a4ea03394"),
"students" : [
{
"_id" : ObjectId("584020b6410ebb5a4ea03394"),
"name" : "foo"
}
]
}
{
"_id" : ObjectId("584020ff410ebb5a4ea03397"),
"name" : "t 1",
"studentIds" : ObjectId("584020b7410ebb5a4ea03395"),
"students" : [
{
"_id" : ObjectId("584020b7410ebb5a4ea03395"),
"name" : "bill"
}
]
}
Refs:
https://docs.mongodb.com/v3.2/reference/operator/aggregation/
https://docs.mongodb.com/v3.2/reference/operator/aggregation/lookup/

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/