MongoDB - Delete item in nested array - arrays

I saw a lot of similar questions about this but couldn't make it work in my collection.
How can I delete a specific comment (you can assume that I know the userId).
For example, I want to remove comment with commentId=3 and I know that it's under userId=1.
[
{
userId: "1",
posts: [
{
postId: "2",
comments: [
{
commentId: "3",
...
},
...
]
},
...
]
},
...
]
Thanks!

This works with Mongodb 4.2 and will remove first array entry from "comments" if it matches the "commentId" 3
db.posts.update(
{"userId" : "1"},
{$pull : {"posts.$[].comments" : {"commentId":"3"}}}
)
If you would like to remove all array entries use:
db.posts.update(
{"userId" : "1"},
{$pull : {"posts.$[].comments" : {"commentId":"3"}}},
{"multi": true}
)

Have you tried using the deleteOne()function ? try and have a look on MongoDb documentation. MongoDbDocumentation

Related

MongoError: Found multiple array filters with the same top-level field name

folks. I'm working in Node.js with a MongoDB collection that has a field that is an array of objects, like so:
{_id: 'someIdNumber',
text: 'some text',
replies: [{_id: 'someReplyId', replyText: 'some reply text', password: 'somePassword'}, {...}]
I'm trying to update the replyText field of the replies array using the $[<identifier>] array update operator as shown in the MongoDB documentation: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/ What I'm trying to do is as follows:
db.collection('collectionName').updateOne(
{ _id: ObjectID('whateverId') },
{ $set: { "replies.$[elem].replyText": "new text" } },
{
arrayFilters: [{ "elem._id": ObjectID(req.body.reply_id)}, {"elem.password": 'whateverPassword}]
},
(err, data) => {console.log('hooray, it worked')}
This throws an error, MongoError: Found multiple array filters with the same top-level field name elem. If I get rid of one of my arrayFilters, this fixes the error, but obviously at the expense of my filtering conditions.
The MongoDB documentation's example of this process, (I've shortened the collection students2 to a single document,) is as follows:
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 6 },
{ "grade" : 85, "mean" : 100, "std" : 4 },
{ "grade" : 85, "mean" : 100, "std" : 6 }
]
}
db.students2.update(
{ },
{ $inc: { "grades.$[elem].std" : -1 } },
{ arrayFilters: [ { "elem.grade": { $gte: 80 }, "elem.std": { $gt: 5 } } ], multi: true }
)
The syntax is a tiny bit different because the documentation is using the Mongo shell method, not Node.js, but otherwise, it looks to me like I'm doing what the documentation says to do. I'm using updateOne and not update because I only want to update one document, and I'm not using multi for the same reason.
Any insight on how I could get this to work with both arrayFilters intact would be much appreciated.
Thanks for your time!
Got it! Unfortunately I cannot upvote the question/comment hence adding here for others to benefit. Thanks #LuosRestil and #typesafe for getting me to the fix. I had the same syntax error where I tried to add multiple expressions for the same field in the array for the arrayFilters. It should be once expression per field.
WRONG:
arrayFilters: [
{ "elemA.<fieldName1>": "value1" },
{ "elemA.<fieldName2>": "value2" },
{ "elemA.<fieldName3>": "value3" }
];
CORRECT:
arrayFilters: [
{
"elemA.<fieldName1>": "value1",
"elemA.<fieldName2>": "value2",
"elemA.<fieldName3>": "value3"
}
];

How to delete particular no of elements from array in mongodb

I have collection as below
"_id" : "PS8720_18",
"AlertID" : "18",
"Status" : "ACTIVE",
"triggerHistory" : [
{"triggerId" : "1535081507421"},
{"triggerId" : "1535105196735"},
{"triggerId" : "1535341330335"},
{"triggerId" : "1535364578821"}
]
I want to delete all element and just want keep last two entries in the array. Each document has different no of elements in array. How do I achieve this?
Please Check with the following Query
db.getCollection('youtablename').update({}, {
$push: {
triggerHistory: {
$each: [ ],
$slice: -2
}
}},{multi:true})
Hope it Helps !!

Create a document with an array from filtered elements in an existing document array

I've asked this question before but not in the clearest way since I had no responses :( so I thought I would try again.
I have a document as shown below, I want to create a new document which only picks the names in the array where language = "English".
{
"_id" : ObjectId("564d35d5150699558156942b"),
"objectCategory" : "Food",
"objectType" : "Fruit",
"objectName" : [
{
"language" : "English",
"name" : "Apple"
},
{
"language" : "French",
"name" : "Pomme"
},
{
"language" : "English",
"name" : "Strawberry"
},
{
"language" : "French",
"name" : "Fraise"
}
]
}
I want the $out document to look like this below. I know I can filter a document by content but, I want to filter within a single document not across a collection. For getting the right document in the first place, I would have a query to $find objectCategory = "Food" and objectType = "Fruit"
{
"_id" : ObjectId("564d35d5150699558156942b"),
"objectCategory" : "Food",
"objectType" : "Fruit",
"objectName" : [
"name" : "Apple",
"name" : "Strawberry"
]
}
Thanks, Matt
wow, ah, I really thought I found it with:
db.serviceCatalogue.find({objectName: {"$elemMatch": {language: "English"}}}, {"objectName.name": 1})
;thanks to: Retrieve only the queried element in an object array in MongoDB collection
However, it did nothing, I must have dreamt it worked. How do you just get the array positions where the value of a field called language = 'English'?
this is only an example of what I want to do, it seems like this is just painful, especially with no-one answering other than me :)

Logstash split xml into array

Is it possible to convert xml into array of objects using logstash?
That'd be my sample document:
{
"Title" : "My blog title",
"Body" : "My first post ever",
"Metadata" : "<root><Tags><TagTypeID>1</TagTypeID><TagValue>twitter</TagValue></Tags><Tags><TagTypeID>1</TagTypeID><TagValue>facebook</TagValue></Tags><Tags><TagTypeID>2</TagTypeID><TagValue>usa</TagValue></Tags><Tags><TagTypeID>3</TagTypeID><TagValue>smartphones</TagValue></Tags></root>"
}
Ideally, I'd like to output this:
{
"Title" : "My blog title",
"Body" : "My first post ever",
"Metadata" : [
{
"TagTypeID" : "1",
"TagValue" : "twitter"
},
{
"TagTypeID" : "1",
"TagValue" : "facebook"
},
{
"TagTypeID" : "2",
"TagValue" : "usa"
},
{
"TagTypeID" : "3",
"TagValue" : "smartphones"
}
]
}
However I'm not able to achieve that. I tried using xml filter like that:
xml
{
source => "Metadata"
target => "Parsed"
}
However, it outputs this
{
"Title" : "My blog title",
"Body" : "My first post ever",
"#version" : "1",
"#timestamp" : "2015-10-27T17:21:31.961Z",
"Parsed" : {
"Tags" : [
{
"TagTypeID" : ["1"],
"TagValue" : ["twitter"]
},
{
"TagTypeID" : ["1"],
"TagValue" : ["facebook"]
},
{
"TagTypeID" : ["2"],
"TagValue" : ["usa"]
},
{
"TagTypeID" : ["3"],
"TagValue" : ["smartphones"]
}
]
}
}
I don't want my values to be stored as arrays (I know there's always going to be just one value there).
I know what fields are going to be brought back from my input, so I can map structure myself and this doesn't need to be dynamic (although that would be nice).
Allow splitting of lists / arrays into multiple events seemed to be useful, but it's poorly documented and I couldn't find information how to use this filter for my use-case.
Logstash, split event from an xml file in multiples documents keeping information from root tags is similar, but not exactly what I'd like to achieve.
Logstash: XML to JSON output from array to string this seems to be useful, however it hardcodes that first element of array must be outputed as single item (not part of array). It brings me back this:
{
"Title" : "My blog title",
"Body" : "My first post ever",
"#version" : "1",
"#timestamp" : "2015-10-27T17:21:31.961Z",
"Parsed" : {
"Tags" : [
{
"TagTypeID" : "1",
"TagValue" : "twitter"
},
{
"TagTypeID" : ["1"],
"TagValue" : ["facebook"]
},
{
"TagTypeID" : ["2"],
"TagValue" : ["usa"]
},
{
"TagTypeID" : ["3"],
"TagValue" : ["smartphones"]
}
]
}
}
Can this be done without having to create custom filters? (I've no
experience in Ruby)
Or am I missing something basic here?
Here is one approach using logstash's builtin ruby filter.
Filter section:
filter {
xml {
source => "Metadata"
target => "Parsed"
}
ruby { code => "
event['Parsed']['Tags'].each do |x|
x.each do |key, value|
x[key] = value[0]
end
end"
}
}
Output:
"Parsed":{
"Tags":[
{
"TagTypeID":"1",
"TagValue":"twitter"
},
{
"TagTypeID":"1",
"TagValue":"facebook"
},
{
"TagTypeID":"2",
"TagValue":"usa"
},
{
"TagTypeID":"3",
"TagValue":"smartphones"
}
]
}
If I understand you correctly this is your desired result. You need to specify the xml field inside the ruby filter: event['Parsed']['Tags']. Does it need to be more dynamic? Let me know if you need anything else.
Can this be done without having to create custom filters? (I've no experience in Ruby)
Well, yes and no. Yes, because this is not really a custom filter but a built-in solution. No, because I tend to say this can not be done without Ruby. I must admit that Ruby seems to be an unattractive solution. However, this is a flexible approach and 5 lines of code shouldn't hurt that much.
Most recent Logstash version (5.1.1 at this point) has updated XML filter, which has force_array option. It is enabled by default. Setting this to false will do exactly the same thing as ruby filter in accepted answer.
Taken from documentation:
force_contentedit
Value type is boolean
Default value is false
By default the filter will expand attributes differently from content inside of tags. This option allows you to force text content and attributes to always parse to a hash value.
https://www.elastic.co/guide/en/logstash/current/plugins-filters-xml.html#plugins-filters-xml-force_array

MongoDB: How do I update a single subelement in an array, referenced by the index within the array?

I'm trying to update a single subelement contained within an array in a mongodb document. I want to reference the field using its array index (elements within the array don't have any fields that I can guarantee will be unique identifiers). Seems like this should be easy to do, but I can't figure out the syntax.
Here's what I want to do in pseudo-json.
Before:
{
_id : ...,
other_stuff ... ,
my_array : [
{ ... old content A ... },
{ ... old content B ... },
{ ... old content C ... }
]
}
After:
{
_id : ...,
other_stuff ... ,
my_array : [
{ ... old content A ... },
{ ... NEW content B ... },
{ ... old content C ... }
]
}
Seems like the query should be something like this:
//pseudocode
db.my_collection.update(
{_id: ObjectId(document_id), my_array.1 : 1 },
{my_array.$.content: NEW content B }
)
But this doesn't work. I've spent way too long searching the mongodb docs, and trying different variations on this syntax (e.g. using $slice, etc.). I can't find any clear explanation of how to accomplish this kind of update in MongoDB.
As expected, the query is easy once you know how. Here's the syntax, in python:
db["my_collection"].update(
{ "_id": ObjectId(document_id) },
{ "$set": { 'documents.'+str(doc_index)+'.content' : new_content_B}}
)
Update of an array element referenced by an index (e.g. 1 ) in Mongo Shell can also be done by directly indicating the index value:
db.my_collection.update(
{_id : "document_id"},
{$set : {"my_array.1.content" : "New content B"}}
)
In mongo style, using '$' positional operator.
Check out this link for details.
db.my_collection.update(
{_id: ObjectId(document_id), my_array.1 : 1 },
{ $set: { "my_array.$.content" : "NEW content B" } }
)
When it's required to update an array element without knowing it's actual index but having a unique identifier of the element:
// Modify a comment in a bucket
db.POST_COMMENT.update(
{
"_id": ObjectId("5ec424a1ed1af85a50855964"),
"bucket.commentId": "5eaf258bb80a1f03cd97a3ad_lepf4f"
},
{
$set: {
"bucket.$.text": "Comment text changed",
"bucket.$.createdDate": ISODate("2015-12-11T14:12:00.000+0000")
}
}
)
Here "bucket.commentId" is the unique identifier of an array element.
A neat way to do it in Javascript, with backticks, is:
const index = 1;
... { $set: { [`myArray.${index}.value`]: "new content"} }, ...
db.my_collection.update(
{_id: ObjectId(document_id), my_array : { ... old content A ... } },
{ $set: { "my_array.$.content" : "NEW content B" } }
)
When it's required to update an array element without knowing it's an actual index but having a unique identifier of the element
db.getCollection('profiles').update(
{
'userId':'4360a380-1540-45d9-b902-200f2d346263',
'skills.name':'css'
},
{
$set: {'skills.$.proficiencyLevel': 5}
},
{
multi: true
}
)
If you want to update the authorName of the testimonial having _id = 60c4918d74c30165ba585c14 from the following document:
"business": {
"ownerId": "60a5ebad7432d91b853c0277",
"testimonials": [
{
"_id": "60c4912877dd5664f2201b08",
"authorName": "user1",
"authorBio": "User from 10 years",
"image": "user1/img1",
"review": "asdfiuahsdfpoiuashdpfoaspdlfkjn;alsfpuoh"
},
{
"_id": "60c4918d74c30165ba585c14",
"authorName": "user2",
"authorBio": "User from 3 years",
"image": "user/img1",
"review": "asdpfuahsfljnsadfoihsf."
}
],
"createdAt": "2021-06-11T20:12:56.666Z",
"updatedAt": "2021-06-12T11:11:56.696Z",
}
Then the following mongoose query works:
await BusinessModel.updateOne(
{
'_id': Mongoose.Types.ObjectId(businessId),
'testimonials._id': Mongoose.Types.ObjectId('60c4918d74c30165ba585c14')
},
{
$set: { 'testimonials.$.authorName' : 'new author name' }
}
);
Also refer to https://docs.mongodb.com/drivers/node/fundamentals/crud/write-operations/embedded-arrays/
You can use the updateOne function of mongoDB passing the index of the element in array, if the key of old content B is "value" per example:
[
...
"value" : "old content A"
"value" : "old content B"
"value" : "old content C"
...
]
the command should be like this:
db.collection.updateOne({"_id" : "...,"},{$set: {"my_array.1.value": "NEW content B"}})
If you have a "plain" array containing simple strings, this did the trick:
db.paintings.insertMany([
{_id: 1, colors: ["red", "blue", "green"]},
{_id: 2, colors: ["red", "yellow"]}
db.paintings.updateMany(
{colors: "red"},
{$set: {"colors.$": "magenta"}})
the positional $ operator acts as a placeholder for the first element that matches the query document
Source

Resources