MongoDB $elemsMatch in practice - mongodb

These are my documents:
[
{
uuid: 1,
emailNotifications: [
{
targetEmailAddress: "w#w.pl"
},
{
targetEmailAddress: "w2#w.pl"
}
]
},
{
uuid: 2,
emailNotifications: [
{
targetEmailAddress: "xxxw#w.pl"
},
{
targetEmailAddress: "xxxw2#w.pl"
},
]
},
]
I want query those with emailNotifications.targetEmailAddress equals to "w#w.pl":
db.collection.find({
emailNotifications: {
$elemMatch: {
targetEmailAddress: "w#wp.pl"
}
}
})
but it finds nothing. Where is the error?

Your value of targetEmailAdress in the query is wrong. You don't have w#wp.pl in your documents.

Related

MongoDB aggregate query - How to make array of array list in to single array

I have a document like this:
this is my example data is attached below,
[
{
"_id": ObjectId("6218b836405919280c209f7e"),
"projectId": ObjectId("6218a31f405919280c209e18"),
"accountId": ObjectId("621888e852bd8836c04b8f82"),
"personalIdRoot": [
{
"_id": ObjectId("6221e7514195b43f24c9953f"),
"personalId": ObjectId("6218b48c405919280c209f6c"),
},
{
"_id": ObjectId("6221e7514195b43f24c99540"),
"personalId": ObjectId("621ef1e40bd3a220f487cd96"),
}
],
"personalIdFill": [
{
"_id": ObjectId("6221e7514195b43f24c9953d"),
"personalId": ObjectId("6218b48c405919280c209f6c"),
},
{
"_id": ObjectId("6221e7514195b43f24c9953e"),
"personalId": ObjectId("621ef1e40bd3a220f487cd96"),
}
],
"personalIdCap": [
{
"_id": ObjectId("6221e7514195b43f24c9953b"),
"personalId": ObjectId("6218b48c405919280c209f6c"),
},
{
"_id": ObjectId("6221e7514195b43f24c9953c"),
"personalId": ObjectId("621ef1e40bd3a220f487cd96"),
}
],
"aps": ObjectId("6218bc18405919280c209f8e"),
}
]
i used this example data for my aggregate query.
my aggregate query is attached below:
please find below code:
db.getCollection('funds').aggregate([
{
$match: {
accountId: ObjectId("621888e852bd8836c04b8f82"),
projectId: ObjectId("6218a31f405919280c209e18"),
aps: {
$in: [
ObjectId("6218bc18405919280c209f8e")
]
}
}
},
{
$facet: {
"results": [
{
$group: {
_id: 0,
values: { "$addToSet": "$personalIdRoot.welderId" },
values: { "$addToSet": "$personalIdFill.welderId" },
values: { "$addToSet": "$personalIdCap.welderId" }
}
},
],
}
}
])
my result:
/* 1 */
{
"results" : [
{
"_id" : 0.0,
"values" : [
[
ObjectId("6218b48c405919280c209f6c")
],
[
ObjectId("6218b2e4405919280c209f68")
],
[
ObjectId("6218b48c405919280c209f6c"),
ObjectId("621ef1e40bd3a220f487cd96")
]
]
}
]
}
But i need my result in a single query:
/* 1 */
{
"results" : [
{
"_id" : 0.0,
"values" : [
ObjectId("6218b48c405919280c209f6c"),
ObjectId("621ef1e40bd3a220f487cd96")
]
}
]
}
I have a result of array of array collections.
but i need it in a single array. like above result
Thanks in advance.
It sounds like what you want is:
db.collection.aggregate([
{
$match: {
accountId: ObjectId("621888e852bd8836c04b8f82"),
projectId: ObjectId("6218a31f405919280c209e18"),
aps: {
$in: [
ObjectId("6218bc18405919280c209f8e")
]
}
}
},
{
$project: {
values: {
$setUnion: [
"$personalIdCap.personalId",
"$personalIdFill.personalId",
"$personalIdRoot.personalId"
]
}
}
}
])
See how it works on the playground example

nested condition in mongodb aggregation

I have a collection product which looks like this.
{
should_show: {
type: String,
enum: ['always', 'never', 'on_date'],
default: 'always',
},
show_start_date: { type: Date },
show_end_date: { type: Date },
categories: [{ type: ObjectId }],
price: number,
}
find condition like this works fine when I find many products.
const condition = {
'$and': [
{ '$and': [ { categories: '61bdd930fb1dfb1f65a21f13' } ] },
{
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:19.589Z },
show_end_date: { '$gt': 2022-01-03T08:56:19.589Z }
}
]
}
]
}
Products.find(condition)
But I changed my business logic to use Model.aggregate() instead of `Model.find()
(because I have to use allowdiskuse option)
const condition = {
'$and': [
{ '$and': [ { categories: '61bdd930fb1dfb1f65a21f13' } ] },
{
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:19.589Z },
show_end_date: { '$gt': 2022-01-03T08:56:19.589Z }
}
]
}
]
}
Products.aggregate([
{ $match: condition },
])
And returned results is always empty array []
I changed my condition simpler like this
const condition = {
'$and': [ { categories: '61bdd930fb1dfb1f65a21f13' } ],
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:50.161Z },
show_end_date: { '$gt': 2022-01-03T08:56:50.161Z }
}
]
}
Products.aggregate([
{ $match: condition },
])
but still returns empty array.
How should I fix this problem?
Self Answer:
I have to pass ObjectId by explicit ObjectId type, not string.
const condition = {
'$and': [
{ '$and': [{
// like this:
categories: Mongoose.Types.ObjectId('61bdd930fb1dfb1f65a21f13'),
}] },
{
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:19.589Z },
show_end_date: { '$gt': 2022-01-03T08:56:19.589Z }
}
]
}
]
}
Products.aggregate([
{ $match: condition },
])

How to remove field conditionally mongoodb

I have a collection and its documents look like:
{
_id: ObjectId('111111111122222222223333'),
my_array: [
{
id: ObjectId('777777777788888888889999')
name: 'foo'
},
{
id: ObjectId('77777777778888888888555')
name: 'foo2'
}
//...
]
//more attributes
}
However, some documents have my_array: [{}] (with one element which is an empty array).
How can I add conditionally a projection or remove it?
I have to add it to a mongo pipeline at the end of the query, and I want to get my_array only when it has at least one element which is not an empty object. If there's an empty object remove it.
I tried with $cond and $eq in a projection stage but it is not supported. Any suggestion to solve this?
Suppose you have documents like this with my_array field:
{ "my_array" : [ ] }
{ "my_array" : [ { "a" : 1 } ] } // #(1)
{ "my_array" : null }
{ "some_fld" : "some value" }
{ "my_array" : [ { } ] }
{ "my_array" : [ { "a" : 2 }, { "a" : 3 } ] } // #(2)
And, the following aggregation will filter and the result will have the two documents (1) and (2):
db.collection.aggregate([
{
$match: {
$expr: {
$and: [
{ $eq: [ { $type: "$my_array" }, "array" ] },
{ $gt: [ { $size: "$my_array" }, 0 ] },
{ $ne: [ [{}], "$my_array" ] }
]
}
}
}
])
This also works with a find method:
db.collection.find({
$expr: {
$and: [
{ $eq: [ { $type: "$my_array" }, "array" ] },
{ $gt: [ { $size: "$my_array" }, 0 ] },
{ $ne: [ [{}], "$my_array" ] }
]
}
})
To remove the my_array field, from a document when its empty, then you try this aggregation:
db.collection.aggregate([
{
$addFields: {
my_array: {
$cond: [
{$and: [
{ $eq: [ { $type: "$my_array" }, "array" ] },
{ $gt: [ { $size: "$my_array" }, 0 ] },
{ $ne: [ [{}], "$my_array" ] }
]},
"$my_array",
"$$REMOVE"
]
}
}
}
])
The result:
{ }
{ "my_array" : [ { "a" : 1 } ] }
{ }
{ "a" : 1 }
{ }
{ "my_array" : [ { "a" : 2 }, { "a" : 3 } ] }
You can't do that in a query, however in an aggregations you can add $filter to you pipeline, like so:
db.collection.aggregate([
{
$project: {
my_array: {
$filter: {
input: "$my_array",
as: "elem",
cond: {
$ne: [
{},
"$$elem"
]
}
}
}
}
}
])
Mongo Playground
However unless this is "correct" behavior I suggest you clean up your database, it's much simpler to maintain "proper" structure than to update all your queries everywhere.
You can use this update to remove these objects:
db.collection.update({
"myarray": {}
},
[
{
"$set": {
"my_array": {
$filter: {
input: "$my_array",
as: "elem",
cond: {
$ne: [
{},
"$$elem"
]
}
}
}
}
},
],
{
"multi": false,
"upsert": false
})
Mongo Playground

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.