Skip to content

[CT-3469] - Autocomplete for home search bar #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 11, 2022

Conversation

ben-brooker
Copy link
Contributor

@ben-brooker ben-brooker commented Jun 29, 2022

TICKET

Added autocomplete search to the home page using elastic search and filtered the results based on relevance score. On click, the links search for the content.

Before:
Screenshot 2022-07-01 at 13 58 08
After:
Screenshot 2022-07-01 at 13 58 19

@ben-brooker ben-brooker requested a review from ztsorojev June 29, 2022 14:35
@ben-brooker ben-brooker force-pushed the bb/search-autocomplete branch 2 times, most recently from e70f64d to f392311 Compare July 1, 2022 12:32
@ben-brooker ben-brooker changed the title feat: base autocomplete functionality [CT-3469] - Autocomplete for home search bar Jul 1, 2022
@ben-brooker ben-brooker marked this pull request as ready for review July 1, 2022 13:00
@ben-brooker ben-brooker requested a review from Mim1991 July 1, 2022 13:07
Copy link
Contributor

@ztsorojev ztsorojev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments for optimizations you can do! Functionality seems to work well when I try 👍
It's already a big improvement! 🔥

There is just one thing I noticed is that if I type 2 or 3 characters for example, it can show me 1 package suggestion, but if I add a new character, it starts to show me 5 suggestions. Shouldn't it already show 5 before?

Example: plot package, if I write pl or plo, I only get this one package
image

However if I write plot, I get 5 package suggestions:
image

pages/index.tsx Outdated
Comment on lines 32 to 69
async function autoComplete(query) {
try {
// fetch the data
const resPackages = await fetch(
`${API_URL}/search_packages?q=${query}&page=1&latest=1`,
{
headers: {
Accept: 'application/json',
},
},
);

const { packages } = await resPackages.json();
const relevantPackages = packages.filter((p)=>(p.score>2.5));
if (relevantPackages.length<5) {
setPackageSuggestions(packages.slice(0, relevantPackages.length));
}
setPackageSuggestions(relevantPackages.slice(0,5));

const resFunctions = await fetch(
`${API_URL}/search_functions?q=${query}&page=1&latest=1`,
{
headers: {
Accept: 'application/json',
},
},
);
const { functions } = await resFunctions.json();
const relevantFunctions = functions.filter((f)=>(f.score>10));
if (relevantFunctions.length<5) {
setFunctionSuggestions(functions.slice(0, relevantFunctions.length));
}
setFunctionSuggestions(functions.slice(0,5));

} catch (err) {
console.error(err);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this live in Autocomplete.tsx?
So that the component handles all the logic itself

pages/index.tsx Outdated
Comment on lines 51 to 58
const resFunctions = await fetch(
`${API_URL}/search_functions?q=${query}&page=1&latest=1`,
{
headers: {
Accept: 'application/json',
},
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do a Promises.all so that both queries are sent at the same time and you don't have to wait for one to be finished?

You can do something like

Suggested change
const resFunctions = await fetch(
`${API_URL}/search_functions?q=${query}&page=1&latest=1`,
{
headers: {
Accept: 'application/json',
},
},
);
const [resPackages, resFunctions] = await Promises.all([
fetch(...),
fetch(...)
]);

You can read about the difference between using multilple await and Promise.all here: https://stackoverflow.com/questions/45285129/any-difference-between-await-promise-all-and-multiple-await

pages/index.tsx Outdated
Comment on lines 59 to 64
const { functions } = await resFunctions.json();
const relevantFunctions = functions.filter((f)=>(f.score>10));
if (relevantFunctions.length<5) {
setFunctionSuggestions(functions.slice(0, relevantFunctions.length));
}
setFunctionSuggestions(functions.slice(0,5));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw I think it's more clear to call it topics and not functions since that's what we already use it everywhere in the codebase (UI, API, db)

pages/index.tsx Outdated
Comment on lines 61 to 64
if (relevantFunctions.length<5) {
setFunctionSuggestions(functions.slice(0, relevantFunctions.length));
}
setFunctionSuggestions(functions.slice(0,5));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (relevantFunctions.length<5) {
setFunctionSuggestions(functions.slice(0, relevantFunctions.length));
}
setFunctionSuggestions(functions.slice(0,5));
setTopicSuggestions(relevantTopics.slice(0, Math.min(relevantPackages.length, 5));

pages/index.tsx Outdated
);

const { packages } = await resPackages.json();
const relevantPackages = packages.filter((p)=>(p.score>2.5));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is typescript, so you can use optional chaining in places where you are not sure if the variable you are accessing is defined or not.

Suggested change
const relevantPackages = packages.filter((p)=>(p.score>2.5));
const relevantPackages = packages?.filter((p)=>(p?.score>2.5));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments for optimizations you can do! Functionality seems to work well when I try 👍 It's already a big improvement! 🔥

There is just one thing I noticed is that if I type 2 or 3 characters for example, it can show me 1 package suggestion, but if I add a new character, it starts to show me 5 suggestions. Shouldn't it already show 5 before?

Example: plot package, if I write pl or plo, I only get this one package image

However if I write plot, I get 5 package suggestions: image

I think this is because I set the limits on the elasticsearch for packages the score defaults to 1.82... if the results are a low match. In order for the score to go above this number, the query has to be quite similar. (see picture below).
I can either:

  • Set the filter for the scores so that irrelevant results don't come up. This will mean that there are sometimes no suggestions

  • Don't filter the results so that there are always suggestions but they might be irrelevant

Screenshot 2022-07-08 at 19 43 10

Comment on lines 50 to 60
</div>
<div>
{
functionSuggestions.length>0
&&
searchInput
&&
<ul>
<li className="my-2 ml-2 pl-4 text-dc-grey200 flex justify-between">
<Heading as="h3" size={300}>FUNCTIONS</Heading>
</li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some space between the the sections?
It will look better 👍

Comment on lines 12 to 52
function autoCompleteSearch(query) {
router.push(`/search?q=${encodeURIComponent(searchInput)}`);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can rename it so it's more clear, it can even be called onClick tbh

Comment on lines 6 to 7
packageSuggestions: Array<any>,
functionSuggestions: Array<any>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion you shouldn't need these props, the component should handle them itself.

There are actually two approaches:

  1. Move all the logic to Autocomplete.tsx so that everything is handled in one place. Everyone who imports Autocomplete will get a search bar that just works.
  2. Make Autocomplete a wrapper component that handles all the logic. Then inside it, you have a child component that is basically how you wrote the current Autocomplete component: it takes props and just renders them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I decided to move all of the logic into Autocomplete

@ben-brooker ben-brooker requested a review from ztsorojev July 8, 2022 18:50
@ben-brooker ben-brooker force-pushed the bb/search-autocomplete branch from 311e7ec to beef2f3 Compare July 8, 2022 18:58
pages/index.tsx Outdated
Comment on lines 26 to 27
useEffect(() => {
}, [searchInput])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this

pages/index.tsx Outdated
@@ -1,13 +1,15 @@
import { GetServerSideProps } from 'next';
import { useRouter } from 'next/router';
import { useState } from 'react';
import { useEffect, useState } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the useEffect in that case

Comment on lines 84 to 88
key={p.fields.package_name}
onClick={()=>onClick(p.fields.package_name)}
className="flex items-center px-4 py-2 cursor-pointer hover:bg-dc-beige200 hover:opacity-0.5"
>
<Paragraph className="pl-2 text-lg">{p.fields.package_name}</Paragraph>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add optional chaining here just in case

Comment on lines 109 to 117
key={t.fields.package_name+t.fields.name}
onClick={()=>onClick(t.fields.name)}
className="flex items-center px-4 py-2 cursor-pointer hover:bg-dc-beige200 hover:opacity-0.5"
>
<div>
<Paragraph className="px-2 text-lg">{`${t.fields.name}`}</Paragraph>
</div>
<div>
<Paragraph className="text-lg text-dc-red">{`(${t.fields.package_name})`}</Paragraph>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too, optional chaining

@ben-brooker ben-brooker merged commit bd0936d into main Jul 11, 2022
@ben-brooker ben-brooker deleted the bb/search-autocomplete branch July 11, 2022 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants