Conditional $push with a single query - arrays

Given the document schema in a collection called NamedLists:
{
"name":"FOO",
"items":[
{"primary":4524013,"secondary":9753847},
{"primary":4420220},
{"primary":5317116,"secondary":3838379},
{"primary":4230616},
{"primary":7389755,"secondary":3838576}
]
}
And the rules:
The "secondary" field may or may not exist on the objects in the items array
A document cannot have objects in its items array which have the same "primary" value
No two documents in the "NamedLists" collection may have an identical object in their items arrays
For Example the following is NOT allowed since {"primary":4420220} is in two different items arrays. However {"primary":4420220, "secondary": 7897897 } would be permitted.
{ "name":"FOO", "items":[
{"primary":4524013,"secondary":9753847},
{"primary":4420220},
{"primary":5317116,"secondary":3838379},
{"primary":4230616},
{"primary":7389755,"secondary":3838576}
]}
{ "name":"BOO", "items":[
{"primary":4420220}
]}
This query satisfies rule #2 but not #3:
db.NamedLists.update({ name: "FOO", { primary: { $ne: 4524013 } },
{ $push: { items: 4524013 } });
Is there a way to $push a new object into a given document's items array only when its primary value (or the whole object itself) does not match an object in another document's items array?
In SQL you could use nested SELECTs to achieve this. MongoDB?

Related

Delete many items from array of documents in mongodb that match this value

I am trying to delete all documents from an array of documents that match this value but cannot figure it out,
This is my delete query that is not working, courses is the array I need to be in and code is the document I need to check to see its value for deletetion. So if courses.code == 123, then delete and keep going sort of thing.
result = await mongoClient.db(DB_NAME).collection("technologies").deleteMany({ 'courses': {$in: 'courses.code'} });
That is what it looks like in my collection.
"name": "Sass",
"description": "Sass (Syntactically awesome style sheets) is a preprocessor scripting language that is interpreted or compiled into Cascading Style Sheets (CSS).",
"difficulty":2,
"courses":[
{"code":"PROG2700","name":"Client Side Programming"},
{"code":"PROG3017","name":"Full Stack Programming"}
]
},
Any help would be great, thanks!
You can filter documents that contain the field you want to update
Do tests before updates on the original collection.
After some searches, I've found this method:
const filter = { }
db.collection.update(filter,
{
$pull: {
"courses": {
code: {
$in: [
"PROG3017",
"PROG2700"
]
}
}
}
},
{
multi: true
})
This pulls out any element in the array where the value of code is $in the array.

How to query in list inside list inside list.... MongoDB aggregate query

I have a collection of documents "EvaluationGroups" in which each evaluation group object has a list of Evaluations.
Each Evaluation object has a list of Detail objects.
And each Detail contains a list of Label objects. Label is an object with a property named "LabelId" and a property named "Value".
(Details define a combination of labels and show some other data, and each combination of labels represents a different kind of evaluation type).
Example doc:
What I would like to do, is to filter all Evaluations in the lists inside EvaluationGroups, that have Details with any Label with the same LabelId and LabelValue (strings) of a Label given.
So, to sum up, I want to filter evaluations by label.
How can I achieve this with a mongodb query, using aggregate and match stages?
I tried this:
{ "$match" :
{
"$expr" : {
$anyElementTrue: {
$and: [
{ $eq: [ "$Evaluations.Details.Labels.LabelId", "5fe34b13f0031e1078e08b5c" ] },
{ $eq: [ "$Evaluations.Details.Labels.Value", "CREDIT" ] }
]
}
} }}
But I get 0 results, knowing that I do have evaluations that have that label given (with that Id and Value)..
Any help would be appreciated!
Playground
You used Id whereas your field is LabelId. Hence it didn't work.
You can simplify your query as mentioned below
have Details with any Label with the same LabelId and LabelValue (strings) of a Label given
{
"$match": {
$and: [
{
"Evaluations.Details.Labels.LabelId": 1
},
{
"Evaluations.Details.Labels.Value": "b"
}
]
}
}
If there are multiple match, it will return only the first match. It works similar to $elemMatch If you need to retrieve all matched Labels with the given Id and value, you need to make your query complex.
If it is the complete requirment, you can use this variant
db.collection.find({
$and: [
{
"Evaluations.Details.Labels.LabelId": 1
},
{
"Evaluations.Details.Labels.Value": "b"
}
]
})

Mongo find position by id in an Array of Objects

I have a range of documents
{
_id: ObjectId("5e388da4df54cb8efb47e61b"),
userId:'test_user'
productId:'product_6_id'
recommendations:{
_id:123
rankedList:[
0:{id:ObjectId('product_5_id'),Name:'Product_5'},
1:{id:ObjectId('product_6_id'),Name:'Product_6'},
2:{id:ObjectId('product_3_id'),Name:'Product_3'}],
Date:'2020-02-25T05:03:55.439+00:00'
}
},
{
_id: ObjectId("5e388da4df54cb8efb47e62b"),
userId:'test_user1'
productId:'product_3_id'
recommendations:{
_id:123
rankedList:[
0:{id:ObjectId('product_1_id'),Name:'Product_1'},
1:{id:ObjectId('product_5_id'),Name:'Product_5'},
2:{id:ObjectId('product_3_id'),Name:'Product_3'}],
Date:'2020-02-25T05:03:55.439+00:00'
}
}
and I need to find each time the position of productId within the Array of objects rankedList.
Thus here the answer would be positionIndex=1 for first doc and positionIndex=2 for second document.
I am quite confused with $indexOfArray and how I should use it here with aggregate.
Yes, you need $indexOfArray. The tricky part is that recommendations.rankedList is an array of objects however MongoDB allows you to use following expression:
$recommendations.rankedList.id
which evaluates to a list of strings, ['product_5_id', 'product_6_id', 'product_3_id'] in this case so your code can look like this:
db.collection.aggregate([
{
$project: {
index: {
$indexOfArray: [ "$recommendations.rankedList.id", "$productId" ]
}
}
}
])
Mongo Playground

Mongo update multiple fields of an object which is inside an array

Using Mongo findOneAndUpdate, I am trying to update just some fields in an object from array of objects.
My object:
mainObject:{
_id: '123',
array:[
{title:'title' , name:'name', keep:'keep'},
{title:'title', keep:'keep'},
]
}
I want to change title and name for the first object in array, and keep the keep field unchanged.
This is my closest approach using Positional Operator:
// here i set dynamic arguments for query update
// sometimes i need to update only one field, sometime i need to update more fields
// also, is there a better way to do this?
let title
let name
if (args.title) {
title = { title: args.title };
}
if (args.name) {
name= { name: args.name};
}
db.Test.findOneAndUpdate(
{ _id: args.id, 'mainObject.array.title': args.title},
{
$set: {
'mainObject.array.$[]': {
...title,
...name
}
}
}
)
this problem is that it replace the whole object, the result is:
mainObject:{
array:[
{title:'changed' , name:'changed'}, //without keep... :(
{title:'title', keep:'keep'},
]
}
Should I use aggregation framework for this?
It has to be like this :
db.test.findOneAndUpdate({'mainObject.array.title': 'title'},
{$set : {'mainObject.array.$.title':'changed','mainObject.array.$.name': 'changed'}})
From your query, $ will update the first found element in array that matches the filter query, if you've multiple elements/objects in array array then you can use $[] to update all of those, let's see your query :
'mainObject.array.$[]': {
...title,
...name
}
Major issue with above query is that it will update all the objects in array array that match the filter with below object :
{
...title,
...name
}
So, it a kind of replace entire object. Instead use . notation to update particular values.

Elasticsearch | Query over array of objects

Lets say I have an object in ES such as:
{
name: "calendar";
events: [
{
birthday: "1992-10-09",
graduation: "2018-06-15",
wedding: "2016-12-12"
}
]
}
Is there a way I can query over the events array to find an element in the events array that is passed the current date.
So far I have:
GET /index/type/_search
{
"query": {
"range" : {
"events" : {
"gte" : "now"
}
}
}
}
but that does not iterate over each object in the array and return the name of the event. For this scenario it should return the object graduation: "2018-06-15". Thanks for the help!
You can use Nested datatypes to store nested documents inside parent document.
And to query back the Nested types you can use Nested Query.

Resources