diff --git a/src/jyut-dict/jyut-dict.pro b/src/jyut-dict/jyut-dict.pro index 8f3fe507..6928a442 100644 --- a/src/jyut-dict/jyut-dict.pro +++ b/src/jyut-dict/jyut-dict.pro @@ -210,6 +210,7 @@ HEADERS += \ logic/search/isearchobservable.h \ logic/search/isearchobserver.h \ logic/search/isearchoptionsmediator.h \ + logic/search/searchqueries.h \ logic/search/searchoptionsmediator.h \ logic/search/searchparameters.h \ logic/search/sqlsearch.h \ @@ -364,11 +365,6 @@ unix|win32:!macx { QMAKE_EXTRA_COMPILERS += copy_settings } -# Default rules for deployment. -#qnx: target.path = /tmp/$${TARGET}/bin -#else: unix:!android: target.path = /opt/$${TARGET}/bin -#!isEmpty(target.path): INSTALLS += target - DISTFILES += \ resources/images/chevron-down_inverted.png \ resources/images/chevron-up_inverted.png diff --git a/src/jyut-dict/logic/search/searchqueries.h b/src/jyut-dict/logic/search/searchqueries.h new file mode 100644 index 00000000..49e95fe3 --- /dev/null +++ b/src/jyut-dict/logic/search/searchqueries.h @@ -0,0 +1,1016 @@ +constexpr auto SEARCH_SIMPLIFIED_QUERY + = "WITH " + " matching_entry_ids AS ( " + " SELECT rowid FROM entries WHERE simplified GLOB ? " + " ), " + " matching_definition_ids AS ( " + " SELECT definition_id, definition " + " FROM definitions " + " WHERE fk_entry_id IN matching_entry_ids " + " ), " + " matching_chinese_sentence_ids AS ( " + " SELECT definition_id, fk_chinese_sentence_id " + " FROM " + " matching_definition_ids AS mdi " + " JOIN definitions_chinese_sentences_links AS dcsl " + " ON mdi.definition_id = dcsl.fk_definition_id " + " ), " + " matching_translations AS ( " + " SELECT " + " mcsi.fk_chinese_sentence_id, " + " json_group_array( " + " DISTINCT json_object( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " JOIN sentence_links AS sl " + " ON mcsi.fk_chinese_sentence_id = sl.fk_chinese_sentence_id " + " JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " GROUP BY mcsi.fk_chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT fk_chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " json_object( " + " 'traditional', " + " traditional, " + " 'simplified', " + " simplified, " + " 'pinyin', " + " pinyin, " + " 'jyutping', " + " jyutping, " + " 'language', " + " language, " + " 'translations', " + " json(translation) " + " ) AS sentence " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.fk_chinese_sentence_id " + " ), " + " matching_definitions AS ( " + " SELECT " + " definition_id, " + " fk_entry_id, " + " fk_source_id, " + " definition, " + " label " + " FROM definitions " + " WHERE " + " definitions.definition_id IN ( " + " SELECT definition_id FROM matching_definition_ids " + " ) " + " ), " + " matching_definitions_with_sentences AS ( " + " SELECT " + " fk_entry_id, " + " fk_source_id, " + " json_object( " + " 'definition', " + " definition, " + " 'label', " + " label, " + " 'sentences', " + " json_group_array(json(sentence)) " + " ) AS definition " + " FROM " + " matching_definitions AS md " + " LEFT JOIN matching_chinese_sentence_ids AS mcsi " + " ON md.definition_id = mcsi.definition_id " + " LEFT JOIN matching_sentences_with_translations AS mswt " + " ON mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " + " GROUP BY md.definition_id " + " ), " + " matching_definition_groups AS ( " + " SELECT " + " fk_entry_id, " + " json_object( " + " 'source', " + " sourcename, " + " 'definitions', " + " json_group_array(json(definition)) " + " ) AS definitions " + " FROM " + " matching_definitions_with_sentences AS mdws " + " LEFT JOIN sources " + " ON sources.source_id = mdws.fk_source_id " + " GROUP BY fk_entry_id, fk_source_id " + " ), " + " matching_entries AS ( " + " SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " json_group_array(json(definitions)) AS definitions " + " FROM " + " matching_definition_groups AS mdg " + " LEFT JOIN entries " + " ON entries.entry_id = mdg.fk_entry_id " + " GROUP BY entry_id " + " ORDER BY frequency DESC " + " ) " + "SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " definitions " + "FROM matching_entries; "; + +constexpr auto SEARCH_TRADITIONAL_QUERY + = "WITH " + " matching_entry_ids AS ( " + " SELECT rowid FROM entries WHERE traditional GLOB ? " + " ), " + " matching_definition_ids AS ( " + " SELECT definition_id, definition " + " FROM definitions " + " WHERE fk_entry_id IN matching_entry_ids " + " ), " + " matching_chinese_sentence_ids AS ( " + " SELECT definition_id, fk_chinese_sentence_id " + " FROM " + " matching_definition_ids AS mdi " + " JOIN definitions_chinese_sentences_links AS dcsl " + " ON mdi.definition_id = dcsl.fk_definition_id " + " ), " + " matching_translations AS ( " + " SELECT " + " mcsi.fk_chinese_sentence_id, " + " json_group_array( " + " DISTINCT json_object( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " JOIN sentence_links AS sl " + " ON mcsi.fk_chinese_sentence_id = sl.fk_chinese_sentence_id " + " JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " GROUP BY mcsi.fk_chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT fk_chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " json_object( " + " 'traditional', " + " traditional, " + " 'simplified', " + " simplified, " + " 'pinyin', " + " pinyin, " + " 'jyutping', " + " jyutping, " + " 'language', " + " language, " + " 'translations', " + " json(translation) " + " ) AS sentence " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.fk_chinese_sentence_id " + " ), " + " matching_definitions AS ( " + " SELECT " + " definition_id, " + " fk_entry_id, " + " fk_source_id, " + " definition, " + " label " + " FROM definitions " + " WHERE " + " definitions.definition_id IN ( " + " SELECT definition_id FROM matching_definition_ids " + " ) " + " ), " + " matching_definitions_with_sentences AS ( " + " SELECT " + " fk_entry_id, " + " fk_source_id, " + " json_object( " + " 'definition', " + " definition, " + " 'label', " + " label, " + " 'sentences', " + " json_group_array(json(sentence)) " + " ) AS definition " + " FROM " + " matching_definitions AS md " + " LEFT JOIN matching_chinese_sentence_ids AS mcsi " + " ON md.definition_id = mcsi.definition_id " + " LEFT JOIN matching_sentences_with_translations AS mswt " + " ON mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " + " GROUP BY md.definition_id " + " ), " + " matching_definition_groups AS ( " + " SELECT " + " fk_entry_id, " + " json_object( " + " 'source', " + " sourcename, " + " 'definitions', " + " json_group_array(json(definition)) " + " ) AS definitions " + " FROM " + " matching_definitions_with_sentences AS mdws " + " LEFT JOIN sources " + " ON sources.source_id = mdws.fk_source_id " + " GROUP BY fk_entry_id, fk_source_id " + " ), " + " matching_entries AS ( " + " SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " json_group_array(json(definitions)) AS definitions " + " FROM " + " matching_definition_groups AS mdg " + " LEFT JOIN entries " + " ON entries.entry_id = mdg.fk_entry_id " + " GROUP BY entry_id " + " ORDER BY frequency DESC " + " ) " + "SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " definitions " + "FROM matching_entries; "; + +constexpr auto SEARCH_JYUTPING_QUERY + = "WITH " + " matching_entry_ids AS ( " + " SELECT rowid FROM entries WHERE jyutping GLOB ? " + " ), " + " matching_definition_ids AS ( " + " SELECT definition_id, definition " + " FROM definitions " + " WHERE fk_entry_id IN matching_entry_ids " + " ), " + " matching_chinese_sentence_ids AS ( " + " SELECT definition_id, fk_chinese_sentence_id " + " FROM " + " matching_definition_ids AS mdi " + " JOIN definitions_chinese_sentences_links AS dcsl " + " ON mdi.definition_id = dcsl.fk_definition_id " + " ), " + " matching_translations AS ( " + " SELECT " + " mcsi.fk_chinese_sentence_id, " + " json_group_array( " + " DISTINCT json_object( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " JOIN sentence_links AS sl " + " ON mcsi.fk_chinese_sentence_id = sl.fk_chinese_sentence_id " + " JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " GROUP BY mcsi.fk_chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT fk_chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " json_object( " + " 'traditional', " + " traditional, " + " 'simplified', " + " simplified, " + " 'pinyin', " + " pinyin, " + " 'jyutping', " + " jyutping, " + " 'language', " + " language, " + " 'translations', " + " json(translation) " + " ) AS sentence " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.fk_chinese_sentence_id " + " ), " + " matching_definitions AS ( " + " SELECT " + " definition_id, " + " fk_entry_id, " + " fk_source_id, " + " definition, " + " label " + " FROM definitions " + " WHERE " + " definitions.definition_id IN ( " + " SELECT definition_id FROM matching_definition_ids " + " ) " + " ), " + " matching_definitions_with_sentences AS ( " + " SELECT " + " fk_entry_id, " + " fk_source_id, " + " json_object( " + " 'definition', " + " definition, " + " 'label', " + " label, " + " 'sentences', " + " json_group_array(json(sentence)) " + " ) AS definition " + " FROM " + " matching_definitions AS md " + " LEFT JOIN matching_chinese_sentence_ids AS mcsi " + " ON md.definition_id = mcsi.definition_id " + " LEFT JOIN matching_sentences_with_translations AS mswt " + " ON mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " + " GROUP BY md.definition_id " + " ), " + " matching_definition_groups AS ( " + " SELECT " + " fk_entry_id, " + " json_object( " + " 'source', " + " sourcename, " + " 'definitions', " + " json_group_array(json(definition)) " + " ) AS definitions " + " FROM " + " matching_definitions_with_sentences AS mdws " + " LEFT JOIN sources " + " ON sources.source_id = mdws.fk_source_id " + " GROUP BY fk_entry_id, fk_source_id " + " ), " + " matching_entries AS ( " + " SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " json_group_array(json(definitions)) AS definitions " + " FROM " + " matching_definition_groups AS mdg " + " LEFT JOIN entries " + " ON entries.entry_id = mdg.fk_entry_id " + " GROUP BY entry_id " + " ORDER BY frequency DESC " + " ) " + "SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " definitions " + "FROM matching_entries; "; + +constexpr auto SEARCH_PINYIN_QUERY + = "WITH " + " matching_entry_ids AS ( " + " SELECT " + " rowid " + " FROM entries " + " WHERE pinyin GLOB ? " + " ), " + " matching_definition_ids AS ( " + " SELECT " + " definition_id, " + " definition " + " FROM definitions " + " WHERE fk_entry_id IN matching_entry_ids " + " ), " + " matching_chinese_sentence_ids AS ( " + " SELECT " + " definition_id, " + " fk_chinese_sentence_id " + " FROM " + " matching_definition_ids AS mdi " + " JOIN definitions_chinese_sentences_links AS dcsl " + " ON mdi.definition_id = dcsl.fk_definition_id " + " ), " + " matching_translations AS ( " + " SELECT " + " mcsi.fk_chinese_sentence_id, " + " json_group_array( " + " DISTINCT json_object( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " JOIN sentence_links AS sl " + " ON mcsi.fk_chinese_sentence_id = sl.fk_chinese_sentence_id " + " JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " GROUP BY mcsi.fk_chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT " + " fk_chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " json_object( " + " 'traditional', " + " traditional, " + " 'simplified', " + " simplified, " + " 'pinyin', " + " pinyin, " + " 'jyutping', " + " jyutping, " + " 'language', " + " language, " + " 'translations', " + " json(translation) " + " ) AS sentence " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.fk_chinese_sentence_id " + " ), " + " matching_definitions AS ( " + " SELECT " + " definition_id, " + " fk_entry_id, " + " fk_source_id, " + " definition, " + " label " + " FROM definitions " + " WHERE " + " definitions.definition_id IN ( " + " SELECT " + " definition_id " + " FROM matching_definition_ids " + " ) " + " ), " + " matching_definitions_with_sentences AS ( " + " SELECT " + " fk_entry_id, " + " fk_source_id, " + " json_object( " + " 'definition', " + " definition, " + " 'label', " + " label, " + " 'sentences', " + " json_group_array(json(sentence)) " + " ) AS definition " + " FROM " + " matching_definitions AS md " + " LEFT JOIN matching_chinese_sentence_ids AS mcsi " + " ON md.definition_id = mcsi.definition_id " + " LEFT JOIN matching_sentences_with_translations AS mswt " + " ON mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " + " GROUP BY md.definition_id " + " ), " + " matching_definition_groups AS ( " + " SELECT " + " fk_entry_id, " + " json_object( " + " 'source', " + " sourcename, " + " 'definitions', " + " json_group_array(json(definition)) " + " ) AS definitions " + " FROM " + " matching_definitions_with_sentences AS mdws " + " LEFT JOIN sources " + " ON sources.source_id = mdws.fk_source_id " + " GROUP BY fk_entry_id, fk_source_id " + " ), " + " matching_entries AS ( " + " SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " json_group_array(json(definitions)) AS definitions " + " FROM " + " matching_definition_groups AS mdg " + " LEFT JOIN entries " + " ON entries.entry_id = mdg.fk_entry_id " + " GROUP BY entry_id " + " ORDER BY frequency DESC " + " ) " + "SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " definitions " + "FROM matching_entries; "; + +constexpr auto SEARCH_ENGLISH_QUERY + = "WITH " + " matching_entry_ids AS ( " + " SELECT " + " fk_entry_id, " + " rowid AS definition_id, " + " bm25(definitions_fts, 0, 1) AS RANK " + " FROM definitions_fts " + " WHERE definitions_fts MATCH ? AND definition LIKE ? " + " ), " + " matching_definition_ids AS ( " + " SELECT " + " definition_id, " + " definition " + " FROM definitions " + " WHERE " + " fk_entry_id IN ( " + " SELECT " + " fk_entry_id " + " FROM matching_entry_ids " + " ) " + " ), " + " definitions_and_ranks AS ( " + " SELECT " + " mdi.definition_id AS definition_id, " + " mdi.definition AS definition, " + " mei.rank AS RANK " + " FROM " + " matching_definition_ids AS mdi " + " LEFT JOIN matching_entry_ids AS mei " + " ON mdi.definition_id = mei.definition_id " + " ), " + " matching_chinese_sentence_ids AS ( " + " SELECT " + " definition_id, " + " fk_chinese_sentence_id " + " FROM " + " definitions_and_ranks AS dar " + " JOIN definitions_chinese_sentences_links AS dcsl " + " ON dar.definition_id = dcsl.fk_definition_id " + " ), " + " matching_translations AS ( " + " SELECT " + " mcsi.fk_chinese_sentence_id, " + " JSON_GROUP_ARRAY( " + " DISTINCT JSON_OBJECT( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " JOIN sentence_links AS sl " + " ON mcsi.fk_chinese_sentence_id = sl.fk_chinese_sentence_id " + " JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " GROUP BY mcsi.fk_chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT " + " fk_chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " JSON_OBJECT( " + " 'traditional', " + " traditional, " + " 'simplified', " + " simplified, " + " 'pinyin', " + " pinyin, " + " 'jyutping', " + " jyutping, " + " 'language', " + " language, " + " 'translations', " + " JSON(translation) " + " ) AS sentence " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.fk_chinese_sentence_id " + " ), " + " matching_definitions AS ( " + " SELECT " + " dar.definition_id, " + " d.fk_entry_id, " + " d.fk_source_id, " + " d.definition, " + " d.label, " + " RANK " + " FROM " + " definitions_and_ranks AS dar " + " JOIN definitions AS d " + " ON dar.definition_id = d.definition_id " + " ), " + " matching_definitions_with_sentences AS ( " + " SELECT " + " fk_entry_id, " + " fk_source_id, " + " md.rank AS RANK, " + " JSON_OBJECT( " + " 'definition', " + " definition, " + " 'label', " + " label, " + " 'sentences', " + " JSON_GROUP_ARRAY(JSON(sentence)) " + " ) AS definition " + " FROM " + " matching_definitions AS md " + " LEFT JOIN matching_chinese_sentence_ids AS mcsi " + " ON md.definition_id = mcsi.definition_id " + " LEFT JOIN matching_sentences_with_translations AS mswt " + " ON mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " + " GROUP BY md.definition_id " + " ), " + " matching_definition_groups AS ( " + " SELECT " + " fk_entry_id, " + " CASE sourceshortname " + " WHEN 'ABY' THEN AVG(mdws.rank) * 3 " + " WHEN 'CCY' THEN AVG(mdws.rank) * 3 " + " WHEN 'WHK' THEN AVG(mdws.rank) * 3 " + " ELSE AVG(mdws.rank) " + " END AS RANK, " + " JSON_OBJECT( " + " 'source', " + " sourcename, " + " 'definitions', " + " JSON_GROUP_ARRAY(JSON(definition)) " + " ) AS definitions " + " FROM " + " matching_definitions_with_sentences AS mdws " + " LEFT JOIN sources " + " ON sources.source_id = mdws.fk_source_id " + " GROUP BY fk_entry_id, fk_source_id " + " ), " + " matching_entries AS ( " + " SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " SUM(RANK) AS RANK, " + " JSON_GROUP_ARRAY(JSON(definitions)) AS definitions " + " FROM " + " matching_definition_groups AS mdg " + " LEFT JOIN entries " + " ON entries.entry_id = mdg.fk_entry_id " + " GROUP BY entry_id " + " ORDER BY RANK ASC, frequency DESC " + " ) " + "SELECT " + " RANK, " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " definitions " + "FROM matching_entries; "; + +constexpr auto SEARCH_UNIQUE_QUERY + = "WITH " + " matching_entry_ids AS ( " + " SELECT rowid " + " FROM entries " + " WHERE " + " simplified LIKE ? " + " AND traditional LIKE ? " + " AND jyutping LIKE ? " + " AND pinyin LIKE ? " + " ), " + " matching_definition_ids AS ( " + " SELECT definition_id, definition " + " FROM definitions " + " WHERE fk_entry_id IN matching_entry_ids " + " ), " + " matching_chinese_sentence_ids AS ( " + " SELECT definition_id, fk_chinese_sentence_id " + " FROM " + " matching_definition_ids AS mdi " + " JOIN definitions_chinese_sentences_links AS dcsl " + " ON mdi.definition_id = dcsl.fk_definition_id " + " ), " + " matching_translations AS ( " + " SELECT " + " mcsi.fk_chinese_sentence_id, " + " json_group_array( " + " DISTINCT json_object( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " JOIN sentence_links AS sl " + " ON mcsi.fk_chinese_sentence_id = sl.fk_chinese_sentence_id " + " JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " GROUP BY mcsi.fk_chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT fk_chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " json_object( " + " 'traditional', " + " traditional, " + " 'simplified', " + " simplified, " + " 'pinyin', " + " pinyin, " + " 'jyutping', " + " jyutping, " + " 'language', " + " language, " + " 'translations', " + " json(translation) " + " ) AS sentence " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.fk_chinese_sentence_id " + " ), " + " matching_definitions AS ( " + " SELECT " + " definition_id, " + " fk_entry_id, " + " fk_source_id, " + " definition, " + " label " + " FROM definitions " + " WHERE " + " definitions.definition_id IN ( " + " SELECT definition_id FROM matching_definition_ids " + " ) " + " ), " + " matching_definitions_with_sentences AS ( " + " SELECT " + " md.fk_entry_id, " + " md.fk_source_id, " + " json_object( " + " 'definition', " + " md.definition, " + " 'label', " + " label, " + " 'sentences', " + " json_group_array(json(sentence)) " + " ) AS definition " + " FROM " + " matching_definitions AS md " + " LEFT JOIN matching_chinese_sentence_ids AS mcsi " + " ON md.definition_id = mcsi.definition_id " + " LEFT JOIN matching_sentences_with_translations AS mswt " + " ON mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " + " GROUP BY md.definition_id " + " ), " + " matching_definition_groups AS ( " + " SELECT " + " fk_entry_id, " + " json_object( " + " 'source', " + " sourcename, " + " 'definitions', " + " json_group_array(json(definition)) " + " ) AS definitions " + " FROM " + " matching_definitions_with_sentences AS mdws " + " LEFT JOIN sources " + " ON sources.source_id = mdws.fk_source_id " + " GROUP BY fk_entry_id, fk_source_id " + " ), " + " matching_entries AS ( " + " SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " json_group_array(json(definitions)) AS definitions " + " FROM " + " matching_definition_groups AS mdg " + " LEFT JOIN entries " + " ON entries.entry_id = mdg.fk_entry_id " + " GROUP BY entry_id " + " ORDER BY frequency DESC " + " ) " + "SELECT " + " simplified, " + " traditional, " + " jyutping, " + " pinyin, " + " definitions " + "FROM matching_entries; "; + +constexpr auto SEARCH_TRADITIONAL_SENTENCES_QUERY + = "WITH " + " matching_chinese_sentence_ids AS ( " + " SELECT chinese_sentence_id " + " FROM chinese_sentences " + " WHERE traditional LIKE ? ESCAPE '\\' " + " ), " + " translations_with_source AS ( " + " SELECT " + " s.sourcename AS source, " + " mcsi.chinese_sentence_id AS chinese_sentence_id, " + " json_group_array( " + " DISTINCT json_object( " + " 'sentence', " + " sentence, " + " 'language', " + " language, " + " 'direct', " + " direct " + " ) " + " ) AS translation " + " FROM " + " matching_chinese_sentence_ids AS mcsi " + " LEFT JOIN sentence_links AS sl " + " ON mcsi.chinese_sentence_id = sl.fk_chinese_sentence_id " + " LEFT JOIN nonchinese_sentences AS ncs " + " ON ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " + " LEFT JOIN sources AS s " + " ON s.source_id = sl.fk_source_id " + " GROUP BY s.sourcename, mcsi.chinese_sentence_id " + " ), " + " matching_translations AS ( " + " SELECT " + " chinese_sentence_id, " + " json_group_array( " + " json_object( " + " 'source', " + " source, " + " 'translations', " + " json(translation) " + " ) " + " ) AS translations " + " FROM translations_with_source AS tws " + " GROUP BY chinese_sentence_id " + " ), " + " matching_sentences AS ( " + " SELECT " + " chinese_sentence_id, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language " + " FROM chinese_sentences AS cs " + " WHERE " + " chinese_sentence_id IN ( " + " SELECT chinese_sentence_id " + " FROM matching_chinese_sentence_ids " + " ) " + " ), " + " matching_sentences_with_translations AS ( " + " SELECT " + " max(sourcename) AS sourcename, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language, " + " translations " + " FROM " + " matching_sentences AS ms " + " LEFT JOIN matching_translations AS mt " + " ON ms.chinese_sentence_id = mt.chinese_sentence_id " + " LEFT JOIN definitions_chinese_sentences_links AS dcsl " + " ON ms.chinese_sentence_id = dcsl.fk_chinese_sentence_id " + " LEFT JOIN definitions AS d " + " ON dcsl.fk_definition_id = d.definition_id " + " LEFT JOIN sources AS s ON d.fk_source_id = s.source_id " + " GROUP BY " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language, " + " translations " + " ORDER BY ms.chinese_sentence_id " + " ) " + "SELECT " + " sourcename, " + " traditional, " + " simplified, " + " pinyin, " + " jyutping, " + " language, " + " translations " + "FROM matching_sentences_with_translations; "; diff --git a/src/jyut-dict/logic/search/sqlsearch.cpp b/src/jyut-dict/logic/search/sqlsearch.cpp index 49287dba..6041b647 100644 --- a/src/jyut-dict/logic/search/sqlsearch.cpp +++ b/src/jyut-dict/logic/search/sqlsearch.cpp @@ -1,6 +1,7 @@ #include "sqlsearch.h" #include "logic/database/queryparseutils.h" +#include "logic/search/searchqueries.h" #include "logic/utils/chineseutils.h" #include "logic/utils/utils.h" @@ -215,116 +216,7 @@ void SQLSearch::searchSimplifiedThread(const QString &searchTerm, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - //// Get list of entry ids whose simplified form matches the query - "WITH matching_entry_ids AS ( " - " SELECT rowid FROM entries WHERE simplified GLOB ?" - "), " - " " - //// Get the list of all definitions for those entries - //// This CTE is used multiple times; would be nice if could materialize it - "matching_definition_ids AS ( " - " SELECT definition_id, definition FROM definitions WHERE fk_entry_id " - " IN matching_entry_ids " - "), " - " " - //// Get corresponding sentence ids for each of those definitions - //// This CTE is used multiple times; would be nice if could materialize it - "matching_chinese_sentence_ids AS ( " - " SELECT definition_id, fk_chinese_sentence_id " - " FROM matching_definition_ids AS mdi " - " JOIN definitions_chinese_sentences_links AS dcsl ON " - " mdi.definition_id = dcsl.fk_definition_id " - "), " - " " - //// Get translations for each of the sentences - "matching_translations AS ( " - " SELECT mcsi.fk_chinese_sentence_id, " - " json_group_array(DISTINCT " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct " - " )) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " JOIN sentence_links AS sl ON mcsi.fk_chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " JOIN nonchinese_sentences AS ncs ON ncs.non_chinese_sentence_id = " - " sl.fk_non_chinese_sentence_id " - " GROUP BY mcsi.fk_chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence ids - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT fk_chinese_sentence_id FROM " - " matching_chinese_sentence_ids " - " ) " - ")," - " " - //// Get translations for each of those sentences - "matching_sentences_with_translations AS ( " - " SELECT chinese_sentence_id, " - " json_object('traditional', traditional, " - " 'simplified', simplified, " - " 'pinyin', pinyin, " - " 'jyutping', jyutping, " - " 'language', language, " - " 'translations', json(translation)) AS sentence " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.fk_chinese_sentence_id " - "), " - " " - //// Get definition data for each matching definition - "matching_definitions AS ( " - " SELECT definition_id, fk_entry_id, fk_source_id, definition, " - " label " - " FROM definitions " - " WHERE definitions.definition_id IN ( " - " SELECT definition_id FROM matching_definition_ids" - " ) " - "), " - " " - //// Create definition object with sentences for each definition - "matching_definitions_with_sentences AS ( " - " SELECT fk_entry_id, fk_source_id, " - " json_object('definition', definition, " - " 'label', label, 'sentences', " - " json_group_array(json(sentence))) AS definition " - " FROM matching_definitions AS md " - " LEFT JOIN matching_chinese_sentence_ids AS mcsi ON " - " md.definition_id = mcsi.definition_id " - " LEFT JOIN matching_sentences_with_translations AS mswt ON " - " mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " - " GROUP BY md.definition_id " - "), " - " " - //// Create definition groups for definitions of the same entry that come from the same source - "matching_definition_groups AS ( " - " SELECT fk_entry_id, " - " json_object('source', sourcename, " - " 'definitions', " - " json_group_array(json(definition))) AS definitions " - " FROM matching_definitions_with_sentences AS mdws " - " LEFT JOIN sources ON sources.source_id = mdws.fk_source_id " - " GROUP BY fk_entry_id, fk_source_id " - "), " - " " - //// Construct the final entry object - "matching_entries AS ( " - " SELECT simplified, traditional, jyutping, pinyin, " - " json_group_array(json(definitions)) AS definitions " - " FROM matching_definition_groups AS mdg " - " LEFT JOIN entries ON entries.entry_id = mdg.fk_entry_id " - " GROUP BY entry_id " - " ORDER BY frequency DESC " - ") " - " " - "SELECT simplified, traditional, jyutping, pinyin, definitions FROM " - " matching_entries"); + query.prepare(SEARCH_SIMPLIFIED_QUERY); if (searchExactMatch) { query.addBindValue(searchTermWithoutQuotes); } else if (dontAppendWildcard) { @@ -367,116 +259,7 @@ void SQLSearch::searchTraditionalThread(const QString &searchTerm, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - //// Get list of entry ids whose traditional form matches the query - "WITH matching_entry_ids AS ( " - " SELECT rowid FROM entries WHERE traditional GLOB ?" - "), " - " " - //// Get the list of all definitions for those entries - //// This CTE is used multiple times; would be nice if could materialize it - "matching_definition_ids AS ( " - " SELECT definition_id, definition FROM definitions WHERE fk_entry_id " - " IN matching_entry_ids " - "), " - " " - //// Get corresponding sentence ids for each of those definitions - //// This CTE is used multiple times; would be nice if could materialize it - "matching_chinese_sentence_ids AS ( " - " SELECT definition_id, fk_chinese_sentence_id " - " FROM matching_definition_ids AS mdi " - " JOIN definitions_chinese_sentences_links AS dcsl ON " - " mdi.definition_id = dcsl.fk_definition_id " - "), " - " " - //// Get translations for each of the sentences - "matching_translations AS ( " - " SELECT mcsi.fk_chinese_sentence_id, " - " json_group_array(DISTINCT " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct " - " )) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " JOIN sentence_links AS sl ON mcsi.fk_chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " JOIN nonchinese_sentences AS ncs ON ncs.non_chinese_sentence_id = " - " sl.fk_non_chinese_sentence_id " - " GROUP BY mcsi.fk_chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence ids - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT fk_chinese_sentence_id FROM " - " matching_chinese_sentence_ids " - " ) " - ")," - " " - //// Get translations for each of those sentences - "matching_sentences_with_translations AS ( " - " SELECT chinese_sentence_id, " - " json_object('traditional', traditional, " - " 'simplified', simplified, " - " 'pinyin', pinyin, " - " 'jyutping', jyutping, " - " 'language', language, " - " 'translations', json(translation)) AS sentence " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.fk_chinese_sentence_id " - "), " - " " - //// Get definition data for each matching definition - "matching_definitions AS ( " - " SELECT definition_id, fk_entry_id, fk_source_id, definition, " - " label " - " FROM definitions " - " WHERE definitions.definition_id IN ( " - " SELECT definition_id FROM matching_definition_ids" - " ) " - "), " - " " - //// Create definition object with sentences for each definition - "matching_definitions_with_sentences AS ( " - " SELECT fk_entry_id, fk_source_id, " - " json_object('definition', definition, " - " 'label', label, 'sentences', " - " json_group_array(json(sentence))) AS definition " - " FROM matching_definitions AS md " - " LEFT JOIN matching_chinese_sentence_ids AS mcsi ON " - " md.definition_id = mcsi.definition_id " - " LEFT JOIN matching_sentences_with_translations AS mswt ON " - " mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " - " GROUP BY md.definition_id " - "), " - " " - //// Create definition groups for definitions of the same entry that come from the same source - "matching_definition_groups AS ( " - " SELECT fk_entry_id, " - " json_object('source', sourcename, " - " 'definitions', " - " json_group_array(json(definition))) AS definitions " - " FROM matching_definitions_with_sentences AS mdws " - " LEFT JOIN sources ON sources.source_id = mdws.fk_source_id " - " GROUP BY fk_entry_id, fk_source_id " - "), " - " " - //// Construct the final entry object - "matching_entries AS ( " - " SELECT simplified, traditional, jyutping, pinyin, " - " json_group_array(json(definitions)) AS definitions " - " FROM matching_definition_groups AS mdg " - " LEFT JOIN entries ON entries.entry_id = mdg.fk_entry_id " - " GROUP BY entry_id " - " ORDER BY frequency DESC " - ") " - " " - "SELECT simplified, traditional, jyutping, pinyin, definitions FROM " - " matching_entries"); + query.prepare(SEARCH_TRADITIONAL_QUERY); if (searchExactMatch) { query.addBindValue(searchTermWithoutQuotes); } else if (dontAppendWildcard) { @@ -529,116 +312,7 @@ void SQLSearch::searchJyutpingThread(const QString &searchTerm, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - //// Get list of entry ids whose jyutping starts with the queried string - "WITH matching_entry_ids AS ( " - " SELECT rowid FROM entries WHERE jyutping GLOB ?" - "), " - " " - //// Get the list of all definitions for those entries - //// This CTE is used multiple times; would be nice if could materialize it - "matching_definition_ids AS ( " - " SELECT definition_id, definition FROM definitions WHERE fk_entry_id " - " IN matching_entry_ids " - "), " - " " - //// Get corresponding sentence ids for each of those definitions - //// This CTE is used multiple times; would be nice if could materialize it - "matching_chinese_sentence_ids AS ( " - " SELECT definition_id, fk_chinese_sentence_id " - " FROM matching_definition_ids AS mdi " - " JOIN definitions_chinese_sentences_links AS dcsl ON " - " mdi.definition_id = dcsl.fk_definition_id " - "), " - " " - //// Get translations for each of the sentences - "matching_translations AS ( " - " SELECT mcsi.fk_chinese_sentence_id, " - " json_group_array(DISTINCT " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct " - " )) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " JOIN sentence_links AS sl ON mcsi.fk_chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " JOIN nonchinese_sentences AS ncs ON ncs.non_chinese_sentence_id = " - " sl.fk_non_chinese_sentence_id " - " GROUP BY mcsi.fk_chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence ids - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT fk_chinese_sentence_id FROM " - " matching_chinese_sentence_ids " - " ) " - ")," - " " - //// Get translations for each of those sentences - "matching_sentences_with_translations AS ( " - " SELECT chinese_sentence_id, " - " json_object('traditional', traditional, " - " 'simplified', simplified, " - " 'pinyin', pinyin, " - " 'jyutping', jyutping, " - " 'language', language, " - " 'translations', json(translation)) AS sentence " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.fk_chinese_sentence_id " - "), " - " " - //// Get definition data for each matching definition - "matching_definitions AS ( " - " SELECT definition_id, fk_entry_id, fk_source_id, definition, " - " label " - " FROM definitions " - " WHERE definitions.definition_id IN ( " - " SELECT definition_id FROM matching_definition_ids" - " ) " - "), " - " " - //// Create definition object with sentences for each definition - "matching_definitions_with_sentences AS ( " - " SELECT fk_entry_id, fk_source_id, " - " json_object('definition', definition, " - " 'label', label, 'sentences', " - " json_group_array(json(sentence))) AS definition " - " FROM matching_definitions AS md " - " LEFT JOIN matching_chinese_sentence_ids AS mcsi ON " - " md.definition_id = mcsi.definition_id " - " LEFT JOIN matching_sentences_with_translations AS mswt ON " - " mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " - " GROUP BY md.definition_id " - "), " - " " - //// Create definition groups for definitions of the same entry that come from the same source - "matching_definition_groups AS ( " - " SELECT fk_entry_id, " - " json_object('source', sourcename, " - " 'definitions', " - " json_group_array(json(definition))) AS definitions " - " FROM matching_definitions_with_sentences AS mdws " - " LEFT JOIN sources ON sources.source_id = mdws.fk_source_id " - " GROUP BY fk_entry_id, fk_source_id " - "), " - " " - //// Construct the final entry object - "matching_entries AS ( " - " SELECT simplified, traditional, jyutping, pinyin, " - " json_group_array(json(definitions)) AS definitions " - " FROM matching_definition_groups AS mdg " - " LEFT JOIN entries ON entries.entry_id = mdg.fk_entry_id " - " GROUP BY entry_id " - " ORDER BY frequency DESC " - ") " - " " - "SELECT simplified, traditional, jyutping, pinyin, definitions FROM " - " matching_entries"); + query.prepare(SEARCH_JYUTPING_QUERY); // Don't add wildcard characters to GLOB term if searching for exact match const char *globJoinDelimiter = searchExactMatch ? "" : "?"; @@ -700,115 +374,7 @@ void SQLSearch::searchPinyinThread(const QString &searchTerm, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - //// Get list of entry ids whose pinyin starts with the queried string - "WITH matching_entry_ids AS ( " - " SELECT rowid FROM entries WHERE pinyin GLOB ?" - "), " - " " - //// Get the list of all definitions for those entries - //// This CTE is used multiple times; would be nice if could materialize it - "matching_definition_ids AS ( " - " SELECT definition_id, definition FROM definitions WHERE fk_entry_id " - " IN matching_entry_ids " - "), " - " " - //// Get corresponding sentence ids for each of those definitions - //// This CTE is used multiple times; would be nice if could materialize it - "matching_chinese_sentence_ids AS ( " - " SELECT definition_id, fk_chinese_sentence_id " - " FROM matching_definition_ids AS mdi " - " JOIN definitions_chinese_sentences_links AS dcsl ON " - " mdi.definition_id = dcsl.fk_definition_id " - "), " - " " - //// Get translations for each of the sentences - "matching_translations AS ( " - " SELECT mcsi.fk_chinese_sentence_id, " - " json_group_array(DISTINCT " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct " - " )) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " JOIN sentence_links AS sl ON mcsi.fk_chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " JOIN nonchinese_sentences AS ncs ON ncs.non_chinese_sentence_id = " - " sl.fk_non_chinese_sentence_id " - " GROUP BY mcsi.fk_chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence ids - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT fk_chinese_sentence_id FROM matching_chinese_sentence_ids " - " ) " - ")," - " " - //// Get translations for each of those sentences - "matching_sentences_with_translations AS ( " - " SELECT chinese_sentence_id, " - " json_object('traditional', traditional, " - " 'simplified', simplified, " - " 'pinyin', pinyin, " - " 'jyutping', jyutping, " - " 'language', language, " - " 'translations', json(translation)) AS sentence " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.fk_chinese_sentence_id " - "), " - " " - //// Get definition data for each matching definition - "matching_definitions AS ( " - " SELECT definition_id, fk_entry_id, fk_source_id, definition, " - " label " - " FROM definitions " - " WHERE definitions.definition_id IN ( " - " SELECT definition_id FROM matching_definition_ids" - " ) " - "), " - " " - //// Create definition object with sentences for each definition - "matching_definitions_with_sentences AS ( " - " SELECT fk_entry_id, fk_source_id, " - " json_object('definition', definition, " - " 'label', label, 'sentences', " - " json_group_array(json(sentence))) AS definition " - " FROM matching_definitions AS md " - " LEFT JOIN matching_chinese_sentence_ids AS mcsi ON " - " md.definition_id = mcsi.definition_id " - " LEFT JOIN matching_sentences_with_translations AS mswt ON " - " mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " - " GROUP BY md.definition_id " - "), " - " " - //// Create definition groups for definitions of the same entry that come from the same source - "matching_definition_groups AS ( " - " SELECT fk_entry_id, " - " json_object('source', sourcename, " - " 'definitions', " - " json_group_array(json(definition))) AS definitions " - " FROM matching_definitions_with_sentences AS mdws " - " LEFT JOIN sources ON sources.source_id = mdws.fk_source_id " - " GROUP BY fk_entry_id, fk_source_id " - "), " - " " - //// Construct the final entry object - "matching_entries AS ( " - " SELECT simplified, traditional, jyutping, pinyin, " - " json_group_array(json(definitions)) AS definitions " - " FROM matching_definition_groups AS mdg " - " LEFT JOIN entries ON entries.entry_id = mdg.fk_entry_id " - " GROUP BY entry_id " - " ORDER BY frequency DESC " - ") " - " " - "SELECT simplified, traditional, jyutping, pinyin, definitions FROM " - " matching_entries"); + query.prepare(SEARCH_PINYIN_QUERY); // Don't add wildcard characters to GLOB term if searching for exact match const char *globJoinDelimiter = searchExactMatch ? "" : "?"; @@ -843,116 +409,7 @@ void SQLSearch::searchEnglishThread(const QString &searchTerm, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - //// Get list of entry ids where at least one definition matches the query - "WITH matching_entry_ids AS ( " - " SELECT fk_entry_id FROM definitions_fts WHERE definitions_fts MATCH " - " ? AND definition LIKE ? " - "), " - " " - //// Get the list of all definitions for those entries - //// This CTE is used multiple times; would be nice if could materialize it - "matching_definition_ids AS ( " - " SELECT definition_id, definition FROM definitions WHERE fk_entry_id " - " IN matching_entry_ids " - "), " - " " - //// Get corresponding sentence ids for each of those definitions - //// This CTE is used multiple times; would be nice if could materialize it - "matching_chinese_sentence_ids AS ( " - " SELECT definition_id, fk_chinese_sentence_id " - " FROM matching_definition_ids AS mdi " - " JOIN definitions_chinese_sentences_links AS dcsl ON " - " mdi.definition_id = dcsl.fk_definition_id " - "), " - " " - //// Get translations for each of the sentences - "matching_translations AS ( " - " SELECT mcsi.fk_chinese_sentence_id, " - " json_group_array(DISTINCT " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct " - " )) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " JOIN sentence_links AS sl ON mcsi.fk_chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " JOIN nonchinese_sentences AS ncs ON ncs.non_chinese_sentence_id = " - " sl.fk_non_chinese_sentence_id " - " GROUP BY mcsi.fk_chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence ids - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT fk_chinese_sentence_id FROM matching_chinese_sentence_ids " - " ) " - ")," - " " - //// Get translations for each of those sentences - "matching_sentences_with_translations AS ( " - " SELECT chinese_sentence_id, " - " json_object('traditional', traditional, " - " 'simplified', simplified, " - " 'pinyin', pinyin, " - " 'jyutping', jyutping, " - " 'language', language, " - " 'translations', json(translation)) AS sentence " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.fk_chinese_sentence_id " - "), " - " " - //// Get definition data for each matching definition - "matching_definitions AS ( " - " SELECT definition_id, fk_entry_id, fk_source_id, definition, " - " label " - " FROM definitions " - " WHERE definitions.definition_id IN ( " - " SELECT definition_id FROM matching_definition_ids" - " ) " - "), " - " " - //// Create definition object with sentences for each definition - "matching_definitions_with_sentences AS ( " - " SELECT fk_entry_id, fk_source_id, " - " json_object('definition', definition, " - " 'label', label, 'sentences', " - " json_group_array(json(sentence))) AS definition " - " FROM matching_definitions AS md " - " LEFT JOIN matching_chinese_sentence_ids AS mcsi ON " - " md.definition_id = mcsi.definition_id " - " LEFT JOIN matching_sentences_with_translations AS mswt ON " - " mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " - " GROUP BY md.definition_id " - "), " - " " - //// Create definition groups for definitions of the same entry that come from the same source - "matching_definition_groups AS ( " - " SELECT fk_entry_id, " - " json_object('source', sourcename, " - " 'definitions', " - " json_group_array(json(definition))) AS definitions " - " FROM matching_definitions_with_sentences AS mdws " - " LEFT JOIN sources ON sources.source_id = mdws.fk_source_id " - " GROUP BY fk_entry_id, fk_source_id " - "), " - " " - //// Construct the final entry object - "matching_entries AS ( " - " SELECT simplified, traditional, jyutping, pinyin, " - " json_group_array(json(definitions)) AS definitions " - " FROM matching_definition_groups AS mdg " - " LEFT JOIN entries ON entries.entry_id = mdg.fk_entry_id " - " GROUP BY entry_id " - " ORDER BY frequency DESC " - ") " - " " - "SELECT simplified, traditional, jyutping, pinyin, definitions FROM " - " matching_entries"); + query.prepare(SEARCH_ENGLISH_QUERY); if (searchExactMatch) { query.addBindValue("\"" + searchTermWithoutQuotes + "\""); query.addBindValue(searchTermWithoutQuotes); @@ -985,119 +442,7 @@ void SQLSearch::searchByUniqueThread(const QString &simplified, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - //// Get list of entry ids whose traditional form matches the query - "WITH matching_entry_ids AS ( " - " SELECT rowid FROM entries WHERE " - " simplified LIKE ? " - " AND traditional LIKE ? " - " AND jyutping LIKE ? " - " AND pinyin LIKE ? " - "), " - " " - //// Get the list of all definitions for those entries - //// This CTE is used multiple times; would be nice if could materialize it - "matching_definition_ids AS ( " - " SELECT definition_id, definition FROM definitions WHERE fk_entry_id " - " IN matching_entry_ids " - "), " - " " - //// Get corresponding sentence ids for each of those definitions - //// This CTE is used multiple times; would be nice if could materialize it - "matching_chinese_sentence_ids AS ( " - " SELECT definition_id, fk_chinese_sentence_id " - " FROM matching_definition_ids AS mdi " - " JOIN definitions_chinese_sentences_links AS dcsl ON " - " mdi.definition_id = dcsl.fk_definition_id " - "), " - " " - //// Get translations for each of the sentences - "matching_translations AS ( " - " SELECT mcsi.fk_chinese_sentence_id, " - " json_group_array(DISTINCT " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct " - " )) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " JOIN sentence_links AS sl ON mcsi.fk_chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " JOIN nonchinese_sentences AS ncs ON ncs.non_chinese_sentence_id = " - " sl.fk_non_chinese_sentence_id " - " GROUP BY mcsi.fk_chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence ids - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT fk_chinese_sentence_id FROM matching_chinese_sentence_ids " - " ) " - ")," - " " - //// Get translations for each of those sentences - "matching_sentences_with_translations AS ( " - " SELECT chinese_sentence_id, " - " json_object('traditional', traditional, " - " 'simplified', simplified, " - " 'pinyin', pinyin, " - " 'jyutping', jyutping, " - " 'language', language, " - " 'translations', json(translation)) AS sentence " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.fk_chinese_sentence_id " - "), " - " " - //// Get definition data for each matching definition - "matching_definitions AS ( " - " SELECT definition_id, fk_entry_id, fk_source_id, definition, " - " label " - " FROM definitions " - " WHERE definitions.definition_id IN ( " - " SELECT definition_id FROM matching_definition_ids" - " ) " - "), " - " " - //// Create definition object with sentences for each definition - "matching_definitions_with_sentences AS ( " - " SELECT md.fk_entry_id, md.fk_source_id, " - " json_object('definition', md.definition, " - " 'label', label, 'sentences', " - " json_group_array(json(sentence))) AS definition " - " FROM matching_definitions AS md " - " LEFT JOIN matching_chinese_sentence_ids AS mcsi ON " - " md.definition_id = mcsi.definition_id " - " LEFT JOIN matching_sentences_with_translations AS mswt ON " - " mcsi.fk_chinese_sentence_id = mswt.chinese_sentence_id " - " GROUP BY md.definition_id " - "), " - " " - //// Create definition groups for definitions of the same entry that come from the same source - "matching_definition_groups AS ( " - " SELECT fk_entry_id, " - " json_object('source', sourcename, " - " 'definitions', " - " json_group_array(json(definition))) AS definitions " - " FROM matching_definitions_with_sentences AS mdws " - " LEFT JOIN sources ON sources.source_id = mdws.fk_source_id " - " GROUP BY fk_entry_id, fk_source_id " - "), " - " " - //// Construct the final entry object - "matching_entries AS ( " - " SELECT simplified, traditional, jyutping, pinyin, " - " json_group_array(json(definitions)) AS definitions " - " FROM matching_definition_groups AS mdg " - " LEFT JOIN entries ON entries.entry_id = mdg.fk_entry_id " - " GROUP BY entry_id " - " ORDER BY frequency DESC " - ") " - " " - "SELECT simplified, traditional, jyutping, pinyin, definitions FROM " - " matching_entries"); + query.prepare(SEARCH_UNIQUE_QUERY); query.addBindValue(simplified); query.addBindValue(traditional); query.addBindValue(jyutping); @@ -1125,71 +470,7 @@ void SQLSearch::searchTraditionalSentencesThread(const QString &searchTerm, std::vector results; QSqlQuery query{_manager->getDatabase()}; - query.prepare( - "WITH matching_chinese_sentence_ids AS ( " - " SELECT chinese_sentence_id " - " FROM chinese_sentences " - " WHERE traditional LIKE ? ESCAPE '\\'" - "), " - " " - //// Get translations for each of the sentence - "translations_with_source AS ( " - " SELECT s.sourcename AS source, " - " mcsi.chinese_sentence_id AS chinese_sentence_id, " - " json_group_array(distinct " - " json_object('sentence', sentence, " - " 'language', language, " - " 'direct', direct)) AS translation " - " FROM matching_chinese_sentence_ids AS mcsi " - " LEFT JOIN sentence_links AS sl ON mcsi.chinese_sentence_id = " - " sl.fk_chinese_sentence_id " - " LEFT JOIN nonchinese_sentences AS ncs ON " - " ncs.non_chinese_sentence_id = sl.fk_non_chinese_sentence_id " - " LEFT JOIN sources AS s ON s.source_id = sl.fk_source_id" - " GROUP BY s.sourcename, mcsi.chinese_sentence_id" - "), " - "" - //// Group translations by source - "matching_translations AS ( " - " SELECT chinese_sentence_id, " - " json_group_array( " - " json_object('source', source, " - " 'translations', json(translation)) " - " ) AS translations " - " FROM translations_with_source AS tws " - " GROUP BY chinese_sentence_id " - "), " - " " - //// Get sentence data for each of the sentence id - "matching_sentences AS ( " - " SELECT chinese_sentence_id, traditional, simplified, pinyin, " - " jyutping, language " - " FROM chinese_sentences AS cs " - " WHERE chinese_sentence_id IN ( " - " SELECT chinese_sentence_id FROM matching_chinese_sentence_ids " - " ) " - "), " - " " - //// Match up the translations with their sentences, and get the linked - //// definition's source name if it exists - "matching_sentences_with_translations AS ( " - " SELECT max(sourcename) AS sourcename, traditional, simplified, " - " pinyin, jyutping, language, translations " - " FROM matching_sentences AS ms " - " LEFT JOIN matching_translations AS mt ON ms.chinese_sentence_id = " - " mt.chinese_sentence_id " - " LEFT JOIN definitions_chinese_sentences_links AS dcsl ON " - " ms.chinese_sentence_id = dcsl.fk_chinese_sentence_id " - " LEFT JOIN definitions AS d ON dcsl.fk_definition_id = " - " d.definition_id " - " LEFT JOIN sources AS s ON d.fk_source_id = s.source_id " - " GROUP BY traditional, simplified, pinyin, jyutping, language, " - " translations " - " ORDER BY ms.chinese_sentence_id " - ") " - " " - "SELECT sourcename, traditional, simplified, pinyin, jyutping, " - " language, translations FROM matching_sentences_with_translations "); + query.prepare(SEARCH_TRADITIONAL_SENTENCES_QUERY); query.addBindValue("%" + searchTerm + "%"); query.exec(); diff --git a/src/jyut-dict/resources/sql/.gitignore b/src/jyut-dict/resources/sql/.gitignore new file mode 100644 index 00000000..c6bba591 --- /dev/null +++ b/src/jyut-dict/resources/sql/.gitignore @@ -0,0 +1,130 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/src/jyut-dict/resources/sql/README.md b/src/jyut-dict/resources/sql/README.md new file mode 100644 index 00000000..fd95526d --- /dev/null +++ b/src/jyut-dict/resources/sql/README.md @@ -0,0 +1,7 @@ +To format SQL queries: +- Install the dependencies in package-lock.json using NPM. +- Remove all quotes from the multi-line strings. +- Replace placeholder values ("?") with empty strings (""). +- Run prettier: `prettier in.sql --plugin=prettier-plugin-sql-cst --print-width 60 > out.sql`. +- Surround the output with quotes: in Sublime Text, `Ctrl+Shift+L`, `"`, `→+Space`. +- Copy and paste the output into `searchqueries.h`. diff --git a/src/jyut-dict/resources/sql/package-lock.json b/src/jyut-dict/resources/sql/package-lock.json new file mode 100644 index 00000000..e9f7273b --- /dev/null +++ b/src/jyut-dict/resources/sql/package-lock.json @@ -0,0 +1,55 @@ +{ + "name": "sql", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "prettier": "^3.0.3", + "prettier-plugin-sql-cst": "^0.8.3" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-sql-cst": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-sql-cst/-/prettier-plugin-sql-cst-0.8.3.tgz", + "integrity": "sha512-kOJeybkHwZ50fZi60mMW8lRRPEFnxhygflcQ7PUCdhjv+TwkW4cjxe/2hNHZa09SLHwYYrNLP90mGubvkVTZkw==", + "dependencies": { + "prettier": "^2.8.2", + "sql-parser-cst": "^0.17.1" + } + }, + "node_modules/prettier-plugin-sql-cst/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/sql-parser-cst": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/sql-parser-cst/-/sql-parser-cst-0.17.1.tgz", + "integrity": "sha512-h+sysEIkBLUDaHx95H/Vn7kQC1w3ib6lwOFrdBUseh1ZSSqR9tZbcicSRDqkChWN7TLhwzC2pVq0acXK3/pAyQ==" + } + } +} diff --git a/src/jyut-dict/resources/sql/package.json b/src/jyut-dict/resources/sql/package.json new file mode 100644 index 00000000..d997ee8f --- /dev/null +++ b/src/jyut-dict/resources/sql/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "prettier": "^3.0.3", + "prettier-plugin-sql-cst": "^0.8.3" + } +}