Mongoose distinct query - node.js

Excuse my ignorance but it is possible to get distinct tag attribute with all the links in one query?
I know you can using .distinct('tag') but then I need to send the data twice which require querying the same link again
I have the following schema
var Link = new Schema({
title:{type:String, required:true},
url:{type:String, required:true},
tag:[{type: String,required:true}],
});
link
.find({})
.exec(function(err,q){
res.json(q);
});

In case you need all URLs per tag:
db.link.aggregate({
$group : {
_id : "$tag",
links: { $addToSet: "$url" }
}
})

Related

mongodb query events done to an item and group them into an array

I know that the title might be a bit vague, but I will elaborate here.
basically what I am trying to achieve is the following:
I have a collection with documents that have the following scheme:
bookId: <uuid>
genre: <string>
isTaken: true
historyIndex: each time something happens i increment this
returnedAt: not required but will be present on documents with historyIndex
takenAt: not required but will be present on documents with historyIndex
there are documents with no historyIndex field because they are representing the book itself without the action that were done to the book.
what i want to do is this:
I want to query the books by their unique uuid and then use the documents with historyIndex and add them to the main documents as in an array as called bookEvents
so the final results will be
bookId:
bookEvents: [] --> an array with all the entries that contain history index
basically everytime the status of the book changes, i am inserting an event with the date it was taken on and the date it was returned on
What would be the best way of achieving such thing ?
Should I query once and iterate in my code ?
Should I query twice (once where the fields exist and once where they don't) ?
Thank you in advance for all the people that will try to help!
You can use the plugin or events to achieve this.
var CounterSchema = new mongoose.Schema({
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);
var entitySchema = mongoose.Schema({
sort: {type: String}
});
entitySchema.pre('save', function(next) {
var doc = this;
counter.findByIdAndUpdateAsync({_id: 'entityId'}, {$inc: { seq: 1} }, {new: true, upsert: true}).then(function(count) {
console.log("...count: "+JSON.stringify(count));
doc.sort = count.seq;
next();
})
.catch(function(error) {
console.error("counter error-> : "+error);
throw error;
});
});
refer https://stackoverflow.com/a/40137723/8201020

Mongoose can't find any elements after changing property type

I originally have these two schemas:
var UserSchema = new Schema({
first: String,
last: String
});
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: { type: Schema.ObjectId, ref: 'User' }
});
But I want to edit my SaleSchema to save the user name instead of the ID, so I changed it for:
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: String
});
Next, I wanted to edit all the Sales documents and replace the user IDs on registeredBy for the user's full name, but I can't seem to be able to perform a find query for the old ID's.
Long story short, this query returns no matches on mongoose, but it works perfectly using the mongo console:
Mongoose
Sale.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
.then(results => res.json(results))
.catch(err => res.status(500).json(err));
// result: []
MongoDB console
db.sales.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
// result: 8 elements
After I modify my schema's property back to ObjectId, the mongoose query works again. Since I need to migrate to a new datatype, I want to be able to query and store both types of values. Is this possible?
Good question this is a complicated edge case. I am not super familiar with Mongoose specifically, but one way to do this would be to migrate your data at a lower level. For example, create a mongo script that uses the low-level mongo API to do the migration, something along the lines of:
db.sales.find().forEach(function(doc){
var user = db.users.find({ _id: doc.registeredBy });
db.sales.update({ _id: doc._id, }, {
$set: { registeredBy: user.first + ' ' + user.last }
});
});
This is similar to what a module like https://github.com/balmasi/migrate-mongoose does, but I've personally found it easier to use mongo scripts on the cli directly.
mongo < sale-schema-migration.js

Not able to persist array of objects in mongo using mongoose

I'm trying to persist an array of objects in a document using mongoose. I have tried multiple times but it's not persisting array in document. It places an empty array in document.
Following is my Schema:
var ProfileSchema = new Schema({
name: String,
PagesData: [{
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
}]
});
module.exports = mongoose.model('Profile', ProfileSchema);
I'm trying to save a document with an array of objects using following query:
var newProfile = new Profile();
newProfile.name = "someName";
newProfile.PagesData = [ { pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ];
newProfile.save(function(err, result, numAffected){
if(err) {
console.log(err);
res.send(500, "Error");
}
console.log(result);
res.send(200, "Success");
});
I tried debugging the mongo commands using
require('mongoose').set('debug', true)
On Debug logs it shows, empty array during insert command execution.
Can anyone please tell me how can I store this array of object in my schema ?
Thanks,
Update:
It's been too long and I'm still not able to figure out the root cause of the problem. There is a long thread going on github for this.
https://github.com/Automattic/mongoose/issues/3249
I would like other experts to please take a look and suggest me some way by which I can solve the issue. I'm really stuck at this.
Update 2:
None of the solution worked for me so far, so I decided to modify the schema only to meet my requirements. This resulted in a different problem:
I want to create a map with a objectId as key and an array of string values as its value. The closest that I can get is:
var schema = new Schema({
map: [{myId: {type:mongoose.Schema.Types.ObjectId, ref: 'MyOtherCollection'}, values: [String]}]
});
But somehow this is not working for me. When I perform an update with {upsert: true}, it is not correctly populating the key: value in the map. In fact, I'm not even sure if I have declared the schema correctly.
Can anyone tell me if the schema is correct ? Also, How can I perform an update with {upsert: true} for this schema?
Also, if above is not correct and can;t be achieved then how can I model my requirement by some other way. My use case is I want to keep a list of values for a given objectId. I don't want any duplicates entries with same key, that's why picked map.
Please suggest if the approach is correct or should this be modelled some other way?
Thanks
I tried the exact code you have provided here and it's working for me. I am not sure what is causing the issue for you. Until and unless we get the same issue, it's very difficult to rectify it.
Here are few suggestions which you might try:
Create a simple schema and try storing the object, that way you can
figure it out if it has to do something with the schema.
You can try out your schema in a sample app to find if some
dependency is causing the problem.
Once you know where exactly the problem is, you would be able to figure out a solution too. I hope it helps.
I tested this and the insert works for me using the below:
(I had to remove the get: decryptText, set: encryptText)
var n = { name: "Testing for mongoose", PagesData : [{ pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms:
[ 'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT' ] } ] }
Profile.create(n, function (err) {
if (!err) {
return 'records saved successfully';
}
else {
return error on save:' + err;
}
});
To create multiple pageDatas you can use it as an embedded collection instead of using arrays.
The Schema will be as follows:
var PagesDataSchema = new Scheme({
pageAccessToken: {type: String, get: decryptText, set: encryptText},
category: String,
name: String,
id: String,
perms: [String]
})
var ProfileSchema = new Schema({
name: String,
PagesData: [PagesDataSchema]
});
module.exports = mongoose.model('Profile', ProfileSchema);
Reference: http://mongoosejs.com/docs/subdocs.html
For Saving the document you can use like.
exports.save = function(req,res){
var test = new ProfileSchema; // new object for ProfileSchema domain.
test.name= req.body.name;
if(req.body.PagesData){
req.body.PagesData.forEach(function(page){ // For every element of pageData from client.
test.PagesData.push(page) // This pushes each and every pagedata given from the client into PagesData.
})
}
test.save(function (saveErr, saved) { // Saves the new document into db.
if (saveErr) {
console.log(saveErr)
return;
}
res.status(HttpStatus.OK).json(saved);
});
};
Hope this helps.
Have you tried
Profile.create({
name: "someName",
PagesData: [
{
pageAccessToken: 'someToken',
category: 'Bags/Luggage',
name: 'someBrandName',
id: '12345',
perms: [
'ADMINISTER',
'EDIT_PROFILE',
'CREATE_CONTENT'
]
}
]
}, function(err, profile) {
// do your stuff
})
?

save mongoose model and submodel after populate

I'm trying to find a way to simplify my backend logic and optimize my DB, for this it would be great if I could save a document and it's subdocuments all at once so that creating subdocuments becomes easier.
Here is a simple example:
the primary model:
var ItemSchema = new Schema({
name: {type : String},
extrainfo: {type: Schema.Types.ObjectId, ref: 'Extrainfo'},
})
mongoose.model('Item', ItemSchema);
the subdocument:
var ExtrainfoSchema = new Schema({
this: {type : String},
that: {type: String},
})
mongoose.model('Extrainfo', ExtrainfoSchema);
how I hoped to be able to save:
Item.findOne({ name: 'whatever' }).populate('extrainfo').exec(function (err, item) {
item.extrainfo.this = "something diferen" //some random change in subdocument
item.save(function(){console.log("yay")}
});
Is there a way to do this whithout having to save to the document and the subdocument individually ?
Is there a way to do this whithout having to save to the document and the subdocument individually ?
No. mongodb will write to one and only one collection during a given write operation, and mongoose doesn't have any feature as far as I know to provide a single-callback facility to write to multiple collections.

Use mongoose populate to "join" matching subrecords

Using node.js, mongoose (3.5+), mongodb. Have got two collections in the DB:
var AuthorSchema = new mongoose.Schema({
name: { type: String },
});
var StorySchema = new mongoose.Schema({
title: { type: String },
author: { type: type: Schema.Types.ObjectId },
});
What I would like to do is retrieve an author and populate it with a subcollection (say, "stories") that is looked up from Story and match the author. Yes, much like a SQL join.
All of the examples out there work on the AuthorSchema having an array of objectids that reference StorySchema objects - that works fine. But I want to go the opposite direction; partly due to minimizing insert/updates. If I follow the example, adding a new store requires adding a new Story document and updating the Author. I want to just insert a new Story that references the Author.
I suspect that populate() is the right way to go, but can't get it to work. I'm doing something like this:
Author.find({name: 'Asimov').populate({
path: 'stories',
model: 'Story',
match: {'author': this['_id']},
}).exec(function(err, authors) {
console.log(authors);
})
But this doesn't return any stories member in the returned authors. Is this not a populate() solution? Do I really need to structure the schemas differently? Or is there some other feature of mongoose/mongo that would do what I'm looking for.
In the story schema, do this:
author: { type: type: Schema.Types.ObjectId, ref:'Author' }, //or whatever the model name is
then you can run
Story.find({}).populate('author').exec(function(err,stories) {...});

Resources