Mongoose - Remove several objects from an array (not exact match) - arrays

I have a collection Playlist that contains an array of items
{
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [
{
id: { // do not mix up with _id, which is the autogenerated id of the pair {id,type}. ID is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
}
]
}
Mongo automatically adds the _id field to the items when I push a pair {id,type} to items (but I don't care about it).
Now I would like to remove several "pairs" at once from the items array.
I have tried using $pullAll but it requires an exact match, and I do not know the _id, so it does not remove anything from items
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
I have tried using $pull with different variants, but it removed ALL objects from items
playlistModel.update({userId:userId},{$pull:{items:{"items.id":{$in:["123","456"]}}}},null,function(err){
playlistModel.update({userId:userId},{$pull:{items:{$in:[{id:"123",type:"video"},{id:"456",type:"video"}]}}},null,function(err){
Am I missing something or am I asking something that isn't implemented?
If the latter, is there a way I can go around that _id issue?

OK I found a way that works using $pull:
playlistModel.update({userId:userId},{$pull:{items:{id:{$in:["123","456"]}}}},null,function(err){
It doesn't take the type into account but I can't see any issue with that since the id is unique across all types anyway.
Although I will wait a bit to see if someone has a better solution to offer
EDIT
With Veeram's help I got to this other solution, which IMO is more elegant because I don't have _ids that I don't need in the database, and the $pullAll option seems more correct here
var playlistItemSchema = mongoose.Schema({
id: { // do not mix up with autogenerated _id. id is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
},{ _id : false });
var schema = new Schema({
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [playlistItemSchema]
});
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){

tips:
you can use _id field to handle your playlistModel data.
mongoose api : new mongoose.Types.ObjectId to generate an Object_id
let _id=new mongoose.Types.ObjectId;
playlistModel.updateMany({_id:_id},{ $set: { name: 'bob' }}).exec(data=>{console.log('exec OK')});

Related

TanStack Table 8 : access nested array in data model

I'm looking for a way to access a nested array in my data structure.
My objects in my data array look like this :
{
name: name,
logo: url,
categories: [
{
name: Entertainment,
slug: "entertainment
},
{
name: Kids,
slug: kids
},
]
}
In the Docs I states that I need to use columnHelper.accessor to extract primitive values for each item in your data array.
My question is : how can I configure my accessor on "categories" to display both of them in my cell (using map I guess) ?
First, you don't need to use columnHelper; you can, for convenience (well, type safety, mostly).
You can do something like this:
cell: info => {
const value = info.getValue() as YourCategoryType[]; //casting may not be required here
return <>{value.map(v => <p key={v.slug}>{v.name}</p>)}</>
},

Next js with Prisma: Upsert based on two conditions

I'm trying to do the following using Prisma:
If a category row with the same hash and user_id I have exists, just update it's "name" field, otherwise, create the row
Is this possible? TS is giving me an error saying that the type of the key given on "where" has to be of categoriesWhereUniqueInput, yet neither hash nor user_id are unique, they can repeat, it's the combination between the two that's gonna be unique
How can I work around this? Do I have to manually check if there's an id and update/create based on that?
Thanks a lot in advance!
const category = await prisma.categories.upsert({
where: {
hash,
user_id: id,
},
update: {
name,
},
create: {
hash,
name,
user_id: id,
},
});
When a combination of fields is unique, Prisma will generate a new type for the where condition which is basically just the name of all the relevant fields appended together with _.
Take a look at the categoriesWhereUniqueInput to see what the name is. Most likely it is called user_id_hash or hash_user_id.
This is what the query will look like, roughly speaking:
const category = await prisma.categories.upsert({
where: {
user_id_hash: { // user_id_hash is the type generated by Prisma. Might be called something else though.
user_id: 1,
hash: "foo"
}
},
update: {
name: "bar",
},
create: {
hash: "foo",
name: "bar",
user_id: 1,
},
})

Mongo query - push unique items and preserve order in array

So this is a fairly common scenario where I want to add unique items and preserve order in an array - I have a collection of mongo docs that follow this schema:
{
_id: ObjectID,
name: string,
email: string
favorites: [array of objectIDs]
}
I'm trying to update the favorites array only if the ID I am trying to add doesn't already exist. I also need to PRESERVE ORDER in the array and hence only want to append at the end.
I'm getting a "Cannot read property '_id' of null" error with the following update command...what am I doing wrong?
Customer.findOneAndUpdate(
{
_id: customerId, //find correct customer doc
favorites: {
$ne: itemId //only push if array doesn't already contain
}
},
{
$push: {
favorites: itemId
}
},
{ new: true} //return updated doc
)
I'm using $push to preserve order, and wanted to use the $ne operator to prevent duplicates in the customers favorites array. If I take out the favorites block it can find the doc but adds duplicates. As soon as I add in the favorites filter with the $ne, it complains with "Cannot read property '_id' of null".
Try $addToSet instead of $push.
$addToSet by default only adds to set if it is not present. For example...
{
$addToSet: {
favorites: itemId
}
}
Source: https://database.guide/mongodb-push-vs-addtoset-whats-the-difference/

mongoose query: find an object by id in an array

How could I find an image by id in this Schema. I have the id of the User and the id of the image I am looking for. What would be the best way to do this and do all images in this case have different ids or could they have the same id because they don't belong to the same User?
My Schema looks like this:
var userSchema = new Schema({
local: {
email: String,
password: String
},
facebook: {
id: String,
token: String,
email: String,
name: String
},
name: String,
about: String,
images: [{
id: Schema.ObjectId,
link: String,
main: Boolean
}]
});
When you are interested in the full object it is a simple find:
.find({"facebook.id":"<id>", "images.id":<image-id>})
I don't think that there is a way to reduce the image array in the result.
To update a single element in the image array you can use this:
.update({"facebook.id":"<id>", "images.id":<image-id>}, {$set : {"images.$.main" :false} } );
userSchema .find({facebook.id: "some ID",{ "images.id": { $in: [ id1, id2, ...idn] }}
since images are inside the document you can have same ID's however every time you query you should keep in mind that you send some other parameters such as facebook.id or facebook.email along with image id's to retrieve them. Otherwise you end up getting all that might be irrelevant only because you decide to keep same ID's for images.
tl;dr
I struggled with this and came up with a solution. Like you, I was trying to query for a deeply nested object by the _id, but I kept coming up empty with the results. It wasn't until I did some type checking that I realized the id value I was getting from my frontend, while directly supplied by mongoose, was in fact a String and not an Object.
I realize this question was already partially answered before, but that person's solution didn't work for me, and the comment on the answer tells me you wanted to update the specific image you queried for, which is exactly what I was trying to do.
The solution
In order to select an object from the nested array by the _id value, first you'll have to install the npm package bson-objectid and use the provided method to convert your string into an objectId in your query.
In your terminal:
npm i bson-objectid
In your code:
const ObjectId = require('bson-objectid')
userSchema.findOneAndUpdate(
{ "facebook.id": <user-id>, "images._id": ObjectId(<image-id>) },
{ "$set": { "images.$.main": false } },
{ new: true }, // an extra options parameter that returns the mutated document
(err, user) => {
if (err) {
handleErr(err)
} else {
console.log(user)
// do something with new user info
}
)

Unique array values in Mongoose

Currently trailing out Mongoose and MongoDB for a project of mine but come across a segment where the API is not clear.
I have a Model which contains several keys and documents, and one of those keys os called watchList. This is an array of ID's that the user is watching, But I need to be sure that these values stay unique.
Here is some sample code:
var MyObject = new Mongoose.Schema({
//....
watching : {type: Array, required: false},
//....
});
So my question is how can I make sure that the values pushed into the array only ever store one, so making the values unique, can i just use unique: true ?
Thanks
To my knowledge, the only way to do this in mongoose is to call the underlying Mongo operator (mentioned by danmactough). In mongoose, that'd look like:
var idToUpdate, theIdToAdd; /* set elsewhere */
Model.update({ _id: idToUpdate },
{ $addToSet: { theModelsArray: theIdToAdd } },
function(err) { /*...*/ }
);
Note: this functionality requires mongoose version >= 2.2.2
Take a look at the Mongo documentation on the $addToSet operator.
Mongoose is an object model for mongodb, so one option is to treat the document as a normal javascript object.
MyModel.exec(function (err, model) {
if(model.watching.indexOf(watchId) !== -1) model.watching.push(watchId);
model.save(...callback);
});
Although, I do agree that mongoose should have some support for this built in the form of a validator for the collection document reference feature-- especially because most of the time you want to add only unique references.
That's how you can do it using Mongoose,
IF your upcoming value is an Array
Model
.findOneAndUpdate({ _id: yourID },
{ $addToSet: { watching: { $each: yourWatchingArr } } },
function(err) { /*...*/ }
);
IF your upcoming value is a string
Model
.findOneAndUpdate({ _id: yourID },
{ $addToSet: { watching: yourStringValue } },
function(err) { /*...*/ }
);

Resources