Last active
July 24, 2023 04:12
-
-
Save kristopolous/19260ae54967c2219da8 to your computer and use it in GitHub Desktop.
hn job query search
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Usage: | |
// Copy and paste all of this into a debug console window of the "Who is Hiring?" comment thread | |
// then use as follows: | |
// | |
// query(term | [term, term, ...], term | [term, term, ...], ...) | |
// | |
// When arguments are in an array then that means an "or" and when they are seperate that means "and" | |
// | |
// Term is of the format: | |
// ((-)text/RegExp) ( '-' means negation ) | |
// | |
// A first argument of '+' signifies an additional pass on the filtered data as opposed to | |
// resetting everything. | |
// | |
// Example: Let's look for jobs in california that involve rust or python and not crypto: | |
// | |
// > query('ca', '-crypto', ['rust', 'python']); | |
// {filtered: '98.57%', query: 'ca AND NOT crypto AND (rust OR python)'} | |
// | |
// Then you see, "oh right, I don't care about blockchain either": | |
// | |
// > query('+', '-blockchain'); | |
// {filtered: '98.57%', query: 'ca AND NOT crypto AND (rust OR python) AND NOT blockchain'} | |
// | |
// Another example: | |
// > query(['ca', 'sf', 'san jose', 'mountan view']) | |
// {filtered: '90.61%', query: '(ca OR sf OR san jose OR mountan view)'} | |
// | |
// COVID killed Silicon Valley. Quod Erat Demonstrandum! | |
// | |
// Changelog for 2022-08-02 | |
// | |
// ADDED | |
// | |
// * Negation via '-' | |
// | |
// * Multi-pass querying via first argument being '+' | |
// | |
// * Debugging query string added in the response | |
// | |
// CHANGED | |
// | |
// * "or" and "and" works the opposite of how it did previously. | |
// This form seems to be more useful. | |
// | |
// * Whole word matching is default | |
// | |
// * Terms such as "c++" are properly escaped | |
// | |
// UPDATED | |
// | |
// * Rewrote as an absurd implementation. | |
// I had a fun afternoon writing this. | |
// | |
function query(...queryList) { | |
// HN is done with very unsemantic classes. | |
let jobList = [...document.querySelectorAll('.c5a,.cae,.c00,.c9c,.cdd,.c73,.c88')], | |
// Traverses up the dom stack trying to find a match of a specific class | |
upto = (node, klass) => node.classList.contains(klass) ? node : upto(node.parentNode, klass), | |
display = (node, what) => upto(node, 'athing').style.display = what, | |
hide = node => { display(node, 'none'); node.show = false}, | |
show = node => { display(node, 'block'); node.show = true}, | |
// Use RegExp as is. Otherwise make it a case insensitive RegExp | |
destring = what => [ | |
what[0] === '-', | |
what.test ? what : new RegExp([ | |
'\\b', | |
what.toString() | |
.replace(/^-/,'') | |
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), | |
'\\b' | |
].join(''), 'i'), what | |
]; | |
// This is our grand reset | |
if(queryList[0] !== '+') { | |
jobList.forEach(show); | |
// Have fun with that. | |
query.hidden = +!( query.fn = [] ); | |
} else { | |
queryList.shift(); | |
} | |
// The AND is an artifact of the design. It's just iterative napped subsets | |
query.fn = query.fn.concat(queryList.map(arg => { | |
// Make it an array if it isn't one and pass it through our destring | |
let orList = Array.of(arg).flat().map(destring); | |
// If we're showing the job, then go through the list of terms | |
// If all of them do not match, hide it, then return the length. | |
query.hidden += jobList.filter(node => node.show | |
&& orList.every(([neg, r]) => neg ^ !(node.innerHTML.search(r) + 1)) | |
).map(hide).length; | |
// You're on your own here - this is just the construction of | |
// the debug string. There's far more reasonable ways to do this | |
// But what fun would that be?! | |
return ( | |
' ('[+!!(orList.length - 1)] + | |
orList.map(([neg, ig, r]) => ['', 'NOT '][+neg] + r.slice(+neg)).join(' OR ') + | |
' )'[+!!(orList.length - 1)] | |
).trim(); | |
})); | |
return { | |
filtered: (100 * query.hidden / jobList.length).toFixed(2) + '%', | |
query: query.fn.join(' AND ') | |
}; | |
} |
You're right. I was so careful in this. damn it. That's extremely disappointing. I apparently foolishly introduced the bug here when I was just using a phone and their textbox interface: https://gist.github.com/kristopolous/19260ae54967c2219da8/revisions#diff-63e9a5e5dead19d4e7a3ee13c24221089b165a04e534e6e675d491e9422576d1
Fixed. Thanks for the report @nemanjam
Thank you.
Updated pagination code using fetch
and async
/await
, and while
instead of recursion. Forked from @Ivanca
It'd be nice to merge this into query()
.
(async function () {
var more;
while (more = document.querySelector('a.morelink')) {
const r = await fetch(more.href);
more.remove();
const div = document.createElement('div');
div.innerHTML = await r.text();
document.querySelector('.comment-tree > tbody').innerHTML += div.querySelector('.comment-tree > tbody').innerHTML;
}
})();
Also, maybe a bookmarklet? You can drag/drop or copy/paste to your boomarks toolbar. eg:
javascript:
/* /Say hello# */
(function () {
alert('Hello, HN');
})();
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Damn it
I'm still in bed. I'll look when I'm at my office