My documents look like this:
{
"_id": {
"$oid": "60841a6047fda45b6391dbce"
},
"title": "Week 1",
"dates": [{
"start": {
"$date": "2021-04-26T00:00:00.000Z"
},
"end": {
"$date": "2021-05-02T00:00:00.000Z"
}
}, {
"start": {
"$date": "2021-05-10T00:00:00.000Z"
},
"end": {
"$date": "2021-05-16T00:00:00.000Z"
}
}]
}
Now I try to find documents where a given date in in one of these ranges. (Expected result is no documents, but this query is returning my document.)
{
$and: [
{'dates.start': {$lte: ISODate('2021-05-05')}},
{'dates.end': {$gte: ISODate('2021-05-05')}}
]
}
How can I query if the start and end date in one object includes my date?
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria,
{
dates: {
$elemMatch: {
start: { $lte: ISODate("2021-05-05") },
end: { $gte: ISODate("2021-05-05") }
}
}
}
Playground
Related
I have a MongoDB document collection with multiple arrays that looks like this :
{
"_id": "1235847",
"LineItems": [
{
"StartDate": ISODate("2017-07-31T00:00:00.000+00:00"),
"EndDate": ISODate("2017-09-19T00:00:00.000+00:00"),
"Amount": {"$numberDecimal": "0.00"}
},
{
"StartDate": ISODate("2022-03-20T00:00:00.000+00:00"),
"EndDate": ISODate("2022-10-21T00:00:00.000+00:00"),
"Amount": {"$numberDecimal": "6.38"}
},
{
"StartDate": ISODate("2022-09-20T00:00:00.000+00:00"),
"EndDate": ISODate("9999-12-31T00:00:00.000+00:00"),
"Amount": {"$numberDecimal": "6.17"}
}
]
}
Is there a simple way to find documents where the startdate has overlapped with previously startdate, enddate?
The startdate can not be before previous end dates within the array
The start/end can not be between previous start/end dates within the array
The below works but I don't want to hardcode the array index to find all the documents
{
$match: {
$expr: {
$gt: [
'LineItems.3.EndDate',
'LineItems.2.StartDate'
]
}
}
}
Here's one way you could find docs where "StartDate" is earlier than the immediately previous "EndDate".
db.collection.find({
"$expr": {
"$getField": {
"field": "overlapped",
"input": {
"$reduce": {
"input": {"$slice": ["$LineItems", 1, {"$size": "$LineItems"}]},
"initialValue": {
"overlapped": false,
"prevEnd": {"$first": "$LineItems.EndDate"}
},
"in": {
"overlapped": {
"$or": [
"$$value.overlapped",
{"$lt": ["$$this.StartDate", "$$value.prevEnd"]}
]
},
"prevEnd": "$$this.EndDate"
}
}
}
}
}
})
Try it on mongoplayground.net.
Here is my data. I wanted to change year but It should effective to only the first item of the document array
{
"_id": {
"$oid": "62053aa8aa1cfbe8c4e72662"
},
"school": "Test",
"reports": [
{
"year": "2020", // This has to be changed to 2019
"createdAt": {
"$date": "2022-02-10T17:05:25.682Z"
},
"pid": {
"$oid": "620545d5097761628f32365a"
},
"details": {
"end_date": {
"$date": "2020-03-31T00:00:00.000Z" // when end date is prior to July 01 of the $year mentioned.
}
}
}, {
"year": "2020",
"createdAt": {
"$date": "2022-03-14T19:08:38.125Z"
},
"pid": {
"$oid": "622f92b68a408531d4b784de"
},
"details": {
"end_date": {
"$date": "2021-03-31T00:00:00.000Z"
}
}
}
]
}
In the above data, I want to reduce the year to the previous year, if details.end_date is prior to July 01 of the current mentioned year. But It should change only the first item of the embedded array.
For example,
If Year is 2020 and details.end_date is prior to 01-July-2020, then change the year to 2019
If Year is 2020 and details.end_date is after 01-July-2020, then do not change the year
If Year is 2021 and details.end_date is prior to 01-July-2021, then change the year to 2020
If Year is 2021 and details.end_date is after 01-July-2021, then do not change the year
You can do the followings in an aggregation pipeline:
isolate the first elem of the reports array for easier processing using $arrayElemAt
use $cond to derive the year value with $month. Use $toInt and $toString for type conversion
use $concatArrays to append back the processed first Elem back to the reports array. Keep only the "tail" (i.e. without the first elem) using $slice
$merge to update the result back to the collection
db.collection.aggregate([
{
"$addFields": {
"firstElem": {
"$arrayElemAt": [
"$reports",
0
]
}
}
},
{
"$addFields": {
"firstElem.year": {
"$cond": {
"if": {
$lt: [
{
"$month": "$firstElem.end_date"
},
7
]
},
"then": {
"$toString": {
"$subtract": [
{
"$toInt": "$firstElem.year"
},
1
]
}
},
"else": "$firstElem.year"
}
}
}
},
{
"$addFields": {
"reports": {
"$concatArrays": [
[
"$firstElem"
],
{
"$slice": [
"$reports",
1,
{
"$subtract": [
{
"$size": "$reports"
},
1
]
}
]
}
]
}
}
},
{
"$project": {
firstElem: false
}
},
{
"$merge": {
"into": "collection",
"on": "_id",
"whenMatched": "replace"
}
}
])
Here is the Mongo playground for your reference.
So heres my problem.
I am new to mongodb and have a collection which documents are saved like this:
{
"_id": {
"$oid": "60626db173b4ca321c02ee3e"
},
"year": "2021",
"name": "Book 1",
"authors": ["Joe, B", "Jessica, K"],
"createdAt": {
"$date": "2021-03-30T00:15:45.859Z"
}
},
{
"_id": {
"$oid": "60626db173b4ca321c02ee4e"
},
"year": "2021",
"authors": ["Carl, B", "Jessica, K"],
"name": "Book 2"
"createdAt": {
"$date": "2021-03-30T00:15:45.859Z"
}
},
I need to get both the frequency of all authors and the years of the books.
The expected result would be something like this (as long as i can get each element frequency it doesn't really matter how the results are returned):
{
"authors": {
"Joe, B": 1,
"Carl, B": 1,
"Jessica, K": 2
},
"year": {
"2021": 2
}
}
I've seen this thread How to count occurence of each value in array? which does the job in one array but i have no idea if its possible to adapt to get the frequency of multiple elements (year, authors) at the same time or how to do it.
I appreciate any help. Thank you.
Demo - https://mongoplayground.net/p/95JtQEThxvV
$group by year $push authors into the array get $sum count of the year occurrence, $unwind into individuals documents.
$group by authors and get $sum count of the author occurrence
$group by null to combine all documents, use $addToSet to push unique values and convert $arrayToObject to get final output in $project
$first
db.collection.aggregate([
{
$group: {
_id: { year: "$year" },
authors: { $push: "$authors" },
yearCount: { $sum: 1 }
}
},
{ $unwind: "$authors" },
{ $unwind: "$authors"},
{
$group: {
_id: { author: "$authors" },
year: { $first: "$_id.year" },
yearCount: { $first: "$yearCount" },
authors: { $push: "$authors" },
authorCount: { $sum: 1 }
}
},
{
"$group": {
_id: null,
years: {
$addToSet: { k: "$year", v: "$yearCount" }
},
authors: {
$addToSet: { k: "$_id.author", v: "$authorCount" }
}
}
},
{
$project: {
_id: 0,
years: { $arrayToObject: "$years" },
authors: { $arrayToObject: "$authors" }
}
}
])
Demo 2 - For author count grouped by year- https://mongoplayground.net/p/_elnjmknroF
I trying to use get only values from Mongodb query results in to array.
My query is
db.collection_name.find({"actual_time":{"$gt": start_date, "$lte": end_date}}, {'_id': False})
and my result is
{
"result": [
{
"start": 7299.69,
"end": 7299.73,
"low": 7297.38,
"open": 7297.84,
"time": 1536007500,
"volumefrom": 16.98,
"volumeto": 123447.27,
},
{
"start": 7307.24,
"end": 7308.11,
"low": 7299.69,
"open": 7299.69,
"time": 1536007680,
"volumefrom": 78.7,
"volumeto": 575049.25,
}
]
}
But I don't want this result. I want to get result as below
{
"result": [
{
7299.69,
7299.73,
7297.38,
7297.84,
1536007500,
16.98,
123447.27,
},
{
7307.24,
7308.11,
7299.69,
7299.69,
1536007680,
78.7,
575049.25,
}
]
}
How to write the query in Mongodb?
You can use below aggregation in 3.4.
Use $let with $objectToArray on $$ROOT to extract the key values.
db.collection_name.aggregate([
{"$match":{"actual_time":{"$gt":start_date,"$lte":end_date}}},
{"$project":{
"_id":0,
"keyvalues":{
"$let":{
"vars":{"dockv":{
"$filter":{
"input":{"$objectToArray":"$$ROOT"},
"cond":{"$not":{"$in":["$$this.k",["_id"]]}}
}
}},
"in":"$$dockv.v"
}
}
}},
{"$group":{"_id":null,"results":{"$push":"$keyvalues"}}}
])
Let's say I have 2 documents:
{
"id": "1234",
"things": [
{
"datetime": "2016-01-01T12:00:00+03:00"
},
{
"datetime": "2016-01-06T12:00:00+03:00"
},
{
"datetime": "2100-01-01T12:00:00+03:00"
}
]
}
and
{
"id": "5678",
"things": [
{
"datetime": "2016-01-03T12:00:00+03:00"
},
{
"datetime": "2100-01-06T12:00:00+03:00"
}
]
}
things.datetime is mapped as { "type": "date", "format": "date_time_no_millis" }.
I want to sort these documents based on the latest things.datetime value that is not in the future.
I.e. sorted by simply the max things.datetime would use the dates 2100-01-01T12:00:00+03:00 and 2100-01-06T12:00:00+03:00. I want the sorting to be based on the values 2016-01-06T12:00:00+03:00 and 2016-01-03T12:00:00+03:00.
How can I achieve this, using ElasticSearch 2.x?
I've tried:
"sort": {
"things.datetime": {
"order": "desc",
"mode": "max"
}
}
But that doesn't seem to sort even by the 2100 dates.
I also tried to use nested_filter like so:
"sort": {
"things.datetime": {
"order": "desc",
"mode": "max",
"nested_filter": {
"range": {
"things.datetime": { "lte": "now" }
}
}
}
}
But it doesn't work as I'd expect.
Also the "sort" value in the response is a negative number. So for a document with dates:
"2015-10-24T05:50:00+03:00",
"2015-10-26T22:05:48+02:00",
"2015-10-24T08:05:43+03:00"
gets a negative sort value:
"sort": [
-9223372036854775808
]
The correct way to achieve this seems to be:
"sort": {
"things.datetime": {
"order": "desc",
"mode": "max",
"nested_path": "things",
"nested_filter": {
"range": {
"things.datetime": { "lte": "now" }
}
}
}
}
When there are no more dates left after the nested_filter, the sort value becomes a negative number to ensure the correct order.