2017-08-31 97 views
3

我在做什么?

我在使用Electron在JavaScript中编写文件标记程序,我想使用SQLite。尽管如此,我无法弄清楚如何实现通过标签进行搜索。我不熟悉SQL和SQLite,所以我不确定这完全可以通过查询来完成。我如何去搜索如下所述?如何在SQLite中进行高级查询以通过标签搜索文件?

检索详情:

我看了一下FTS3/4。除了通配符搜索外,我可以做任何我想要的东西。

  • 搜索与所有指定标记的文件:blue_sky AND green_grass
  • 搜索文件而不给出标签:NOT blue_sky AND NOT green_grass
  • 搜索文件与一些给出标签:green_sky OR blue_sky
  • 使用通配符搜索文件在标签中:上述的*sky AND *grass AND *bl*e*
  • 组合:blue_sky AND green*/green_grass AND blue_sky OR green_sky

表:

可以改变

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 
); 

实例:

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); 

查询:blue_sky and green_grass
结果:file_1

查询:blue_sky or green_sky
结果:file_1, file_2, file_4

查询:blue_sky and green_grass or blue_grass
结果:file_1, file_2

查询:*ish*
结果:file_3

查询:*bl*e*
结果:file_1, file_2, file_3, file_4

查询:*sky and not blue_grass
结果:file_1, file3

注意:如果SQLite是不是因为工作的工具,我很开放的建议。

+0

通配符搜索可以通过使用'LIKE'来实现:https://www.tutorialspoint.com/sqlite/sqlite_like_clause.htm – juzraai

+0

我不确定关于sqlLite,因为我不使用它。你的数据库设计很好。它将使您能够达到您的既定目标。我认为是一个注意吸气剂是你使用JavaScript的意图。它运行在客户端上。数据库通常位于服务器上。也许Electron解决了这个问题。我不知道这是什么,所以我不能说。 –

+0

@juzraai我尝试过'LIKE',但我不确定如何使用它来构造一个查询,以满足我的其他条件。 – Yuki

回答

1

在我看来,您可以通过修改数据库结构来简化操作。
例如

  • 使用 '的file_id' 在这两种情况下
  • 使用外键 'TAG_ID' 一致,
    而不是有时 'ID'(诚然,可能需要一个不可用的功能)

你可能在前几种情况下,能够使用tag_ids作为inpit,具体取决于键的来源(当然,“一个非常用户”当然会输入颜色)。这也会降低错别字的风险。

所以,你可以做的是:在“file_tags”表

  • 使用连接,
    一个你想要在你的逻辑中使用的每个标签
  • 加入文件表,访问文件名字输出
  • 使用子查询使用,而不是标签ID 或使用标签名称更加入,而不是,我证明都低于
  • 相当直接复制搜索逻辑到“其中”
  • 组通过的文件名,为了得到每一个文件根据您不错的MCVE只有一次

,这里是你的榜样查询建议:

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') 
     ); 

输出:

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 

如果我另外加上:

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

然后最后的输出部分是:

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

使用SQLite 3.18.0

+0

我的答案比删除文件扩展名的编辑稍旧(尽管发布日期更近)。它不会更改查询代码。 – Yunnosch

+0

谢谢,这是一个非常好的答案。乍一看,它看起来正是我需要的东西。我将不得不花费这些查询来详细了解它们。但有一件事。在最后一个例子中,我期望文件1和3,而不是1,2和4.它看起来像你的查询应该产生我期望的输出。我不明白为什么没有。 – Yuki

+0

我没有注意到(不能说为什么)。原因是两种检查都是在每个标签上进行,而不是在一个组合上。加入file_tags两次需要看两者。我会稍后修复(从现在开始大约6小时)。 – Yunnosch