How can I remove an element that is contained in the third level of an array? - mongodb

my database has a structure like this:
{
"universe": "comics",
"saga": [
{
"name": "x-men",
"characters": [
{
"character": "wolverine",
"powers": [
{
"power": "self-recovery"
},
{
"power": "Super strength"
},
{
"power": "steels claws"
}
]
},
{
"character": "cyclops",
"powers": [
{
"power": "rays"
},
{
"power": "Super strength"
}
]
}
]
}
]
},
{
"universe": "comics",
"saga": [
{
"name": "spiderman",
"characters": [
{
"character": "venom",
"powers": [
{
"power": "Super strength"
}
]
}
]
}
]
}
I basically want to learn how to do operations with complex arrays, this time I think I will learn too much if I get the answer to this question.
I want to delete the objects where "power:"self-recovery"
something like $.saga.characters.$.powers
I don't know how to do it because of the number of levels this property is under the main root.
normally i would use something like that:
db.mydb.update(
{
saga: {
$elemMatch: { saga.characters.$ },
},
},
{ $pull: { powers: { power: "Super strength"
} }},
{
new: true,
multi:true
},
for this example it should delete as many objects where {" power ":" self-recovery"} (in this case, only one object is deleted, which is where the character is wolverine)
but i don't know how to do what i need.

Try the positional all $[] operator in MongoDb 3.6+
var query = {
universe: 'comics'
};
var update = {
$pull: {
'saga.$[].characters.$[].powers': {
power: 'self-recovery'
}
}
};
var options = {
multi: true
};
db.collection.update(query, update, options);

db.test.update(
{
"saga.characters.powers": {
"$elemMatch": {
"power": "self-recovery"
}
}
},
{
"$pull": {
"saga.$[].characters.$.powers": {
"power":"self-recovery"
}
}
}
)
It removes the respective power object.It results
/* 1 */
{
"_id" : ObjectId("5f11e25a9e001b53e39985fd"),
"universe" : "comics",
"saga" : [
{
"name" : "x-men",
"characters" : [
{
"character" : "wolverine",
"powers" : [
{
"power" : "Super strength"
},
{
"power" : "steels claws"
}
]
},
{
"character" : "cyclops",
"powers" : [
{
"power" : "rays"
},
{
"power" : "Super strength"
}
]
}
]
}
]
}
Kindly check and let me know missing things

Related

Query nested array from document

Given the following document data in collection called 'blah'...
[
{
"_id" : ObjectId("60913f55987438922d5f0db6"),
"procedureCode" : "code1",
"description" : "Description 1",
"coding" : [
{
"system" : "ABC",
"code" : "L111"
},
{
"system" : "DEFG",
"code" : "S222"
}
]
},
{
"_id" : ObjectId("60913f55987438922d5f0dbc"),
"procedureCode" : "code2",
"description" : "Description 2",
"coding" : [
{
"system" : "ABC",
"code" : "L999"
},
{
"system" : "DEFG",
"code" : "X3333"
}
]
}
]
What I want to get is all of the coding elements where system is ABC for all parents, and an array of codes like so.
[
{ "code": "L111" },
{ "code": "L999" },
]
If I use db.getCollection('blah').find({"coding.system": "ABC"}) I get the parent document with any child in the coding array of ICD.
If I use...
db.getCollection("blah")
.find({ "coding.system": "ABC" })
.projection({ "coding.code": 1 })
I do get the parent documents which have a child with a system of "ABC", but the coding for "DEFG" seems to come along for the ride too.
{
"_id" : ObjectId("60913f55987438922d5f0db6"),
"coding" : [
{
"code" : "L989"
},
{
"code" : "S102"
}
]
},
{
"_id" : ObjectId("60913f55987438922d5f0dbc"),
"coding" : [
{
"code" : "L989"
},
{
"code" : "X382"
}
]
}
I have also tried experimenting with:
db.getCollection("blah").aggregate(
{ $unwind: "$coding" },
{ $match: { "system": "ICD" } }
);
.. as per this page: mongoDB query to find the document in nested array
... but go no where fast with that approach. i.e. no records at all.
What query do I need, please, to achieve something like this..?
[
{ "code": "L111" },
{ "code": "L999" },
...
]
or even better, this..?
[
"L111",
"L999",
...
]
db.collection.aggregate([
{
$match: { "coding.system": "ABC" }
},
{
$unwind: "$coding"
},
{
$match: { "coding.system": "ABC" }
},
{
$project: { code: "$coding.code" }
}
])
mongoplayground
db.collection.aggregate([
{
$match: { "coding.system": "ABC" }
},
{
$unwind: "$coding"
},
{
$match: { "coding.system": "ABC" }
},
{
$group: {
_id: null,
coding: { $push: "$coding.code" }
}
}
])
mongoplayground
Instead of $unwind, $match you can also use $filter:
db.collection.aggregate([
{ $match: { "coding.system": "ABC" } },
{
$project: {
coding: {
$filter: {
input: "$coding",
cond: { $eq: [ "$$this.system", "ABC" ] }
}
}
}
}
])

MongoDb fetch subset instead of whole record

I have a MongoDB document structure like this:
[
{
"locale":"en",
"translations":[
{
"name":"translation1",
"value":"enValue"
},
{
"name":"translation2",
"value":"enValue"
},
{
"name":"translation3",
"value":"enValue"
}
]
},
{
"locale":"ru",
"translations":[
{
"name":"translation1",
"value":"ruValue"
},
{
"name":"translation2",
"value":"ruValue"
},
{
"name":"translation3",
"value":"ruValue"
}
]
}
]
and I need to get the translation with name translation1 for locale en. I'm trying to do it like this: db.translations.find({"locale" : "en", "translation.name": "translation1"} ) but it returning whole locale row, with all translations instead of just translation1. I'm new to MongoDB, what I'm doing wrong?
you can use $elemMatch for this.
db.translations.find({"locale" : "en"},
{ translations: { $elemMatch: { name: "translation1" } } } )
Result:
{
"_id" : ObjectId("5e845ba1005e625a6237d2e0"),
"translations" : [
{
"name" : "translation1",
"value" : "enValue"
}
]
}

Mongo Db query using if condition

I have a Mongodb Data which looks like this
{
"userId" : "123",
"dataArray" : [
{
"scheduledStartDate" : ISODate("2018-08-30T11:34:36.000+05:30"),
"scheduledEndDate" : ISODate("2018-08-30T11:34:36.000+05:30"),
"Progress" : 0,
"ASD":""
},
{
"scheduledStartDate" : ISODate("2018-09-22T11:34:36.000+05:30"),
"scheduledEndDate" : ISODate("2018-10-01T11:34:36.000+05:30"),
"Progress" : 0,
"ASD":ISODate("2018-08-30T11:34:36.000+05:30"),
}
],
"userStatus" : 1,
"completionStatus" : "IP",
}
I want to find those document where condition is something like this
(PROGRESS<100||(PROGRESS==100&&ASD not exists)).
This should get you going ($elemMatch):
db.collection.find({
dataArray: {
$elemMatch: {
$or: [
{ Progress: { $lt: 100 } },
{ $and: [
{ Progress: { $eq: 100 } },
{ ASD: { $exists: false } }
]}
]
}
}
})
UPDATE based on your comment - this is even easier:
db.collection.find({
$or: [
{ "dataArray.Progress": { $lt: 100 } },
{ $and: [
{ "dataArray.Progress": { $eq: 100 } },
{ "dataArray.ASD": { $exists: false } }
]}
]
})

How do I get the current validator of a mongo collection?

How do I get the current validator of a mongo collection?
Or is there no way, and I just need to overwrite it?
You can get it with the db.getCollectionInfos() method.
Example:
Create a collection in an empty database:
db.createCollection( "contacts",
{
validator: { $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /#mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
]
},
validationAction: "warn"
}
)
{ "ok" : 1 }
Run the command:
db.getCollectionInfos()[0].options.validator
Result:
{
"$or" : [
{
"phone": {
"$type": "string"
}
},
{
"email": {
"$regex": /#mongodb\.com$/
}
},
{
"status": {
"$in": [
"Unknown",
"Incomplete"
]
}
}
]
}
The validator is found as an object in the options object.

correctly grouping documents in aggregate pipeline in order to find setintersection

say I have these two documents:
{
"_id":"sampleA",
"value":{
"data":[
{
"thing":"A"
},
{
"thing":"B"
},
{
"thing":"C"
},
{
"thing":"D"
},
{
"thing":"E"
}
]
}
}
{
"_id":"sampleB",
"value":{
"data":[
{
"thing":"C"
},
{
"thing":"D"
},
{
"thing":"E"
},
{
"thing":"F"
}
]
}
}
and I want to group them into one document, retaining the label of "sampleA" or "sampleB" such as
{
"_id": null,
"sampleA": [
{
"thing": "A"
},
{
"thing": "B"
},
{
"thing": "C"
},
{
"thing": "D"
},
{
"thing": "E"
}
],
"sampleB": [
{
"thing": "C"
},
{
"thing": "D"
},
{
"thing": "E"
},
{
"thing": "F"
}
]
}
So that way I can use the set intersection operator. How do I go about doing this? I tried:
db.testz.aggregate(
[{
$match: {
_id: {
$in: ["sampleA", "sampleB"]
}
}
}, {
'$group': {
_id: null,
a: {
$push: "$value"
}
}
}]
);
which gives me
{
"_id": null,
"a": [
{
"data": [
{
"thing": "A"
},
{
"thing": "B"
},
{
"thing": "C"
},
{
"thing": "D"
},
{
"thing": "E"
}
]
},
{
"data": [
{
"thing": "C"
},
{
"thing": "D"
},
{
"thing": "E"
},
{
"thing": "F"
}
]
}
]
}
Presumably I could then use the set intersection operator if I could index the items in a?
db.testz.aggregate(
[{
$match: {
_id: {
$in: ["sampleA", "sampleB"]
}
}
}, {
'$group': {
_id: null,
a: {
$push: "$value"
}
}
}, {
'$project': {
int: {
$setIntersection: ["$a.0", "$a.1"]
}
}
}]
);
^^ obviously this last step here doesn't work, but I am trying to illustrate the point.
I think the only way to do this presently (MongoDB 2.6) is to unwind the arrays and then recollect in a set:
> db.testz.aggregate([
{ "$match" : { "_id" : { "$in" : ["sampleA", "sampleB"] } } },
{ "$unwind" : "$value.data" },
{ "$group" : { "_id" : 0, "intersection" : { "$addToSet" : "$value.data" } } }
])
It's not an efficient way to do it, but it gets the job done. I was probing you for more specific info to see if there was some way to avoid this answer :(