mardi 29 septembre 2015

Logical expressions in full text search in SQLite executed with PHP PDO

The logical expressions does not work as expected while full text search in SQLite database. Tested on XAMPP-PORTABLE 1.8.3 [PHP: 5.5.6].

Example data

There is table clubs with columns

  • name for the football club
  • city for the city
  • nations for the abbreviations of players' nationalities

What is important, the cities with be used for OR statements (example, London or Liverpool). The nationalities will be used for AND statements.

The PHP script that creates such table

<?php
$db = new  PDO('sqlite:mydb.sqlite3');
$sql = "CREATE VIRTUAL TABLE clubs USING fts4 " .
       "(name, city, nations)";
$db->exec($sql);

$sql = "DELETE FROM clubs";
$db->exec($sql);

$teams = array(
  array(
    "name" => "FC Chelsea", 
    "city" => "London",
    "nations" => "ENG BEL BIH FRA SEN ESP GHA SRB NIG COL" 
  ),
  array(
    "name" => "FC Arsenal", 
    "city" => "London",
    "nations" => "ENG COL CZE FRA GER BRA ESP WAL CHI CRC" 
  ),
  array(
    "name" => "Tottenham Hotspur", 
    "city" => "London",
    "nations" => "ENG FRA NED BEL ARG AUT WAL ALG DEN CAM KOR" 
  ),
  array(
    "name" => "West Ham United", 
    "city" => "London",
    "nations" => "ENG ESP IRL SUI NZL WAL ITA CAN SCO CAM SEN FRA ARG ECU" 
  ),
  array(
    "name" => "Manchester City", 
    "city" => "Manchester",
    "nations" => "ENG ARG BEL FRA SRB BRA CIV ESP NIG" 
  ),
  array(
    "name" => "Manchester United", 
    "city" => "Manchester",
    "nations" => "ENG ESP ARG NED ITA ECU FRA BEL GER BRA" 
  ),
  array(
    "name" => "FC Everton", 
    "city" => "Liverpool",
    "nations" => "ENG ESP USA ARG CRC IRL BIH RSA BEL SCO URU" 
  ),
  array(
    "name" => "FC Liverpool", 
    "city" => "Liverpool",
    "nations" => "ENG BRA HUN FRA CRO ESP SVK CIV GER WAL POR BEL" 
  ),
);

for($i=0; $i<count($teams); $i++) {
  $sql = "INSERT INTO clubs VALUES (?, ?, ?)";
  $stmt = $db->prepare($sql);
  $stmt->execute(
    array(
      $teams[$i]["name"], $teams[$i]["city"], $teams[$i]["nations"]
    )     
  );
}

Examples of logical expressions

The following function wil be used for the display of the results

function doQuery($db, $sql, $desc) {
  $arr = array();
  $q = $db->query($sql);
  $rows = $q->fetchAll(PDO::FETCH_ASSOC);
  echo "<tt>" . $sql . "</tt><br/>";
  echo "<b> " . $desc . " (" . count($rows) . "</b>): " ;
  foreach($rows as $row) {
    $arr[] = $row['name'];
  }
  $results = implode(", ", $arr);
  echo $results . "<br/>";  
}

In the following examples the results will be displayed with italic to distinguish them from the other text.

One logical expression statement

1.1. Search clubs with Spanish players

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('nations:ESP')
SQL;
doQuery($db, $sql, "ESP");

There are found all teams except Tottenham

ESP (7): FC Chelsea, FC Arsenal, West Ham United, Manchester City, Manchester United, FC Everton, FC Liverpool

1.2. Search clubs with Argentine players

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('nations:ARG')
SQL;
doQuery($db, $sql, "ARG");

Results are also correct.

ARG (5): Tottenham Hotspur, West Ham United, Manchester City, Manchester United, FC Everton

Two logical expressions statement

2.1. Search clubs from London OR Liverpool

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('city:London OR city:Liverpool')
SQL;
doQuery($db, $sql, "London or Liverpool");

No problem.

London or Liverpool (6): FC Chelsea, FC Arsenal, Tottenham Hotspur, West Ham United, FC Everton, FC Liverpool

2.2. Search clubs with Spanish players AND Argentine players

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('nations:ARG nations:ESP')
SQL;
doQuery($db, $sql, "ARG ESP");

Implicit AND operator works fine.

ARG ESP (4): West Ham United, Manchester City, Manchester United, FC Everton

but when go for explicit AND

2.3. Search clubs with Spanish players AND Argentine players (explicit)

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('nations:ARG AND nations:ESP')
SQL;
doQuery($db, $sql, "ARG and ESP");

No club is found.

ARG and ESP (0):

The explicit AND does not seem to work.

Three logical expressions statement

Now the results are unexpected

3.1. Search clubs from London OR clubs from Liverpool with Argentine players

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('city:London OR city:Liverpool nations:ARG')
SQL;
doQuery($db, $sql, "London or Liverpool and ARG");

We would have expected all London teams selected, but instead we get only those with Argentine players

London or Liverpool and ARG (3): Tottenham Hotspur, West Ham United, FC Everton

The same result is obtained for query

SELECT * FROM clubs WHERE clubs MATCH ('city:Liverpool OR city:London nations:ARG')

It looks like OR is executed before AND: (city:Liverpool OR city:London) AND nations:ARG. But what happens when the brackets are really used around OR-statement.

3.2. Search clubs (from London OR clubs from Liverpool) with Argentine players (brackets)

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('(city:Liverpool OR city:London) nations:ARG')
SQL;
doQuery($db, $sql, "(London or Liverpool) and ARG");

No club is found.

(London or Liverpool) and ARG (0):

What if the sequence of logical expressions changes

3.3 Search clubs from Liverpool with Argentine players or clubs from London.

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('city:Liverpool nations:ARG OR city:London')
SQL;
doQuery($db, $sql, "Liverpool and ARG or London");

Only team from Liverpool with Argentine players is found.

Liverpool and ARG or London (1): FC Everton

3.4. Same search with switched nations:ARG and city:Liverpool.

$sql = <<<SQL
SELECT * FROM clubs WHERE clubs MATCH ('nations:ARG city:Liverpool OR city:London')
SQL;
doQuery($db, $sql, "ARG and Liverpool or London");

The same result as in the 3.1. example.

ARG and Liverpool or London (3): Tottenham Hotspur, West Ham United, FC Everton

Question

I got no clue how logical expressions are interpreted. Can anybody explain?

Aucun commentaire:

Enregistrer un commentaire