comment in comment for blog post - angularjs

I'm using the Mean Stack and trying to get a comment in comment feature to work for a blog post.
Where i'm getting stuck is trying to get from angular to mongo to update a comment for a given blog and a comment for a comment. I basically don't know how to get angular/express/mongoose to use the a subdocument id or nested subdocument id to update the parent blog or comment.
What I have managed to do so far is:
Create my schema -
var mongoose = require('mongoose'), Schema = mongoose.Schema;
var childSchema = new Schema;
childSchema.add({
firstName: 'string',
lastName: 'string',
comment: 'string',
children: [childSchema]
});
var parentSchema = new Schema({
firstName: 'string',
lastName: 'string',
blog: 'string',
children: [childSchema]
});
var Parent = mongoose.model('Parent', parentSchema);
Load Mongo with some data -
{
"firstName": "bobby",
"lastName": "edwards",
"blog": "this is blog 6",
"children": [
{
"firstName": "cat",
"lastName": "edwards",
"comment": "this is blog 6.1",
"children": [
{
"firstName": "dave",
"lastName": "edwards",
"comment": "this is blog 6.2",
"children": []
}]
}]
}
Get all blogs with nested comments from mongo and display correctly in angular - each blog or comment has a form attached
Return a single parent blog -
Create a parent blog -
UPDATED
I have managed to get one level down but for this i had to send the comment id as a param, modify the express route and pass the results of findbyid to results.comment.id({_id: req.params.comment_id}).
Node App
app.get('/parent/:post_id/:comment_id', postsController.single);
Node Controller
module.exports.single = function (req, res) {
console.log(req.params.post_id);
Parent.findById({ _id: req.params.post_id }, function (err, results) {
if (err) {
console.log(err);
}
var id = results.children.id({ _id: req.params.comment_id }); //function (err, commentresults) {
console.log('triggered 2');
console.log(id);
res.json([id]);
});
};
That being said this was for learning/testing and can't see how this would work if i needed to create/return/update a comment 10 levels down.

Looking at this from a different perspective, I'd just have two optional fields, topLevelCommentId and commentOwnerId, in your schema.
Anytime you insert a comment, then, as long as you appropriately set the topLevelCommentId as the top level parent, and commentOwnerId as the immediate parent, you should have no problem getting your comments nested appropriately.

Thanks for the feedback. I think I need to gain a better understanding of what is possible in MongoDB. Therefore I have decided to sit a couple of MongoDB University course. One on the Development Side and one on the Operation Side.

Related

How do I query a referenced object in MongoDB

There are two collections in my mongo database: Book and Author
The Book collection has an author field which references the author who created it
const schema = new mongoose.Schema({
...
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Author'
})
I am using graphql, and my current code looks like this. booksByAuthor is a query that takes in a name, then returns the books written by that author.(this is working and gives me the correct output)
booksByAuthor: async (root) => {
const author = await Author.findOne({name: root.name})
return await Book.find({
author: author._id
})
}
My current implementation first queries the Author collection, and then uses the id of the author to then query the Book collection. However, I feel like there must be a way to do this without going through the Author collection.
Ideally, I want it to be something like this: (pseudo-code)
return await Book.find({
author.name: {root.name}
})
I've tried doing
return await Book.find({
"author.name": { $in: [root.name] }
})
and
return await Book.find({
name: { author: { $in: [root.name] } }
})
but both returns an empty array. I don't know if I should even be using $in for this case
You can use $lookup to achieve this in single query. $lookup is similar to joins in sql.
Based on your question i have created some sample documents. Below is the query for getting the books by author.
db.book.aggregate([
{
"$lookup": {
"from": "author",
"localField": "author",
"foreignField": "_id",
"as": "author"
}
},
{
"$match": {
"author.name": "Kafka"
}
}
])
Here is the Mongo Playground. Please follow the link to see the query in action. You will also able to see the sample documents i have used. Feel free to play around with it according to your needs.

How to compose a graphQL query from a REST Api response?

I have an API response from a REST API server which I called using my REACT + Apollo with apollo-link-rest. but the response of the API looks like this
[
[
{
"name": "joe"
"address": "123 street hello city"
},
{
"name": "joe2"
"address": "1233 street2 hello2 city"
}
],
2356
]
How can I create a query with this kind of response that has an array with a number as a last item in the array and the first item of the array consist of list of users?
So far I just have this query.
const QueryTest = gql`
query people {
people #rest(type: "People", path: "/allpersons")
}
`;
1st: you need name and address props within (inside) people query - 'ask for what you need' - to get results.
2nd: Work with apollo-link-rest usually needs type patching - technique used to adapt/transform existing (REST) responses to the current needs (structures). It can be combined with response transformer but this step isn't required.
Sometimes you need to add id (calculated from other fields, unique) for nested types (apollo normalizing cache requirement) or define additional 'response type' above desired/required type - google for type patching examples (f.e. patchDeeper usage) or tutorials... define patchers, use debugger...
It's far easier when you know what to expect/what is needed - compare responses with working graphql responses.
Had the same issue, you can use responseTransformer in your link to transform the response.
const restLink = new RestLink({
uri: '...',
responseTransformer: async response =>
response.json()
.then((data: any) => {
if (Array.isArray(data)) return { data }
else return data
})
});
So you should be able to use the query like this:
const QueryTest = gql`
query people {
people #rest(type: "People", path: "/allpersons") {
data
}
}
`;
Where data would contain:
[
[
{
"name": "joe"
"address": "123 street hello city"
},
{
"name": "joe2"
"address": "1233 street2 hello2 city"
}
],
2356
]

How can we set context variables for IBM Watson from backend

I want to pass some values to frontend in form of context variables in IBM Watson through my Node app. How can I achieve it?
I tried to add the value I want to add to current context variable object and sent that back. Still no help. Is there any way I can do it?
Edit:
Right now, I am adding the required value as a new key-value pair to the context object from Node app as follows.
...
let user_name = "MJ"
context.user_name = user_name
response.send({
output: output,
context: JSON.stringfy(context)
})
...
And in Watson Console, in one of the dialogue nodes I have used like,
Hey $user_name, How are you?
But the output I am getting is,
Hey , How are you?
I can see user_name value in the context object, but I can't use it in the way I mentioned above. Is there any other way to do so?
Any help is appreciated. Thanks in advance!
I was having the same problem. My solution was changing the code when you call the IBM server and request .json:
...
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text,
context: {username : variable},
...
The username I set in Watson Assistant as well as a context, the variable I used a function to get the name though Query (Because in my application, I am calling the chatbot though an IFrame.), but you can use any variable set on javascript.
You can add any value to the Context object, which can be accessed in your Node.JS app and if you send that value to the front-end, then it should be accessible in the UI as well.
Below I've mentioned a sample welcome response from Conversation service. You can access the Context object from the response of Conversation service and add a new key-value pair to that object. In the response, you'll see that I'm accessing a context variable username that has the value MJ, which has been added dynamically to the context.
`
{
"intents": [],
"entities": [],
"input": {
"text": ""
},
"output": {
"text": ["Hello MJ! How can I help you today?"],
"nodes_visited": ["Conversation Start"],
"log_messages": []
},
"context": {
"username": "MJ",
"conversation_id": "5835fa3b-6a1c-4ec5-92f9-22844684670e",
"system": {
"dialog_stack": [{
"dialog_node": "Conversation Start"
}],
"dialog_turn_counter": 1,
"dialog_request_counter": 1,
"_node_output_map": {
"Conversation Start": [0]
}
}
}
`
Now to update the context, fetch the response and add a new key-value pair
`
var convResponse = <responseObj from Conversation call>;
var context = convResponse.context;
//add new value to context
context["new_key"] = "new value";
`
Now the next call that you make to Conversation, use this updated context instead of the context you received from the previous call. You can send back the response from Conversation to the front-end as well which can then be shown to the user.
var payload = {
assistantId: assistantId,
sessionId: req.body.session_id,
context: {
skills: {
"main skill": {
user_defined: {
username: 'John Doe'
}
}
}
},
input: {
message_type: 'text',
text: "blah",
},
};
works for me. Seen here: https://medium.com/#pranavbhatia_26901/watson-assistant-v2-context-sharing-3ca18626ed0d

Best practice Backand regarding JSON transformation and related objects

I am currently trying to figure out how to setup my Backand app and its REST API. This question is related to question: Backand deep querying. However, I was hoping that I could get some best practice code examples on how to perform server side code to perform a loop and create a JSON responds with the following criteria:
I want to be able to make a REST request to Backand and get one data object back that has manipulated/merged two data objects from my database.
I have an object called "media" and another named "users". Obviously, users contain user information and media contains information on a picture that the user has uploaded. The two objects are related by the userId and by collection set in Backand. I want to make a GET request that responds with a JSON object with all pictures and a nested user object on each picture object that contains the related user information. I know that I get back "relatedObjects", and I could then make some manipulation on the client side, but I am hoping that there is another easier way to do this from the Backand administration system either on server side code or as a query.
So, my question is, what's the best way to produce a REST call that responds a database object with nested related data object through Backand?
Here's the object models (shorten for clarity):
User object model as set up in Backand
{
"name": "users",
"fields": {
"media": {
"collection": "media",
"via": "user"
},
"email": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
} }
Media object model as set up in Backand
{
"name": "media",
"fields": {
"description": {
"type": "string"
},
"thumbnail": {
"type": "string"
},
"fullImage": {
"type": "string"
},
"user": {
"object": "users"
}
}}
Final JSON response that I am looking for:
{
description: 'Blah',
thumbnail: 'someImageUrl.jpg',
fullImage: 'someImageUrl.jpg',
user: {
firstName: 'John'
lastName: 'Smith'
email: 'john#smith.com'
}
}
Just in case anybody else comes across this, I chose to do it with server-side javascript code, since my backend, SQL and NoSQL query skills are very weak. I'm guessing a noSQL query would probably be better in terms of performance. And I would still like to see how it could be done in noSQL. Anyway my server-side javascript code in a Backand action does the job. Here it is:
/* globals
$http - Service for AJAX calls
CONSTS - CONSTS.apiUrl for Backands API URL
Config - Global Configuration
socket - Send realtime database communication
files - file handler, performs upload and delete of files
request - the current http request
*/
'use strict';
function backandCallback(userInput, dbRow, parameters, userProfile) {
var response = [];
var request =
$http({
method: "GET",
url: CONSTS.apiUrl + "/1/objects/media",
headers: {"Authorization": userProfile.token},
params: {
exclude: 'metadata',
deep: true
}
});
var object = request.data;
var related = request.relatedObjects.users;
for (media in object) {
if (object.hasOwnProperty(media)) {
for (user in related) {
if (object[media].user == related[user].id) {
response.push({
id: object[media].id,
thumbnailUrl: object[media].thumbnail,
description: object[media].description,
fullName: related[user].firstName + ' ' + related[user].lastName,
email: related[user].email
});
}
}
}
}
return response;
}

How to set up Firebase location as a Backbone Model ID?

I use BackFire - Firebase integration with Backbone.js
Examining BackFire demo application I see that Firebase location is set as "root" key and as a value for ID key.
JSON response from Firebase
{
"-It-MYaWEFBI1QGD8PXB":
{
"title":dummy title",
"id": "-It-MYaWEFBI1QGD8PXB"
}
}
In Model I see that Firebase location is set as Model ID.
Backbone Model attributes
attributes: {
id: '-IswDV_2rwZu3WHO0giQ'
title: 'dummy title'
},
id : '-IswDV_2rwZu3WHO0giQ'
I wonder how does data structure look like in Firebase Forge (backend) which creates response described above?
By going to the TODO page and typing in this command:
new Firebase('https://backbone.firebaseio.com').once('value', function(ss) {
console.log(JSON.stringify(ss.val(), null, 2))});
I am able to see the data structure looks like this:
{
"-It-MYaWEFBI1QGD8PXB": {
"id": "-It-MYaWEFBI1QGD8PXB",
"title": "prepare lunch"
},
"-It-Mch8_bA23vkwAbUZ": {
"id": "-It-Mch8_bA23vkwAbUZ",
"title": "install Linux"
}
}
So basically, because it's a demo, Anant has obeyed KISS and put the todo items at the root level. In a sophisticated application, you'd need to have more nesting and depth, of course.

Resources