don't allow partially matching document to add to array with $push using updateOne function - mongodb

check a document whether it is partially matching (2 or more fields) with existing documents in array('claims') with $push / $addToSet using updateOne and shouldn't allow to be pushed to array
structure is as follows:
{
"_id": ObjectId("5f50c93dda654e891c903efb"),
"claims":[
{
"username": "Foo",
"claim_id":"12345",
"claimed_at":"2020-08-31T17:18:52.818737",
},
{
"username": "Bar",
"claim_id":"54321",
"claimed_at":"2020-08-31T17:18:52.818737",
}
]
}
couldn't use $addToSet since other field value like claimed_at is not known,
if it was to match single field only like user_id it was working,
I was missing something here
db.claims.updateOne(
{
"_id": ObjectId("5f50c93dda654e891c903efb"),
"claims":{"$ne": {"username":"Foo"}}
},
{
"$push":
{
"claims":
{
"username": "Foo",
"claim_id":"12345",
"claimed_at":"2020-09-02T17:08:23.514342",
}
}
})

You can try using $elemMatch and $not,
db.claims.updateOne({
"_id": ObjectId("5f50c93dda654e891c903efb"),
claims: {
$not: {
$elemMatch: {
username: "Foo",
claim_id: "12345"
}
}
}
}, {
"$push": {
"claims": {
"username": "Foo",
"claim_id": "12345",
"claimed_at": "2020-09-02T17:08:23.514342",
}
}
})

Related

Update nested array mongodb

I want do an updateMany() operation on a nested array for all documents of my collection.
Here is the documents format :
{
"number": 1,
"products": [{
"name": "test",
"compositions": ["water", "sugar"],
}]
},
{
"number": 2,
"products": [{
"name": "test12",
"compositions": ["cotton", "linen"],
}]
}
How can add element ("color" for example) in compositions array nested in product array for all documents by doing updateMany() operation ?
I try this but it is not work :
db.getSiblingDB("mydatabase").getCollection("stock").find().forEach(element => {
element.products.forEach(product => {
db.stock.updateOne(
{$set: {
'compositions': { $addToSet: { 'product.compositions' : "color"}}
}})
})
})
Thank you in advance.
You can use all positional operator $[] to update all elements in the array.
db.getSiblingDB("mydatabase").getCollection("stock").updateMany(
{},
{ $addToSet: { "products.$[].compositions": "color" } }
)
In case of if you don't want to update all elements, you can use arrayFilters (which allows you to use $[i] notation inside option section):
mongodb example playground
db.getSiblingDB("mydatabase").getCollection("stock").updateMany(
{},
{ $addToSet: { "products.$[i].compositions": "color" } },
{ arrayFilters: [{"i.name": "test12"}]
)

Find all objects in array that match IDs of other array - mongoDB

I'm looking for the solution since yesterday, but can't find it. I tried different approaches like $elemMatch, aggregation etc. but nothing works. Either I get all objects or just the first one, but never only that ones with the matching IDs.
What I try to achieve: Get all objects of an array with one of multiple IDs.
First parameter is the user ID like 60a3f7d0f988f1e57212a81e. Every Document in this collection is unique by the user ID.
Second parameter is an array like ['609d1cfe0806daba1b0502bf', '609d1bba887035b9cd4292aa'] that consists of different IDs to be matched with the objects of the array words.
My approach gets only the first object and doesn't even work with an array of IDs (wordIDs) as parameter.
Words.prototype.getSpecificPersonalWords = async function(userID, wordIDs) {
const words = await personalWords.aggregate([
{$match:
{"user_id": userID}
},
{$unwind:
{"path": "$words"}
},
{$match:
{"words.word_id": { $in: ['609d1cfe0806daba1b0502bf', '609d1bba887035b9cd4292aa'] } }
}
])
return words
}
Document, that I want to query:
{
"_id": {
"$oid": "60a3f7d0f988f1e57212a81e"
},
"user_id": "609d22188ea8aebac56f9dc3",
"__v": {
"$numberInt": "0"
},
"words": [{
"word_id": "609d1bba887035b9cd4292aa",
"last_practiced": "2021-05-15 16:00:00",
"strength": {
"$numberInt": "1"
}
}, {
"word_id": "609d1c35861d99b9effc0027",
"last_practiced": "2021-05-15 16:00:00",
"strength": {
"$numberInt": "1"
}
}, {
"word_id": "609d1dcbc99e8dba538208d7",
"last_practiced": "2021-05-15 16:00:00",
"strength": {
"$numberInt": "1"
}
}]
}
Schema of that document
{
user_id: {
type: String,
unique: true
},
words: Array
});
I hope someone can help me to figure this out. Thanks in advance.
$match both user_id and word_id conditions
$filter to iterate loop of words and match condition id word_id in array of ids
const words = await personalWords.aggregate([
{
$match: {
"user_id": "609d22188ea8aebac56f9dc3",
"words.word_id": {
$in: ["609d1cfe0806daba1b0502bf", "609d1bba887035b9cd4292aa"]
}
}
},
{
$addFields: {
words: {
$filter: {
input: "$words",
cond: {
$in: [
"$$this.word_id",
["609d1cfe0806daba1b0502bf", "609d1bba887035b9cd4292aa"]
]
}
}
}
}
}
])
Playground

Mongo DB aggregate match not returning value

I have the following mongo db schema and I am trying to build an aggregate query that searches under github_open_issues under the repo key and can return me a match for all the values with repoA as the value. I have tried the following as my query however its not returning any result. Im a bit confused why this is not working as I have another db with a schema similar to this and this type of query works there but here something seems to be different and is not working. I have also put together this interactive example mongoplayground
query
db.collection.aggregate([
{
"$unwind": "$github_open_issues"
},
{
"$match": {
"github_open_issues.repo": {
"$in": [
"repoA"
]
}
}
},
])
schema
[
{
"github_open_issues": {
"0": {
"git_url": "https://github.com/",
"git_assignees": "None",
"git_open_date": "2019-09-26",
"git_id": 253113,
"repo": "repoA",
"git_user": "userA",
"state": "open"
},
"1": {
"git_url": "https://github.com/",
"git_assignees": "None",
"git_open_date": "2019-11-15",
"git_id": 294398,
"repo": "repoB",
"git_user": "userB",
"state": "open"
},
"2": {
"git_url": "https://github.com/",
"git_assignees": "None",
"git_open_date": "2021-04-12",
"git_id": 661208,
"repo": "repoA",
"state": "open"
}
},
"unique_label_seen": {
"568": {
"label_name": "some label",
"times_seen": 12,
"535": {
"label_name": "another label",
"times_seen": 1
}
}
}
}
]
$objectToArray convert github_open_issues object to array in key-value format
$filter to iterate loop of above converted array and filter your search condition
$match to filter github_open_issues not empty
$arrayToObject convert github_open_issues array to object
db.collection.aggregate([
{
$addFields: {
github_open_issues: {
$filter: {
input: { $objectToArray: "$github_open_issues" },
cond: { $in: ["$$this.v.repo", ["repoA"]] }
}
}
}
},
{ $match: { github_open_issues: { $ne: [] } } },
{ $addFields: { github_open_issues: { $arrayToObject: "$github_open_issues" } } }
])
Playground
You query is correct but you data in schema placed wrong inside github_open_issues.repo your objects are place by numbers like {"0": {values... }, "1":{values... }} which cannot get your desired value. You can check the playground now playground

MongoDb aggregation project onto collection

I've a problem with a huge MongoDb aggregation pipeline. I've many constraint and I've simplified the problem a lot. Hence, don't discuss the goal for this query.
I've a mongo aggregation that gives something similar to this:
[
{
"content": {
"processes": [
{
"id": "101a",
"title": "delivery"
},
{
"id": "101b",
"title": "feedback"
}
]
}
}
]
To this intermediate result I'm forced to apply a project operation in order to obtain something similar to this:
[
{
"results":
{
"titles": [
{
"id": "101a",
"value": "delivery"
},
{
"id": "101b",
"value": "feedback"
}
]
}
}
]
enter code here
But applying this projections:
"results.titles.id": "$content.processes.id",
"results.titles.value": "$content.processes.title"
I obtain this:
[
{
"results":
{
"titles": {
"id": ["101a", "101b"]
"value": ["delivery", "feedback"]
}
}
}
}
]
Collection are created but not in the proper position.
Is it possible to exploit some operator inside the project operation in order to tell mongo to create an array in a parent position?
Something like this:
"results.titles.$[x].value" : "$content.processes.value"
You can use the dot notation to project entire array:
db.col.aggregate([
{
$project: {
"results.titles": "$content.processes"
}
}
])
and if you need to rename title to value then you have to apply $map operator:
db.col.aggregate([
{
$project: {
"results.titles": {
$map: {
input: "$content.processes",
as: "process",
in: {
id: "$$process.id",
value: "$$process.title"
}
}
}
}
}
])

Aggregate documents where objects in array matches multiple conditions

I have a collection with documents similar to such:
{
"_id": ObjectId("xxxxx"),
"item": [
{ "property": ["attr1", "+1"] },
{ "property": ["attr2", "-1"] }
]
}
{
"_id": ObjectId("xxxxy"),
"item": [
{ "property": ["attr1", "-1"] },
{ "property": ["attr2", "0"] }
]
}
{
"_id": ObjectId("xxxxz"),
"item": [
{ "property": ["attr1", "0"] },
{ "property": ["attr2", "+1"] }
]
}
Preferably using an aggregation pipeline, is there any way to match the document if and only if any one of the properties match more than one condition?
For example, I want a query where one object in the array matches both of these conditions:
("item.property": "attr1") AND ("item.property": /^\+/)
That is, a single property where it contains "attr1" and an element that starts with "+".
However, using my current query that looks like this:
collection.aggregate(
{ $match:
{ $and:
[
{ "item.property": "attr1" },
{ "item.property": /^\+/ }
]
}
}
This would match both the first and last document because both contain a property with "attr1" and an element that stats with "+". However, I do not want this query to match the last document, since the element that starts with "+" does not belong to the same object in the array.
Is there any way to achieve this behavior using the aggregation framework?
You can use the below query with $elemMatch to match the array's both values.
Something like
db.collection_name.aggregate({
"$match": {
"item": {
"$elemMatch": {
"property.0": "attr1",
"property.1": /^\+/
}
}
}
});
Also, you can use $all operator if you don't want to reference array index.
db.collection_name.aggregate({
"$match": {
"item": {
"$elemMatch": {
"property": {
"$all": [
"attr1",
/^\+/
]
}
}
}
}
});