Facet search on collection not working properly on clients device

Hello guys,

I hope you are doing well. I have one strange issue which I can’t explain.
I am using following code snippet to filter my search result on a particular collection

var filterState = FilterState()
 filterState = setupFilter(indexName: "challenges", challengeState: SELECTED_CHALLENGE_STATE, userObject: selectedUserObject)
        
myChallengesIndexSearcher.connectFilterState(filterState)
myChallengesIndexSearcher.query = searchBarController.searchBar.text
myChallengesIndexSearcher.search()

This query works without any issues for me. I get the results I want. However, a client reports that he sees to many results on the screen. When I got a screenshot of the problem, I could see that every object from the “challenges” collection was fetched. But the ideal case is just to load the own challenges only.

I created two test accounts on the same code basis and used two different devices. In both cases, everything worked as expected.

Could you guys give me any hint about possible problems which I may have not checked? Was there any known issue like this before? How could I activate the logging on the UI dashboard to check what is happening with the InstantSearch query?

Thanks!

Hi @kaan548 . This is definitely surprising.

Unfortunately, there’s nothing obvious that comes to my mind that would explain the issue.
The most likely explanation would be that SELECTED_CHALLENGE_STATE would be empty.

How could I activate the logging on the UI dashboard to check what is happening with the InstantSearch query?

Logging is activated by default, we keep and display the last 1000 logs of your application. You can see index specific logs in your dashboard > Indices > Logs tab.
However, it’s often inconvenient to find a specific call if there’s a bit of traffic on your site.

Assuming your client is on desktop and happy to help you investigate, I’d rather suggest you to ask your client to send you the parameters sent to Algolia:

  • Open her browser’s developer tools with Right click anywhere in the page > Inspect Element
  • Open the Network tab
  • Type her request in the site’s input
  • Isolate the call to Algolia (it should look like query?x-algolia-agent...)
  • Copy as Fetch and send it to you

To verify the theory of an empty filter, I would also suggest using a reporting tool to isolate those cases.

if (!SELECTED_CHALLENGE_STATE) {
  // Report error with context
}
if (!selectedUserObject) {
  // Report error with context 
}

Hello @Jerska,

thanks for the reply. You are right. SELECTED_CHALLENGE_STATE is empty. Could you enlighten me, please? Why is that causing the strange behavior on the client but working as expected for me?

Thanks!

Hi again @Jerska,

so the issue always appears when the app is used from the app store. But if I deploy the app using xcode on my mobile device, everything works as expected. This makes the whole thing a more strange to me. What could be the cause?

I mean, I use the release branch to deploy it on my mobile device. And the code base of that release branch matches the version of the app in the store.

And actually, I can’t see anything in the logs :frowning:
Once I try to debug it on my device, the downloaded app gets overwritten and everything works as expected but when I download it from the app store again, the facet filter doesn’t work.

Hi @kaan548 ,

Do you mean that SELECTED_CHALLENGE_STATE value is always empty, but you get a filtered result while debugging with Xcode? Did you discover it from the logs or while debugging?

Hi @vladislav.fitc ,

I found out that SELECTED_CHANGE_STATE variable has actually no impact even if it empty.
in the setupFilter()… method, I just build a query based on the value of the SELECTED_CHANGE_STATE value. So there is basically an if-clause .

if it is empty, in the end we just have a facet filter looking like [organizerId:XXXX]
A very simple one just to fetch user related objects from the indices.

However, since it is very simple, I do not understand why I am facing this only when downloading the app from the store. Once I try to debug it on code level in xcode, everything works as expected.

And yes, I noticed that while debugging. When the build is triggered from xcode and deployed on the mobile device, it just works!

Many thanks in advance!
// Kaan

Maybe your organizerId facet filter value is not set correctly in the release version of the app somehow?
Try to hardcode this value and publish a Testflight build to check if the problem is still here.

Yes, I will verify that and update my findings here. Many thanks for the assistance!

Hello @vladislav.fitc,

please help :frowning: I tried couple of things, even hard coded the value for organizerId and tested via test flight. Issue still appears.

To be on the safe side, I’ll show you all my code snippet:

    class MyChallengesView: AbstractChallengesView, HitsController {

    var singleIndexHitsInteractor: HitsInteractor<JSON> = .init(infiniteScrolling: .on(withOffset: 20), showItemsOnEmptyQuery: true)
    public weak var hitsSource: HitsInteractor<JSON>?
    
    let myChallengesIndexSearcher = SingleIndexSearcher(appID: ApplicationID(rawValue: APPID), apiKey: APIKey(rawValue: APIKEY), indexName: "challenges")
..
 private func searchCreatedChallenges() {
    addSubview(activityView)
    bringSubviewToFront(activityView)
      
    var filterState = FilterState()
    filterState = setupFilter(indexName: "challenges", challengeState: SELECTED_CHALLENGE_STATE, userObject: selectedUserObject)
            
    myChallengesIndexSearcher.connectFilterState(filterState)
    myChallengesIndexSearcher.query = searchBarController.searchBar.text
            
    myChallengesIndexSearcher.search()
}
  ... 
class AbstractChallengesView: UIView {
 public func setupFilter(indexName:String, challengeState:String, userObject:User) ->FilterState {
    let filterState = FilterState()
       
    let currentDate = Date()
    let currentDateIntValue = Int(currentDate.timeIntervalSince1970)
       
    if (indexName == "challenges") {
        filterState[or: "groupName"].add(Filter.Facet(attribute: "organizerId", stringValue: userObject.id))
    }
           
    else if (indexName == "participation" || indexName == "winnings") {
        filterState[or: "groupName"].add(Filter.Facet(attribute: "userId", stringValue: userObject.id))
    }

    else {
        filterState[or: "groupName"].add(FacetFilter(attribute: "supporterId", stringValue: userObject.id))
    }
       
    if (challengeState == "Active") {
        filterState[and: "groupName"].add(NumericFilter(attribute: "beginningTimeStamp",operator: .lessThan, value: Double(currentDateIntValue)),
                                           Filter.Numeric(attribute: "endingTimeStamp",operator: .greaterThan, value: Double(currentDateIntValue)))
    }
                  
    else if (challengeState == "Finished") {
        filterState[and: "groupName"].add(NumericFilter(attribute: "endingTimeStamp",operator: .lessThan, value: Double(currentDateIntValue)))
    }
       
    else if (challengeState == "AboutToStart") {
        filterState[and: "groupName"].add(NumericFilter(attribute: "beginningTimeStamp",operator: .greaterThan, value: Double(currentDateIntValue)))
    }
                       
    return filterState
}

To be precise about the what I did, I hard coded the value for organizerId like

filterState[or: "groupName"].add(Filter.Facet(attribute: "organizerId", stringValue: "XXXX"))

When I deployed the app on the mobile device from Xcode, I got no results (as expected). When I published the app to iTunes connect and tested it via Test Fight, I got all objects from the challenges index.

Furthermore, I also removed

myChallengesIndexSearcher.query = searchBarController.searchBar.text

since

searchBarController.searchBar.text

was empty anyway when I am doing the searches. But that one had no impact either.

Do you have any ideas what could be wrong? I really don’t get it. I published three different versions and tested each of them via Test Flight. All have the same issue.

Is there a chance, you can try to produce that on your side, maybe? Thanks in advance!

Hi @kaan548 ,

First of all, the FilterState component is not supposed to be recreated on each search request. It keeps the state of you filters and communicates them to the Searcher one you established a connection between them. You can clear its state using removeAll() method. But this is unrelated to your issue.

Otherwise, I’m really confused. Algolia libraries don’t have any on-device specific code,.

One more thing to try I can advise is to set your filters directly in the Query object.

let someOrganizerIDValue: String = ...
myChallengesIndexSearcher.indexQueryState.query.filters = "organizerId:\(someOrganizerIDValue)"

The FilterState is supposed to do it for you, but to find the reason of your issue, comment or isolate all your FilterState setup code and try this “low-level” approach. If it works properly, that means that the issue is hidden in your FilterState logic.

Hi @vladislav.fitc , I can try that. But as I mentioned, it works without any issues when I deploy from Xcode to the mobile device. So the code is probably correct.

Unfortunately, I am not able to see also a hint in the log file. I assume, the InstantSearch is not being logged?

UPDATE: I added a simple text view which should show me the exact filter when I launch the app via TestFlight, so at least I can see what happens in the prod. environment. Filter itself is looking good but it isn’t applied somehow. I made a screenshot and then deployed it directly from Xcode again to my device. Same filter but less results (the correct ones only) :frowning:

Hi @vladislav.fitc ,

using following approrach:

 let id = "SdPJ4dDyqPYp7aUkQO4cxb7gH793"
        
myChallengesIndexSearcher.indexQueryState.query.filters = "organizerId:\(id)"

doesn’t seem to work for me even when I deploy the app to the mobile device using Xcode.
Just noticed one thing. When I do a print like

print(myChallengesIndexSearcher.indexQueryState.query.filters)

I get

Optional(“organizerId:SdPJ4dDyqPYp7aUkQO4cxb7gH793”)

Does the Optional thing have an impact on the query?
Thanks!

UPDATE - 21st March 2021

let id = “SdPJ4dDyqPYp7aUkQO4cxb7gH793”
myChallengesIndexSearcher.query = “(id)”

The code above provides the correct results in both ways - deploying the app directly from Xcode to the mobile device but also when using TestFlight. It might be a hint but it wouldn’t solve my problem since I need to join multiple filter criterias? What can we do?

Hi @kaan548 ,

doesn’t seem to work for me even when I deploy the app to the mobile device using Xcode.

Try to set Logger.minSeverityLevel = .debug to check the actual request/response in the console.

Does the Optional thing have an impact on the query?

No, the Query.filters property is an optional string, so it’s expected that it is wrapped into Optional when you print it.

Could you double check if the organizerId attribute is correctly configured as an attribute for faceting in the settings of your index?

Hi @vladislav.fitc

Logger.minSeverityLevel = .debug

didn’t work for me. Instead, I used

Logger.InstantSearchInsights.minLogSeverityLevel = .debug

which I put into the didFinishLaunchingWithOptions method within the AppDelegate class.

Unfortunately, I don’t see anything in the console in Xcode :frowning:

Could you double check if the organizerId attribute is correctly configured as an attribute for faceting in the settings of your index?

Yes, I checked it. It is configured on the UI actually not on code level.

Thank you so much for your patience!

EDIT
I got it working!!!

I declared the filterState variable outside like:

class MyChallengesView: AbstractChallengesView, HitsController {

var singleIndexHitsInteractor: HitsInteractor<JSON> = .init(infiniteScrolling: .on(withOffset: 20), showItemsOnEmptyQuery: **true** )

public weak var hitsSource: HitsInteractor<JSON>?

let myChallengesIndexSearcher = SingleIndexSearcher(appID: ApplicationID(rawValue: APPID), apiKey: APIKey(rawValue: APIKEY), indexName: "challenges")

var filterState = FilterState() ..

and also modified the searchCreatedChallenges method like

private func searchCreatedChallenges() {
        ...
  singleIndexHitsInteractor.notifyQueryChanged()
 filterState.removeAll()
            
  filterState = setupFilter(indexName: "challenges", challengeState: SELECTED_CHALLENGE_STATE,  
        userObject: selectedUserObject)
  filterState[or: "groupName"].add(Filter.Facet(attribute: "organizerId", stringValue: selectedUserObject.id))
                    
  myChallengesIndexSearcher.connectFilterState(filterState)

It works when deploying from Xcode but also via TestFlight. Question is which of the changes had an impact? :slight_smile: To be honest, I started to do things completely randomly, just to try out.

Best regards,
Kaan

Hi @kaan548 ,

Glad to hear that it finally works.

As I replied previously:

First of all, the FilterState component is not supposed to be recreated on each search request. It keeps the state of you filters and communicates them to the Searcher one you established a connection between them. You can clear its state using removeAll() method. But this is unrelated to your issue.

But I still have no idea why it behaves differently in the debug/release environments and was sure that it’s unrelated :slight_smile: There is no magic inside the FilterState, it just keeps the state of filters, provides a convenient and consistent interface to alter them and propagate them to connected searchers.