DEV Community

Riccardo Solazzi
Riccardo Solazzi

Posted on • Originally published at thezal.dev

Sky's the Limit! Supercharging Your Astro Blog with Orama, the Ultimate Stargazing Search Engine!

Hello There, TheZal here! Ever wanted to slap a fully customizable full-text search on your Astro website?
Well, if you've so much as dreamt about it once, then this guide is right up your alley! Let's discover Orama!

What is Orama Search?

What is Orama Search?

So, let's talk about Orama and how it works: this search engine is the real deal, written entirely in TypeScript. It's lightning-fast, able to search through millions of documents in less than a millisecond, yeah, we're talking microseconds!

Orama comes packed with a bunch of impressive features like typo tolerance, facets, filters, and even supports stop word removal and stemming in 26 languages. The best part? It's built with zero dependencies, so you know it's got that independent spirit. You can run Orama wherever JavaScript runs, which is pretty sweet.

And guess what? It's free and open source, so you can join in the discussions on GitHub or hop on the Slack channel!

Get involved in this fast-growing community of developers who are shaping the next generation of full-text search engines. Exciting stuff, right?

How the heck does Orama actually work?

Orama Squad

Let's break into the steps to utilize Orama and analyze how it works. I won't dig into the technical stuff because, hey, it's an open-source project, which means you can easily peek at the source code, no problemo!

Installation

It's possible to install it using npm, yarn, pnpm:

npm i @orama/orama
Enter fullscreen mode Exit fullscreen mode
yarn add @orama/orama
Enter fullscreen mode Exit fullscreen mode
pnpm add @orama/orama
Enter fullscreen mode Exit fullscreen mode

or import it directly in a browser module:

<html>
  <body>
    <script type="module">
      import {(create, search, insert)} from 'https://unpkg.com/@orama/orama@latest/dist/index.js' // ...
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Creation of the database

Orama uses its own database which can be created with few lines of code.

Let me show an example of a simple Pokémon database

import { create, insert, remove, search } from '@orama/orama';

const pokemon_db = await create({
  schema: {
    name: 'string',
    description: 'string',
    id_pokedex: 'number',
  },
});
Enter fullscreen mode Exit fullscreen mode

So, here's the deal with Orama. It's all about indexing string stuff, like properties and whatnot. But hey, if you need to set and store some extra data, no worries, you can totally do that too.

Once you've got your db instance all setup, it's time to start throwing in some documents. Let the fun begin!

await insert(pokemon_db, {
  name: 'Bulbasaur',
  description: 'A strange seed was planted on its back at birth. The plant sprouts and grows with this Pokémon.',
  id_pokedex: 1,
});

await insert(pokemon_db, {
  name: 'Charmander',
  description: 'Obviously prefers hot places. When it rains, steam is said to spout from the tip of its tail.',
  id_pokedex: 4,
});

await insert(pokemon_db, {
  name: 'Squirtle',
  description: 'After birth, its back swells and hardens into a shell. Powerfully sprays foam from its mouth.',
  id_pokedex: 7,
});
Enter fullscreen mode Exit fullscreen mode

Query the database

Alright, now that all the data's in the house, it's time to get down to business and start querying that database like a boss! Let the search extravaganza begin!

const searchResult = await search(pokemon_db, {
  term: 'squirtle',
});
Enter fullscreen mode Exit fullscreen mode

In this scenario, we're on a mission to find all the documents that have the word "squirtle" in them. We're gonna dive deep into every nook and cranny, searching through every schema property (yeah, they are called indexes). Get ready to unleash the search power!

{
  elapsed: {
    raw: 95357,
    formatted: '95μs',
  },
  hits: [
    {
      id: '53290575-24',
      score: 0.814974732853017259,
      document: {
          name: 'Squirtle',
          description: 'After birth, its back swells and hardens into a shell. Powerfully sprays foam from its mouth.',
          id_pokedex: 7
      }
    }
  ],
  count: 1
}
Enter fullscreen mode Exit fullscreen mode

It is also possible to specify the property to lookup

const searchResult = await search(pokemon_db, {
  term: 'tail',
  properties: ['description'],
});
Enter fullscreen mode Exit fullscreen mode

and of course, we will get:

{
  elapsed: {
    raw: 51687,
    formatted: '51μs',
  },
  hits: [
    {
      id: '64437599-23',
      score: 0.669342702734917259,
      document: {
          name: 'Charmander',
          description: 'Obviously prefers hot places. When it rains, steam is said to spout from the tip of its tail.',
          id_pokedex: 4
      }
    }
  ],
  count: 1
}
Enter fullscreen mode Exit fullscreen mode

Oramaze everything!

Oramaze everything

Since the beginning of this rad project, Orama has been all about keeping things simple and making it super easy to add new features. They've nailed it with a slick plugin system that lets you customize Orama just the way you want, while keeping the core super lean and mean.

Right now, the awesome Orama core team is busy cooking up some cool hooks that will let the community dive right in and tinker with Orama's inner workings. Talk about getting hands-on!

But wait, there's more! The Orama core team is also working on some official plugins to support specific features that might not be everyone's cup of tea. So, no worries, you can cherry-pick the exact features you dig and leave the rest behind.

And guess what? You can find all the juicy source code of these official plugins chilling out in the Orama monorepo. Just head over to the packages directory and dive into the magic. It's open for all to see!

So, check it out: since I added Orama to my Astro-based blog, I thought it would be dope to write about how I actually implemented Orama on Astro using its plugin.

A little bit of code

A little bit of code

The "plugin-astro" plugin is an incredible tool that brings a whole new level of functionality to your Astro websites.

With this plugin, you can effortlessly index the content of your Astro site and provide your visitors with a powerful text search feature, it's like having a customized search engine right on your site, giving your users a seamless search experience!

Let's set up it!

Installation

It is possible to install the plugin with any major Node.js package manager:

npm install @orama/plugin-astro
Enter fullscreen mode Exit fullscreen mode
yarn add @orama/plugin-astro
Enter fullscreen mode Exit fullscreen mode
pnpm add @orama/plugin-astro
Enter fullscreen mode Exit fullscreen mode

and that's it!

Database creation

The database will be created inside the dist folder of the project and will be named after the configuration inside the astro.config.mjs file.

Mine is:

import orama from '@orama/plugin-astro';

export default defineConfig({
  integrations: [
    orama({
      // It is possible to have more than one DB, with different configurations
      articles: {
        // Required. Only pages matching this path regex will be indexed. The regex starts from the dist folder.
        pathMatcher: /^article_/,

        // Optional. 'english' by default
        language: 'english',

        // Optional. ['body'] by default. Use it to constraint what is used to
        // index a page.
        contentSelectors: ['body'],
      },
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

Let's have a fast look at what I have inside the configuration:

  1. articles: : it's the database name that I am using
  2. pathMatcher: /^article_/ : it's the regex that will be used to create the database: will be added any file that respects the rule written here: , I use /^article_/ because every article in this blog has the 'article_' prefix in its name
  3. language: 'english' : I use english by default
  4. contentSelectors: ['body'] : I use the default contestSelector

From now on when the build is launched a fresh database will be created!

The search component

To use Orama I created a react component called OramaSearch.tsx that allows me to use it inside the Header.astro component.

This is my first react component so there are errors for sure but let's see some code:

import { getOramaDB, search as searchOrama } from '@orama/plugin-astro/client';
import React, { useState, CSSProperties } from 'react';

export const OramaSearch = () => {
  const [result, setResult] = (useState < any) | (undefined > undefined);
  const [showDiv, setShowDiv] = useState(false);

  const search = async (searchTerm: string) => {
    if (searchTerm.length > 2) {
      //searchTerm is the input of the input box

      const db = await getOramaDB('articles');

      const res = await searchOrama(db, { term: searchTerm });
      var hits: { title: string, path: string }[] = res['hits'].map((hit: any) => [
        hit.document.title,
        hit.document.path,
      ]);

      setShowDiv(hits.length > 0);

      setResult(hits);
    } else {
      setShowDiv(false);
    }
  };

  const searchResultsDivStyle: CSSProperties = {
    position: 'fixed',
    borderTop: '1px solid white',
    borderRight: '1px solid white',
    borderBottom: '1px solid white',
    borderLeft: '1px solid white',
    borderRadius: '10px',
    opacity: 1,
    padding: '20px',
    zIndex: 2,
    backgroundColor: localStorage.theme === 'dark' ? '#1F2937' : 'white',
  };

  return (
    <div>
      <div>
        <input
          type="text"
          className="searchInputBox"
          style={{ height: '60px' }}
          onChange={(e) => search(e.target.value)}
        />
        {showDiv && (
          <div id="searchResults" style={searchResultsDivStyle}>
            {result.map((element, index) => (
              <React.Fragment key={index}>
                <a href={element[1]}>{element[0]}</a>
                {index !== result.length - 1 && <br />}
              </React.Fragment>
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

export default OramaSearch;
Enter fullscreen mode Exit fullscreen mode

I made the search result div even cooler by dynamically setting its background color, this way, it seamlessly adapts to the theme changes.

Speaking of themes, I also added an instruction to the function that handles theme changes, so the background of the div reflects the current theme. It's all about those little details that make the user experience top-notch!

Use the component

Inside the navbar in the header component, I added the component created with a few lines of code:

<li>
  <a class="flex items-center">
    <OramaSearch client:load />
  </a>
</li>
Enter fullscreen mode Exit fullscreen mode

and that's all! The final result is under the eyes of everyone! Just look up!

Contribute to Orama!

Contribution is the mission

To wrap it all up, integrating Orama into your Astro website is a piece of cake. Although the project is still in its early stages and may not have all the fancy bells and whistles just yet, rest assured that it works like a charm. The team behind Orama has big plans to add exciting new features soon, so there's definitely more awesomeness to come.

If you have a feature in mind that you'd like to see or if you're feeling extra adventurous and want to contribute to this incredible project, you're in luck! Check out the contribute guidelines on the GitHub repository and start collaborating. You can even explore the issues and see if there are any bounties up for grabs. Contributing not only helps improve the project but can also put some extra cash in your pocket. It's a win-win!

So, don't hesitate to give Orama a whirl and join the growing community of developers who are shaping the future of full-text search engines. Exciting times lie ahead!

Conclusions

Orama gets it!

After all, we can say that the addition of a search engine it's a piece of cake using Orama! 🔍

A little shoutout to the Microsoft MVP Luca Del Puppo, check out his articles on how to use Orama inside a React application:

And another little shoutout to Balastrong aka DevLeonardo for helping me in the early steps of the integration of Orama inside an Astro blog!

If you found this useful feel free to leave a comment here or to reach me on Twitter, GitHub, or mail and share it with your dev friends!

Top comments (0)