From f2f44fa08a16d627ecb0476f173137191c70e099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20K=C3=B6rner?= Date: Thu, 11 Jul 2024 23:02:16 +0200 Subject: [PATCH] Add CQL language support --- components.json | 4 + components/prism-cql.js | 114 ++++++++++++++++++++ examples/prism-cql.html | 60 +++++++++++ tests/languages/cql/boolean_modifiers.test | 57 ++++++++++ tests/languages/cql/boolean_operators.test | 49 +++++++++ tests/languages/cql/case_insensitive.test | 33 ++++++ tests/languages/cql/clause_feature_1.test | 15 +++ tests/languages/cql/clause_feature_2.test | 13 +++ tests/languages/cql/clause_feature_3.test | 15 +++ tests/languages/cql/index_feature.test | 23 ++++ tests/languages/cql/prefix_feature_1.test | 21 ++++ tests/languages/cql/prefix_feature_2.test | 21 ++++ tests/languages/cql/prefix_feature_3.test | 19 ++++ tests/languages/cql/relation_feature.test | 23 ++++ tests/languages/cql/relation_modifiers.test | 49 +++++++++ tests/languages/cql/sort_feature_1.test | 23 ++++ tests/languages/cql/sort_feature_2.test | 17 +++ tests/languages/cql/sort_feature_3.test | 26 +++++ tests/languages/cql/spec_1.test | 45 ++++++++ tests/languages/cql/spec_2.test | 21 ++++ tests/languages/cql/term_feature_1.test | 13 +++ tests/languages/cql/term_feature_2.test | 13 +++ tests/languages/cql/term_feature_3.test | 13 +++ tests/languages/cql/term_feature_4.test | 13 +++ 24 files changed, 700 insertions(+) create mode 100644 components/prism-cql.js create mode 100644 examples/prism-cql.html create mode 100644 tests/languages/cql/boolean_modifiers.test create mode 100644 tests/languages/cql/boolean_operators.test create mode 100644 tests/languages/cql/case_insensitive.test create mode 100644 tests/languages/cql/clause_feature_1.test create mode 100644 tests/languages/cql/clause_feature_2.test create mode 100644 tests/languages/cql/clause_feature_3.test create mode 100644 tests/languages/cql/index_feature.test create mode 100644 tests/languages/cql/prefix_feature_1.test create mode 100644 tests/languages/cql/prefix_feature_2.test create mode 100644 tests/languages/cql/prefix_feature_3.test create mode 100644 tests/languages/cql/relation_feature.test create mode 100644 tests/languages/cql/relation_modifiers.test create mode 100644 tests/languages/cql/sort_feature_1.test create mode 100644 tests/languages/cql/sort_feature_2.test create mode 100644 tests/languages/cql/sort_feature_3.test create mode 100644 tests/languages/cql/spec_1.test create mode 100644 tests/languages/cql/spec_2.test create mode 100644 tests/languages/cql/term_feature_1.test create mode 100644 tests/languages/cql/term_feature_2.test create mode 100644 tests/languages/cql/term_feature_3.test create mode 100644 tests/languages/cql/term_feature_4.test diff --git a/components.json b/components.json index b353778e90..0180538021 100644 --- a/components.json +++ b/components.json @@ -346,6 +346,10 @@ "title": "Content-Security-Policy", "owner": "ScottHelme" }, + "cql": { + "title": "Contextual Query Language", + "owner": "Querela" + }, "cooklang": { "title": "Cooklang", "owner": "ahue" diff --git a/components/prism-cql.js b/components/prism-cql.js new file mode 100644 index 0000000000..1dc97ef7a3 --- /dev/null +++ b/components/prism-cql.js @@ -0,0 +1,114 @@ +(function (Prism) { + + var boolExp = /\b(?:AND|NOT|OR|PROX)\b/i; + var stringExp = /(?:"(?:\\[\s\S]|(?!")[^\\])*")/; + var wordExp = /[^\s()=<>"\/]+/; + var identifierExp = RegExp('(?:' + stringExp.source + '|' + wordExp.source + ')'); + + var comparitorNamedExp = identifierExp; + var comparitorSymbolExp = /(?:<>|[=><]=?)/; + var comparitorExp = RegExp('(?:' + comparitorSymbolExp.source + '|' + comparitorNamedExp.source + ')'); + + var modifierExp = RegExp('/\\s*' + identifierExp.source + '(?:\\s*' + comparitorSymbolExp.source + '\\s*' + identifierExp.source + ')?'); + var modifierListExp = RegExp('(?:\\s*' + modifierExp.source + ')*'); + + var relationExp = RegExp(comparitorExp.source + modifierListExp.source); + + var modifier = { + pattern: modifierExp, + inside: { + 'modifier': { + pattern: RegExp('(/\\s*)' + identifierExp.source), + lookbehind: true, + alias: 'property', + }, + 'value': { + pattern: RegExp(identifierExp.source + '$'), + alias: 'string', + }, + 'comparitor': { + pattern: comparitorSymbolExp, + alias: 'operator', + }, + 'punctuation': /\//, + } + }; + + var searchClause = { + pattern: RegExp('(?:' + identifierExp.source + '\\s*' + relationExp.source + '\\s*)?' + identifierExp.source), + inside: { + // required last part, search term + 'term': { + pattern: RegExp(identifierExp.source + '(?!.)'), + alias: 'string', + }, + // optional index with relation + 'index': { + pattern: RegExp('^' + identifierExp.source), + alias: 'property', + }, + 'relation-modifier': modifier, + 'relation': { + pattern: comparitorExp, + alias: 'operator', + }, + }, + }; + + var boolClause = { + pattern: RegExp(boolExp.source + modifierListExp.source, 'i'), + inside: { + 'boolean': { + pattern: boolExp, + alias: 'operator' + }, + 'boolean-modifier': modifier, + }, + }; + + var prefix = { + pattern: RegExp('(^\\s*)>\\s*(?:' + identifierExp.source + '\\s*=\\s*)?' + identifierExp.source), + lookbehind: true, + inside: { + 'uri': { + pattern: RegExp(identifierExp.source + '$'), + alias: 'string', + }, + 'prefix': { + pattern: identifierExp, + alias: 'property', + }, + 'punctuation': /[>=]/, + } + }; + + var sortby = { + // XXX: too complex exponential/polynomial backtracking possible ... + //pattern: RegExp('sortby(?:\\s*' + identifierExp.source + modifierListExp.source + ')+\\s*$', 'i'), + pattern: RegExp('(^|\\s)sortby\\b(?:' + identifierExp.source + '\\b|[\\s=>CQL Query + +
dc.title any fish
+
dc.title any fish or dc.creator any sanderson
+
dc.title any fish sortBy dc.date/sort.ascending
+
> dc = "info:srw/context-sets/1/dc-v1.1" dc.title any fish
+ +

Search Clause

+ +
dc.title any fish
+
fish
+
cql.serverChoice = fish
+ +

Search Term

+ +
"fish"
+
fish
+
"squirrels fish"
+
""
+ +

Index Name

+ +
title any fish
+
dc.title any fish
+ +

Relation

+ +
dc.title any fish
+
dc.title cql.any fish
+ +

Relation Modifiers

+ +
dc.title any/relevant fish
+
dc.title any/ relevant /cql.string fish
+
dc.title any/rel.algorithm=cori fish
+ +

Boolean Operators

+ +
dc.title any fish or dc.creator any sanderson
+
dc.title any fish or (dc.creator any sanderson and dc.identifier = "id:1234567")
+ +

Boolean Modifiers

+ +
dc.title any fish or/rel.combine=sum dc.creator any sanderson
+
dc.title any fish prox/unit=word/distance>3 dc.title any squirrel
+ +

Sorting

+ +
"cat" sortBy dc.title
+
"dinosaur" sortBy dc.date/sort.descending dc.title/sort.ascending
+ +

Prefix Assignment

+ +
> dc = "http://deepcustard.org/" dc.custardDepth > 10
+
> "http://deepcustard.org/" custardDepth > 10
+ +

Case Insensitive

+ +
dC.tiTlE any fish
+
dc.TitlE Any/rEl.algOriThm=cori fish soRtbY Dc.TitlE 
diff --git a/tests/languages/cql/boolean_modifiers.test b/tests/languages/cql/boolean_modifiers.test new file mode 100644 index 0000000000..4ef945389f --- /dev/null +++ b/tests/languages/cql/boolean_modifiers.test @@ -0,0 +1,57 @@ +dc.title any fish or/rel.combine=sum dc.creator any sanderson + +dc.title any fish prox/unit=word/distance>3 dc.title any squirrel + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["bool-group", [ + ["boolean", "or"], + ["boolean-modifier", [ + ["punctuation", "/"], + ["modifier", "rel.combine"], + ["comparitor", "="], + ["value", "sum"] + ]] + ]], + ["search-clause", [ + ["index", "dc.creator"], + ["relation", "any"], + ["term", "sanderson"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["bool-group", [ + ["boolean", "prox"], + ["boolean-modifier", [ + ["punctuation", "/"], + ["modifier", "unit"], + ["comparitor", "="], + ["value", "word"] + ]], + ["boolean-modifier", [ + ["punctuation", "/"], + ["modifier", "distance"], + ["comparitor", ">"], + ["value", "3"] + ]] + ]], + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "squirrel"] + ]] +] + +---------------------------------------------------- + +Boolean modifiers. diff --git a/tests/languages/cql/boolean_operators.test b/tests/languages/cql/boolean_operators.test new file mode 100644 index 0000000000..9e66c66dd4 --- /dev/null +++ b/tests/languages/cql/boolean_operators.test @@ -0,0 +1,49 @@ +dc.title any fish or dc.creator any sanderson + +dc.title any fish or (dc.creator any sanderson and dc.identifier = "id:1234567") + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["bool-group", [ + ["boolean", "or"] + ]], + ["search-clause", [ + ["index", "dc.creator"], + ["relation", "any"], + ["term", "sanderson"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["bool-group", [ + ["boolean", "or"] + ]], + ["punctuation", "("], + ["search-clause", [ + ["index", "dc.creator"], + ["relation", "any"], + ["term", "sanderson"] + ]], + ["bool-group", [ + ["boolean", "and"] + ]], + ["search-clause", [ + ["index", "dc.identifier"], + ["relation", "="], + ["term", "\"id:1234567\""] + ]], + ["punctuation", ")"] +] + +---------------------------------------------------- + +Boolean operators. diff --git a/tests/languages/cql/case_insensitive.test b/tests/languages/cql/case_insensitive.test new file mode 100644 index 0000000000..6fe598ed24 --- /dev/null +++ b/tests/languages/cql/case_insensitive.test @@ -0,0 +1,33 @@ +dC.tiTlE any fish + +dc.TitlE Any/rEl.algOriThm=cori fish soRtbY Dc.TitlE + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dC.tiTlE"], + ["relation", "any"], + ["term", "fish"] + ]], + + ["search-clause", [ + ["index", "dc.TitlE"], + ["relation", "Any"], + ["relation-modifier", [ + ["punctuation", "/"], + ["modifier", "rEl.algOriThm"], + ["comparitor", "="], + ["value", "cori"] + ]], + ["term", "fish"] + ]], + ["sortby", [ + ["keyword", "soRtbY"], + ["index", "Dc.TitlE"] + ]] +] + +---------------------------------------------------- + +Case Insenstivity. diff --git a/tests/languages/cql/clause_feature_1.test b/tests/languages/cql/clause_feature_1.test new file mode 100644 index 0000000000..dcbbd8c602 --- /dev/null +++ b/tests/languages/cql/clause_feature_1.test @@ -0,0 +1,15 @@ +dc.title any fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Search clauses. diff --git a/tests/languages/cql/clause_feature_2.test b/tests/languages/cql/clause_feature_2.test new file mode 100644 index 0000000000..031ce22987 --- /dev/null +++ b/tests/languages/cql/clause_feature_2.test @@ -0,0 +1,13 @@ +fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Search clauses. diff --git a/tests/languages/cql/clause_feature_3.test b/tests/languages/cql/clause_feature_3.test new file mode 100644 index 0000000000..e90ec2f820 --- /dev/null +++ b/tests/languages/cql/clause_feature_3.test @@ -0,0 +1,15 @@ +cql.serverChoice = fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "cql.serverChoice"], + ["relation", "="], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Search clauses. diff --git a/tests/languages/cql/index_feature.test b/tests/languages/cql/index_feature.test new file mode 100644 index 0000000000..d0c8b12981 --- /dev/null +++ b/tests/languages/cql/index_feature.test @@ -0,0 +1,23 @@ +title any fish + +dc.title any fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "title"], + ["relation", "any"], + ["term", "fish"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Index names. diff --git a/tests/languages/cql/prefix_feature_1.test b/tests/languages/cql/prefix_feature_1.test new file mode 100644 index 0000000000..98515b798c --- /dev/null +++ b/tests/languages/cql/prefix_feature_1.test @@ -0,0 +1,21 @@ +> dc = "info:srw/context-sets/1/dc-v1.1" dc.title any fish + +---------------------------------------------------- + +[ + ["prefix", [ + ["punctuation", ">"], + ["prefix", "dc"], + ["punctuation", "="], + ["uri", "\"info:srw/context-sets/1/dc-v1.1\""] + ]], + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Prefix assignments. diff --git a/tests/languages/cql/prefix_feature_2.test b/tests/languages/cql/prefix_feature_2.test new file mode 100644 index 0000000000..3c6a572de7 --- /dev/null +++ b/tests/languages/cql/prefix_feature_2.test @@ -0,0 +1,21 @@ +> dc = "http://deepcustard.org/" dc.custardDepth > 10 + +---------------------------------------------------- + +[ + ["prefix", [ + ["punctuation", ">"], + ["prefix", "dc"], + ["punctuation", "="], + ["uri", "\"http://deepcustard.org/\""] + ]], + ["search-clause", [ + ["index", "dc.custardDepth"], + ["relation", ">"], + ["term", "10"] + ]] +] + +---------------------------------------------------- + +Prefix assignments. diff --git a/tests/languages/cql/prefix_feature_3.test b/tests/languages/cql/prefix_feature_3.test new file mode 100644 index 0000000000..c0e8c247a4 --- /dev/null +++ b/tests/languages/cql/prefix_feature_3.test @@ -0,0 +1,19 @@ +> "http://deepcustard.org/" custardDepth > 10 + +---------------------------------------------------- + +[ + ["prefix", [ + ["punctuation", ">"], + ["uri", "\"http://deepcustard.org/\""] + ]], + ["search-clause", [ + ["index", "custardDepth"], + ["relation", ">"], + ["term", "10"] + ]] +] + +---------------------------------------------------- + +Prefix assignments. diff --git a/tests/languages/cql/relation_feature.test b/tests/languages/cql/relation_feature.test new file mode 100644 index 0000000000..2ef005bfba --- /dev/null +++ b/tests/languages/cql/relation_feature.test @@ -0,0 +1,23 @@ +dc.title any fish + +dc.title cql.any fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "cql.any"], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Relations. diff --git a/tests/languages/cql/relation_modifiers.test b/tests/languages/cql/relation_modifiers.test new file mode 100644 index 0000000000..9a11f81eb3 --- /dev/null +++ b/tests/languages/cql/relation_modifiers.test @@ -0,0 +1,49 @@ +dc.title any/relevant fish + +dc.title any/ relevant /cql.string fish + +dc.title any/rel.algorithm=cori fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["relation-modifier", [ + ["punctuation", "/"], + ["modifier", "relevant"] + ]], + ["term", "fish"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["relation-modifier", [ + ["punctuation", "/"], + ["modifier", "relevant"] + ]], + ["relation-modifier", [ + ["punctuation", "/"], + ["modifier", "cql.string"] + ]], + ["term", "fish"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["relation-modifier", [ + ["punctuation", "/"], + ["modifier", "rel.algorithm"], + ["comparitor", "="], + ["value", "cori"] + ]], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Relation modifiers. diff --git a/tests/languages/cql/sort_feature_1.test b/tests/languages/cql/sort_feature_1.test new file mode 100644 index 0000000000..24ad724b98 --- /dev/null +++ b/tests/languages/cql/sort_feature_1.test @@ -0,0 +1,23 @@ +dc.title any fish sortBy dc.date/sort.ascending + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["sortby", [ + ["keyword", "sortBy"], + ["index", "dc.date"], + ["sortby-index-modifier", [ + ["punctuation", "/"], + ["modifier", "sort.ascending"] + ]] + ]] +] + +---------------------------------------------------- + +Sorting. diff --git a/tests/languages/cql/sort_feature_2.test b/tests/languages/cql/sort_feature_2.test new file mode 100644 index 0000000000..b1da8feb85 --- /dev/null +++ b/tests/languages/cql/sort_feature_2.test @@ -0,0 +1,17 @@ +"cat" sortBy dc.title + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "\"cat\""] + ]], + ["sortby", [ + ["keyword", "sortBy"], + ["index", "dc.title"] + ]] +] + +---------------------------------------------------- + +Sorting. diff --git a/tests/languages/cql/sort_feature_3.test b/tests/languages/cql/sort_feature_3.test new file mode 100644 index 0000000000..8c542c67cd --- /dev/null +++ b/tests/languages/cql/sort_feature_3.test @@ -0,0 +1,26 @@ +"dinosaur" sortBy dc.date/sort.descending dc.title/sort.ascending + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "\"dinosaur\""] + ]], + ["sortby", [ + ["keyword", "sortBy"], + ["index", "dc.date"], + ["sortby-index-modifier", [ + ["punctuation", "/"], + ["modifier", "sort.descending"] + ]], + ["index", "dc.title"], + ["sortby-index-modifier", [ + ["punctuation", "/"], + ["modifier", "sort.ascending"] + ]] + ]] +] + +---------------------------------------------------- + +Sorting. diff --git a/tests/languages/cql/spec_1.test b/tests/languages/cql/spec_1.test new file mode 100644 index 0000000000..42c53dab87 --- /dev/null +++ b/tests/languages/cql/spec_1.test @@ -0,0 +1,45 @@ +dc.title any fish +dc.title any fish or dc.creator any sanderson +dc.title any fish sortBy dc.date/sort.ascending + +---------------------------------------------------- + +[ + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["bool-group", [ + ["boolean", "or"] + ]], + ["search-clause", [ + ["index", "dc.creator"], + ["relation", "any"], + ["term", "sanderson"] + ]], + + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]], + ["sortby", [ + ["keyword", "sortBy"], + ["index", "dc.date"], + ["sortby-index-modifier", [ + ["punctuation", "/"], + ["modifier", "sort.ascending"] + ]] + ]] +] + +---------------------------------------------------- + +Some examples from SRU CQL spec. diff --git a/tests/languages/cql/spec_2.test b/tests/languages/cql/spec_2.test new file mode 100644 index 0000000000..26cbf6e51c --- /dev/null +++ b/tests/languages/cql/spec_2.test @@ -0,0 +1,21 @@ +> dc = "info:srw/context-sets/1/dc-v1.1" dc.title any fish + +---------------------------------------------------- + +[ + ["prefix", [ + ["punctuation", ">"], + ["prefix", "dc"], + ["punctuation", "="], + ["uri", "\"info:srw/context-sets/1/dc-v1.1\""] + ]], + ["search-clause", [ + ["index", "dc.title"], + ["relation", "any"], + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Some examples from SRU CQL spec. diff --git a/tests/languages/cql/term_feature_1.test b/tests/languages/cql/term_feature_1.test new file mode 100644 index 0000000000..0db77935b2 --- /dev/null +++ b/tests/languages/cql/term_feature_1.test @@ -0,0 +1,13 @@ +"fish" + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "\"fish\""] + ]] +] + +---------------------------------------------------- + +Search terms. diff --git a/tests/languages/cql/term_feature_2.test b/tests/languages/cql/term_feature_2.test new file mode 100644 index 0000000000..ee386d3c93 --- /dev/null +++ b/tests/languages/cql/term_feature_2.test @@ -0,0 +1,13 @@ +fish + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "fish"] + ]] +] + +---------------------------------------------------- + +Search terms. diff --git a/tests/languages/cql/term_feature_3.test b/tests/languages/cql/term_feature_3.test new file mode 100644 index 0000000000..d9230cbe0e --- /dev/null +++ b/tests/languages/cql/term_feature_3.test @@ -0,0 +1,13 @@ +"squirrels fish" + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "\"squirrels fish\""] + ]] +] + +---------------------------------------------------- + +Search terms. diff --git a/tests/languages/cql/term_feature_4.test b/tests/languages/cql/term_feature_4.test new file mode 100644 index 0000000000..2fe63c68e3 --- /dev/null +++ b/tests/languages/cql/term_feature_4.test @@ -0,0 +1,13 @@ +"" + +---------------------------------------------------- + +[ + ["search-clause", [ + ["term", "\"\""] + ]] +] + +---------------------------------------------------- + +Search terms.