2017-08-31 4 views
3

Was mache ich?

Ich schreibe ein Dateikennzeichnungsprogramm in JavaScript mit Electron und ich möchte SQLite verwenden. Ich kann mir nicht vorstellen, wie ich die Suche nach Tags implementieren würde. Ich bin neu in SQL und SQLite, daher bin ich mir nicht sicher, ob dies überhaupt nur durch Abfragen möglich ist. Wie kann ich wie unten beschrieben vorgehen?Wie kann ich erweiterte Abfragen in SQLite durchführen, um nach Dateien nach Tags zu suchen?

Suche Details:

Ich habe in FTS3/4 aussah. Ich kann damit alles machen, was ich will, AUSSER der Wildcard-Suche, von der Optik her.

  • Suche nach Dateien mit allen gegebenen Tags:blue_sky AND green_grass
  • Suche nach Dateien ohne bestimmte Tags:NOT blue_sky AND NOT green_grass
  • Suche nach Dateien mit einigen der gegebenen tags:green_sky OR blue_sky
  • suchen Sie nach Dateien mit Wildcards überall in einem Tag:*sky AND *grass AND *bl*e*
  • Kombinationen der oben:blue_sky AND green*/green_grass AND blue_sky OR green_sky

Tables:

Mai

CREATE TABLE files (
    id INTEGER PRIMARY KEY, 
    name TEXT 
); 

CREATE TABLE tags (
    id INTEGER PRIMARY KEY, 
    name TEXT 
); 

CREATE TABLE file_tags (
    id INTEGER PRIMARY KEY, 
    file_id INTEGER, 
    tag_id INTEGER 
); 

Beispiele geändert werden:

INSERT INTO files (name) VALUES ('file_1.png'); 
INSERT INTO files (name) VALUES ('file_2.png'); 
INSERT INTO files (name) VALUES ('file_3.png'); 
INSERT INTO files (name) VALUES ('file_4.png'); 

INSERT INTO tags (name) VALUES ('blue_sky'); 
INSERT INTO tags (name) VALUES ('green_sky'); 
INSERT INTO tags (name) VALUES ('green_grass'); 
INSERT INTO tags (name) VALUES ('blue_grass'); 
INSERT INTO tags (name) VALUES ('greenish_blue_sky'); 


INSERT INTO file_tags (file_id, tag_id) VALUES(file1_id, blue_sky_id); 
INSERT INTO file_tags (file_id, tag_id) VALUES(file1_id, green_grass_id); 

INSERT INTO file_tags (file_id, tag_id) VALUES(file2_id, blue_sky_id); 
INSERT INTO file_tags (file_id, tag_id) VALUES(file2_id, blue_grass_id); 

INSERT INTO file_tags (file_id, tag_id) VALUES(file3_id, greenish_blue_sky_id); 

INSERT INTO file_tags (file_id, tag_id) VALUES(file4_id, green_sky_id); 
INSERT INTO file_tags (file_id, tag_id) VALUES(file4_id, blue_grass_id); 

Abfrage: blue_sky and green_grass
Ergebnis: file_1

Abfrage: blue_sky or green_sky
Ergebnis: file_1, file_2, file_4

Abfrage: blue_sky and green_grass or blue_grass
Ergebnis: file_1, file_2

Abfrage: *ish*
Ergebnis: file_3

Abfrage: *bl*e*
Ergebnis: file_1, file_2, file_3, file_4

Abfrage: *sky and not blue_grass
Ergebnis: file_1, file3

Hinweis: Wenn SQLite nicht das richtige Werkzeug für den Job ist, ich bin offen für Vorschläge.

+0

Wildcard-Suche kann mit 'LIKE' erreicht werden: https://www.tutorialspoint.com/sqlite/sqlite_like_clause.htm – juzraai

+0

Ich bin mir über sqlLite nicht sicher, weil ich es nicht verwende. Ihr Datenbankdesign sieht gut aus. Es wird Ihnen ermöglichen, Ihre erklärten Ziele zu erreichen. Was ich als Aufmerksamkeits-Getter sehe, ist Ihre Absicht, Javascript zu verwenden. Es läuft auf Kunden. Datenbanken sitzen normalerweise auf Servern. Vielleicht löst Electron dieses Problem. Ich weiß nicht was es ist, also kann ich nicht sagen. –

+0

@juzraai Ich habe 'LIKE' versucht, aber ich bin mir nicht sicher, wie ich eine Abfrage strukturieren soll, die den Rest meiner Bedingungen erfüllt. – Yuki

Antwort

1

Meiner Meinung nach könnten Sie dies mit einer modifizierten Datenbankstruktur leichter erreichen.
Zum Beispiel


  • statt manchmal von 'id' konsequent 'file_id' und 'tag_id' verwendet wird, in beiden Fällen
  • Fremdschlüssel (zugegebenermaßen, dass möglicherweise eine nicht verfügbare Funktion erforderlich)

Das könnte dir in den ersten paar Fällen tag_ids als inpit verwenden können, abhängig davon, woher die Schlüssel kommen (ein "unerwarteter Benutzer" würde natürlich Farben eingeben). Das würde auch das Tippfehlerrisiko verringern.

Also, was Sie tun können, ist:

  • Verwendung schließt sich der Tisch ‚file_tags‘,
    eine für jeden Tag, das Sie in Ihrer Logik verwenden möchten
  • die Dateien Join-Tabelle, auf Datei zugreifen Namen für die Ausgabe
  • Verwendung von Unterabfragen für die Verwendung anstelle von Tag-IDs Tag-Namen oder verwenden mehr schließt sich stattdessen ich gezeigt, beide unter
  • kopieren Sie die Suchlogik ziemlich direkt in das ‚wo‘
  • Gruppe von Dateinamen, um jede Datei zu erhalten nur einmal

auf Ihrem schönen MCVE Basierend, hier sind Vorschläge für Ihre Beispielabfragen:

select fs.name from file_tags t1 
     inner join file_tags t2 on t1.file_id = t2.file_id 
     inner join files fs on fs.id = t1.file_id 
where t1.tag_id = (select id from tags where name = 'blue_sky') 
    and t2.tag_id = (select id from tags where name = 'green_grass') 
group by fs.name; 

select fs.name from file_tags t1 
     inner join file_tags t2 on t1.file_id = t2.file_id 
     inner join files fs on fs.id = t1.file_id 
where t1.tag_id = (select id from tags where name = 'blue_sky') 
    or t2.tag_id = (select id from tags where name = 'green_sky') 
group by fs.name; 

-- note, here I had to derive from your desired output 
-- that you want a '()' around the 'or' 
select fs.name from file_tags t1 
     inner join file_tags t2 on t1.file_id = t2.file_id 
     inner join file_tags t3 on t1.file_id = t3.file_id 
     inner join files fs on fs.id = t1.file_id 
where t1.tag_id = (select id from tags where name = 'blue_sky') 
and (t2.tag_id = (select id from tags where name = 'green_grass') 
    or t3.tag_id = (select id from tags where name = 'blue_grass') 
    ) 
group by fs.name; 

select fs.name from file_tags t1 
     inner join files fs on fs.id = t1.file_id 
     inner join tags ts on ts.id = t1.tag_id 
where ts.name like '%ish%' 
group by fs.name; 

select fs.name from file_tags t1 
     inner join files fs on fs.id = t1.file_id 
     inner join tags ts on ts.id = t1.tag_id 
where ts.name like '%bl%e%' 
group by fs.name; 

select fs.name from file_tags t1 
     inner join files fs on fs.id = t1.file_id 
     inner join tags ts on ts.id = t1.tag_id 
where ts.name like '%sky' and not ts.name = 'blue_grass' 
group by fs.name; 

select name from file_tags t1 
     inner join files fs on t1.file_id = fs.id 
where (select name from tags where id = t1.tag_id) like "%sky" 
and not file_id in 
     (select file_id from file_tags 
     where tag_id = (select id from tags where name = 'blue_grass') 
     ); 

Ausgang:

name 
---------- 
file_1.png 
name 
---------- 
file_1.png 
file_2.png 
file_4.png 
name 
---------- 
file_1.png 
file_2.png 
name 
---------- 
file_3.png 
name 
---------- 
file_1.png 
file_2.png 
file_3.png 
file_4.png 
name 
---------- 
file_1.png 

Wenn ich hinzufügen, zusätzlich:

INSERT INTO tags (name) VALUES ('greenish_blue_sky'); 
INSERT INTO file_tags (file_id, tag_id) VALUES(file3_id, greenish_blue_sky_id); 

Dann wird der letzte Ausgabeteil ist:

name 
---------- 
file_1.png 
file_3.png 

Mit SQLite 3.18.0

+0

Meine Antwort ist etwas älter (obwohl kürzlich gepostet) als Ihre Bearbeitung, die die Dateinamenerweiterungen entfernt. Es ändert den Abfragecode nicht. – Yunnosch

+0

Danke, das ist eine sehr nette Antwort. Auf den ersten Blick sieht es genau so aus, wie ich es brauche. Ich muss etwas mit diesen Abfragen spielen, um sie im Detail zu verstehen. Eine Sache jedoch. Im letzten Beispiel erwarte ich die Dateien 1 und 3, nicht 1, 2 und 4. Es sieht so aus, als ob Ihre Abfrage die erwartete Ausgabe erzeugen sollte. Ich verstehe nicht, warum es nicht so ist. – Yuki

+0

Ich habe nicht bemerkt (kann nicht sagen, warum). Der Grund dafür ist, dass beide Überprüfungen zusammen bei jedem Tag durchgeführt werden, nicht bei einer Kombination. Es ist erforderlich, die file_tags zweimal zu verbinden, um beide zu betrachten. Ich werde später reparieren (etwa 6h von jetzt an). – Yunnosch

Verwandte Themen