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=>])+$', 'i'), + lookbehind: true, + inside: { + 'keyword': /sortby/i, + 'sortby-index-modifier': modifier, + 'index': { + pattern: identifierExp, + alias: 'property', + } + } + }; + + Prism.languages['cql'] = { + // prefix / suffix + 'prefix': prefix, + 'sortby': sortby, + + // conjuctions + 'bool-group': boolClause, + // search clause triples + 'search-clause': searchClause, + + // grouping + 'punctuation': /[()]/, + }; + +}(Prism)); diff --git a/examples/prism-cql.html b/examples/prism-cql.html new file mode 100644 index 0000000000..3caa7a65fe --- /dev/null +++ b/examples/prism-cql.html @@ -0,0 +1,60 @@ +
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
+
+dc.title any fish
+fish
+cql.serverChoice = fish
+
+"fish"
+fish
+"squirrels fish"
+""
+
+title any fish
+dc.title any fish
+
+dc.title any fish
+dc.title cql.any fish
+
+dc.title any/relevant fish
+dc.title any/ relevant /cql.string fish
+dc.title any/rel.algorithm=cori fish
+
+dc.title any fish or dc.creator any sanderson
+dc.title any fish or (dc.creator any sanderson and dc.identifier = "id:1234567")
+
+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
+
+"cat" sortBy dc.title
+"dinosaur" sortBy dc.date/sort.descending dc.title/sort.ascending
+
+> dc = "http://deepcustard.org/" dc.custardDepth > 10
+> "http://deepcustard.org/" custardDepth > 10
+
+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.