<?php
// manage the dynamical additional information in the left panel.
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
// error_reporting(-1);

// exemple call:
// http://blabla/LOCALDB/info_div.php?type=semantic&bi=0&query=[%22Monte%20Carlo%22]&gexf=%22line/AXA/RiskV2PageRank1000.gexf%22&index=ISItermsAxa_2015

include('tools.php');
include('parameters_details.php');

if ($output_mode == "json") {
  header('Content-Type: application/json');
}

if ($dbtype == "CortextDB") {
  $base = new PDO("sqlite:".$mainpath.$graphdb);
  include('default_div.php');
}

else {
  // to index: the union of "searchable columns" qcols for all nodetypes
  $idxcolsbytype = [];
  $altcsvsep = $csvsep;
  for ($i = 0; $i < $ntypes ; $i++) {

    // if nodetype is active
    if (count($my_conf["node".$i])) {
      // ... and well-formed
      if (array_key_exists('qcols', $my_conf["node".$i][$dbtype])) {
        $idxcolsbytype[$i] = $my_conf["node".$i][$dbtype]['qcols'];
      }
      else {
        echo("<p>Your settings for relatedDocsType are set on a local database,
            but your servermenu file does not provide any information about
            the CSV or DB table to query for related documents
            (on nodetypeId ".$i.")</p>");
      }

      // optional conf items: separator
        // FIXME: since conf is defined by node type,
        //  only the last separator will be taken into account
        //  but allowing two different separators contradicts
        //  the 1-base-per-graph structure in csv_indexation
      if (array_key_exists('delim', $my_conf["node".$i][$dbtype])) {
        $altcsvsep = $my_conf["node".$i][$dbtype]['delim'];
      }
    }
  }

  if (! $idxcolsbytype) {
    echo('<br> info_div.php (csv mode): please provide reldbqcols param in db.json <br>');
  }
  else {
    // DO THE INDEXATION (or RETRIEVE CACHED ONE)
    // we use cache if memcached is present (and if we indexed the csv already)
    include('csv_indexation.php');

    $csv_search_base = NULL;
    if(class_exists('Memcached')){
      $mcd = new Memcached;
      $mcd->addServer($memserver, $memport);

      // test if we indexed it already
      $csv_search_base_seri = $mcd->get(mem_entry_name($graphdb));

      if ($csv_search_base_seri !== False) {
        // echo("Using cached base<br>");
        $csv_search_base = json_decode($csv_search_base_seri, $assoc=true);
      }
    }

    if (! $csv_search_base) {
      // echo("Creating new base<br>");

      // indexing ----------------------------------------------------------------------
      // must know about all (sem+soc) cols typed, even if each search doesn't need them
      // echodump("filename CSV", $mainpath.$graphdb);
      $csv_search_base = parse_and_index_csv($mainpath.$graphdb,
                                             $idxcolsbytype,
                                            $altcsvsep, $csvquote);
      // -------------------------------------------------------------------------------

      if(class_exists('Memcached')){
        // **store** in cache for 2 min
        $mcd->set(mem_entry_name($graphdb), json_encode($csv_search_base), 120);
      }
    }

    $base = $csv_search_base[0];
    $postings = $csv_search_base[1];
    $idfvals = $csv_search_base[2];
    // echodump("postings", $postings);
    // echodump("base", $base);
    // echodump("idfvals", $idfvals);


    // DO THE SEARCH
    // -------------
    $searchcols = $my_conf["node".$ntid][$dbtype]['qcols'];
    //var_dump($searchcols);

    $sims = array();

    $totalfound=0;

    // ------------   Textual search -------------
    // Search $base cols using preg_match_all : see regex, PCRE mask¶

    // 1) Format input query into search string

    // get search operator if provided. Default is 'or'
    $searchop="or";
    $queryop_key="queryOp";

    if (array_key_exists( $queryop_key , $_GET )){
        $searchop = strtolower($_GET[$queryop_key]);
        if (!(in_array($searchop, array('and','or')))){
          echo "<br> Erreur : queryOp must be 'and' or 'or' <br>";
          return;
        }
    }
    //echodump("searchOp :", $searchop);

    // get and format input query string
    // ex input query string:
    // [\"medical device\"] or [\"anti-inflammatory\",\"non-specific anti-inflammatory\"]
    $searchinput = $_GET["query"];
    //echodump("Query :", $searchinput);
    // remove '"' around inner terms, and leading and trailing []
    $searchinput=str_replace("\"","",$searchinput);
    $searchinput=trim($searchinput,"[]");
    //echodump("Query :", $searchinput);

    // some checks : no <>(){}[]:=\/$*?+^| characters
    $regex_char="<>(){}[]:=\/$*.?+^|"; // Leave the '.' : eq to any char
    $check_pattern='/['.preg_quote($regex_char, '/').']/'; // escape all
    if (preg_match($check_pattern, $searchinput)){
      echo '<br> info_div.php (csv mode): The input query contains a forbidden character <br>';
      echodump("Query :", $searchinput);
      return;
    }

    // build regex pattern see https://www.php.net/manual/fr/book.pcre.php
    // PCRE for Perl Compatible Regular Expression
    // le and est plus compliqué que le et. Ci dessous, ça a l'air de marcher
    //$pattern="/(?=.*ceci)(?=.*test)/i";  $pattern="/(?=.*est)(?=.*ceci est un)/i";
    // "/(?=.*nutrition)(?=.*oral nutrition supplement)/i"
    if ($searchop === 'and'){ // (?=.*ceci)(?=.*test)
      $searchpattern = str_replace(",",")(?=.*",$searchinput);
      $searchpattern = "(?=.*".$searchpattern.")";
    }else{
      $searchpattern=str_replace(",","|",$searchinput);
    }
    $searchpattern="/".$searchpattern."/i"; // ex "/medical device/i"
    //echodump("Search pattern :", $searchpattern);

    // Search $searchpattern in $base. Update $sims if found.
    // $base is a 2D array : (array(array).
    // echodump("base", $base);
    // The first row is the first element in the $base array:
    // echodump("base row 0", $base[0]);
    // The column "abstract" for first row :
    // echodump("base row 0, 'abstract' col:", $base[0]["abstract"]);

    foreach ( $base  as $idrow => $row) {
      // test search text with : $row["abstract"]

      // search nb occurences. if > 1, update $sims
      $res=0;
      foreach ($searchcols as $idcol => $col){
        $res=$res+preg_match_all($searchpattern,$row[$col],$out);
      }
      if ($res > 0) {
        $docid = 'd'.$idrow;
        //echo "doc ". $docid.", ".$res ." occurence(s) found \n";
        $totalfound++;
        // build sims array similar to what token-based search does
        $sims[$docid] = $res;
      }
      //echo "id : ". $id . "\n";
      //echo "Abstract : ". $row["abstract"]. "\n";
    }
    // echo "total doc found :". $totalfound."\n";

    /*
    // ------- Token based search search ------

    // Split the query
    $qtokens = preg_split('/[\p{Z}\p{P}\p{C}]+/u', $_GET["query"]);
    // echodump("tockens :", $qtokens);

    // for each token, compute freq similarity per doc
    for ($k=0 ; $k < count($qtokens) ; $k++) {

      $tok = $qtokens[$k];

      if (! strlen($tok)) {
        continue;
      }

      $tok = strtolower($tok);

      // echodump("tok", $tok);
      // echodump("searchcols", $searchcols);

      $tfs_per_tok_and_doc[$tok] = array();

      for ($l=0 ; $l < count($searchcols) ; $l++) {

        // set of values we could find a match in
        $searchable = $postings[$ntid][$searchcols[$l]];

        if (array_key_exists($tok, $searchable)) {

          // matches
          $matching_docs = $searchable[$tok];

          foreach ($matching_docs as $doc_id => $tf) {

            // echodump("tok freq in this doc", $tf);

            // cumulated freq of tokens per doc
            if (array_key_exists($doc_id, $sims)) {
              $sims[$doc_id] += $tf * $idfvals[$tok];
            }
            else {
              $sims[$doc_id] = $tf * $idfvals[$tok];
            }
          }
        }

      }
    }
    */

    // c - sorted score per doc
    //
    arsort($sims);
    // echodump("sims", $sims);

    include ('csv_div.php');

  }
}

// just to make sure we use the same conventional name at cache read/write
function mem_entry_name($graphdbname) {
  return "twbackendmem/".$graphdbname."/csv_search_base";
}

?>
