Unique documents in a MongoDB collection - mongodb

I need to store three id:s in a document but they can only occur once. For example the document below can only occur once in this collection:
{
"user": ObjectId("j8uwh902w5489"),
"comment": ObjectId("09890583457jkjsf4"),
"whatever": ObjectId("j8uwh902w5489")
}
How do I make sure this will be a unique document in MongoDB?

Well you can use MongoDB's Unique Compound Index
db.users.createIndex( { user: 1, comment: 1, whatever: 1 }, { unique: true } )
A few Cases:
> db.users.insert({user: "A", comment: "B", whatever: "C"})
WriteResult({ "nInserted" : 1 })
> db.users.insert({user: "A", comment: "C", whatever: "B"})
WriteResult({ "nInserted" : 1 })
> db.users.insert({user: "A", comment: "B", whatever: "C"})
> WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.users.$user_1_comment_1_whatev
er_1 dup key: { : \"A\", : \"B\", : \"C\" }"
}
})
I know I have used strings here. But something similar might also be possible with ObjectIds. Please do give it a try.

Related

mongdb ensure uniqueness on two fields both ways

Say I have the fields a and b. I want to have a compound uniqueness where if a: 1, b: 2, I would not be able to do a: 2, b: 1.
The reason I want this is because I'm making a "friends list" kind of collection, where if a is connected to b, then it's automatically the reverse as well.
is this possible on a schema level or do I need to do queries to check.
If you don't need to differentiate between requester and requestee, you could sort the values before saving or querying so that your two fields a and b have a predictable order for any pair of friend IDs (and you can take advantage of the unique index constraint).
For example, using the mongo shell:
Create a helper function to return friend pairs in predictable order:
function friendpair (friend1, friend2) {
if ( friend1 < friend2) {
return ({a: friend1, b: friend2})
} else {
return ({a: friend2, b: friend1})
}
}
Add a compound unique index:
> db.friends.createIndex({a:1, b:1}, {unique: true});
{
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
Insert unique pairs (should work)
> db.friends.insert(friendpair(1,2))
WriteResult({ "nInserted" : 1 })
> db.friends.insert(friendpair(1,3))
WriteResult({ "nInserted" : 1 })
Insert non-unique pair (should return duplicate key error):
> db.friends.insert(friendpair(2,1))
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.friends index: a_1_b_1 dup key: { : 1.0, : 2.0 }"
}
})
Search should work in either order:
db.friends.find(friendpair(3,1)).pretty()
{ "_id" : ObjectId("5bc80ed11466009f3b56fa52"), "a" : 1, "b" : 3 }
db.friends.find(friendpair(1,3)).pretty()
{ "_id" : ObjectId("5bc80ed11466009f3b56fa52"), "a" : 1, "b" : 3 }
Instead of handling duplicate key errors or insert versus update, you could also use findAndModify with an upsert since this is expected to be a unique pair:
> var pair = friendpair(2,1)
> db.friends.findAndModify({
query: pair,
update: {
$set: {
a : pair.a,
b : pair.b
},
$setOnInsert: { status: 'pending' },
},
upsert: true
})
{
"_id" : ObjectId("5bc81722ce51da0e4118c92f"),
"a" : 1,
"b" : 2,
"status" : "pending"
}
Doesn't seem like you can do a unique on the entire array's values so I'm doing a kind of work around. I'm using the $jsonSchema as follows:
{
$jsonSchema:
{
bsonType:
"object",
required:
[
"status",
"users"
],
properties:
{
status:
{
enum:
[
"pending",
"accepted"
],
bsonType:
"string"
},
users:
{
bsonType:
"array",
description:
"references two user_id",
items:
{
bsonType:
"objectId"
},
maxItems:
2,
minItems:
2,
},
}
}
}
then I will use $all to find the connected users, e.g.
db.collection.find( { users: { $all: [ ObjectId1, ObjectId2 ] } } )

Creating index in mongo 3

I want to create an index so that my DB does not allow me to insert documents whose value for the key lema is already present in some document of the DB. I did this:
db.version()
3.0.14
> db.rae.ensureIndex({"lema":1, unique: true})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 2,
"note" : "all indexes already exist",
"ok" : 1
}
> db.rae.insert({"lema":"a"})
WriteResult({ "nInserted" : 1 })
> db.rae.insert({"lema":"a"})
WriteResult({ "nInserted" : 1 })
> db.rae.insert({"lema":"a"})
WriteResult({ "nInserted" : 1 })
> db.rae.find()
{ "_id" : ObjectId("591a0ce372329f3162a314cc"), "lema" : "a" }
{ "_id" : ObjectId("591a0ce472329f3162a314cd"), "lema" : "a" }
{ "_id" : ObjectId("591a0ce572329f3162a314ce"), "lema" : "a" }
Clearly the DB is letting me insert documents whose values of lema are all a. How can I fix this? Thanks a lot
From Stennie's comment:
I should use createIndex instead of ensureIndex. Also I had a mistake, I should use db.rae.createIndex({"lema":1}, { unique: true}).

mongodb combination of fields unique

I need to ensure combination of some fields to be unique , so i found out we can use indexes to achieve it , like :
db.user.ensureIndex({'fname':1,'lname':1},{unique:true})
However in my scenario i allow the user to add email or phone or even both . So how do i achieve the uniqueness in this case . I tried like setting 2 indexed like
db.user.ensureIndex({'fname':1,'lname':1,'email':1},{unique:true})
And
db.user.ensureIndex({'fname':1,'lname':1,'mobile':1},{unique:true})
this dint work when i try to add a document with email unique , like below
> db.user.insert({fname:'tony',lname:'stark',email:'aa#gmail.com'})
WriteResult({ "nInserted" : 1 })
> db.user.insert({fname:'tony',lname:'stark',email:'xyz#gmail.com'})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error index: temp.user.$fname_1_lname_1_mobile_1 dup key: { : \"tony\", : \"stark\", : null }"
}
})
I guess the error is because of the second index ,since both the inserts have null value for the mobile field . I then tried this
db.user.ensureIndex({'fname':1,'lname':1,'email':1,'mobile':1},{unique:true})
And that dint help ,is there any way to do this with mongodb or check the uniqueness with code otherwise .
Update
I need to ensure uniqueness in this case too :
db.user.insert({fname:'tony',lname:'stark',email:'abc#gmail.com'})
<<insert>>
db.user.insert({fname:'tony',lname:'stark',email:'abc#gmail.com',mobile:554545435})
<<dont insert>>
Update 2
Even Unique partial Index didn't help
> db.users.createIndex({ "fname": 1, "lname": 1, "email":1 },
... { unique: true, partialFilterExpression:{
... email: { $exists: true, $gt : { $type : 10 } } } })
{
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.users.createIndex({ "fname": 1, "lname": 1, "mobile":1 },
... { unique: true, partialFilterExpression:{
... mobile: { $exists: true, $gt : { $type : 10 } } } })
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
Here is the query :
>db.users.insert({fname:"tony",lname:"stark",mobile:"123",email:"123#gmail.com"})
WriteResult({ "nInserted" : 1 })
>db.users.insert({fname:"tony",lname:"stark",mobile:"123",email:"123#gmail.com"})
WriteResult({ "nInserted" : 1 })
Mongo version - v3.2.0
I have no experience with mongodb much neither have used indexes in other db's , i guess it's kind of noob question .Thanks in advance

Indexing array/subobject in mongoDB causes duplicate key error

I have a collection where I will have a _children attribute like this:
{
_children: {
videoTags: [ { id: '1', name: 'one'}, { id: '2', name: 'two'} ],
},
a: 10
}
Since I WILL search in videoTags, I create an index as such:
> db.test4.createIndex({ "_children.videosTags.id" : 1 }, { "unique" : true } );
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
Trouble is, I can no longer add anything to that table since I get a duplicate index error. Here is how to reproduce it:
Step 1: insert to a collection
db.test4.insert({a:20})
WriteResult({ "nInserted" : 1 })
Step 2: make the index
db.test4.createIndex({ "_children.videosTags.id" : 1 }, { "unique" : true } );
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
Step 3: try to insert again
db.test4.insert({a:30})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: wonder_1.test4.$_children.videosTags.id_1 dup key: { : null }"
}
})
I think the issue here is that there is already a record where _children.videoTags.id is not defined.
However, what I expected was a behaviour where if videoTags.id was specified, it needed to be unique. Instead, an empty one is considered a "taken" key.
What am I doing that is stupidly wrong?
This will work if you don't set unique as true, but I have the feeling I need to fix it for real...
There could be two reasons.
There could be other documents exists in collection with same _children.videosTags.id
It's quite possible that more than one document may have missing _children.videosTags.id" or having null value.
As you are creating unique key, null or empty values are give you tough time. Solution is either create sparse index and if your MongoDB version is 3.2+, create partial index. See documentation for partial indexes.

MongoDB weird writeResult behaviour

I am using mongodb (v2.6.7) and mongo (2.6.7) shell client.
I am trying to use the WriteResult object returned by the insert and and update commands.
According to mongodocs in case of error it returns a writeResult object with writeError subdocument. But I am not able to access this subdocument in shell or in mongo's javascript file.
Below is an illustration of my problem.
I insert an object and get the successful writeResult.
Then I am inserting again with the same _id and I am getting the writeResult correctly printed on the screen which has "writeError" correctly set.
Also the writeResult object seems to contain the writeError when I print it with printjson() method
But when I print it with JSON.stringify() then I am not able to see the "writeError".
Also writeResult.writeError is coming as undefined so I am not able to access its writeResult.writeError.code property.
Can someone please explain why is this happening and what is the correct way.
afv:PRIMARY>writeResult=db.sysinfo.insert({_id:"myid",otherData:"otherDataValue"})
WriteResult({ "nInserted" : 1 })
afv:PRIMARY>writeResult=db.sysinfo.insert({_id:"myid",otherData:"otherDataValue"})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: afvadmin.sysinfo.$_id_ dup key: { : \"myid\" }"
}
})
afv:PRIMARY> printjson(writeResult)
{
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: afvadmin.sysinfo.$_id_ dup key: { : \"myid\" }"
}
}
afv:PRIMARY> JSON.stringify(writeResult);
{"nInserted":0,"nUpserted":0,"nMatched":0,"nModified":0,"nRemoved":0}
afv:PRIMARY> writeResult.writeError
afv:PRIMARY> writeResult.nInserted
0
afv:PRIMARY> writeResult.writeError.code
2015-02-02T16:34:42.402+0530 TypeError: Cannot read property 'code' of undefined
afv:PRIMARY> writeResult["writeError"]
I don't know why it was implemented this way, but to access the contained writeError subdocument, you call getWriteError() on a WriteResult object:
> writeResult.getWriteError()
WriteError({
"index": 0,
"code": 11000,
"errmsg": "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.test.$_id_ dup key: { : \"myid\" }",
"op": {
"_id": "myid",
"otherData": "otherDataValue"
}
})
> writeResult.getWriteError().code
11000
I couldn't find any documentation for this. I figured it out by hitting Tab twice after typing writeResult. in the shell to see what's available.
In addition to using the methods to get the WriteError object as mentioned already, you can also get the raw response and work on that:
> writeResult.getRawResponse()
{
"writeErrors" : [
{
"index" : 0,
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: foo.bar.$_id_ dup key: { : ObjectId('54cf8152733aa5e886f0e400') }",
"op" : {
"_id" : ObjectId("54cf8152733aa5e886f0e400"),
"a" : 1
}
}
],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
}
So, you can then do something like this and things are a bit more consistent:
> raw = writeResult.getRawResponse();
> raw.writeErrors[0].errmsg
insertDocument :: caused by :: 11000 E11000 duplicate key error index: foo.bar.$_id_ dup key: { : ObjectId('54cf8152733aa5e886f0e400') }
> printjson(raw.writeErrors[0])
{
"index" : 0,
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: foo.bar.$_id_ dup key: { : ObjectId('54cf8152733aa5e886f0e400') }",
"op" : {
"_id" : ObjectId("54cf8152733aa5e886f0e400"),
"a" : 1
}
}
> JSON.stringify(raw.writeErrors[0])
{"code":11000,"index":0,"errmsg":"insertDocument :: caused by :: 11000 E11000 duplicate key error index: foo.bar.$_id_ dup key: { : ObjectId('54cf8152733aa5e886f0e400') }"}