Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to display facets with count #29

Open
mario202k opened this issue Sep 23, 2022 · 8 comments
Open

How to display facets with count #29

mario202k opened this issue Sep 23, 2022 · 8 comments
Labels
question Further information is requested

Comments

@mario202k
Copy link

I'm trying to get the list of facet that I've configured as describe here. I use the new official Algolia package along with the unofficial for settings. I thought that use the official package would change the response. Despite in the console browser every thing is display as attended, the searchResponse.facets is always empty. Check the code below.

import 'dart:io';

import 'package:algolia/algolia.dart';
import 'package:algolia_helper_flutter/algolia_helper_flutter.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:masterecommerce/src/constants/app_sizes.dart';
import 'package:masterecommerce/src/exceptions/app_exception.dart';
import 'package:masterecommerce/src/features/menu/presentation/search_header.dart';
import 'package:masterecommerce/src/features/products/domain/algolia_output.dart';
import 'package:masterecommerce/src/features/products/domain/algolia_params.dart';

import '../constants/credentials.dart';
import '../features/products/domain/product.dart';
import 'firestore_service.dart';

final searchResponseProvider = StateProvider.autoDispose<SearchResponse>((ref) {
  return SearchResponse({});
});
final searchStateProvider = StateProvider.autoDispose<SearchState>((ref) {
  return const SearchState(indexName: kIndexName);
});
final algoliaInputProvider = StateProvider.autoDispose<AlgoliaInput>((ref) {
  return const AlgoliaInput();
});

final algoliaControllerProvider = StateNotifierProvider.autoDispose<
    AlgoliaController, AsyncValue<List<Product>>>((ref) {
  return AlgoliaController(
      ref: ref, algoliaInput: ref.watch(algoliaInputProvider));
});

final algoliaControllerSearchProvider = StateNotifierProvider.autoDispose<
    AlgoliaController, AsyncValue<List<Product>>>((ref) {
  return AlgoliaController(
      ref: ref,
      algoliaInput: AlgoliaInput(
          query: ref.watch(currentQuerySearchProvider), hitsPerPage: 3));
});

class AlgoliaController extends StateNotifier<AsyncValue<List<Product>>> {
  StreamSubscription? subResponses;
  StreamSubscription? subSearchState;

  late HitsSearcher searcher;
  late Algolia algoliaForSettings;
  final Ref ref;
  final AlgoliaInput algoliaInput;

  AlgoliaController({required this.ref, required this.algoliaInput})
      : super(const AsyncLoading()) {
    algoliaForSettings = const Algolia.init(
        applicationId: algoliaKeyAppId, apiKey: algoliaKeyCustom);
    searcher = HitsSearcher(
      applicationID: algoliaKeyAppId,
      apiKey: algoliaKeyCustom,
      indexName: kIndexName,
    );

    subResponses = searcher.responses.handleError((onError) {
      print(onError);
      state = AsyncError(onError);
    }).listen((searchResponse) {
      print(searchResponse.facets);
      print(searchResponse.raw["facets"]);
      ref.read(searchResponseProvider.state).update((state) => searchResponse);
      
      final products = searchResponse.hits
          .map((e) => Product.fromMapAlgolia(e.cast()))
          .toList();
      if (mounted) {
        state = AsyncValue<List<Product>>.data(products);
      }
    });

    subSearchState = searcher.state.handleError((onError) {
      print(onError);
      state = AsyncError(onError);
    }).listen((searchState) {
      ref.read(searchStateProvider.state).update((state) => searchState);
    });

    _newAlgoliaState(algoliaInput: algoliaInput);
  }

  @override
  void dispose() {
    searcher.dispose();
    subResponses?.cancel();
    subSearchState?.cancel();
    super.dispose();
  }

  Future<void> _newAlgoliaState({required AlgoliaInput algoliaInput}) async {
    state = const AsyncLoading();

    final algoliaOutput = algoliaInput.toAlgoliaOutput();
    await _setAlgoliaRanking(algoliaOutput: algoliaOutput);
    if (!mounted) {
      return;
    }
    searcher.connectFilterState(algoliaOutput.filterState);
    searcher.applyState((state) => algoliaOutput.toSearchState());
  }

  Future<void> _setAlgoliaRanking(
      {required AlgoliaOutput algoliaOutput}) async {
    AlgoliaIndexReference algoliaIndexReference =
        algoliaForSettings.instance.index("products");
    final ranking = algoliaOutput.ranking;
    if (ranking != null && ranking.isNotEmpty) {
      AlgoliaTask algoliaTask =
          await algoliaIndexReference.settings.setRanking([
        ...ranking,
        "typo",
        "geo",
        "words",
        "filters",
        "proximity",
        "attribute",
        "exact",
        "custom",
      ]).setSettings();

      await algoliaTask
          .waitTask()
          .timeout(const Duration(seconds: timeOutSecond))
          .catchError((onError) {
        if (onError is SocketException) {
          throw const AppException.noInternet();
        } else if (onError is TimeoutException) {
          throw const AppException.timeOut();
        } else {
          throw const AppException.unknown();
        }
      });
    }


    final customRanking = algoliaOutput.customRanking;

    if (customRanking != null && customRanking.isNotEmpty) {
      final AlgoliaTask algoliaTask = await algoliaIndexReference.settings
          .setCustomRanking(customRanking)
          .setSettings();

      await algoliaTask
          .waitTask()
          .timeout(const Duration(seconds: timeOutSecond))
          .catchError((onError) {
        if (onError is SocketException) {
          throw const AppException.noInternet();
        } else if (onError is TimeoutException) {
          throw const AppException.timeOut();
        } else {
          throw const AppException.unknown();
        }
      });
    }
  }
}

@aallam
Copy link
Member

aallam commented Sep 23, 2022

Hi @mario202k, SearchResponse.facets will only have values if SearchState.facets was not empty. Could you please share how do you create your SearchState? (it's toSearchState I guess)!
You can also enable logging to see exactly what query is being built and sent under the hood.
Edit: since what you need is to get and display facets, maybe you want to take a look at FacetList, here is an example of it usage :)

@mario202k
Copy link
Author

mario202k commented Sep 23, 2022

Hi @mario202k, SearchResponse.facets will only have values if SearchState.facets was not empty. Could you please share how do you create your SearchState? (it's toSearchState I guess)! You can also enable logging to see exactly what query is being built and sent under the hood. Edit: since what you need is to get and display facets, maybe you want to take a look at FacetList, here is an example of it usage :)

Thank you. I have forgotten to add facets in SearchState. :-)

@mario202k
Copy link
Author

Hi @aallam. Why when I set my facets like this : facets: ["availableSizes","availableColors"], it always display the last facets? if I remove "availableColors" it will display "availableSizes" not both?

@aallam
Copy link
Member

aallam commented Sep 26, 2022

Hey @mario202k, how are you displaying your facets lists? are you using FacetList or doing it manually ?

@mario202k
Copy link
Author

I don't know what is facets lists. For me it's "Attributes for faceting" in the console. I found a way: facets: ["*"] Like this I have all the facets that I've configured in the Algolia console. Am I right?

@VladislavFitz
Copy link
Contributor

Hi @mario202k,
The "Attributes for faceting" value is the persistent setting of your index. By setting this parameter, you specify the fields of your records you want to use for faceting. It's enough to do it just once from the dashboard.
Then, you can specify the facets values you want to fetch with each search request using the facets parameter. By default its value is ["*"] which means getting all the facets, so

I found a way: facets: ["*"] Like this I have all the facets that I've configured in the Algolia console. Am I right?

Yes, you are right.

Why when I set my facets like this : facets: ["availableSizes","availableColors"], it always display the last facets?

What do you mean by "last facets" here?

If you want to build the UI presenting the list of selectable facets, we recommend to use the FacetList component.
You can find it here with its documentation. It automatically sets the proper facets value being connected to your HitsSearcher, so you don't need to configure it manually.

@aallam aallam added the question Further information is requested label Oct 25, 2022
@gbolahan507
Copy link

@aallam pls i only need to display two facet from my atribute and have added it using filterstate but am getting empty screen
Screenshot 2023-01-27 at 10 37 50

@aallam
Copy link
Member

aallam commented Jan 27, 2023

Hi @gbolahan507, could you please share how do you display your facets ? also I would recommend enabling logging to see the actual http calls and responses for better debugging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants