Instantly share code, notes, and snippets.

Embed
What would you like to do?
hn job query search
function query() {
var
// HN is done with very unsemantic classes.
job_list = Array.prototype.slice.call(document.querySelectorAll('.c5a,.cae,.c00,.c9c,.cdd,.c73,.c88')),
query_list = Array.prototype.slice.call(arguments),
shown = 0, total = job_list.length;
// Traverses up the dom stack trying to find a match of a specific class
function up_to(node, klass) {
if (node.classList.contains(klass)) {
return node;
}
if(node === document.body) {
throw new Exception();
}
return up_to(node.parentNode, klass);
}
function display(node, what) {
up_to(node, 'athing').style.display = what;
}
// If we have a RegEx, return it
// Otherwise make it a case insensitive regex.
function destring(what) {
return what.test ? what : new RegExp(what.toString(), 'i');
}
// Hide all the postings
job_list.forEach(function(node) {
display(node, 'none');
});
query_list.forEach(function(query) {
if (query.forEach) {
var and_query_list = query.map(destring);
job_list.forEach(function(node) {
var
doesMatch = true,
toTest = node.innerHTML;
and_query_list.forEach(function(query) {
doesMatch &= toTest.search(query) > -1;
})
if(doesMatch) {
display(node, 'block');
shown ++;
}
});
} else {
query = destring(query);
job_list.forEach(function(node) {
if(node.innerHTML.search(query) > -1) {
display(node, 'block');
shown ++;
}
});
}
});
return {shown: shown, total: total}
}
@nicnilov

This comment has been minimized.

Show comment
Hide comment
@nicnilov

nicnilov Aug 7, 2015

Nice 👍

nicnilov commented Aug 7, 2015

Nice 👍

@notjordan

This comment has been minimized.

Show comment
Hide comment
@notjordan

notjordan Aug 15, 2015

You are the man

notjordan commented Aug 15, 2015

You are the man

@interludic

This comment has been minimized.

Show comment
Hide comment
@interludic

interludic Sep 1, 2015

Entry point? how does this know which url to search?

interludic commented Sep 1, 2015

Entry point? how does this know which url to search?

@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Sep 1, 2015

On the hacker news comment thread site you need to run this script through your browser's debug console.

Go to the comment thread. Now Open up the "web console" for your browser ... usually by pressing "F12" or right clicking and going to "inspect element". Make sure you are on the "console" tab with the input field at the bottom. Copy and paste the script into there, press enter.

Then type in your query, like "query('javascript')" and press enter.

A number of posts should hide themselves, those that don't contain, in this case, the word 'javascript'.

Owner

kristopolous commented Sep 1, 2015

On the hacker news comment thread site you need to run this script through your browser's debug console.

Go to the comment thread. Now Open up the "web console" for your browser ... usually by pressing "F12" or right clicking and going to "inspect element". Make sure you are on the "console" tab with the input field at the bottom. Copy and paste the script into there, press enter.

Then type in your query, like "query('javascript')" and press enter.

A number of posts should hide themselves, those that don't contain, in this case, the word 'javascript'.

@interludic

This comment has been minimized.

Show comment
Hide comment
@interludic

interludic Sep 2, 2015

Sweet thanks!

interludic commented Sep 2, 2015

Sweet thanks!

@meiamsome

This comment has been minimized.

Show comment
Hide comment
@meiamsome

meiamsome Sep 3, 2015

Version With More Powerful Syntax

(title added by original author - not a self-promotion by meiamsome)

Hello!
I took your code and made some improvements (at least in my eyes):

I added some more functions, that make it so you can do more complex querys.
For example, you can do the following:
query(and('remote', or('python', 'ruby')))
to execute the query you used to do via
query(['remote', 'python'], ['remote', 'ruby'])

I also added xor and nand functionality, as well as logical inversion with not.
The functions can all be passed into each other, so if you want you can do something like:
query(and(not(or('python', 'php')), 'scala'))
If you want to find jobs that have scala but don't feature python or php.
Of course, it is more versatile than that as it can (at least, theoretically) allow any depth of search.

And, as an added bonus, Arrays still work like they used to and the query function defaults to or functionality, meaning that if you type a query in the old format it still functions in the same way.

You can find them at my gist here:
https://gist.github.com/meiamsome/94c1aa5f0b431a21b5aa

(If you want to copy that gist into this one that'd be great, I would do a pull request but they aren't yet available on gists, sadly)

meiamsome commented Sep 3, 2015

Version With More Powerful Syntax

(title added by original author - not a self-promotion by meiamsome)

Hello!
I took your code and made some improvements (at least in my eyes):

I added some more functions, that make it so you can do more complex querys.
For example, you can do the following:
query(and('remote', or('python', 'ruby')))
to execute the query you used to do via
query(['remote', 'python'], ['remote', 'ruby'])

I also added xor and nand functionality, as well as logical inversion with not.
The functions can all be passed into each other, so if you want you can do something like:
query(and(not(or('python', 'php')), 'scala'))
If you want to find jobs that have scala but don't feature python or php.
Of course, it is more versatile than that as it can (at least, theoretically) allow any depth of search.

And, as an added bonus, Arrays still work like they used to and the query function defaults to or functionality, meaning that if you type a query in the old format it still functions in the same way.

You can find them at my gist here:
https://gist.github.com/meiamsome/94c1aa5f0b431a21b5aa

(If you want to copy that gist into this one that'd be great, I would do a pull request but they aren't yet available on gists, sadly)

@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Sep 3, 2015

hey thanks - simply beautiful!

Think about using some markdown and give your comment an attractive title like "Updated version" or "Version with more powerful syntax"!

oh hey wait looks like I can do that

As far as replacing it with your implementation, I like the simplicity of my implementation for these purposes. If anything, I'd advocate for a far simpler, more basic and powerful system, not a more complex one. You've simplified some of it ... so a merging would be appropriate

Owner

kristopolous commented Sep 3, 2015

hey thanks - simply beautiful!

Think about using some markdown and give your comment an attractive title like "Updated version" or "Version with more powerful syntax"!

oh hey wait looks like I can do that

As far as replacing it with your implementation, I like the simplicity of my implementation for these purposes. If anything, I'd advocate for a far simpler, more basic and powerful system, not a more complex one. You've simplified some of it ... so a merging would be appropriate

@VarunAgw

This comment has been minimized.

Show comment
Hide comment
@VarunAgw

VarunAgw Feb 1, 2016

I feel filter to match exact word will be nice. e.g. Searching for "intern" also match "international" which I don't want

VarunAgw commented Feb 1, 2016

I feel filter to match exact word will be nice. e.g. Searching for "intern" also match "international" which I don't want

@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Mar 1, 2016

@VarunAgw I agree. There's two things: Case insensitivity is great except when it isn't. Perhaps I want "CA" and not "ca" and second is your thing. Probably the best way to do this would be to support RegExp along with string matching. So you could do query(/intern[^\w]/) since
/intern[^\w]/.test('international') => false and /intern[^\w]/.test('intern.') => true ...

edit: ok, done. Classic string matching is of course, still supported. easy things easy, hard things possible.

So a los angeles search now could look like

query(/LA[^\w]/, 'los angeles')

If I could get the matches stylized (say, in bold) in under 2 lines of complexity I'll do it. Otherwise I'm drifting away from KISS. Let me think...

edit on that: it's not styling it that's the challenge, it's unstyling it for a subsequent search. That sounds like a kind of cleverness that comes with bugs.

Owner

kristopolous commented Mar 1, 2016

@VarunAgw I agree. There's two things: Case insensitivity is great except when it isn't. Perhaps I want "CA" and not "ca" and second is your thing. Probably the best way to do this would be to support RegExp along with string matching. So you could do query(/intern[^\w]/) since
/intern[^\w]/.test('international') => false and /intern[^\w]/.test('intern.') => true ...

edit: ok, done. Classic string matching is of course, still supported. easy things easy, hard things possible.

So a los angeles search now could look like

query(/LA[^\w]/, 'los angeles')

If I could get the matches stylized (say, in bold) in under 2 lines of complexity I'll do it. Otherwise I'm drifting away from KISS. Let me think...

edit on that: it's not styling it that's the challenge, it's unstyling it for a subsequent search. That sounds like a kind of cleverness that comes with bugs.

@jeff303

This comment has been minimized.

Show comment
Hide comment
@jeff303

jeff303 May 26, 2016

Just a simple tweak, but calling with zero args could reset the view (i.e. remove filters). Add this just before query_list.forEach...

  if (query_list.length === 0) {
    // no query passed; show everything
    job_list.forEach(function(node) {
      display(node, 'block');
    });
    return {shown: total, total: total};
  }

jeff303 commented May 26, 2016

Just a simple tweak, but calling with zero args could reset the view (i.e. remove filters). Add this just before query_list.forEach...

  if (query_list.length === 0) {
    // no query passed; show everything
    job_list.forEach(function(node) {
      display(node, 'block');
    });
    return {shown: total, total: total};
  }
@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Jun 1, 2016

oh hey thanks ... I'll fix that soon!

Owner

kristopolous commented Jun 1, 2016

oh hey thanks ... I'll fix that soon!

@himanshugarg

This comment has been minimized.

Show comment
Hide comment
@himanshugarg

himanshugarg Jul 2, 2016

I am seeing this with Jul 2016 - Who is hiring on FF and Chrome. Never happened earlier:-
query(['python', 'remote', 'open', 'source'])
VM51:14 Uncaught ReferenceError: Exception is not defined(…)

himanshugarg commented Jul 2, 2016

I am seeing this with Jul 2016 - Who is hiring on FF and Chrome. Never happened earlier:-
query(['python', 'remote', 'open', 'source'])
VM51:14 Uncaught ReferenceError: Exception is not defined(…)

@SamHasler

This comment has been minimized.

Show comment
Hide comment
@SamHasler

SamHasler Jul 5, 2016

the class of TRs is now "athing comtr" so if (node.className === klass) { won't work any more.

This works in chrome:

if (node.classList.contains(klass)) {

SamHasler commented Jul 5, 2016

the class of TRs is now "athing comtr" so if (node.className === klass) { won't work any more.

This works in chrome:

if (node.classList.contains(klass)) {

@sadokx

This comment has been minimized.

Show comment
Hide comment
@sadokx

sadokx Jul 14, 2016

SamHasler's answer works now. Can you update kristopolus? Thanks! :)

sadokx commented Jul 14, 2016

SamHasler's answer works now. Can you update kristopolus? Thanks! :)

@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Aug 1, 2016

OK just saw this I'm on mobile. I'll get to it today

Owner

kristopolous commented Aug 1, 2016

OK just saw this I'm on mobile. I'll get to it today

@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Aug 1, 2016

there you go. It's insanity that there's a specialized function for string tokenization and matching that is specific to the contents of the classname attribute on dom nodes. of course you could just use regular string functions ... great, so why the hell does this thing exist? there's not a special function for strings that contain the letter 'q' or are exactly 11 characters long ... bah, nonsense.

anyway, it's fixed.

Owner

kristopolous commented Aug 1, 2016

there you go. It's insanity that there's a specialized function for string tokenization and matching that is specific to the contents of the classname attribute on dom nodes. of course you could just use regular string functions ... great, so why the hell does this thing exist? there's not a special function for strings that contain the letter 'q' or are exactly 11 characters long ... bah, nonsense.

anyway, it's fixed.

@JonathanMatthey

This comment has been minimized.

Show comment
Hide comment
@JonathanMatthey

JonathanMatthey Aug 2, 2016

@kristopolous thanks for fixing this... use this script every few months... great work :)

JonathanMatthey commented Aug 2, 2016

@kristopolous thanks for fixing this... use this script every few months... great work :)

@jeff303

This comment has been minimized.

Show comment
Hide comment
@jeff303

jeff303 Aug 3, 2016

It seems like it might be nice to have an option to only search the first line for certain terms (ex: REMOTE), since usually occurrences in other lines or subsequent paragraphs aren't actually what you're after. Does anyone else agree? If so, I could try to take a crack at that.

jeff303 commented Aug 3, 2016

It seems like it might be nice to have an option to only search the first line for certain terms (ex: REMOTE), since usually occurrences in other lines or subsequent paragraphs aren't actually what you're after. Does anyone else agree? If so, I could try to take a crack at that.

@kristopolous

This comment has been minimized.

Show comment
Hide comment
@kristopolous

kristopolous Aug 16, 2016

@jeff303 "line" is an amorphous term. I'm not trying to be a jerk, but the computer is and always is - that jerk. Anyway, without a more solid definition (as "line" depends on font size, monitor size etc), I've got no hope here and I want to make it useful for you.

I could do something like "Of the first 50 space-separated tokens (aka, words)" or "the first paragraph that contains more than 40 characters" or something.

The nuances here is that there's essentially the following formats:

1:

PLACE | JOB | TERMS

DESCRIPTION  

2:

PLACE
JOB
TERMS

DESCRIPTION  

3:

PLACE, JOB, TERMS, DESCRIPTION ...

etc.

So there has to be a flexible "do what I mean, not what I say" style definition that can accommodate these various degrees of freedom. It's far too easy to have a solution that is very complex, fragile, and useless (which I know is very trendy these days, but I'm not a trendy guy like that).

What would be most useful to you?

Owner

kristopolous commented Aug 16, 2016

@jeff303 "line" is an amorphous term. I'm not trying to be a jerk, but the computer is and always is - that jerk. Anyway, without a more solid definition (as "line" depends on font size, monitor size etc), I've got no hope here and I want to make it useful for you.

I could do something like "Of the first 50 space-separated tokens (aka, words)" or "the first paragraph that contains more than 40 characters" or something.

The nuances here is that there's essentially the following formats:

1:

PLACE | JOB | TERMS

DESCRIPTION  

2:

PLACE
JOB
TERMS

DESCRIPTION  

3:

PLACE, JOB, TERMS, DESCRIPTION ...

etc.

So there has to be a flexible "do what I mean, not what I say" style definition that can accommodate these various degrees of freedom. It's far too easy to have a solution that is very complex, fragile, and useless (which I know is very trendy these days, but I'm not a trendy guy like that).

What would be most useful to you?

@ThomasRooney

This comment has been minimized.

Show comment
Hide comment
@ThomasRooney

ThomasRooney Sep 6, 2016

A slight divergence, but I saw this and was inspired to squeeze it into a single (ish) console command. Relies on curl, jq, pup. Further output can then be pretty easily filtered by jq or just grep'd. Outputs a big array of the top level comments of the most recent Who's hiring thread.

curl https://news.ycombinator.com/submitted\?id\=whoishiring |
  pup 'td.title a json{}' | jq '.[] | select(.text | contains("Who is hiring")) | .href' |
  head -n 1 |
  xargs -I{} curl https://news.ycombinator.com/\{\} |
  pup '.comtr json{}' |
  jq '.[] | select(.. | select(select(.class?=="ind") | .children[0].width == "0")) |
            select(.class="comment") |
            map(..| if .tag? != "font" then .text? else null end) | map(select(. != null)) |
            del(.[length-1])'

EDIT: To filter, pipe the output somewhere then do something like this cat results | jq 'select(.[2]? | contains("London"))'

ThomasRooney commented Sep 6, 2016

A slight divergence, but I saw this and was inspired to squeeze it into a single (ish) console command. Relies on curl, jq, pup. Further output can then be pretty easily filtered by jq or just grep'd. Outputs a big array of the top level comments of the most recent Who's hiring thread.

curl https://news.ycombinator.com/submitted\?id\=whoishiring |
  pup 'td.title a json{}' | jq '.[] | select(.text | contains("Who is hiring")) | .href' |
  head -n 1 |
  xargs -I{} curl https://news.ycombinator.com/\{\} |
  pup '.comtr json{}' |
  jq '.[] | select(.. | select(select(.class?=="ind") | .children[0].width == "0")) |
            select(.class="comment") |
            map(..| if .tag? != "font" then .text? else null end) | map(select(. != null)) |
            del(.[length-1])'

EDIT: To filter, pipe the output somewhere then do something like this cat results | jq 'select(.[2]? | contains("London"))'

@jeff303

This comment has been minimized.

Show comment
Hide comment
@jeff303

jeff303 Sep 7, 2016

@kristopolous, I'm absolutely aware of the complexity in what seems like a simple thing on the surface. It was more of just a brainstorming idea (mostly driven by my own desire to brush up on Javascript), and not an urgent request for that particular functionality.

Attempting to parse the different sections based on commonly seen formats is pretty interesting, but would be fragile as you point out. I think, at a minimum, attempting to look in only the first "paragraph" (the breaks between which actually do correspond to new <p> elements) might be a start. And also, only looking at top-level posts would be useful, to filter out "Would you consider remote candidates?" type replies, where that word didn't occur in the parent posting.

jeff303 commented Sep 7, 2016

@kristopolous, I'm absolutely aware of the complexity in what seems like a simple thing on the surface. It was more of just a brainstorming idea (mostly driven by my own desire to brush up on Javascript), and not an urgent request for that particular functionality.

Attempting to parse the different sections based on commonly seen formats is pretty interesting, but would be fragile as you point out. I think, at a minimum, attempting to look in only the first "paragraph" (the breaks between which actually do correspond to new <p> elements) might be a start. And also, only looking at top-level posts would be useful, to filter out "Would you consider remote candidates?" type replies, where that word didn't occur in the parent posting.

@gotoc

This comment has been minimized.

Show comment
Hide comment
@gotoc

gotoc Jun 2, 2017

place, job, terms, description... here is a crazy idea, why not have a form field, with required fields for remote/onsite.
ie, it would have to be enforced as part of hn netiquette or just plain common sense. :)

just a thought. if they want, the right people to apply, and not get flooded, with mismatched applicants,
then at the least, they should be specific about these basic items, right?

btw, @kristopolous, thanks for script, its a way better than seeing the page raw.

gotoc commented Jun 2, 2017

place, job, terms, description... here is a crazy idea, why not have a form field, with required fields for remote/onsite.
ie, it would have to be enforced as part of hn netiquette or just plain common sense. :)

just a thought. if they want, the right people to apply, and not get flooded, with mismatched applicants,
then at the least, they should be specific about these basic items, right?

btw, @kristopolous, thanks for script, its a way better than seeing the page raw.

@frosas

This comment has been minimized.

Show comment
Hide comment
@frosas

frosas Nov 3, 2017

Thanks @kristopolous and @meiamsome, browser search functionality is definitely not enough to search hundreds of job positions!

Because I wanted a mix of both scripts (i.e. nested criterias AND regular expressions being first-class objects), and because it was fun to write, I ended up creating just another version which looks like this:

// Non-Angular Javascript contract positions in London or remote
hn.filter(
  hn.or(/(javascript|typescript)/i, /ES\d/, 'JS'),
  hn.not(/angular/i),
  /contract/i,
  hn.or(hn.and('ONSITE', /london/i), 'REMOTE')
);

Details at https://gist.github.com/frosas/4cadd8392a3c4af82ef640cbedea3027

frosas commented Nov 3, 2017

Thanks @kristopolous and @meiamsome, browser search functionality is definitely not enough to search hundreds of job positions!

Because I wanted a mix of both scripts (i.e. nested criterias AND regular expressions being first-class objects), and because it was fun to write, I ended up creating just another version which looks like this:

// Non-Angular Javascript contract positions in London or remote
hn.filter(
  hn.or(/(javascript|typescript)/i, /ES\d/, 'JS'),
  hn.not(/angular/i),
  /contract/i,
  hn.or(hn.and('ONSITE', /london/i), 'REMOTE')
);

Details at https://gist.github.com/frosas/4cadd8392a3c4af82ef640cbedea3027

@Ivanca

This comment has been minimized.

Show comment
Hide comment
@Ivanca

Ivanca Dec 2, 2017

This script loads all pages via AJAX; you may execute it before this one so you search on all pages instead of just first one

;(function ajaxLoadNextPage () {
    var more = document.querySelector('.comment-tree > tbody > tr:last-child a');
    if (more && more.innerHTML === "More") {    
        var httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = function () {
            if (httpRequest.readyState === XMLHttpRequest.DONE) {
                if (httpRequest.status === 200) {
                  more.remove();
                  var div = document.createElement('div');
                  div.innerHTML = httpRequest.responseText;
                  var nextHTML = div.querySelector('.comment-tree > tbody').innerHTML;
                  document.querySelector('.comment-tree > tbody').innerHTML += nextHTML;
                  ajaxLoadNextPage();
                } else {
                  alert('There was a problem with the request to ' + more.href);
                }
            }
        };
        httpRequest.open('GET', more.href);
        httpRequest.send();
    }
})();

BTW I'm looking for a job, developer with more than 10+ years of JavaScript & PHP experience; ping me at ivanca [ at ] gmail

Ivanca commented Dec 2, 2017

This script loads all pages via AJAX; you may execute it before this one so you search on all pages instead of just first one

;(function ajaxLoadNextPage () {
    var more = document.querySelector('.comment-tree > tbody > tr:last-child a');
    if (more && more.innerHTML === "More") {    
        var httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = function () {
            if (httpRequest.readyState === XMLHttpRequest.DONE) {
                if (httpRequest.status === 200) {
                  more.remove();
                  var div = document.createElement('div');
                  div.innerHTML = httpRequest.responseText;
                  var nextHTML = div.querySelector('.comment-tree > tbody').innerHTML;
                  document.querySelector('.comment-tree > tbody').innerHTML += nextHTML;
                  ajaxLoadNextPage();
                } else {
                  alert('There was a problem with the request to ' + more.href);
                }
            }
        };
        httpRequest.open('GET', more.href);
        httpRequest.send();
    }
})();

BTW I'm looking for a job, developer with more than 10+ years of JavaScript & PHP experience; ping me at ivanca [ at ] gmail

@janklimo

This comment has been minimized.

Show comment
Hide comment
@janklimo

janklimo Dec 2, 2017

Any plans to package this as an extension?

janklimo commented Dec 2, 2017

Any plans to package this as an extension?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment