On Meteor, get data from a collection while looping on another - node.js

I have a collection that stores a list of posts users follow, and each user have its own document for it. It is structured like this:
{"_id": "12365",
"user": "123548" //user ID
"posts" : [
{"postId": "225"},
{"postId": "688"},
{"postId": "55"},
(...)
]}
user key refers to the _id of that user, created using account package.
I am trying to, while on user profile page, list all posts that user follow, but I am not being able to, by each postId, show that postTitle instead of its Id.
The HTML to render the list is like this:
<p class="title">{{postId}}</p>
And the template helper that get that user's followed posts is like this:
Template.singleUser.helpers({
postsLibrary: function () {
var postsLibraryContent = postsLibrary.find({
user: Meteor.user();
});
},
});
How can I loop in the posts array from the collection of followed posts, but show each of the posts title that refer to each postId? Should I do it inside this helper above?
-- update --
Title is stored on a Collection of posts, like this:
{
"_id": "9845", //same as postId from the other collection
"title": "Lorem Ipsum",
"author": "Peter Parker",
(...)
}

Change your html from this
<p class="title">{{postId}}</p>
To this
{{#each posts}}
<p class="title">{{postId}}</p>
{{/each}}
This will loop over the array posts.

Ok, got it!
Template.singleUser.helpers({
postsLibrary: function () {
var data = [];
//first we find all this current user postLibrary collection
var postsLibraryContent = PostsLibrary.find({
user: this._id
}).posts;
//then for each item on `posts` array:
for (var i = 0; i < postsLibraryContent.length; i++) {
//get that array item postId value and store on a var
var postId = postsLibraryContent[i].postId;
//creates an array with the post data by searching for its _id
postData = Posts.find({
_id: postId
}).fetch();
//and then push those data to an array above created
data.push({
postTitle: postData[0].title,
postAuthor: postData[0].author
})
};
//then return data array
return data;
}
});
And to render it on HTML:
{{#each postsLibrary}}
<li>
<p class="title">{{postTitle}} - {{postAuthor}}</p>
</li>
{{/each}}

Related

How to extract $oid from _id in Mongodb Services?

i want to extract $oid from _id object
first find user & get User id then find notifs by userId
var userId=myUsers.findOne({UniqueDeviceId:payload.query.UniqueDeviceId},{'_id':1});
result of code :
"_id": {
"$oid": "5cc45aa8e432ee5c12b59260"
}
my expect : "5cc45aa8e432ee5c12b59260"
User Table :
Notif table:
i found my answer
var userId=myUsers.findOne({UniqueDeviceId:payload.query.UniqueDeviceId},{'_id':1});
retun a promise so :
var result=userId.then(function(res){
var notifs = myNotif.find({UserId: res._id}).toArray();
return {UserID : res._id, Notif : notifs};});
finally
retun result;
i wish be usefull

Trying to create an instance and multiple related instances in a many to many relationship

I am attempting to create an instance and multiple related instances with a many to many relation using a junction table.
While creating the multiple related instances, I need to add a value to a property on the junction table as well. I don't know if it is my lack of knowledge of sequelize or promises that is causing my problem.
The code I am using is below. This code does add the items to the database, but I need to redirect after the operation has completed, which is not working.
Basically, I need to create a Recipe. Once that is created, I need to create Ingredients and relate them to that Recipe. The ingredients are stored in an array coming from a form on an HTML page. While relating the Ingredients, I need to add the ingredient_quantity to the RecipeIngredients table, which is the through part of the relationship (the junction table).
global.db.Recipe.belongsToMany(
global.db.Ingredient,
{
as: 'Ingredients',
through: global.db.RecipeIngredients,
foreignKey: 'recipe_id'
});
global.db.Ingredient.belongsToMany(
global.db.Recipe,
{
as: 'Recipes',
through: global.db.RecipeIngredients,
foreignKey: 'ingredient_id'
});
router.post('/new', ensureLoggedIn, bodyParser.json(), function (req, res) {
var recipeName = req.body.recipe_name;
var steps = req.body.steps;
var ingredients = req.body.ingredients;
var ingredientQty = {};
var currentIngredient;
var ingredientsToAdd = [];
db.Recipe.create({
recipe_name: recipeName,
directions: steps,
FamilyId: req.user.FamilyId,
CreatedBy: req.user._id
})
.then(function (recipe) {
for (var i = 0; i < ingredients.length; i++) {
currentIngredient = ingredients[i];
ingredientQty[currentIngredient.ingredient_name] =
currentIngredient.quantity;
db.Ingredient.findOrCreate({
where: {
ingredient_name: currentIngredient.ingredient_name,
FamilyId: req.user.FamilyId
}
})
.spread(function (ingredient, created) {
if (created) {
console.log("Added Ingredient to DB: " +
currentIngredient.ingredient_name);
}
ingredient.Recipes = {
ingredient_quantity:
ingredientQty[ingredient.ingredient_name]
};
ingredient.CreatedBy = req.user._id;
recipe.addIngredient(ingredient)
.then(function () {
console.log("Added Ingredient " + ingredient.ingredient_name
+ " to Recipe " + recipe.recipe_name);
});
})
}
})
.finally(function(recipe){
res.redirect('/recipes');
});
});
Any help would be greatly appreciated. I know that I am running into issues because of trying to use promises inside of a loop, I just don't know how else I can accomplish this.
Using sequelize, you can create objects along with its associated objects in one step, provided all objects that you're creating are new. This is also called nested creation. See this link and scroll down to section titled "Creating with associations"
Coming to your issue, you've a many-to-many relationship between Recipe and Ingredient, with RecipeIngredients being the join table.
Suppose you've a new Recipe object which you want to create, like:
var myRecipe = {
recipe_name: 'MyRecipe',
directions: 'Easy peasy',
FamilyId: 'someId',
CreatedBy: 'someUserId'
}
And an array of Ingredient objects, like:
var myRecipeIngredients = [
{ ingredient_name: 'ABC', FamilyId: 'someId'},
{ ingredient_name: 'DEF', FamilyId: 'someId'},
{ ingredient_name: 'GHI', FamilyId: 'someId'}]
// associate the 2 to create in 1 step
myRecipe.Ingredients = myRecipeIngredients;
Now, you can create myRecipe and its associated myRecipeIngredients in one step as shown below:
Recipe.create(myRecipe, {include: {model: Ingredient}})
.then(function(createdObjects){
res.json(createdObjects);
})
.catch(function(err){
next(err);
});
And that is all !!
Sequelize will create 1 row in Recipe, 3 rows in Ingredient and 3 rows in RecipeIngredients to associate them.
I was able to fix the problem that I was having. The answers here helped me come up with my solution.
I am posting the solution below in case anyone else runs into a similar issue. I created a variable to store the Promise from Recipe.create(), I used Promise.map to findOrCreate all of the ingredients from the form data. Because findOrCreate returns an array containing Promise and a boolean for if the item was created, I then had to get the actual ingredients out of the results of the Promise.map function. So I used the JavaScript array.map() function to get the first item from the arrays. And finally, use Promise.map again to add each Ingredient to the Recipe
var ingredients = req.body.ingredients,
recipeName = req.body.recipeName,
ingredientsQty = {}; // Used to map the ingredient and quantity for the
// relationship, because of the Junction Table
var recipe = models.Recipe.create({recipe_name: recipeName});
// Use Promise.map to findOrCreate all ingredients from the form data
Promise.map(ingredients, function(ing){
ingredientsQty[ing.ingredient_name] = ing.ingredient_quantity;
return models.Ingredient.findOrCreate({ where: { ingredient_name: ing.ingredient_name}});
})
// Use JavaScript's Array.prototype.map function to return the ingredient
// instance from the array returned by findOrCreate
.then(function(results){
return results.map(function(result){
return result[0];
});
})
// Return the promises for the new Recipe and Ingredients
.then(function(ingredientsInDB){
return Promise.all([recipe, ingredientsInDB]);
})
// Now I can use Promise.map again to create the relationship between the /
// Recipe and each Ingredient
.spread(function(addedRecipe, ingredientsToAdd){
recipe = addedRecipe;
return Promise.map(ingredientsToAdd, function(ingredientToAdd){
ingredientToAdd.RecipeIngredients = {
ingredient_quantity: ingredientsQty[ingredientToAdd.ingredient_name]
};
return recipe.addIngredient(ingredientToAdd);
});
})
// And here, everything is finished
.then(function(recipeWithIngredients){
res.end
});

How to retrieve the desired field's value from a collection and store it's ID in another collection

I am using node js and mongodb. I have this this route code
router.get('/taglist', function(req, res) {
var db = req.db;
var collection = db.get('tagcollection');
collection.findOne({},{},function(e,docs){
res.render('taglist', {
"taglist" : docs
});
});
});
this is for getting all taglist from the database and the response rendering to taglist.jade which is given below-
extends layout
block content
h1.
Tag List
ul
each tag, i in taglist
li
#{tag.tagname}= tag._id
Now I want to search a tagname which is already stored in my taglist collection and want to store that ID belonging to tagname which matched and store it in another collection.
Right now my code is just finding all the tagname and printing all the tag_id in browser. I am new in web application and just started with node and MongoDB.
UPDATE
first code getting all the tagcollection data and second one is displaying all the id of each document.but this is not what I am expecting.
I am explaining what I am expecting-
let's suppose tagcollection contain 2 or more documents.
{
"_id" : ObjectId("ksjsed3cccjdkmx"),
"tagname" : "dinner"
}
{
"_id" : ObjectId("skd9a93cccb8kdn"),
"tagname" : "music"
}
and so on ...
first I want to search for music tag
if I find music tag is available in my tagcollection then it will display id of music tag and store this id in new collection(say in newtagcollection)
router.js:
router.get('/searchtag/:tag', function(req, res) {
var db = req.db;
var collection = db.collection('tagcollection');
collection.findOne({ tagname: req.params['tag'] }, function(e, result) {
res.render('searchtag', { tag: result });
});
});
searchtag.pug:
extends layout
block content
h1.
Tag Search
if tag
p= tag.tagname + ' = ' + tag._id;
else
p Tag not found!

Dynamic URL with mongo method

Regarding these post : Mongoose prototype : how to insert an url dynamically?
I'm looking to do pretty much the same (URI dynamically insert).
The solution provided is good but I do not understand some things...
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PicturesSchema = new Schema({
album : { type : String, required : true, trim : true },
pictures : { type : Array, required : false, trim : true }
});
PicturesSchema.virtual('pictureUrls').get(function() {
return this.pictures.map(function(picture) {
return 'https://s3.amazonaws.com/xxxxx/'+ picture;
});
});
var Pictures = mongoose.model('Pictures', PicturesSchema);
// Demo:
var pictures = new Pictures({
album : 'album1',
pictures : [
'1434536659272.jpg',
'1434536656464.jpg',
'1434535467767.jpg'
]
});
console.log( pictures.getPics() );
The given solution injects a "virtual field" in addition to retaining the "pictures" field with the name of the images (without URL).
How to retain only the field "PicturesURL" containing the full URL of the image without displaying a redundancy with the "Pictures" field?
How to retrieve and display the JSON format query results as it knowing that data.ToJson returns an error: has no method 'toJson'?
Pictures.find().exec(function(err, data){
if(err){
console.log(err);
}
console.log('%j', data.toJSON({ virtuals : true }) );
});
A strategy to store only the picture url without the picture would be to create a Mongoose middleware on save.
You could then populate the picture field with the name of the picture, and the full url will be the one stored in the database and later retrieved.
For example:
PictureSchema.pre('save', function (next){
if (this.isModified(pictureUrls){
this.pictureUrls = this.pictureUrls.map(function(picture){
return 'https://s3.amazonaws.com/xxxxx/'+ picture;
})
}
});
With this solution, every time you modify the pictures array, it will go over every element of the array and add the s3 path.
Since it won't be a virtual, it will be easier for you to use the model with event using toObject or toJson.

Mongoose dynamic query

Query parsed from URL, example :
?year=2014&cat=sonny
Or it can be
?year=2014&id=223&something=high&cat=sonny
I could do
Model.find({year: 2014}).where('cat').equals('sonny')
But what if there a second example? How can I make it dynamic?
You can set the query to a variable and add multiple conditions:
var query = Model.find();
query.where('year').equals('2014');
query.where('cat').equals('sonny');
query.where('id').equals('223');
query.where('something').equals('high');
query.exec(callback);
For dynamic, just pass the query to a for loop and iterate through an array of your filter objects:
var query = Model.find();
var filters = [
{fieldName: "year", value: "2014"},
{fieldName: "cat", value: "sonny"}
...
];
for (var i = 0; i < filters.length; i++) {
query.where(filters[i].fieldName).equals(filters[i].value)
}
query.exec(callback);
Building on the cdbajorin's answer - I suspect many coders are trying to take input from a form and dynamically build a Mongoose filter from the end users input. (or at least that was my scenario).
If you 'name' the html input fields the same as your Mongoose Schema name
<input type='text' name='person.address'>
Then in your code you can use the req.body object
var query = Model.find();
for (var fieldName in req.body)
{
if(req.body.hasOwnProperty(fieldName)) //no inherited properties
{
if(req.body[fieldName]) //get rid of empty fields
{
query.where(fieldName).equals(req.body[fieldName]);
}
}
}
query.exec(function(err,data){console.log('QUERY EXECUTE : ' + err, data, data.length);});

Resources