MongoDb-Node:Agregation node-mongo query for chat - javascript

im new on mongo and want to get data for a chat, let me explain.
i have a colection of messages:
_id:id
viewed:boolean
created_at:date
text:String
receiver:ObjectId
emitter:ObjectId
i want all the list of messager for a certain emitter and receiver order by the date (like a normal chat)
i have tryed aggregation like this:
db.messages.aggregate(
[
{
$lookup: {
from: "users",
localField: "emitter", // field in the orders collection
foreignField: "_id", // field in the items collection
as: "fromItems"
}
},
{
$match: {
'emitter':ObjectId("5c8917b4ef9ebf2e608c68dc")
}
}
,
{
$addFields: {
ids: { _id: "$_id" } ,
created: { created_at: "$created_at" }
}
},
{
$group:
{
_id: { tot:["$emitter", "$receiver"] },
text: { $addToSet:"$text"},
}
},
{
$sort: {created_at: 1}
}
]
)
But this gives me an array of messages only of a certain emitter and dont give me the date or the viewed data.
Im really new on mongo and node so if someone can help me with a explain will be great.
Thanks for reading and sory for the bad english

You must include date or the viewed data in $group stage.
Try with this.
{
$group:
{
_id: { tot:["$emitter", "$receiver"] },
text: { $addToSet:{text:"$text",created:"$created.created_at"}},
created_at:{$last:"$created.created_at"}
}
},
Why there is ids and need for tot fields and created as a object ??

Related

How do I pick then sort MongoDB documents and then find next document to mine?

I have a collection of documents, which I need to first narrow down by set criteria, then sort alphabetically by string value inside those documents — let's say that's a "search result". I then need to find document that matches a given _id and then pick a document next to it (before or after) from the above "search result".
Background:
I use mongoose to query my database via Node.js.
I have a set of "special sections" in my blog that are comprised of all the articles that must have three particular conditions associated within the keys in the document. I can get the list of articles belonging to said section like so:
const specialSectionListQuery = Article.find({
tag: { $ne: "excluded" },
[`collections.cameras`]: { $exists: true },
status: "published",
})
To finish creating the "special section," I must sort the documents alphabetically via their title attribute:
.sort({ [`collections.cameras.as.title`]: "asc" })
Now I want to add a link to "next article within the same special section" at the bottom of such articles. I know _id and any other value needed from the current article. The above query gives me an ordered list of documents within the section so I can easily find it within that list specialSectionListQuery.findOne({ _id: "xxx" }).exec().
However, I need to find the next article within the above list. How do I do that?
My attempts thus far:
I tried to create article list via aggregation, which led me nowhere (I simply made my app do exactly the same thing — make a list for a "special sectin"):
Article.aggregate([
{
$match: {
tag: { $ne: "excluded" },
[`collections.cameras`]: { $exists: true },
status: "published",
},
},
{
$sort: {
[`collections.cameras.as.title`]: 1,
},
}
]).exec()
But I can't for the life of me figure out how to iterate to the next document in the list properly.
I have thought of saving the list in Node.js memory (variable) and then finding what I need via JavaScript but that can't be scalable.
I have considered creating a new collection and saving the above list there but that would require me to either 1) do it every time a document is altered/added/deleted via Node.js — which is a lot of code and it may break if I interact with database another way 2) rebuild the colleciton every time I run the query, but that feels like it'll lack in performance.
Please help and thank you!
P.S.:
Example collection which should cover most of the cases I'm looking to solve for:
[
{
_id: 1,
name: "Canon",
collections: { cameras: { as: { title: "Half-Frame" } } },
tag: "included",
status: "published"
},
{
_id: 2,
name: "Pentax",
collections: { cameras: { as: { title: "Full-Frame" } } },
tag: "included",
status: "published"
},
{
_id: 3,
name: "Kodak",
collections: { film: { as: { title: "35mm Film" } } },
tag: "included",
status: "published"
},
{
_id: 4,
name: "Ricoh",
collections: { cameras: { as: { title: "Full-Frame" } } },
tag: "included",
status: "published"
},
{
_id: 5,
name: "Minolta",
collections: { cameras: { as: { title: "Half-Frame Review" } } },
tag: "excluded",
status: "published"
},
{
_id: 4,
name: "FED",
collections: { cameras: { as: { title: "Full-Frame" } } },
tag: "included",
status: "draft"
}
]
One thing you can try is to extend your $sort by adding _id so that it always returns documents in deterministic order:
{
$sort: {
"collections.cameras.as.title": 1,
_id: 1
}
},
{
$limit: 1
}
Once your first query returns the document with _id: 2 and collections.cameras.as.title: Full-Frame, you can use below query to get subsequent document:
{
$match: {
$and: [
{
tag: { $ne: "excluded" },
"collections.cameras": { $exists: true },
status: "published",
},
{
$or: [
{
$and: [
{ "collections.cameras.as.title": { $eq: "Full-Frame" } },
{ "_id": { $gt: 2 } }
]
},
{ "collections.cameras.as.title": { $gt: "Full-Frame" } }
]
}
]
}
},
{
$sort: {
"collections.cameras.as.title": 1,
_id: 1
}
},
{
$limit: 1
}
In this case due to deterministic $sort you can exclude previously found document by adding additional filtering criteria and the order should be preserved.
Mongo Playground

Mongoose : find document with subdocument filter

I have an User like so the id is NOT the _id)
{
id: string;
}
Which can create files like so
{
name: string;
author: User;
}
I would like to get all Files where the author is a given User, but I do not know how to use the "filter" function to do that.
So currently I do
const author = await this.userModel.find({ id });
return this.filesModel.find({ author });
Is there a more efficient way to do it ?
(I use NestJS with the Mongoose integration, the syntax used is the same as the Mongoose library)
EDIT
Given the User document
{
_id: 'OVZVIbovibiugb44'
id: 10
}
And the Files documents
[
{ name: "1.docx", author: ObjectId('OVZVIbovibiugb44') },
{ name: "2.docx", author: ObjectId('voisbvOVISBEIVBv') },
]
I would like to use the function
findOwned(authorId = 10) {
const author = await this.userModel.find({ id });
return this.filesModel.find({ author });
// But do it only with "filesModel"
}
And get, as a result,
[
{ name: '1.docx', author: 'ObjectId('OVZVIbovibiugb44') },
]
You can use $lookup into an aggregation query to merge collections.
Also, as your id is an String and your author is an ObjectId you will need one previous stage using $toObjectId
So the query is similar to this:
$match stage (optional) to query only with documents you want. Like a filter
$project to convert id String field to ObjectId. You can user $set also.
$lookup to merge collection and the ouput is in a field called files.
$project to output only files array from the merge.
db.User.aggregate([
{ "$match": { "id": "5a934e000102030405000001" } },
{ "$project": { "id": { "$toObjectId": "$id" } } },
{ "$lookup": {
"from": "Files",
"localField": "id",
"foreignField": "author",
"as": "files" }
},
{ "$project": { "files": 1 } }
])
Example here

Using $match on an embedded document in an aggregate

I am trying to use $match to find items with a specific _id in a double embedded document.
I have a document called users which contains information such as name, email, and it also contains an embedded document which has the business this user is with.
I also have a document called businesses, which contains an embedded document which has the building that this business is in.
I also have a document called building.
I am trying to have a mongo query which returns all of the users with a business at a certain building ID.
I have an aggregate function which uses $lookup to match the users to the building they are in. and this does work. However now I am trying to use $match to only return the documents with a specific building id.
Here is an example of my user, business and building documents:
_id: 5ca487c0eeedbe8ab59d7a7a
name: "John Smith"
email: "jsmith9#gmail.com"
business: Object
_id: 5ca48481eeedbe8ab59d7a38
name: "Visitors"
_id: 5ca48481eeedbe8ab59d7a38
name: "Visitors"
building: Object
_id: 5ca48481eeedbe8ab59d7a36
name: "Building1"
_id: 5ca48481eeedbe8ab59d7a36
name: "Building1"
When I return the aggregated query it returns documents in the following format:
{
"_id": "5ca487c0eeedbe8ab59d7a7a",
"name": "John Smith",
"email": "jsmith9#gmail.com",
"business": {
"_id": "5ca48481eeedbe8ab59d7a38",
"name": "Visitors"
},
"__v": 0,
"user_building": {
"_id": "5ca48481eeedbe8ab59d7a38",
"name": "Visitors",
"building": {
"_id": "5ca48481eeedbe8ab59d7a36",
"name": "Building1"
},
"__v": 0
}
},
However when I add the match in, it returns []. What am i doing wrong here?
router.get("/:id", async (req, res) => {
const users_buildings = await User.aggregate([
{
$lookup: {
from: "businesses",
localField: "business._id",
foreignField: "_id",
as: "user_building"
}
},
{ $unwind: "$user_building" },
{
$match: {
"user_building.building": { _id: req.params.id }
}
}
]);
You need to match _id inside the building object. Try with this
{
$match: {
"user_building.building._id": req.params.id
}
}
if not working
{
$match: {
"user_building.building._id": ObjectId(req.params.id)
}
}
op edit: I imported ObjectId with:
var ObjectId = require('mongodb').ObjectID;
and used the second solution and it worked correctly.

Mongoose, get child document and group by

I'm trying to get some results and group the by an id, but I don't know a way to do that, I was trying with $unwind and $lookup, but when I use the $group, it doesn't give me results, so, I will appreciate any feedback.
usercalification model
{
calification: Number,
status: Number,
place: {
type: Schema.ObjectId,
ref: 'Place',
}
}
place model
{
name: String,
description: String
}
I was trying to do something like this:
mongoose.model('usercalification').aggregate([
{
$lookup: {
from: 'places',
localField: 'place',
foreignField: '_id',
as: 'places'
}
},
{
{
$unwind: '$places'
}
}
]);
It works like it should, the issue is when I try to $groupBy
mongoose.model('usercalification').aggregate([
{
$lookup: {
from: 'places',
localField: 'place',
foreignField: '_id',
as: 'places'
}
},
{
{
$unwind: '$places'
}
},
{
$group: {
_id: '$place',
calification: { $avg: '$calification' },
count: {$sum: 1}
}
}
]);
I'm grouping by $place cuz that's the Place Id that I need to group by.
I'll appreciate any feedback.
The issue was that those were not ordered in the correct way.

Filter array during publication

I have a document that looks as follows.
{
_id: "1234",
orderIds: [1,2,3,4,5]
}
I want to publish this document, however, I want to lookup and filter this reactively. With an aggregation in 3.2, it would be something like this:
var id = "1234"
db.users.aggregate([
{ $match: { _id: id } },
{ $unwind: '$orderIds' },
{
$lookup: {
from: 'orders',
as: 'orders',
localField: 'orderIds',
foreignField: '_id'
}
},
{ $match: { 'orders.status': { $ne: 'closed' } } },
{
$group: {
_id: "$_id",
orders: { $push: '$orders' }
}
}
])
However, I can't find a way to do this with plain publications, and mongo 2.6.7 that meteor ships with. Is there a way to do it?

Categories