Find array in array data in MongoDB - arrays

I want find in this document groups:
"document": {
"groups": [
{
"id": "5ccd5f7f34f82b0e3315b2f6"
},
{
"id": "73b43unbfkfmdmddfdf84jjk"
}
]
}
are contains some of my query array groups ID:
[ '5ccd5f7f34f82b0e3315b2f6',
'5cdeded7ace07216f5873b5d',
'5cdee5d114edac2cc00bb333' ]

A simple find query suffices:
db.collection.find({ 'groups.id' : {$in : [ '5ccd5f7f34f82b0e3315b2f6',
'5cdeded7ace07216f5873b5d',
'5cdee5d114edac2cc00bb333' ] }})

Related

Remove only one of the selected elements from the array in mongodb

I have an array that is like [1,2,1,2,3,5,2]. And I want to remove only one element amongst the selected elements. I used $pull operator and it doesn't work as I required, it remove all elements I specified.
db.user.updateOne({_id: ...}, {$pull:{'array': 1}})
I tried it and give this result: [2,2,3,5,2].
is there any way to get the result: [2,1,2,3,5,2]
This feature does not exist (and won't), as you can see in this Jira. ticket they choose they won't do this.
Here is a hacky work around - the strategy will be to find the index of the first matching element and slice it out of the array, like so:
db.collection.update({},
[
{
"$set": {
"array": {
"$concatArrays": [
{
$cond: [
{
$gt: [
{
"$indexOfArray": [
"$array",
1
]
},
0
]
},
{
"$slice": [
"$array",
0,
{
"$indexOfArray": [
"$array",
1
]
}
]
},
[]
]
},
{
"$slice": [
"$array",
{
"$add": [
{
"$indexOfArray": [
"$array",
1
]
},
1
]
},
{
"$size": "$array"
}
]
}
]
}
}
}
])
Mongo Playground
Query
$reduce the array starting with {"n-ar": [], "found": false}
the first time you find it you ignore it, and you set found=true
else you just $concat to add the member to the new-ar
*it can be generalized, like remove the first 4 occurences, if integer is used as found
*its pipeline update requires MongoDB >= 4.2
Playmongo
update({},
[{"$set":
{"ar":
{"$getField":
{"field": "n-ar",
"input":
{"$reduce":
{"input": "$ar",
"initialValue": {"n-ar": [], "found": false},
"in":
{"$cond":
[{"$and":
[{"$eq": ["$$this", 1]}, {"$eq": ["$$value.found", false]}]},
{"n-ar": "$$value.n-ar", "found": true},
{"n-ar": {"$concatArrays": ["$$value.n-ar", ["$$this"]]},
"found": "$$value.found"}]}}}}}}}])

Dictionary to Array of Objects

I have a dictionary field in my data like so:
"details": {
"code": "PPIO",
"product": [{
"productCode": "ADGT"
}]
}
I need to convert the field to an array of objects:
"details":
[
{
"code": "PPIO",
"product": [{
"productCode": "ADGT"
}]
}
]
You can use $addFields to overwrite existing field:
db.collection.aggregate([
{
$addFields: {
details: [ "$details" ]
}
}
])
Mongo Playground
EDIT:
For update you can use below syntax:
db.col.updateMany({}, [{ $set: { details: [ "$details" ] } }])
EDIT 2:
Alternatively, if updateMany doesn't work for you, you can use $out operator which will replace your collection entirely with an aggregation outcome:
db.collection.aggregate([
{
$addFields: {
details: [ "$details" ]
}
},
{ $out: "collection" }
])

MongoDB - Match all document not containing a combination of value

Let's say I have three document structured like so :
{
"_id": 1,
"conditions": [
["Apple", "Orange"],
["Lemon"],
["Strawberry"]
]
},
{
"_id": 2,
"conditions": [
["Apple"],
["Strawberry"]
]
},
{
"_id": 3,
"conditions": [
["Apple", "Lime"]
]
}
And I have an array, I'll call it ARC for this example :
ARC = [
"Apple",
"Lime",
"Banana",
"Avocado",
"Cherry"
]
I would like to return all document in which all conditions subarray values can't be found in the ARC array.
For example, with the data above, the first document should be returned because :
The Apple AND Orange combination is not in the ARC array
Lemon is not in the ARC array
Strawberry is not in the ARC array
The second document shouldn't be returned because :
Apple is in the ARC array
And the third document shouldn't be returned because :
The Apple AND Lime combination is in the ARC array
I've tried
db.example.find({"conditions": {$not: {$elemMatch: {$all: [ARC]}}}})
But it seems way too simple.. So, as expected, it doesn't work.
I know mongoDB is pretty powerful with all the aggregation and stuff but I'm a bit lost.
Do you know if it's possible with a query alone and if so, what should I look for ?
The query below should solve your problem.
var ARC = [
"Apple",
"Lime",
"Banana",
"Avocado",
"Cherry"
];
db.test.find(
{ $expr: {
$eq: [
{ $filter: { input: "$conditions", as: "c", cond: { $setIsSubset: [ "$$c", ARC] } } },
[ ]
]
}
}
)
It's made up of lots of parts so I'll try to break it down a bit, The first part is $expr within a find (or can be used within a $match in an aggregation) this allows us aggregation expressions within the query. So this allows us to use a $filter.
The $filter expression allows us to filter down the arrays in the condition field to check if any are a subset of the array ARC passed in.
We can actually take that filter an execute it on its own using an aggregation query:
db.test.aggregate([
{ $project: {
"example" : { $filter: { input: "$conditions", as: "c", cond: { $setIsSubset: [ "$$c", ARC] } } }
} }])
{ "_id" : 1, "example" : [ ] }
{ "_id" : 2, "example" : [ [ "Apple" ] ] }
{ "_id" : 3, "example" : [ [ "Apple", "Lime" ] ] }
The last part of the query is the $eq which is taking the value that is created with the filter and then matching it against an empty array [ ].
This is an aggregation approach. You should use $setIsSubset.
Below should be helpful:
db.collection.aggregate([
{
$match: {
$expr: {
$eq: [
true,
{
$allElementsTrue: {
$map: {
input: "$conditions",
as: "c",
in: {
$not: {
$setIsSubset: [
"$$c",
[
"Apple",
"Lime",
"Banana",
"Avocado",
"Cherry"
]
]
}
}
}
}
}
]
}
}
}
])
MongoPlayGroundLink

MongoDB - How to get all documents not being referenced by any document in a different collection

We have two collections, Teams and Matches. Every time a Match is reported, a new document is saved in that collection and its added to an array in the Team documents (teams[i].matches).
A now solved bug in our system has caused that the new Matches document were not referenced in their respectives Teams documents.
Is there a query for Mongo DB 3.6.9 that can help us find the Matches not referenced in Teams?
An aggregation pipeline may help you, using $lookup.
$lookup fetches documents from "Teams" that match the pipeline's $match.
let: { match_id: "$_id" } create a variable match_id corresponding to Match's _id.
$match expression keeps only Teams with match_id into Team's matches array.
as: "matches" stores Team that validate previous $match.
Last $match after $lookup step keeps matches array that are empty (Matches with no Teams)
db.Matches.aggregate([
{
$lookup: {
from: "Teams",
let: { match_id: "$_id" },
pipeline: [{
$match: {
$expr: {
$in: [ "$$match_id", "$matches" ]
}
}
}],
as: "matches"
},
},
{
$match: {
$expr: { $eq: [{ $size: "$matches" }, 0] }
}
}
]);
This has been tested with the following collection template and Mongo playground online editor :
db={
"Matches": [
{ "_id": 0 },
{ "_id": 1 },
{ "_id": 2 },
{ "_id": 3 },
{ "_id": 4 },
],
"Teams": [
{
"_id": 0,
matches: [ 0, 3 ],
},
{
"_id": 1,
matches: [],
},
{
"_id": 2,
matches: [ 0 ],
},
{
"_id": 3,
matches: [ 2 ],
}
]
}
The resulting output is :
[
{
"_id": 1,
"matches": []
},
{
"_id": 4,
"matches": []
}
]

Update array content within another array that don't have key

I have mongoDB content as below:
[
{
"_id":{
"$oid":"57c6699711bd6a0976cabe8a"
},
"ID":"1111",
"FullName":"AAA",
"Category":[
{
"CategoryId":{
"$oid":"57c66ebedcba0f63c1ceea51"
},
"_id":{
"$oid":"57e38a8ad190ea1100649798"
},
"Value":[
{
"Name":""
}
]
},
{
"CategoryId":{
"$oid":"57c3df061eb1e59d3959cc40"
},
"_id":{
"$oid":"57e38a8ad190ea1100649797"
},
"Value":[
[
"111",
"XXXX",
"2005"
],
[
"1212",
"YYYY",
"2000"
],
[
"232323",
"ZZZZZ",
"1999"
]
]
}
]
},
{
"_id":{
"$oid":"57c6699711bd6a0976cabe8a"
},
"ID":"1111",
"FullName":"BBB",
"Category":[
{
"CategoryId":{
"$oid":"57c66ebedcba0f63c1ceea51"
},
"_id":{
"$oid":"57e38a8ad190ea1100649798"
},
"Value":[
{
"Name":""
}
]
},
{
"CategoryId":{
"$oid":"57c3df061eb1e59d3959cc40"
},
"_id":{
"$oid":"57e38a8ad190ea1100649797"
},
"Value":[
[
"4444",
"XXXX",
"2005"
],
[
"7777",
"GGGG",
"2000"
],
[
"8888",
"ZZZZZ",
"1999"
]
]
}
]
}
]
Here I have an array named 'Category' where it contains objects with different category id.
I need to
select a particular category id - '57c3df061eb1e59d3959cc40'
From the above selected Category, we get 'Value' array
From Value array need to find if the second value is equal to 'ZZZZZ' ie. value[1] == 'ZZZZZ'
And now, update the matched value arrays with a new value at the end
Eg:
[
"232323",
"ZZZZZ",
"1999"
]
should be updated to
[
"232323",
"ZZZZZ",
"1999",
"update1"
]
and
[
"8888",
"ZZZZZ",
"1999"
]
should be updated to
[
"8888",
"ZZZZZ",
"1999",
"update1"
]
I have tried as below:
resume.update({
"Category.CategoryId": new ObjectId('57c3df191eb1e59d3959cc43'),
"Category.Value.$.1": 'ZZZZZ'
},
{"$set": {"Category.Value.$.3": "update1"}
}, function(err, resData){
res.send(resData);
});
But, nothing gets updated. Its there any way to get this work. Please help to update the inner array.
Thanks in advance.
Your goal is not possible at the moment since you need to update two positional elements.
There is a JIRA trackable for the sort of behaviour you want here: https://jira.mongodb.org/browse/SERVER-831
It's a problem since you need to match two elements positions:
the Category element with the matched CategoryId
the Value element in the Value array of arrays
If one of these wouldn't be an array it would have been possible.
Anyway, Your update try above was wrong. IF this feature was possible (and it is not!!!) it would have been something like this:
db.resume.update(
{
Category: {
$elemMatch: {
CategoryId: ObjectId('57c3df061eb1e59d3959cc40'),
Value: {
$elemMatch: {
'1': 'ZZZZZ'
}
}
}
}
},
{
$push: {
'Category.$.Value.$': 'update1'
}
}
)
The positional $ operator should be used during the update and not the find like you did, and it will update the first element that matched the query.
Doing the above will return the error:
Too many positional (i.e. '$') elements found in path 'Category.$.Value.$'
Because of the missing feature I explained at the top.
So, currently (version 3.2) you will not be able to do this unless you change your schema.

Resources