comigor / fuzzy

Fuzzy search in Dart
MIT License
37 stars 20 forks source link

Fuzzy search on custom objects #7

Closed gsouf closed 4 years ago

gsouf commented 4 years ago

Hi,

According to the documentation we can only search strings. Can we somehow search on a list of objects that have a string to match against?

Example:

void main() {
  final fuse = Fuzzy([Fruit("orange"), Fruit("banana")]);

  final result = fuse.search('ran', (item) => item.name);
  // second argument returns the string to be searched
  // returns list of Fruit instances that matched
}

class Fruit{
  String name;

  Something(this.name);
}
comigor commented 4 years ago

Hey @gsouf!

Absolutely, there are some tests that guarantee that: https://github.com/comigor/fuzzy/blob/master/test/fuzzy_test.dart#L134-L141

Feel free to reopen this or create another issue if you have any other doubt!

gsouf commented 4 years ago

@comigor amazing, exactly what I was looking for

gsouf commented 4 years ago

@comigor there is a minor drawback with this implementation. It asserts that all objects passed to the Fuzzy instance will be searched the same way.

However it does not allow to implement a custom search key logic per object. Example of what I would expect it to support:

abstract class Food {
   List<WeightedKey> getKeys();
}

class Meat extends Food {
   String animal;
   String name;

   // ...

  getKeys () {
     return [
         WeightedKey(weight: 0.7, value: this.name),
         WeightedKey(weight: 0.3, value: this.animal),
     ]
  }
}

class Fruit extends Food {
   String name;

   // ...

  getKeys () {
     return [
         WeightedKey(weight: 1, value: this.name),
     ]
  }
}

Would that represent a lot of effort to implement?

gsouf commented 4 years ago

Actually same applies as soon as I have a dynamic number of keys to provide

comigor commented 4 years ago

Hello again, @gsouf

Actually, that's why I've implemented getter as a custom function: it receives T (the object type you're iterating over) and outputs a String, so you could do something like this:

abstract class Food {
  Food(this.name);
  String name;
}

class Meat extends Food {
  Meat(String name, this.animal) : super(name);
  String animal;
}

class Fruit extends Food {
  Fruit(String name) : super(name);
}

final List<Food> list = [
  Meat('bacon', 'pork'),
  Meat('belly', 'pork'),
  Fruit('apple'),
  Fruit('banana'),
];

final fuse = Fuzzy<Food>(
  list,
  options: FuzzyOptions(keys: [
    WeightedKey(getter: (f) => f.name, weight: 0.8, name: 'name'),
    WeightedKey(
      getter: (f) {
        if (f is Meat) return f.animal;
        return '';
      },
      weight: 0.2,
      name: 'animal',
    ),
  ]),
);

It's not possible to have dynamic WeightedKeys for each object, or else each individual objects would interfere with the general result, which doesn't make sense as you want to calibrate the results as a whole.

gsouf commented 4 years ago

@comigor ok got it thanks. Still uncertain how it exactly works. I'll need more experiment to understand how the scoring things work