Mongodb Nodejs Regex Search - node.js

Hey I am making a website which as a partial search form. Reference from:https://www.youtube.com/watch?v=ZC2aRON3fWw&t=42s But I couldnt understand why it doesnt work. I use pug instead of hbs.
And these are my codes:
app.get('/sonuc', function(req, res, next){
var q = req.query.q;
Article.find({
title : {
$regex: new RegExp(q)
}
}, {
_id:0,
__v:0
}, function(err, data){
res.render('sonuc', {data:data})
}).limit(10);
});
});
Then this is my layout pug:
.ui-widget
form.form-inline.my-2.my-lg-0
input.form-control.mr-sm-2(type='text', onkeyup='showResults(this.value)', placeholder='Search',action='/article/'+sorgu, aria-label='Search')
button.btn.btn-secondary.my-2.my-sm-0(type='submit')
#search-results(style='width:60px;top:-1px;position:relative')
In this layout pug I thing the onkeyup issue is not working. How can I implement that fu nction on that form?
And ths is my main.js whihc takes query from database and write it in html form:
var showResults = debounce(function(arg){
var value = arg.trim();
if(value == "" || value.length <= o){
$("#search-results").fadOut();
return;
}else{
$("#search-results").fadeIn();
};
var jqhr = $.get('/article/' + value, function(data){
})
.done(function(data){
if(data.length == 0){
$("search-resuts").append('<p classs="lead text-center mt-2">No Results</p>');
}else{
data.forEach(x => {
$("search-resuts").append('<p class="m-2 lead"><img style="width:60px;" src="images/supreme1.jpg">' + x.title +'</p>');
});
}
})
.fail(function(err){
console.log(err);
})
}); 200;
function debounce(func, wait, immediate){
var timeout;
return function(){
var context = this;
args = arguments;
var later = function(){
timeout= null;
if(!immediate)func.apply(context,args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if(callNow)func.apply(context,args);
};
};
I cannot understand these issues and why it doesnt work.As a summary, I want to make a search engine which works with regex and mongodb. İt will be partial that is shown in that youtoube video which is on the above of my article that I referenced. But the real issue is, I couldnt understand the code block of function showResults and I dont know how to translate this codes to my project. So that I am waiting your help. I cannot upload a video to this website so that if you can give me your facebook, instagram or email account I can send the issue which should be solved. I need your help. I have been making this project for a long time for my school but I cannot move on. Please I need your help.
I hope I could express myself well and your helps will solve it.

Yes I got it know. I have made lots of changes instead of using this code blockes.
I wathced : https://www.youtube.com/watch?v=9_lKMTXVk64
And also I documented : Fuzzy Searching with Mongodb?
These two documentations help me a lot. So that my code is almost approximately 90% same as shown in these documentations.
This is my app.js :
app.get('/sonuc', function(req, res){
if (req.query.search) {
const regex = new RegExp(escapeRegex(req.query.search), 'gi');
Article.find({ "title": regex }, function(err, articles) {
if(err) {
console.log(err);
} else {
res.render("index", {
articles: articles
});
}
});
}
});
This is my index.pug file :
extends layout
block content
body
br
br
br
ul.list-group
each article, i in articles
li.list-group-item
a(href="/article/" + article._id)= article.title
And this is my layout.pug file(But I will consider the text form)
form.form-inline.my-2.my-lg-0(action="/sonuc" method="GET")
input.form-control.mr-sm-2(type='text', name,="search", onkeyup='showResults(this.value)', placeholder='Search',action='/article/'+sorgu, aria-label='Search')
button.btn.btn-secondary.my-2.my-sm-0(type='submit')
Please look at:
https://github.com/nax3t/fuzzy-search
Because in my solution I didnt add the message that is applied when we cannot find the word. Or when there is no such a word which is searched by user. The noMatch query.
But after I will apply it to my project I will add it to here.

Related

IBM Watson Speech to Text API with node. How to output to DOM?

I am using a npm module to work with IBM's Watson to do speech to text. I'm using this package here: https://github.com/watson-developer-cloud/speech-javascript-sdk.
I can authenticate fine, but other than that nothing happens. I want to take the text from the response and insert it in the DOM. I tried the following just to try it out and I'm not getting any kind of feedback.
WatsonSpeech.SpeechToText.recognizeMicrophone({token: token, keepmic: true, ouputElement: "body"}).promise().then(function() {
console.log("talking");
})
The docs say the following for this method:
Other options passed to WritableElementStream if options.outputElement
is set.
And
Pipes results through a FormatStream by default, set options.format=false > to disable.
I would think that the
WatsonSpeech.SpeechToText.recognizeMicrophone
would take a callback function so I can handle the response and put insert it in my DOM, but I can't figure that out. Also, I'm not really a JS guy, so I don't know what the promise does.
Chapter 3 of "Zero to Cognitive" has exactly this code applied.
https://github.com/rddill-IBM/ZeroToCognitive
I recommend you to take a look at his lessons on youtube, but here is the code that I found.
function initPage ()
{
var _mic = $('#microphone'); var _stop = $("#stop");
_mic.addClass("mic_enabled");
_stop.addClass("mic_disabled");
_mic.on("click", function ()
{
var _className = this.className;
if(this.className == "mic_enabled")
{
_mic.addClass("mic_disabled");
_mic.removeClass("mic_enabled");
_stop.addClass("mic_enabled");
_stop.removeClass("mic_disabled");
$.when($.get('/api/speech-to-text/token')).done(
function (token) {
stream = WatsonSpeech.SpeechToText.recognizeMicrophone({
token: token,
outputElement: '#speech' // CSS selector or DOM Element
});
stream.on('error', function(err) { console.log(err); });
});
}
});
_stop.on("click", function() {
console.log("Stopping text-to-speech service...");
if (stream != undefined) {stream.stop(); }
_mic.addClass("mic_enabled");
_mic.removeClass("mic_disabled");
_stop.addClass("mic_disabled");
_stop.removeClass("mic_enabled");
});
}

Variable query parameter for MongoDB + NodeJS + ExpressJS

What I'm trying to do is pass a parameter into a get() method (I am currently using Express as my middleware). Right now, I'm able to pick up which color is passed, and see if it's undefined or not. However, it seems like the query doesn't seem to work.
The color is defined by an option that the user selects (in this case, a link). It seems to pass the parameter fine - it's just the query that I can't seem to figure out.
Here's the code that I'm using right now:
router.get('/cards/cardlist', function(req, res) {
console.log('get /cardlist');
var reqColor = req.query.color;
var query = {};
query[reqColor] = 1;
var db = req.db;
if(reqColor !== undefined) {
db.collection('creaturecards').find(query).toArray(function (err, items) {
res.json(items);
});
} else {
db.collection('creaturecards').find().toArray(function (err, items) {
res.json(items);
});
}
});
basically, if I manually add in (for example) red : 1, then it's able to pick up red just fine.
For reference, not every record will have a color saved to it. If the color isn't set, then I just don't have it in the record - that seemed to be the best setup for this particular project.
Thanks in advance!

Get a collection and add a value to the response

I want to create in the Server script a function that can return a collection plus some extra value.
For example:
Meteor.publish("users", function () {
var users;
users = Meteor.users.find();
users.forEach(function (user){
user.profile.image = "some-url";
});
return users;
});
But this don't work proper. My question is: What is the right way to add a value to a collection reponse in a publish function.
There are 2 ways you can implement a publish function:
By returning a cursor (or an array of cursors)
By using this.added(), this.changed() and this.removed().
Only method 2 allows to modify returned documents.
Please refer to Meteor documentation here. However, since the provided sample code might look complex, here is another one:
// server: publish the rooms collection
Meteor.publish("rooms", function () {
return Rooms.find({});
});
is equivalent to:
// server: publish the rooms collection
Meteor.publish("rooms", function () {
var self = this;
var handle = Rooms.find({}).observeChanges({
added: function(id, fields) { self.added("rooms", id, fields); },
changed: function(id, fields) { self.changed("rooms", id, fields); },
removed: function(id) { self.added("rooms", id); },
}
});
self.ready();
self.onStop(function () { handle.stop(); });
});
In the second sample, you can modify the 'field' parameter before sending it for publication, like this:
added: function(id, fields) {
fields.newField = 12;
self.added("rooms", id, fields);
},
Source: this post.
Is this important to do with the server? You could use the transform function on the client:
Client JS
//Somewhere where it can run before anything else (make sure you have access to the other bits of the document i.e services.facebook.id otherwise you'll get a services is undefined
Meteor.users._transform = function(doc) {
doc.profile.image = "http://graph.facebook.com/" + doc.services.facebook.id + "/picture";
return doc;
}
Now when you do:
Meteor.user().profile.image
=> "http://graph.facebook.com/55592/picture"
I have opened an issue before with regards to sharing a transform onto the client: https://github.com/meteor/meteor/issues/821

Filtering Markdown content in Jade / Node.js

Esteemed Hackers,
I wish to filter a string full of Markdown within a Jade template.
I have the Markdown in a variable.
Jade interpolates a variable inside the Markdown just fine:
This:
var jade = require('jade');
var jade_string = [
':markdown',
' ## This is markdown!',
' * First',
' * #{var2}',
' * Third'
].join('\n');
var fn = jade.compile( jade_string, { pretty: true } );
console.log( fn( { var1: "First!", var2: "Second!" } ) );
Begets this:
<h2>This is markdown!</h2>
<ul>
<li>First</li>
<li>Second!</li>
<li>Third</li>
</ul>
However, what I have is actual complete Markdown inside the variable.
And this:
var jade = require('jade');
var jade_string = [
'div.markedup',
' :markdown',
' \\#{var2}'
].join('\n');
var fn = jade.compile( jade_string, { pretty: true } );
var markdown = [
'## I am marked!',
'* One',
'* Two'
].join('\n');
console.log( fn( { var1: "First!", var2: markdown } ) );
Delivers only this:
<div class="markedup"><p>## I am marked!
* One
* Two</p>
</div>
Thus it looks to me like Jade filters the block before doing any variable
interpolation, then interpolates variables in the resulting HTML. This is
fine if you wish to write your templates in Markdown, but it's not much
help if you want to write your content in Markdown.
I know I can solve this problem with more programming but I feel like I
must be missing something. After all, holding snippets of Markdown content in
a database and stuffing the resulting HTML fragments into templates seems like
the most obvious use-case for a :markdown filter.
Is there a "normal" way to do this in Jade?
Many thanks in advance for the pending enlightenment.
I think the answer is more programming, but I'll show you what I do. I use custom middleware that let's me combine arbitrary transformative processes before I get to my final HTML document output. So, for example, I have the following filters in my middleware.js module, which I will explain in turn.
So simple views just use normal jade with its various filters for markdown, javascript, coffeescript. Some views, for example a blog post, require a more sophisticated middleware chain, which goes like this.
First, based on the request, I establish the file that holds the core content for this response, and set that as a property on res.viewPath. This could be a raw HTML fragment file or a markdown file. Then I send the response through a series of middleware transformations. I use res.html and res.dom to store intermediate representations of the response as it is being built up.
This one just stores raw HTML (just a document body fragment with no head or layout).
html = function(req, res, next) {
if (!/\.html$/.test(res.viewPath)) return next();
return fs.readFile(res.viewPath, "utf8", function(error, htmlText) {
res.html = htmlText;
return next(error);
});
};
This one will convert a markdown file to HTML (using the markdown-js module).
markdownToHTML = function(req, res, next) {
if (!/\.md$/.test(res.viewPath)) return next();
return fs.readFile(res.viewPath, "utf8", function(error, markdownText) {
res.html = markdown(markdownText);
return next(error);
});
};
I have a sub-layout that goes within my master layout but around each blog post. So I wrap the blog post in the sublayout here. (Separate code not shown generates the res.post object from a json metadata file).
blogArticle = function(req, res, next) {
var footerPath, post;
post = res.post;
footerPath = path.join(__dirname, "..", "templates", "blog_layout.jade");
return fs.readFile(footerPath, "utf8", function(error, jadeText) {
var footerFunc;
if (error) return next(error);
footerFunc = jade.compile(jadeText);
res.html = footerFunc({
post: post,
body: res.html
});
return next();
});
};
Now I wrap my layout around the main content HTML. Note that I can set things like the page title here, or wait until later since I can manipulate the response via jsdom after this. I do body: res.html || "" so I can render an empty layout and insert the body later if that is more convenient.
exports.layout = function(req, res, next) {
var layoutPath;
layoutPath = path.join(__dirname, "..", "templates", "layout.jade");
return fs.readFile(layoutPath, "utf8", function(error, jadeText) {
var layoutFunc, locals;
layoutFunc = jade.compile(jadeText, {
filename: layoutPath
});
locals = {
config: config,
title: "",
body: res.html || ""
};
res.html = layoutFunc(locals);
return next(error);
});
};
Here's where the really powerful stuff comes. I convert the HTML string into a jsdom document object model which allows for jQuery based transformations on the server side. The toMarkup function below just allows me to get the HTML back without the extra <script> tag for our in-memory jquery which jsdom has added.
exports.domify = function(req, res, next) {
return jsdom.env(res.html, [jqueryPath], function(error, dom) {
if (error) return next(error);
res.dom = dom;
dom.toMarkup = function() {
this.window.$("script").last().remove();
return this.window.document.doctype + this.window.document.innerHTML;
};
return next(error);
});
};
So here's a custom transformation I do. This can replace a made-up DSL tag like <flickrshow href="http://flickr.com/example"/> with real valid HTML, which otherwise would be a big nasty <object> boilerplate I would have to duplicate in each blog post, and if flickr ever changed the boilerplate markup they use, it would be a maintenance pain to go fix it in many individual blog post markdown files. The boilerplate they currently use is in the flickrshowTemplate variable and contains a little mustache placeholders {URLs}.
exports.flickr = function(req, res, next) {
var $ = res.dom.window.$;
$("flickrshow").each(function(index, elem) {
var $elem, URLs;
$elem = $(elem);
URLs = $elem.attr("href");
return $elem.replaceWith(flickrshowTemplate.replace(/\{URLs\}/g, URLs));
});
return next();
};
Ditto for embedding a youtube video. <youtube href="http://youtube.com/example"/>.
exports.youtube = function(req, res, next) {
var $ = res.dom.window.$;
$("youtube").each(function(index, elem) {
var $elem, URL;
$elem = $(elem);
URL = $elem.attr("href");
return $elem.replaceWith(youtubeTemplate.replace(/\{URL\}/, URL));
});
return next();
};
Now I can change the title if I want, add/remove javascripts or stylesheets, etc. Here I set the title after the layout has already been rendered.
postTitle = function(req, res, next) {
var $;
$ = res.dom.window.$;
$("title").text(res.post.title + " | Peter Lyons");
return next();
};
OK, time to go back to final HTML.
exports.undomify = function(req, res, next) {
res.html = res.dom.toMarkup();
return next();
};
Now we ship it off!
exports.send = function(req, res) {
return res.send(res.html);
};
To tie it all together in order and have express use it, we do
postMiddleware = [
loadPost,
html,
markdownToHTML,
blogArticle,
layout,
domify,
postTitle,
flickr,
youtube,
undomify,
send
]
app.get("/your/uri", postMiddleware);
Concise? No. Clean? I think so. Flexible? Extremely. Blazingly fast? Probably not wicked fast as I believe jsdom is one of the more heavyweight things you can do, but I'm using this as a static site generator so speed is irrelevant. Of course, it would be trivial to add another function to the begining and end of the middleware chain to write the final HTML to a static file and serve it directly if it is newer than the corresponding markdown page body content file. Stackoverflowers, I'd love to hear thoughts and suggestions on this approach!
Jade people say passing variables to filters is not supported. I couldn’t get through Peter Lyons’s answer, so I used this:
marked = require 'marked'
marked.setOptions
<my options>
app.locals.md = marked
Then in Jade:
!= md(<markdown string>)
Quick and dirty. Probably suboptimal because it runs conversion each time without caching the result (I think), but at least it works.
(Edit)
You could also use marked to render markdown in the browser, offloading some work from your server and making loading faster.

Passing an array to a JSON object for Jade rendering

I have a node.js server written in express and at a certain moment I send to some .jade page an array. The problem is that when rendering the Jade page, the Jade compiler renders the array as [object Object] and the JavaScript compiler on Chrome complains about it saying "Unexpected identifier".
This is the Jade code:
!!! 5
html(lang="en")
head
title= "Rankings"
body
h1 Ranking
div(id="rankings")
script(type='text/javascript')
function fillRanking(){
var rankArray = #{ranking};
alert("inside fillranking");
var divElement = document.getElementById("rankings");
for(var i = 0; i< rankArray.length; i++){
divElements.innerHTML += "" + i+1 + ". " + rankArray[i].Username + " " + rankArray[i].Points;
}
}
fillRanking();
As you can see it's really simple, I just fill a div with the info given by what's inside the #{ranking} variable passed by node.js to Jade. The alert on the second line doesn't fire because the Unexpected Identifier error happens as soon as I try to assign the #{ranking} variable.
The following is the code in my node.js with express
app.get('/ranking', function (req, res) {
//get the first ten people in the ranking
var firstTen = getRanking(10, function(results){
//compute the array of results
var result = {
ranking: [],
}
for(var i = 0; i < results.length; i++){
result.ranking[i] = results[i];
}
//render the ranking with all the info
console.log(result);
res.render(__dirname + '/pages/ranking/ranking.jade', {
ranking: result,
});
});
});
I create an object with inside an array of results, I put the results I found out of the query inside it and I pass it to the rendering engine. The console.log(results) call prints the result object correctly, for example like this:
{ ranking:
[ { Username: 'usr1',
_id: 4ed27319c1d767f70e000002,
Points: 100 },
{ Username: 'usr2',
_id: 4ed27326c1d767f70e000003,
Points: 100 } ]
}
I don't really know how to handle the variable passed to the Jade page. Whichever thing I do I keep getting the "Unexpected identifier" error.
Does anyone of you know how do I solve this?
Thanks
Looking at the comments above and investigating a little bit more, here's what I've found to work:
Use this on your javascript (/controller):
...
res.render(__dirname + '/pages/ranking/ranking.jade', {
ranking: JSON.stringify(ranking),
})
...
And on jade template:
...
function fillRanking(){
var rankArray = !{ranking};
alert("inside fillranking");
...
This works because !{} doesn't perform escaping.
This works for me.
JSON.stringify the array (or any arbitrary JSON object really) on the server and then JSON.parse it on the client.
server-side:
res.render(__dirname + '/pages/ranking/ranking.jade', {
ranking: JSON.stringify(result),
});
client-side:
var rankArray = JSON.parse( !{JSON.stringify(ranking)} );
Because I also use the array from the controller for iteration, I did this:
res.render('template', pArrayOfData);
And in the jade code:
script(type='text/javascript').
var _histData = !{JSON.stringify(pArrayOfData)};
I encountered the same problem and was able make it work with a slight variation (thanks to the solutions above).
From my code:
Backend:
Stringify the JSON array
router.get('/', function(req, res) {
var data = JSON.stringify(apiData); // <====
res.render('gallery', { data: apiData });
});
Frontend:
Stringify again with the !{}
function injectDataOnView() {
var data = !{JSON.stringify(data)}; // <====
var divElement = document.getElementById('data');
divElement.innerHTML = data[1]['id']; // <=== just a test, so no for loop
}
injectDataOnView();

Resources