Reputation: 726
I'm trying to figure out how to make a search engine for my media database.
The structure for the virtual folders is:
table directories
id name parent
The structure for the medias is:
table medias
id name parent
I'm using the following php function to list all my directories
function print_menu($id = 0) {
$sql = "SELECT directories.id AS id, directories.name AS name, directories.parent AS parent, directories.icon AS icon FROM directories
WHERE directories.parent = '".$id."' GROUP BY directories.id ORDER BY directories.name ASC";
$req = $cnx->prepare($sql);
$req->execute();
if ($req->rowCount()>0) {
echo "<ul>";
while ($data=$req->fetch()) {
echo "<li>".$data['name']."</li>";
print_menu($data['id']);
}
echo "</ul>";
}
}
Now lets say I got the following folders
And I want to search for medias named "%media_1%" in "project 1" including all its subfolders, how can I do that without making a query for each folder and subfolders? The level of subfolders can vary.
Upvotes: 1
Views: 111
Reputation: 15941
In general, with a "tree" table, to get a list of all descendants, and a known max depth you can construct a query like this:
SELECT layerN.ID
FROM theTable AS layer1
INNER JOIN theTable AS layer2
ON layer1.id = layer2.parent_id
OR layer1.id = layer2.id
INNER JOIN theTable AS layer3
ON layer2.id = layer3.parent_id
OR layer2.id = layer3.id
...
INNER JOIN theTable AS layerN
ON `layerN-1`.id = layerN.parent_id
OR `layerN-1`.id = layerN.id
WHERE layer1.parent_id = [chosen_parent_node_id]
;
Without the additional "OR" clauses in the ON conditions, intermediate nodes would not be included.
If intermediate nodes are not desired and the "leaf" nodes are of varying depths, the INNER JOIN
s will need to be LEFT JOIN
s, and the SELECT
field will be a bit more complicated:
SELECT IFNULL(layerN.id, IFNULL(`layerN-1`.id, IFNULL(....))) AS leafID
Alternately
You could have an additional table for summarizing total ancestry, the simplest version would have two fields ancestor_id
and descendant_id
(though you could include a "distance" as well). The biggest problem would be that it would need to be maintained through code of some form, triggers on the tree table would probably be the most effective and reliable method.
Upvotes: 1
Reputation: 726
Ok so I found a solution, I'm using the function posted above to get the ids of all subdirectories for a given directory, lets say "project 1" has the ID 42 I'll use
$all_ids = array();
function print_menu($id = 0) {
$sql = "SELECT directories.id AS id, directories.name AS name, directories.parent AS parent, directories.icon AS icon FROM directories
WHERE directories.parent = '".$id."' GROUP BY directories.id ORDER BY directories.name ASC";
$req = $cnx->prepare($sql);
$req->execute();
if ($req->rowCount()>0) {
while ($data=$req->fetch()) {
$all_ids[] = $data['id'];
print_menu($data['id']);
}
}
}
all_ids[] = 42;
print_menu(42)
Then I'm just doing a request like
SELECT * FROM medias WHERE medias.parent IN (".implode(',', $all_ids).") AND medias.name LIKE '%media_1%'
The request will only search for media in "project 1" and its subdirectories, no matter how many levels
Upvotes: 0