How to store array of ObjectID's in Mongo with Sails? - mongodb

After my initial question:
Handling relationships in Mongo and Sails?
I managed to use this approach, however I now need to resolve this the way I originally thought. For example, I have my Photo and Category models & collections, but now my category also contains addresses and business details.
In my Photo model I have the following:
attributes: {
caption : { type : 'string' },
fid : { type : 'string' },
user : { model : 'user' },
categories : { model : 'category', type : 'array'}
}
In my Category model I have the following:
attributes: {
photo : { model: 'photo' },
category : { type: 'string'},
address : { type: 'string'},
phone : { type: 'integer'},
//Foreign Keys
categories : { collection: 'photo', via: 'categories', type : 'integer'}
}
Now I can get the categories in the Photo collection to show as an ObjectID if I removed the type array and just send a single ObjectID of a category, however in my case, a photo can have more than one Category.
If I try sending an array of ObjectID's in my query, they just show up in the database as a String array. Is there anyway I get store these as an array of ObjectID's instead?

Basically it's many to many relationship. You can use Waterline scheme like this:
Photo Model
attributes: {
caption : { type : 'string' },
fid : { type : 'string' },
user : { model : 'user' },
categories : { collection : 'category', via : 'photos'}
}
Category Model
attributes: {
photos : { collection: 'photo', via: 'categories' },
category : { type: 'string'},
address : { type: 'string'},
phone : { type: 'integer'},
}

Related

Mongoose remove an element from array

This is my spa schema.
const spaSchema = new Schema({
name: String,
contactNumbers: String,
address: String,
images: [String],
});
I want to delete the element in the images array where the element name is 'abc.jpg' and documant id is x. How do I achive this?
I not really sure about your question, so I will cover 2 options. On both, I will assume that you are making an update on the spa collection.
1) Removing the images field from the document
To totally remove the field images from the desired document you can use the MongoDB $unset operator, as:
Original document:
{
_id : ObjectId('some_id'),
name : 'abc.jpg',
contactNumbers: '...',
address: '...',
images: ['...']
}
Update method:
Spa.update(
{ name : 'abc.jpg', _id : ObjectId('some_id') },
{ $unset : { images : null } }
);
That will result in the :
{
_id : ObjectId('some_id'),
name : 'abc.jpg',
contactNumbers: '...',
address: '...'
}
2) Removing one element within the images field
If you are trying to remove just one element with a specific value from the images array, you can use the MongoDB $pull operator, like:
Original document:
{
_id : ObjectId('some_id'),
name : '...',
contactNumbers: '...',
address: '...',
images: ['123.jpg','abc.jpg','def.jpg']
}
Update method:
Spa.update(
{ _id : ObjectId('some_id') },
{ $pull : { images : 'abc.jpg' } }
);
That will result in the :
{
_id : ObjectId('some_id'),
name : '...',
contactNumbers: '...',
address: '...',
images: ['123.jpg','def.jpg']
}
I hope that is what you looking for. =]
To remove one element from images(where you store image's name) you should use $pull as bellow
Spa.update(
{ _id : mongoose.Types.ObjectId('some_id') },
{ $pull : { images : 'IMAGE_NAME_THAT_YOU_WANT_TO_REMOVE_FROM_ARRAY' } }
);

Referencing for mongoose

i'm currently trying to reference a collection called items with the structure below
packageSchema = schema({
recipient: String,
contents: [{item :{type: mongoose.Schema.Types.ObjectId,
ref: 'items', required : true}, amount: String}]
Below is my code for getting one package via its id
getOnePackage : function(id,callback)
{
packageModel.findById(id,callback)
.populate('contents')
}
So when i call the above function i'm expecting to get this result
{
recipient : Dave
contents : [
{item : {
_id:5d2b0c444a3cc6438a7b98ae,
itemname : "Statue",
description : "A statue of Avery"
} ,amount : "2"},
{item : {
_id:5d25ad29e601ef2764100b94,
itemname : "Sugar Pack",
description : "Premium Grade Sugar From China"
} ,amount : "5"},
]
}
But what i got from testing in Postman is this :
{
recipient : Dave,
contents : []
}
May i know where did it went wrong? And also how do i prevent mongoose from automatically insert an objectId for every single element in the contents array....
Because element in contents array is object with item field so your populate should be:
.populate('contents.item')

Cannot query nested document's _id (other fields work)

I want to find all documents where vendor._id has a certain value. Below is the code, I tried, but it returns nothing.
let name = sampleData.name, _id = sampleData._id
Product.find({"vendor._id":ObjectID(_id)}).then((products) => {
//returns empty array
})
With the same method I tried to query a different field and it works. But I want to query with _id because other fields could vary with time.
Product.find({"vendor.name":name}).then((products) => {
//returns all documents that satisfy the condition.
})
Below is a sample document which I want to find
{
"status" : "active",
"connectedFarms" : [
{
"_id" : "5c412c62bf8a6602f04ae0bf",
"status" : "inActive",
"margin" : 10,
"price" : 55
},
{
"_id" : "5c4567bcb3845b0536a4d92e",
"status" : "inActive",
"margin" : 20,
"price" : 60
},
{
"_id" : "5c4567c4b3845b0536a4d931",
"status" : "active",
"margin" : 7,
"price" : 53.5
}
],
"vendor" : {
"_id" : ObjectId("5c3fcc0c7657ee02ac24bc21"),
"name" : "manna"
}
}
And here is the schema for this document.
let ProductSchema = new mongoose.Schema({
vendor:{_id:String, name:String},
connectedFarms:[{_id:String, name:String, status:String, price:Number, margin:Number}],
status:{
type:String,
trim: true,
minlength:1
}
});
Let's take a different approach on this, and make vendor its own schema. Mongoose does not allow you to nest schemas, so you cannot make the vendor._id a true ObjectID.
Vendor Schema
const VendorSchema = new mongoose.Schema({
name: string
});
module.exports = mongoose.model('Vendor', VendorSchema);
Product Schema
const ProductSchema = new mongoose.Schema({
vendor: {
type: mongoose.Types.ObjectID,
ref: 'Vendor'
},
connectedFarms: [{
_id: String,
name: String,
status: String,
price: Number,
margin: Number
}],
status: {
type: String,
trim: true,
minlength: 1
}
});
module.exports = mongoose.model('Product', ProductSchema);
Now when you want to query a product based on the vendors _id, it's very simple! All you need to do is supply the _id of the vendor in the query. NOTE: There is no reason to convert the _id to an ObjectID in the query, as mongoose accepts strings and converts them later on.
Query
const vendorID = myVendor._id;
Product.find({ vendor: vendorID })
.then((products) => {
// Do something with the found products
});
That's it! Much simpler to do, and much cleaner in the database. The vendor field is now easier to reference. You also have the ability to get the full vendor object in a query if desired by populating in the query. The difference is, the population will return the vendor name and _id, rather than just the _id. To do this, run the following:
Product.find({ vendor: vendorID })
.populate('vendor')
.then((products) => {
// Do something with the populated found products
});

MongoDB insert a document for every documents in another collection

I have a collection (users) containing some documents like so :
{
_id: ObjectId("56d45406be05db4022be51f9"),
morecontent : ""
},
{
_id: ObjectId("56d45406be05db3021be32e3"),
morecontent : ""
}
I would like to create a new document for every entry in the user collection.
The documents would be created from a notification object like this :
{
type: 'alert',
msg: 'This is important'
}
The collection notifications should look something like this :
{
_id: ObjectId("56d45406be05db3021bf20a1"),
someoldcontent: "This was here before the request"
},
{
_id : ObjectId("56d45406be05db4022be20b1"),
user: ObjectId("56d45406be05db4022be51f9"),
type: 'alert',
msg: 'This is important'
},
{
_id : ObjectId("56d45406be05db3021be32e3"),
user: ObjectId("56d45406be05db3021be32e3"),
type: 'alert',
msg: 'This is important'
}
Is there any way to do this in a mongodb request?
Thanks to professor79 for helping me a lot with the question.
After lots of effort, the query was found. We used the aggregation framework in order to succeed.
The only 2 aggregations needed are $project and $out. The $out will automatically take care of the new _id in the notification document.
Given this collection named user :
{
_id: ObjectId("56d45406be05db4022be51f9"),
morecontent : ""
},
{
_id: ObjectId("56d45406be05db3021be32e3"),
morecontent : ""
}
We want to create a notification containing the same msg and type field for every document in the user collection.
Each notifications will be in the notifications collection an will have as a reference its corresponding userId.
Here is the query to achieve such a result :
db.user.aggregate([
{
$project: {
userId: '$_id',
type: { $literal: 'danger' },
msg: { $literal: 'This is a message' },
},
},
{ $out: 'notifications' },
])
The output in notifications collection:
{
"_id" : ObjectId("56d6112e197b4ea11a87de1a"),
"userId" : ObjectId("56d45406be05db4022be51f9"),
"type" : "danger",
"msg" : "This is a message"
},
{
"_id" : ObjectId("56d6112e197b4ea11a87de1b"),
"userId" : ObjectId("56d45406be05db3021be32e3"),
"type" : "danger",
"msg" : "This is a message"
}
As we have some chat to clarify problem:
the steps are needed to perform this operation server side:
first step - match > get users id
project ids to new documents as required
out -> store output in notification collection

Limiting embedded document size

I have user schema for MongooseJS with embedded document photos by PhotoSchema:
var UserSchema = new Schema({
email : { type : String, index : 'unique', set: toLower }
, login : { type : String, index : 'unique' }
, password : { type : String }
, salt : { type : String }
, photos : [ PhotoSchema ]
, name : { type : String }
});
When I retreive one user, how can I limit quantity of photos in result set?
Or should I retrive all photos that user have (even if there is a million)?
You can't retreive users with limited quantity of photos, but you can:
1.Load user(s) without photos first:
db.users.find( { _id: 1 }, { photos : 0 } );
2.Load just user photos thats you need:
db.users.find({}, {photos:{$slice: [20, 10]}}) // skip 20, limit 10
Documentation