QuicKey icon indicating copy to clipboard operation
QuicKey copied to clipboard

Match words individually against a `title + URL` string, while ignoring the word order

Open garyking opened this issue 6 years ago • 6 comments
trafficstars

I assume that this ext doesn't allow space, because it just takes the query string and searches for it in titles and URLs, with a case-insensitive match with spaces removed.

I suggest allowing spaces, then each word in the query is matched against another word in the title and URL of each tab.

So typing test example would still match Test | https://www.example.com/ but won't match Test | https://www.test.com/ or Example | https://www.example.com/.

garyking avatar Aug 23 '19 21:08 garyking

Thanks for the feedback. QuicKey does in fact have an option to insert spaces instead of using the spacebar to navigate the list. Let me know if you're not able to find it (small usability test, if you don't mind :).

It sounds like you'd want to treat the title and URL as one long string, so that you'd be able to match any part of it with the query. In that case testexample would match just as well as test example. Or are you suggesting that space is a delimiter, and the first part must match the title and second part must match the URL?

fwextensions avatar Aug 23 '19 21:08 fwextensions

I found the space option.


I wrote a matcher function and some tests, to show what I mean. That would be easier to explain, than with words, because this is best determined intuitively, for me.

test.js:

module.exports = {
  /**
   * Match the given search query with the title and URL.
   *
   * @param {string} query The search query that the user inputs.
   * @param {string} title The page's title.
   * @param {string} url The page's URL.
   * @returns {boolean} The search query has a match.
   */
  matchTitleAndUrl(query, title, url) {
    const queryWords = query.toLowerCase().split(' ');

    const formattedTarget = (title + url)
      .replace(/[^A-Za-z0-9]/g, '')
      .toLowerCase();

    return queryWords.every((queryWord) => formattedTarget.includes(queryWord));
  },
};

test.test.js:

const { matchTitleAndUrl } = require('./test');

describe('match title and URL', () => {
  const match = matchTitleAndUrl;

  test('match', () => {
    // Query is in both title and URL.
    expect(match('test example', 'Test', 'https://www.example.com/')).toBe(
      true,
    );

    // Matches in the middle.
    expect(match('es xampl', 'Test', 'https://www.example.com/')).toBe(true);

    // Matches even when reversed search terms.
    expect(match('example test', 'Test', 'https://www.example.com/')).toBe(
      true,
    );

    // Allow matching the same word multiple times.
    expect(match('tes test', 'Test', 'https://www.example.com/')).toBe(true);
  });

  test('no match', () => {
    // `example` does not match.
    expect(match('test example', 'Test', 'https://www.test.com/')).toBe(false);

    // `test` does not match.
    expect(match('test example', 'Example', 'https://www.example.com/')).toBe(
      false,
    );

    // The search words are combined.
    expect(match('esxampl', 'Test', 'https://www.example.com/')).toBe(false);
  });
});

garyking avatar Aug 23 '19 22:08 garyking

From a brief test, the above code also seems to be how Chrome itself matches titles and URLs in the address bar.

garyking avatar Aug 23 '19 23:08 garyking

Code updated above.

garyking avatar Aug 24 '19 00:08 garyking

Thanks for the example code. I assume words in this order would still be true?

expect(match('example test', 'Test', 'https://www.example.com/')).toBe(
  true,
);

The tricky thing is right now, the title and url are scored separately, and the highest score is used to sort the tab. If not all the characters are found in the order they were typed, then the score is 0. I'd have to think about how to handle multiple tokens that can be in any order.

fwextensions avatar Aug 24 '19 00:08 fwextensions

Yeah, the query word order in my example doesn't matter.

garyking avatar Aug 24 '19 01:08 garyking