Search three ways with Xata

There comes a time in every project where the next feature on the roadmap is search. In this example, we’ll walk you through three different ways you can search your Xata database.

Written by

Benedicte Raae

Published on

April 14, 2023

There comes a time in every project where the next feature on the roadmap is search

In this example, we’ll walk you through three different ways you can search your Xata database. The code examples are written with Gatsby Functions but are transferrable to any framework and can be viewed in this repository.

The first method available is partial match search. This involves grabbing a search term from a request and then using Xata to grab the first N accounts with either name, username, description, or location containing the search term. In the code below, a helper function is created to replace every instance of the search term with the same term enclosed in an <em> tag. This helper function is then used to add a highlighted version of the name and other values to create partial match search with highlighting.

import { contains } from "@xata.io/client";
import { getXataClient } from "../../xata";
 
const xata = getXataClient();
 
export default async function handler(req, res) {
  const { term } = req.query;
 
  const { accounts } = xata.db;
 
  const records = await accounts
    .any(
      accounts.filter("name", contains(term)),
      accounts.filter("username", contains(term)),
      accounts.filter("meta.description", contains(term)),
      accounts.filter("meta.location", contains(term))
    )
    .getMany({ pagination: { size: 40 } });
 
  const enrichedResults = records.map((record) => {
    const highlight = (match) => `<em>${match}</em>`;
    return {
      record: record,
      highlight: {
        name: record.name?.replace(term, highlight),
        username: record.username?.replace(term, highlight),
        meta: {
          description: record.meta.description?.replace(term, highlight),
          location: record.meta.location?.replace(term, highlight),
        },
      },
    };
  });
 
  res.json(enrichedResults);
}

The second method Xata provides is fuzzy full-text search. Fuzzy search involves grabbing the search term from the request and then using Xata to search for the term in the accounts table. This method is different from partial match search in that it also matches words that are not identical to the search term. In the code below use Xata's helper function, getMetadata(), to access a highlighted version of the record.

import { getXataClient } from "../../xata";
 
const xata = getXataClient();
 
export default async function handler(req, res) {
  const { term } = req.query;
 
  const results = await xata.search.all(term, {
    tables: [
      {
        table: "accounts",
        target: ["name", "username", "meta.description", "meta.location"],
      },
    ],
    fuzziness: 1,
    prefix: "phrase",
  });
 
  const enrichedResults = results.map((result) => {
    return {
      ...result,
      ...result.record.getMetadata(),
    };
  });
 
  res.json(enrichedResults);
}

Last but certainly not least, the final method is semantic (AI) search. Semantic search involves grabbing the search term from the request and transforming it into an embedding using OpenAI. Xata can then perform a vector search with the search term embedding against the embedding for each account. The embeddings are vectors of floating-point numbers, and the distance between two vectors measures their relatedness. This allows you to search for terms like the search term from the request and receive responses that are related.

import { Configuration, OpenAIApi } from "openai";
import { getXataClient } from "../../xata";
 
const xata = getXataClient();
 
const openAIConfig = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openAI = new OpenAIApi(openAIConfig);
 
export default async function handler(req, res) {
  const { term } = req.query;
 
  const response = await openAI.createEmbedding({
    input: term,
    model: "text-embedding-ada-002",
  });
  const [{ embedding }] = response.data.data;
 
  const records = await xata.db.accounts.vectorSearch("embedding", embedding, {
    size: 20,
  });
 
  const enrichedResults = records.map((record) => {
    return {
      record: record,
      ...record.getMetadata(),
    };
  });
 
  res.json(enrichedResults);
}

We hope this helped you get a sense for the different types of search that just work out of the box with Xata! Be sure to watch the video for a quick overview of the search with Xata and subscribe to our channel for similar videos.

Would you like a deep dive on any other feature? Come chat with us in Discord and let us know what you’d like to see.

Copyright © 2023 Xatabase Inc.
All rights reserved.

Product

RoadmapFeature requestsPricingStatusAI solutions