How to match exact numerical pattern using FlexSearch

Viewed 226

Is there a way to have FlexSearch (https://github.com/nextapps-de/flexsearch) find only results that contains exact character sequence including numerical characters ?

The documentation is there : https://flexsearch.net/docs/flexsearch-manual.pdf There is a section page 35 called Analyzer that seems to give hints on how to do, but there is a TODO note at the end when is expected the list of Analyzer that we could try alternatively.

The following thread works fine only for plain characters : https://stackoverflow.com/a/69677055/2143734

Or if you know any equivalent efficient and browser compatible search API, it is just that I seem to miss something there !

If you input C3, 1, C0 or 4, you get results although none of these numbers appears...

var data = Array.from(Array(1000000).keys()).map(String);
data.unshift("CA", "VIS-CD", "CATDIR-U", "UE5", "GAE");

(function() {

  const index = new FlexSearch.Index({
    tokenize: "full",
    matcher: "default",
    cache: true
  });

  for (var i = 0; i < data.length; i++) {
    index.add(i, data[i]);
  }

  var suggestions = document.getElementById("suggestions");
  var userinput = document.getElementById("userinput");

  userinput.addEventListener("input", show_results, true);

  function show_results() {

    var value = this.value;
    var results = index.search(value);
    var entry, childs = suggestions.childNodes;
    var i = 0,
      len = results.length;

    for (; i < len; i++) {

      entry = childs[i];

      if (!entry) {
        entry = document.createElement("div");
        suggestions.appendChild(entry);
      }

      entry.textContent = data[results[i]];
    }

    while (childs.length > len) {
      suggestions.removeChild(childs[i])
    }
  }
}());
<!doctype html>
<html>

<head>
  <title>FlexSearch Sample</title>
  <script src="https://cdn.jsdelivr.net/gh/nextapps-de/flexsearch@master/dist/flexsearch.compact.js"></script>
</head>

<body>
  <input type="text" id="userinput" placeholder="Search by keyword...">
  <br></br>
  <div id="suggestions"></div>
</body>

</html>

1 Answers

find only results that contains exact character sequence

ok, so instead of Array.prototype.includes logic, I used a cache system for the kind of search you want :D

var data = Array.from(Array(1000000).keys()).map(String);
data.unshift("CA", "VIS-CD", "CATDIR-U", "UE5", "GAE");
//in essence, you can just change the condition(match_condition) to append a result based on whatever other condition you want
//the edit has me changing the match condition :D
//the edit also has me attempting a cache block(for speed)
(function() {
  var suggestions = document.getElementById("suggestions");
  var userinput = document.getElementById("userinput");
  
  var cacheText={}, resultLimit=100, length=Symbol(null) //I'm doing caching based on how you want results(as in your snippet this part will lag a bit)
  for(let i=0;i<data.length;i++){
    if(typeof data[i]!="string"){continue}
    for(let j=0;j<data[i].length;j++){
      let string="" //for caching all direct text indexes
      for(let k=0;k<=j;k++){string+=data[i][k]}
      if(!cacheText[string]){
        cacheText[string]={[data[i]]:true}
        cacheText[string][length]=1 //length measurement
      }
      else{
        if(cacheText[string][length]==resultLimit){continue}
        cacheText[string][data[i]]=true
        cacheText[string][length]++ //adding to length
      }
    }
  }
  
  userinput.addEventListener("input", show_results, true);

  function show_results() {
    var {value}=this, values={}
    let match_condition=(text)=> cacheText[value]?cacheText[value][text]:false && value!="" //match condition(that you can change to whatever other logic)
    for(let i=0; i<suggestions.children.length; i++){
      //the purpose of this loop is to only remove elements that won't be on the new result list
      let matchCondition=()=>
      !suggestions.children[i]? true: //test(if child still exists)
      match_condition(suggestions.children[i].innerText) //condition(in this case if data exactly includes user input)
      
      while(!matchCondition()){ suggestions.children[i].remove() } //remove only those which won't match up to the condition
      if(!suggestions.children[i]){break} //end loop(since if this is true, there is no child left)
      values[suggestions.children[i].innerText]=true //to indicate that this value already exists when doing the second loop below
    }
    for(let i=0; i<data.length; i++){
      //the purpose of this loop is to append UNIQUE results
      if(match_condition(data[i]) && !values[data[i]]){
        var child=document.createElement('div')
        child.innerText=data[i]
        suggestions.appendChild(child)
      }
    }
  }
}());
<body>
  <input type="text" id="userinput" placeholder="Search by keyword..." />
  <br></br>
  <div id="suggestions"></div>
</body>

Related