I created a test collection for this and it looks like this:
{
"createDate": ISODate("2021-01-04T15:00:00.000+00:00"),
"arr": [
{
"date": ISODate("2021-01-04T15:00:00.000+00:00")
}
]
},
{
"createDate": ISODate("2021-01-04T16:00:00.000+00:00"),
"arr": [
{
"date": ISODate("2021-01-04T15:00:00.000+00:00")
}
]
}
The only difference is: The first entry has the same date, the second one has different dates.
Now I want to create a query to find all entries, where the two dates are different. I tried it with this:
db.getCollection('test').find(
{
"createDate": { $ne: "arr.0.date" }
}
)
Unfortunately, this query delivers me both entries. What am I doing wrong?
It requires to match in $expr expression, that compare fields from the same document, to access element from specific index use $arrayElemAt
db.getCollection('test').find({
$expr: {
$ne: [
"$createDate",
{ $arrayElemAt: ["$arr.date", 0] }
]
}
})
Playground
Related
i have a collection with more then 1000 documents and there are some documents with same value in some fields, i need to get those
the collection is:
[{_id,fields1,fields2,fields3,etc...}]
what query can i use to get all the elements that have the same 3 fields for example:
[
{_id:1,fields1:'a',fields2:1,fields3:'z'},
{_id:2,fields1:'a',fields2:1,fields3:'z'},
{_id:3,fields1:'f',fields2:2,fields3:'g'},
{_id:4,fields1:'f',fields2:2,fields3:'g'},
{_id:5,fields1:'j',fields2:3,fields3:'g'},
]
i need to get
[
{_id:2,fields1:'a',fields2:1,fields3:'z'},
{_id:4,fields1:'f',fields2:2,fields3:'g'},
]
in this way i can easly get a list of "duplicate" that i can delete if needed, it's not really important get id 2 and 4 or 1 and 3
but 5 would never be included as it's not 'duplicated'
EDIT:
sorry but i forgot to mention that there are some document with null value i need to exclude those
This is the perfect use case of window field. You can use $setWindowFields to compute $rank in the grouping/partition you want. Then, get those rank not equal to 1 to get the duplicates.
db.collection.aggregate([
{
$match: {
fields1: {
$ne: null
},
fields2: {
$ne: null
},
fields3: {
$ne: null
}
}
},
{
"$setWindowFields": {
"partitionBy": {
fields1: "$fields1",
fields2: "$fields2",
fields3: "$fields3"
},
"sortBy": {
"_id": 1
},
"output": {
"duplicateRank": {
"$rank": {}
}
}
}
},
{
$match: {
duplicateRank: {
$ne: 1
}
}
},
{
$unset: "duplicateRank"
}
])
Mongo Playground
I think you can try this aggregation query:
First group by the feilds you want to know if there are multiple values.
It creates an array with the _ids that are repeated.
Then get only where there is more than one ($match).
And last project to get the desired output. I've used the first _id found.
db.collection.aggregate([
{
"$group": {
"_id": {
"fields1": "$fields1",
"fields2": "$fields2",
"fields3": "$fields3"
},
"duplicatesIds": {
"$push": "$_id"
}
}
},
{
"$match": {
"$expr": {
"$gt": [
{
"$size": "$duplicatesIds"
},
1
]
}
}
},
{
"$project": {
"_id": {
"$arrayElemAt": [
"$duplicatesIds",
0
]
},
"fields1": "$_id.fields1",
"fields2": "$_id.fields3",
"fields3": "$_id.fields2"
}
}
])
Example here
I'll start with the example as it's easier to explain for me.
[
{
"_id": 100,
"narr": [
{
"field": 1
}
]
},
{
"_id": 101,
"narr": [
{
"field": 1,
},
{
"field": 2
}
]
}
]
Goal is to find document exactly with values specified by me for a field.
Example:
for lookup = [1] find document with _id=100.
for lookup = [1,2] find document with _id=101.
So far I came up with (for second example with [1,2]):
db.col.find(
{
"narr": {
"$all": [
{
"$elemMatch": {
"field": {
"$in": [1, 2]
}
}
}
]
}
}
)
But it also includes document with _id=100. How can I make it perform strict match?
Building whole arrays won't work as there are multiple fields with unknown values in each nested structure.
Without considering duplication in the field and your input, you can simply do a find on narr.field. It is like performing search on an array with values from field.
db.collection.find({
$expr: {
$eq: [
"$narr.field",
[
1,
2
]
]
}
})
Here is the Mongo playground for your reference.
If duplication may happens, try to use $setEquals.
db.collection.find({
$expr: {
"$setEquals": [
"$narr.field",
[
1,
2
]
]
}
})
Here is the Mongo playground for your reference.
I have a scenario where I want to pull documents that have a lastAlertSentDate field that's over 30 days old. This will run in a daily cron job. Upon querying, this field will then be reset to NOW. So it's meant to act as a "rotating 30 day window" if you will.
The complication here is that the field won't exist if it hasn't been set yet. In this edge case, we'll then have to use a createdDate field of the document to do the 30-day comparison against.
So effectively, I want something like, "If lastAlertSentDate exists, then get all docs where it's older than 30days from now. ---Otherwise, get all docs where createdDate is older than 30days from now"
So the logic between both fields are the same, it's just the field itself that can be different. Because of this, I was thinking to first USE addFields a dateToUseField and then do a match on the second stage based on this.
[
{
'$addFields': {
'dateToUse': {
'$cond': {
'if': {
'$ne': [
'$lastAlertSentDate', undefined
]
},
'then': '$lastAlertSentDate',
'else': '$createdDate'
}
}
}
}, {
'$match': {
'dateToUse': {
'$lte': '30_DAYS_PRIOR'
}
}
}
]
So the else part doesn't seem to work. It doesn't assign $createdDate to dateToUse.
What am I missing? Also, how can I condense this? I'm sure I don't need the addFields first and I can do everything within the $match
You have two options here:
Use a $or query with two predicates, where each of them is a $and predicate:
Either lastAlertSentDate does not exists and createdDate > n
Or lastAlertSentDate exists and it is > n
Playground Link
db.collection.find({
$or: [
{
$and: [
{
"lastAlertSentDate": {
"$exists": false
}
},
{
"createdDate": {
$gt: 5
}
}
]
},
{
$and: [
{
"lastAlertSentDate": {
"$exists": true
}
},
{
"lastAlertSentDate": {
$gt: 5
}
}
]
}
]
})
Use an aggregation using the $ifNull
Playground Link
db.collection.aggregate([
{
$match: {
$expr: {
$gt: [
{
"$ifNull": [
"$lastAlertSentDate",
"$createdDate"
]
},
5
]
}
}
}
])
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
I have the below structure for my collection:
{
"price":123,
"totalPrices": [
{
"totPrice":123
}
]
}
I am trying to query for all the documents in my collection where price is not equals to totalPrice.totPrice (so above should not be returned).
But it keeps returning the documents which have equal prices as well (such as above sample).
This is the query I'm using:
{
$where : "this.price!== this.totalPrices.totPrice",
totalPrice:{$size:1}
}
What am I doing wrong :(
First, you need to match the size of the array totalPrices is equal to 1. Second, you need to unwind the totalPrices, since it's an array field. Last, you should match the equality of price and totalPrices.totPrice. Try the below code:
db.collection.aggregate([
{
$match: {
$expr: {
$eq: [
{
$size: "$totalPrices"
},
1
]
}
}
},
{
$unwind: "$totalPrices"
},
{
$match: {
$expr: {
$ne: [
"$price",
"$totalPrices.totPrice"
]
}
}
}
])
MongoPlayGroundLink