Full text search with Firestore & Meili search - Implementing Full text search function in React Native app
Tags: react, reactnative, javascript, fulltextsearch
Published: 2023-11-07 03:34:43

In this post, we will be looking into how you can create a search bar interface in your React Native application. We will be creating a React Native project and will be connecting our melli search instance with our frontend app and create a nice search interface.

Lets create a react native app with the following command.

```jsx

npx react-native init yourappname
cd yourappname

```

After this lets install libraries called axios to call our APIs. We will also be installing a library called 'react-native-fast-image' which is a brilliant library to cache remote images in react native. We will also be installing lodash to use a function called debounce in Javascript.

```jsx 

npm i react-native-fast-image lodash

```

After we have installed the packages, we need to run pod install to install necessary pods so that we can run our app in iOS. There is no additional step required for android.

```jsx

pod install

```

For M1 macs, you can use the following command to install pods without any errors.

```jsx

arch -x86_64 pod install

```

Now lets get into the coding part. We are going to setup a folder called API and create a folder called Components where we will be creating our Search Interface.


Before that I am initialising axios instance to be used in our app.

```jsx


import axios from 'axios';

export const BASE_URL = 'yourapi.com';

export const instance = axios.create({
  baseURL: BASE_URL,
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
});


```

Lets setup our API functions

Now lets create our search input to get data from our API.
This will look and feel the same as the Next JS search interface we have created before

```jsx


import {instance} from './Api';

export const addResourceFromMelli = async melliSearchData => {
  const melliData = {
    index: 'movies',
    data: melliSearchData?.movieData,
  };

  try {
    await instance.post(`/mellisearch/add-document`, melliData);
  } catch (error) {
    console.log(error);
  }
};

export const searchResourcesFromMelli = async melliSearchData => {
  try {
    const melliData = {
      index: 'movies',
      search: melliSearchData?.search,
    };

    const res = await instance.post(`/mellisearch/search`, melliData);

    return res.data.hits;
  } catch (error) {
    console.log(error);

    return [];
  }
};

export const deleteResourceFromMelli = async melliSearchData => {
  const melliData = {
    index: 'movies',
    id: melliSearchData?.movieId,
  };

  try {
    await instance.post(`/mellisearch/delete-document`, melliData);
  } catch (error) {}
};



```

Now let us call the API in our debounced search input with help of lodash and retrieve data points and show case it to the frontend.

Here below is the full code of the search interface.

```jsx 

import React, {useState} from 'react';
import {
  View,
  TextInput,
  Text,
  StyleSheet,
  SafeAreaView,
  ScrollView,
  ActivityIndicator,
  Platform,
  Dimensions
} from 'react-native';

import FastImage from 'react-native-fast-image';
import {useCallback} from 'react';
import {searchResourcesFromMelli} from '../API/MellisearchAPI';
import {debounce} from 'lodash';

const screenWidth = Dimensions.get('screen').width

const SearchInterface = () => {
  const [query, setQuery] = useState('');
  const [moviesData, setMoviesData] = useState([]);

  const [searchLoader, setSearchLoader] = useState(false);
  async function searchMelli(e) {
    const melliSearch = {
      search: e,
    };

    const res = await searchResourcesFromMelli(melliSearch);

    setMoviesData(res);

    setSearchLoader(false);
  }

  const debouncedAPICall = useCallback(debounce(searchMelli, 1200), []);

  async function debouncedFullTextSearch(e) {
    setSearchLoader(true);

    setQuery(e);

    debouncedAPICall(e);
  }

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView showsVerticalScrollIndicator={false}>
        <View style={styles.center}>
          <TextInput
            style={styles.searchInput}
            value={query}
            onChangeText={debouncedFullTextSearch}
            placeholder="Search a movie..."
            placeholderTextColor={'black'}
          />
        </View>

        {searchLoader && (
          <View style={styles.searchLoader}>
            <ActivityIndicator color={'#00A9F1'} size={'large'} />
          </View>
        )}

        {moviesData?.length >= 1 ? (
          moviesData?.map(item => {
            return (
              <View style={styles.movieItem} key={item?.movieId}>
                <FastImage
                  style={styles.poster}
                  source={{
                    uri: item.posterUrl,
                    priority: FastImage.priority.high,
                  }}
                  resizeMode="cover"
                />
                <View style={styles.movieDetails}>
                  <Text style={styles.title}>{item.title}</Text>
                  <Text style={styles.description}>{item.description}</Text>
                </View>
              </View>
            );
          })
        ) : (
          <View style={styles.center}>
            {!searchLoader && (
              <View>
                <FastImage
                  source={require('../Assets/SearchMovies.png')}
                  style={styles.searchMovies}
                  resizeMode="contain"
                />
                <View style={styles.center}>
                  <Text style={styles.searchDescription}>
                    Start typing to search movies!
                  </Text>
                </View>
              </View>
            )}
          </View>
        )}
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
    padding: 20,
  },
  center: {
    alignItems: 'center',
    justifyContent: 'center',
  },
  searchInput: {
    borderWidth: 1,
    borderColor: '#00A9F1',
    borderRadius: 5,
    paddingLeft: 10,
    marginVertical: 10,
    width: 300,
    fontFamily: 'Pangram-Regular',
    fontSize: 18,
    padding: 15,
    backgroundColor: '#F9FAFC',
    color: 'black',
  },
  movieItem: {
    marginVertical: 10,
    alignItems: 'center',
    backgroundColor: 'white',
    marginHorizontal: 10,
    padding: 5,
    borderColor: '#00A9F1',
    borderWidth: 1,
  },
  poster: {
    width: Platform.OS === 'ios' ? screenWidth * 0.85 : screenWidth * 0.8,
    height: 220,
    resizeMode: 'cover',
    marginVertical: 10,
  },
  movieDetails: {
    marginLeft: 10,
  },
  title: {
    fontSize: 20,
    marginBottom: 5,
    fontFamily: 'Pangram-Bold',
    lineHeight: 26,
  },
  description: {
    fontSize: 17,
    fontFamily: 'Pangram-Regular',
    lineHeight: 26,
    marginBottom: 10,
  },
  searchDescription: {
    fontSize: 22,
    fontFamily: 'Pangram-Regular',
    marginBottom: 10,
  },
  searchLoader: {
    paddingTop: 30,
    marginTop: 20,
    marginBottom: 30,
  },
  searchMovies: {
    width: screenWidth * 0.8,
    height: 300,
  },
});

export default SearchInterface;

```

Thats it we have successfully integrated a neat search interface for our React Native app. 

You can view the complete code and see how the search interface works in a live demo below.

https://github.com/Gautham495/blog-code-snippets

https://demos.gauthamvijay.com/


This concludes the fulltext search feature with multiple technologies like Firebase, Meilisearch, Express JS, React/Next, React Native to make a end to end full text search solution.

Let me know if I can help you in this integration or you need any help in your projects. This series was made to help out developers to make a full text search functionality from the backend to the frontend in a neat concise way.