Empty result try find element in array Mongodb v4 (broke compatibility) - mongodb

why second command return empty result? Its work in mongodb 3 version after update to 4 version broking...
// Command #1:
db.c.find({
AlbumId: HexData(3, "ca79977db39b1640a45e13dc5a06cb48"),
})
// Result:
{ "_id" : ObjectId("5e30aaadfc253f6d211aefb1"), "AlbumId" : BinData(3,"ynmXfbObFkCkXhPcWgbLSA=="), "Items" : [ ] }
// Command #2:
db.c.find({
AlbumId: HexData(3, "ca79977db39b1640a45e13dc5a06cb48"),
Items: { "$elemMatch" : { "field" : { "$ne" : HexData(3, "ad326a5404856c4e9fb4aafa6f0430e6") }}}
})

Related

Insert new fields to document at given array index in MongoDB

I have the following document structure in a MongoDB collection :
{
"A" : [ {
"B" : [ { ... } ]
} ]
}
I'd like to update this to :
{
"A" : [ {
"B" : [ { ... } ],
"x" : [],
"y" : { ... }
} ]
}
In other words, I want the "x" and "y" fields to be added to the first element of the "A" array without loosing "B".
Ok as there is only one object in A array you could simply do as below :
Sample Collection Data :
{
"_id" : ObjectId("5e7c3cadc16b5679b4aeec26"),
A:[
{
B: [{ abc: 1 }]
}
]
}
Query :
/** Insert new fields into 'A' array's first object by index 0 */
db.collection.updateOne(
{ "_id" : ObjectId("5e7c3f77c16b5679b4af4caf") },
{ $set: { "A.0.x": [] , "A.0.y" : {abcInY :1 }} }
)
Output :
{
"_id" : ObjectId("5e7c3cadc16b5679b4aeec26"),
"A" : [
{
"B" : [
{
"abc" : 1
}
],
"x" : [],
"y" : {
"abcInY" : 1.0
}
}
]
}
Or Using positional operator $ :
db.collection.updateOne(
{ _id: ObjectId("5e7c3cadc16b5679b4aeec26") , 'A': {$exists : true}},
{ $set: { "A.$.x": [] , "A.$.y" : {abcInY :1 }} }
)
Note : Result will be the same, but functionally when positional operator is used fields x & y are inserted to first object of A array only when A field exists in that documents, if not this positional query would not insert anything (Optionally you can check A is an array condition as well if needed). But when you do updates using index 0 as like in first query if A doesn't exist in document then update would create an A field which is an object & insert fields inside it (Which might cause data inconsistency across docs with two types of A field) - Check below result of 1st query when A doesn't exists.
{
"_id" : ObjectId("5e7c3f77c16b5679b4af4caf"),
"noA" : 1,
"A" : {
"0" : {
"x" : [],
"y" : {
"abcInY" : 1.0
}
}
}
}
However, I think I was able to get anothe#whoami Thanks for the suggestion, I think your first solution should work. However, I think I was able to get another solution to this though I'm not sure if its better or worse (performance wise?) than what you have here. My solution is:
db.coll.update( { "_id" : ObjectId("5e7c4eb3a74cce7fd94a3fe7") }, [ { "$addFields" : { "A" : { "x" : [ 1, 2, 3 ], "y" : { "abc" } } } } ] )
The issue with this is that if "A" has more than one array entry then this will update all elements under "A" which is not something I want. Just out of curiosity is there a way of limiting this solution to only the first entry in "A"?

Update a json document in a mongodb database

I'm trying to update an existing document in a MongoDb. There are many explanations how to do this if you want to update or add key/value pairs on the first level. But in my use-case, I need to create with the first updateOne (with upsert option set) a document with the following structure:
{
"_id" : "1234",
"raw" : {
"meas" : {
"meas1" : {
"data" : "blabla"
}
}
}
}
In the second command, I need to add - in the same document - a "meas2" field at the level of "meas1". My desired output is:
{
"_id" : "1234",
"raw" : {
"meas" : {
"meas1" : {
"data" : "blabla"
},
"meas2" : {
"data" : "foo"
}
}
}
}
I played with statements like
updateOne({"_id":"1234"},{$set:{"raw":{"meas":{"meas2":{"data":"foo"}}}}}, {"upsert":true})
and also with $push, both variants with insert - here only the document and also insertOne, but nothing produces the desired output. Is there a MongoDb expert who could give a hint ? ... I'm sure this functionality exists... Thanks in advance!
When you update {$set: {"raw":{"meas":{"meas2":{"data":"foo"}}}} you're not adding "mesa2" to "meas" but rather you're overriting "raw" completely.
In order to change / add one field in a document refer to it with dot notations.
The command you want is updateOne({"_id": "1234"}, {$set: {"raw.meas.mesa2": { "data" : "foo" }}}, {"upsert":"true"})
You need to understand the below concept first
Set Fields in Embedded Documents, with details document check at official documentation of mongo
For your problem, just look at the below execution on the mongo shell:
> db.st4.insert({
... "_id" : "1234",
... "raw" : {
... "meas" : {
... "meas1" : {
... "data" : "blabla"
... }
... }
... }
... })
WriteResult({ "nInserted" : 1 })
> db.st4.find()
{ "_id" : "1234", "raw" : { "meas" : { "meas1" : { "data" : "blabla" } } } }
>
> // Below query will replace the raw document with {"meas":{"meas2":{"data":"foo"}}}, will not add
> //db.st4.updateOne({"_id":"1234"},{$set:{"raw":{"meas":{"meas2":{"data":"foo"}}}}}, {"upsert":true})
>// By using the dot operator, you actually write the values inside the documents i.e you are replacing or adding at raw.meas.mesa2 i.e inside the document of mesa2.
> db.st4.updateOne({"_id":"1234"},{$set: {"raw.meas.mesa2": { "data" : "foo" }}}, {"upsert":"true"})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.st4.find().pretty()
{
"_id" : "1234",
"raw" : {
"meas" : {
"meas1" : {
"data" : "blabla"
},
"mesa2" : {
"data" : "foo"
}
}
}
}
>

$inc vs. $setOnInsert in MongoCollection.findOneAndUpdate()

I'm trying to implement a 'server-side counter-versioned' item in mongodb and trying to do following /* using Java API */
Document dbDoc = dbCollection.findOneAndUpdate(
new Document("_id", "meta"),
new Document("$inc", new Document("version", 1))
.append("$setOnInsert", new Document("version", 0)),
new FindOneAndUpdateOptions().upsert(true)
.returnDocument(ReturnDocument.AFTER));
Assumed logic is simple: if there is no record in database - start counting from zero (and with a whole fresh new object), otherwise - increment counter.
Code sample fails with: 'Cannot update 'version' and 'version' at the same time'
My assumption is that in 'upsert' mode mongo should only use "$setOnInsert" when no matching item is found - but it works in some other way.
Is it possible to implement such operation in one atomic mongoDB call?
PS: MongoDB documentation regarding findOneAndUpdate() and upsert() is fuzzy - at least I cannot get why this error arizes from their description.
Also there is similar question here - findAndModify fails with error: "Cannot update 'field1' and 'field1' at the same time - accepted, but again with no clear reasoning.
You can just remove the update operator of $setOnInsert as this will be set to the value specified in the $inc operator if the document does not exist
https://docs.mongodb.com/v3.2/reference/operator/update/inc/#behavior
If the field does not exist, $inc creates the field and sets the field to the specified value.
Example from the mongo shell:
> db.dropDatabase()
{ "ok" : 1 }
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true})
{ "_id" : "meta", "version" : 1 }
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true})
{ "_id" : "meta", "version" : 2 }
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true})
{ "_id" : "meta", "version" : 3 }
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true})
{ "_id" : "meta", "version" : 4 }
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true})
{ "_id" : "meta", "version" : 5 }
If you need to set a given version on the first initial insert, then mongodb does not support any operators to support this atomically, however the follow would be safe and is a common workaround:
> db.dropDatabase()
{ "dropped" : "test", "ok" : 1 }
> function updateMeta(){
... function update(){
... return db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {returnNewDocument: true});
... }
...
... var result = update();
...
... if(result === null){
... db.test.insert({_id: "meta", version: -10});
... result = update();
... }
...
... return result;
... }
>
> updateMeta()
{ "_id" : "meta", "version" : -9 }
> updateMeta()
{ "_id" : "meta", "version" : -8 }
> updateMeta()
{ "_id" : "meta", "version" : -7 }
> updateMeta()
{ "_id" : "meta", "version" : -6 }
> updateMeta()
{ "_id" : "meta", "version" : -5 }
>

Positional operator and field limitation

In a find query projection, fields I specify after the positional operator are ignored and the whole document is always returned.
'myArray.$.myField' : 1 behave exactly like 'myArray.$' : 1
the positional operator selects the right document. But this document is quite big. I would like to project only 1 field from it.
Exemple:
db.getCollection('match').find({"participantsData.id" : 0001}, { 'participantsData.$.id': 1, })
here the response I have
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001,
"plenty" : "of",
"other" : "fields",
"and" : "subdocuments..."
}
]
}
This is what I want
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001
}
]
}
Is it possible with mongo?
Yes it can be done in mongo
Please try the below query
db.getCollection('match').find(
{"participantsData.id" : 0001},
{"participantsData.id": 1, "matchCreation": 1 })
This will give you the below result
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 1
}
]
}

mongodb findAndModify update element in array

There is an bson document:
{
"_id" : ObjectId("5718441f5116a60b08000b8c"),
"mails" : [
{
"id" : 2,
"a" : [
{
"a" : 1
},
{
"a" : 2
}
]
},
{
"id" : 1,
"a" : [
{
"a" : 1
},
{
"a" : 2
}
]
}
]
}
I need to return and clear the array "a" which belong to "mails.id == x" for given document. So I use findAndModify like:
db.mail.findAndModify({query: {"_id":ObjectId("5718441f5116a60b08000b8c")}, update: {$set:{"mails.$.a":[]}}, new: false, fields:{"mails":{$elemMatch:{"id":1}}}})
However this don't work. The problem is the $set should apply on one document in array rather than the whole document. So I need a projection to project it out.
If I left update to blank, it will return the desired part:
{
"_id" : ObjectId("5718441f5116a60b08000b8c"),
"mails" : [
{
"id" : 1,
"a" : [
{
"a" : 1
},
{
"a" : 2
}
]
}
]
}
But I don't know how to clear the array 'a' in 'mails'
You have to specify array element match in the query:
db.mail.findAndModify({query: {"_id":ObjectId("5718441f5116a60b08000b8c"), "mails":{$elemMatch:{"id":1}}}, update: {$set:{"mails.$.a":[]}}, new: false, fields:{"mails":{$elemMatch:{"id":1}}}})
mails.$ in you update matches the first matched element in the doc, so you have to match it in the query. Also, this query will update the doc, but it will return the old version, since you use new: false, if you want to get the updated version set it to true.