meteor-autocomplete icon indicating copy to clipboard operation
meteor-autocomplete copied to clipboard

Support lookup in multiple fields per collection

Open dandv opened this issue 11 years ago • 16 comments

Sample use case: search among company names and stock symbols, a la Google Finance.

dandv avatar Jan 22 '14 23:01 dandv

Why can't you just do this with different tokens? Are you saying you want to use the same token to search in different fields (and possibly in different collections?)

mizzao avatar Jan 23 '14 00:01 mizzao

I want to search for the input string in two fields of the same collection, the Name and the Symbol of a company. I don't want to use a token at all actually (see issue #4) but for now I'll settle for ' '.

dandv avatar Jan 23 '14 00:01 dandv

The selector field can now be used in a rule to generate a custom selector for the autocomplete search selector. Does this sufficiently allow what you want to do?

mizzao avatar May 29 '14 18:05 mizzao

So currently I am trying to get the following effect. In my autocomplete I want to drill down based on either the profile.display_name or profile.login.When a user wants to complete the autocomplete, I would like for the autocomplete to complete to the profile.login

The only thing I have working, which is the simple part of this setup is that the template shows the profile.display_name.

This is what I have tried so far and will continue to work on this, but I could use some help.

Template.autocomplete.settings = () ->
    return {
        position: "top",
        limit: 10,
        rules: [
            token: '@',
            collection: Meteor.users,
            field: "profile.display_name",
            template: Template.getUsers,
            selector: (match) ->
                return Meteor.users.find({$or: [{'profile.display_name': match}, {'profile.login': match}]})
        ]
}

kfollmer avatar Jul 29 '14 15:07 kfollmer

@kfollmer instead of returning a cursor for selector, just use the { $or: ... } part. You may also want to do something like regex = new RegExp(match, "i") and pass that in to the selector, if you want to do partial matches and not be case-sensitive either.

mizzao avatar Jul 29 '14 17:07 mizzao

@mizzao Do you mean setting up something like this for the field?

field: {$or: {'profile.display_name'}, {'profile.login'}}

Im not quite sure where to use the { $or: ... } and how to use it properly

kfollmer avatar Jul 29 '14 17:07 kfollmer

No, I think you should just do

selector: (match) ->
  regex = new RegExp(match, 'i')
  return {$or: [{'profile.display_name': regex}, {'profile.login': regex}]}

Does that work?

mizzao avatar Jul 29 '14 17:07 mizzao

Yeah that worked! I'm 95% of the way there, just one last thing.

Currently I am getting my display_name as a user's full name (i.e. John Doe), and as it is now the autocomplete just stops working when you type a space.

For example: If I typed @John and then left the menu up, and there just so happened to be more than one John, is there a way to keep the autocomplete up when I type a space? That would be awesome.

Thanks for the help

kfollmer avatar Jul 29 '14 17:07 kfollmer

Currently, not unless you use the whole field as the autocomplete, because we can't tell apart spaces in selected items from other word boundaries.

You could modify it yourself in https://github.com/mizzao/meteor-autocomplete/blob/master/autocomplete-client.coffee#L12. I have thought about this a little but I'm not sure what a good way it is to support it without either making it the entire field or just whitespace-separated.

mizzao avatar Jul 29 '14 18:07 mizzao

Okay, that's no problem.

Thanks for all the help.

If you ever get around to doing it or having an update that works around that, if you could send me a message on here so I get notified that would be awesome.

Thanks again

kfollmer avatar Jul 29 '14 18:07 kfollmer

Just pushed multi-field autocomplete in production for StockBase - thanks @mizzao.

Two minor issues:

  1. The docs (here and code (my last code commit, and here) assume in places that there's only one field to match against. Does this need updating in light of multiple-field selectors?
  2. The autocomplete template may need to be customized according to what field had matched. How could I pass the match suffix to the template, or fetch it from there, the way it's passed to the selector function?

dandv avatar Aug 02 '14 00:08 dandv

Re. this comment, I found that { symbol: new RegExp(match, 'i') } was received by the server as symbol: {}. Can RegExp's be serialized correctly?

dandv avatar Aug 02 '14 01:08 dandv

@dandv not sure how you implemented multi-field autocomplete. Did you just make multiple rules with the same symbol? Or are you using an $or selector?

Answers to your questions:

  1. If you use $or in the selector, you probably want to just fill in one thing for rule.field - so you would probably pick the stock symbol or whatever the canonical representation is.
  2. Maybe we could make template take a function as well and pass in the match, in addition to a constant template.

Regarding your second post, use { symbol: { $regex: match, $options: 'i' } }

mizzao avatar Aug 02 '14 05:08 mizzao

$or selector:

var orSearch = [
    { symbol: { $regex: match.toUpperCase().regexEscape() } },  // symbols are always uppercase
    { name: { $regex: match.regexEscape(), $options: 'i' } }
  ];

A polymorphic signature for the template would be great (taking a function as well as a template object).

dandv avatar Aug 02 '14 05:08 dandv

How many companies are in your DB? Isn't that query going to eat CPU since it's matching in the middle of the string?

To take advantage of indexes, consider making a lowercase key for each company name (as well as the uppercase symbol), and sending a lowercase regex match starting with ^ in the query.

mizzao avatar Aug 02 '14 05:08 mizzao

About 20k companies. No performance issues so far, but a secondary key is a good suggestion to have in mind.

dandv avatar Aug 02 '14 05:08 dandv