SwiftListTreeDataSource icon indicating copy to clipboard operation
SwiftListTreeDataSource copied to clipboard

Search Not working

Open AKGupta31 opened this issue 1 year ago • 9 comments

The search is not working as per the implementation in the example. I am unable to filter out the data based on search. Find the following code.

if textField.stringValue.isEmpty { self.cratesDataSource.resetFiltering(collapsingAll: true) self.outlineviewCrates.reloadData() } else { let searchText = textField.stringValue self.cratesDataSource.filterItemsKeepingParents(by: { $0.crateName! .localizedCaseInsensitiveContains(searchText)}) { [weak self] in guard let self = self else { return } self.outlineviewCrates.reloadData() } }

AKGupta31 avatar Dec 02 '23 18:12 AKGupta31

Hello @AKGupta31 , could you please share actual & expected behavior? Maybe you can just provide data set you're using and test cases?

dzmitry-antonenka avatar Dec 02 '23 19:12 dzmitry-antonenka

I am using outline view for hierarical data like expandable listview.

Following Code:

func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
      if isSearchMode {
          return cratesDataSource.items.count
      }
       guard let cratesDataSource = self.cratesDataSource else {
          return 0
      }
      return item == nil ? cratesDataSource.backingStore.count : (item as? TreeItem<SeratoCrate>)?.subitems.count ?? 1
  }
  
  func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
      if isSearchMode {
          return cratesDataSource.items[index]
      }
      if item == nil {
          return cratesDataSource.backingStore[index]
      }
      return (item as? TreeItem<SeratoCrate>)?.subitems[index] ?? item!
  }

The above code is to show the data. But backing store represents the same data every time. I think for this reason i am unable to filter the data. We must filter out the backing store as well as its subitems or can use some other variables in case of search..Since the below code is giving some filtered data but still with parents which i am not able to represent in hierarchy.

  if isSearchMode {
            return cratesDataSource.items[index]
        }

AKGupta31 avatar Dec 03 '23 03:12 AKGupta31

Hello @AKGupta31 ,

  • The backingStore is original hierarchical store and shouldn't be used directly (ideally).
  • The items should contain all the items after filtering (with parents that lead to search target items)

I haven't used NSOutlineView, so just trying some ideas:

guard let item = item as? TreeItem<SeratoCrate> else {
    // filter for roots only
    return cratesDataSource.items.filter { $0.parent == nil }.count
}
return item.subitems.count
guard let item = item as? TreeItem<SeratoCrate> else {
    // filter for roots only
    return cratesDataSource.items.filter { $0.parent == nil }[index]
}
return item.subitems[index]

Discussion: Original inspiration for current API subset was NSDiffableDataSourceSnapshot I see APIs NSOutlineView differs & tries to know all hierarchical structure what makes it little bit more complicated.

Please feel free to submit archive with sample app so we can improve it and make use-case running. Thanks!

dzmitry-antonenka avatar Dec 03 '23 09:12 dzmitry-antonenka

Hi @dzmitry-antonenka , The above code is almost fine for me. But just one point is left. Explained below.

Suppose there is one child named inside a parent (root) . If i am searching for then i must get the parent as expanded by default while searching but its showing the parent only. If you can help me out achieving the same.

AKGupta31 avatar Dec 03 '23 11:12 AKGupta31

Hi @dzmitry-antonenka ,

The above code is almost fine for me.

But just one point is left. Explained below.

Suppose there is one child named inside a parent (root) .

If i am searching for then i must get the parent as expanded by default while searching but its showing the parent only. If you can help me out achieving the same.

You mean 'items' doesn't contain target search item but only parent? Or expanded not true for them. Are you sure target item matches filter criteria?

search logic we expand search target item parent (path to be visible for all items)

https://github.com/dzmitry-antonenka/SwiftListTreeDataSource/blob/a7e697180d09d391f67ce9bea233e12b7924beb4/Sources/SwiftListTreeDataSource/FilterableListTreeDataSource.swift#L48

No pressure, but please feel free to attach simple use case model you try in sample app and expected vs. actual behavior. In this case it would be more productive to support with this

dzmitry-antonenka avatar Dec 03 '23 14:12 dzmitry-antonenka

Please feel free to share data source memory dump for inputs and outputs. Could you also try debug section to see what's the root cause?

dzmitry-antonenka avatar Dec 03 '23 16:12 dzmitry-antonenka

Hi @dzmitry-antonenka , I need a functionality similar to this...

https://github.com/dzmitry-antonenka/SwiftListTreeDataSource/assets/51131294/65df2558-f551-435a-91ef-88065319fdc0

AKGupta31 avatar Dec 04 '23 18:12 AKGupta31

Hi @dzmitry-antonenka , I need a functionality similar to this...

ScreenRecording.mov

Right, that's exactly what our example apps are doing:

Could you please explore these code these snippets carefully? Example for performing search

func performSearch(with searchText: String) {
        if !searchText.isEmpty {
            self.searchBar.isLoading = true
            self.displayMode = .filtering(text: searchText)

            self.listTreeDataSource.filterItemsKeepingParents(by: { $0.title.lowercased().contains(searchText.lowercased()) }) { [weak self] in
                guard let self = self else { return }
                self.searchBar.isLoading = false
                self.reloadUI(animating: false)
            }
        } else {
            self.displayMode = .standard
            self.searchBar.isLoading = false
            self.listTreeDataSource.resetFiltering(collapsingAll: true)
            self.reloadUI(animating: false)
        }
    }

After self.listTreeDataSource.filterItemsKeepingParents self.listTreeDataSource.items should contain all search items and their parents. Could you please examine this property to validate filter logic? Maybe small data set can be helpful

dzmitry-antonenka avatar Dec 04 '23 20:12 dzmitry-antonenka

@AKGupta31 ,

From debugging section

// to get testable access to internal stuff, including to backingStore. @testable import SwiftListTreeDataSource

// Make conform to CustomDebugStringConvertible. extension OutlineItem: CustomDebugStringConvertible { public var debugDescription: String { "(title)" } }

// Correct usage: let output1 = debugDescriptionTopLevel(listTreeDataSource.items) // ✅ Description for top level since items already flattened (one-level perspective) and include expanded children. Try this to see what output after filtering it provides?

dzmitry-antonenka avatar Dec 04 '23 20:12 dzmitry-antonenka

@AKGupta31 do you have any updates on this? Please feel free to post simple example project or use existing TreeView projects with your data set. Describe Actual vs Expected results so we can investigate. Thank you!

dzmitry-antonenka avatar May 14 '24 15:05 dzmitry-antonenka