{
_id: new ObjectId("61da0ab855483312e8f4483b"),
products: [
{
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483c"),
productCode: 'otf',
productName: 'facebookmeta',
claims: [Array],
permissions: []
},
{
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483f"),
productCode: '4pf',
productName: 'twitteroauth',
claims: [Array],
permissions: [Array]
}
],
__v: 0
}
Now i’ve been trying to get just one object from this array with the find() and findOne method without any luck. if i pass in a certain conditions, it still ends up giving me back an array with both objects. i just want to be able to dynamically pass conditions that belongs to a single object in the array and retrieve that object
MongoDB is applying query conditions on collection and returns the result with the matching documents. As both products twitteroauth & facebookmeta are part of the same document so the whole matching document will be returned.
If you want only a single matching entry in the document then you can use the MongoDB aggregation pipeline (with $unwind) where you can modify the result set.
For example:
db.collection_name.aggregate([
{
"$match": {
// pass matching criteria/conditions here
}
},
{
"$unwind": "$products" //to deconstruct products field in the result
},
{
"$match": {
"products.productName": "twitteroauth"
}
}
])
Note that the second match condition is used to add additional matching criteria/conditions on the deconstructed result set so that products can be filtered.
This will get you the result something like this:-
{
_id: new ObjectId("61da0ab855483312e8f4483b"),
products: {
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483f"),
productCode: '4pf',
productName: 'twitteroauth',
claims: [Array],
permissions: [Array]
},
__v: 0
}
Reference: https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/
Related
I'm new to mongoose, I'm confuse while create the query. Can you help me?
I have video collection like this:
{
_id: 603dea86cef0aed372cd9ce6,
category: [
"603dea86cef0aed372cd9cd8", // array of category objectId
"603dea86cef0aed372cd9cd9"
],
hashtag: [
"603dea86cef0aed372cd9cee" // array of hashtag objectId
],
video_id: '6925666264463576320',
share_id: 'greate_video',
author: 603dea86cef0aed372cd9cd8, // ref to Author objectId
cover: 'https://path.to/img.jpg',
post_by: 60083f24edae03650406ad48, // ref to User objectId
status: 1, // enum status [0, 1, 2]
date: 2021-03-02T07:34:30.635Z
}
I want to query to get data with structure like below. I mean, I will find by _id and get related data form other collections, more than that, I want the video list show with status 1, 2 (not 0) and sort by video _id: -1.
{
_id: 603dea86cef0aed372cd9ce6,
category: [
{_id: "603dea86cef0aed372cd9cd8", category_name: Tech},
{_id: "603dea86cef0aed372cd9cd9", category_name: Mobile},
],
hashtag: [
{_id: "603dea86cef0aed372cd9cee", hashtag_name: tech},
],
video_id: '6925666264463576320',
share_id: 'greate_video',
author: {_id: "603dea86cef0aed372cd9cd8", author_name: Nani, avatar: 'https://path.to/avatar.jpg'},
cover: 'https://path.to/img.jpg',
post_by: {_id: "603dea86cef0aed372cd9cd8", user_name: Username, avatar: 'https://path.to/avatar.jpg'},
status: 1,
date: 2021-03-02T07:34:30.635Z
}
How do I write the aggregation query? I tried with query like this but doesn't work, it show empty [] result.
const videoList = await Video.aggregate([
{
$lookup:
{
from: 'Author',
localField: "author",
foreignField: "_id",
as: "author_info"
}
}
])
Thank you
name of collection usually written in plural lowercase letters so I think you should change Author to authors
Background
Here's part of my User model:
const Group = require("./Group")
...
groups: {
type: [{ type: Schema.ObjectId, ref: Group }],
default: [],
},
And here's my Group model:
module.exports = mongoose.model(
"Group",
new Schema(
{
name: {
type: String,
required: true,
unique: true,
},
/**
* Array of User ObjectIDs that have owner rights on this group
*/
owners: {
type: [{ type: Schema.ObjectId, ref: User }],
default: [],
},
},
{
timestamps: true,
}
)
)
The Code
Here's the code I'm running to try and populate:
const user = await (await User.findOne({ _id: ... })).execPopulate("Group")
console.log(user.groups)
My console.log is outputting an array of object IDs, when I'd like it to output an actual Group document.
Attempted solutions
I've tried changing my ref to be using the string ("Group"), I've tried arranging my query differently, etc. I'm not sure how I'd go about doing this.
Apologies in advance if this is a duplicate, I've done my best to search but can't really find a solution that works for me.
Specifically, what do I need help with?
I'm trying to create a 'link' between a user model and a group model. In my console.log, I expect it to output a Group document; but it outputs an object ID (which is how it's stored raw in the database, meaning that Mongoose isn't transforming it correctly)
When you change execPopulate to populate like:
async function findUserAndPopulate(userId){
const response = await User.findOne({
_id: userId,
}).populate('groups')
console.log("response",response)
}
You got:
{
groups: [
{
owners: [Array],
_id: 5ecc637916a2223f15581ec7,
name: 'Crazy',
createdAt: 2020-05-26T00:31:53.379Z,
updatedAt: 2020-05-26T00:31:53.379Z,
__v: 0
}
],
_id: 5ecc6206820d583b99b6b595,
fullname: 'James R',
createdAt: 2020-05-26T00:25:42.948Z,
updatedAt: 2020-05-26T00:36:12.186Z,
__v: 1
}
So you can access the user.groups
See the doc: https://mongoosejs.com/docs/populate.html
I want to populate a date range picker display with highlighted cells where data exists in my database. I thus need to reduce my collection to an array of dates where records exist e.g.
// collection
[{
timestamp: ISODate("2020-01-28T20:42:00.000Z"),
data: 1,
},{
timestamp: ISODate("2020-01-28T18:42:00.000Z"),
data: 10,
},{
timestamp: ISODate("2020-01-28T15:42:00.000Z"),
data: 100,
},{
timestamp: ISODate("2020-01-25T15:42:00.000Z"),
data: 1000,
},{
timestamp: ISODate("2020-01-17T15:42:00.000Z"),
data: 10000,
}]
reduces to:
['2020-01-28', '2020-01-25', '2020-01-17']
The nature of the data stored in my database means that if any data exists on a given date, lots of data exists on that date. It is therefore slow to query the entire collection for a given date range and then reduce the result.
Is there a fast(er) way to query a collection to return the distinct set of dates on which data exists?
As I know you can only get json format result from mongodb query.
I could get the following result, which can be easily converted to the string array in javascript code:
[
{
"_id": "20200125"
},
{
"_id": "20200117"
},
{
"_id": "20200128"
}
]
I used $dateToString aggregation operator inside $project stage.
db.collection.aggregate([
{
$project: {
_id: 0,
date: {
$dateToString: {
format: "%Y%m%d",
date: "$timestamp"
}
}
}
},
{
$group: {
_id: "$date"
}
}
])
Playground
I have a Chats collection with a participants subdocument like so..
{
_id: '1',
participants: [ { _id: 'A', seen: false }, { _id: 'B', seen: false } ],
messages: []
}
There are only 2 participants and one of them is the currentUser. I don't know which. Also, there is only ever one chat for any pair of users.
In order to find a chat I use both user ids. So a find query looks like this:
Chats.find(
{ $and: [
{ participants: {$elemMatch: {_id: otherUserId}}},
{ participants: {$elemMatch: {_id: currentUserId}}}
]}
)
I'd like to allow the currentUser to update his own seen field in one update operation.
Currently I have to find the chat first, determine which index of the two users is the currentUser, then build a document to update that user and update it in a separate operation.
Is there something similar to a regex capture group so I can know the id of the element that matched currentUserId? Possibly like this.....
Chats.update( { $and: [
{ participants: {$elemMatch: {_id: otherUserId}}},
{ participants: capture({$elemMatch: {_id: currentUserId}})}
]},
{
$set: {"participants.(capture[0]).seen": true}
})
Or is there a better way?
Well this may not be a solution you're looking for but I thought I should suggest in case it helps.
Chats.update( { $and: [
{ participants: {$elemMatch: {_id: otherUserId}}},
{ participants: {$elemMatch: {_id: currentUserId}}}
]},
{
$set: {"participants.$.seen": true}
})
This will work for you because the $elemMatch stores the index of the matched array. As your anding the $eleMatch, it will store the index matched by the last $eleMatch which will be current user in your case. So, when you use the positional opertor, it will update the seen field for current user.
A simple way to easily update the 'seen' status would be to structure the data like this:
{
_id: '1',
participants: [ 'A', 'B'],
seen: [],
messages: []
}
Then you can update the 'A' has seen the message like this:
Chats.update(
{ $and: [ {participants: 'A'}, {participants: 'B'} ] },
{ $addToSet: { seen: 'A' } }
)
$elemMatch is not required if you have only a single query
condition inside it
$addToSet will only add 'A' if it does not
already exist.
I have the following data structure for my user model:
{
_id: object,
name: string,
quotes: [ { quote: string, createdAt: date } ],
friends: [ { _id: object } ] (this refers to the _id from the user model)
}
What i'd like to do is return this:
{
quote: string,
createdAt: date,
_id: object (friend's)
}
{
quote: string,
createdAt: date,
_id: object (friend's)
}
...
sorted by createdAt = recent to oldest
What I understood from your question is you want to unwind two array elements based on recent time.
db.user.aggregate([
{
$unwind: "$quotes"
},
{
$unwind: "$friends"
},
{
$sort: {
"quotes.createdAt": -1
}
}
])