How to convert into java mongodb using mongotemplate or mongo repository(custom repository) - spring-data-mongodb

db.profileInterest.aggregate([
{ $project: { _id: 1, fromProfile: 1, toProfile: 1, contactNoViewed:1, shortListed: 1, count: {$size: '$interestStatus'}, threeFavorites: { $slice: [ "$interestStatus", -1 ] }} },
{$match: {"threeFavorites.id": {$ne: 100}}}
]).pretty();

save aggregation output in List<DBObject> as below :
AggregationOutput aggregationOutput = mongoTemplate
.getCollection("collection_name")
.aggregate(aggregationList);
List<DBObject> dbObjects = (List<DBObject>) aggregationOutput.results();
convert List<DBObject> in java type as below :
for(DBObject dbObject : dbObjects) {
mongoTemplate.getConverter().read(klass, dbObject);
}

Related

How to add field to nested array that looks at another field in the same array item MongoDb

for example i have
{
...
myObjects = [ {nmbr: 1}, {nmbr:2}]
}
now I want:
{
...
myObjects = [ {nmbr: 1, id: 1}, {nmbr:2, id :2}]
}
using:
db.collection.aggregate([
{
"$addFields": {
"myObjects.id": "$myObjects.nmbr"
}
}
])
has this result
{
...
myObjects = [ {nmbr: 1, id_:[1,2]}, {nmbr:2, id:[1,2]}]
}
which is not what I expected, any solution?
$unwind: Deconstructs myObjects array field from the source documents to output a document for each element.
$addFields: Create id property with value myObject.nmbr in myObject field.
$group: Group by $id (ObjectId) to combine into myObjects array (reverse $unwind).
db.collection.aggregate([
{
"$unwind": "$myObjects"
},
{
"$addFields": {
"myObjects.id": "$myObjects.nmbr"
}
},
{
$group: {
_id: "$id",
"myObjects": {
$push: "$myObjects"
}
}
}
])
Output
[
{
"_id": null,
"myObjects": [
{
"id": 1,
"nmbr": 1
},
{
"id": 2,
"nmbr": 2
}
]
}
]
Sample Mongo Playground

Query for returning documents having specific date range number then order them then sort

I have a Song schema that looks like:
const songSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
URL: {
type: String,
required: true,
},
playedOn: [mongoose.Schema.Types.Date],
});
And in the database I have values such as:
{
"_id" : ObjectId("6013e57d909d5252e869eac0"),
"playedOn" : [
ISODate("2021-01-29T10:41:55.444Z"),
ISODate("2021-01-29T10:41:55.447Z"),
ISODate("2021-01-29T10:41:55.448Z"),
ISODate("2021-01-29T10:41:55.450Z"),
ISODate("2021-01-29T10:41:55.451Z"),
ISODate("2021-01-29T10:41:55.452Z")
],
"name" : "Ariana Grande - Positions",
"URL" : "https://firebasestorage.googleapis.com/v0/b/reactnativeauth-sj.appspot.com/o/003.%20Ariana%20Grande%20-%20positions.mp3?alt=media&token=d206e095-6360-46c4-ad1e-40a9204ca9d0",
"__v" : 1
}
{
"_id" : ObjectId("6013e57d909d5252e869eac1"),
"playedOn" : [
ISODate("2021-01-29T10:41:53.708Z")
],
"name" : "Dua Lipa - Break My Heart",
"URL" : "https://firebasestorage.googleapis.com/v0/b/reactnativeauth-sj.appspot.com/o/005.%20Dua%20Lipa%20-%20Break%20My%20Heart.mp3?alt=media&token=7feea778-761c-4e0e-8840-6f60e7795651",
"__v" : 1
}
I want to first get the songs played most in the last 7 days (say top 10) sorted in descending order by the number of plays, The result be like:
{name: 'Positions', _id: '39s39s', numberOfPlays: 9}
{name: 'Irresistible', _id: '39s39s', numberOfPlays: 5}
{name: 'Closer', _id: '39s39s', numberOfPlays: 3}
I have tried things like:
db.songs.find({playedOn: {elemMatch: {$lt: ISODate()}}}, {_id: 1, playedOn: 1, name: 1}).pretty();
db.songs.find({playedOn: {$lt: ISODate() , $gte: ISODate("2021-01-28T09:20:37.732Z")}}, {_id: 1, playedOn: 1, name: 1}).pretty();
And many different things, but I just can't figure out the query that I should write for the same. I habe also tried using aggregate, but in vain. Please give a direction as to how it can be done.
Using aggregate(),
$match start date and end date in $elemMatch
$filter to get filtered date from array
$size to get size of returned elements from $filter
$sort by numberOfPlays in descending order
$limit to return 10 documents
// start date and end date set your logic as per your requirement
let endDate = new Date();
let startDate = new Date(endDate);
startDate.setDate(startDate.getDate() - 7);
db.songs.aggregate([
{
$match: {
playedOn: {
$elemMatch: {
$gte: startDate,
$lt: endDate
}
}
}
},
{
$project: {
name: 1,
numberOfPlays: {
$size: {
$filter: {
input: "$playedOn",
cond: {
$and: [
{ $gte: ["$$this", startDate] },
{ $lt: ["$$this", endDate] }
]
}
}
}
}
}
},
{ $sort: { numberOfPlays: -1 } },
{ $limit: 10 }
])
Playground
Using find() Starting in MongoDB v4.4, as part of making projection consistent with aggregation’s $project stage,
for limit and sort try .sort("-numberOfPlays").limit(10) end of the query
Playground

Need help in querying mongodb

I have a a few documents that have the following structure. See attached image.
document structure
Each document includes an array of 'FileMeta' objects and each FileMeta object includes an array of 'StatusHistory' objects. I'm trying to get only the FileMetas that contain StatusCode equal to 4 and that the TimeStamp is greater than a certain datetime.
Tried the following query but it only returns the first FileMeta element of each document.
db.getCollection('Collection').find({'ExternalParams.RequestingApplication':'aaa.bbb'},
{ "FileMeta": { $elemMatch: { "StatusHistory":{ $elemMatch:{ "StatusCode": 4, "TimeStamp": { $gt: ISODate("2020-06-28T11:02:26.542Z")} } } } }} )
What am I doing wrong?
here is the document structure:
{
"_id" : ObjectId("5ef84e2ec08abf38b0043ab4"),
"FileMeta" : [
{
"StatusHistory" : [
{
"StatusCode" : 0,
"StatusDesc" : "New File",
"TimeStamp" : ISODate("2020-06-28T11:00:46.286Z")
},
{
"StatusCode" : 2,
"StatusDesc" : "stby",
"TimeStamp" : ISODate("2020-06-28T11:02:20.400Z")
},
{
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.937Z")
}
]
},
{
"StatusHistory" : [
{
"StatusCode" : 0,
"StatusDesc" : "New File",
"TimeStamp" : ISODate("2020-06-28T11:00:46.286Z")
},
{
"StatusCode" : 2,
"StatusDesc" : "stby",
"TimeStamp" : ISODate("2020-06-28T11:02:20.617Z")
},
{
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.542Z")
}
]
}
],
}
I want to return only the FileMeta objects that include a StatusHistory that match the following conditions: StatusCode = 4 and TimeStamp > SomeDateTime
Sorry for the delay, mate, I've been quite busy lately. Hope you already solved your problem. Anyway, I think that I found the solution.
As you can see on this link, the example shows that by default the $elemMatch operator returns the whole array in case of match on any element.
For instance, consider the following collection:
{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }
If you do the following query, for example:
db.survey.find(
{ results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } }
)
The output will be:
{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }
Not:
{ "_id" : 3, "results" : [{ "product" : "xyz", "score" : 8 }]}
That said, if you want to return only the document in the array that matches the specified query, you must use the db.collection.aggregate() function with the $unwind and $match operator.
The query below shall give you what you want.
Query:
db.collection.aggregate([
{"$unwind" : "$FileMeta"},
{"$unwind" : "$FileMeta.StatusHistory"},
{
"$match" : {
"FileMeta.StatusHistory.StatusCode" : 4,
"FileMeta.StatusHistory.TimeStamp" : {"$gte" : ISODate("2020-06-28T11:02:26.937Z")}
}
}
]).pretty()
Result:
{
"_id" : ObjectId("5ef84e2ec08abf38b0043ab4"),
"FileMeta" : {
"StatusHistory" : {
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.937Z")
}
}
}
One last tip. Consider changing your modeling to something that looks like the unwinded document, and remember that one document should be equivalent to one row in a normal relational database. So avoid storing information that should be on "several rows" on a single document.
Useful links:
The $elemMatch operator.
The $unwind operator.

In Mongo, how can I use $IN condition with values coming from another field?

Data :
{
_id :1111,
col1_array : ['a','b','c'],
col2_array : ['a','f','g']
}
I would like to find all documents where col2_array contains any value of col1_array. I tried using the $IN condition but failed to refer to other field content as an array. How can I do ?
db.collection.aggregate([
{ $unwind: "$col2_array" },
{
$project:
{
index: { $indexOfArray: ["$col1_array", "$col2_array"] },
col2_array: 1,
col1_array: 1,
_id: 1
}
},
{ $match: { index: { $gte: 0 } } },
{ $group: { _id: "$_id" } }
])
After that you have to find records of this particular _id
db.collection.find({_id:{$in:[result_of_above_query_ids]}})
I have finally resolved the problem using arrays intersections I have just discovered digging Mongo documentation :
db.getCollection('test').aggregate([
{$set : {
commonValues: { $setIntersection: [ "$col1_array", "$col2_array"]},
}},
{$match : {"commonValues" : {"$ne" : []}}}
])

Find specific field in an array of embedded documents

I'm trying to find specific element of array in my MongoDB document, but with all my tries query returns all array. How can I get the right object?
My collection's document looks like:
{
"Achievements": [
{
"AchievementID": 1,
"AchievementEventID": 0
},
{
"AchievementID": 2,
"AchievementEventID": 1
}
],
"Buildings": [
{
"BuildingID": 1,
"BuildingType": "type1"
},
{
"BuildingID": 2,
"BuildingType": "type1"
},
]
}
I tried to get only one element of my Achievements array:
db.data.find({'Achievements.AchievementEventID': 0})
I expected to get only element with AchievementEventID equal to 0:
{
"AchievementID": 1,
"AchievementEventID": 0
}
But I got whole Achievements array.
How I could get only specific element?
You can use the Aggregation query like in the following example. The sample achive collection has 3 documents:
{
"_id" : 1,
"Achievements" : [
{
"AchievementID" : 1,
"AchievementEventID" : 0
},
{
"AchievementID" : 2,
"AchievementEventID" : 1
}
],
"Buildings" : [
{
"BuildingID" : 1,
"BuildingType" : "type1"
},
{
"BuildingID" : 2,
"BuildingType" : "type1"
}
]
}
{
"_id" : 2,
"Achievements" : [
{
"AchievementID" : 2,
"AchievementEventID" : 2
}
],
"Buildings" : [
{
"BuildingID" : 2,
"BuildingType" : "type2"
}
]
}
{
"_id" : 3,
"Achievements" : [
{
"AchievementID" : 31,
"AchievementEventID" : 1
}
],
"Buildings" : [
{
"BuildingID" : 3,
"BuildingType" : "type3"
}
]
}
The Query:
db.achive.aggregate( [
{ $project: { Buildings: 0} },
{ $unwind: "$Achievements" },
{ $match: { "Achievements.AchievementEventID": { $eq: 1 } } }
])
=>
{ "_id" : 1, "Achievements" : { "AchievementID" : 2, "AchievementEventID" : 1 } }
{ "_id" : 3, "Achievements" : { "AchievementID" : 31, "AchievementEventID" : 1 } }
I added the _id field to identify the documents selected.
Updated Query:
db.achive.aggregate( [
{ $unwind: "$Achievements" },
{ $match: { "Achievements.AchievementEventID": { $eq: 1 } } },
{ $project: { AchievementID: "$Achievements.AchievementID", _id: 0} },
])
=>
{ "AchievementID" : 2 }
{ "AchievementID" : 31 }
You can use $elemMatch for that
db.data.find(
{ 'Achievements.AchievementEventID': 0},{Buildings:0, Achievements: {$elemMatch: {AchievementEventID: 0}}}
)
If the above solution doesn't for you then try the below one:
db.data.find({"Achievements.AchievementEventID": 0}, {Buildings: 0, 'Achievements.$': 1});
You can use aggregate instead of a find like this
db.data.aggregate(
[{'$unwind':'$Achievements'},{$match:{
'Achievements.AchievementEventID':0
}},{$project:{
'AchievementID':'$Achievements.AchievementID',
'AchievementEventID':'$Achievements.AchievementEventID'
}}]
);

Resources