Say we have a collection of documents similar to this:
{
foo: "Bar",
foos: [1, 2, 3]
}
I would like to define a unique index such that no document identical to this one can be inserted into the database.
db.stuffs.ensureIndex({ foos: 1 }, { unique: true })
Seems to block any document containing a foos array with any intersection, eg. if the document above was already in the database, then
{
foo: "Bar",
foos: [ 1 ]
}
Would also be blocked.
> db.stuffs.ensureIndex({ foos: 1 }, { unique: true })
> db.stuffs.insert({ foo: "Bar", foos: [ 1, 2, 3 ]})
> db.stuffs.insert({ foo: "Bar", foos: [ 1 ]})
E11000 duplicate key error index: test.stuffs.$foos_1 dup key: { : 1.0 }
I would like to be able to make insertions of [ 1, 2 ], [ 2, 1 ], [ 1, 3 ], etc. but not two [ 1, 2 ]
The array index will not meet your requirement. But I think you can switch to the other format to store your data.
If there is no need to use the feature of array (such as $addToSet, $push op), you can simply hash/map your data to another format. e.g.: [1,2,3] to the string "1,2,3".
While I assume that you want to remain the array operations in order to make some updates. Then you can try the subdocument below:
db.stuffs.ensureIndex({ foos: 1 }, { unique: true }) // build the index for the sub doc
db.stuffs.insert({ foo: "Bar", foos: {k:[ 1, 2, 3 ]}})
db.stuffs.insert({ foo: "Bar", foos: {k:[ 1 ]}})
db.stuffs.update({ "_id" : ObjectId("54081f544ea4d4e96bffd9ad")}, {$push:{"foos.k": 2}})
db.stuffs.insert({ foo: "Bar", foos: {k:[1, 2]}})
E11000 duplicate key error index: test.stuffs.$foos_1 dup key: { : { k: [ 1.0, 2.0 ] } }
Please refer to this question. Explains why Unique Index won't work in this case.
Unique index in MongoDB
To index a field that holds an array value, MongoDB creates an index
key for each element in the array.
If the array that you want to index is of constant size, you can create a unique multikey index like this:
collection.createIndex({"coordinate.0": 1, "coordinate.1": 1}, {unique: true})
coordinate is an array of size 2.
When I try to insert a duplicate coordinate, it returns an error as expected.
Related
Let's say we have a document like
{
someField: [
[
{
a: 1,
b: 2
},
{
a: 3,
b: 4
}
],
[
{
a: 5,
b: 6
}
]
]
}
It has been created an index of someField.a.
I'v tried some different filters (query criteria) for searching.
{'someField.a': 5} couldn't return the expected result.
{someField': {$elemMatch: {$elemMatch: {'a': 5}}}} could return the expected documents, but it seems that the query planner shows no index available for this query.
So could anyone help me that how to make a query of such an embedded field using the index?
I have a MongoDB index:
Reservation.index(
{
source: 1,
accountID: 1, // <-- This is the only required field
confirmationCode_1: 1,
confirmationCode_2: 1,
confirmationCode_3: 1
},
{name: "Unique_reservation_index_1", unique: true}
);
Here are some sample entries I have in the database and I want to make sure that duplicates can't be made:
[
{
source: "A",
accountID: "AAA",
confirmationCode_1: "ABC"
},
{
source: "B",
accountID: "BBB",
confirmationCode_1: "ABC"
confirmationCode_2: "DEF"
},
{
source: "C",
accountID: "CCC",
confirmationCode_3: "GHI"
}
]
Sometimes I have confirmationCode_1 set and not confirmationCode_2 other times I both confirmationCode_1 and confirmationCode_2 set. Other times I have confirmationCode_3 set.
I want MongoDB to allow me to have the following doc (missing the confirmationCode_2 and confirmationCode_3 fields). Will it let me with the above index?
{
source: "A",
accountID: "123",
confirmationCode_1: "ABC"
}
Will it prevent me from adding two similar docs with confirmationCode_2 not defined or will that be considered the same? For example, if it does allow the above doc, will this be prevented?
{
source: "A",
accountID: "AAA",
confirmationCode_1: "ABC_2"
}
If I don't supply the confirmationCode_2 field, does it set the confirmationCode_2 field to null?
If I change the unique index to include sparse: true, how will it act differently?
Reservation.index(
{
source: 1,
accountID: 1, // <-- This is the only required field
confirmationCode_1: 1,
confirmationCode_2: 1
},
{name: "Unique_reservation_index_1", unique: true, sparse: true}
);
From MongoDB document on unique Index,
A unique index ensures that the indexed fields do not store duplicate values
undefined / empty / null field is allowed as long as you do not have the same tuple of values of the fields in the compound index.
Below is my actual testing result:
You can observe that the document is successfully added under the unique index.
Will unique indexes ignore fields that don't exist?
No, the index will store a null value for this field, MongoDB will enforce uniqueness on the combination of the index key values.
//You have this docuemt on you MongoDB
{
source: "A",
accountID: "123",
confirmationCode_1: "ABC"
}
//You try to insert the next document, note the missing "accountID" field
//Even though "source" and "confirmationCode_1"
//This operation SUCCESS because
//MongoDB will enforce uniqueness on the "combination" of the index key values
{
source: "A",
confirmationCode_1: "ABC"
}
//You try to insert the next document
//The operation FAIL to insert the document
//because of the violation of the unique constraint
//on the combination of key values
{
source: "A",
accountID: "123",
confirmationCode_1: "ABC"
}
What if you change unique: true to unique: true, sparse: true ?
An index that is both sparse and unique prevents collection from
having documents with duplicate values for a field but allows multiple
documents that omit the key.
I have some documents in my mongodb database with the following structure, I want to know how to create an index for document.data.0.data[0-9999].val1?
My problem is where I type [0-9999] I don't know how many elements will be inside this data array, but I want that all the elements inside this array have an index on the val1 key.
Any tips?
{
"_id" : ObjectId("55c455c2fc07853b78c4b6b9"),
"data": {
"0": {
"data": [
{
val1: "abc",
val2: "def"
},
{
val1: "abc",
val2: "def"
}
]
}
}
}
You don't include the array index values in the index, so it would just be:
db.mycoll.ensureIndex({'data.0.data.val1': 1})
This is termed a multikey index in MongoDB.
According to this answer, removing an element from an array by index implies two distinct updates:
db.lists.update({}, {$unset : {"interests.0" : 1 }})
db.lists.update({}, {$pull : {"interests" : null}})
This works... but it introduces some complexity to make the operation atomic and prevent race conditions. Is there any update on that topic?
Yes and No, really. Consider the following array:
{ array: [ { a: "a", b: "b" }, { a:"a", b:"c" } ] }
So you could $pull the second element from here with the following statement:
db.collection.update({}, {$pull: { array: { a: "a", b: "b"} } })
And that would remove the matching document. But if did the same thing on this array:
{ array: [ { a: "a", b: "b" }, { a: "a", b:"c", c: "c" }, { a:"a", b:"c" } ] }
Then this would be the result:
{ array: [ { a: "a", b: "b" }] }
So the query matched two of the documents in the array, and pulled them both.
Unfortunately the seemingly obvious:
db.collection.update({}, {$pull: { array: { $elemMatch:{ a: "a", b: "b" } }} })
Just wont work. Which is a pity. In the too hard basket to parse at this time.
So the moral of the story is, if you really concerned about concurrency and race conditions for this type of update, .find() the matching element first, then update() with a statement that matches the whole document, as long as it will not also partially match another document in the array.
If you cannot do that, then the method you describe in the question is currently the only way.
There is a JIRA issue on this, but it is not exactly terribly popular.
Is this possible?
I have a collection C, with an array of attributes A1.
Each attribute has an array of subattributes A2.
How can I add a subdocument to a specific C.A1 subdocument ?
Here is an example.
db.docs.insert({_id: 1, A1: [{A2: [1, 2, 3]}, {A2: [4, 5, 6]}]})
If you know the index of the subdocument you want to insert, you can use dot notation with the index (starting from 0) in the middle:
db.docs.update({_id: 1}, {$addToSet: {'A1.0.A2': 9}})
This results in:
{
"A1" : [
{
"A2" : [
1,
2,
3,
9
]
},
{
"A2" : [
4,
5,
6
]
}
],
"_id" : 1
}
Yes, this is possible. If you post an example I can show you more specifically what the update query would look like. But here's a shot:
db.c.update({ A1: value }, { $addToSet: { "A1.$.A2": "some value" }})
I haven't actually tried this (I'm not in front of a Mongo instance right now) and I'm going off memory, but that should get you pretty close.
Yes, $push can be used to do the same. Try below given code.
db.c.update({ A1: value }, { $push: { "A1.$.A2": num }});