skip to Main Content

I have the following code that is working fine, I am trying to fetch about 50,000 cities in a searchable textfield using laravel but it takes alot of time and browser becoming inactive. How can I optimize so that it is fast.

public function searchCity()
{
    $country = Country::with('states.cities')->find(231);

    if ($country) {
        $cities = $country->states->flatMap->cities->pluck('name')->toArray();;
        return $cities;
    } else {
        return response()->json(['message' => 'Country not found'], 404);
    }

}

and the ajax

 $.ajax({
        method: "GET",
        url: "/searchcity",
        success: function (response) {
          townsTags = response;
          startAutocomplete(townsTags, "#search_towns");
        }
      });

2

Answers


  1. Why don’t you perform your search on the server side?

    You could send the item you’re searching as query params:

    $.ajax({
        method: "GET",
        url: "/searchcity",
        data: { query: search },
        success: function (response) {
            townsTags = response;
            startAutocomplete(townsTags, "#search_towns");
        }
    });
    

    And then search for specific result:

    public function searchCity(Request $request)
    {
        $country = Country::with('states.cities')->find(231);
    
        $query = $request->input('query');
    
        if ($country) {
            $cities = $country->states
                ->flatMap->cities
                ->pluck('name')
                ->where('name', 'like', '%' . $query . '%')
                ->toArray();
    
            return $cities;
        } else {
            return response()->json(['message' => 'Country not found'], 404);
        }
    }
    

    Also consider implementing lazy loading for the results. Display a few initial results and load more as the user scrolls down.

    Login or Signup to reply.
  2. I would also like to suggest taking the approach of letting the backend do more of the work, as 9uifranco suggests.

    Some initial thoughts and pointers;

    • The browser becoming inactive could be because of the code in startAutocomplete. While this code can probably be optimized, doing the work on the frontend will probably hurt users with slower devices.
    • If possible, I would suggest linking cities directly to countries. Not every country has states and it will make your query easier and faster.
    • When autocompleting, you probably don’t always need to display all options.
    • You might not want to start suggesting when less than 3 chars are typed. Throttle and debounce are also interesting topics to look into.
    • In case you want an autocomplete and not a search, you can replace '%' . $query . '%' with $query . '%' (removing the first %). Add an index to your database on name, and it will be much quicker.

    As for the backend query, you can limit cities in the query. No need to map.

    return City::query()
      ->where('country_id', 231)
      ->where('name', 'like', '%' . $query . '%')
      ->limit(10)
      ->pluck('name');
    

    If you still need states it can be something like

    return City::query()
      ->whereHas('state', function ($builder) {
        $builder->where('country_id', 321);
      })
      ->where('name', 'like', '%' . $query . '%')
      ->limit(10)
      ->pluck('name');
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search