skip to Main Content

I’m not sure if this already exists in some other library, but what I am trying to do is implement a typesense-style search that gives you results as you type in your search term.

The source is a json object that will be fetched by the client when the window loads. It will have approximately 10K keys that I want to search through. For example: https://redditstatsbot.com/json/alphabetbot.json

What I don’t want to do is have to type in the full key. The user must be able to get their unique ID by typing in the first few letters of their ID.

For example, typing in "str" would yield all keys that start with "str", ["stranger", "strongman", "stranded", ... ] and then the user can select from the returned smaller list, whith the returned list growing or shrinking depending on the typed search term.

So basically, I don’t think that a full-blown Typesense implementation is necessary for my needs, because this is not a massive database, but I’m not really sure what my other options are.

3

Answers


  1. The HTML and CSS can be styled, but this is just the basic implementation of it. Pretty much all it does is check the search bar’s input every time something is inputted, and then whatever item in the search results array starts the same as the search bar’s input is added onto a separate array. Then with your search result’s container, it fills up the divs’ inside of it with the separate array items, given the amount of results.

    let results = ['stranger', 'strong', 'strength', 'stretch', 'stress'];
    document.getElementById('search_bar').addEventListener('input', function() {
      // Check if the input value's string starts the same as any search results
      let input = this.value;
      let possible_results = [];
      let resultsContainer = document.getElementById('search_results');
      let containerChildren = resultsContainer.children;
      for (let i = 0; i < results.length; i++) {
        if (results[i].startsWith(input)) {
          possible_results.push(results[i]);
        }
      }
      //Fill up empty spots
      for (let i = 0; i <= containerChildren.length - possible_results.length; i++) {
        possible_results.push("");
      }
      //Fill up search result divs with the actual results
      for (let i = 0; i < containerChildren.length; i++) {
        containerChildren[i].innerHTML = possible_results[i];
      }
    });
    <html>
    
    <body>
      <input id='search_bar' placeholder='Search Here'>
      <div id='search_results'>
        <div></div>
        <div></div>
      </div>
    </body>
    
    </html>
    Login or Signup to reply.
  2. This is certainly solvable:

    let items = ["commercially","important","edible","mushrooms","include","portobellos","agaricus","bisporus","whose","forms","include","button","mushrooms","cremini","and","baby","bellas","and","shiitake","lentinula","edode","the","morels","morchella","verpa","and","false","morels","or","lorchels","gyromitra","helvella","are","popularly","included","with","the","true","mushrooms","because","of","their","shape","and","fleshy","structure","they","resemble","a","deeply","folded","or","pitted","conelike","sponge","at","the","top","of","a","hollow","stem","some","are","among","the","most","highly","prized","edible","fungi","e.g.","morchella","esculenta","edible","truffles","various","tuber","species","which","hardly","resemble","mushrooms","are","also","popularly","labeled","as","such","these","and","other","edible","mushrooms","and","fungi","are","free","of","cholesterol","and","contain","small","amounts","of","essential","amino","acids","and","b","vitamins","however,","their","chief","worth","is","as","a","specialty","food","of","delicate","subtle","flavour","and","agreeable","texture","by","fresh","weight","the","common","commercially","grown","mushroom","is","more","than","90","percent","water","less","than","3","percent","protein","less","than","5","percent","carbohydrate","less","than","1","percent","fat","and","about","1","percent","mineral","salts","and","vitamins"];
    
    let previews = document.getElementsByClassName("preview");
    
    for (let preview of previews) {
        preview.addEventListener("click", function() {
            if (this.innerText) {
                document.getElementById("search").value = this.innerText;
            }
            for (let p of previews) p.innerText = "";
        });
    }
    
    function search(val) {
        let results = [];
        for (let i = 0; i < items.length; i++) {
            if (items[i].indexOf(val) >= 0) {
                results.push(items[i]);
                if (results.length >= 10) {
                    i = items.length;
                }
            }
        }
        for (let i = 0; i < previews.length; i++) {
            previews[i].innerText = ((results.length > i) ? results[i] : "");
        }
    }
    .preview {
        border: 1px color black;
        cursor: pointer;
    }
    
    .preview:hover {
        background-color: green;
    }
    <input type="text" id="search" oninput="search(this.value)">
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>
    <div class="preview"></div>

    The above is an unoptimized proof-of-concept. We create events for previews so if you click them, then they refresh the search box and disappear. On the other hand, changing the search text induces a search for the first 10 matches.

    You should try out something like this and see whether it works well for your large inputs. If not, then you will need to optimize performance.

    Login or Signup to reply.
  3. A useful solution maybe to use something like minisearch package. Assuming you are loading an array of objects, you can try something like:

    /* Data object example
      {
       "field1": "value1",
       "field2": "value2",
       ... other fields
      }
    */
    const dataArray = [ /* data objects ... */ ];
    
    // initialize and load
    const searchDb = new Minisearch({
      fields: ["field1"], // where "field1" is some field in each object
      storeFields: ["field1", "field2"], // fields to return in query response
    });
    searchDb.addAll(dataArray);
    
    // search
    const results = searchDb.search("value1"); // { "field1": "value1", "field2": "value2" }
    

    To use the package, make sure to include it as a script in your HTML:

    <script src="https://cdn.jsdelivr.net/npm/minisearch@YOUR_SPECIFIC_VERSION/dist/umd/index.min.js"></script>
    

    You can query on keyup event on the search input and use the results to display a dropdown selector. There are many good examples for that so I am leaving it out.

    This package can also handle prefix and fuzzy search. Hope that helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search