How to include documents as subdocuments in MongoDB? - node.js

I have a basic user collection that consists of document like so:
{
user: 3949fj9fn9rjhfne93,
name: "Jerry",
country: 'au',
friends: ['20fn39r4hf93', 'g9en3irhr934', '4i5henifuw92']
}
Each of those friends also has a document the same, along with there being a large collection of countries that can be queried via the country code.
{
code: 'AU',
name: 'Australia'
}, {
code: 'NZ',
name: 'New Zealand'
}
My question is, how would I include the full document for each of those array items within the result, like so:
{
user: 3949fj9fn9rjhfne93,
name: "Jerry",
country: {
code: 'AU',
name: 'Australia'
},
friends: [{
user: 20fn39r4hf93,
name: "Bob",
friends: ['2049429fr', 'djwij393i4']
}, {
user: g9en3irhr934,
name: "Foo",
friends: []
}, {
user: 4i5henifuw92,
name: "Bar",
friends: ['2049429fr']
}]
}
I am using Mongoose in my application, and I understand that this could be done by using a simple for loop and pushing the results to the user object and then returning it in node with res.json(user), although what if the user had hundreds (or even thousands) of friends? The request would be huge. I also need to do this in multiple places within my API.
Is there a more efficient way to achieve this?

Related

How to query an attribute from an object inside of an array with Mongoose

I am trying to filter a list of 'professionals' using model.find() from Mongoose. The query I have works normally in MongoDB Compass but with Mongoose it does nothing.
The model goes like this:
{
name: string,
email: string,
password: string,
phoneNumber: number,
city: string,
myDescription: string,
specialty: [
{ name: string, certificate: string, isCertified: boolean }
],
image: { profile: string, jobs: [strings] },
availability: Boolean
}
The query in MongoDB Compass is { specialties: { $elemMatch: { name: 'Soldador General', isCertified: true } } } and it returns the list of 'professionals' as expected. When I use it in Mongoose it returns the entire list, so no filter.
Going through the documentation of Mongoose there is a method Query.elemMatch() but the problem is that the filter is dynamic, I want the users to filter by city, specialty or both, and also I might add some filters in the future. For now the queries look something like:
{ specialties: { $elemMatch: { name: 'Soldador General', isCertified: true } } }
{ city: 'Barranquilla' }
{ city: 'Barranquilla', 'specialties': { $elemMatch: { name: 'Soldador General', isCertified: true } } }
And so I don't see how to do this dynamically with Query.elemMatch(). BTW I retrieve the object using React useSearchParams and send the request via NodeJS.
Any help or guidance is highly appreciated!

MongoDB design for multiple auth user accounts

I have a backend with nodeJS, Express and MongoDB / mongoose...
My users can signUp/Login with Google or email and password. But now my question is what's the best option of building a model for that.
My first option is this:
{
firstname: myname,
lastname: mylastname',
email: 'email#example.com',
accounts: [
{ provider: google,
idGoogle: myid,
},
{ provider: simple,
email: my#email.com,
password: myp4ssw0rd
}
]
}
or:
{
firstname: myname,
lastname: mylastname',
email: 'email#example.com',
accounts: [
{ id: q1w2e3,provider:google},
{ id: r4t5y5, provider:simple},
]
}
and a reference to another collection
{
_id:q1w2e3,
provider:google
idGoogle:wqwqe24},
{
id: r4t5y5,
provider:simple,
email:my#email.com
password: myp4ssw0rd
}
My main goal is optimal performance with many users
I would not use a separate collection and lookup. MongoDB will manage the embedded document without much overhead. And if it starts degrading, just add an index.
The simplicity of queries with the embedded document outweighs any overhead.

Gremlin NodeJS query returns different results than Groovy

I have a query that I originally wrote in the console:
g.V().hasLabel('group')
.has('type', 'PowerUsers')
.local(__.union(
__.project('group').by(__.valueMap().by(__.unfold())),
__.inE().outV().project('user').by(__.valueMap().by(__.unfold())))
.fold()).unfold().toList()
I get something like:
==>{group={owner=A, group_id=21651399-91fd-4da4-8608-1bd30447e773, name=Group 8, type=PowerUsers}}
==>{user={name=John, user_id=91f5e306-77f1-4aa1-b9d0-23136f57142d}}
==>{user={name=Jane, user_id=7f133d0d-47f3-479d-b6e7-5191bea52459}}
==>{group={owner=A, group_id=ef8c81f7-7066-49b2-9a03-bad731676a8c, name=Group B, type=PowerUsers}}
==>{user={name=Max, user_id=acf6abb8-08b3-4fc6-a4cb-f34ff523d628}}
==>{group={owner=A, group_id=07dff798-d6db-4765-8d74-0c7be66bec05, name=Group C, type=PowerUsers}}
==>{user={name=John, user_id=91f5e306-77f1-4aa1-b9d0-23136f57142d}}
==>{user={name=Max, user_id=acf6abb8-08b3-4fc6-a4cb-f34ff523d628}}
When I run that query with NodeJS, I was expecting to get a similar result, but I don't. I get something like this:
[ { group:
{ owner: 'A',
group_id: '21651399-91fd-4da4-8608-1bd30447e773',
name: 'Group 8',
type: 'PowerUsers' } },
{ user:
{ name: 'John',
user_id: '91f5e306-77f1-4aa1-b9d0-23136f57142d'} },
{ user:
{ name: 'John',
user_id: '91f5e306-77f1-4aa1-b9d0-23136f57142d'} },
{ user:
{ name: 'Jane',
user_id: '7f133d0d-47f3-479d-b6e7-5191bea52459'} },
{ user:
{ name: 'Jane',
user_id: '7f133d0d-47f3-479d-b6e7-5191bea52459'} },
{ group:
{ owner: 'A',
group_id: 'ef8c81f7-7066-49b2-9a03-bad731676a8c',
name: 'Group B',
type: 'PowerUsers' } },
{ user:
{ name: 'Max',
user_id: 'acf6abb8-08b3-4fc6-a4cb-f34ff523d628' } },
...
Because I have the same users in different groups, I can't use dedup(), and if the results where the same in NodeJS as Groovy, that'd be perfect. Unfortunately, they are not, and I don't understand why the results in NodeJS are all messed up, considering that the query is exactly the same
I feel like you could just use a nested project() to keep your group with your users:
g.V().hasLabel('group')
.has('type', 'PowerUsers')
.project('group', 'users')
.by(__.valueMap().by(__.unfold()))
.by(__.in().project('user').by(__.valueMap().by(__.unfold()).fold()))
In this way the you don't have to worry about ordering anything. I think it might be more Gremlin-esque though to use the "group" as a key in a Map with the values being a list of users:
g.V().hasLabel('group')
.has('type', 'PowerUsers')
.group()
.by(__.valueMap().by(__.unfold()))
.by(__.in().project('user').by(__.valueMap().by(__.unfold()).fold()))

Require a mongodb db model deign. Whether to choose Refs or Embeded doc

I'm designing a backend for a Talent hunt application. Initially I designed the DB using refs. Like I followed the primary key forign key using refs to join my collections. But as the collection requirement increases it's being hard to join all the collections. Then I come across the one to many collections. So I'm thinking to get a suggestion from experts. Let me tell the requirements.
I have the following collections initially.
User.js
{
_id: "5ecfdc903165f709b49a4a14",
name: "Lijo",
email: "lijo#gmail.com"
}
then I have a category table for serving the categories
Category.js
{
_id: 5ecfdc903165f709b49a5a18,
title: "Acting",
code: "ACT"
}
If one user adds his talents to profile. I used one another collection for storing. Rmember the user can save multilple talents. So i created,
UserTalents.js
{
_id: "5ecfdc903165f709b49a6c87",
categoryId: "5ecfdc903165f709b49a5a18",
userId: "5ecfdc903165f709b49a4a14",
level: "beginner"
}
For each catgeory need to upload atleast one media along with description Soagain I created a new collection for that.
Media.js
{
_id: "5ecfdc903165f709b49a8a14",
talentId: "5ecfdc903165f709b49a6c87",
userId: "5ecfdc903165f709b49a4a14"
media: "5ecfdc903165f709b49a4a14_1.jpg"
}
And I need to have these users connected. For that craeted.
Friends.js
{
_id: "5ecfdc903165f709b49a8a18",
sender: "5ecfdc903165f709b49a4a14",
receiver: "5ecfdc903165f709b49a4a15"
status: "accepted"
}
Is this good to continue??? Expecting a huge amount of users. Or can I follow like:
User.js
{
_id: "5ecfdc903165f709b49a4a14",
name: "Lijo",
email: "lijo#gmail.com",
talents: [
{
_id: "5ecfdc903165f709b49a5a18", // _id from Category
title: "Acting",
code: "ACT",
level: "beginner",
media: "5ecfdc903165f709b49a4a14_1.jpg"
}
],
friends: [
{
_id: "5ecfdc903165f709b49a4a15",
name: "Test",
status: "approved"
}
]
}
I I follow this, then how do I update the name fileds in talents and friends array either one of its original name is changed?
Which is better approach?

MongoDB - Update/Insert the Array of object

I've one collection i.e 'comments', Which contain two fields. Like below -
Comments -
{
_id: 'unique_string',
dataVar: [{
name: {
type: string
},
value: {
type: string
}
}]
}
You can assume the collection data is like below -
[
_id: 'xxxxxx',
user: 'robin',
dataVar: [{
name: 'abc',
value: 123
}, {
name: 'bcd',
value: 12345
}]
]
Now the problem is - (I'm using mongoose in nodeJS application)
- How to update and insert the data in 'dataVar' ?
- If data is no available then new document should be created.
Case 1.
If user send the post data like
{
_id: 'xxxxxx',
user: 'robin',
dataVar: [{name: 'abc', value: 12345}]
}
then after performing query, the above document (whose _id is 'xxxxxx') should be update like below -
{
_id: 'xxxxxx',
user: 'robin',
dataVar: [
{name: 'abc', value: 12345},
{name: 'bcd', value: 12345}
]
}
Case 2.
If data is not present then new document should be created.
To update record in mongodb, you can use like
comments.findOneAndUpdate({ _id: 'xxxxxx' }, { $push: { dataVar: {$each: [{name: 'abc', value: 123}, {name: 'def', value: 456}] } } })
You can use {upsert: true} option for creating field in collection if not exist.
For updating record, you have to use sub-schema inside your main schema. So that _id field is created in every object of array. So you can uniquely identify object to update inside array.
comments.findOneAndUpdate({ "dataVar._id": 'xxxxxx' }, { $set: { name: 'xyz', value: 789 } })
In this way, you can update object, which is inside array.
If you don't want to use _id, you have at-least one field which contains unique values. So that you can findOneAndUpdate that specific object inside array.

Resources