Mongoose findOneAndUpdate conditional field value based on update or insert..? [duplicate] - javascript

This question already has answers here:
mongodb: upserting: only set value if document is being inserted
(4 answers)
Closed 5 years ago.
I'm using findOneAndUpdate with upsert: true in the options.
I'd like to conditionally set a field based on whether the document will be updated or inserted.
const find = { email: 'me#me.com' }
const newDoc = { name: 'me', email: 'me#me.com', key: '???' }
const options = { new: true, upsert: true }
Accounts.findOneAndUpdate(find, newDoc, options)
When it's an update, I need to leave the key field alone, but when it's an insert I need to generate a new key.
Is this possible..?

You can set a default in your Mongoose model.
For instance :
const generateKey = () => {
// ...
return key
}
// in your Mongoose model
key : {
type : String,
default : generateKey
}
Then, don't specify the key in the document you're sending to Mongo :
const newDoc = { name: 'me', email: 'me#me.com' } // No key
So it will generate a key on insert, or leave it be otherwise.

Related

Node.js: Mongoose filter data from array of ObjectIDs stored in collection [duplicate]

This question already has answers here:
mongodb/mongoose findMany - find all documents with IDs listed in array
(9 answers)
Closed 3 months ago.
I am trying to search using node.js, ejs and mongoose. All the filter parameters are working perfectly but only categoryIds is not (stored as a collection of ObjectIDs in the mongodb document, referring to the respective document in categories collection), always giving me the empty record set.
For example:
If I need to find the a movie called Cosmos (see the attached screenshot) then I can easily find it with all or any filter except categories. Once I select any category, the record-set will go blank even if the I have selected the one which it belongs to.
model.js
const Model = mongoose.model('Movie', new Schema({
...
categoryIds: [{
type: Schema.Types.ObjectId,
trim: true,
default: null,
ref: 'Category',
}],
copyrightId: {
type: Schema.Types.ObjectId,
trim: true,
default: null,
ref: 'Copyright',
},
...
}, {
timestamps: true
});
Controller.js
Router.get('/', (req, res) => {
const search = req.query;
const conditions = (() => {
let object = {};
['releaseYear', 'languageId', 'copyrightId'].forEach(filter => {
if (search[filter] != '') {
object[filter] = search[filter];
}
});
if (typeof search.categoryIds !== 'undefined') {
object.categoryIds = [];
search.categoryIds.forEach(item => object.categoryIds.push(item));
}
if (search.keywords != '') {
object.title = {
$regex: search.keywords,
$options: 'i'
};
}
return object;
})();
const count = await Model.count(conditions);
const items = await Model.find(conditions, {
__v: false,
imdb: false,
trailer: false,
createdAt: false,
updatedAt: false,
}).sort({
status: -1,
releaseYear: -1,
title: 1
})
.populate('languageId', ['title'])
.populate('copyrightId', ['title'])
.populate('categoryIds', ['title'])
.skip(serialNumber)
.limit(perPage);
...
});
All the fields in the search form
{
categoryIds: [
'6332a8a2a336e8dd78e3fe30',
'6332a899a336e8dd78e3fe2e',
'6332a87ba336e8dd78e3fe2c',
'634574ab339b1a6b09c1e144'
],
languageId: '',
copyrightId: '',
releaseYear: '',
rating: '',
seen: '',
status: '',
keywords: '',
submit: 'search' // button
}
filtered search parameters
{
categoryIds: [
'6332a8a2a336e8dd78e3fe30',
'6332a899a336e8dd78e3fe2e',
'6332a87ba336e8dd78e3fe2c',
'634574ab339b1a6b09c1e144'
]
}
Here is the screenshot of mongodb document.
...
if (typeof search.categoryIds !== 'undefined') {
object.categoryIds = {
$in: []
};
search.categoryIds.forEach(item => object.categoryIds.$in.push(
mongoose.Types.ObjectId(item))
);
}
console.log(object);
return object;
The is the final filter object
{
categoryIds: {
'$in': [
new ObjectId("6332a87ba336e8dd78e3fe2c"),
new ObjectId("634669f4a2725131e80d99f1")
]
}
}
Now, all the filters are working perfectly.
Thank you everyone.
The filter should contain all categoryIds and in the same order to match the document. It's not quite clear from the question if it is the intended functionality. If not, most popular usecases are documented at https://www.mongodb.com/docs/manual/tutorial/query-arrays/
I don't recall how mongoose handles types when you query with array function like $all, so you may need to convert string IDs to ObjectIDs manually, e.g.:
search.categoryIds.forEach(item => object.categoryIds.push(
mongoose.Types.ObjectId(item))
);

How to find all users that have emails in an array in prisma using Typescript?

Hi sorry for the confusing question. Basically, I want to find all the users that have emails that are included in the array.
For example I have an array:
const arr = ['test#gmail.com', 'test2#gmail.com']
and I have my model
model User {
id Int
username String
name String
email String
}
I want to do something like
const data = await prisma.user.findMany({
where : {
email: in arr
},
select : {
id: true
}
});
I have been trying to find a workaround for this for the longest time.
This is the syntax for the in operator in Prisma.
const arr = ['test#gmail.com', 'test2#gmail.com']
const data = await prisma.user.findMany({
where : {
email: {in: arr}
},
select : {
id: true
}
});
You can read more in the Prima Client Reference in the docs.

Update multiple documents in MongoDB by field's current value [duplicate]

This question already has answers here:
How to increment a field in mongodb?
(1 answer)
How to decrement like $dec?
(6 answers)
Closed 3 years ago.
I have an Employees Schema :
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const EmployeesSchema = new Schema({
EmployeeId: {
type: String,
required: true
},
// .... more attributes
Copies: {
type: Number,
required: true
InsertDate: {
type: Date,
default: Date.now
}
});
module.exports = Employees = mongoose.model(
"employees",
EmployeesSchema
);
and ids array ...
const _ids = // I get this one from some code manipulation - return the `_id` attribute
and I want to update those documents that are in _ids , by one attribute - Copies :
await Employees.update(
{
_id: {
$in: _ids
}
},
{
$set: { Copies: Copies - 1 } // here it looks for a var called Copies
},
{ multi: true }
);
How can I reduce 1 from Copies attribute , based on each document's Copies value ?

how do I increment field in array of objects after finding it (using findOne()) and before saving it?

I want to update an object inside an array of schemas without having to do two requests to the database. I currently am incrementing the field using findOneAndUpdate() if the object already exists and it works fine. but in case the object does not exist then I am having to make another request using update() to push the new object and make it available for later increments.
I want to be able to do only one request (e.g. findOne()) to get the user and then increment the field only if object exists in the array and if not I would like to push the new object instead. then save the document. this way I am only making one read/request from the database instead of two.
this is the function now:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } }
);
if (itemInDb) return true;
const updated = await Model.update(
{ _id: userId },
{ $push: { cart: body } }
);
if (updated.ok !== 1)
return createError(500, 'something went wrong in userService');
return true;
}
what I would like to do is:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOne(
{
_id: userId,
'cart.productId': body.productId,
}
);
if (itemInDb) {
/**
*
* increment cart in itemInDb then do itemInDb.save() <<------------
*/
} else {
/**
* push product to itemInDb then save
*/
}
Thank you!
You can try findOneAndUpdate with upsert.
upsert: true then create data if not exists in DB.
Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } },
{
upsert: true,
}
)
Use $set and $inc in one query.
try {
db.scores.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $set: { "cart.$.productName" : "A.B.C", "cart.$.productPrice" : 5}, $inc : { "cart.$.count" : 1 } },
{ upsert:true, returnNewDocument : true }
);
}
catch (e){
//error
}
reference Link : here
You can use upsert.
upsert is defined as an operation that creates a new document when no document matches the query criteria and if matches then it updates the document. It is an option for the update command. If you execute a command like below it works as an update, if there is a document matching query, or as an insert with a document described by the update as an argument.
Example: I am just giving a simple example. You have to change it according to your requirement.
db.people.update(
{ name: "Andy" },
{
name: "Andy",
rating: 1,
score: 1
},
{ upsert: true }
)
So in the above example, if the people with name Andy is found then the update operation will be performed. If not then it will create a new document.

MongoDb Node.js Querying Nested Objects not working?

I need to query for 2 dynamic properties using the node.js driver for mongodb.
Here's the data structure:
{
"_id":"123456",
"dateAdded":"2017-09-20T08:36:40.325Z",
"followers":{
"name1":{
"followedOn":"2017-09-20T08:36:40.325Z",
"unfollowedOn":null
},
"name2":{
"followedOn":"2017-09-20T08:36:40.325Z",
"unfollowedOn":null
}
}
}
Here's my code:
//Working but not dynamic
collections.find({ '_id': '123456', 'followers.name1': { $exists: false } })
//My failed attempt at making it dynamic
const id = "123456"
const username = "name1"
let query = {}
query['followers.'+username] = { $exists: true }
collections.find( { "_id": id, query }
Note that this is not a duplicate of "how to make a dynamic key in an object literal". The .find() method of node.js mongodb driver does not accept object literals. I can't find documentation of what it accepts exactly.
Your _id property needs to be within query object, not separate.
Here's how to do it:
let query = { _id: id };
query['followers.'+username] = { $exists: true }
collections.find(query);

Categories