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

Better ranked search results in app list #561

Merged
merged 7 commits into from
Jan 20, 2016
Merged

Conversation

pierluigi
Copy link
Contributor

This fixes mesosphere/marathon#3018

ACs

  • First result should be the exact app name match.
  • Followed by deepest group match first.

From:

screen shot 2016-01-19 at 12 29 32

To:

screen shot 2016-01-19 at 16 57 25

@@ -186,7 +198,11 @@ var AppListComponent = React.createClass({

if (filterText != null && filterText !== "") {
nodesSequence = nodesSequence
.filter(app => app.id.indexOf(filterText) !== -1);
.filter(app => {
app.filterScore = score(app.id, filterText);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are modifying the app here, this should be done only in the store (with emiting an event afterwards).
Also this is done in a filter. It's not expectable that a filter modifies data.
So this isn't the right approach. Sorry. :)

Let's try to sort and score the list where the sorting belongs to.
Could you add a sort function after the .sort()-function at line 316?

      if (filterText != null) {
        nodesSequences.sort((a, b) => {
          return score(a.id, filterText) > score(b.id, filterText)
            ? -1
            : 1;
        });
      }

Didn't tested it, but it should go into this direction.
Maybe there is also a more efficient way by integrating this into the already existing sort-function. Not sure.

This doesn't modify the app itself. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're totally right. Will follow your suggestions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That won't work, however, if we plan on supporting manual overriding of the sorting. I would like, for example, to search for phil and get the ranked sorting. But if I then click on one of the sorting columns, (for example CPU), this should override the search rank.
My solution is hacky so it's a no-go, so let's have a chat about a better solution that addresses all of that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a simple condition to the if should do the trick:

if (filterText != null && sortKey !== "id") {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That means i can't sort my search results alphabetically?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alphabetically sorting is done before.
https://github.com/mesosphere/marathon-ui/blob/master/src/js/components/AppListComponent.jsx#L315

The sort-function mentioned above should be added below that.

I did a mistake in the condition, must be:

if (filterText != null && sortKey === "id") {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the link goes to the wrong view type I think. May bad.
But doesn't change the principle how it works.

An additional sort function should be added here:

https://github.com/mesosphere/marathon-ui/blob/master/src/js/components/AppListComponent.jsx#L301

@aldipower
Copy link
Contributor

A test would be neat! :)

@aldipower
Copy link
Contributor

According to the branchname, this is actually a feature and not a fix.
But this is very very minor nitpicking.

@aldipower
Copy link
Contributor

I had a look at the fuzzaldrin code.
It looks slightly inefficient and it's not because it is coffescript. :)
A lot of replaces and matches there.
https://github.com/atom/fuzzaldrin/tree/master/src
I wonder if this works together without our scalability objective, when we will have a lot of apps.

Is there is maybe a simpler solution?

Maybe just taking a second sorting round only on the applications name without the groups-portion?
Then we had 2 sort steps (like with fuzzaldrin anyways).

  1. Sorting on the complete id -> this affects mostly the sorting on the groups segements (like it is already)
  2. Sorting only at the application name -> kind of a ranking

Sounds simple. Do I miss something?

@pierluigi
Copy link
Contributor Author

I picked fuzzAldrin specifically because it adds fuzzy matching:

This library is used by Atom and so its focus will be on scoring and filtering paths, methods, and other things common when writing code. It therefore will specialize in handling common patterns in these types of strings such as characters like /, -, and _, and also handling of camel cased text.

That is a very useful feature to have in our toolbelt, imho. Imagine searching very nested appIds, without the need to be precise.

Regarding performance, I will do some tests. Surely indexOf is more performant, but this does not look bad at all:

Filtering 66672 entries for 'index' took 146ms for 6168 results
Matching 66672 entries for 'index' took 140ms for 6168 results

(I ran npm run benchmark in the fuzzaldrin source code repo).

We are only using the score function by the way.

I agree we should add some tests, and I'll do that next.

@pierluigi
Copy link
Contributor Author

@aldipower I've squashed the changes, please take another look now.

Here's a gist that helps you generate a number of fake apps for testing performance and ranking: https://gist.github.com/pierlo-upitup/c325692b3e9511da6590

@pierluigi
Copy link
Contributor Author

Should we disable the sorting caret for the Name column when filterText != null?

@aldipower
Copy link
Contributor

Thanxman! Going to take a second round now.
Having problems to install the modules on the first try. I think the shrinkwrap is faulty.

@pierluigi
Copy link
Contributor Author

Oh yeah. I need to re-generate it...

@@ -186,7 +187,10 @@ var AppListComponent = React.createClass({

if (filterText != null && filterText !== "") {
nodesSequence = nodesSequence
.filter(app => app.id.indexOf(filterText) !== -1);
.filter(app => {
return score(app.id, filterText) > 0.025;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? It leads to unexpected behavior.

Before (like it was), I see all result (ranked with the sorting later):
score-before
👍

After - one result is hidden, I don't expect this:
score-after
👎

Let's stick with indexOf() and keep it simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a matter of tweaking that 0.25 to something lower, maybe 0.2.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what sense?
And what else is hidden then unexpected?
Cannot detect the use case for the scoring here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The score value is our way of determining matches in the app list for the given filterText. https://github.com/atom/fuzzaldrin#scorestring-query

If we move back to indexOf we lose the whole fuzz matching algorithm completely! Why would you want to do that? I agree that the result not showing up was a regression, but that's because i set the score threshold too high. Please try again now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, now I got it. You want fuzzy matched results, besides direct matches.
This makes sense.
I was confused because this behavior isn't reflected in the related issue.
It only says that the given results should be ranked better. That's how I understand it.

With this, now, you modify the complete result set.
But I like it!

Confirmative. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@aldipower
Copy link
Contributor

@pierlo-upitup OK! Review completed. :)
Some things need a little bit rework, but after that I am going to merge this.

I am fine by going with fuzzaldrin, he is doing a good job. :) And my solution I thought about is too simple, doesn't really work out.

This makes the search result way meaningful.
Thanx for the work @pierlo-upitup !

@pierluigi
Copy link
Contributor Author

Thanks for that! I just squashed your requested changes and especially tweaked the score value to 0.02 – that should be fine! Please let me know your thoughts.

FTR: I think at some point it might be a really good idea to move this whole filtering logic to the AppsStore or to a new ApssFilterStore because this component is doing far too much!

@aldipower
Copy link
Contributor

Thx, please look at my comment regarding the "0.02".
Don't get the use case for that.
Let's keep it simple please.

@@ -16,6 +16,7 @@
- \#2831 - Convert alert, confirm and prompt dialogs to new design
- \#2909 - Update dialog messages
- \#2655 - Never preselect dangerous actions in modal dialogs
- \#3018 - Better search result ranking
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you be a little bit more precise here? That we do a fuzzy search.

@aldipower
Copy link
Contributor

Cool!
So, maybe a little bit more precise changelog, a test and a rebase is left.
hurray

@pierluigi
Copy link
Contributor Author

I have removed the caret from the Name column when a filterText is present, as I believe it only generates confusion (you don't order search results on Google...). I've also added some tests. We should be good now!

@pierluigi
Copy link
Contributor Author

@aldipower please take a look at this, for a clarification as to why I think we should not allow Name sorting when showing ranked search results:
weird

When i click on the "Name" column, as a user I expect the results sorting by Name. But what this accomplishes, is to sort results by rank in the opposite direction instead.

@aldipower
Copy link
Contributor

@pierlo-upitup Yeah, got what you mean, I would add a hover text on the caret, that explains the sorting by score.
I like to have this functionality of sorting the name column by score.
"Name" is the title of the column, not the definition of sorting the column by id alphabetically.

@aldipower
Copy link
Contributor

The health column is also not alphabetically sorted, but ranked! Same thing. Ok, slightly.. ;)

@pierluigi
Copy link
Contributor Author

The Name column works exactly on the id value itself outside of the search page.
Why should that behaviour change in this context to work on a completely different and invisible value (score)? The tooltip explanation feels like a workaround to me, and changing the behaviour of a sorting column is bad UX.

Sorting by the healthbar is consistent across all other pages, so it's not a good comparison.

Why would a user you want to see the worst ranking results first, anyway?

@pierluigi
Copy link
Contributor Author

Take a look at Google Drive.The same is true: sorting by Name is disabled in the search results page.

@aldipower
Copy link
Contributor

OK, the context change is a good argument to me. Thank you for argue this out. The healthbar example wasn't good.
Disabling the caret is also a workaround, but I think probably the better one.

var filterText = this.props.filters[FilterTypes.TEXT];

if (sortKey === this.state.sortKey &&
!(sortKey === "id" && filterText != null && filterText !== "")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for bothering you, could we negate this?

(sortKey !== "id" || filterText == null || filterText === "")) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forcepushed, sir!

@aldipower
Copy link
Contributor

Thank you for the tests, very cool!

@pierluigi
Copy link
Contributor Author

Aahaha and of course coveralls.io decided to die on us on the first PR with some tests 😛

@aldipower
Copy link
Contributor

LOL

@aldipower
Copy link
Contributor

Great!!!1!ELEVEN!!
Thanx a lot for your patience on my comments.

Very well done!

This will be merged now.

merger-ahead

@aldipower
Copy link
Contributor

Mission accomplished!

aldipower added a commit that referenced this pull request Jan 20, 2016
Better ranked search results in app list
@aldipower aldipower merged commit 5e612c6 into master Jan 20, 2016
@aldipower aldipower deleted the fix/3018-ranked-search branch January 20, 2016 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants