About this talk
Watch what improvements Trouva made with Algolia and Glimmer, vs the old Elasticsearch and pre-Glimmer 2.0.
- So my name is Michael Schinis. I'm a software developer at Trouva. And I'm gonna assume that everyone knows what Glimmer is. But can I get a show of hands if anyone has heard of Algolia before or if anyone has been using it. Okay, so for anyone else that doesn't know what Algolia is, so, essentially Algolia is a software service provider. They give you a way to basically index your data. And distribute it across the world for your customers to access. It's a bit like Elasticsearch, but it's a lot faster. You get about 30 milliseconds response times. Which is very impressive. So in order to set the context first, I need to talk a little bit about Trouva, who we are, and what we do. So Trouva is a community of boutiques. Our mission is to get really, truly unique products that are curated by the boutique owners themselves. Get them to the hands of everyone around the world. And, being an e-commerce, you would understand that speed is of the essence. Every half-seconds that we spend on fetching data, rendering the results, displaying it to the user is another customer that we lose. And then let me talk a little bit about the relationship that we have at Trouva with Ember. We, seriously, love Ember. We believe that Ember gives us all of the tooling, all of the software conventions, the developer conventions, everything that we need in order to scale our applications and grow it, without losing, without compromising on speed and everything else, especially not with Ember engines. We also believe that it lets us focus on getting the right product to our customer. Which is really important. And we don't have to spend any of our time trying to build all of the tooling, like Ember CLI. We have the experience and the expertise of the community that will help us achieve that. So let's get right into it. When you learn in Trouva, the second most used case scenario is using search. So, for us, it's really important to optimise that. When you hit on the search and you start typing something out, you will get the results here. And we will then try and help the customer figure out where they're gonna go next. So what I did is I basically tried to recreate the exact same search conditions here with a little bit more data. So about 500 results. And build three different Ember applications that all do the same thing. That will all do an API call, fetch data, and then they will all render it into the viewport here. So, cool, I sent three applications. The one application uses Elasticsearch to the API call. And then uses an Ember app which is on 2.9, pre-Glimmer 2.0. And renders the results as you see them here. The second application is Algolia. And, again, a pre-Glimmer version, 2.9. And we did that so that we can see just the difference of switching our Elasticsearch with Algolia. And the third application, we took it a step further. And basically just upgraded Ember to 2.11, with the Glimmer 2.0. And we're going to see a few benchmarks right now. Right, so Elasticsearch powered search. Too many search words. Right, so the response time here, we can see that it takes about 113 milliseconds to do the request. You see that most of it is spent during waiting for the server to reply back. And then just the content download time is quite short. But, nevertheless, it took 113 milliseconds. And then I find that quite staggering, for some reason. It's 152 milliseconds to render the results. But do keep in mind that it is rendering 500, approximately 500 results. Then for the same query, exact same query, we just switched out Elasticsearch with Algolia. And we saw a massive improvement in the speed. So it went down from 113 milliseconds, down to 31 milliseconds. That's impressive, that's really impressive. And then the rendering time is approximately the same as before. You know, rendering time sometimes vary so it went from 150 to 133. No real speed improvement there because, again, we're using the same rendering engine. And next, the third Ember application. We used, again, Algolia. And we used Glimmer to render the results. So it took about 36 milliseconds this time to get the results from Algolia. And then it took about 102 milliseconds to render the results. So that's a further 30 milliseconds speed improvement in rendering the results. So, I took all of these results and run the same experiment 20 times. And took the averages of each one and put them on the board. So these are the averages of 20 runs, and rounded off. So you'll see that Elasticsearch and pre-Glimmer, the response time was about 100 milliseconds. And then Algolia, and Algolia took again 30 milliseconds. And I've done in bold the two more important speed improvements here. So we saw a 70 millisecond improvement just from the API call. Then we also saw a further 30 millisecond response, sorry, 30 millisecond just for rendering the results. So just by doing these two optimizations, we saw a 100 millisecond difference. Now for the average user it takes 100 milliseconds to register that there's a slight delay on the UI. That means that, for us, as an e-commerce business, it's super important to get that number down as close as possible to 130 milliseconds, or even less. Because, as the user types, we wanna be as instant as possible. We wanna show them the results. We want to let them know that we're doing something about what they're actually looking for. So that's search experience. But I guess the more important thing is what happens after you search for something. What happens after you get a, oops, yep. So you get a full list of 48 products in this case. How do you find the product that you're looking for? Well, that's why we have the things which are filters. So as in any e-commerce site, you have filters where you can narrow down your search results to exactly what you're looking for. And you'll sometimes have a way to change the number of results that you're going to see. So we have our other options are 92 and 186. And you can imagine that, before Glimmer, 186 was really bad. Now it's almost instant. And then we have a way to sort the results as well. But how do you combine these filters? How do you make sure that the combinations that the user selected are actually exactly what the user is expecting to see and is expecting to get back? Okay, so that's the filters I already talked about. Well, it turns out that actually getting all of these different filters and combining them is actually really hard. And that's where InstantSearch comes into play. So InstantSearch is a library by Algolia. It's built in React. And essentially what it does is they give you all of the components required so that you can build the, either an e-commerce site or you can do any type of search. So, for example, in the Ember learning team we're gonna be introducing a new search page where you can search for, I dunno, an API that you're looking for. And we will be able to give you a filter so you can search for in between Ember API or Ember data API. You can select the versions and all that kind of things. So InstantSearch gives you all of these different components. So these are the result components, that's the pagination, some different types of filters and the search box. And all you have to do is just plug them into your site. Algolia knows exactly where your data is. They know how to optimise the retrieval. And they take everything into consideration. So that works nicely with React applications. But I'm not sure if you have tried to incorporate React into Ember. I guess we were the only crazy company to do that. So, at some point, Tomster said "I've had enough, "I've had enough." The site did not perform. And although we did get really, really quick the results back from Algolia. The integration between React and Ember was horrible. It was very slow. And, actually, let me take you through one of the components that we created. Okay, so we created a component which we called, at the time, Shop Page Component. Which was responsible for fetching results from Algolia and showing them into the template of this component. If I scroll down, you will see this is a declaration of how we created an extra component using InstantSearch. But you can see it's very easy. All you have to do is just say, okay, I want a pagination widget. That's where it's gonna be inserted. So into the DOM we had a Div with that ID. And then InstantSearch went and found that ID. And created that widget for us, inserted it into the DOM, it all worked nicely. You also had a way to customise different labels. CSS classes so that you could customise even further. We then had a Hits Per Page filter. Which is basically how many results we're gonna show per page. And we had a variety of different other filters as well. So, like the way that we're gonna sort. A commented out hierarchy menu. Refinement list, refinement list, and a variety of different filters. Okay, so here is the important bit. In order to get the results back from Algolia and render them into the template, we could have used their own hits component which basically renders the results into the page. But we decided not to do that. Mainly because we lost the internal Embers transitions. And, you know, every time that you would click on a product it would be no different to using any other non-single page application. It would reload the whole site. It would download again all of the assets. Make sure Chrome and every other browser does performance optimizations. To do that caching and all that kinda stuff. But it's not the same. So what we decided to do is create our own custom widget. So you'll see here, search.addWidget. And then basically our own component. Which basically its job was setting up. Here. It was setting up different parameters on Algolia. And then its primary job was mainly to do this. So that gets all of the results from Algolia. Inserts all of the results into the component. And then the template essentially takes over, renders all of the results, and job done. We can then use Ember, Embers templates, html bars, Glimmer, whatever, to render our results. And we can do, also, internal transitions, everything that you can do with Ember. So then here comes this part. This part is essentially the integration between, our integration between Ember and React. Now there might be a better way. But, at the time, this was the best that we could come up with. And it really was the best thing. So all we had to do is whenever something, whenever a query parameter changed, we would do the, sorry, instant search without the query parameters. Sometimes, not always, the Ember lifecycle hooks would fire because React took over. So not always, but most times it would. And then we would pass all of the query parameters down into the component, which would force the data update attributes to recalculate. And then we would basically reset the helper. The Search Helper which is the Algolia Search Helper. And continue doing the searches. Now going back to the slides. I mentioned that Tomster didn't approve. But so did we. Essentially by doing the integration between React and Ember we lost all of the developer economics that I mentioned that we loved from Ember. We also lost the ability to do pass down actions into the different filters. So that we can do accurate tracking from the customers point of view. We lost the ability to track all of the query parameters that changed. Sometimes it did track them, sometimes it didn't. But we just found out that basically React and Ember were fighting over the query parameters. Okay, so then comes our solution. So what we did is we decided, okay, we're gonna switch out these components one by one. And we're gonna do that by accessing the Search Helper from inside InstantSearch. And we're gonna do everything that InstantSearch does. In a better way for Ember. So our final solution looks a bit like this. There's a few moving parts here. So the search service is what is responsible for doing any API calls to Algolia, getting the results back and passing them to the route and the template so that they can be rendered. The route is responsible for calling the configuration method on the search service. Which essentially sets up the filters. And then it also has the ability to run the search separately. So that you can do, you can use Ember parachutes, if you're familiar with it, to do your query parameters in the searching different completely outside of the route, if you want to. Then we have the filters which are essentially native Ember components. And we have the controller, which holds the state of the query parameters. One assumption that we did here is that there will always be query parameters when associated with a page that you're filtering. And I think that's a fairly good, that's a really good assumption to make. Because, when you're filtering things, you want to keep the state of the page on the URL. So that if any customer shares the URL, you're always looking at the same page. Okay, so say a customers visits one of our product listing pages. The route is essentially first gonna call the configuration method on the search service. Which will set up the Algolia Search Helper. Which is responsible for doing the API calls with Algolia. Then it will run the search. As I mentioned before, you can do that in the controller if you like. But we run the search through the route on the model hook. And then we get the results back, pass them to the template and render the results. When these filters are then inserted into the templates, what they essentially do is they call it a register method on the search service. Which passes the context of the component into an array of the search service. So that every time there's updates on the search service, it will also update the UI on the filters. And then if a user selects one of the filters, changes the, I don't know, refinements on the left-hand side, all we have to do is pass down a normal Ember action. Just, you know, normal data down, actions up approach. Call the update, mutate one of the query parameters, which will force a refresh on the model. Run the search again. And then the search service knows all of the filters, so we'll update the UI. It took us a long time to come up with this approach. And we wanted to get it right. And I think we got it right. And I want to announce that we're open sourcing this up. The add-on is there. It's not 100% there, it's now 90% there. So by the end of next week, we're hoping to have a fully stable solution working. And essentially all you will have to do is define the query parameter to each key on your objects. So, for example, if I want the, I don't know, say we've got a size on the product, all we have to do is find that the size query parameter is going to match on the sized value inside Algolia. You then have to basically the search service that's provided inside the other one. Which handles all of the searching for you. And re-export it. And then you can use our own add-on components. You can create your own. You can extend your own if you'd like to. To like further customization. But if you do create components of your own please do contribute back to the repository so that we can collectively grow the list of components that we can share with everyone. And then, final thing, is call the configuration method and the search functions and you've got the results lightning fast. Okay, so that's our solution but we obviously need help. We want to add more filters. We want to add Ember data adapters so that we can serialise the data coming from Algolia. Push them into the store. We want to add less verbosity, make it easier to implement with. Although it is already simple, we want to make it even more simple. And we want to add more tests to ensure that every release cycle doesn't break something else. And ensure that we're backwards compatible. Thank you. Yeah, I guess. - We can take one or two questions. - Yes. - [Audience Member] Have you let Algolia know about the add-on? - I did message them over Twitter because they got in touch with me after they saw the Tweet. They haven't responded back yet. But I'm waiting for that. But, yeah, the add-on that we've created is essentially meant to not obviously replace InstantSearch but act as complimentary to it. So we want to add this same functionality that InstantSearch has and give it to the Ember power users. We want to have the same functionality. So it would be really great to see what they come back with. Yeah, so that's one of the things that I'm gonna definitely ask for. But, for now, the proof of concept is literally Trouva.com. So you can use Trouva to test it out. Yeah, it's pretty solid. Any other questions? Nope? Cool, thank you very much.