Add a new field in mongoDB from another collection independently? - javascript

I have two collections: profiles and contents
The profiles collection looks like this:
{
_id: ObjectId('618ef65e5295ba3132c11111'),
blacklist: [ObjectId('618ef65e5295ba3132c33333'), ObjectId('618ef65e5295ba3132c22222')],
//more fields
}
The contents collection looks like this:
{
_id: ObjectId('618ef65e5295ba3132c00000'),
owner: ObjectId('618ef65e5295ba3132c22222'),
//more fields
}
What I need is to get those contents where owner is not included in the blacklist. I thought about to put the blacklist field into the contents documents. I could get the profile by id separately (in another query) and set it manually in the aggregation where I get the contents, but this requires one extra connection.
So my question is: Is there a way to add my profile into each document of another collection? keep in mind that I have the profile ID.

Here is some psuedo code on how it "should" look like, First fetching the blacklists owners, then using that variable as a parameter in the pipeline.
const profileId = input;
const blackListIds = await db.getCollection('profiles').distinct('blacklist', { _id: profileId });
const aggregationResults = await db.getCollection('contents').aggregate([
{
$match: {
owner: {$nin: blackListIds}
}
}
... continuation of pipeline ...
])

Related

MongoDB - Insert 2 documents, each in another collection but share ObjectId

I have this question:
I want to insert 2 documents into 2 collections.
One is for user and one is for company.
Both inserts are requested via api.
In created(Inserted) company I want to know, which user[Created/inserted] create this company. And in user i want to have _id of company that he inserted.
User
{
_id: "userGeneratedId",
companyId : Company._id
}
Company
{
_id: "companyGeneratedId",
registeredByID : user._id
}
How can this be done?
Thank you,
Dufino
There are two ways to go about this
The first and easy way
Add new fields to your user and company model. maybe call it userSaveId or whatever you choose. Now you will insert same unique id to these new fields fields, so that when you are retrieving a company, you can just retrieve it based on that ID.
The second way this could be done is by performing 4 operations. 2 insert operations and two update operations. Note that this would slightly increase the response time of your operations.
Suppose you have inserted a user and company, get the IDs of both the user document and company document as such:
const user = User.save(yourData);
const company = Company.save(yourCompanyData);
Afterwards get the ids and use it to update the documents that are already stored as such:
const userId = user._id;
const companyId = company._id;
User.updateOne({_id: userId}, {$set: {companyId: companyId}});
Company.updateOne({_id: companyId}, {$set: {registeredByID: userId}});
So the complete code would be this:
const user = User.save(yourData);
const company = Company.save(yourCompanyData);
const userId = user._id;
const companyId = company._id;
User.updateOne({_id: userId}, {$set: {companyId: companyId}});
Company.updateOne({_id: companyId}, {$set: {registeredByID: userId}});

Firebase / Firestore Add Document to Sub collection Version 9

Some reason, I just am not getting it.
I want to add a new document to a sub-collection. Here is my layout as follows:
Users----------- Collection
UID----------- Document
Lists------- Collection
Category-- Document
Category-- Document
...--
For the documents in the "Lists", I want to add a Doc to Lists.
The Doc is custom named.
I've tried the following:
async function AddCategory (category) {
return new Promise((resolve, reject) => {
const uid = auth.currentUser.uid
console.log(`UID: ${uid}`)
setDoc(doc(db, 'users', uid, 'lists', category), {
name: 'Johnny Doey'
}).then((res) => {
resolve(res)
}).catch((err) => {
reject(err)
})
})
}
This does not seem to work. The error I am receiving is 'Undefined'.
I almost feel like there is something simple I am missing.... I've checked my auth rules. Everything checks out. Tried to test with hard strings in place of my variables, still no luck...
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{documents=**} {
allow read, write: if request.auth != null && request.auth.uid == userId
}
}
}
now, the firestore data shows the initial user (UID) document to be italicized,
Even though non-existent ancestor documents appear in the console,
they do not appear in queries and snapshots. You must create the
document to include it in query results.
What in the heck...
Could someone please overlook this? Thanks!
According to this post:
"When you create a reference to a subcollection using a document id like this:
db.collection('coll').doc('doc').collection('subcoll')
If document id doc doesn't already exist, that's not a problem at all. In fact, after adding documents to subcoll, doc will appear in the Firebase console in italics, indicating that it doesn't exist."
What you can do is: First, create a setDoc() for collection "users" with its own document (whether auto-generated or manually coded), second, you can input your setDoc() query: setDoc(doc(db, 'users', uid, 'lists', category)....
For a better visualisation, here's a sample code:
setDoc(doc(db, 'users', uid), {
name: 'Johnny Doey'
}).then(() => {
setDoc(doc(db, 'users', uid, 'lists', category), {
// some additional inputs here...
})
}).catch((e) => {
console.log(e)
})
For additional reference, you can check, Non-existent ancestor documents.
Well - it works. I think one of the issues I was facing was altering the generated users.
My fix was to delete the users, and build it from scratch. That seemed to solve it for me. Not sure what exact constraints the generated user collection/doc has, but it seems it does have some restrictions tied into it.

Add, update and read sub-collection in firestore using Angular (#angular/fire)

I am trying to learn firebase. I am making a project like Linkedin using Angular and Firebase.
I am using the #angular/fire package in my project.
So far I have done authentication and adding some basic info of a user. I have users collection where each documentId have information like name, email and etc
Now my next goal is to create a work experience section. Since a user can have multiple work experience.
I have to decided to proceed with sub-collection under each user document id instead of creating a separate collection for work-experience.
Now I am having a bit trouble how to add sub-collection in my users collection.
A user can only add/update one experience at a time. My UI will be something similar to Linkedin. When the 'Add Experience' button is clicked a modal will pop up and there will be a form inside the form with fields such as jobTitle, companyName and etc. So once the submit is clicked I want to save that data under the work-experience sub-collection with a unique documentId.
Currently I am adding my basic info like this
Adding
addUser(user: any): Observable<void> {
const ref = doc(this.firestore, 'users', user.uid);
return from(setDoc(ref, user));
}
Updating
updateUser(user: any): Observable<void> {
const ref = doc(this.firestore, 'users', user.uid);
return from(updateDoc(ref, { ...user }));
}
Now I want to add sub-collection called work-experience under the users collection.
From your other question I think you need to add a sub-collection which contains a document with ID work-experience.
If my assumption is correct, do as follows to create the DocumentReference to this Document:
const workExperienceDocRef = doc(this.firestore, `users/${user.uid}/sub-sections/work-experience`);
Note: Make sure to use back ticks.
And then you can set the data of this doc as follows, for example:
return from(setDoc(workExperienceDocRef, {experiences: [{years: "2015-2018", company: "comp1"}, {years: "2018-2021", company: "comp2"}]}));
If you want to create a sub-collection for the user's document, do as follows:
const userSubCollection = collection(this.firestore, `users/${user.uid}/sub-collection`);
Then you can use the addDoc() method, which will automatically generate the document ID.:
const docRef = await addDoc(userSubCollection, {
foo: "bar",
bar: "foo"
});

Is there a way to shorten the code of getting one user from postgres DB in node.js?

I use node.js and pg library (node-postgres) to interact with database.
This is the code I use to retrieve one user of specific ID:
const { rows: users } = await db.query("SELECT * FROM users WHERE id = $1", [id]);
const user = users[0];
Even if there is only one record postgres always returns an array, so I retrieve this first item in the second line.
Is there a way to shorten this code if there is only one user? Like make it one line instead of two.
You can directly get the first user object by destructuring the rows array
const { rows: [user] } = await db.query(...)

Find after populate mongoose

I'm having some trouble querying a document by values matching inside the document after population by mongoose.
My schemas are something like this:
var EmailSchema = new mongoose.Schema({
type: String
});
var UserSchema = new mongoose.Schema({
name: String,
email: [{type:Schema.Types.ObjectId, ref:'Email'}]
});
I would like to have all users which have a email with the type = "Gmail" for example.
The following query returns empty results:
Users.find({'email.type':'Gmail').populate('email').exec( function(err, users)
{
res.json(users);
});
I have had to resort to filtering the results in JS like this:
users = users.filter(function(user)
{
for (var index = 0; index < user.email.length; index++) {
var email = user.email[index];
if(email.type === "Gmail")
{
return true;
}
}
return false;
});
Is there any way to query something like this straight from mongoose?
#Jason Cust explained it pretty well already - in this situation often the best solution is to alter the schema to prevent querying Users by properties of documents stored in separate collection.
Here's the best solution I can think of that will not force you to do that, though (because you said in the comment that you can't).
Users.find().populate({
path: 'email',
match: {
type: 'Gmail'
}
}).exec(function(err, users) {
users = users.filter(function(user) {
return user.email; // return only users with email matching 'type: "Gmail"' query
});
});
What we're doing here is populating only emails matching additional query (match option in .populate() call) - otherwise email field in Users documents will be set to null.
All that's left is .filter on returned users array, like in your original question - only with much simpler, very generic check. As you can see - either the email is there or it isn't.
Mongoose's populate function doesn't execute directly in Mongo. Instead after the initial find query returns a set a documents, populate will create an array of individual find queries on the referenced collection to execute and then merge the results back into the original documents. So essentially your find query is attempting to use a property of the referenced document (which hasn't been fetched yet and therefore is undefined) to filter the original result set.
In this use case it seems more appropriate to store emails as a subdocument array rather than a separate collection to achieve what you want to do. Also, as a general document store design pattern this is one of the use cases that makes sense to store an array as a subdocument: limited size and very few modifications.
Updating your schema to:
var EmailSchema = new mongoose.Schema({
type: String
});
var UserSchema = new mongoose.Schema({
name: String,
email: [EmailSchema]
});
Then the following query should work:
Users.find({'email.type':'Gmail').exec(function(err, users) {
res.json(users);
});
I couldn't find any other solution other than using Aggregate. It will be more troublesome, but we will use Lookup.
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}

Categories