Code Monkey home page Code Monkey logo

algoliasearch-helper-flutter's Introduction

Algolia Flutter Helper

📦 Packages

Package Description Version Release stage
algolia_insights Algolia Insights for Flutter pub package stable version
algolia_helper_flutter Algolia Helpers for Flutter pub package stable version

📄 Documentation

❓Getting Help

♻️ Getting involved

  • If you want to contribute please feel free to submit pull requests.
  • If you have a feature request please open an issue.
  • If you use Algolia Flutter Helper in your app, we would love to hear about it! Drop us a line on Twitter.

algoliasearch-helper-flutter's People

Contributors

aallam avatar jairofernandez avatar peetee06 avatar seafoox avatar vladislavfitz avatar wackymax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

algoliasearch-helper-flutter's Issues

V1.0.0 TypeError: null: type 'Null' is not a subtype of type 'String'

After version 1.0.0 of algolia_helper_flutter, I noticed that the hit data includes '_highlightResult', which does not match the user's model data. This results in a TypeError.

  Stream<_HitsPage> get _searchPage =>
       hitsSearcher.responses.map(_HitsPage.fromResponse);

....
class _HitsPage {
  const _HitsPage(this.items, this.pageKey, this.nbPages, this.nextPageKey);
  final List<ItemData> items;
  final int pageKey;
  final int? nextPageKey;
  final int nbPages;

  factory _HitsPage.fromResponse(SearchResponse response) {
    logger.info('>>>> data hits:${response.hits} / nbPages:${response.nbPages}');


   // TypeError : null: type 'Null' is not a subtype of type 'String'
    final List<ItemData> items =  response.hits.map(ItemData.fromJson).toList(); 


   logger.info('>>>> items hits:${items.length}');

    final bool isLastPage = response.page >= response.nbPages;
    final int? nextPageKey = isLastPage ? null : response.page + 1;

    return _HitsPage(items, response.page, response.nbPages, nextPageKey);
  }
}

Hit data

   INFO: >>>> data hits:[Hit{ json: 
    {objectID: sw7YaNb0YKwmvxJKdKxw, 
    _highlightResult: {name: {value: Rolex stock 22, matchLevel: none, matchedWords: []}, 
                                   mainCategory: {value: StockOffer, matchLevel: none, matchedWords: []}}, 
   ............. published: true, createdAt: 2023-12-11T23:45:31.509, updatedAt: 2024-04-25 22:26:44.829}}] 
  
ℹ️   INFO: >>>Has Error:TypeError: null: type 'Null' is not a subtype of type 'String'

Is it possible to send segment events data directly to Algolia?

Use case
while going through algolia documentation, came across this page, which mentions that we can configure the segment to send events data directly to algolia.

while sending events to segment we have to pass the algolia params as mentioned below:
analytics.track('Product Clicked', {
product_id: '507f1f77bcf86cd799439011',
sku: 'G-32',
category: 'Games',
name: 'Monopoly: 3rd Edition',
brand: 'Hasbro',
variant: '200 pieces',
price: 18.99,
quantity: 1,
coupon: 'MAYDEALS',
position: 3,
url: 'https://www.example.com/product/path',
image_url: 'https://www.example.com/product/path.jpg',

/// Algolia-related properties
index: 'my-algolia-index',
eventType: 'click',
queryID: 'fd0bbaadc287937s7671d00f1d053b88',
objectID: '131280270'
});

but we couldn't able to get any of the above required params from the flutter algolia plugins ( in my case I've used algolia_helper_flutter: ^0.2.3).

Ref link: https://www.algolia.com/doc/guides/sending-events/connectors/segment/

NOTE: All the segment and algolia configurations were properly done as per the documents.

Proposal
Expectation: Need the algolia params to pass along with segment events data.

Add support for "Around" queries

Is your feature request related to a problem? Please describe 🙏
Currently when using the HitSearcher you can't define any of the "around" parameters for the index search.

Describe the solution you'd like 🤔
Adding the parameters to the search state so that it is passed into the index search.

Describe alternatives you've considered ✨
Currently I have to make use of the pure dart implementation since that exposes the properties.

Additional context
None

Category page

I am looking for a way to manage product categories efficiently.
For example, I want to change the category name.
Which of the two solutions below does Algolia recommend?

For Solution A, all records must be modified to rename the category. Is it right?

For Solution B, it requires join, and I want to know if it provides this function.

스크린샷 2022-10-29 오후 8 38 15

Help filtering by array

The question exists in the docs but the docs lacks implementation specifically for Dart/Flutter. I have an array attribute 'services'. I've already added the attribute to attributesForFaceting. The 'services' attribute contains string values such as 'cleaner', 'electrician', 'plumber' and etc. I'm just not quite sure on how to implement it. Much of the code below is from the Getting Started docs, in their example, a string attribute is used. In its current state of my implementation, the StreamBuilder's snapshot is null. How does the helper know what services are there? Does it read from my index all the available services from the attribute?

  final _filterState = FilterState();

  late final _facetList = _professionalSearcher.buildFacetList(
      filterState: _filterState, attribute: 'services');

  @override
  void initState() {
    _searchTextController.addListener(() => _professionalSearcher.applyState(
        (state) => state.copyWith(query: _searchTextController.text, page: 0)));
    _searchPage.listen((page) {
      if (page.pageKey == 0) {
        _pagingController.refresh();
      }
      _pagingController.appendPage(page.items, page.nextPageKey);
    }).onError((error) => _pagingController.error = error);
    _pagingController.addPageRequestListener((pageKey) => _professionalSearcher
        .applyState((state) => state.copyWith(page: pageKey)));

    _professionalSearcher.connectFilterState(_filterState);
    _filterState.filters.listen((_) => _pagingController.refresh());

    super.initState();
   }
SizedBox(
                  height: 200,
                  child: StreamBuilder<List<SelectableItem<Facet>>>(
                      stream: _facetList.facets,
                      builder: (context, snapshot) {
                        if (!snapshot.hasData) {
                          return const SizedBox.shrink();
                        }
                        final selectableFacets = snapshot.data!;
                        return ListView.builder(
                            padding: const EdgeInsets.all(8),
                            itemCount: selectableFacets.length,
                            itemBuilder: (_, index) {
                              final selectableFacet = selectableFacets[index];
                              return CheckboxListTile(
                                value: selectableFacet.isSelected,
                                title: Text(
                                    "${selectableFacet.item.value} (${selectableFacet.item.count})"),
                                onChanged: (_) {
                                  _facetList.toggle(selectableFacet.item.value);
                                },
                              );
                            });
                      }),
                )

There's an issue with fetching data for the next page.

I've written a small sample code for testing as follows.
The issue is that it's not fetching the next page.
Can you help identify and fix any problems in the code?

class DemoView extends StatefulWidget {
  const DemoView({super.key});

  @override
  State<DemoView> createState() => _DemoViewState();
}

class _DemoViewState extends State<DemoView> {
  final PagingController<int, DemoData> _pagingController =
      PagingController<int, DemoData>(firstPageKey: 0);

  late HitsSearcher _hitsSearcher;
  Stream<_HitsPage> get _searchPage =>
      _hitsSearcher.responses.map(_HitsPage.fromResponse);

  @override
  void initState() {
    _hitsSearcher = HitsSearcher(
      applicationID: Env.algoliaApplicationID,
      apiKey: Env.algoliaApiKey, // adminKey search-only demo key
      indexName: AlgoliaCredentials.demoEcommerceIndex, // demo_ecommerce
    );

    _searchPage.listen((_HitsPage page) {
      if (page.pageKey == 0) {
        _pagingController.refresh();
      }
      logger.info('nextPageKey:${page.nextPageKey} / Page item length:${page.items.length}'); // 1 / 20
      _pagingController.appendPage(page.items, page.nextPageKey);
    }).onError((error) => _pagingController.error = error);

    _pagingController.addStatusListener((status) {
      // status:PagingStatus.ongoing
      logger.info('status:${status.toString()}');
    });
    super.initState();
  }
  @override
  void dispose() {
    _pagingController.dispose();
    _hitsSearcher.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
     appBar: AppBar(),

      body: PagedListView<int, DemoData>(
          pagingController: _pagingController,
          builderDelegate: PagedChildBuilderDelegate<DemoData>(
              noItemsFoundIndicatorBuilder: (_) => const Center(
                    child: Text('No results found')),
              itemBuilder: (_, DemoData item, __) => Container(
                    color: Colors.white,
                    height: 80,
                    padding: const EdgeInsets.all(8),
                    child: Row(
                      children: [
                        if (item.image != null)
                          SizedBox(
                              width: 50, child: Image.network(item.image!)),
                        const SizedBox(width: 20),
                        Expanded(child: Text('${item.name}'))
                      ],
                    ),
                  ))),
    );

  }
}

class _HitsPage {
  const _HitsPage(this.items, this.pageKey, this.nextPageKey);

  final List<DemoData> items;
  final int pageKey;
  final int? nextPageKey;

  factory _HitsPage.fromResponse(SearchResponse response) {
    final items = response.hits.map(DemoData.fromJson).toList();
    final isLastPage = response.page >= response.nbPages;
    final nextPageKey = isLastPage ? null : response.page + 1;

    //INFO: Algolia page: 0, nbPages: 50, isLastPage: false, nextPageKey: 1
    logger.info('Algolia page: ${response.page}, nbPages: ${response.nbPages}, isLastPage: $isLastPage, nextPageKey: $nextPageKey');

    return _HitsPage(items, response.page, nextPageKey);
  }
}

Screen.Recording.2023-11-26.at.4.35.28.PM.mov

Support algolia version 1.0.0

Is your feature request related to a problem? Please describe 🙏
The Algolia library recently moved to stable version 1.0.0.

Describe the solution you'd like 🤔
Can this lib be updated to support the latest version of the Algolia dart SDK?

Describe alternatives you've considered ✨
N/A

Additional context
N/A

when the facetlist has not and EventTracker breaks the FaceList usage

Describe the bug 🐛
when the facetlist has not and EventTracker breaks the FaceList usage

To Reproduce 🔍
Steps to reproduce the behavior:

Just try to instanciate the object like this:

late final _facetList = _productsSearcher.buildFacetList(
    filterState: _filterState,
    attribute: 'activity_type',
  );

After that when you try to access to _facetList.facets the validation try to use the getters, because the property was marked as late final, the code break.

Expected behavior 💭
Should be work with that param in null

Environment:

  • OS: MacOS Sonoma 14.2.1 (23C71)
  • Library Version 1.0.0-pre

Simple Filtering Question

I have a List<String> in my Dart code named connectionsUids, and I want HitsSearcher to return only the results whose objectID is in connectionsUids.

How do I go about it?

TODO: Add a user-agent

We want to track the adoption of this helper, so let's add a proper user-agent field.
My contribution to the community client makes it really simple.

Personalization?

I do not want to import data registered by other users.
What settings do I need for personalization?

 final HitsSearcher _addressBookSearcher = HitsSearcher.create(
      applicationID: AlgoliaCredentials.applicationID,
      apiKey: AlgoliaCredentials.apiKey,
      state:  const SearchState(
          indexName: AlgoliaCredentials.addressBookIndex,
          query: '',
         userToken: <<<<<<<< ?
      ));

I passed userToken but it doesn't work.

DataTable pagination?

I would like to implement pagination using DataTable as shown in the following image. Can you provide a sample?

Screenshot 2024-04-26 at 9 25 47 PM

Recommendation features for flutter/dart

I am currently using Algolia in my flutter app for the search and Recommend feature. There are different package for searching features one is from Algolia itself and another package is provided by knoxpo.com which is good for the search feature. but there is no mention of how can I fetch Recommend models data in Algolia . I request Algolia team to add recommend features for flutter and more support.

I have also use official API to fetch data from Recommend feature using different trained model. but somehow it providing me a slow response and also I don't think this is the appropriate thing to do. It will be great if any one from the community can help me with this if there is any alternative.

Local Cache Feature

Hello, I'm new to Algolia and I'm curious if it has a feature that stores data on the device locally to improve the performance. I couldn't find any information on caching in the documentation.

Does this package always request data from the server every time for the same query?

[Filter Snapshot]: Unable to read the 'value' value from the filter snapshot.

Unable to read the 'value' value from the filter snapshot.

 final FilterState _filterState = FilterState();
   Filters filterList = _filterState.snapshot();
   Set<Filter> fs = filterList.getFilters();
logger.info('filter>>>>:${fs.toString()}');

value: product
INFO: filter>>>>:{FilterFacet{attribute: type, isNegated: false, value: product, score: null}}

I want to display the selected filter value list in the UI, but I am unable to read the 'value'.

Toggle 2 facets with forEach doesn't listen both changes

Describe the bug 🐛
When you try to toggle a facet almost at the same time that the previous one, the filter state who listen filters, detects only one instead of adding the second one.

To Reproduce 🔍
Steps to reproduce the behavior:

  1. Create a list of facets to toggle
  2. Toggle facets which are only different than the previous one

Expected behavior 💭
Both Facets should be added

Here is an example of my code:

  var _previousFacets = <SelectableFilter>[];

  @override
  void setFacet(List<SelectableFilter> facet) {
    if (_previousFacets.isEmpty) {
      _previousFacets = facet;
      facet.where((element) => element.selected).forEach(
        (element) {
          _facetList.toggle(element.name);
        },
      );
      return;
    }

    facet.asMap().forEach((index, currentElement) {
      final previousElement = _previousFacets[index];
      if (previousElement.selected != currentElement.selected) {
        _facetList.toggle(currentElement.name);
      }
    });

    _previousFacets = facet;
  }

And here is what the listener of _filterState.filters prints

flutter: Filters{facetGroups: {FilterGroupID{ name: containedBranches, operator: FilterOperator.or}: {FilterFacet{attribute: containedBranches, isNegated: false, value: FILTER1, score: null}}}, tagGroups: {}, numericGroups: {}, hierarchicalGroups: {}}
flutter: Filters{facetGroups: {FilterGroupID{ name: containedBranches, operator: FilterOperator.or}: {FilterFacet{attribute: containedBranches, isNegated: false, value: FILTER2, score: null}}}, tagGroups: {}, numericGroups: {}, hierarchicalGroups: {}}

Instead of:
flutter: Filters{facetGroups: {FilterGroupID{ name: containedBranches, operator: FilterOperator.or}: {FilterFacet{attribute: containedBranches, isNegated: false, value: FILTER1, score: null}, FilterFacet{attribute: containedBranches, isNegated: false, value: FILTER2, score: null}}}, tagGroups: {}, numericGroups: {}, hierarchicalGroups: {}}

Rerun of the search request

Describe the bug 🐛
Im faced with issue when Im sending the same search query using 'searcher.rerun()' method Im not receiving any results, and it can only be fixed by sending another request with new text. It was working while ago but now it's not. I also tried to do it with searcher.query('query') method but it's also not working

To Reproduce 🔍
Steps to reproduce the behavior:

  1. Send search query
  2. Receive search results
  3. Send the same search query again
  4. No results, no errors from searcher

Expected behavior 💭
Receiving search results after sending the same search query again

Screenshots 🖥
image

Environment:

  • OS: iOS 16.4
  • Library Version 0.2.3, 0.3.1

Additional context

Unhandled Exception: FileSystemException: Creation failed, path = 'algolia' (OS Error: Read-only file system, errno = 30)

When I run the app on the iOS emulator(iPhone 14), I encounter the following error. Do you know how to resolve it? Just to note, there are no issues when running it on the web.

algolia_helper_flutter: ^0.3.2

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: FileSystemException: Creation failed, path = 'algolia' (OS Error: Read-only file system, errno = 30)
#0      _checkForErrorResponse (dart:io/common.dart:55:9)
#1      _Directory.create.<anonymous closure> (dart:io/directory_impl.dart:114:9)
<asynchronous suspension>
#2      BackendManager.open (package:hive/src/backend/vm/backend_manager.dart:34:7)
<asynchronous suspension>
#3      HiveImpl._openBox (package:hive/src/hive_impl.dart:101:21)
<asynchronous suspension>
#4      HiveImpl.openBox (package:hive/src/hive_impl.dart:142:12)
<asynchronous suspension>
#5      UserTokenStorage.read (package:algolia_insights/src/user_token_storage.dart:101:17)
<asynchronous suspension>
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: FileSystemException: Creation failed, path = 'algolia' (OS Error: Read-only file system, errno = 30)
#0      _checkForErrorResponse (dart:io/common.dart:55:9)
#1      _Directory.create.<anonymous closure> (dart:io/directory_impl.dart:114:9)
<asynchronous suspension>
#2      BackendManager.open (package:hive/src/backend/vm/backend_manager.dart:34:7)
<asynchronous suspension>
#3      HiveImpl._openBox (package:hive/src/hive_impl.dart:101:21)
<asynchronous suspension>
#4      HiveImpl.openBox (package:hive/src/hive_impl.dart:142:12)

Flutter doctor

[✓] Flutter (Channel stable, 3.10.4, on macOS 13.0 22A380 darwin-arm64 (Rosetta), locale ko-KR)
    • Flutter version 3.10.4 on channel stable at /Users/patrick386/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 682aa387cf (4 days ago), 2023-06-05 18:04:56 -0500
    • Engine revision 2a3401c9bb
    • Dart version 3.0.3
    • DevTools version 2.23.1

[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
    • Android SDK at /Users/patrick386/Library/Android/sdk
    • Platform android-33, build-tools 32.1.0-rc1
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14C18
    • CocoaPods version 1.12.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] IntelliJ IDEA Ultimate Edition (version 2022.2.4)
    • IntelliJ at /Applications/IntelliJ IDEA.app
    • Flutter plugin version 74.0.2
    • Dart plugin version 222.4582

[✓] VS Code (version 1.71.1)
    • VS Code at /Users/patrick386/Downloads/Visual Studio Code.app/Contents
    • Flutter extension version 3.58.0

[✓] VS Code (version 1.75.1)
    • VS Code at /Users/patrick386/Visual Studio Code.app/Contents
    • Flutter extension version 3.58.0

[✓] Connected device (3 available)
    • iPhone 14 (mobile) • 35FC9EF0-A1B7-4E3F-988B-6488783A5868 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-2 (simulator)
    • macOS (desktop)    • macos                                • darwin-arm64   • macOS 13.0 22A380 darwin-arm64 (Rosetta)
    • Chrome (web)       • chrome                               • web-javascript • Google Chrome 114.0.5735.106

[✓] Network resources
    • All expected network resources are available.

Dependency Conflict with `algolia_helper` and `http` in `algolia_helper_flutter`

Description:

I am currently using the algolia_helper_flutter package in my bizbhaarat project. During the installation of algolia_helper_flutter, I encountered a dependency version solving failure. The issue arises when I try to run flutter pub add algolia_helper_flutter.

As per my understanding, the algolia_helper_flutter package has different versions depending on various versions of algolia_helper.

For algolia_helper_flutter:

  • Versions <0.1.2 depend on algolia_helper ^0.1.0
  • Versions >=0.1.2 and <0.1.3 depend on algolia_helper ^0.1.2
  • Versions >=0.1.3 and <0.1.4 depend on algolia_helper ^0.1.3
  • Versions >=0.1.4 and <0.1.5 depend on algolia_helper ^0.1.4
  • Versions ^0.1.5 depend on algolia_helper ^0.1.5
  • Versions >=0.2.0 and <0.2.1 depend on algolia_helper ^0.2.0
  • Versions >=0.2.1 and <0.2.3 depend on algolia_helper ^0.2.1
  • Versions >=0.2.3 and <0.3.2 depend on algolia_helper ^0.2.3
  • Versions >=0.3.2 depend on algolia_helper ^0.3.2

This results in a requirement of algolia_helper ^0.1.0 or >=0.2.0 <0.3.0 or ^0.3.2 for every version of algolia_helper_flutter.

Additionally, every version of algolia_helper depends on algolia ^1.1.1 which in turn depends on http ^0.13.0.

The problem arises as my project bizbhaarat depends on http ^1.0.0 and algolia_helper_flutter. The dependency version of http in bizbhaarat and algolia_helper_flutter are conflicting.

This leads to a version solving failure.

Expected Outcome:

Ideally, algolia_helper_flutter should be compatible with http ^1.0.0 or higher versions.

Actual Outcome:

algolia_helper_flutter is not compatible with http ^1.0.0 causing version solving failure.

Steps to reproduce:

  1. Create a new flutter project.
  2. Add http: ^1.0.0 and algolia_helper_flutter: any in the pubspec.yaml file.
  3. Run the flutter pub get command.
  4. Observe the version solving failure.

Please let me know if there is a workaround for this or if an update to address this issue is planned. Thank you for your assistance!

MultiSearchers

Is your feature request related to a problem? Please describe 🙏
I'm trying to query two indexes, with requires MultiSearcher method.

Describe the solution you'd like 🤔
A need for the MultiSearcher method since HitsSearcher method doesn't handle multiple indexes.

Describe alternatives you've considered ✨
I've tried different ways, but none works.

Additional context
Add any other context or screenshots about the feature request here.
image

How to display facets with count

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();
        }
      });
    }
  }
}

Results stream not updated after network access restored

I implemented the code in the article, Algolia getting started with Flutter Helper and have run into an issue. When putting the Android emulator into airplane mode and scrolling down, the PageController returns an error as expected. For PagedChildBuilderDelegate I have defined a newPageErrorIndicatorBuilder handler. I allow the user to refresh and call using _pagingController.retryLastFailedRequest() to retry the query. The page request listener is invoke and executes a applyState((state) => state.copyWith(page: pageKey)).

For some reason the response stream never updates, so my UI is hung displaying the circular progress indicator. Prior to executing the refresh, I turned off airplane mode so the network connection was restored. I would have expected the response stream to update with the next page of results, yet nothing happens. I also would have expected some sort of stream update even if the phone was still in airplane mode.

Am I missing something here? I have debugged this and the code execution path is always the same, minus the stream never updates after it encounters the network error for the first time.

Going into airplane mode prior to the first query does work, i.e., firstPageErrorIndicatorBuilder. When restoring the network and refreshing the query causes the response stream to update and the search results are retrieved.

Thanks.

Multi-index search?

Hi,
Multi-index search function is required.
Is this package provided them?

FilterState.filters stream not updated on FilterState.clear

Describe the bug 🐛

I have a FilterState and listen to the filterState.filters stream for updates. When adding filters i receive updates, but not when calling filterState.clear.

To Reproduce 🔍

  1. create a FilterState instance
  2. listen to the FilterState.filters stream using a StreamBuilder
  3. add filters and confirm we receive stream updates
  4. add a button which calls filterState.clear and confirm the StreamBuilder does not receive an update

Expected behavior 💭

I'd expect StreamBuilder to receive an update when calling filterState.clear

Screenshots 🖥

Environment:

  • OS: iOS
  • Library Version ^0.2.3

Additional context

flutter --version

Flutter 3.7.6 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 12cb4eb7a0 (8 days ago) • 2023-03-01 10:29:26 -0800
Engine • revision ada363ee93
Tools • Dart 2.19.3 • DevTools 2.20.1

Starter Code in Algolia's Getting started with Flutter Helper Documentation throws an exception

Describe the bug 🐛
I was just playing around with the Getting Started with Flutter Helper Documentation, and when I started to use my own index it's been giving me Response ({"status":422,"message":"No events to process"}). What I understood was that there is something wrong with the response. I kept trying to backtrack doing the steps in the documentation over and over again to find exactly what this exception means. The first issue I have is that after following section 1 and 2, if you type a couple of characters in the searchbar and when the hits reaches 0 it throws an exception with the same 422 error. My guess is it doesn't know how to handle when there is no data in the response?

At the bottom of the documentation is a final version of the code, copied and pasted that to try to see if the same thing happens and it does, also I had to remove filtering as Facet type is not found or defined. This is not the main issue that I have but I feel like it could be related to the issue I was having when I replaced the mock data with my own index as it was giving the same response error.

To Reproduce 🔍
https://www.algolia.com/doc/guides/building-search-ui/getting-started/flutter/

The exact code that you'd get after following the documentation from 1 to 2

import 'package:algolia_helper_flutter/algolia_helper_flutter.dart';
import 'package:flutter/material.dart';

class SearchMetadata {
  final int nbHits;

  const SearchMetadata(this.nbHits);

  factory SearchMetadata.fromResponse(SearchResponse response) =>
      SearchMetadata(response.nbHits);
}

class TestSearchPage extends StatefulWidget {
  const TestSearchPage({super.key, required this.title});

  final String title;

  @override
  State<TestSearchPage> createState() => _TestSearchPageState();
}

class _TestSearchPageState extends State<TestSearchPage> {
  final _searchTextController = TextEditingController();

  final _productsSearcher = HitsSearcher(
      applicationID: 'latency',
      apiKey: '927c3fe76d4b52c5a2912973f35a3077',
      indexName: 'STAGING_native_ecom_demo_products');

  Stream<SearchMetadata> get _searchMetadata =>
      _productsSearcher.responses.map(SearchMetadata.fromResponse);

  @override
  void initState() {
    super.initState();
    _searchTextController
        .addListener(() => _productsSearcher.query(_searchTextController.text));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Algolia & Flutter'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            SizedBox(
                height: 44,
                child: TextField(
                  controller: _searchTextController,
                  decoration: const InputDecoration(
                    border: InputBorder.none,
                    hintText: 'Enter a search term',
                    prefixIcon: Icon(Icons.search),
                  ),
                )),
            StreamBuilder<SearchMetadata>(
              stream: _searchMetadata,
              builder: (context, snapshot) {
                if (!snapshot.hasData) {
                  return const SizedBox.shrink();
                }
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text('${snapshot.data!.nbHits} hits'),
                );
              },
            )
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _searchTextController.dispose();
    _productsSearcher.dispose();
    super.dispose();
  }
}

Stack

RetryStrategy.execute (c:\Users\...\AppData\Local\Pub\Cache\hosted\pub.dev\algolia_client_core-0.3.0\lib\src\transport\retry_strategy.dart:64)
<asynchronous gap> (Unknown Source:0)
InsightsClient.pushEvents (c:\Users\...\AppData\Local\Pub\Cache\hosted\pub.dev\algolia_client_insights-0.3.0\lib\src\api\insights_client.dart:170)
<asynchronous gap> (Unknown Source:0)
AlgoliaEventService.send.<anonymous closure> (c:\Users\...\AppData\Local\Pub\Cache\hosted\pub.dev\algolia_insights-0.2.2\lib\src\algolia_event_service.dart:0)
<asynchronous gap> (Unknown Source:0)

Environment:

  • OS: Windows 10
  • algolia_helper_flutter: ^0.5.0
  • Flutter 3.13.6
  • Dart 3.1.3

FacetList.eventTracker.clickedFilters throws 422 syntax error

Describe the bug 🐛
Insights Events API request payload body has a syntax error
AlgoliaApiException{statusCode: 422, error:
{"status":422,"message":"Syntax error near: \"Facet Value\", expected a filter"}}:

To Reproduce 🔍
Steps to reproduce the behavior:

  1. click Facet List and toggle selection of a facet filter value
  2. eventTracker is enabled

Expected behavior 💭

FacetList.eventTracker.clickedFilters(
        eventName: 'testing clicked filter',
        values: ["facet_name:"+Uri.encodeComponent(facet_value)]);

Screenshots 🖥
If applicable, add screenshots to help explain your problem.

Environment:

  • all
    Additional context
    Add any other context about the problem here.

HitSearcher is fetching data from Algolia already when just initially adding a stream listener

Describe the bug 🐛

TL;DR:
Algolia backend gets called with a search immediately after adding a listener function to the response stream, even before actively starting a search by executing the hitsSearcher.query(String query) method.

Long version:

I'm following Getting started with Flutter Helper, but want to use algolia_helper_flutter without infinite_scroll_pagination and following clean architecture using the BLoC pattern.

I'm initializing the HitsSearcher in my BLoC constructor the common way like this:

_productsSearcher = HitsSearcher(applicationID: 'latency',
                                        apiKey: '927c3fe76d4b52c5a2912973f35a3077',
                                        indexName: 'STAGING_native_ecom_demo_products');

I also want to add a listener to the responses stream as soon as responses are coming in after queries.

I can do that with

_productsSearcher.responses.listen(_searchPageListener);

⚠️ As soon as I call listen() to add the listener function, HitsSearcher executes a call to the Algolia backend. This I don't want, because I'm still in the setup phase in my BLoC constructor.

This call is happening because when accessing the responses stream, this code is executed: searchService.search(req.state):

.switchMap((req) => Stream.fromFuture(searchService.search(req.state)))

..which in turn executes this:

To Reproduce 🔍

  1. Initialize HitsSearcher()
  2. Add a listener function to the response stream
  3. ❌ See listener function being called immediately, even without a call to hitsSearcher.query() before.

Expected behavior 💭

Algolia search does not get called immediately when adding the response listener, but only after calling searcher.query()

Environment:

  • OS: Android API 33
  • Library Versions
    • algolia_helper_flutter: 0.5.0 and algolia_helper_flutter: ^1.0.0-pre as well

Additional context

Thanks for supporting Flutter ❤️

Algolia Timeout Exception

Describe the bug 🐛
From time to time we are facing this AlgoliaTimeoutException.

To Reproduce 🔍
No clear steps to reproduce

Expected behavior 💭
Connection timeout shouldn't be thrown.

Screenshots 🖥

SC Screenshot 2024-03-11 at 9 01 36 AM

Environment:

  • OS: iOS, Android both
  • Library Version. 1.0.0-pre

Additional context

Error Log

Crashlytics - Stack trace

Application: com.alhabibpharmacy.staging

Platform: android

Version: 1.0.193 (194)

Issue: 01b5f1c27da7a2f1b52fd2c2d9bb02f2

Session: 65EEEA050020000105663FA47CE60191_DNE_7_v2

Date: Mon Mar 11 2024 05:50:08 GMT-0600 (Mountain Daylight Time)

Non-fatal Exception: io.flutter.plugins.firebase.crashlytics.FlutterError: Exception: UnreachableHostsException{errors: [AlgoliaTimeoutException{error: DioException [connection timeout]: The request connection took longer than 0:00:02.000000 and it was aborted. To get rid of this exception, try raising the RequestOptions.connectTimeout above the duration of 0:00:02.000000 or improve the response time of the server.}, AlgoliaTimeoutException{error: DioException [connection timeout]: The request connection took longer than 0:00:02.000000 and it was aborted. To get rid of this exception, try raising the RequestOptions.connectTimeout above the duration of 0:00:02.000000 or improve the response time of the server.}, AlgoliaTimeoutException{error: DioException [connection timeout]: The request connection took longer than 0:00:02.000000 and it was aborted. To get rid of this exception, try raising the RequestOptions.connectTimeout above the duration of 0:00:02.000000 or improve the response time of the server.}, AlgoliaTimeoutException{error: DioException [connection timeout]: The request connection took longer than 0:00:02.000000 and it was aborted. To get rid of this exception, try raising the RequestOptions.connectTimeout above the duration of 0:00:02.000000 or improve the response time of the server.}]}
at AlgoliaHitsSearchService._disjunctiveSearch(algolia_hits_search_service.dart:94)

'demo_ecommerce' data search error: Method not allowed with this API key

Algolia demo data:
To read Algolia 'demo_ecommerce,' which key should I use? I've tried both the admin key and the search key, but I couldn't read the data.
Thank you.

  HitsSearcher hitsSearcher = HitsSearcher(
    applicationID: Env.algoliaApplicationID,
    apiKey: Env.algoliaAdminKey, // adminKey 
    indexName: AlgoliaCredentials.demoEcommerceIndex, // demo_ecommerce
  );

image

image

search key: demo_ecommerce
image

Getting QueryID for events

I'm having trouble getting the queryId from HitsSearch in my Flutter App (Im using Flutter Helper and Algolia Insights as docs recommend).

In my pubspec.yaml:

algolia_helper_flutter: ^1.0.0-pre
algolia_insights: ^1.0.0-pre

This is how I set my HitsSearcher

final _productsSearcher = HitsSearcher(
        applicationID: Environment.algoliaApplicationId,
        apiKey: Environment.algoliaSearchOnlyKey,
        indexName: Environment.algoliaProductsHitsIndex,
        insights: true, ← Here activating insights to get queryId in every query.
);

Also added this in the paginationController to see if it works:

_pagingController.addPageRequestListener(
        (pageKey) => _productsSearcher.applyState(
               (state) => state.copyWith(
                     clickAnalytics: true, ← Here activating click analytics
                     analytics: true, ← Here activating analytics
                     page: pageKey,
                     ruleContexts: [widget.algoliaContext],
               ),
        ),
);

Now, the problem is that, as defined in the SearchResponse model from the Flutter Helper package, it should have (if the setup is ok) a response.queryID.

This parameter is optional in the model, but can't get it to be returned.

Also, I found out that _productSearcher has a field called queryId (which is also optional) and again, cant get it to be else than null.

So, after tryng to find queryId I found out that there are more than one place where it can be reached from. But from any of those I always get a null as queryId.

This is a mandatory input for the click and conversion events after search. Which are really important for the personalization. So i beleive there ir something missing from my configs. But docs for this functionality is still in progress i assume.

Expected behavior 💭

  • Receive queryId after every search

Environment:

  • OS: Develping in windows - Testing in android.
  • Library Version 1.0.0-pre and 0.5.0

It is not updated immediately.

When you add or modify record, it is not updated immediately.
Can you improve this?
Thank you.

flutter web
algolia_helper_flutter: ^0.2.3


  late HitsSearcher hitSearcher;
  final TextEditingController searchText = TextEditingController();
  PagingController<int, LabelData> pagingController =
      PagingController(firstPageKey: 0);
  Stream<HitsPage> get _searchPage =>
      hitSearcher.responses.map(HitsPage.fromResponse);

  @override
  void initState() {
    /// Algolia DB 검색
    Account account = ref.read(Dependency.account);
    hitSearcher = HitsSearcher.create(
        applicationID: AlgoliaCredentials.applicationID,
        apiKey: account.securedKey!,
        state: const SearchState(
          indexName: AlgoliaCredentials.addressLabelIndex,
          //query: '',
        ));

    searchText.addListener(() {
      hitSearcher.query(searchText.text);
      pagingController.refresh();
    });

    _searchPage.listen((HitsPage page) {
      if (page.pageKey == 0) {
        pagingController.refresh();
      }
      pagingController.appendPage(page.items, page.nextPageKey);
    }).onError((error) => pagingController.error = error);

    pagingController.addPageRequestListener((int pageKey) {
      /// data fetch
      hitSearcher
          .applyState((SearchState state) => state.copyWith(page: pageKey));
    });
    super.initState();
  }

  @override
  void dispose() {
    hitSearcher.dispose();
    searchText.dispose();
    super.dispose();
  }

  Future<void> _refresh() async {
    hitSearcher.query('');
    Future.delayed(const Duration(seconds: 1), () {
      pagingController.refresh();
    });

    if (mounted) {
      return Future<void>.value();
    }
  }

 @override
  Widget build(BuildContext context) {
.....

              PagedSliverList<int, LabelData>(
                        pagingController: pagingController,
                        builderDelegate: PagedChildBuilderDelegate<LabelData>(
                          noItemsFoundIndicatorBuilder: (_) =>
                              NoItemFoundMessageView(),
                          itemBuilder: (BuildContext context, LabelData item,
                                  int index) =>
                              Center(
                            child: SizedBox(
                              width: 700.0,
                              child: ListTile(
                                leading: const Icon(FluentIcons.tag_24_regular),
                                title: Text(
                                  item.name,
                                  style: context.titleMedium,
                                ), ),
                            ),
                          ),
                        ),
                      )
2022-12-20.6.16.32.mov

Algolia search for flutter web

Hi
I need to build a search for an e commerce website using Algolia and the library is not compatible with flutter web! So can you suggest me any workaround or any time by which we can get the Algolia search for web?

Unsafe header Issue with HitsSearcher

Describe the bug 🐛
Every time I execute a search I get an error in the chrome dev console. Only on chrome, nothing noted on Firefox. The code I use to execute a search is below.
To Reproduce 🔍
The first time the page loads I this runs, I am also using the bloc architecture. I get this issue whether I use HitsSearcher then use applyState or the factory method like below.

      final _searcher = HitsSearcher.create(
        applicationID: ALGOLIA_APPLICATION_ID,
        apiKey: ALGOLIA_INVOICES_INDEX_KEY,
        state: SearchState(
          indexName: ALGOLIA_INVOICES_INDEX_NAME,
          page: page,
          hitsPerPage: 10,
        ),
      );

Deploy it to Chrome

Expected behavior 💭
In the web browser Console

Refused to set unsafe header "User-Agent"

Screenshots 🖥

Environment:

  • OS: [5.15.78-1-MANJARO]
  • Browser: [Chrome Chrome Version 108.0.5359.71 (Official Build) (64-bit)]
  • Library Version [algolia_helper_flutter: ^0.2.1]

Additional context
Add any other context about the problem here.

filterOnly ?

Cloud functions

  • IndexName: company
  • filterOnly(status)
  • filterOnly(published)

attributesForFaceting: [ ‘filterOnly(status)’, ‘filterOnly(published)’, ],

client:

  HitsSearcher hitsSearcher = HitsSearcher.create(
      applicationID: AlgoliaCredentials.applicationID,
      apiKey: AlgoliaCredentials.apiKey,
      state:  const SearchState(
        indexName: 'company',
        facetFilters: ['published:true'],
        //query: '',
      ));

I want to create a page that has status ‘processing’ and published ‘true’. How do I do that?

Algolia Events triggered from Segment but not shown in algolia dashboard

I have used the algolia_helper_flutter: ^0.3.1 as the zip and integrated directly to the project, because the plugin is not merged properly so we don't have the click anayltics param.After integrating it directly i can trigger the event of segment using algolia params also.

Segment.track(eventName: 'test', options: {
'product_id': '507f1f77bcf86cd799439011',
'sku': 'G-32',
'category': 'Games',
'name': 'Monopoly: 3rd Edition',
'brand': 'Hasbro',
'variant': '200 pieces',
'price': 18.99,
'quantity': 1,
'coupon': 'MAYDEALS',
'position': 3,
'url': 'https://www.example.com/product/path',
'image_url': 'https://www.example.com/product/path.jpg',

//algolia params
'index': 'my-algolia-index',
'eventType': 'click',
'queryID': _shoeSearcher.responses,
'objectID': '131280270'
});

Algolia Events triggered from Segment but not shown in algolia dashboard

I have used the algolia_helper_flutter: ^0.3.1 as the zip and integrated directly to the project, because the plugin is not merged properly so we don't have the click anayltics param.After integrating it directly i can trigger the event of segment using algolia params also.

Segment.track(eventName: 'test', options: {
'product_id': '507f1f77bcf86cd799439011',
'sku': 'G-32',
'category': 'Games',
'name': 'Monopoly: 3rd Edition',
'brand': 'Hasbro',
'variant': '200 pieces',
'price': 18.99,
'quantity': 1,
'coupon': 'MAYDEALS',
'position': 3,
'url': 'https://www.example.com/product/path',
'image_url': 'https://www.example.com/product/path.jpg',

//algolia params
'index': 'my-algolia-index',
'eventType': 'click',
'queryID': _shoeSearcher.responses,
'objectID': '131280270'
});

In segment the event delivered successfully, but i cannot see any trace in the algolia dashboard.

Screenshot 2023-05-26 at 1 51 12 PM Screenshot 2023-05-24 at 6 41 49 PM

Am expecting to visible the event in algolia.

Selected Facet value is not applied to search filter after toggling another Facet

Describe the bug 🐛
When having two Facets that are filterable, the state of a selected facet is lost when selecting another facet.

To Reproduce 🔍
I followed the steps in the getting started example exactly. With this setup, the following steps show the issue

  • Select a value of Facet A and apply filter
  • Select a value of Facet B and apply filter
  • Unselect the selected value of Facet B and apply filter
    -> Found results are completely unfiltered, filter state of Facet A is lost

Note, the checkbox of the selected value for Facet A is still checked.

Expected behavior 💭
After the above steps, it should still filter based on the selected value of Facet A

Environment:

  • OS: tested on iOS and Macos
  • Library Version 0.5.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.