Use mongoosastic for an autocomplete
Asked Answered
O

2

10

I'm trying to create an autocomplete using mongoosastic and Elastic Search, and so far, I have been able to create it using sense but I'm having trouble porting it to mongoosastic.

I followed this tutorial from ElasticSearch docs, and I was able to achieve what I wanted using "sense" with a mapping that looks like this:

PUT storys/story/_mapping
{
    "story" : { 
        "properties": {
            "description": {
                "type": "string"
            },
            "title": {
                "type" : "completion",
                "index_analyzer": "simple",
                "search_analyzer": "simple"
            }
        }  
    }
}

and a query like this:

GET storys/_suggest
{
    "story-suggest": {
        "text": "bow",
        "completion": {
            "field": "title"
        }
    }
}

However, I'm having trouble porting this to mongoosastic. I tried the following approach:

    var StorySchema = new Schema({
        title:{
            type: String, es_type:'completion', es_index_analyzer: 'simple', es_search_analyzer: 'simple', es_payloads: true
        },
        description: { 
            type: String
        }
    });

StorySchema.plugin(mongoosastic);

And when querying from the server controller:

Story.search({ 
    query: {
        "match": { title : req.query.q }
    },
    suggest: {
            "my-title-suggestions-1" :{
                text: req.query.q,
                completion: {
                    field: 'title'
                }
            }
        }
});

I understand that when I use "sense", I'm using the _suggest endpoint, and that's why the "story-suggest" query works. However, when using mongoosastic, I'm limited to use the .search({}) for querying which acts like _search I suppose. However, I cannot seem to find a way to accomplish the _suggest behavior I'm seeking for an autocomplete, and I keep getting parsing errors in ElasticSearch when I try to do a query with a suggest.

Is there a way to accomplish what I'm trying to do either with mongoosastic or elastic search?

I have tried doing this using "sense" but even though I get the suggestions for "autocomplete" I also get a bunch of SearchParseExceptions:

GET _search
{
    "query": {
       "match": { title : "bow" }
    },
    "suggest": {
        "story-suggest": {
            "text": "bow",
            "completion": {
                "field": "title"
            }
        }
    }
}
Ostia answered 8/9, 2015 at 15:6 Comment(0)
D
13

Here is a complete solution for a basic autocomplete:

  1. Add the necessary parameters to your schema:

    var TagSchema = new Schema({
        name: {
            type: String,
            unique: true,
            required: true,
            es_type: 'completion',
            es_index_analyzer: 'simple',
            es_search_analyzer: 'simple',
            es_payloads: true
        }
    });
    
  2. Add the plugin to your schema and create the model:

    TagSchema.plugin(mongoosastic);
    var Tag = mongoose.model('Tag', TagSchema);
    
  3. Use the create mapping method to register the mapping with ES. If you don't do this step your index will get registered with defaults, when the first document is created and indexed:

    Tag.createMapping(function(err, mapping) {
        if (err) {
            console.log('error creating mapping (you can safely ignore this)');
            console.log(err);
        } else {
            console.log('mapping created!');
            console.log(mapping);
        }
    });
    
  4. *Optional - Index existing documents in your database:

    var stream = Tag.synchronize(),
        count = 0;
    
    stream.on('data', function(err, doc) {
        count++;
    });
    stream.on('close', function() {
        console.log('indexed ' + count + ' documents!');
    });
    stream.on('error', function(err) {
        console.log(err);
    });
    
  5. Search with an empty query body and supply two options - 1) Use the ES suggest query which uses the "es_completion" field we created in our schema; 2) size = 0 so that no tags are returned by the empty body query.

    Tag.search(null, {
        suggest: {
            "tag-suggest": {
                "text": "aTermToAutocomplete",
                "completion": {
                    "field": "name"
                }
            }
        },
        "size" : 0
    },
    function(err, results) {
        if (err) {
            return console.log(JSON.stringify(err, null, 4));
        }
        return console.log(JSON.stringify(results, null, 4));
    });
    
Disown answered 30/9, 2015 at 5:16 Comment(2)
I get this error when I try to use this now, "msg": "[illegal_argument_exception] request [/products/product/_search] contains unrecognized parameter: [suggest] -> did you mean [suggest_field]?", and when I change to suggest_field I get another error that looks simple to fix but it doesnt work.Udall
When i am doing the same it taking type as text.Casper
O
1

I have found a way. With the latest update to mongoosastic library you could just do something like this:

GET _search
{
    "query": {
       "match_all": {}
    },
    "suggest": {
        "story-suggest": {
            "text": "bow",
            "completion": {
                "field": "title"
            }
        }
    }
}

This is because the new mongoosastic version supports suggesters. Just be aware of what you are doing, specially in the query part, as a "match_all" would match all the documents and give you an unnecessarily big response; but whatever you place in the "text" part of your suggester would return you the words to autocomplete.

Also be aware that in order for this to work, you need to use the createMapping method in your model definition, like this:

var StorySchema = new Schema({
    title:{
        type: String, es_type:'completion', es_index_analyzer: 'simple', es_search_analyzer: 'simple', es_payloads: true
    },
    description: { 
        type: String
    }
});

StorySchema.plugin(mongoosastic);
var Story = mongoose.model('Story', StorySchema);

Story.createMapping({}, function(err, mapping){
    // ...
});
Ostia answered 25/9, 2015 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.