The dollar ($) prefixed field '$oid' in 'features.0._id.$oid' is not valid for storage - mongodb

I have the following Mongo collection attached below.
it is existing one not new collection created, I am trying to push new item in the features array, but I am not able to achieve that due to the following error: The dollar ($) prefixed field '$oid' in 'features.0._id.$oid' is not valid for storage.
using this script:
db.getCollection("test").update({ _id: "RYB0001" },
{
$push: {
"features": {
"_id": {
"$oid": ObjectId(),
},
"featureItems": [],
"typeId": "type3"
}
}
});
If it is a driver version issue, any workaround can I do to push the new item to the current collection?
MongoDb version: 4.0.4
Mongo Collection screenshot

The $oid notation is part of MongoDB's Extended JSON. I assume the data in your database doesn't actually have that key-value pair and it was only represented that way after using something like JSON.stringify(obj)
> db.test.find({});
{ "_id" : "RYB0001", "features" : [ { "_id" : ObjectId("5e40d46a97abdef3faa0d5d9"), "featureItems" : [ ], "typeId" : "type3" } ] }
> JSON.stringify(db.test.find({})[0]);
{"_id":"RYB0001","features":[{"_id":{"$oid":"5e40d46a97abdef3faa0d5d9"},"featureItems":[],"typeId":"type3"}]}
You'll need to generate a new ObjectId using its constructor "_id": new ObjectId(), note the new keyword there, I think #Thilo may have missed that in their comment.
db.getCollection("test").update({ _id: "RYB0001" },
{
$push: {
"features": {
"_id": new ObjectId(),
"featureItems": [],
"typeId": "type3"
}
}
});

Related

Mongo pull multiple elements inside an array of object

I'm trying to pull one or multiple objects from an array and I noticed something odd.
Let's take the following document.
{
"_id" : UUID("f7e80c8e-6b4a-4741-95a3-2567cccf9e5f"),
"createdAt" : ISODate("2021-07-19T17:07:28.499Z"),
"description" : null,
"externalLinks" : [
{
"id" : "ZV8xMjM0NQ==",
"type" : "event"
},
{
"id" : "cF8xMjM0NQ==",
"type" : "planning"
}
],
"updatedAt" : ISODate("2021-07-19T17:07:28.499Z")
}
I wrote a basic query to pull one element of externalLinks which looks like
db.getCollection('Collection').update(
{
_id: {
$in: [UUID("f7e80c8e-6b4a-4741-95a3-2567cccf9e5f")]
}
}, {
$pull: {
externalLinks: {
"type": "planning",
"id": "cF8xMjM0NQ=="
}
}
})
And it's working fine. But it's getting trickier when I want to pull multiple element from the externalLinks. And I'm using the operator $in for that.
And the strange behaviour is here :
db.getCollection('Collection').update(
{
_id: {
$in: [UUID("f7e80c8e-6b4a-4741-95a3-2567cccf9e5f")]
}
}, {
$pull: {
externalLinks: {
$in: [{
"type": "planning",
"id": "cF8xMjM0NQ=="
}]
}
}
})
And this query doesn't work. The solution is to switch both field from externalLinks
and do something like :
$in: [{
"id": "cF8xMjM0NQ==",
"type": "planning"
}]
I tried multiple things like : $elemMatch, $positioning but it should be possible to pull multiple externalLinks.
I also tried the $and operator without success.
I could easily iterate over the externalLinks to update but it'd be too easy.
And it's tickling my brain to choose that solution.
Any help would be appreciate, thank you !
Document fields have order, and MongoDB compares documents based on the order of the fields see here, so what field you put first matters.
After MongoDB 4.2 we can also do pipeline updates, that can be sometimes bigger, but they are much more powerful, and feels more like programming.
(less declarative and pattern matching)
This doesn't mean that you need pipeline update in your case but check this way also.
Query
pipeline update
filter and keep members that the condition doesn't exist
Test code here
db.collection.update(
{_id: {$in: ["f7e80c8e-6b4a-4741-95a3-2567cccf9e5f"]}},
[{"$set":
{"externalLinks":
{"$filter":
{"input": "$externalLinks",
"cond":
{"$not":
[{"$and":
[{"$eq": ["$$this.id", "ZV8xMjM0NQ=="]},
{"$eq": ["$$this.type", "event"]}]}]}}}}}])

mongodb $ causing error The positional operator did not find the match needed from the query

I’ve been trying to update the data in my mongoDB.
I want to update all products with a new productName field.
my data looks something like:
{
"id": "12345",
"products": [{
"id": 0
"productCode": "test",
"status": "PENDING",
},
{
"id": 1
"productCode": "test",
"status": "COMPLETE",
}],
}
When I try the following. I get this error The positional operator did not find the match needed from the query.
db.customers.updateMany(
{ id: "12345" },
{ $set: {
"products.$.productName": "Name here" }
}
)
If I do account.0.productName then it’s fine and updates. I’m not sure why $ is not working for me
db.customers.updateMany(
{ id: "12345" },
{ $set: {
"products.0.productName": "Name here" }
}
)
Positional operator is not working because you are not using the array into the find (first object)
If you try this query it will work as expected because you have the position finding by products.id.
Otherwise, if you don't have the position into array where update, yo can't use $ operator in this way. You need this query:
db.collection.update({
"id": "12345",
},
{
"$set": {
"products.$[].newField": "test2"
}
},
{
"multi": true
})
Mongo playground example here
Using $[] you can reference the array and add the value into each object.
$[] docs here
It says:
The all positional operator $[] indicates that the update operator should modify all elements in the specified array field.
That's exactly we want :)

how to update model from a list of documents to an array in mongodb

We have a MongoDB instance with a collection of users that is something like this:
{
"Username": "Amin-AMD",
"FriendsList": [
{
"UserId": "5e076f4b19e8cd000162c962",
"NickName": "Amin-Mobile",
"ClanName": null,
"ClanId": null,
"Level": NumberInt(1),
"ActiveCosmeticItems": [
"hair0",
"skin0",
"eye0",
"mouth0",
"daub0",
"acc"
],
"IsOnline": false
},
{
"UserId": "5e08a4a119e8cd000167929e",
"NickName": "saeed",
"ClanName": null,
"ClanId": null,
"Level": NumberInt(7),
"ActiveCosmeticItems": [
"hair5",
"skin2",
"eye2",
"mouth10",
"daub0",
"acc0"
],
"IsOnline": false
}
]
}
As shown above, I have embedded a list of Friends in our User's collection. but for a reason, we need to change this model to reference Friends. So I need to write a query to replace the whole FriendModel with just a UserId.
I have reached to this query but it throws an exception.
db.Users.updateMany({ "FriendsList" : { $ne : [] }}, { $set : { "FriendsList.$" : "FriendsList.$.UserId" }})
In fact, for each friend, I just need the UserId so the new FriendsList will be an array and it should be something like this:
{
"Username": "Amin-AMD",
"FriendsList": [
"5e076f4b19e8cd000162c962",
"5e08a4a119e8cd000167929e"
]
}
MongoDB version: 4.2.1
You have to do it in two steps :
Update existing data in DB :
As you can use aggregation pipeline in updates starting MongoDB version 4.2.
Query to update data :
db.collection.updateMany({ "FriendsList" : { $ne : [] }},[{$set :{FriendsList: '$FriendsList.UserId'}}])
But if your FriendsList.UserId are strings better convert them to ObjectId() as like below :
db.colleciton.updateMany({ "FriendsList" : { $ne : [] }},[{$set :{FriendsList: { $map: { input: '$FriendsList.UserId', in: {$toObjectId: '$$this'}} }}}])
Update existing mongoose model to restrict future writes on DB :
Mongoose Model :
So FriendsList will be an array of ObjectId()'s which will be referred to another new schema via ref field.
FriendsList:[{
type: mongoose.Schema.Types.ObjectId,
ref: "FriendList" /** 'FriendList' will be a mongoose schema refers to a collection */
}]
Ref : mongoose-populate
Try using User model
FriendsList:{
type: mongoose.Schema.Types.ObjectId,
ref: "FriendList"
}

Cannot add _id field to mongo subdocument in Mlab

In my Mlab mongo 3.2 database I have a collection that looks like this:
{
"_id": {
"$oid": "5752d....87985"
},
"name": "...etation",
"description": null,
"user_id": ".....",
"questions": [
{
"prompt": "The conclusions drawn seemed clear to most researchers, however, others were unconvinced, arguing that everything is open to ____________.",
"answer": "interpretation",
"created_at": "2014-11-09T14:59:38.154",
"updated_at": "2014-11-09T14:59:38.154",
"filled_answer": null
},
{
"id": 922,
"prompt": "His existential quest for Truth is in fact the key to his understanding and ____________ of the Bhagavad-Gītā.",
"answer": "interpretation",
"created_at": "2014-10-03T08:07:40.295",
"updated_at": "2014-10-03T08:07:40.295",
"filled_answer": null
},
}
There are two problems with the questions subdocument that I am struggling with:
Sometimes but not always there is a legacy "id" field that I want to $unset but my query is not working.
I want to add an _id ObjectID field where they do not already exist. Currently some have them and some don't.
I have tried a number of queries but none seem to work. For example:
db.droplets.updateMany({"questions.$._id": { $exists: false }},{ $set: {"questions.$._id": new ObjectId()}},{"multi": true, "upsert": true})
Mongo tells me "The positional operator did not find the match needed from the query"
Update
I have successfully found a way to delete all the questions using the following script:
db.droplets4.find().forEach(function (doc) {
doc.questions.forEach(function (question) {
if (question.id) {
delete question.id
}
});
db.droplets.save(doc);
});
But the same strategy is not working for adding Object IDs. This code does not work:
db.droplets4.find().forEach(function (doc) {
doc.questions.forEach(function (question) {
if (!question._id) { question._id = new ObjectId() }
});
db.droplets.save(doc);
});
This should work fine for you
db.droplets4.updateMany( {
"questions._id" : null
},{ $set: {"questions.$._id": new ObjectId()}},{"multi": true, "upsert": true})

How is findById() + save() different from update() in MongoDB

While trying to update a MongoDB document using Mongoose, can I use a findById() with a save() in the callback, or should I stick with traditional update methods such as findByIdAndModify, findOneAndModify, update(), etc.? Say I want to update the name field of the following document (please see a more elaborate example in the edit at the end, which motivated my question):
{
"_id": ObjectId("123"),
"name": "Development"
}
(Mongoose model name for the collection is Category)
I could do this:
Category.update({ "_id" : "123" }, { "name" : "Software Development" }, { new: true })
or I could do this:
Category.findById("123", function(err, category) {
if (err) throw err;
category.name = "Software Development";
category.save();
});
For more elaborate examples, it feels easier to manipulate a JavaScript object that can simply be saved, as opposed to devising a relatively complex update document for the .update() operation. Am I missing something fundamentally important?
Edited 7/21/2016 Responding to the comment from #Cameron, I think a better example is warranted:
{
"_id": ObjectId("123"),
"roles": [{
"roleId": ObjectId("1234"),
"name": "Leader"
}, {
"roleId": ObjectId("1235"),
"name": "Moderator"
}, {
"roleId": ObjectId("1236"),
"name": "Arbitrator"
}]
}
What I am trying to do is remove some roles as well as add some roles in the roles array of sub-documents in a single operation. To add role sub-documents, $push can be used and to remove role sub-documents, $pull is used. But if I did something like this:
Person.update({
"_id": "123"
}, {
$pull : {
"roles" : {
"roleId" : {
$in : [ "1235", "1236" ]
}
}
},
$push : {
"roles" : {
$each: [{
"roleId" : ObjectId("1237"),
"name" : "Developer"
}]
}
}
}
When I try to execute this, I get the error Cannot update 'roles' and 'roles' at the same time, of course. That's when I felt it is easier to find a document, manipulate it any way I want and then save it. In that scenario, I don't know if there is really any other choice for updating the document.
I typically like to use findById() when I am performing more elaborate updates and don't think you are missing anything fundamentally important.
However one method to be aware of in mongoose is findByIdAndUpdate(), this issues a mongodb findAndModify update command and would allow you to perform your first example with the following code: Category.findByIdAndUpdate("123", function(err, savedDoc) {...}).