Render data for autocompletion from a MongoDB database

Viewed 380

I am trying to generate an autocompletion in an HTML field. I want to change the source of the data from hardcoded to myMongoDB database. I was able to render the data from an hardcoded array with:

$(document).ready(function() {

  var perfumes = ["dior", "pacco", "moschino"];

  $("#autocomplete").autocomplete({
    source: perfumes,
    minLength: 0
  }).focus(function() {
    /* Muestra todas las opciones al enfocar el input */
    $(this).autocomplete('search', $(this).val())
  });

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="recommendations">
  <!-- <div class="login-page"> -->
  <div class="form">
    <form class="form" action="{% url 'similar_results' %}" method="get" autocomplete="off">
      <input id="autocomplete" type="text" placeholder="Perfume name...">
      <input id="perfumename" type="submit" value="Find Similar Perfumes" />
    </form>
  </div>
  <!-- jQuery-UI CSS -->
  <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.1/themes/cupertino/jquery-ui.css" />
  <!-- jQuery-Min JS -->
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <!-- jQuery-UI JS -->
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" integrity="sha256-T0Vest3yCU7pafRw9r+settMBX6JkKN06dqBnpQ8d30=" crossorigin="anonymous"></script>
</div>

But when trying to adapt it to data I have from a query to an Index of a MongoDB database, it's not as simple. Indeed, this:

  $(document).ready(function () {
    $("#autocomplete").autocomplete({
        source: async function(request, response) {
            let data = await fetch(`http://localhost:8000/similar/similar_results/?query=${request.term}`)
                .then(results => results.json())
                .then(results => results.map(result => {
                    return { label: result.name, value: result.name, id: result._id };
                }));
            response(data);
        }
    });
  });

Doesn't render anything. Even if http://localhost:8000/similar/similar_results/?query=${request.term} returns the expected documents.

I also tried to do something close to this fiddle, which works well

$(document).ready(function () {
  const datos = await fetch(`http://localhost:8000/similar/similar_results/?query=${request.term}`);
  const filtrar = (w) => {
    return datos

  }
  // handle click and add class
  $("#autocomplete").autocomplete({
    source: async function(request, response) {
        let data = filtrar().map(result => {
                return { label: result.name, value: result.name, id: result._id };
            });
        response(data);
    },
    minLength:2,
    select: function(event, ui){
      console.log(ui.item);
    }
  });
})

But my attempt doesn't.

Update

Django version 3.1.1, using settings 'scentmate.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
JE SUIS PASSE PAR LA # This shows we went through the Django code which query the database
JE SUIS PASSE PAR LA # This shows we went through the Django code which query the database
[27/Mar/2021 19:01:20] "GET /similar/similar_results/?query=at HTTP/1.1" 200 11389
[27/Mar/2021 19:01:21] "GET /similar/similar_results/?query=atel HTTP/1.1" 200 11389

Here is the code that query the database:

class SearchResultsView(ListView):
    model = Perfume
    template_name = 'todo/search_similar_results.html'

    def get_queryset(self):  # new
        query = self.request.GET.get('q')
        print("JE SUIS PASSE PAR LA")
        # object_list = list(collection.find({"q0.Results.0.Name": {"$regex": str(query), "$options": "i"}}))
        object_list = list(collection.aggregate([
                {
                    '$search': {
                        'index': 'default',
                        'compound': {
                            'must': {
                                'text': {
                                    'query': str(query),
                                    'path': 'name',
                                    'fuzzy': {
                                        'maxEdits': 2
                                    }
                                }
                            }
                        }
                    }
                }
            ]
        ))
        # print(object_list[0])
        return object_list
1 Answers

https://api.jqueryui.com/autocomplete/#option-source is pretty clear on the interface for the function as a source:

Function: The third variation, a callback, provides the most flexibility and can be used to connect any data source to Autocomplete, including JSONP. The callback gets two arguments:

  • A request object, with a single term property, which refers to the value currently in the text input. For example, if the user enters "new yo" in a city field, the Autocomplete term will equal "new yo".
  • A response callback, which expects a single argument: the data to suggest to the user. This data should be filtered based on the provided term, and can be in any of the formats described above for simple local data. It's important when providing a custom source callback to handle errors during the request. You must always call the response callback even if you encounter an error. This ensures that the widget always has the correct state.

You return call the response callback with an array of objects { label: result.name, value: result.name, id: result._id }; which doesn't match the documented format. Return a plain string in your map's lambda instead:

  $(document).ready(function () {
    $("#autocomplete").autocomplete({
        source: function(request, response) {
            fetch(`http://localhost:8000/similar/similar_results/?query=${request.term}`)
                .then(results => results.json())
                .then(results => results.map(result => result.name))
                .then(response);
        }
    });
  });

As a side note, try to avoid mixing promises with async/await syntax. Pick the one you prefer and keep it consistent. It will make the code a bit easier to maintain and may help you to prevent unexpected behaviour.

Related