matteodem / meteor-easy-search

Easy-to-use search for Meteor with Blaze Components
MIT License
435 stars 68 forks source link

Search over multiple collections #10

Closed yanndebelgique closed 10 years ago

yanndebelgique commented 10 years ago

Man that would be sweet. Is that in the pipeline?

wbashir commented 10 years ago

I think if we can index multiple collections and use the resultset to search off of, then there might be minimal changes to make this work

matteodem commented 10 years ago

But that would mean that you'd get 2 different sets of data back, and you'd have to filter out if the doc is from either of the several collections you specified.

I'd make the collection a possible array field, eg:

EasySearch.createSearchIndex('mixedSearch', {
    'collection'    : [Cars, Fruits, Roles],         // instanceof Meteor.Collection
    'fields' : ['name', 'price']
});

Price wouldn't be a field in the Roles collection which means inconsistent data. Did you have that in mind? If not, what would you like? I see that having a facet search over several collections would be nice. B eing able to define the 'es' fields which are being indexed would be another option.

yanndebelgique commented 10 years ago

Yes I would specify for each collection the fields to search over. I don't think that would be redundant.

In a webapp you want one general search bar that searches all collections. Today's internet user is used to the google search bar that looks across multiple types of docs.

yanndebelgique commented 10 years ago

sorry for the late reply.

wbashir commented 10 years ago

@matteodem I like that approach of identifying the collection as an option when making the index. I think it would be nice to filter out inconsistent data at runtime and let the user make that decision.

matteodem commented 10 years ago

How would you filter out inconsistent data? I'd probably split up the results by collection (index).

wbashir commented 10 years ago

@matteodem Yea as long as the index is tagged with which collection, then I can get the information returned based on each index. Will this cause problems ?

matteodem commented 10 years ago

It's not problematic, but it'll most probably need some overhaul of the current logic. I'll start in the next weeks with it.

matteodem commented 10 years ago

I implemented it with a rather simple approach, please give me feedback and re-open if you miss anything https://github.com/matteodem/meteor-easy-search#searching-over-several-collections.

gordo88 commented 10 years ago

I would like to use it via Blaze Components, but i'm not sure i fully understand that documentation :

"If you want to use it with the Blaze Components, you can simply change the index parameter to an array and define one esEach loop for each index defined."

Can you please provide an exemple of how and where to declare things to make it work ?

Thanks

matteodem commented 10 years ago

Added an example to the REAME.md which looks like this:

<div class="search-input">
     <!-- indexes is a javascript array which holds 'players' and 'cars' -->
     {{> esInput index=indexes placeholder="Search..." }}
</div>
<div class="results-wrapper">
     {{#esEach index="players"}}
         {{> player}}
     {{/esEach}}

     {{#esEach index="cars"}}
         {{> car}}
     {{/esEach}}
</div>

For further clarification, you'd have to declare the variable like this:

Template.yourTemplate.indexes = ['players', 'cars'];
gordo88 commented 10 years ago

Thanks a lot ! ;)

ghost commented 9 years ago

I am also trying to use Blaze Components for multi-collection searching (e.g., dogs, cats and birds). I have created the search index for each collection separately, each having multiple fields, and searching each collection separately works well. I am using those same individual search indices for the multi-collection searching according to @matteodem 's example code two posts above. It doesn't show any results when I am searching. Should I not use the same search indices and create a new one? If so, how do I create a search index for multi-collection? Thanks!

matteodem commented 9 years ago

@artitw It doesn't work at the moment and there's a bug #72. I'll have a look this week

ghost commented 9 years ago

Thank you, @matteodem ! Great stuff so far. Multi-collection search would be beyond awesome. I really look forward to it.

matteodem commented 9 years ago

It worked at some time, bugs me that it doesn't anymore. A CI would be great but I'll have to find the time for it. Thanks!

tyler-dunkel commented 9 years ago

hi, is the code example you posted above still the best way to do this? I am trying to search over 2 collections using elastic search.

matteodem commented 9 years ago

yep should work, this is also useable in the easy-search-leaderboard example.

alearcy commented 9 years ago

Hi @matteodem! My table contains multiple fields of multiple collections through some helpers. I don't want to separate search results by index, but each table row must contains all collections fields. Can I do it?

matteodem commented 9 years ago

Hello @alearcy What do you mean? You can create an index for each collection and pass in an array of indexes for the search. I'll add a Recipe section about it, if that could help you.

chompomonim commented 9 years ago

@matteodem I can't find docs about search in multiple collections anymore, is it added somewhere?

I already found examples how to do that in this issue but think, that example must be added into documentation.

matteodem commented 9 years ago

http://matteodem.github.io/meteor-easy-search/docs/blaze-components/, see "Components with multiple indexes"

matt-jay commented 8 years ago

The above link results in a 404. What is the current status on search across multiple indexes?

matteodem commented 8 years ago

Please have a closer look at the docs, components here show that some accept multiple indexes http://matteodem.github.io/meteor-easy-search/docs/components/ and with the new API in v2 you can search through multiple indexes by using the search method on all the indexes you want to search.

matt-jay commented 8 years ago

I saw that part of the doc. That gives me the answer to my question, indeed.

matt-jay commented 8 years ago

Is there any documentation on how to implement it? I tried earlier following the above example (index=indexes, declaring indexes in the template helper), but got errors about no index or array of indexes being passed on (if I recall correctly).

For the record: It seems to me that the above examples are outdated in terms of syntax by now (got a warning when trying to use Template.yourTemplate.indexes = ['players', 'cars'];", suggesting to set this up in the template helper).

matteodem commented 8 years ago

Yes they are, because they refer to the v1 API. It should be indexes=indexes if you want to use multiple indexes. I'll add a new section about searching with multiple indexes on the components page.

matt-jay commented 8 years ago

I get the following error when trying to search on multiple indexes:

Error: Did not receive an index or an array of indexes: "[object Object],[object Object]" [invalid-configuration]

I am trying with this code:

MyTemplate.html

{{> EasySearch.Input index=indexes }}

MyTemplate.js

Template.MyTemplateName.helpers({
    indexes: () => [firstIndex, secondIndex]
});

Both indexes are declared via new EasySearch.Index().

What is it I'm missing/doing wrong?

matteodem commented 8 years ago

The parameters should be indexes instead of index on your input.

{{> EasySearch.Input indexes=indexes }}
matt-jay commented 8 years ago

I don't quite get what you are hinting at, sorry.

Assuming I have:

firstIndex = new EasySearch.Index({
    collection: FirstCollection,
    fields: ['field1', 'field2'],
    engine: new EasySearch.MongoDB()
});

and

secondIndex = new EasySearch.Index({
    collection: SecondCollection,
    fields: ['field1', 'field2'],
    engine: new EasySearch.MongoDB()
});

What should my template and my helpers look like?

Shelagh-Lewins commented 8 years ago

I also can't get search over multiple indexes to work, and would be very grateful if you can explain what I need to do. I'm using this in my template:

{{> EasySearch.Input indexes=indexes attributes=attributes }}

I get this error on the client: "Error: Can only specify one index [only-single-index]"

The rest of my code is:

client and server:

PatternsIndex = new EasySearch.Index({
  collection: Patterns,
  fields: ['name', 'tags'],
  engine: new EasySearch.Minimongo(),
  defaultSearchOptions: {
    limit: 8
  }
});

ColorsIndex = new EasySearch.Index({
  collection: Colors,
  fields: ['name'],
  engine: new EasySearch.Minimongo(),
  defaultSearchOptions: {
    limit: 8
  }
});

and client only: Template.search.indexes = [PatternsIndex, ColorsIndex];

Shelagh-Lewins commented 8 years ago

FYI, I also tried assigning a name to each Index like this:

PatternsIndex = new EasySearch.Index({
  name: 'patternsIndex',
  collection: Patterns,
  fields: ['name', 'tags'],
  engine: new EasySearch.Minimongo(),
  defaultSearchOptions: {
    limit: 8
  }
});

and changing my helper to this:

Template.search.indexes = ["patternsIndex", "testIndex"];

But I get the error "Error: Did not receive an index or an array of indexes: "patternsIndex,testIndex" [invalid-configuration]"

Easy Search is an awesome package and I'd really like to get this working!

matteodem commented 8 years ago

firstIndex = new EasySearch.Index({
    collection: FirstCollection,
    fields: ['field1', 'field2'],
    engine: new EasySearch.MongoDB()
});

secondIndex = new EasySearch.Index({
    collection: SecondCollection,
    fields: ['field1', 'field2'],
    engine: new EasySearch.MongoDB()
});

Template.myTemplate.helpers({
  indexes: function () {
    return [firstIndex, secondIndex];
  },
  firstIndex: function () {
    return firstIndex;
  },
  secondIndex: function () {
    return secondIndex;
  }
});
<template name="myTemplate">
  {{> EasySearch.Input indexes=indexes}}

  {{#EasySearch.Each index=firstIndex}}
    ...
  {{/EasySearch.Each}}

  {{#EasySearch.Each index=secondIndex}}
    ...
  {{/EasySearch.Each}}
</template>
ctaloi commented 8 years ago

Having the above in the docs would be super helpful, got it working but the syntax is easy to get wrong.. thanks for this - the example you posted is great.

matteodem commented 8 years ago

you're right. I'll add an issue for that

Shelagh-Lewins commented 8 years ago

Thank you! I agree the syntax is tricky - I could only get it to work by copying your example and then changing one name at a time, but it is now working, awesome!

Is there any way to do IfSearching, IfNoResults across multiple indexes? Or do I need to construct nested statements in my template?

How will LoadMore work with multiple indexes? I don't want multiple buttons...

matteodem commented 8 years ago

please create new issues for these questions.

Shelagh-Lewins commented 8 years ago

Thank you. I've created three new issues:

petr24 commented 8 years ago

Just a quick comment for anyone viewing this. There is a typo in @matteodem code example.

{{/EasySarch.Each}}
instead of 
{{/EasySearch.Each}}
just missing an "e"
matteodem commented 8 years ago

thanks, changed it.

matteodem commented 8 years ago

added examples for multiple indexes here http://matteodem.github.io/meteor-easy-search/docs/components/ (Using multiple indexes)

Roshdy commented 7 years ago

It's not working for me though i followed the documentation step by step. Here are my configurations (sorry for the very long post, but to make everything clear):-

Indexes

ProductsIndex = new Index({
     collection: Products,
     fields: ['name', 'brand', 'category'],
     engine: new EasySearch.MongoDB({
          transform(doc){
                try{
                    let imageFile = Images.findOne({_id: doc.picture});
                    doc.imageFile = imageFile;
                    return doc;
               }
               catch(ex){
                    console.log(ex);
                    return doc;
               }
          }
     }),
     defaultSearchOptions: { 
          limit: 25
     }
});
CategoriesIndex = new Index({
    collection: Categories,
    fields: ['name'],
        engine: new EasySearch.MongoDB(),
        defaultSearchOptions: { 
            limit: 25
     }
});

JS

Template.MyTemplate.helpers({
    productsIndex: function(){
    return ProductsIndex;
    },
    categoriesIndex: function(){
    return CategoriesIndex;
    },
    searchIndexes: function(){
    return [ProductsIndex, CategoriesIndex];
    },
    searchInputAtts: {
    class: 'form-control search-input',
    placeholder: (Session.get('lang') == 'ar')? 'بحث...' : 'Search...'
    },
    loadMoreAtts: {
    class: 'btn load-more-btn'
    },
    loadMoreContent: function(){
    /*return '<i class="fa fa-spinner"></i> ' + 
        '<span>' +
            ((Session.get('lang') == 'ar')? 'المزيد...' : 'More...') + 
        '</span>';*/
    return 'More...';
    },
    empty_text: () => {
    return (Session.get('lang') == 'ar')? 'أدخل كلمات للبحث...' : 'Enter search text...';
    },
    no_result_text: () => {
    return (Session.get('lang') == 'ar')? 'لا توجد نتائج للبحث...' : 'No matches...';
    },
    searching_text: () => {
        return (Session.get('lang') == 'ar')? 'جاري البحث...' : 'Searching...';
    },
    notInSearchRoute: () => {
        return !Session.get('inSearch');
    }
});

HTML

<template name="MyTemplate">
    {#if Template.subscriptionsReady}}
        <div class="form-group search-form">
            {{> EasySearch.Input indexes=searchIndexes attributes=searchInputAtts }}
                {{#if notInSearchRoute}}
                    {{#EasySearch.IfInputEmpty indexes=searchIndexes}}
                        <div class="no-results">{{empty_text}}</div>
                    {{else}}
                        {{#EasySearch.IfNoResults indexes=searchIndexes logic="AND" }}
                            <div class="no-results">searchIndexes {{no_result_text}}</div>
                        {{else}}
                            {{#EasySearch.IfNoResults index=productsIndex}}
                                <div class="no-results">productsIndex {{no_result_text}}</div>
                            {{/EasySearch.IfNoResults}}

                            <ul class="quick-search-list">
                                {{#EasySearch.Each index=productsIndex}}
                                    ...
                </div>
            {{/if}}
</template>

Now the only result i get is: searchIndexes No Matches... no matter the query is.

PS: it used to work with one index smoothly

PS2: When I try from chrome devTools console, this is what I get (first time no results, second time has results):-

ProductsIndex.search('a');
>Cursor {_mongoCursor: L…n.Cursor, _count: 0, _isReady: false, _publishHandle: null}
ProductsIndex.search('a');
>Cursor {_mongoCursor: L…n.Cursor, _count: 8, _isReady: true, _publishHandle: Object}
CategoriesIndex.search('a');
>Cursor {_mongoCursor: L…n.Cursor, _count: 0, _isReady: false, _publishHandle: null}
CategoriesIndex.search('a');
>Cursor {_mongoCursor: L…n.Cursor, _count: 2, _isReady: true, _publishHandle: Object}

Please advise...