initial commit
This commit is contained in:
commit
b41479bf7f
86 changed files with 7211 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
/data
|
||||
/work
|
||||
/logs
|
||||
/.idea
|
||||
/target
|
||||
.DS_Store
|
||||
*.iml
|
||||
/.settings
|
||||
/.classpath
|
||||
/.project
|
||||
/.gradle
|
||||
/build
|
7
.travis.yml
Normal file
7
.travis.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
sudo: false
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
202
LICENSE.txt
Normal file
202
LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
8
README.adoc
Normal file
8
README.adoc
Normal file
|
@ -0,0 +1,8 @@
|
|||
# xbib Contextual Query Language Compiler
|
||||
|
||||
image:https://api.travis-ci.org/xbib/cql.svg[title="Build status", link="https://travis-ci.org/xbib/cql/"]
|
||||
image:https://img.shields.io/sonar/http/nemo.sonarqube.com/org.xbib%3Acql/coverage.svg?style=flat-square[title="Coverage", link="https://sonarqube.com/dashboard/index?id=org.xbib%3Acql"]
|
||||
image:https://maven-badges.herokuapp.com/maven-central/org.xbib/cql/badge.svg[title="Maven Central", link="http://search.maven.org/#search%7Cga%7C1%7Cxbib%20cql"]
|
||||
image:https://img.shields.io/badge/License-Apache%202.0-blue.svg[title="Apache License 2.0", link="https://opensource.org/licenses/Apache-2.0"]
|
||||
image:https://img.shields.io/twitter/url/https/twitter.com/xbib.svg?style=social&label=Follow%20%40xbib[title="Twitter", link="https://twitter.com/xbib"]
|
||||
image:https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif[title="PayPal", link="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=GVHFQYZ9WZ8HG"]
|
65
build.gradle
Normal file
65
build.gradle
Normal file
|
@ -0,0 +1,65 @@
|
|||
plugins {
|
||||
id 'org.xbib.gradle.plugin.jflex' version '1.1.0'
|
||||
id 'org.xbib.gradle.plugin.jacc' version '1.1.3'
|
||||
id "org.sonarqube" version '2.2'
|
||||
}
|
||||
|
||||
group = 'org.xbib'
|
||||
version = '1.0.0'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'signing'
|
||||
apply plugin: 'findbugs'
|
||||
apply plugin: 'pmd'
|
||||
apply plugin: 'checkstyle'
|
||||
apply plugin: 'jacoco'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
wagon
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.xbib:content-core:1.0.6'
|
||||
testCompile 'junit:junit:4.12'
|
||||
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << "-Xlint:all" << "-profile" << "compact1"
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
showStandardStreams = false
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
classifier 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier 'javadoc'
|
||||
}
|
||||
artifacts {
|
||||
archives sourcesJar, javadocJar
|
||||
}
|
||||
if (project.hasProperty('signing.keyId')) {
|
||||
signing {
|
||||
sign configurations.archives
|
||||
}
|
||||
}
|
||||
|
||||
apply from: 'gradle/ext.gradle'
|
||||
apply from: 'gradle/publish.gradle'
|
||||
apply from: 'gradle/sonarqube.gradle'
|
323
config/checkstyle/checkstyle.xml
Normal file
323
config/checkstyle/checkstyle.xml
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!-- This is a checkstyle configuration file. For descriptions of
|
||||
what the following rules do, please see the checkstyle configuration
|
||||
page at http://checkstyle.sourceforge.net/config.html -->
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<module name="FileTabCharacter">
|
||||
<!-- Checks that there are no tab characters in the file.
|
||||
-->
|
||||
</module>
|
||||
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf"/>
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that FIXME is not used in comments. TODO is preferred.
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))FIXME" />
|
||||
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="RegexpSingleline">
|
||||
<!-- Checks that TODOs are named. (Actually, just that they are followed
|
||||
by an open paren.)
|
||||
-->
|
||||
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
|
||||
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
|
||||
</module>
|
||||
|
||||
<module name="JavadocPackage">
|
||||
<!-- Checks that each Java package has a Javadoc file used for commenting.
|
||||
Only allows a package-info.java, not package.html. -->
|
||||
</module>
|
||||
|
||||
<!-- All Java AST specific tests live under TreeWalker module. -->
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!--
|
||||
|
||||
IMPORT CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="RedundantImport">
|
||||
<!-- Checks for redundant import statements. -->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ImportOrder">
|
||||
<!-- Checks for out of order import statements. -->
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="groups" value="com,junit,net,org,java,javax"/>
|
||||
<!-- This ensures that static imports go first. -->
|
||||
<property name="option" value="top"/>
|
||||
<property name="tokens" value="STATIC_IMPORT, IMPORT"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
JAVADOC CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="allowMissingJavadoc" value="true"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||
<property name="allowUndeclaredRTE" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="JavadocStyle">
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!--
|
||||
|
||||
NAMING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<!-- Item 38 - Adhere to generally accepted naming conventions -->
|
||||
|
||||
<module name="PackageName">
|
||||
<!-- Validates identifiers for package names against the
|
||||
supplied expression. -->
|
||||
<!-- Here the default checkstyle rule restricts package name parts to
|
||||
seven characters, this is not in line with common practice at Google.
|
||||
-->
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="TypeNameCheck">
|
||||
<!-- Validates static, final fields against the
|
||||
expression "^[A-Z][a-zA-Z0-9]*$". -->
|
||||
<metadata name="altname" value="TypeName"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ConstantNameCheck">
|
||||
<!-- Validates non-private, static, final fields against the supplied
|
||||
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
|
||||
<metadata name="altname" value="ConstantName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="false"/>
|
||||
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="StaticVariableNameCheck">
|
||||
<!-- Validates static, non-final fields against the supplied
|
||||
expression "^[a-z][a-zA-Z0-9]*_?$". -->
|
||||
<metadata name="altname" value="StaticVariableName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MemberNameCheck">
|
||||
<!-- Validates non-static members against the supplied expression. -->
|
||||
<metadata name="altname" value="MemberName"/>
|
||||
<property name="applyToPublic" value="true"/>
|
||||
<property name="applyToProtected" value="true"/>
|
||||
<property name="applyToPackage" value="true"/>
|
||||
<property name="applyToPrivate" value="true"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="MethodNameCheck">
|
||||
<!-- Validates identifiers for method names. -->
|
||||
<metadata name="altname" value="MethodName"/>
|
||||
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="ParameterName">
|
||||
<!-- Validates identifiers for method parameters against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalFinalVariableName">
|
||||
<!-- Validates identifiers for local final variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="LocalVariableName">
|
||||
<!-- Validates identifiers for local variables against the
|
||||
expression "^[a-z][a-zA-Z0-9]*$". -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
LENGTH and CODING CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="LineLength">
|
||||
<!-- Checks if a line is too long. -->
|
||||
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<!--
|
||||
The default ignore pattern exempts the following elements:
|
||||
- import statements
|
||||
- long URLs inside comments
|
||||
-->
|
||||
|
||||
<property name="ignorePattern"
|
||||
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
|
||||
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
|
||||
</module>
|
||||
|
||||
<module name="LeftCurly">
|
||||
<!-- Checks for placement of the left curly brace ('{'). -->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<module name="RightCurly">
|
||||
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
|
||||
the same line. e.g., the following example is fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
} else
|
||||
</pre>
|
||||
-->
|
||||
<!-- This next example is not fine:
|
||||
<pre>
|
||||
if {
|
||||
...
|
||||
}
|
||||
else
|
||||
</pre>
|
||||
-->
|
||||
<property name="option" value="same"/>
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for braces around if and else blocks -->
|
||||
<module name="NeedBraces">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||
</module>
|
||||
|
||||
<module name="UpperEll">
|
||||
<!-- Checks that long constants are defined with an upper ell.-->
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="FallThrough">
|
||||
<!-- Warn about falling through to the next case statement. Similar to
|
||||
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
|
||||
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
|
||||
some other variants which we don't publicized to promote consistency).
|
||||
-->
|
||||
<property name="reliefPattern"
|
||||
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
MODIFIERS CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="ModifierOrder">
|
||||
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
|
||||
8.4.3. The prescribed order is:
|
||||
public, protected, private, abstract, static, final, transient, volatile,
|
||||
synchronized, native, strictfp
|
||||
-->
|
||||
</module>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
WHITESPACE CHECKS
|
||||
|
||||
-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<!-- Checks that various tokens are surrounded by whitespace.
|
||||
This includes most binary operators and keywords followed
|
||||
by regular or curly braces.
|
||||
-->
|
||||
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
|
||||
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
|
||||
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
|
||||
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
|
||||
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter">
|
||||
<!-- Checks that commas, semicolons and typecasts are followed by
|
||||
whitespace.
|
||||
-->
|
||||
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<!-- Checks that there is no whitespace after various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
|
||||
UNARY_PLUS"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="NoWhitespaceBefore">
|
||||
<!-- Checks that there is no whitespace before various unary operators.
|
||||
Linebreaks are allowed.
|
||||
-->
|
||||
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
<property name="severity" value="error"/>
|
||||
</module>
|
||||
|
||||
<module name="ParenPad">
|
||||
<!-- Checks that there is no whitespace before close parens or after
|
||||
open parens.
|
||||
-->
|
||||
<property name="severity" value="warning"/>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
</module>
|
||||
|
8
gradle/ext.gradle
Normal file
8
gradle/ext.gradle
Normal file
|
@ -0,0 +1,8 @@
|
|||
ext {
|
||||
user = 'xbib'
|
||||
projectName = 'cql'
|
||||
projectDescription = 'Contextual Query Language compiler for Java'
|
||||
scmUrl = 'https://github.com/xbib/cql'
|
||||
scmConnection = 'scm:git:git://github.com/xbib/cql.git'
|
||||
scmDeveloperConnection = 'scm:git:git://github.com/xbib/cql.git'
|
||||
}
|
66
gradle/publish.gradle
Normal file
66
gradle/publish.gradle
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
task xbibUpload(type: Upload, dependsOn: build) {
|
||||
configuration = configurations.archives
|
||||
uploadDescriptor = true
|
||||
repositories {
|
||||
if (project.hasProperty('xbibUsername')) {
|
||||
mavenDeployer {
|
||||
configuration = configurations.wagon
|
||||
repository(url: uri('scpexe://xbib.org/repository')) {
|
||||
authentication(userName: xbibUsername, privateKey: xbibPrivateKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task sonatypeUpload(type: Upload, dependsOn: build) {
|
||||
configuration = configurations.archives
|
||||
uploadDescriptor = true
|
||||
repositories {
|
||||
if (project.hasProperty('ossrhUsername')) {
|
||||
mavenDeployer {
|
||||
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||
repository(url: uri(ossrhReleaseUrl)) {
|
||||
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||
}
|
||||
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
|
||||
authentication(userName: ossrhUsername, password: ossrhPassword)
|
||||
}
|
||||
pom.project {
|
||||
groupId project.group
|
||||
artifactId project.name
|
||||
version project.version
|
||||
name project.name
|
||||
description projectDescription
|
||||
packaging 'jar'
|
||||
inceptionYear '2016'
|
||||
url scmUrl
|
||||
organization {
|
||||
name 'xbib'
|
||||
url 'http://xbib.org'
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id user
|
||||
name 'Jörg Prante'
|
||||
email 'joergprante@gmail.com'
|
||||
url 'https://github.com/jprante'
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url scmUrl
|
||||
connection scmConnection
|
||||
developerConnection scmDeveloperConnection
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name 'The Apache License, Version 2.0'
|
||||
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
gradle/sonarqube.gradle
Normal file
41
gradle/sonarqube.gradle
Normal file
|
@ -0,0 +1,41 @@
|
|||
tasks.withType(FindBugs) {
|
||||
ignoreFailures = true
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = false
|
||||
}
|
||||
}
|
||||
tasks.withType(Pmd) {
|
||||
ignoreFailures = true
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
tasks.withType(Checkstyle) {
|
||||
ignoreFailures = true
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled true
|
||||
csv.enabled false
|
||||
xml.destination "${buildDir}/reports/jacoco-xml"
|
||||
html.destination "${buildDir}/reports/jacoco-html"
|
||||
}
|
||||
}
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property "sonar.projectName", "${project.group} ${project.name}"
|
||||
property "sonar.sourceEncoding", "UTF-8"
|
||||
property "sonar.tests", "src/test/java"
|
||||
property "sonar.scm.provider", "git"
|
||||
property "sonar.java.coveragePlugin", "jacoco"
|
||||
property "sonar.junit.reportsPath", "build/test-results/test/"
|
||||
}
|
||||
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
#Thu Nov 24 21:44:19 CET 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip
|
164
gradlew
vendored
Executable file
164
gradlew
vendored
Executable file
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = 'cql'
|
1
src/main/jacc/org/xbib/cql/CQL.jacc
Normal file
1
src/main/jacc/org/xbib/cql/CQL.jacc
Normal file
File diff suppressed because one or more lines are too long
26
src/main/java/org/xbib/cql/AbstractNode.java
Normal file
26
src/main/java/org/xbib/cql/AbstractNode.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* This abstract node class is the base class for the CQL abstract syntax tree.
|
||||
*/
|
||||
public abstract class AbstractNode implements Node {
|
||||
|
||||
/**
|
||||
* Try to accept this node by a visitor.
|
||||
*
|
||||
* @param visitor the visitor
|
||||
*/
|
||||
@Override
|
||||
public abstract void accept(Visitor visitor);
|
||||
|
||||
/**
|
||||
* Compare this node to another node.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Node object) {
|
||||
if (this == object) {
|
||||
return 0;
|
||||
}
|
||||
return toString().compareTo(object.toString());
|
||||
}
|
||||
}
|
38
src/main/java/org/xbib/cql/BooleanGroup.java
Normal file
38
src/main/java/org/xbib/cql/BooleanGroup.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Abstract syntax tree of CQL - Boolean Group.
|
||||
*/
|
||||
public class BooleanGroup extends AbstractNode {
|
||||
|
||||
private BooleanOperator op;
|
||||
private ModifierList modifiers;
|
||||
|
||||
BooleanGroup(BooleanOperator op, ModifierList modifiers) {
|
||||
this.op = op;
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
BooleanGroup(BooleanOperator op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public BooleanOperator getOperator() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public ModifierList getModifierList() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return op != null && modifiers != null ? op + modifiers.toString()
|
||||
: op != null ? op.toString() : null;
|
||||
}
|
||||
}
|
80
src/main/java/org/xbib/cql/BooleanOperator.java
Normal file
80
src/main/java/org/xbib/cql/BooleanOperator.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Abstract syntax tree of CQL - boolean operator enumeration.
|
||||
*/
|
||||
public enum BooleanOperator {
|
||||
|
||||
AND("and"),
|
||||
OR("or"),
|
||||
NOT("not"),
|
||||
PROX("prox");
|
||||
/**
|
||||
* Token/operator map.
|
||||
*/
|
||||
private static Map<String, BooleanOperator> tokenMap;
|
||||
/**
|
||||
* Operator/token map.
|
||||
*/
|
||||
private static Map<BooleanOperator, String> opMap;
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* Creates a new Operator object.
|
||||
*
|
||||
* @param token the operator token
|
||||
*/
|
||||
BooleanOperator(String token) {
|
||||
this.token = token;
|
||||
map(token, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map token to operator.
|
||||
*
|
||||
* @param token the token
|
||||
* @param op the operator
|
||||
*/
|
||||
private static void map(String token, BooleanOperator op) {
|
||||
if (tokenMap == null) {
|
||||
tokenMap = new HashMap<>();
|
||||
}
|
||||
tokenMap.put(token, op);
|
||||
if (opMap == null) {
|
||||
opMap = new HashMap<>();
|
||||
}
|
||||
opMap.put(op, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token.
|
||||
*
|
||||
* @return the token
|
||||
*/
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operator for token.
|
||||
*
|
||||
* @param token the token
|
||||
* @return the operator
|
||||
*/
|
||||
static BooleanOperator forToken(Object token) {
|
||||
return tokenMap.get(token.toString().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write operator representation.
|
||||
*
|
||||
* @return the operator token
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return token;
|
||||
}
|
||||
}
|
224
src/main/java/org/xbib/cql/CQLGenerator.java
Normal file
224
src/main/java/org/xbib/cql/CQLGenerator.java
Normal file
|
@ -0,0 +1,224 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import org.xbib.cql.model.CQLQueryModel;
|
||||
import org.xbib.cql.model.Facet;
|
||||
import org.xbib.cql.model.Filter;
|
||||
import org.xbib.cql.model.Option;
|
||||
|
||||
/**
|
||||
* This is a CQL abstract syntax tree generator useful for normalizing CQL queries.
|
||||
*/
|
||||
public final class CQLGenerator implements Visitor {
|
||||
|
||||
/**
|
||||
* helper for managing our CQL query model (facet/filter/option contexts, breadcrumb trails etc.).
|
||||
*/
|
||||
private CQLQueryModel model;
|
||||
|
||||
/**
|
||||
* A replacement string.
|
||||
*/
|
||||
private String replacementString;
|
||||
|
||||
/**
|
||||
* String to be replaced.
|
||||
*/
|
||||
private String stringToBeReplaced;
|
||||
|
||||
public CQLGenerator() {
|
||||
this.replacementString = null;
|
||||
this.stringToBeReplaced = null;
|
||||
this.model = new CQLQueryModel();
|
||||
}
|
||||
|
||||
public CQLGenerator model(CQLQueryModel model) {
|
||||
this.model = model;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CQLQueryModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return model.getQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SortedQuery node) {
|
||||
if (node.getSortSpec() != null) {
|
||||
node.getSortSpec().accept(this);
|
||||
}
|
||||
if (node.getQuery() != null) {
|
||||
node.getQuery().accept(this);
|
||||
}
|
||||
model.setQuery(node.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Query node) {
|
||||
if (node.getPrefixAssignments() != null) {
|
||||
for (PrefixAssignment assignment : node.getPrefixAssignments()) {
|
||||
assignment.accept(this);
|
||||
}
|
||||
}
|
||||
if (node.getQuery() != null) {
|
||||
node.getQuery().accept(this);
|
||||
}
|
||||
if (node.getScopedClause() != null) {
|
||||
node.getScopedClause().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SortSpec node) {
|
||||
if (node.getSingleSpec() != null) {
|
||||
node.getSingleSpec().accept(this);
|
||||
}
|
||||
if (node.getSortSpec() != null) {
|
||||
node.getSortSpec().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SingleSpec node) {
|
||||
if (node.getIndex() != null) {
|
||||
node.getIndex().accept(this);
|
||||
}
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PrefixAssignment node) {
|
||||
node.getPrefix().accept(this);
|
||||
node.getURI().accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ScopedClause node) {
|
||||
if (node.getScopedClause() != null) {
|
||||
node.getScopedClause().accept(this);
|
||||
}
|
||||
node.getSearchClause().accept(this);
|
||||
if (node.getBooleanGroup() != null) {
|
||||
node.getBooleanGroup().accept(this);
|
||||
BooleanOperator op = node.getBooleanGroup().getOperator();
|
||||
checkFilter(op, node);
|
||||
checkFilter(op, node.getScopedClause());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BooleanGroup node) {
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SearchClause node) {
|
||||
if (node.getQuery() != null) {
|
||||
node.getQuery().accept(this);
|
||||
}
|
||||
if (node.getTerm() != null) {
|
||||
node.getTerm().accept(this);
|
||||
}
|
||||
if (node.getIndex() != null) {
|
||||
node.getIndex().accept(this);
|
||||
String context = node.getIndex().getContext();
|
||||
if (CQLQueryModel.FACET_INDEX_NAME.equals(context)) {
|
||||
Facet<Term> facet = new Facet<>(node.getIndex().getName());
|
||||
facet.setValue(node.getTerm());
|
||||
model.addFacet(facet);
|
||||
} else if (CQLQueryModel.OPTION_INDEX_NAME.equals(context)) {
|
||||
Option<Term> option = new Option<>();
|
||||
option.setName(node.getIndex().getName());
|
||||
option.setValue(node.getTerm());
|
||||
model.addOption(option);
|
||||
}
|
||||
}
|
||||
if (node.getRelation() != null) {
|
||||
node.getRelation().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Relation node) {
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Modifier node) {
|
||||
if (node.getTerm() != null) {
|
||||
node.getTerm().accept(this);
|
||||
}
|
||||
if (node.getName() != null) {
|
||||
node.getName().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ModifierList node) {
|
||||
for (Modifier modifier : node.getModifierList()) {
|
||||
modifier.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Term node) {
|
||||
if (replacementString != null && stringToBeReplaced.equals(node.getValue())) {
|
||||
node.setValue(replacementString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Identifier node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SimpleName node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Index node) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a substitution query, for example when a term has been
|
||||
* suggested to be replaced by another term.
|
||||
*
|
||||
* @param oldTerm the term to be replaced
|
||||
* @param newTerm the replacement term
|
||||
* @return the new query with the term replaced
|
||||
*/
|
||||
public synchronized String writeSubstitutedForm(String oldTerm, String newTerm) {
|
||||
this.stringToBeReplaced = oldTerm;
|
||||
this.replacementString = newTerm;
|
||||
CQLParser parser = new CQLParser(model.getQuery());
|
||||
parser.parse();
|
||||
parser.getCQLQuery().accept(this);
|
||||
String result = model.getQuery();
|
||||
this.stringToBeReplaced = null;
|
||||
this.replacementString = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public String withBreadcrumbs() {
|
||||
return model.toCQL();
|
||||
}
|
||||
|
||||
private void checkFilter(BooleanOperator op, ScopedClause node) {
|
||||
if (node.getSearchClause().getIndex() != null
|
||||
&& CQLQueryModel.FILTER_INDEX_NAME.equals(node.getSearchClause().getIndex().getContext())) {
|
||||
String filtername = node.getSearchClause().getIndex().getName();
|
||||
Comparitor filterop = node.getSearchClause().getRelation().getComparitor();
|
||||
Term filterterm = node.getSearchClause().getTerm();
|
||||
Filter<AbstractNode> filter2 = new Filter<>(filtername, filterterm, filterop);
|
||||
model.addFilter(op, filter2);
|
||||
}
|
||||
}
|
||||
}
|
80
src/main/java/org/xbib/cql/Comparitor.java
Normal file
80
src/main/java/org/xbib/cql/Comparitor.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* CQL operators.
|
||||
*/
|
||||
public enum Comparitor {
|
||||
|
||||
EQUALS("="),
|
||||
GREATER(">"),
|
||||
GREATER_EQUALS(">="),
|
||||
LESS("<"),
|
||||
LESS_EQUALS("<="),
|
||||
NOT_EQUALS("<>"),
|
||||
WITHIN("within"),
|
||||
CQLWITHIN("cql.within"),
|
||||
ENCLOSES("encloses"),
|
||||
CQLENCLOSES("cql.encloses"),
|
||||
ADJ("adj"),
|
||||
CQLADJ("cql.adj"),
|
||||
ALL("all"),
|
||||
CQLALL("cql.all"),
|
||||
ANY("any"),
|
||||
CQLANY("cql.any");
|
||||
private static HashMap<String, Comparitor> tokenMap;
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* Creates a new Operator object.
|
||||
*
|
||||
* @param token the operator token
|
||||
*/
|
||||
private Comparitor(String token) {
|
||||
this.token = token;
|
||||
map(token, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map token to operator
|
||||
*
|
||||
* @param token the token
|
||||
* @param op the operator
|
||||
*/
|
||||
private static void map(String token, Comparitor op) {
|
||||
if (tokenMap == null) {
|
||||
tokenMap = new HashMap<>();
|
||||
}
|
||||
tokenMap.put(token, op);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get token.
|
||||
*
|
||||
* @return the token
|
||||
*/
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get operator for token.
|
||||
*
|
||||
* @param token the token
|
||||
* @return the operator
|
||||
*/
|
||||
static Comparitor forToken(Object token) {
|
||||
return tokenMap.get(token.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write operator representation.
|
||||
*
|
||||
* @return the operator token
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return token;
|
||||
}
|
||||
}
|
33
src/main/java/org/xbib/cql/Identifier.java
Normal file
33
src/main/java/org/xbib/cql/Identifier.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* An Identifier is a SimpleName or a String in double quotes.
|
||||
*/
|
||||
public class Identifier extends AbstractNode {
|
||||
|
||||
private String value;
|
||||
private boolean quoted;
|
||||
|
||||
public Identifier(String value) {
|
||||
this.value = value;
|
||||
this.quoted = true;
|
||||
}
|
||||
|
||||
public Identifier(SimpleName name) {
|
||||
this.value = name.getName();
|
||||
this.quoted = false;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value != null && quoted ? "\"" + value.replaceAll("\"", "\\\\\"") + "\"" : value;
|
||||
}
|
||||
}
|
51
src/main/java/org/xbib/cql/Index.java
Normal file
51
src/main/java/org/xbib/cql/Index.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Abstract syntax tree of CQL - Index.
|
||||
* The Index consists of <b>context</b> and <b>name</b>
|
||||
* The default context is "cql" and is of the same concept like a namespace.
|
||||
*/
|
||||
public class Index extends AbstractNode {
|
||||
|
||||
private String context;
|
||||
private String name;
|
||||
|
||||
public Index(String name) {
|
||||
this.name = name;
|
||||
int pos = name.indexOf('.');
|
||||
if (pos > 0) {
|
||||
this.context = name.substring(0, pos);
|
||||
this.name = name.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public Index(SimpleName name) {
|
||||
this(name.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the context of the index
|
||||
*/
|
||||
public String getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the index
|
||||
*
|
||||
* @return the name of the index
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return context != null ? context + "." + name : name;
|
||||
}
|
||||
|
||||
}
|
45
src/main/java/org/xbib/cql/Modifier.java
Normal file
45
src/main/java/org/xbib/cql/Modifier.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Modifier.
|
||||
*/
|
||||
public class Modifier extends AbstractNode {
|
||||
|
||||
private SimpleName name;
|
||||
|
||||
private Comparitor op;
|
||||
|
||||
private Term term;
|
||||
|
||||
public Modifier(SimpleName name, Comparitor op, Term term) {
|
||||
this.name = name;
|
||||
this.op = op;
|
||||
this.term = term;
|
||||
}
|
||||
|
||||
public Modifier(SimpleName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public SimpleName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Comparitor getOperator() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public Term getTerm() {
|
||||
return term;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "/" + (term != null ? name.toString() + op + term : name.toString());
|
||||
}
|
||||
|
||||
}
|
39
src/main/java/org/xbib/cql/ModifierList.java
Normal file
39
src/main/java/org/xbib/cql/ModifierList.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Modifier list. This is a recursive data structure with a Modifier and optionally a ModifierList.
|
||||
*/
|
||||
public class ModifierList extends AbstractNode {
|
||||
|
||||
private List<Modifier> modifierList = new LinkedList<>();
|
||||
|
||||
public ModifierList(ModifierList modifiers, Modifier modifier) {
|
||||
modifierList.addAll(modifiers.modifierList);
|
||||
modifierList.add(modifier);
|
||||
}
|
||||
|
||||
public ModifierList(Modifier modifier) {
|
||||
modifierList.add(modifier);
|
||||
}
|
||||
|
||||
public List<Modifier> getModifierList() {
|
||||
return modifierList;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Modifier m : modifierList) {
|
||||
sb.append(m.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/org/xbib/cql/Node.java
Normal file
14
src/main/java/org/xbib/cql/Node.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* This is a node interface for the CQL abstract syntax tree.
|
||||
*/
|
||||
public interface Node extends Comparable<Node> {
|
||||
|
||||
/**
|
||||
* Accept a visitor on this node.
|
||||
*
|
||||
* @param visitor the visitor
|
||||
*/
|
||||
void accept(Visitor visitor);
|
||||
}
|
38
src/main/java/org/xbib/cql/PrefixAssignment.java
Normal file
38
src/main/java/org/xbib/cql/PrefixAssignment.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Prefix assignment.
|
||||
*/
|
||||
public class PrefixAssignment extends AbstractNode {
|
||||
|
||||
private Term prefix;
|
||||
|
||||
private Term uri;
|
||||
|
||||
public PrefixAssignment(Term prefix, Term uri) {
|
||||
this.prefix = prefix;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public PrefixAssignment(Term uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Term getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public Term getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "> " + prefix + " = " + uri;
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/org/xbib/cql/Query.java
Normal file
57
src/main/java/org/xbib/cql/Query.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CQL query.
|
||||
*/
|
||||
public class Query extends AbstractNode {
|
||||
|
||||
private List<PrefixAssignment> prefixes = new LinkedList<>();
|
||||
|
||||
private Query query;
|
||||
|
||||
private ScopedClause clause;
|
||||
|
||||
Query(PrefixAssignment assignment, Query query) {
|
||||
prefixes.add(assignment);
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
Query(ScopedClause clause) {
|
||||
this.clause = clause;
|
||||
}
|
||||
|
||||
public List<PrefixAssignment> getPrefixAssignments() {
|
||||
return prefixes;
|
||||
}
|
||||
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public ScopedClause getScopedClause() {
|
||||
return clause;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (PrefixAssignment assignment : prefixes) {
|
||||
sb.append(assignment.toString()).append(' ');
|
||||
}
|
||||
if (query != null) {
|
||||
sb.append(query);
|
||||
}
|
||||
if (clause != null) {
|
||||
sb.append(clause);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
21
src/main/java/org/xbib/cql/QueryFacet.java
Normal file
21
src/main/java/org/xbib/cql/QueryFacet.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Query facet.
|
||||
*/
|
||||
public interface QueryFacet<V> extends QueryOption<V> {
|
||||
/**
|
||||
* The size of the facet.
|
||||
*
|
||||
* @return the facet size
|
||||
*/
|
||||
int getSize();
|
||||
|
||||
/**
|
||||
* Get the filter name which must be used for filtering facet entries.
|
||||
*
|
||||
* @return the filter name
|
||||
*/
|
||||
String getFilterName();
|
||||
|
||||
}
|
7
src/main/java/org/xbib/cql/QueryFilter.java
Normal file
7
src/main/java/org/xbib/cql/QueryFilter.java
Normal file
|
@ -0,0 +1,7 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* A Filter for a query.
|
||||
*/
|
||||
public interface QueryFilter<V> extends QueryOption<V> {
|
||||
}
|
16
src/main/java/org/xbib/cql/QueryOption.java
Normal file
16
src/main/java/org/xbib/cql/QueryOption.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Qery option.
|
||||
* @param <V> parameter type
|
||||
*/
|
||||
public interface QueryOption<V> {
|
||||
|
||||
void setName(String name);
|
||||
|
||||
String getName();
|
||||
|
||||
void setValue(V value);
|
||||
|
||||
V getValue();
|
||||
}
|
39
src/main/java/org/xbib/cql/Relation.java
Normal file
39
src/main/java/org/xbib/cql/Relation.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Relation to a ModifierList.
|
||||
*/
|
||||
public class Relation extends AbstractNode {
|
||||
|
||||
private Comparitor comparitor;
|
||||
private ModifierList modifiers;
|
||||
|
||||
public Relation(Comparitor comparitor, ModifierList modifiers) {
|
||||
this.comparitor = comparitor;
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
public Relation(Comparitor comparitor) {
|
||||
this.comparitor = comparitor;
|
||||
}
|
||||
|
||||
public Comparitor getComparitor() {
|
||||
return comparitor;
|
||||
}
|
||||
|
||||
public ModifierList getModifierList() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return modifiers != null ? comparitor + modifiers.toString()
|
||||
: comparitor.toString();
|
||||
}
|
||||
|
||||
}
|
50
src/main/java/org/xbib/cql/ScopedClause.java
Normal file
50
src/main/java/org/xbib/cql/ScopedClause.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Scoped clause. This is a recursive data structure with a SearchClause and
|
||||
* optionally a ScopedClause.
|
||||
* SearchClause and ScopedClause are connected through a BooleanGroup.
|
||||
*/
|
||||
public class ScopedClause extends AbstractNode {
|
||||
|
||||
private ScopedClause clause;
|
||||
private BooleanGroup booleangroup;
|
||||
private SearchClause search;
|
||||
|
||||
ScopedClause(ScopedClause clause, BooleanGroup bg, SearchClause search) {
|
||||
this.clause = clause;
|
||||
this.booleangroup = bg;
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
ScopedClause(SearchClause search) {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
public ScopedClause getScopedClause() {
|
||||
return clause;
|
||||
}
|
||||
|
||||
public BooleanGroup getBooleanGroup() {
|
||||
return booleangroup;
|
||||
}
|
||||
|
||||
public SearchClause getSearchClause() {
|
||||
return search;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = search.toString();
|
||||
boolean hasQuery = s.length() > 0;
|
||||
return clause != null && hasQuery ? clause + " " + booleangroup + " " + search
|
||||
: clause != null ? clause.toString()
|
||||
: hasQuery ? search.toString()
|
||||
: "";
|
||||
}
|
||||
}
|
62
src/main/java/org/xbib/cql/SearchClause.java
Normal file
62
src/main/java/org/xbib/cql/SearchClause.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import org.xbib.cql.model.CQLQueryModel;
|
||||
|
||||
/**
|
||||
* Search clause.
|
||||
*/
|
||||
public class SearchClause extends AbstractNode {
|
||||
|
||||
private Query query;
|
||||
private Index index;
|
||||
private Relation relation;
|
||||
private Term term;
|
||||
|
||||
SearchClause(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
SearchClause(Index index, Relation relation, Term term) {
|
||||
this.index = index;
|
||||
this.relation = relation;
|
||||
this.term = term;
|
||||
}
|
||||
|
||||
SearchClause(Term term) {
|
||||
this.term = term;
|
||||
}
|
||||
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public Index getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public Term getTerm() {
|
||||
return term;
|
||||
}
|
||||
|
||||
public Relation getRelation() {
|
||||
return relation;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CQL string
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return query != null && query.toString().length() > 0 ? "(" + query + ")"
|
||||
: query != null ? ""
|
||||
: index != null && !CQLQueryModel.isVisible(index.getContext()) ? ""
|
||||
: index != null ? index + " " + relation + " " + term
|
||||
: term != null ? term.toString()
|
||||
: null;
|
||||
}
|
||||
|
||||
}
|
28
src/main/java/org/xbib/cql/SimpleName.java
Normal file
28
src/main/java/org/xbib/cql/SimpleName.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* A SimpleName consists of a String which is not surrounded by double quotes.
|
||||
*/
|
||||
public class SimpleName extends AbstractNode {
|
||||
|
||||
private String name;
|
||||
|
||||
public SimpleName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
37
src/main/java/org/xbib/cql/SingleSpec.java
Normal file
37
src/main/java/org/xbib/cql/SingleSpec.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Single spec.
|
||||
*/
|
||||
public class SingleSpec extends AbstractNode {
|
||||
|
||||
private Index index;
|
||||
private ModifierList modifiers;
|
||||
|
||||
public SingleSpec(Index index, ModifierList modifiers) {
|
||||
this.index = index;
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
public SingleSpec(Index index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public Index getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public ModifierList getModifierList() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return index + (modifiers != null ? modifiers.toString() : "");
|
||||
}
|
||||
|
||||
}
|
38
src/main/java/org/xbib/cql/SortSpec.java
Normal file
38
src/main/java/org/xbib/cql/SortSpec.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Abstract syntax tree of CQL, the sort specification.
|
||||
*/
|
||||
public class SortSpec extends AbstractNode {
|
||||
|
||||
private SortSpec sortspec;
|
||||
private SingleSpec spec;
|
||||
|
||||
public SortSpec(SortSpec sortspec, SingleSpec spec) {
|
||||
this.sortspec = sortspec;
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
public SortSpec(SingleSpec spec) {
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
public SortSpec getSortSpec() {
|
||||
return sortspec;
|
||||
}
|
||||
|
||||
public SingleSpec getSingleSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (sortspec != null ? sortspec + " " : "") + spec;
|
||||
}
|
||||
|
||||
}
|
39
src/main/java/org/xbib/cql/SortedQuery.java
Normal file
39
src/main/java/org/xbib/cql/SortedQuery.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* Sorted query.
|
||||
*/
|
||||
public class SortedQuery extends AbstractNode {
|
||||
|
||||
private Query query;
|
||||
|
||||
private SortSpec spec;
|
||||
|
||||
SortedQuery(Query query, SortSpec spec) {
|
||||
this.query = query;
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
SortedQuery(Query query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public SortSpec getSortSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return query != null && spec != null ? query + " sortby " + spec
|
||||
: query != null ? query.toString() : "";
|
||||
}
|
||||
}
|
25
src/main/java/org/xbib/cql/SyntaxException.java
Normal file
25
src/main/java/org/xbib/cql/SyntaxException.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* CQL Syntax exception.
|
||||
*/
|
||||
public class SyntaxException extends RuntimeException {
|
||||
/**
|
||||
* Creates a new SyntaxException object.
|
||||
*
|
||||
* @param msg the message for this syntax exception
|
||||
*/
|
||||
public SyntaxException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SyntaxException object.
|
||||
*
|
||||
* @param msg the message for this syntax exception
|
||||
* @param t the throwable for this syntax exception
|
||||
*/
|
||||
public SyntaxException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
147
src/main/java/org/xbib/cql/Term.java
Normal file
147
src/main/java/org/xbib/cql/Term.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* A CQL Term.
|
||||
*/
|
||||
public class Term extends AbstractNode {
|
||||
|
||||
private static final TimeZone tz = TimeZone.getTimeZone("GMT");
|
||||
private static final String ISO_FORMAT_SECONDS = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
private static final String ISO_FORMAT_DAYS = "yyyy-MM-dd";
|
||||
|
||||
private String value;
|
||||
private Long longvalue;
|
||||
private Double doublevalue;
|
||||
private Identifier identifier;
|
||||
private Date datevalue;
|
||||
private SimpleName name;
|
||||
|
||||
public Term(String value) {
|
||||
this.value = value;
|
||||
try {
|
||||
// check for hidden dates. CQL does not support ISO dates.
|
||||
this.datevalue = parseDateISO(value);
|
||||
this.value = null;
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Term(Identifier identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public Term(SimpleName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Term(Long value) {
|
||||
this.longvalue = value;
|
||||
}
|
||||
|
||||
public Term(Double value) {
|
||||
this.doublevalue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value, useful for inline replacements
|
||||
* in spellcheck suggestions
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value is a String it is embedded in quotation marks.
|
||||
* If its a Integer or a Double it is returned without
|
||||
* quotation marks.
|
||||
*
|
||||
* @return the value as String
|
||||
*/
|
||||
public String getValue() {
|
||||
return longvalue != null ? Long.toString(longvalue)
|
||||
: doublevalue != null ? Double.toString(doublevalue)
|
||||
: value != null ? value
|
||||
: identifier != null ? identifier.toString()
|
||||
: name != null ? name.toString()
|
||||
: null;
|
||||
}
|
||||
|
||||
public boolean isLong() {
|
||||
return longvalue != null;
|
||||
}
|
||||
|
||||
public boolean isFloat() {
|
||||
return doublevalue != null;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public boolean isName() {
|
||||
return name != null;
|
||||
}
|
||||
|
||||
public boolean isIdentifier() {
|
||||
return identifier != null;
|
||||
}
|
||||
|
||||
public boolean isDate() {
|
||||
return datevalue != null;
|
||||
}
|
||||
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
private Date parseDateISO(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat();
|
||||
sdf.applyPattern(ISO_FORMAT_SECONDS);
|
||||
sdf.setTimeZone(tz);
|
||||
sdf.setLenient(true);
|
||||
try {
|
||||
return sdf.parse(value);
|
||||
} catch (ParseException pe) {
|
||||
// skip
|
||||
}
|
||||
sdf.applyPattern(ISO_FORMAT_DAYS);
|
||||
try {
|
||||
return sdf.parse(value);
|
||||
} catch (ParseException pe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String formatDateISO(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat();
|
||||
sdf.applyPattern(ISO_FORMAT_SECONDS);
|
||||
sdf.setTimeZone(tz);
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return longvalue != null ? Long.toString(longvalue)
|
||||
: doublevalue != null ? Double.toString(doublevalue)
|
||||
: datevalue != null ? formatDateISO(datevalue)
|
||||
: value != null ? value.startsWith("\"") && value.endsWith("\"") ? value
|
||||
: "\"" + value.replaceAll("\"", "\\\\\"") + "\""
|
||||
: identifier != null ? identifier.toString()
|
||||
: name != null ? name.toString()
|
||||
: null;
|
||||
}
|
||||
}
|
38
src/main/java/org/xbib/cql/Visitor.java
Normal file
38
src/main/java/org/xbib/cql/Visitor.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
/**
|
||||
* CQL abstract syntax tree visitor.
|
||||
*/
|
||||
public interface Visitor {
|
||||
|
||||
void visit(SortedQuery node);
|
||||
|
||||
void visit(Query node);
|
||||
|
||||
void visit(PrefixAssignment node);
|
||||
|
||||
void visit(ScopedClause node);
|
||||
|
||||
void visit(BooleanGroup node);
|
||||
|
||||
void visit(SearchClause node);
|
||||
|
||||
void visit(Relation node);
|
||||
|
||||
void visit(Modifier node);
|
||||
|
||||
void visit(ModifierList node);
|
||||
|
||||
void visit(Term node);
|
||||
|
||||
void visit(Identifier node);
|
||||
|
||||
void visit(Index node);
|
||||
|
||||
void visit(SimpleName node);
|
||||
|
||||
void visit(SortSpec node);
|
||||
|
||||
void visit(SingleSpec node);
|
||||
|
||||
}
|
|
@ -0,0 +1,349 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.cql.util.DateUtil;
|
||||
import org.xbib.cql.BooleanGroup;
|
||||
import org.xbib.cql.BooleanOperator;
|
||||
import org.xbib.cql.Comparitor;
|
||||
import org.xbib.cql.Identifier;
|
||||
import org.xbib.cql.Index;
|
||||
import org.xbib.cql.ModifierList;
|
||||
import org.xbib.cql.PrefixAssignment;
|
||||
import org.xbib.cql.Query;
|
||||
import org.xbib.cql.Relation;
|
||||
import org.xbib.cql.ScopedClause;
|
||||
import org.xbib.cql.SearchClause;
|
||||
import org.xbib.cql.SimpleName;
|
||||
import org.xbib.cql.SingleSpec;
|
||||
import org.xbib.cql.SortSpec;
|
||||
import org.xbib.cql.SortedQuery;
|
||||
import org.xbib.cql.SyntaxException;
|
||||
import org.xbib.cql.Term;
|
||||
import org.xbib.cql.Visitor;
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Node;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
import org.xbib.cql.elasticsearch.ast.TokenType;
|
||||
import org.xbib.cql.elasticsearch.model.ElasticsearchQueryModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Generate Elasticsearch filter query from CQL abstract syntax tree.
|
||||
*/
|
||||
public class ElasticsearchFilterGenerator implements Visitor {
|
||||
|
||||
private final ElasticsearchQueryModel model;
|
||||
|
||||
private Stack<Node> stack;
|
||||
|
||||
private FilterGenerator filterGen;
|
||||
|
||||
public ElasticsearchFilterGenerator() {
|
||||
this(new ElasticsearchQueryModel());
|
||||
}
|
||||
|
||||
public ElasticsearchFilterGenerator(ElasticsearchQueryModel model) {
|
||||
this.model = model;
|
||||
this.stack = new Stack<>();
|
||||
try {
|
||||
this.filterGen = new FilterGenerator();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void addOrFilter(String filterKey, Collection<String> filterValues) {
|
||||
for (String value : filterValues) {
|
||||
model.addDisjunctiveFilter(filterKey, new Expression(Operator.OR_FILTER, new Name(filterKey), new Token(value)), Operator.OR);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAndFilter(String filterKey, Collection<String> filterValues) {
|
||||
for (String value : filterValues) {
|
||||
model.addConjunctiveFilter(filterKey, new Expression(Operator.AND_FILTER, new Name(filterKey), new Token(value)), Operator.AND);
|
||||
}
|
||||
}
|
||||
|
||||
public XContentBuilder getResult() throws IOException {
|
||||
return filterGen.getResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SortedQuery node) {
|
||||
try {
|
||||
filterGen.start();
|
||||
node.getQuery().accept(this);
|
||||
Node querynode = stack.pop();
|
||||
if (querynode instanceof Token) {
|
||||
filterGen.visit(new Expression(Operator.TERM_FILTER, new Name("cql.allIndexes"), querynode));
|
||||
} else if (querynode instanceof Expression) {
|
||||
filterGen.visit(new Expression(Operator.QUERY_FILTER, (Expression) querynode));
|
||||
}
|
||||
if (model.hasFilter()) {
|
||||
filterGen.visit(model.getFilterExpression());
|
||||
}
|
||||
filterGen.end();
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException("unable to build a valid query from " + node + ", reason: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SortSpec node) {
|
||||
if (node.getSingleSpec() != null) {
|
||||
node.getSingleSpec().accept(this);
|
||||
}
|
||||
if (node.getSortSpec() != null) {
|
||||
node.getSortSpec().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SingleSpec node) {
|
||||
if (node.getIndex() != null) {
|
||||
node.getIndex().accept(this);
|
||||
}
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
if (!stack.isEmpty()) {
|
||||
model.setSort(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Query node) {
|
||||
for (PrefixAssignment assignment : node.getPrefixAssignments()) {
|
||||
assignment.accept(this);
|
||||
}
|
||||
if (node.getScopedClause() != null) {
|
||||
node.getScopedClause().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PrefixAssignment node) {
|
||||
node.getPrefix().accept(this);
|
||||
node.getURI().accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ScopedClause node) {
|
||||
if (node.getScopedClause() != null) {
|
||||
node.getScopedClause().accept(this);
|
||||
}
|
||||
node.getSearchClause().accept(this);
|
||||
if (node.getBooleanGroup() != null) {
|
||||
node.getBooleanGroup().accept(this);
|
||||
}
|
||||
// evaluate expression
|
||||
if (!stack.isEmpty() && stack.peek() instanceof Operator) {
|
||||
Operator op = (Operator) stack.pop();
|
||||
if (!stack.isEmpty()) {
|
||||
Node esnode = stack.pop();
|
||||
// add default context if node is a literal without a context
|
||||
if (esnode instanceof Token && TokenType.STRING.equals(esnode.getType())) {
|
||||
esnode = new Expression(Operator.ALL, new Name("cql.allIndexes"), esnode);
|
||||
}
|
||||
if (stack.isEmpty()) {
|
||||
// unary expression
|
||||
throw new IllegalArgumentException("unary expression not allowed, op=" + op + " node=" + esnode);
|
||||
} else {
|
||||
// binary expression
|
||||
Node esnode2 = stack.pop();
|
||||
// add default context if node is a literal without context
|
||||
if (esnode2 instanceof Token && TokenType.STRING.equals(esnode2.getType())) {
|
||||
esnode2 = new Expression(Operator.ALL, new Name("cql.allIndexes"), esnode2);
|
||||
}
|
||||
esnode = new Expression(op, esnode2, esnode);
|
||||
}
|
||||
stack.push(esnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SearchClause node) {
|
||||
if (node.getQuery() != null) {
|
||||
// CQL query in parenthesis
|
||||
node.getQuery().accept(this);
|
||||
}
|
||||
if (node.getTerm() != null) {
|
||||
node.getTerm().accept(this);
|
||||
}
|
||||
if (node.getIndex() != null) {
|
||||
node.getIndex().accept(this);
|
||||
}
|
||||
if (node.getRelation() != null) {
|
||||
node.getRelation().accept(this);
|
||||
if (node.getRelation().getModifierList() != null && node.getIndex() != null) {
|
||||
// stack layout: op, list of modifiers, modifiable index
|
||||
Node op = stack.pop();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Node modifier = stack.pop();
|
||||
while (modifier instanceof Modifier) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append('.');
|
||||
}
|
||||
sb.append(modifier.toString());
|
||||
modifier = stack.pop();
|
||||
}
|
||||
String modifiable = sb.toString();
|
||||
stack.push(new Name(modifiable));
|
||||
stack.push(op);
|
||||
}
|
||||
}
|
||||
// evaluate expression
|
||||
if (!stack.isEmpty() && stack.peek() instanceof Operator) {
|
||||
Operator op = (Operator) stack.pop();
|
||||
Node arg1 = stack.pop();
|
||||
Node arg2 = stack.pop();
|
||||
// fold two expressions if they have the same operator
|
||||
boolean fold = arg1.isVisible() && arg2.isVisible()
|
||||
&& arg2 instanceof Expression
|
||||
&& ((Expression) arg2).getOperator().equals(op);
|
||||
Expression expression = fold ? new Expression((Expression) arg2, arg1) : new Expression(op, arg1, arg2);
|
||||
stack.push(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BooleanGroup node) {
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
stack.push(booleanToES(node.getOperator()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Relation node) {
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
stack.push(comparitorToES(node.getComparitor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ModifierList node) {
|
||||
for (org.xbib.cql.Modifier modifier : node.getModifierList()) {
|
||||
modifier.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(org.xbib.cql.Modifier node) {
|
||||
Node term = null;
|
||||
if (node.getTerm() != null) {
|
||||
node.getTerm().accept(this);
|
||||
term = stack.pop();
|
||||
}
|
||||
node.getName().accept(this);
|
||||
Node name = stack.pop();
|
||||
stack.push(new Modifier(name, term));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Term node) {
|
||||
stack.push(termToES(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Identifier node) {
|
||||
stack.push(new Name(node.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Index node) {
|
||||
String context = node.getContext();
|
||||
String name = context != null ? context + "." + node.getName() : node.getName();
|
||||
Name esname = new Name(name, model.getVisibility(context));
|
||||
esname.setType(model.getElasticsearchType(name));
|
||||
stack.push(esname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SimpleName node) {
|
||||
stack.push(new Name(node.getName()));
|
||||
}
|
||||
|
||||
private Node termToES(Term node) {
|
||||
if (node.isLong()) {
|
||||
return new Token(Long.parseLong(node.getValue()));
|
||||
} else if (node.isFloat()) {
|
||||
return new Token(Double.parseDouble(node.getValue()));
|
||||
} else if (node.isIdentifier()) {
|
||||
return new Token(node.getValue());
|
||||
} else if (node.isDate()) {
|
||||
return new Token(DateUtil.parseDateISO(node.getValue()));
|
||||
} else if (node.isString()) {
|
||||
return new Token(node.getValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Operator booleanToES(BooleanOperator bop) {
|
||||
Operator op;
|
||||
switch (bop) {
|
||||
case AND:
|
||||
op = Operator.AND;
|
||||
break;
|
||||
case OR:
|
||||
op = Operator.OR;
|
||||
break;
|
||||
case NOT:
|
||||
op = Operator.ANDNOT;
|
||||
break;
|
||||
case PROX:
|
||||
op = Operator.PROX;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown CQL operator: " + bop);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
private Operator comparitorToES(Comparitor op) {
|
||||
Operator esop;
|
||||
switch (op) {
|
||||
case EQUALS:
|
||||
esop = Operator.EQUALS;
|
||||
break;
|
||||
case GREATER:
|
||||
esop = Operator.RANGE_GREATER_THAN;
|
||||
break;
|
||||
case GREATER_EQUALS:
|
||||
esop = Operator.RANGE_GREATER_OR_EQUAL;
|
||||
break;
|
||||
case LESS:
|
||||
esop = Operator.RANGE_LESS_THAN;
|
||||
break;
|
||||
case LESS_EQUALS:
|
||||
esop = Operator.RANGE_LESS_OR_EQUALS;
|
||||
break;
|
||||
case NOT_EQUALS:
|
||||
esop = Operator.NOT_EQUALS;
|
||||
break;
|
||||
case WITHIN:
|
||||
esop = Operator.RANGE_WITHIN;
|
||||
break;
|
||||
case ADJ:
|
||||
esop = Operator.PHRASE;
|
||||
break;
|
||||
case ALL:
|
||||
esop = Operator.ALL;
|
||||
break;
|
||||
case ANY:
|
||||
esop = Operator.ANY;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown CQL comparitor: " + op);
|
||||
}
|
||||
return esop;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,492 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.cql.BooleanGroup;
|
||||
import org.xbib.cql.BooleanOperator;
|
||||
import org.xbib.cql.CQLParser;
|
||||
import org.xbib.cql.Comparitor;
|
||||
import org.xbib.cql.Identifier;
|
||||
import org.xbib.cql.Index;
|
||||
import org.xbib.cql.ModifierList;
|
||||
import org.xbib.cql.PrefixAssignment;
|
||||
import org.xbib.cql.Query;
|
||||
import org.xbib.cql.Relation;
|
||||
import org.xbib.cql.ScopedClause;
|
||||
import org.xbib.cql.SearchClause;
|
||||
import org.xbib.cql.SimpleName;
|
||||
import org.xbib.cql.SingleSpec;
|
||||
import org.xbib.cql.SortSpec;
|
||||
import org.xbib.cql.SortedQuery;
|
||||
import org.xbib.cql.SyntaxException;
|
||||
import org.xbib.cql.Term;
|
||||
import org.xbib.cql.Visitor;
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Node;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
import org.xbib.cql.elasticsearch.ast.TokenType;
|
||||
import org.xbib.cql.elasticsearch.model.ElasticsearchQueryModel;
|
||||
import org.xbib.cql.util.DateUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Generate Elasticsearch QueryModel DSL from CQL abstract syntax tree
|
||||
*/
|
||||
public class ElasticsearchQueryGenerator implements Visitor {
|
||||
|
||||
private ElasticsearchQueryModel model;
|
||||
|
||||
private ElasticsearchFilterGenerator filterGenerator;
|
||||
|
||||
private Stack<Node> stack;
|
||||
|
||||
private int from;
|
||||
|
||||
private int size;
|
||||
|
||||
private String boostField;
|
||||
|
||||
private String modifier;
|
||||
|
||||
private Float factor;
|
||||
|
||||
private String boostMode;
|
||||
|
||||
private SourceGenerator sourceGen;
|
||||
|
||||
private QueryGenerator queryGen;
|
||||
|
||||
private FilterGenerator filterGen;
|
||||
|
||||
private FacetsGenerator facetGen;
|
||||
|
||||
private XContentBuilder sort;
|
||||
|
||||
public ElasticsearchQueryGenerator() {
|
||||
this.from = 0;
|
||||
this.size = 10;
|
||||
this.model = new ElasticsearchQueryModel();
|
||||
this.filterGenerator = new ElasticsearchFilterGenerator(model);
|
||||
this.stack = new Stack<>();
|
||||
try {
|
||||
this.sourceGen = new SourceGenerator();
|
||||
this.queryGen = new QueryGenerator();
|
||||
this.filterGen = new FilterGenerator();
|
||||
this.facetGen = new FacetsGenerator();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public ElasticsearchQueryModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator setFrom(int from) {
|
||||
this.from = from;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator setSize(int size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator setSort(XContentBuilder sort) {
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator setBoostParams(String boostField, String modifier, Float factor, String boostMode) {
|
||||
this.boostField = boostField;
|
||||
this.modifier = modifier;
|
||||
this.factor = factor;
|
||||
this.boostMode = boostMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator filter(String filter) {
|
||||
CQLParser parser = new CQLParser(filter);
|
||||
parser.parse();
|
||||
parser.getCQLQuery().accept(filterGenerator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator andfilter(String filterKey, Collection<String> filterValues) {
|
||||
filterGenerator.addAndFilter(filterKey, filterValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator orfilter(String filterKey, Collection<String> filterValues) {
|
||||
filterGenerator.addOrFilter(filterKey, filterValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ElasticsearchQueryGenerator facet(String facetLimit, String facetSort) {
|
||||
try {
|
||||
facetGen.facet(facetLimit, facetSort);
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getQueryResult() {
|
||||
return queryGen.getResult().string();
|
||||
}
|
||||
|
||||
|
||||
public String getFacetResult() {
|
||||
try {
|
||||
return facetGen.getResult().string();
|
||||
} catch (IOException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSourceResult() {
|
||||
return sourceGen.getResult().string();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SortedQuery node) {
|
||||
try {
|
||||
if (node.getSortSpec() != null) {
|
||||
node.getSortSpec().accept(this);
|
||||
}
|
||||
queryGen.start();
|
||||
node.getQuery().accept(this);
|
||||
if (boostField != null) {
|
||||
queryGen.startBoost(boostField, modifier, factor, boostMode);
|
||||
}
|
||||
if (model.hasFilter()) {
|
||||
queryGen.startFiltered();
|
||||
} else if (filterGenerator.getResult().bytes().length() > 0) {
|
||||
queryGen.startFiltered();
|
||||
}
|
||||
Node querynode = stack.pop();
|
||||
if (querynode instanceof Token) {
|
||||
Token token = (Token) querynode;
|
||||
querynode = ".".equals(token.getString()) ?
|
||||
new Expression(Operator.MATCH_ALL) :
|
||||
new Expression(Operator.EQUALS, new Name("cql.allIndexes"), querynode);
|
||||
}
|
||||
queryGen.visit((Expression) querynode);
|
||||
if (model.hasFilter()) {
|
||||
queryGen.end();
|
||||
filterGen = new FilterGenerator(queryGen);
|
||||
filterGen.startFilter();
|
||||
filterGen.visit(model.getFilterExpression());
|
||||
filterGen.endFilter();
|
||||
queryGen.end();
|
||||
} else if (filterGenerator.getResult().bytes().length() > 0) {
|
||||
queryGen.end();
|
||||
queryGen.getResult().rawField("filter", filterGenerator.getResult().bytes().toBytes());
|
||||
queryGen.endFiltered();
|
||||
}
|
||||
if (boostField != null) {
|
||||
queryGen.endBoost();
|
||||
}
|
||||
if (model.hasFacets()) {
|
||||
facetGen = new FacetsGenerator();
|
||||
facetGen.visit(model.getFacetExpression());
|
||||
}
|
||||
queryGen.end();
|
||||
Expression sortnode = model.getSort();
|
||||
SortGenerator sortGen = new SortGenerator();
|
||||
if (sortnode != null) {
|
||||
sortGen.start();
|
||||
sortGen.visit(sortnode);
|
||||
sortGen.end();
|
||||
sort = sortGen.getResult();
|
||||
}
|
||||
sourceGen.build(queryGen, from, size, sort, facetGen.getResult());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException("unable to build a valid query from " + node + " , reason: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SortSpec node) {
|
||||
if (node.getSingleSpec() != null) {
|
||||
node.getSingleSpec().accept(this);
|
||||
}
|
||||
if (node.getSortSpec() != null) {
|
||||
node.getSortSpec().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SingleSpec node) {
|
||||
if (node.getIndex() != null) {
|
||||
node.getIndex().accept(this);
|
||||
}
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
if (!stack.isEmpty()) {
|
||||
model.setSort(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Query node) {
|
||||
for (PrefixAssignment assignment : node.getPrefixAssignments()) {
|
||||
assignment.accept(this);
|
||||
}
|
||||
if (node.getScopedClause() != null) {
|
||||
node.getScopedClause().accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PrefixAssignment node) {
|
||||
node.getPrefix().accept(this);
|
||||
node.getURI().accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ScopedClause node) {
|
||||
if (node.getScopedClause() != null) {
|
||||
node.getScopedClause().accept(this);
|
||||
}
|
||||
node.getSearchClause().accept(this);
|
||||
if (node.getBooleanGroup() != null) {
|
||||
node.getBooleanGroup().accept(this);
|
||||
}
|
||||
// format disjunctive or conjunctive filters
|
||||
if (node.getSearchClause().getIndex() != null
|
||||
&& model.isFilterContext(node.getSearchClause().getIndex().getContext())) {
|
||||
// assume that each operator-less filter is a conjunctive filter
|
||||
BooleanOperator op = node.getBooleanGroup() != null
|
||||
? node.getBooleanGroup().getOperator() : BooleanOperator.AND;
|
||||
String filtername = node.getSearchClause().getIndex().getName();
|
||||
Operator filterop = comparitorToES(node.getSearchClause().getRelation().getComparitor());
|
||||
Node filterterm = termToESwithoutWildCard(node.getSearchClause().getTerm());
|
||||
if (op == BooleanOperator.AND) {
|
||||
model.addConjunctiveFilter(filtername, filterterm, filterop);
|
||||
} else if (op == BooleanOperator.OR) {
|
||||
model.addDisjunctiveFilter(filtername, filterterm, filterop);
|
||||
}
|
||||
}
|
||||
// evaluate expression
|
||||
if (!stack.isEmpty() && stack.peek() instanceof Operator) {
|
||||
Operator op = (Operator) stack.pop();
|
||||
if (!stack.isEmpty()) {
|
||||
Node esnode = stack.pop();
|
||||
// add default context if node is a literal without a context
|
||||
if (esnode instanceof Token && TokenType.STRING.equals(esnode.getType())) {
|
||||
esnode = new Expression(Operator.EQUALS, new Name("cql.allIndexes"), esnode);
|
||||
}
|
||||
if (stack.isEmpty()) {
|
||||
// unary expression
|
||||
throw new IllegalArgumentException("unary expression not allowed, op=" + op + " node=" + esnode);
|
||||
} else {
|
||||
// binary expression
|
||||
Node esnode2 = stack.pop();
|
||||
// add default context if node is a literal without context
|
||||
if (esnode2 instanceof Token && TokenType.STRING.equals(esnode2.getType())) {
|
||||
esnode2 = new Expression(Operator.EQUALS, new Name("cql.allIndexes"), esnode2);
|
||||
}
|
||||
esnode = new Expression(op, esnode2, esnode);
|
||||
}
|
||||
stack.push(esnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SearchClause node) {
|
||||
if (node.getQuery() != null) {
|
||||
// CQL query in parenthesis
|
||||
node.getQuery().accept(this);
|
||||
}
|
||||
if (node.getTerm() != null) {
|
||||
node.getTerm().accept(this);
|
||||
}
|
||||
if (node.getIndex() != null) {
|
||||
node.getIndex().accept(this);
|
||||
String context = node.getIndex().getContext();
|
||||
// format facets
|
||||
if (model.isFacetContext(context)) {
|
||||
model.addFacet(node.getIndex().getName(), node.getTerm().getValue());
|
||||
}
|
||||
}
|
||||
if (node.getRelation() != null) {
|
||||
node.getRelation().accept(this);
|
||||
if (node.getRelation().getModifierList() != null && node.getIndex() != null) {
|
||||
// stack layout: op, list of modifiers, modifiable index
|
||||
Node op = stack.pop();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Node modifier = stack.pop();
|
||||
while (modifier instanceof Modifier) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append('.');
|
||||
}
|
||||
sb.append(modifier.toString());
|
||||
modifier = stack.pop();
|
||||
}
|
||||
String modifiable = sb.toString();
|
||||
stack.push(new Name(modifiable));
|
||||
stack.push(op);
|
||||
}
|
||||
}
|
||||
// evaluate expression
|
||||
if (!stack.isEmpty() && stack.peek() instanceof Operator) {
|
||||
Operator op = (Operator) stack.pop();
|
||||
Node arg1 = stack.pop();
|
||||
Node arg2 = stack.pop();
|
||||
// fold two expressions if they have the same operator
|
||||
boolean fold = arg1.isVisible() && arg2.isVisible()
|
||||
&& arg2 instanceof Expression
|
||||
&& ((Expression) arg2).getOperator().equals(op);
|
||||
Expression expression = fold ? new Expression((Expression) arg2, arg1) : new Expression(op, arg1, arg2);
|
||||
stack.push(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BooleanGroup node) {
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
stack.push(booleanToES(node.getOperator()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Relation node) {
|
||||
if (node.getModifierList() != null) {
|
||||
node.getModifierList().accept(this);
|
||||
}
|
||||
stack.push(comparitorToES(node.getComparitor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ModifierList node) {
|
||||
for (org.xbib.cql.Modifier modifier : node.getModifierList()) {
|
||||
modifier.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(org.xbib.cql.Modifier node) {
|
||||
Node term = null;
|
||||
if (node.getTerm() != null) {
|
||||
node.getTerm().accept(this);
|
||||
term = stack.pop();
|
||||
}
|
||||
node.getName().accept(this);
|
||||
Node name = stack.pop();
|
||||
stack.push(new Modifier(name, term));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Term node) {
|
||||
stack.push(termToES(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Identifier node) {
|
||||
stack.push(new Name(node.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Index node) {
|
||||
String context = node.getContext();
|
||||
String name = context != null ? context + "." + node.getName() : node.getName();
|
||||
Name esname = new Name(name, model.getVisibility(context));
|
||||
esname.setType(model.getElasticsearchType(name));
|
||||
stack.push(esname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SimpleName node) {
|
||||
stack.push(new Name(node.getName()));
|
||||
}
|
||||
|
||||
private Node termToES(Term node) {
|
||||
if (node.isLong()) {
|
||||
return new Token(Long.parseLong(node.getValue()));
|
||||
} else if (node.isFloat()) {
|
||||
return new Token(Double.parseDouble(node.getValue()));
|
||||
} else if (node.isIdentifier()) {
|
||||
return new Token(node.getValue());
|
||||
} else if (node.isDate()) {
|
||||
return new Token(DateUtil.parseDateISO(node.getValue()));
|
||||
} else if (node.isString()) {
|
||||
return new Token(node.getValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Node termToESwithoutWildCard(Term node) {
|
||||
return node.isString() || node.isIdentifier()
|
||||
? new Token(node.getValue().replaceAll("\\*", ""))
|
||||
: termToES(node);
|
||||
}
|
||||
|
||||
private Operator booleanToES(BooleanOperator bop) {
|
||||
Operator op;
|
||||
switch (bop) {
|
||||
case AND:
|
||||
op = Operator.AND;
|
||||
break;
|
||||
case OR:
|
||||
op = Operator.OR;
|
||||
break;
|
||||
case NOT:
|
||||
op = Operator.ANDNOT;
|
||||
break;
|
||||
case PROX:
|
||||
op = Operator.PROX;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown CQL operator: " + bop);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
private Operator comparitorToES(Comparitor op) {
|
||||
Operator esop;
|
||||
switch (op) {
|
||||
case EQUALS:
|
||||
esop = Operator.EQUALS;
|
||||
break;
|
||||
case GREATER:
|
||||
esop = Operator.RANGE_GREATER_THAN;
|
||||
break;
|
||||
case GREATER_EQUALS:
|
||||
esop = Operator.RANGE_GREATER_OR_EQUAL;
|
||||
break;
|
||||
case LESS:
|
||||
esop = Operator.RANGE_LESS_THAN;
|
||||
break;
|
||||
case LESS_EQUALS:
|
||||
esop = Operator.RANGE_LESS_OR_EQUALS;
|
||||
break;
|
||||
case NOT_EQUALS:
|
||||
esop = Operator.NOT_EQUALS;
|
||||
break;
|
||||
case WITHIN:
|
||||
esop = Operator.RANGE_WITHIN;
|
||||
break;
|
||||
case ADJ:
|
||||
esop = Operator.PHRASE;
|
||||
break;
|
||||
case ALL:
|
||||
esop = Operator.ALL;
|
||||
break;
|
||||
case ANY:
|
||||
esop = Operator.ANY;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown CQL comparitor: " + op);
|
||||
}
|
||||
return esop;
|
||||
}
|
||||
}
|
177
src/main/java/org/xbib/cql/elasticsearch/FacetsGenerator.java
Normal file
177
src/main/java/org/xbib/cql/elasticsearch/FacetsGenerator.java
Normal file
|
@ -0,0 +1,177 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import static org.xbib.content.json.JsonXContent.contentBuilder;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.cql.SyntaxException;
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Build facet from abstract syntax tree
|
||||
*/
|
||||
public class FacetsGenerator implements Visitor {
|
||||
|
||||
private int facetlength = 10;
|
||||
|
||||
private final XContentBuilder builder;
|
||||
|
||||
public FacetsGenerator() throws IOException {
|
||||
this.builder = contentBuilder();
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
builder.startObject();
|
||||
}
|
||||
|
||||
public void end() throws IOException {
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public void startFacets() throws IOException {
|
||||
builder.startObject("aggregations");
|
||||
}
|
||||
|
||||
public void endFacets() throws IOException {
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public XContentBuilder getResult() throws IOException {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Token node) {
|
||||
try {
|
||||
builder.value(node.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Name node) {
|
||||
try {
|
||||
builder.value(node.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Modifier node) {
|
||||
try {
|
||||
builder.value(node.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Operator node) {
|
||||
try {
|
||||
builder.value(node.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Expression node) {
|
||||
try {
|
||||
Operator op = node.getOperator();
|
||||
switch (op) {
|
||||
case TERMS_FACET: {
|
||||
builder.startObject().field("myfacet", "myvalue")
|
||||
.endObject();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"unable to translate operator while building elasticsearch facet: " + op);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException("internal error while building elasticsearch query", e);
|
||||
}
|
||||
}
|
||||
|
||||
public FacetsGenerator facet(String facetLimit, String facetSort) throws IOException {
|
||||
if (facetLimit == null) {
|
||||
return this;
|
||||
}
|
||||
Map<String, Integer> facetMap = parseFacet(facetLimit);
|
||||
String[] sortSpec = facetSort != null ? facetSort.split(",") : new String[]{"recordCount", "descending"};
|
||||
String order = "_count";
|
||||
String dir = "desc";
|
||||
for (String s : sortSpec) {
|
||||
switch (s) {
|
||||
case "recordCount":
|
||||
order = "_count";
|
||||
break;
|
||||
case "alphanumeric":
|
||||
order = "_term";
|
||||
break;
|
||||
case "ascending":
|
||||
dir = "asc";
|
||||
break;
|
||||
}
|
||||
}
|
||||
builder.startObject();
|
||||
|
||||
for (String index : facetMap.keySet()) {
|
||||
if ("*".equals(index)) {
|
||||
continue;
|
||||
}
|
||||
// TODO range aggregations etc.
|
||||
String facetType = "terms";
|
||||
Integer size = facetMap.get(index);
|
||||
builder.field(index)
|
||||
.startObject()
|
||||
.field(facetType).startObject()
|
||||
.field("field", index)
|
||||
.field("size", size > 0 ? size : 10)
|
||||
.startObject("order")
|
||||
.field(order, dir)
|
||||
.endObject()
|
||||
.endObject();
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
private Map<String, Integer> parseFacet(String spec) {
|
||||
Map<String, Integer> m = new HashMap<String, Integer>();
|
||||
m.put("*", facetlength);
|
||||
if (spec == null || spec.length() == 0) {
|
||||
return m;
|
||||
}
|
||||
String[] params = spec.split(",");
|
||||
for (String param : params) {
|
||||
int pos = param.indexOf(':');
|
||||
if (pos > 0) {
|
||||
int n = parseInt(param.substring(0, pos), facetlength);
|
||||
m.put(param.substring(pos + 1), n);
|
||||
} else if (param.length() > 0) {
|
||||
int n = parseInt(param, facetlength);
|
||||
m.put("*", n);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private int parseInt(String s, int defaultValue) {
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
338
src/main/java/org/xbib/cql/elasticsearch/FilterGenerator.java
Normal file
338
src/main/java/org/xbib/cql/elasticsearch/FilterGenerator.java
Normal file
|
@ -0,0 +1,338 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import static org.xbib.content.json.JsonXContent.contentBuilder;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.cql.SyntaxException;
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Node;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
import org.xbib.cql.util.QuotedStringTokenizer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Build query filter in Elasticsearch JSON syntax from abstract syntax tree
|
||||
*/
|
||||
public class FilterGenerator implements Visitor {
|
||||
|
||||
private XContentBuilder builder;
|
||||
|
||||
public FilterGenerator() throws IOException {
|
||||
this.builder = contentBuilder();
|
||||
}
|
||||
|
||||
public FilterGenerator(QueryGenerator queryGenerator) throws IOException {
|
||||
this.builder = queryGenerator.getResult();
|
||||
}
|
||||
|
||||
public FilterGenerator start() throws IOException {
|
||||
builder.startObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterGenerator end() throws IOException {
|
||||
builder.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterGenerator startFilter() throws IOException {
|
||||
builder.startObject("filter");
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterGenerator endFilter() throws IOException {
|
||||
builder.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
public XContentBuilder getResult() throws IOException {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Token node) {
|
||||
try {
|
||||
builder.value(node.getString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Name node) {
|
||||
try {
|
||||
builder.field(node.toString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Modifier node) {
|
||||
try {
|
||||
builder.value(node.toString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Operator node) {
|
||||
try {
|
||||
builder.value(node.toString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Expression node) {
|
||||
if (!node.isVisible()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Operator op = node.getOperator();
|
||||
switch (op.getArity()) {
|
||||
case 2: {
|
||||
Node arg1 = node.getArg1();
|
||||
Node arg2 = node.getArgs().length > 1 ? node.getArg2() : null;
|
||||
boolean visible = false;
|
||||
for (Node arg : node.getArgs()) {
|
||||
visible = visible || arg.isVisible();
|
||||
}
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
Token tok2 = arg2 instanceof Token ? (Token) arg2 : null;
|
||||
switch (op) {
|
||||
case EQUALS: {
|
||||
String field = arg1.toString();
|
||||
String value = tok2 != null ? tok2.getString() : "";
|
||||
builder.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term");
|
||||
builder.field(field, value).endObject();
|
||||
break;
|
||||
}
|
||||
case NOT_EQUALS: {
|
||||
String field = arg1.toString();
|
||||
String value = tok2 != null ? tok2.getString() : "";
|
||||
builder.startObject("not")
|
||||
.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term")
|
||||
.field(field, value)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case ALL: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
boolean phrase = arg2 instanceof Token && ((Token) arg2).isProtected();
|
||||
if (phrase) {
|
||||
builder.startArray("and");
|
||||
QuotedStringTokenizer qst = new QuotedStringTokenizer(value);
|
||||
while (qst.hasMoreTokens()) {
|
||||
builder.startObject().startObject("term").field(field, qst.nextToken()).endObject().endObject();
|
||||
}
|
||||
builder.endArray();
|
||||
} else {
|
||||
builder.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term")
|
||||
.field(field, value)
|
||||
.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ANY: {
|
||||
boolean phrase = arg2 instanceof Token && ((Token) arg2).isProtected();
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
if (phrase) {
|
||||
builder.startArray("or");
|
||||
QuotedStringTokenizer qst = new QuotedStringTokenizer(value);
|
||||
while (qst.hasMoreTokens()) {
|
||||
builder.startObject().startObject("term")
|
||||
.field(field, qst.nextToken()).endObject().endObject();
|
||||
}
|
||||
builder.endArray();
|
||||
} else {
|
||||
builder.startObject(tok2 != null && tok2.isBoundary() ? "prefix" : "term")
|
||||
.field(field, value)
|
||||
.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RANGE_GREATER_THAN: {
|
||||
String field = arg1.toString();
|
||||
String value = tok2 != null ? tok2.getString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("from", value)
|
||||
.field("include_lower", false)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_GREATER_OR_EQUAL: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("from", value)
|
||||
.field("include_lower", true)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_LESS_THAN: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("to", value)
|
||||
.field("include_upper", false)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_LESS_OR_EQUALS: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("to", value)
|
||||
.field("include_upper", true)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_WITHIN: {
|
||||
String field = arg1.toString();
|
||||
String value = tok2 != null ? tok2.getString() : "";
|
||||
String[] s = value.split(" ");
|
||||
builder.startObject("range").startObject(field).
|
||||
field("from", s[0])
|
||||
.field("to", s[1])
|
||||
.field("include_lower", true)
|
||||
.field("include_upper", true)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case AND: {
|
||||
if (arg2 == null) {
|
||||
if (arg1.isVisible()) {
|
||||
arg1.accept(this);
|
||||
}
|
||||
} else {
|
||||
builder.startObject("bool");
|
||||
builder.startArray("must");
|
||||
Node[] args = node.getArgs();
|
||||
for (int i = 0; i < node.getArgs().length; i++) {
|
||||
if (args[i].isVisible()) {
|
||||
builder.startObject();
|
||||
args[i].accept(this);
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OR: {
|
||||
if (arg2 == null) {
|
||||
if (arg1.isVisible()) {
|
||||
arg1.accept(this);
|
||||
}
|
||||
} else {
|
||||
builder.startObject("bool");
|
||||
builder.startArray("should");
|
||||
Node[] args = node.getArgs();
|
||||
for (int i = 0; i < node.getArgs().length; i++) {
|
||||
if (args[i].isVisible()) {
|
||||
builder.startObject();
|
||||
args[i].accept(this);
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OR_FILTER: {
|
||||
builder.startObject("bool");
|
||||
builder.startArray("should");
|
||||
Node[] args = node.getArgs();
|
||||
for (int i = 0; i < args.length; i += 2) {
|
||||
if (args[i].isVisible()) {
|
||||
builder.startObject().startObject("term");
|
||||
args[i].accept(this);
|
||||
args[i + 1].accept(this);
|
||||
builder.endObject().endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
break;
|
||||
}
|
||||
case AND_FILTER: {
|
||||
builder.startObject("bool");
|
||||
builder.startArray("must");
|
||||
Node[] args = node.getArgs();
|
||||
for (int i = 0; i < args.length; i += 2) {
|
||||
if (args[i].isVisible()) {
|
||||
builder.startObject().startObject("term");
|
||||
args[i].accept(this);
|
||||
args[i + 1].accept(this);
|
||||
builder.endObject().endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
break;
|
||||
}
|
||||
case ANDNOT: {
|
||||
if (arg2 == null) {
|
||||
if (arg1.isVisible()) {
|
||||
arg1.accept(this);
|
||||
}
|
||||
} else {
|
||||
builder.startObject("bool");
|
||||
builder.startArray("must_not");
|
||||
Node[] args = node.getArgs();
|
||||
for (int i = 0; i < node.getArgs().length; i++) {
|
||||
if (args[i].isVisible()) {
|
||||
builder.startObject();
|
||||
args[i].accept(this);
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROX: {
|
||||
String field = arg1.toString();
|
||||
// we assume a default of 10 words is enough for proximity
|
||||
String value = arg2 != null ? arg2.toString() + "~10" : "";
|
||||
builder.startObject("field").field(field, value).endObject();
|
||||
break;
|
||||
}
|
||||
case TERM_FILTER: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("term").field(field, value).endObject();
|
||||
break;
|
||||
}
|
||||
case QUERY_FILTER: {
|
||||
builder.startObject("query");
|
||||
arg1.accept(this);
|
||||
builder.endObject();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("unable to translate operator while building elasticsearch query filter: " + op);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException("internal error while building elasticsearch query filter", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
381
src/main/java/org/xbib/cql/elasticsearch/QueryGenerator.java
Normal file
381
src/main/java/org/xbib/cql/elasticsearch/QueryGenerator.java
Normal file
|
@ -0,0 +1,381 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import static org.xbib.content.json.JsonXContent.contentBuilder;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.cql.SyntaxException;
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Node;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Build Elasticsearch query from abstract syntax tree
|
||||
*/
|
||||
public class QueryGenerator implements Visitor {
|
||||
|
||||
private final XContentBuilder builder;
|
||||
|
||||
public QueryGenerator() throws IOException {
|
||||
this.builder = contentBuilder();
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
builder.startObject();
|
||||
}
|
||||
|
||||
public void end() throws IOException {
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public void startFiltered() throws IOException {
|
||||
builder.startObject("filtered").startObject("query");
|
||||
}
|
||||
|
||||
public void endFiltered() throws IOException {
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public void startBoost(String boostField, String modifier, Float factor, String boostMode) throws IOException {
|
||||
builder.startObject("function_score")
|
||||
.startObject("field_value_factor")
|
||||
.field("field", boostField)
|
||||
.field("modifier", modifier != null ? modifier : "log1p")
|
||||
.field("factor", factor != null ? factor : 1.0f)
|
||||
.endObject()
|
||||
.field("boost_mode", boostMode != null ? boostMode : "multiply")
|
||||
.startObject("query");
|
||||
}
|
||||
|
||||
public void endBoost() throws IOException {
|
||||
builder.endObject().endObject();
|
||||
}
|
||||
|
||||
public XContentBuilder getResult() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Token token) {
|
||||
try {
|
||||
switch (token.getType()) {
|
||||
case BOOL:
|
||||
builder.value(token.getBoolean());
|
||||
break;
|
||||
case INT:
|
||||
builder.value(token.getInteger());
|
||||
break;
|
||||
case FLOAT:
|
||||
builder.value(token.getFloat());
|
||||
break;
|
||||
case DATETIME:
|
||||
builder.value(token.getDate());
|
||||
break;
|
||||
case STRING:
|
||||
builder.value(token.getString());
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unknown token type: " + token);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Name node) {
|
||||
try {
|
||||
builder.field(node.toString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Modifier node) {
|
||||
try {
|
||||
builder.value(node.toString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Operator node) {
|
||||
try {
|
||||
builder.value(node.toString());
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Expression node) {
|
||||
if (!node.isVisible()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Operator op = node.getOperator();
|
||||
switch (op.getArity()) {
|
||||
case 0: {
|
||||
switch (op) {
|
||||
case MATCH_ALL: {
|
||||
builder.startObject("match_all").endObject();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// unary operators, anyone?
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// binary operators
|
||||
Node arg1 = node.getArg1();
|
||||
Node arg2 = node.getArgs().length > 1 ? node.getArg2() : null;
|
||||
Token tok2 = arg2 instanceof Token ? (Token) arg2 : null;
|
||||
boolean visible = false;
|
||||
for (Node arg : node.getArgs()) {
|
||||
visible = visible || arg.isVisible();
|
||||
}
|
||||
if (!visible) {
|
||||
return;
|
||||
}
|
||||
switch (op) {
|
||||
case EQUALS: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("simple_query_string")
|
||||
.field("query", value)
|
||||
.field("fields", new String[]{field})
|
||||
.field("analyze_wildcard", true)
|
||||
.field("default_operator", "and")
|
||||
.endObject();
|
||||
break;
|
||||
}
|
||||
case NOT_EQUALS: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("bool").startObject("must_not");
|
||||
builder.startObject("simple_query_string")
|
||||
.field("query", value)
|
||||
.field("fields", new String[]{field})
|
||||
.field("analyze_wildcard", true)
|
||||
.field("default_operator", "and")
|
||||
.endObject();
|
||||
builder.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case ALL: {
|
||||
String field = arg1.toString();
|
||||
String value = tok2 != null ? tok2.getString() : "";
|
||||
builder.startObject("simple_query_string")
|
||||
.field("query", value)
|
||||
.field("fields", new String[]{field})
|
||||
.field("analyze_wildcard", true)
|
||||
.field("default_operator", "and")
|
||||
.endObject();
|
||||
break;
|
||||
}
|
||||
case ANY: {
|
||||
String field = arg1.toString();
|
||||
String value = tok2 != null ? tok2.getString() : "";
|
||||
builder.startObject("simple_query_string")
|
||||
.field("query", value)
|
||||
.field("fields", new String[]{field})
|
||||
.field("analyze_wildcard", true)
|
||||
.field("default_operator", "or")
|
||||
.endObject();
|
||||
break;
|
||||
}
|
||||
case PHRASE: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
if (tok2 != null) {
|
||||
if (tok2.isProtected()) {
|
||||
builder.startObject("match_phrase")
|
||||
.startObject(field)
|
||||
.field("query", tok2.getString())
|
||||
.field("slop", 0)
|
||||
.endObject()
|
||||
.endObject();
|
||||
} else if (tok2.isAll()) {
|
||||
builder.startObject("match_all").endObject();
|
||||
} else if (tok2.isWildcard()) {
|
||||
builder.startObject("wildcard").field(field, value).endObject();
|
||||
} else if (tok2.isBoundary()) {
|
||||
builder.startObject("prefix").field(field, value).endObject();
|
||||
} else {
|
||||
builder.startObject("match_phrase")
|
||||
.startObject(field)
|
||||
.field("query", value)
|
||||
.field("slop", 0)
|
||||
.endObject()
|
||||
.endObject();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RANGE_GREATER_THAN: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("from", value)
|
||||
.field("include_lower", false)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_GREATER_OR_EQUAL: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("from", value)
|
||||
.field("include_lower", true)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_LESS_THAN: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("to", value)
|
||||
.field("include_upper", false)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_LESS_OR_EQUALS: {
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("to", value)
|
||||
.field("include_upper", true)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case RANGE_WITHIN: {
|
||||
// borders are inclusive
|
||||
String field = arg1.toString();
|
||||
String value = arg2 != null ? arg2.toString() : "";
|
||||
String from = null;
|
||||
String to = null;
|
||||
if (tok2 != null) {
|
||||
if (!tok2.isProtected()) {
|
||||
throw new IllegalArgumentException("range within: unable to derive range from a non-phrase: " + value);
|
||||
}
|
||||
if (tok2.getStringList().size() != 2) {
|
||||
throw new IllegalArgumentException("range within: unable to derive range from a phrase of length not equals to 2: " + tok2.getStringList());
|
||||
}
|
||||
from = tok2.getStringList().get(0);
|
||||
to = tok2.getStringList().get(1);
|
||||
}
|
||||
builder.startObject("range").startObject(field)
|
||||
.field("from", from)
|
||||
.field("to", to)
|
||||
.field("include_lower", true)
|
||||
.field("include_upper", true)
|
||||
.endObject().endObject();
|
||||
break;
|
||||
}
|
||||
case AND: {
|
||||
if (arg2 == null) {
|
||||
if (arg1.isVisible()) {
|
||||
arg1.accept(this);
|
||||
}
|
||||
} else {
|
||||
builder.startObject("bool");
|
||||
if (arg1.isVisible() && arg2.isVisible()) {
|
||||
builder.startArray("must").startObject();
|
||||
arg1.accept(this);
|
||||
builder.endObject().startObject();
|
||||
arg2.accept(this);
|
||||
builder.endObject().endArray();
|
||||
} else if (arg1.isVisible()) {
|
||||
builder.startObject("must");
|
||||
arg1.accept(this);
|
||||
builder.endObject();
|
||||
} else if (arg2.isVisible()) {
|
||||
builder.startObject("must");
|
||||
arg2.accept(this);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OR: {
|
||||
// short expression
|
||||
if (arg2 == null) {
|
||||
if (arg1.isVisible()) {
|
||||
arg1.accept(this);
|
||||
}
|
||||
} else {
|
||||
builder.startObject("bool");
|
||||
if (arg1.isVisible() && arg2.isVisible()) {
|
||||
builder.startArray("should").startObject();
|
||||
arg1.accept(this);
|
||||
builder.endObject().startObject();
|
||||
arg2.accept(this);
|
||||
builder.endObject().endArray();
|
||||
} else if (arg1.isVisible()) {
|
||||
builder.startObject("should");
|
||||
arg1.accept(this);
|
||||
builder.endObject();
|
||||
} else if (arg2.isVisible()) {
|
||||
builder.startObject("should");
|
||||
arg2.accept(this);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ANDNOT: {
|
||||
if (arg2 == null) {
|
||||
if (arg1.isVisible()) {
|
||||
arg1.accept(this);
|
||||
}
|
||||
} else {
|
||||
builder.startObject("bool");
|
||||
if (arg1.isVisible() && arg2.isVisible()) {
|
||||
builder.startArray("must_not").startObject();
|
||||
arg1.accept(this);
|
||||
builder.endObject().startObject();
|
||||
arg2.accept(this);
|
||||
builder.endObject().endArray();
|
||||
} else if (arg1.isVisible()) {
|
||||
builder.startObject("must_not");
|
||||
arg1.accept(this);
|
||||
builder.endObject();
|
||||
} else if (arg2.isVisible()) {
|
||||
builder.startObject("must_not");
|
||||
arg2.accept(this);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROX: {
|
||||
String field = arg1.toString();
|
||||
// we assume a default of 10 words is enough for proximity
|
||||
String value = arg2 != null ? arg2.toString() + "~10" : "";
|
||||
builder.startObject("field").field(field, value).endObject();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("unable to translate operator while building elasticsearch query: " + op);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException("internal error while building elasticsearch query", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
109
src/main/java/org/xbib/cql/elasticsearch/SortGenerator.java
Normal file
109
src/main/java/org/xbib/cql/elasticsearch/SortGenerator.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import static org.xbib.content.json.JsonXContent.contentBuilder;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
import org.xbib.cql.SyntaxException;
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Node;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Build sort in Elasticsearch JSON syntax from abstract syntax tree
|
||||
*/
|
||||
public class SortGenerator implements Visitor {
|
||||
|
||||
private final XContentBuilder builder;
|
||||
|
||||
private final Stack<Modifier> modifiers;
|
||||
|
||||
public SortGenerator() throws IOException {
|
||||
this.builder = contentBuilder();
|
||||
this.modifiers = new Stack<>();
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
builder.startArray();
|
||||
}
|
||||
|
||||
public void end() throws IOException {
|
||||
builder.endArray();
|
||||
}
|
||||
|
||||
public XContentBuilder getResult() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Token node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Name node) {
|
||||
try {
|
||||
if (modifiers.isEmpty()) {
|
||||
builder.startObject()
|
||||
.field(node.getName())
|
||||
.startObject()
|
||||
.field("unmapped_type", "string")
|
||||
.field("missing", "_last")
|
||||
.endObject()
|
||||
.endObject();
|
||||
} else {
|
||||
builder.startObject().field(node.getName()).startObject();
|
||||
while (!modifiers.isEmpty()) {
|
||||
Modifier mod = modifiers.pop();
|
||||
String s = mod.getName().toString();
|
||||
switch (s) {
|
||||
case "ascending":
|
||||
case "sort.ascending": {
|
||||
builder.field("order", "asc");
|
||||
break;
|
||||
}
|
||||
case "descending":
|
||||
case "sort.descending": {
|
||||
builder.field("order", "desc");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
builder.field(s, mod.getTerm());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.field("unmapped_type", "string");
|
||||
builder.field("missing", "_last");
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SyntaxException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Modifier node) {
|
||||
modifiers.push(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Operator node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Expression node) {
|
||||
Operator op = node.getOperator();
|
||||
if (op == Operator.SORT) {
|
||||
for (Node arg : node.getArgs()) {
|
||||
arg.accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import static org.xbib.content.json.JsonXContent.contentBuilder;
|
||||
|
||||
import org.xbib.content.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SourceGenerator {
|
||||
|
||||
private final XContentBuilder builder;
|
||||
|
||||
public SourceGenerator() throws IOException {
|
||||
this.builder = contentBuilder();
|
||||
}
|
||||
|
||||
public void build(QueryGenerator query,
|
||||
int from, int size) throws IOException {
|
||||
build(query, from, size, null, null);
|
||||
}
|
||||
|
||||
public void build(QueryGenerator query, int from, int size, XContentBuilder sort, XContentBuilder facets) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field("from", from);
|
||||
builder.field("size", size);
|
||||
builder.rawField("query", query.getResult().bytes().toBytes() );
|
||||
if (sort != null && sort.bytes().length() > 0) {
|
||||
builder.rawField("sort", sort.bytes().toBytes());
|
||||
}
|
||||
if (facets != null && facets.bytes().length() > 0) {
|
||||
builder.rawField("aggregations", facets.bytes().toBytes());
|
||||
}
|
||||
builder.endObject();
|
||||
builder.close();
|
||||
}
|
||||
|
||||
public XContentBuilder getResult() {
|
||||
return builder;
|
||||
}
|
||||
}
|
24
src/main/java/org/xbib/cql/elasticsearch/Visitor.java
Normal file
24
src/main/java/org/xbib/cql/elasticsearch/Visitor.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Modifier;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface Visitor {
|
||||
|
||||
void visit(Token node);
|
||||
|
||||
void visit(Name node);
|
||||
|
||||
void visit(Modifier node);
|
||||
|
||||
void visit(Operator node);
|
||||
|
||||
void visit(Expression node);
|
||||
|
||||
}
|
110
src/main/java/org/xbib/cql/elasticsearch/ast/Expression.java
Normal file
110
src/main/java/org/xbib/cql/elasticsearch/ast/Expression.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
import org.xbib.cql.elasticsearch.Visitor;
|
||||
|
||||
/**
|
||||
* Elasticsearch expression
|
||||
*/
|
||||
public class Expression implements Node {
|
||||
|
||||
private Operator op;
|
||||
|
||||
private Node[] args;
|
||||
|
||||
private TokenType type;
|
||||
|
||||
private boolean visible;
|
||||
|
||||
/**
|
||||
* Constructor for folding nodes.
|
||||
*
|
||||
* @param expr the expression
|
||||
* @param arg the new argument
|
||||
*/
|
||||
public Expression(Expression expr, Node arg) {
|
||||
this.type = TokenType.EXPRESSION;
|
||||
this.op = expr.getOperator();
|
||||
if (arg instanceof Expression) {
|
||||
Expression expr2 = (Expression) arg;
|
||||
this.args = new Node[expr.getArgs().length + expr2.getArgs().length];
|
||||
System.arraycopy(expr.getArgs(), 0, this.args, 0, expr.getArgs().length);
|
||||
System.arraycopy(expr2.getArgs(), 0, this.args, expr.getArgs().length, expr2.getArgs().length);
|
||||
} else {
|
||||
Node[] exprargs = expr.getArgs();
|
||||
this.args = new Node[exprargs.length + 1];
|
||||
// to avoid copy, organization of the argument list is reverse, the most recent arg is at position 0
|
||||
this.args[0] = arg;
|
||||
System.arraycopy(exprargs, 0, this.args, 1, exprargs.length);
|
||||
}
|
||||
this.visible = false;
|
||||
for (Node node : args) {
|
||||
if (node instanceof Name || node instanceof Expression) {
|
||||
this.visible = visible || arg.isVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Expression(Operator op, Node... args) {
|
||||
this.op = op;
|
||||
this.type = TokenType.EXPRESSION;
|
||||
this.args = args;
|
||||
if (args != null && args.length > 0) {
|
||||
this.visible = false;
|
||||
for (Node arg : args) {
|
||||
if (arg instanceof Name || arg instanceof Expression) {
|
||||
this.visible = visible || arg.isVisible();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Operator getOperator() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public Node[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public Node getArg1() {
|
||||
return args[0];
|
||||
}
|
||||
|
||||
public Node getArg2() {
|
||||
return args[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!visible) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(op.toString());
|
||||
sb.append('(');
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
sb.append(args[i]);
|
||||
if (i < args.length - 1) {
|
||||
sb.append(',');
|
||||
}
|
||||
}
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
49
src/main/java/org/xbib/cql/elasticsearch/ast/Modifier.java
Normal file
49
src/main/java/org/xbib/cql/elasticsearch/ast/Modifier.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
import org.xbib.cql.elasticsearch.Visitor;
|
||||
|
||||
/**
|
||||
* This is a modifier node for Elasticsearch query language
|
||||
*/
|
||||
public class Modifier implements Node {
|
||||
|
||||
private Node name;
|
||||
private Node term;
|
||||
|
||||
public Modifier(Node name, Node term) {
|
||||
this.name = name;
|
||||
this.term = term;
|
||||
}
|
||||
|
||||
public Modifier(Node name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Node getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Node getTerm() {
|
||||
return term;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenType getType() {
|
||||
return TokenType.OPERATOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + "=" + term;
|
||||
}
|
||||
}
|
52
src/main/java/org/xbib/cql/elasticsearch/ast/Name.java
Normal file
52
src/main/java/org/xbib/cql/elasticsearch/ast/Name.java
Normal file
|
@ -0,0 +1,52 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
import org.xbib.cql.elasticsearch.Visitor;
|
||||
|
||||
/**
|
||||
* A name for Elasticsearch fields
|
||||
*/
|
||||
public class Name implements Node {
|
||||
|
||||
private String name;
|
||||
|
||||
private TokenType type;
|
||||
|
||||
private boolean visible;
|
||||
|
||||
public Name(String name) {
|
||||
this(name, true);
|
||||
}
|
||||
|
||||
public Name(String name, boolean visible) {
|
||||
this.name = name;
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setType(TokenType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
16
src/main/java/org/xbib/cql/elasticsearch/ast/Node.java
Normal file
16
src/main/java/org/xbib/cql/elasticsearch/ast/Node.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
import org.xbib.cql.elasticsearch.Visitor;
|
||||
|
||||
/**
|
||||
* This node class is the base class for the Elasticsearch Query Lange abstract syntax tree
|
||||
*/
|
||||
public interface Node {
|
||||
|
||||
void accept(Visitor visitor);
|
||||
|
||||
boolean isVisible();
|
||||
|
||||
TokenType getType();
|
||||
|
||||
}
|
62
src/main/java/org/xbib/cql/elasticsearch/ast/Operator.java
Normal file
62
src/main/java/org/xbib/cql/elasticsearch/ast/Operator.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
import org.xbib.cql.elasticsearch.Visitor;
|
||||
|
||||
/**
|
||||
* Elasticsearch operators
|
||||
*/
|
||||
public enum Operator implements Node {
|
||||
EQUALS(2),
|
||||
NOT_EQUALS(2),
|
||||
RANGE_LESS_THAN(2),
|
||||
RANGE_LESS_OR_EQUALS(2),
|
||||
RANGE_GREATER_THAN(2),
|
||||
RANGE_GREATER_OR_EQUAL(2),
|
||||
RANGE_WITHIN(2),
|
||||
AND(2),
|
||||
ANDNOT(2),
|
||||
OR(2),
|
||||
PROX(2),
|
||||
ALL(2),
|
||||
ANY(2),
|
||||
PHRASE(2),
|
||||
TERM_FILTER(2),
|
||||
QUERY_FILTER(2),
|
||||
SORT(0),
|
||||
TERMS_FACET(0),
|
||||
OR_FILTER(2),
|
||||
AND_FILTER(2),
|
||||
MATCH_ALL(0);
|
||||
|
||||
|
||||
private final int arity;
|
||||
|
||||
Operator(int arity) {
|
||||
this.arity = arity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenType getType() {
|
||||
return TokenType.OPERATOR;
|
||||
}
|
||||
|
||||
public int getArity() {
|
||||
return arity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name();
|
||||
}
|
||||
|
||||
}
|
213
src/main/java/org/xbib/cql/elasticsearch/ast/Token.java
Normal file
213
src/main/java/org/xbib/cql/elasticsearch/ast/Token.java
Normal file
|
@ -0,0 +1,213 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import org.xbib.cql.elasticsearch.Visitor;
|
||||
import org.xbib.cql.util.DateUtil;
|
||||
import org.xbib.cql.util.QuotedStringTokenizer;
|
||||
import org.xbib.cql.util.UnterminatedQuotedStringException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* Elasticsearch query tokens.
|
||||
*/
|
||||
public class Token implements Node {
|
||||
|
||||
public enum TokenClass {
|
||||
|
||||
NORMAL, ALL, WILDCARD, BOUNDARY, PROTECTED
|
||||
}
|
||||
|
||||
private TokenType type;
|
||||
|
||||
private String value;
|
||||
|
||||
private String stringvalue;
|
||||
|
||||
private Boolean booleanvalue;
|
||||
|
||||
private Long longvalue;
|
||||
|
||||
private Double doublevalue;
|
||||
|
||||
private Date datevalue;
|
||||
|
||||
private List<String> values;
|
||||
|
||||
private final EnumSet<TokenClass> tokenClass;
|
||||
|
||||
public Token(String value) {
|
||||
this.value = value;
|
||||
this.tokenClass = EnumSet.of(TokenClass.NORMAL);
|
||||
this.type = TokenType.STRING;
|
||||
// if this string is equal to true/false or on/off or yes/no, convert silently to bool
|
||||
if (value.equals("true") || value.equals("yes") || value.equals("on")) {
|
||||
this.booleanvalue = true;
|
||||
this.value = null;
|
||||
this.type = TokenType.BOOL;
|
||||
|
||||
} else if (value.equals("false") || value.equals("no") || value.equals("off")) {
|
||||
this.booleanvalue = false;
|
||||
this.value = null;
|
||||
this.type = TokenType.BOOL;
|
||||
|
||||
}
|
||||
if (this.value != null) {
|
||||
// protected?
|
||||
if (value.startsWith("\"") && value.endsWith("\"")) {
|
||||
this.stringvalue = value;
|
||||
this.value = value.substring(1, value.length() - 1).replaceAll("\\\\\"", "\"");
|
||||
this.values = parseQuot(this.value);
|
||||
tokenClass.add(TokenClass.PROTECTED);
|
||||
}
|
||||
// wildcard?
|
||||
if (this.value.indexOf('*') >= 0 || this.value.indexOf('?') >= 0) {
|
||||
tokenClass.add(TokenClass.WILDCARD);
|
||||
// all?
|
||||
if (this.value.length() == 1) {
|
||||
tokenClass.add(TokenClass.ALL);
|
||||
}
|
||||
}
|
||||
// prefix?
|
||||
if (this.value.length() > 0 && this.value.charAt(0) == '^') {
|
||||
tokenClass.add(TokenClass.BOUNDARY);
|
||||
this.value = this.value.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Token(Boolean value) {
|
||||
this.booleanvalue = value;
|
||||
this.type = TokenType.BOOL;
|
||||
this.tokenClass = EnumSet.of(TokenClass.NORMAL);
|
||||
}
|
||||
|
||||
public Token(Long value) {
|
||||
this.longvalue = value;
|
||||
this.type = TokenType.INT;
|
||||
this.tokenClass = EnumSet.of(TokenClass.NORMAL);
|
||||
}
|
||||
|
||||
public Token(Double value) {
|
||||
this.doublevalue = value;
|
||||
this.type = TokenType.FLOAT;
|
||||
this.tokenClass = EnumSet.of(TokenClass.NORMAL);
|
||||
}
|
||||
|
||||
public Token(Date value) {
|
||||
this.datevalue = value;
|
||||
// this will enforce dates to get formatted as long values (years)
|
||||
this.longvalue = Long.parseLong(DateUtil.formatDate(datevalue, "yyyy"));
|
||||
this.type = TokenType.DATETIME;
|
||||
this.tokenClass = EnumSet.of(TokenClass.NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as toString(), but ignore stringvalue.
|
||||
*/
|
||||
public String getString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (booleanvalue != null) {
|
||||
sb.append(booleanvalue);
|
||||
} else if (longvalue != null) {
|
||||
sb.append(longvalue);
|
||||
} else if (doublevalue != null) {
|
||||
sb.append(doublevalue);
|
||||
} else if (datevalue != null) {
|
||||
sb.append(DateUtil.formatDateISO(datevalue));
|
||||
} else if (value != null) {
|
||||
sb.append(value);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Boolean getBoolean() {
|
||||
return booleanvalue;
|
||||
}
|
||||
|
||||
public Long getInteger() {
|
||||
return longvalue;
|
||||
}
|
||||
|
||||
public Double getFloat() {
|
||||
return doublevalue;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return datevalue;
|
||||
}
|
||||
|
||||
public List<String> getStringList() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (booleanvalue != null) {
|
||||
sb.append(booleanvalue);
|
||||
} else if (longvalue != null) {
|
||||
sb.append(longvalue);
|
||||
} else if (doublevalue != null) {
|
||||
sb.append(doublevalue);
|
||||
} else if (datevalue != null) {
|
||||
sb.append(DateUtil.formatDateISO(datevalue));
|
||||
} else if (stringvalue != null) {
|
||||
sb.append(stringvalue);
|
||||
} else if (value != null) {
|
||||
sb.append(value);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean isProtected() {
|
||||
return tokenClass.contains(TokenClass.PROTECTED);
|
||||
}
|
||||
|
||||
public boolean isBoundary() {
|
||||
return tokenClass.contains(TokenClass.BOUNDARY);
|
||||
}
|
||||
|
||||
public boolean isWildcard() {
|
||||
return tokenClass.contains(TokenClass.WILDCARD);
|
||||
}
|
||||
|
||||
public boolean isAll() {
|
||||
return tokenClass.contains(TokenClass.ALL);
|
||||
}
|
||||
|
||||
private List<String> parseQuot(String s) {
|
||||
try {
|
||||
QuotedStringTokenizer qst = new QuotedStringTokenizer(s, " \t\n\r\f", "\"", '\\', false);
|
||||
Iterable<String> iterable = () -> qst;
|
||||
Stream<String> stream = StreamSupport.stream(iterable.spliterator(), false);
|
||||
return stream.filter(str -> !word.matcher(str).matches()).collect(toList());
|
||||
} catch (UnterminatedQuotedStringException e) {
|
||||
return Collections.singletonList(s);
|
||||
}
|
||||
}
|
||||
|
||||
private final static Pattern word = Pattern.compile("[\\P{IsWord}]");
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.xbib.cql.elasticsearch.ast;
|
||||
|
||||
/**
|
||||
* Elasticsearch query language token types.
|
||||
*/
|
||||
public enum TokenType {
|
||||
|
||||
STRING, BOOL, INT, FLOAT, DATETIME, NAME, OPERATOR, EXPRESSION
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for abstract syntax tree construction for Elasticsearch query generation.
|
||||
*/
|
||||
package org.xbib.cql.elasticsearch.ast;
|
|
@ -0,0 +1,93 @@
|
|||
package org.xbib.cql.elasticsearch.model;
|
||||
|
||||
import org.xbib.cql.QueryFacet;
|
||||
|
||||
/**
|
||||
* Elasticsearch facet.
|
||||
*
|
||||
* @param <V> parameter type
|
||||
*/
|
||||
public final class ElasticsearchFacet<V> implements QueryFacet<V>, Comparable<ElasticsearchFacet<V>> {
|
||||
|
||||
public enum Type {
|
||||
TERMS,
|
||||
RANGE,
|
||||
HISTOGRAM,
|
||||
DATEHISTOGRAM,
|
||||
FILTER,
|
||||
QUERY,
|
||||
STATISTICAL,
|
||||
TERMS_STATS,
|
||||
GEO_DISTANCE
|
||||
}
|
||||
|
||||
public static int DEFAULT_FACET_SIZE = 10;
|
||||
|
||||
private Type type;
|
||||
|
||||
private String name;
|
||||
|
||||
private V value;
|
||||
|
||||
private int size;
|
||||
|
||||
public ElasticsearchFacet(Type type, String name, V value) {
|
||||
this(type, name, value, DEFAULT_FACET_SIZE);
|
||||
}
|
||||
|
||||
public ElasticsearchFacet(Type type, String name, V value, int size) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(V value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ElasticsearchFacet<V> o) {
|
||||
return name.compareTo(((ElasticsearchFacet) o).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "facet [name=" + name + ",value=" + value + ",size=" + size + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package org.xbib.cql.elasticsearch.model;
|
||||
|
||||
import org.xbib.cql.QueryFilter;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
|
||||
/**
|
||||
* Elasticsearch filter.
|
||||
* @param <V> parameter type
|
||||
*/
|
||||
public class ElasticsearchFilter<V> implements QueryFilter<V>, Comparable<ElasticsearchFilter<V>> {
|
||||
|
||||
private String name;
|
||||
|
||||
private V value;
|
||||
|
||||
private Operator op;
|
||||
|
||||
public ElasticsearchFilter(String name, V value, Operator op) {
|
||||
this.name = name;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setValue(V value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Operator getFilterOperation() {
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ElasticsearchFilter<V> o) {
|
||||
return toString().compareTo(o.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " " + op + " " + value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
package org.xbib.cql.elasticsearch.model;
|
||||
|
||||
import org.xbib.cql.elasticsearch.ast.Expression;
|
||||
import org.xbib.cql.elasticsearch.ast.Name;
|
||||
import org.xbib.cql.elasticsearch.ast.Node;
|
||||
import org.xbib.cql.elasticsearch.ast.Operator;
|
||||
import org.xbib.cql.elasticsearch.ast.Token;
|
||||
import org.xbib.cql.elasticsearch.ast.TokenType;
|
||||
import org.xbib.cql.model.CQLQueryModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* Elasticsearch query model.
|
||||
*/
|
||||
public final class ElasticsearchQueryModel {
|
||||
|
||||
private final Map<String, Expression> conjunctivefilters;
|
||||
|
||||
private final Map<String, Expression> disjunctivefilters;
|
||||
|
||||
private final Map<String, Expression> facets;
|
||||
|
||||
private Expression sortexpr;
|
||||
|
||||
public ElasticsearchQueryModel() {
|
||||
this.conjunctivefilters = new HashMap<>();
|
||||
this.disjunctivefilters = new HashMap<>();
|
||||
this.facets = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the key has a type. Default type is string.
|
||||
*
|
||||
* @param key the key to check
|
||||
* @return the type of the key
|
||||
*/
|
||||
public TokenType getElasticsearchType(String key) {
|
||||
if ("datetime".equals(key)) {
|
||||
return TokenType.DATETIME;
|
||||
}
|
||||
if ("int".equals(key)) {
|
||||
return TokenType.INT;
|
||||
}
|
||||
if ("long".equals(key)) {
|
||||
return TokenType.INT;
|
||||
}
|
||||
if ("float".equals(key)) {
|
||||
return TokenType.FLOAT;
|
||||
}
|
||||
return TokenType.STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expression visibility of a given context.
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if visible
|
||||
*/
|
||||
public boolean getVisibility(String context) {
|
||||
return !CQLQueryModel.isFacetContext(context)
|
||||
&& !CQLQueryModel.isFilterContext(context)
|
||||
&& !CQLQueryModel.isOptionContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this context is the facet context.
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if facet context
|
||||
*/
|
||||
public boolean isFacetContext(String context) {
|
||||
return CQLQueryModel.isFacetContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this context is the filter context.
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if filter context
|
||||
*/
|
||||
public boolean isFilterContext(String context) {
|
||||
return CQLQueryModel.isFilterContext(context);
|
||||
}
|
||||
|
||||
|
||||
public boolean hasFacets() {
|
||||
return !facets.isEmpty();
|
||||
}
|
||||
|
||||
public void addFacet(String key, String value) {
|
||||
ElasticsearchFacet<Node> facet = new ElasticsearchFacet<Node>(ElasticsearchFacet.Type.TERMS, key, new Name(value));
|
||||
facets.put(facet.getName(), new Expression(Operator.TERMS_FACET, facet.getValue()));
|
||||
}
|
||||
|
||||
public Expression getFacetExpression() {
|
||||
return new Expression(Operator.TERMS_FACET, facets.values().toArray(new Node[facets.size()]));
|
||||
}
|
||||
|
||||
public void addConjunctiveFilter(String name, Node value, Operator op) {
|
||||
addFilter(conjunctivefilters, new ElasticsearchFilter<>(name, value, op));
|
||||
}
|
||||
|
||||
public void addDisjunctiveFilter(String name, Node value, Operator op) {
|
||||
addFilter(disjunctivefilters, new ElasticsearchFilter<>(name, value, op));
|
||||
}
|
||||
|
||||
public boolean hasFilter() {
|
||||
return !conjunctivefilters.isEmpty() || !disjunctivefilters.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filter expression.
|
||||
* Only one filter expression is allowed per query.
|
||||
* First, build conjunctive and disjunctive filter terms.
|
||||
* If both are null, there is no filter at all.
|
||||
* Otherwise, combine conjunctive and disjunctive filter terms with a
|
||||
* disjunction, and apply filter function, and return this expression.
|
||||
*
|
||||
* @return a single filter expression or null if there are no filter terms
|
||||
*/
|
||||
public Expression getFilterExpression() {
|
||||
if (!hasFilter()) {
|
||||
return null;
|
||||
}
|
||||
Expression conjunctiveclause = null;
|
||||
if (!conjunctivefilters.isEmpty()) {
|
||||
conjunctiveclause = new Expression(Operator.AND,
|
||||
conjunctivefilters.values().toArray(new Node[conjunctivefilters.size()]));
|
||||
}
|
||||
Expression disjunctiveclause = null;
|
||||
if (!disjunctivefilters.isEmpty()) {
|
||||
disjunctiveclause = new Expression(Operator.OR,
|
||||
disjunctivefilters.values().toArray(new Node[disjunctivefilters.size()]));
|
||||
}
|
||||
if (conjunctiveclause != null && disjunctiveclause == null) {
|
||||
return conjunctiveclause;
|
||||
} else if (conjunctiveclause == null && disjunctiveclause != null) {
|
||||
return disjunctiveclause;
|
||||
} else {
|
||||
return new Expression(Operator.OR, conjunctiveclause, disjunctiveclause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add sort expression.
|
||||
*
|
||||
* @param indexAndModifier the index with modifiers
|
||||
*/
|
||||
public void setSort(Stack<Node> indexAndModifier) {
|
||||
this.sortexpr = new Expression(Operator.SORT, reverse(indexAndModifier).toArray(new Node[indexAndModifier.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sort expression.
|
||||
*
|
||||
* @return the sort expression
|
||||
*/
|
||||
public Expression getSort() {
|
||||
return sortexpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to add a filter.
|
||||
*
|
||||
* @param filters the filter list
|
||||
* @param filter the filter to add
|
||||
*/
|
||||
private void addFilter(Map<String, Expression> filters, ElasticsearchFilter<Node> filter) {
|
||||
Name name = new Name(filter.getName());
|
||||
name.setType(getElasticsearchType(filter.getName()));
|
||||
Node value = filter.getValue();
|
||||
if (value instanceof Token) {
|
||||
value = new Expression(filter.getFilterOperation(), name, value);
|
||||
}
|
||||
if (filters.containsKey(filter.getName())) {
|
||||
Expression expression = filters.get(filter.getName());
|
||||
expression = new Expression(expression, value);
|
||||
filters.put(filter.getName(), expression);
|
||||
} else {
|
||||
filters.put(filter.getName(), (Expression) value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to reverse an expression stack.
|
||||
*
|
||||
* @param in the stack to reverse
|
||||
* @return the reversed stack
|
||||
*/
|
||||
private Stack<Node> reverse(Stack<Node> in) {
|
||||
Stack<Node> out = new Stack<Node>();
|
||||
while (!in.empty()) {
|
||||
out.push(in.pop());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for Elasticsearch query model.
|
||||
*/
|
||||
package org.xbib.cql.elasticsearch.model;
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for compiling CQL to Elasticsearch queries.
|
||||
*/
|
||||
package org.xbib.cql.elasticsearch;
|
230
src/main/java/org/xbib/cql/model/CQLQueryModel.java
Normal file
230
src/main/java/org/xbib/cql/model/CQLQueryModel.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
package org.xbib.cql.model;
|
||||
|
||||
import org.xbib.cql.AbstractNode;
|
||||
import org.xbib.cql.BooleanOperator;
|
||||
import org.xbib.cql.Term;
|
||||
import org.xbib.cql.model.breadcrumb.FacetBreadcrumbTrail;
|
||||
import org.xbib.cql.model.breadcrumb.FilterBreadcrumbTrail;
|
||||
import org.xbib.cql.model.breadcrumb.OptionBreadcrumbTrail;
|
||||
|
||||
/**
|
||||
* A CQL query model.
|
||||
* Special contexts are <code>facet</code>, <code>filter</code>,
|
||||
* and <code>option</code>.
|
||||
* These contexts form breadcrumb trails.
|
||||
* Bread crumbs provide a means for a server to track an chronologically
|
||||
* ordered set of client actions. Bread crumbs are typically rendered as a
|
||||
* user-driven constructed list of links, and are useful when
|
||||
* users select them to drill down and up in a structure,
|
||||
* so that they can find their way and have a notion of where they
|
||||
* currently are.
|
||||
* Bread crumbs in the original sense just represent where users are
|
||||
* situated in a site hierarchy. For example, when browsing a
|
||||
* library catalog, bread crumbs could look like this:
|
||||
* <pre>
|
||||
* Home > Scientific literature > Arts & Human > Philosophy
|
||||
* </pre>
|
||||
* or
|
||||
* <pre>
|
||||
* Main library > Branch library > First floor > Rare book room
|
||||
* </pre>
|
||||
* These items would be rendered as links to the corresponding location.
|
||||
* Classes that implement this interface are responsible for managing
|
||||
* such a bread crumb structure. A typical implementation regards
|
||||
* bread crumbs as a set of elements.
|
||||
* When a bread crumb is activated that was not in the set yet,
|
||||
* it would add it to the set, or when a bread crumb is activated
|
||||
* that is already on the set, it would roll back to the corresponding depth.
|
||||
* In this model, multiple bread crumb trails may exist side by side. They are
|
||||
* separate and do not depend on each other. There is a list of bread crumb
|
||||
* trails, and the notion of a currently active bread crumb within a trail.
|
||||
* This model does not make any presumptions on how it should interact with
|
||||
* breadcrumbs except that a breadcrumb model should be serializable into
|
||||
* a writer.
|
||||
*/
|
||||
public final class CQLQueryModel {
|
||||
|
||||
/**
|
||||
* Contexts 'facet', 'filter', and 'option'.
|
||||
*/
|
||||
public static final String FACET_INDEX_NAME = "facet";
|
||||
|
||||
public static final String FILTER_INDEX_NAME = "filter";
|
||||
|
||||
public static final String OPTION_INDEX_NAME = "option";
|
||||
|
||||
private static final String AND_OP = " and ";
|
||||
|
||||
private static final String OR_OP = " or ";
|
||||
|
||||
/**
|
||||
* the CQL query string.
|
||||
*/
|
||||
private String query;
|
||||
|
||||
/**
|
||||
* breadcrumb trail for facets.
|
||||
*/
|
||||
private FacetBreadcrumbTrail facetTrail;
|
||||
|
||||
/**
|
||||
* breadcrumb trail for conjunctive filters.
|
||||
*/
|
||||
private FilterBreadcrumbTrail conjunctivefilterTrail;
|
||||
|
||||
/**
|
||||
* breadcrumb trail for disjunctive filters.
|
||||
*/
|
||||
private FilterBreadcrumbTrail disjunctivefilterTrail;
|
||||
|
||||
/**
|
||||
* breadcrumb trail for options.
|
||||
*/
|
||||
private OptionBreadcrumbTrail optionTrail;
|
||||
|
||||
public CQLQueryModel() {
|
||||
this.facetTrail = new FacetBreadcrumbTrail();
|
||||
this.conjunctivefilterTrail = new FilterBreadcrumbTrail(BooleanOperator.AND);
|
||||
this.disjunctivefilterTrail = new FilterBreadcrumbTrail(BooleanOperator.OR);
|
||||
this.optionTrail = new OptionBreadcrumbTrail();
|
||||
}
|
||||
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void addFacet(Facet<Term> facet) {
|
||||
facetTrail.add(facet);
|
||||
}
|
||||
|
||||
public void removeFacet(Facet<Term> facet) {
|
||||
facetTrail.remove(facet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add CQL filter.
|
||||
*
|
||||
* @param op boolean operator, AND for conjunctive filter, OR for disjunctive filter
|
||||
* @param filter the filter to add
|
||||
*/
|
||||
public void addFilter(BooleanOperator op, Filter<AbstractNode> filter) {
|
||||
if (op == BooleanOperator.AND && !disjunctivefilterTrail.contains(filter)) {
|
||||
conjunctivefilterTrail.add(filter);
|
||||
}
|
||||
if (op == BooleanOperator.OR && !conjunctivefilterTrail.contains(filter)) {
|
||||
disjunctivefilterTrail.add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove CQL filter.
|
||||
*
|
||||
* @param filter the filter to remove
|
||||
*/
|
||||
public void removeFilter(Filter<AbstractNode> filter) {
|
||||
conjunctivefilterTrail.remove(filter);
|
||||
disjunctivefilterTrail.remove(filter);
|
||||
}
|
||||
|
||||
public void addOption(Option<?> option) {
|
||||
optionTrail.add(option);
|
||||
}
|
||||
|
||||
public void removeOption(Option<?> option) {
|
||||
optionTrail.remove(option);
|
||||
}
|
||||
|
||||
public FacetBreadcrumbTrail getFacetTrail() {
|
||||
return facetTrail;
|
||||
}
|
||||
|
||||
public String getFilterTrail() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!conjunctivefilterTrail.isEmpty()) {
|
||||
sb.append(AND_OP).append(conjunctivefilterTrail.toString());
|
||||
}
|
||||
if (disjunctivefilterTrail.size() == 1) {
|
||||
sb.append(OR_OP).append(disjunctivefilterTrail.toString());
|
||||
} else if (disjunctivefilterTrail.size() > 1) {
|
||||
sb.append(AND_OP).append(disjunctivefilterTrail.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option breadcrumb trail.
|
||||
*
|
||||
* @return the option breadcrumb trail
|
||||
*/
|
||||
public OptionBreadcrumbTrail getOptionTrail() {
|
||||
return optionTrail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query of a given context.
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if visible, false if not
|
||||
*/
|
||||
public static boolean isVisible(String context) {
|
||||
return !isFacetContext(context)
|
||||
&& !isFilterContext(context)
|
||||
&& !isOptionContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this context is the facet context.
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if facet contet
|
||||
*/
|
||||
public static boolean isFacetContext(String context) {
|
||||
return FACET_INDEX_NAME.equals(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this context is the filter context.
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if filter context
|
||||
*/
|
||||
public static boolean isFilterContext(String context) {
|
||||
return FILTER_INDEX_NAME.equals(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this context is the option context
|
||||
*
|
||||
* @param context the context
|
||||
* @return true if option context
|
||||
*/
|
||||
public static boolean isOptionContext(String context) {
|
||||
return OPTION_INDEX_NAME.equals(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the CQL query model as CQL string.
|
||||
*
|
||||
* @return the query model as CQL
|
||||
*/
|
||||
public String toCQL() {
|
||||
StringBuilder sb = new StringBuilder(query);
|
||||
String facets = getFacetTrail().toCQL();
|
||||
if (facets.length() > 0) {
|
||||
sb.append(AND_OP).append(facets);
|
||||
}
|
||||
String filters = getFilterTrail();
|
||||
if (filters.length() > 0) {
|
||||
sb.append(filters);
|
||||
}
|
||||
String options = getOptionTrail().toCQL();
|
||||
if (options.length() > 0) {
|
||||
sb.append(AND_OP).append(options);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
70
src/main/java/org/xbib/cql/model/Facet.java
Normal file
70
src/main/java/org/xbib/cql/model/Facet.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
package org.xbib.cql.model;
|
||||
|
||||
import org.xbib.cql.QueryFacet;
|
||||
|
||||
/**
|
||||
* Facet.
|
||||
*
|
||||
* @param <V> parameter type
|
||||
*/
|
||||
public final class Facet<V> implements QueryFacet<V>, Comparable<Facet<V>> {
|
||||
|
||||
private int size;
|
||||
private String filterName;
|
||||
private String name;
|
||||
private V value;
|
||||
|
||||
public Facet(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Facet(String name, String filterName, int size) {
|
||||
this.name = name;
|
||||
this.filterName = filterName;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(V value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterName() {
|
||||
return filterName;
|
||||
}
|
||||
|
||||
public String toCQL() {
|
||||
return CQLQueryModel.FACET_INDEX_NAME + "." + name + " = " + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Facet<V> o) {
|
||||
return name.compareTo((o).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toCQL();
|
||||
}
|
||||
}
|
68
src/main/java/org/xbib/cql/model/Filter.java
Normal file
68
src/main/java/org/xbib/cql/model/Filter.java
Normal file
|
@ -0,0 +1,68 @@
|
|||
package org.xbib.cql.model;
|
||||
|
||||
import org.xbib.cql.QueryFilter;
|
||||
import org.xbib.cql.Comparitor;
|
||||
|
||||
/**
|
||||
* Filter.
|
||||
* @param <V> filter parameter type
|
||||
*/
|
||||
public class Filter<V> implements QueryFilter<V>, Comparable<Filter<V>> {
|
||||
|
||||
private String name;
|
||||
private V value;
|
||||
private Comparitor op;
|
||||
private String label;
|
||||
|
||||
public Filter(String name, V value, Comparitor op) {
|
||||
this.name = name;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Filter(String name, V value, Comparitor op, String label) {
|
||||
this.name = name;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setValue(V value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Comparitor getFilterOperation() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String toCQL() {
|
||||
return CQLQueryModel.FILTER_INDEX_NAME + "." + name + " " + op.getToken() + " " + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Filter<V> o) {
|
||||
return toString().compareTo((o).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " " + op + " " + value;
|
||||
}
|
||||
|
||||
}
|
48
src/main/java/org/xbib/cql/model/Option.java
Normal file
48
src/main/java/org/xbib/cql/model/Option.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
package org.xbib.cql.model;
|
||||
|
||||
import org.xbib.cql.QueryOption;
|
||||
|
||||
/**
|
||||
* Option.
|
||||
* @param <V> parameter type
|
||||
*/
|
||||
public class Option<V> implements QueryOption<V>, Comparable<Option<V>> {
|
||||
|
||||
private String name;
|
||||
private V value;
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(V value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String toCQL() {
|
||||
return CQLQueryModel.OPTION_INDEX_NAME + "." + name + " = " + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Option<V> o) {
|
||||
return name.compareTo((o).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toCQL();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.xbib.cql.model.breadcrumb;
|
||||
|
||||
import org.xbib.cql.model.Facet;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Facet breadcrumb trail.
|
||||
*/
|
||||
public class FacetBreadcrumbTrail extends TreeSet<Facet> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toCQL();
|
||||
}
|
||||
|
||||
public String toCQL() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isEmpty()) {
|
||||
return sb.toString();
|
||||
}
|
||||
Iterator<Facet> it = iterator();
|
||||
if (it.hasNext()) {
|
||||
sb.append(it.next().toCQL());
|
||||
}
|
||||
while (it.hasNext()) {
|
||||
sb.append(" and ").append(it.next().toCQL());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package org.xbib.cql.model.breadcrumb;
|
||||
|
||||
import org.xbib.cql.BooleanOperator;
|
||||
import org.xbib.cql.model.Filter;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Filter breadcrumbs.
|
||||
*/
|
||||
public class FilterBreadcrumbTrail extends TreeSet<Filter> {
|
||||
|
||||
private BooleanOperator op;
|
||||
|
||||
public FilterBreadcrumbTrail(BooleanOperator op) {
|
||||
super();
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toCQL();
|
||||
}
|
||||
|
||||
public String toCQL() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isEmpty()) {
|
||||
return sb.toString();
|
||||
}
|
||||
if (op == BooleanOperator.OR && size() > 1) {
|
||||
sb.append('(');
|
||||
}
|
||||
Iterator<Filter> it = this.iterator();
|
||||
sb.append(it.next().toCQL());
|
||||
while (it.hasNext()) {
|
||||
sb.append(' ').append(op).append(' ').append(it.next().toCQL());
|
||||
}
|
||||
if (op == BooleanOperator.OR && size() > 1) {
|
||||
sb.append(')');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package org.xbib.cql.model.breadcrumb;
|
||||
|
||||
import org.xbib.cql.model.Option;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* An Option breadcrumb trail is a trail of attributes (key/value pairs).
|
||||
* There is no interdependency between attributes; all values are allowed,
|
||||
* even if they interfere with each other, the trail does not resolve it.
|
||||
*/
|
||||
public class OptionBreadcrumbTrail extends TreeSet<Option> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toCQL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Conjunct all CQL options to form a valid CQL string.
|
||||
*
|
||||
* @return the CQL string
|
||||
*/
|
||||
public String toCQL() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isEmpty()) {
|
||||
return sb.toString();
|
||||
}
|
||||
Iterator<Option> it = iterator();
|
||||
if (it.hasNext()) {
|
||||
sb.append(it.next().toCQL());
|
||||
}
|
||||
while (it.hasNext()) {
|
||||
sb.append(" and ").append(it.next().toCQL());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for breadcrumbs in the CQL model.
|
||||
*/
|
||||
package org.xbib.cql.model.breadcrumb;
|
4
src/main/java/org/xbib/cql/model/package-info.java
Normal file
4
src/main/java/org/xbib/cql/model/package-info.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for CQL query modeling.
|
||||
*/
|
||||
package org.xbib.cql.model;
|
4
src/main/java/org/xbib/cql/package-info.java
Normal file
4
src/main/java/org/xbib/cql/package-info.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for CQL queries.
|
||||
*/
|
||||
package org.xbib.cql;
|
316
src/main/java/org/xbib/cql/util/DateUtil.java
Normal file
316
src/main/java/org/xbib/cql/util/DateUtil.java
Normal file
|
@ -0,0 +1,316 @@
|
|||
package org.xbib.cql.util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DateUtil {
|
||||
|
||||
public static final String ISO_FORMAT_SECONDS = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
public static final String ISO_FORMAT_DAYS = "yyyy-MM-dd";
|
||||
|
||||
public static final String RFC_FORMAT = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";
|
||||
|
||||
public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
private static final Calendar cal = Calendar.getInstance();
|
||||
|
||||
private static final SimpleDateFormat sdf = new SimpleDateFormat();
|
||||
|
||||
/**
|
||||
* Number of milliseconds in a standard second.
|
||||
*/
|
||||
public static final long MILLIS_PER_SECOND = 1000;
|
||||
/**
|
||||
* Number of milliseconds in a standard minute.
|
||||
*/
|
||||
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
|
||||
/**
|
||||
* Number of milliseconds in a standard hour.
|
||||
*/
|
||||
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
|
||||
/**
|
||||
* Number of milliseconds in a standard day.
|
||||
*/
|
||||
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
|
||||
/**
|
||||
* the date masks
|
||||
*/
|
||||
private static final String[] DATE_MASKS = {"yyyy-MM-dd'T'HH:mm:ssz", "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd",
|
||||
"yyyy"};
|
||||
|
||||
public static String formatNow() {
|
||||
return formatDateISO(new Date());
|
||||
}
|
||||
|
||||
public static String formatDate(Date date, String format) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (sdf) {
|
||||
sdf.applyPattern(format);
|
||||
sdf.setTimeZone(GMT);
|
||||
return sdf.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatDateISO(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (sdf) {
|
||||
sdf.applyPattern(ISO_FORMAT_SECONDS);
|
||||
sdf.setTimeZone(GMT);
|
||||
return sdf.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
public static Date parseDateISO(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (sdf) {
|
||||
sdf.applyPattern(ISO_FORMAT_SECONDS);
|
||||
sdf.setTimeZone(GMT);
|
||||
sdf.setLenient(true);
|
||||
try {
|
||||
return sdf.parse(value);
|
||||
} catch (ParseException pe) {
|
||||
// skip
|
||||
}
|
||||
sdf.applyPattern(ISO_FORMAT_DAYS);
|
||||
try {
|
||||
return sdf.parse(value);
|
||||
} catch (ParseException pe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Date parseDateISO(String value, Date defaultDate) {
|
||||
if (value == null) {
|
||||
return defaultDate;
|
||||
}
|
||||
synchronized (sdf) {
|
||||
sdf.applyPattern(ISO_FORMAT_SECONDS);
|
||||
sdf.setTimeZone(GMT);
|
||||
sdf.setLenient(true);
|
||||
try {
|
||||
return sdf.parse(value);
|
||||
} catch (ParseException pe) {
|
||||
// skip
|
||||
}
|
||||
sdf.applyPattern(ISO_FORMAT_DAYS);
|
||||
try {
|
||||
return sdf.parse(value);
|
||||
} catch (ParseException pe) {
|
||||
return defaultDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatDateRFC(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
synchronized (sdf) {
|
||||
sdf.applyPattern(RFC_FORMAT);
|
||||
sdf.setTimeZone(GMT);
|
||||
return sdf.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
public static Date parseDateRFC(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
synchronized (sdf) {
|
||||
sdf.applyPattern(RFC_FORMAT);
|
||||
sdf.setTimeZone(GMT);
|
||||
return sdf.parse(value);
|
||||
}
|
||||
} catch (ParseException pe) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getYear() {
|
||||
synchronized (cal) {
|
||||
cal.setTime(new Date());
|
||||
return cal.get(Calendar.YEAR);
|
||||
}
|
||||
}
|
||||
|
||||
public static String today() {
|
||||
synchronized (cal) {
|
||||
cal.setTime(new Date());
|
||||
return String.format("%04d%02d%02d",
|
||||
cal.get(Calendar.YEAR),
|
||||
cal.get(Calendar.MONTH) + 1,
|
||||
cal.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
}
|
||||
|
||||
public static int getYear(Date date) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
return cal.get(Calendar.YEAR);
|
||||
}
|
||||
}
|
||||
|
||||
public static Date midnight() {
|
||||
return DateUtil.midnight(new Date());
|
||||
}
|
||||
|
||||
public static Date midnight(Date date) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date min() {
|
||||
return new Date(0L);
|
||||
}
|
||||
|
||||
public static Date now() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
public static Date yesterday() {
|
||||
return yesterday(new Date());
|
||||
}
|
||||
|
||||
public static Date yesterday(Date date) {
|
||||
return days(date, -1);
|
||||
}
|
||||
|
||||
public static Date tomorrow() {
|
||||
return tomorrow(new Date());
|
||||
}
|
||||
|
||||
public static Date tomorrow(Date date) {
|
||||
return days(date, 1);
|
||||
}
|
||||
|
||||
public static Date years(int years) {
|
||||
return years(new Date(), years);
|
||||
}
|
||||
|
||||
public static Date years(Date date, int years) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.YEAR, years);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date months(int months) {
|
||||
return months(new Date(), months);
|
||||
}
|
||||
|
||||
public static Date months(Date date, int months) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.MONTH, months);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date weeks(int weeks) {
|
||||
return weeks(new Date(), weeks);
|
||||
}
|
||||
|
||||
public static Date weeks(Date date, int weeks) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.WEEK_OF_YEAR, weeks);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date days(int days) {
|
||||
return days(new Date(), days);
|
||||
}
|
||||
|
||||
public static Date days(Date date, int days) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.DAY_OF_YEAR, days);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date hours(int hours) {
|
||||
return hours(new Date(), hours);
|
||||
}
|
||||
|
||||
public static Date hours(Date date, int hours) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.HOUR_OF_DAY, hours);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date minutes(int minutes) {
|
||||
return minutes(new Date(), minutes);
|
||||
}
|
||||
|
||||
public static Date minutes(Date date, int minutes) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.MINUTE, minutes);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date seconds(int seconds) {
|
||||
return seconds(new Date(), seconds);
|
||||
}
|
||||
|
||||
public static Date seconds(Date date, int seconds) {
|
||||
synchronized (cal) {
|
||||
cal.setTime(date);
|
||||
cal.add(Calendar.MINUTE, seconds);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date parseDate(Object o) {
|
||||
synchronized (sdf) {
|
||||
sdf.setTimeZone(GMT);
|
||||
sdf.setLenient(true);
|
||||
if (o instanceof Date) {
|
||||
return (Date) o;
|
||||
} else if (o instanceof Long) {
|
||||
Long longvalue = (Long) o;
|
||||
String s = Long.toString(longvalue);
|
||||
sdf.applyPattern(DATE_MASKS[3]);
|
||||
Date d = sdf.parse(s, new ParsePosition(0));
|
||||
if (d != null) {
|
||||
return d;
|
||||
}
|
||||
} else if (o instanceof String) {
|
||||
String value = (String) o;
|
||||
for (String DATE_MASK : DATE_MASKS) {
|
||||
sdf.applyPattern(DATE_MASK);
|
||||
Date d = sdf.parse(value, new ParsePosition(0));
|
||||
if (d != null) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
363
src/main/java/org/xbib/cql/util/QueryStringDecoder.java
Normal file
363
src/main/java/org/xbib/cql/util/QueryStringDecoder.java
Normal file
|
@ -0,0 +1,363 @@
|
|||
package org.xbib.cql.util;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Splits an HTTP query string into a path string and key-value parameter pairs.
|
||||
* This decoder is for one time use only. Create a new instance for each URI:
|
||||
* <pre>
|
||||
* {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("/hello?recipient=world&x=1;y=2");
|
||||
* assert decoder.getPath().equals("/hello");
|
||||
* assert decoder.getParameters().get("recipient").get(0).equals("world");
|
||||
* assert decoder.getParameters().get("x").get(0).equals("1");
|
||||
* assert decoder.getParameters().get("y").get(0).equals("2");
|
||||
* </pre>
|
||||
* This decoder can also decode the content of an HTTP POST request whose
|
||||
* content type is <tt>application/x-www-form-urlencoded</tt>:
|
||||
* <pre>
|
||||
* {@link QueryStringDecoder} decoder = new {@link QueryStringDecoder}("recipient=world&x=1;y=2", false);
|
||||
* </pre>
|
||||
* <h3>HashDOS vulnerability fix</h3>
|
||||
* As a workaround to the <a href="http://netty.io/s/hashdos">HashDOS</a> vulnerability, the decoder
|
||||
* limits the maximum number of decoded key-value parameter pairs, up to {@literal 1024} by
|
||||
* default, and you can configure it when you construct the decoder by passing an additional
|
||||
* integer parameter.
|
||||
*/
|
||||
public class QueryStringDecoder {
|
||||
|
||||
private static final int DEFAULT_MAX_PARAMS = 1024;
|
||||
|
||||
private final Charset charset;
|
||||
private final String uri;
|
||||
private final boolean hasPath;
|
||||
private final int maxParams;
|
||||
private String path;
|
||||
private Map<String, List<String>> params;
|
||||
private int nParams;
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI. The decoder will
|
||||
* assume that the query string is encoded in UTF-8.
|
||||
*/
|
||||
public QueryStringDecoder(String uri) {
|
||||
this(uri, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(String uri, boolean hasPath) {
|
||||
this(uri, StandardCharsets.UTF_8, hasPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(String uri, Charset charset) {
|
||||
this(uri, charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(String uri, Charset charset, boolean hasPath) {
|
||||
this(uri, charset, hasPath, DEFAULT_MAX_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(String uri, Charset charset, boolean hasPath, int maxParams) {
|
||||
if (uri == null) {
|
||||
throw new NullPointerException("getUri");
|
||||
}
|
||||
if (charset == null) {
|
||||
throw new NullPointerException("charset");
|
||||
}
|
||||
if (maxParams <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"maxParams: " + maxParams + " (expected: a positive integer)");
|
||||
}
|
||||
|
||||
this.uri = uri;
|
||||
this.charset = charset;
|
||||
this.maxParams = maxParams;
|
||||
this.hasPath = hasPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI. The decoder will
|
||||
* assume that the query string is encoded in UTF-8.
|
||||
*/
|
||||
public QueryStringDecoder(URI uri) {
|
||||
this(uri, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(URI uri, Charset charset) {
|
||||
this(uri, charset, DEFAULT_MAX_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(URI uri, Charset charset, int maxParams) {
|
||||
if (uri == null) {
|
||||
throw new NullPointerException("getUri");
|
||||
}
|
||||
if (charset == null) {
|
||||
throw new NullPointerException("charset");
|
||||
}
|
||||
if (maxParams <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"maxParams: " + maxParams + " (expected: a positive integer)");
|
||||
}
|
||||
|
||||
String rawPath = uri.getRawPath();
|
||||
if (rawPath != null) {
|
||||
hasPath = true;
|
||||
} else {
|
||||
rawPath = "";
|
||||
hasPath = false;
|
||||
}
|
||||
// Also take care of cut of things like "http://localhost"
|
||||
this.uri = rawPath + '?' + uri.getRawQuery();
|
||||
|
||||
this.charset = charset;
|
||||
this.maxParams = maxParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri used to initialize this {@link QueryStringDecoder}.
|
||||
*/
|
||||
public String uri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decoded path string of the URI.
|
||||
*/
|
||||
public String path() {
|
||||
if (path == null) {
|
||||
if (!hasPath) {
|
||||
return path = "";
|
||||
}
|
||||
|
||||
int pathEndPos = uri.indexOf('?');
|
||||
if (pathEndPos < 0) {
|
||||
path = uri;
|
||||
} else {
|
||||
return path = uri.substring(0, pathEndPos);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decoded key-value parameter pairs of the URI.
|
||||
*/
|
||||
public Map<String, List<String>> parameters() {
|
||||
if (params == null) {
|
||||
if (hasPath) {
|
||||
int pathLength = path().length();
|
||||
if (uri.length() == pathLength) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
decodeParams(uri.substring(pathLength + 1));
|
||||
} else {
|
||||
if (uri.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
decodeParams(uri);
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private void decodeParams(String s) {
|
||||
Map<String, List<String>> params = this.params = new LinkedHashMap<String, List<String>>();
|
||||
nParams = 0;
|
||||
String name = null;
|
||||
int pos = 0; // Beginning of the unprocessed region
|
||||
int i; // End of the unprocessed region
|
||||
char c; // Current character
|
||||
for (i = 0; i < s.length(); i++) {
|
||||
c = s.charAt(i);
|
||||
if (c == '=' && name == null) {
|
||||
if (pos != i) {
|
||||
name = decodeComponent(s.substring(pos, i), charset);
|
||||
}
|
||||
pos = i + 1;
|
||||
// http://www.w3.org/TR/html401/appendix/notes.html#h-B.2.2
|
||||
} else if (c == '&' || c == ';') {
|
||||
if (name == null && pos != i) {
|
||||
// We haven't seen an `=' so far but moved forward.
|
||||
// Must be a param of the form '&a&' so add it with
|
||||
// an empty value.
|
||||
if (!addParam(params, decodeComponent(s.substring(pos, i), charset), "")) {
|
||||
return;
|
||||
}
|
||||
} else if (name != null) {
|
||||
if (!addParam(params, name, decodeComponent(s.substring(pos, i), charset))) {
|
||||
return;
|
||||
}
|
||||
name = null;
|
||||
}
|
||||
pos = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != i) { // Are there characters we haven't dealt with?
|
||||
if (name == null) { // Yes and we haven't seen any `='.
|
||||
addParam(params, decodeComponent(s.substring(pos, i), charset), "");
|
||||
} else { // Yes and this must be the last value.
|
||||
addParam(params, name, decodeComponent(s.substring(pos, i), charset));
|
||||
}
|
||||
} else if (name != null) { // Have we seen a name without value?
|
||||
addParam(params, name, "");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean addParam(Map<String, List<String>> params, String name, String value) {
|
||||
if (nParams >= maxParams) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> values = params.get(name);
|
||||
if (values == null) {
|
||||
values = new ArrayList<String>(1); // Often there's only 1 value.
|
||||
params.put(name, values);
|
||||
}
|
||||
values.add(value);
|
||||
nParams++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a bit of an URL encoded by a browser.
|
||||
* This is equivalent to calling {@link #decodeComponent(String, Charset)}
|
||||
* with the UTF-8 charset (recommended to comply with RFC 3986, Section 2).
|
||||
*
|
||||
* @param s The string to decode (can be empty).
|
||||
* @return The decoded string, or {@code s} if there's nothing to decode.
|
||||
* If the string to decode is {@code null}, returns an empty string.
|
||||
* @throws IllegalArgumentException if the string contains a malformed
|
||||
* escape sequence.
|
||||
*/
|
||||
public static String decodeComponent(final String s) {
|
||||
return decodeComponent(s, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a bit of an URL encoded by a browser.
|
||||
* The string is expected to be encoded as per RFC 3986, Section 2.
|
||||
* This is the encoding used by JavaScript functions {@code encodeURI}
|
||||
* and {@code encodeURIComponent}, but not {@code escape}. For example
|
||||
* in this encoding, é (in Unicode {@code U+00E9} or in UTF-8
|
||||
* {@code 0xC3 0xA9}) is encoded as {@code %C3%A9} or {@code %c3%a9}.
|
||||
* This is essentially equivalent to calling
|
||||
* {@link URLDecoder#decode(String, String) URLDecoder.decode(s, charset.name())}
|
||||
* except that it's over 2x faster and generates less garbage for the GC.
|
||||
* Actually this function doesn't allocate any memory if there's nothing
|
||||
* to decode, the argument itself is returned.
|
||||
*
|
||||
* @param s The string to decode (can be empty).
|
||||
* @param charset The charset to use to decode the string (should really
|
||||
* be UTF-8).
|
||||
* @return The decoded string, or {@code s} if there's nothing to decode.
|
||||
* If the string to decode is {@code null}, returns an empty string.
|
||||
* @throws IllegalArgumentException if the string contains a malformed
|
||||
* escape sequence.
|
||||
*/
|
||||
public static String decodeComponent(final String s, final Charset charset) {
|
||||
if (s == null) {
|
||||
return "";
|
||||
}
|
||||
final int size = s.length();
|
||||
boolean modified = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final char c = s.charAt(i);
|
||||
if (c == '%' || c == '+') {
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!modified) {
|
||||
return s;
|
||||
}
|
||||
final byte[] buf = new byte[size];
|
||||
int pos = 0; // position in `buf'.
|
||||
for (int i = 0; i < size; i++) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '+':
|
||||
buf[pos++] = ' '; // "+" -> " "
|
||||
break;
|
||||
case '%':
|
||||
if (i == size - 1) {
|
||||
throw new IllegalArgumentException("unterminated escape"
|
||||
+ " sequence at end of string: " + s);
|
||||
}
|
||||
c = s.charAt(++i);
|
||||
if (c == '%') {
|
||||
buf[pos++] = '%'; // "%%" -> "%"
|
||||
break;
|
||||
}
|
||||
if (i == size - 1) {
|
||||
throw new IllegalArgumentException("partial escape"
|
||||
+ " sequence at end of string: " + s);
|
||||
}
|
||||
c = decodeHexNibble(c);
|
||||
final char c2 = decodeHexNibble(s.charAt(++i));
|
||||
if (c == Character.MAX_VALUE || c2 == Character.MAX_VALUE) {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid escape sequence `%" + s.charAt(i - 1)
|
||||
+ s.charAt(i) + "' at index " + (i - 2)
|
||||
+ " of: " + s);
|
||||
}
|
||||
c = (char) (c * 16 + c2);
|
||||
// Fall through.
|
||||
default:
|
||||
buf[pos++] = (byte) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new String(buf, 0, pos, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to decode half of a hexadecimal number from a string.
|
||||
*
|
||||
* @param c The ASCII character of the hexadecimal number to decode.
|
||||
* Must be in the range {@code [0-9a-fA-F]}.
|
||||
* @return The hexadecimal value represented in the ASCII character
|
||||
* given, or {@link Character#MAX_VALUE} if the character is invalid.
|
||||
*/
|
||||
private static char decodeHexNibble(final char c) {
|
||||
if ('0' <= c && c <= '9') {
|
||||
return (char) (c - '0');
|
||||
} else if ('a' <= c && c <= 'f') {
|
||||
return (char) (c - 'a' + 10);
|
||||
} else if ('A' <= c && c <= 'F') {
|
||||
return (char) (c - 'A' + 10);
|
||||
} else {
|
||||
return Character.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
233
src/main/java/org/xbib/cql/util/QuotedStringTokenizer.java
Normal file
233
src/main/java/org/xbib/cql/util/QuotedStringTokenizer.java
Normal file
|
@ -0,0 +1,233 @@
|
|||
package org.xbib.cql.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* A string tokenizer that understands quotes and escape characters.
|
||||
*/
|
||||
public class QuotedStringTokenizer extends StringTokenizer implements Iterator<String> {
|
||||
|
||||
private String str;
|
||||
|
||||
private String delim;
|
||||
|
||||
private String quotes;
|
||||
|
||||
private char escape;
|
||||
|
||||
private boolean returnDelims;
|
||||
|
||||
private int pos;
|
||||
|
||||
private int len;
|
||||
|
||||
private StringBuilder token;
|
||||
|
||||
/**
|
||||
* Constructs a string tokenizer for the specified string.
|
||||
* The default delimiters for StringTokenizer are used.
|
||||
* "\"\'" are used as quotes, and '\\' is used as the escape character.
|
||||
*/
|
||||
public QuotedStringTokenizer(String str) {
|
||||
this(str, " \t\n\r\f", "\"\'", '\\', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a string tokenizer for the specified string.
|
||||
* "\"\'" are used as quotes, and '\\' is used as the escape character.
|
||||
*/
|
||||
public QuotedStringTokenizer(String str, String delim) {
|
||||
this(str, delim, "\"\'", '\\', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a string tokenizer for the specified string.
|
||||
* Quotes cannot be delimiters, and the escape character can be neither a
|
||||
* quote nor a delimiter.
|
||||
*/
|
||||
public QuotedStringTokenizer(String str, String delim, String quotes, char escape, boolean returnDelims) {
|
||||
super(str, delim, returnDelims);
|
||||
this.str = str;
|
||||
this.len = str.length();
|
||||
this.delim = delim;
|
||||
this.quotes = quotes;
|
||||
this.pos = 0;
|
||||
for (int i = 0; i < quotes.length(); i++) {
|
||||
if (delim.indexOf(quotes.charAt(i)) >= 0) {
|
||||
throw new IllegalArgumentException("Invalid quote character '" + quotes.charAt(i) + "'");
|
||||
}
|
||||
}
|
||||
this.escape = escape;
|
||||
if (delim.indexOf(escape) >= 0) {
|
||||
throw new IllegalArgumentException("Invalid escape character '" + escape + "'");
|
||||
}
|
||||
if (quotes.indexOf(escape) >= 0) {
|
||||
throw new IllegalArgumentException("Invalid escape character '" + escape + "'");
|
||||
}
|
||||
this.returnDelims = returnDelims;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the next non-delimiter character.
|
||||
* Pre-condition: not inside a quoted string (token).
|
||||
*/
|
||||
private int skipDelim(int pos) {
|
||||
int p = pos;
|
||||
while (p < len && delim.indexOf(str.charAt(p)) >= 0) {
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the next delimiter character after the token.
|
||||
* If collect is true, collects the token into the StringBuffer.
|
||||
* Pre-condition: not on a delimiter.
|
||||
*/
|
||||
private int skipToken(int pos, boolean collect) {
|
||||
int p = pos;
|
||||
if (collect) {
|
||||
token = new StringBuilder();
|
||||
}
|
||||
boolean quoted = false;
|
||||
char quote = '\000';
|
||||
boolean escaped = false;
|
||||
for (; p < len; p++) {
|
||||
char curr = str.charAt(p);
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
if (collect) {
|
||||
token.append(curr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr == escape) { // escape character
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
if (quoted) {
|
||||
if (curr == quote) { // closing quote
|
||||
quoted = false;
|
||||
quote = '\000';
|
||||
} else if (collect) {
|
||||
token.append(curr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (quotes.indexOf(curr) >= 0) {
|
||||
// opening quote
|
||||
quoted = true;
|
||||
quote = curr;
|
||||
continue;
|
||||
}
|
||||
if (delim.indexOf(str.charAt(p)) >= 0) {
|
||||
// unquoted delimiter
|
||||
break;
|
||||
}
|
||||
if (collect) {
|
||||
token.append(curr);
|
||||
}
|
||||
}
|
||||
if (escaped || quoted) {
|
||||
throw new UnterminatedQuotedStringException(str);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if there are more tokens available from this tokenizer's string.
|
||||
* Pre-condition: not inside a quoted string (token).
|
||||
*/
|
||||
@Override
|
||||
public boolean hasMoreTokens() {
|
||||
if (!returnDelims) {
|
||||
pos = skipDelim(pos);
|
||||
}
|
||||
return (pos < len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next token from this string tokenizer.
|
||||
*/
|
||||
@Override
|
||||
public String nextToken() {
|
||||
if (!returnDelims) {
|
||||
pos = skipDelim(pos);
|
||||
}
|
||||
if (pos >= len) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if (returnDelims && delim.indexOf(str.charAt(pos)) >= 0) {
|
||||
return str.substring(pos, ++pos);
|
||||
}
|
||||
pos = skipToken(pos, true);
|
||||
return token.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next token in this string tokenizer's string.
|
||||
*/
|
||||
@Override
|
||||
public String nextToken(String delim) {
|
||||
this.delim = delim;
|
||||
return nextToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of times that this tokenizer's nextToken method
|
||||
* can be called before it generates an exception.
|
||||
*/
|
||||
@Override
|
||||
public int countTokens() {
|
||||
int count = 0;
|
||||
int dcount = 0;
|
||||
int curr = pos;
|
||||
while (curr < len) {
|
||||
if (delim.indexOf(str.charAt(curr)) >= 0) {
|
||||
curr++;
|
||||
dcount++;
|
||||
} else {
|
||||
curr = skipToken(curr, false);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (returnDelims) {
|
||||
return count + dcount;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same value as the hasMoreTokens method.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return hasMoreTokens();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same value as the nextToken method, except that its declared
|
||||
* return value is Object rather than String.
|
||||
*/
|
||||
@Override
|
||||
public Object nextElement() {
|
||||
return nextToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return hasMoreTokens();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
return nextToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.xbib.cql.util;
|
||||
|
||||
/**
|
||||
* Exception for string tokenizing.
|
||||
*/
|
||||
public class UnterminatedQuotedStringException extends RuntimeException {
|
||||
|
||||
public UnterminatedQuotedStringException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
4
src/main/java/org/xbib/cql/util/package-info.java
Normal file
4
src/main/java/org/xbib/cql/util/package-info.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for CQL utilities.
|
||||
*/
|
||||
package org.xbib.cql.util;
|
194
src/main/jflex/org/xbib/cql/CQL.jflex
Normal file
194
src/main/jflex/org/xbib/cql/CQL.jflex
Normal file
|
@ -0,0 +1,194 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
%%
|
||||
%class CQLLexer
|
||||
%implements CQLTokens
|
||||
%unicode
|
||||
%integer
|
||||
%eofval{
|
||||
return 0;
|
||||
%eofval}
|
||||
%line
|
||||
%column
|
||||
|
||||
%{
|
||||
private Object yylval;
|
||||
private int token;
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
public int getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public int nextToken() {
|
||||
try {
|
||||
token = yylex();
|
||||
return token;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getSemantic() {
|
||||
return yylval;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return yyline;
|
||||
}
|
||||
|
||||
public int getColumn() {
|
||||
return yycolumn;
|
||||
}
|
||||
|
||||
%}
|
||||
NL = \n | \r | \r\n
|
||||
LPAR = "("
|
||||
RPAR = ")"
|
||||
AND = [aA][nN][dD]
|
||||
OR = [oO][rR]
|
||||
NOT = [nN][oO][tT]
|
||||
PROX = [pP][rR][oO][xX]
|
||||
SORTBY = [sS][oO][rR][tT][bB][yY]
|
||||
SIMPLESTRING = [^ \t\"()=<>\/]+
|
||||
QUOTEDSTRING = [^\"]
|
||||
LT = "<"
|
||||
GT = ">"
|
||||
EQ = "="
|
||||
GE = ">="
|
||||
LE = "<="
|
||||
NE = "<>"
|
||||
EXACT = "=="
|
||||
NAMEDCOMPARITORS = [cC][qQ][lL] "." [eE][xX][aA][cC][tT] | [eE][xX][aA][cC][tT] | [cC][qQ][lL] "." [wW][iI][tT][hH][iI][nN] | [wW][iI][tT][hH][iI][nN] | [cC][qQ][lL] "." [aA][dD][jJ] | [aA][dD][jJ] | [cC][qQ][lL] "." [aA][lL][lL] | [aA][lL][lL] | [cC][qQ][lL] "." [aA][nN][yY] | [aA][nN][yY] | [cC][qQ][lL] "." [eE][nN][cC][lL][oO][sS][eE][sS] | [eE][nN][cC][lL][oO][sS][eE][sS]
|
||||
INTEGER = 0 | [1-9][0-9]*
|
||||
FLOAT = [0-9]+ "." [0-9]+
|
||||
SLASH = "/"
|
||||
|
||||
%state STRING2
|
||||
|
||||
%%
|
||||
|
||||
<YYINITIAL>\" {
|
||||
yybegin(STRING2);
|
||||
sb.setLength(0);
|
||||
}
|
||||
|
||||
<STRING2> {
|
||||
|
||||
\\\" {
|
||||
sb.append("\"");
|
||||
}
|
||||
{QUOTEDSTRING} {
|
||||
sb.append(yytext());
|
||||
}
|
||||
\" {
|
||||
yybegin(YYINITIAL);
|
||||
yylval = sb.toString();
|
||||
return QUOTEDSTRING;
|
||||
}
|
||||
}
|
||||
|
||||
<YYINITIAL>{NL} {
|
||||
return NL;
|
||||
}
|
||||
|
||||
<YYINITIAL>" "|\t {
|
||||
}
|
||||
|
||||
<YYINITIAL>{FLOAT} {
|
||||
yylval = Double.parseDouble(yytext());
|
||||
return FLOAT;
|
||||
}
|
||||
|
||||
<YYINITIAL>{INTEGER} {
|
||||
yylval = Long.parseLong(yytext());
|
||||
return INTEGER;
|
||||
}
|
||||
|
||||
<YYINITIAL>{NAMEDCOMPARITORS} {
|
||||
yylval = yytext();
|
||||
return NAMEDCOMPARITORS;
|
||||
}
|
||||
|
||||
<YYINITIAL>{GE} {
|
||||
yylval = yytext();
|
||||
return GE;
|
||||
}
|
||||
|
||||
<YYINITIAL>{LE} {
|
||||
yylval = yytext();
|
||||
return LE;
|
||||
}
|
||||
|
||||
<YYINITIAL>{NE} {
|
||||
yylval = yytext();
|
||||
return NE;
|
||||
}
|
||||
|
||||
<YYINITIAL>{EXACT} {
|
||||
yylval = yytext();
|
||||
return EXACT;
|
||||
}
|
||||
|
||||
<YYINITIAL>{GT} {
|
||||
yylval = yytext();
|
||||
return GT;
|
||||
}
|
||||
|
||||
<YYINITIAL>{LT} {
|
||||
yylval = yytext();
|
||||
return LT;
|
||||
}
|
||||
|
||||
<YYINITIAL>{EQ} {
|
||||
yylval = yytext();
|
||||
return EQ;
|
||||
}
|
||||
|
||||
<YYINITIAL>{AND} {
|
||||
yylval = yytext();
|
||||
return AND;
|
||||
}
|
||||
|
||||
<YYINITIAL>{OR} {
|
||||
yylval = yytext();
|
||||
return OR;
|
||||
}
|
||||
|
||||
<YYINITIAL>{NOT} {
|
||||
yylval = yytext();
|
||||
return NOT;
|
||||
}
|
||||
|
||||
<YYINITIAL>{PROX} {
|
||||
yylval = yytext();
|
||||
return PROX;
|
||||
}
|
||||
|
||||
<YYINITIAL>{SORTBY} {
|
||||
yylval = yytext();
|
||||
return SORTBY;
|
||||
}
|
||||
|
||||
<YYINITIAL>{SIMPLESTRING} {
|
||||
yylval = yytext();
|
||||
return SIMPLESTRING;
|
||||
}
|
||||
|
||||
<YYINITIAL>{LPAR} {
|
||||
yylval = yytext();
|
||||
return LPAR;
|
||||
}
|
||||
|
||||
<YYINITIAL>{RPAR} {
|
||||
yylval = yytext();
|
||||
return RPAR;
|
||||
}
|
||||
|
||||
<YYINITIAL>{SLASH} {
|
||||
yylval = yytext();
|
||||
return SLASH;
|
||||
}
|
60
src/test/java/org/xbib/cql/QueryTest.java
Normal file
60
src/test/java/org/xbib/cql/QueryTest.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
package org.xbib.cql;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class QueryTest extends Assert {
|
||||
|
||||
@Test
|
||||
public void testValidQueries() throws IOException {
|
||||
test("queries.txt");
|
||||
}
|
||||
|
||||
private void test(String path) throws IOException {
|
||||
int count = 0;
|
||||
int ok = 0;
|
||||
int errors = 0;
|
||||
LineNumberReader lr = new LineNumberReader(new InputStreamReader(getClass().getResourceAsStream(path),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = lr.readLine()) != null) {
|
||||
if (line.trim().length() > 0 && !line.startsWith("#")) {
|
||||
try {
|
||||
int pos = line.indexOf('|');
|
||||
if (pos > 0) {
|
||||
validate(line.substring(0, pos), line.substring(pos + 1));
|
||||
} else {
|
||||
validate(line);
|
||||
}
|
||||
ok++;
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
lr.close();
|
||||
assertEquals(errors, 0);
|
||||
assertEquals(ok, count);
|
||||
}
|
||||
|
||||
private void validate(String line) throws Exception {
|
||||
CQLParser parser = new CQLParser(line);
|
||||
parser.parse();
|
||||
assertEquals(line, parser.getCQLQuery().toString());
|
||||
}
|
||||
|
||||
private void validate(String line, String expected) throws Exception {
|
||||
CQLParser parser = new CQLParser(line);
|
||||
parser.parse();
|
||||
assertEquals(expected, parser.getCQLQuery().toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package org.xbib.cql.elasticsearch;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.xbib.cql.CQLParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.LineNumberReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ElasticsearchQueryTest extends Assert {
|
||||
|
||||
@Test
|
||||
public void testValidQueries() throws IOException {
|
||||
test("queries.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleTermFilter() throws Exception {
|
||||
String cql = "Jörg";
|
||||
CQLParser parser = new CQLParser(cql);
|
||||
parser.parse();
|
||||
ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator();
|
||||
parser.getCQLQuery().accept(generator);
|
||||
String json = generator.getResult().string();
|
||||
//logger.info("{} --> {}", cql, json);
|
||||
assertEquals(json, "{\"term\":{\"cql.allIndexes\":\"Jörg\"}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldTermFilter() throws Exception {
|
||||
String cql = "dc.type = electronic";
|
||||
CQLParser parser = new CQLParser(cql);
|
||||
parser.parse();
|
||||
ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator();
|
||||
parser.getCQLQuery().accept(generator);
|
||||
String json = generator.getResult().string();
|
||||
//logger.info("{} --> {}", cql, json);
|
||||
assertEquals(json, "{\"query\":{\"term\":{\"dc.type\":\"electronic\"}}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleFieldTermFilter() throws Exception {
|
||||
String cql = "dc.type = electronic and dc.date = 2013";
|
||||
CQLParser parser = new CQLParser(cql);
|
||||
parser.parse();
|
||||
ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator();
|
||||
parser.getCQLQuery().accept(generator);
|
||||
String json = generator.getResult().string();
|
||||
//logger.info("{} --> {}", cql, json);
|
||||
assertEquals(
|
||||
"{\"query\":{\"bool\":{\"must\":[{\"term\":{\"dc.type\":\"electronic\"}},{\"term\":{\"dc.date\":\"2013\"}}]}}}",
|
||||
json
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTripleFieldTermFilter() throws Exception {
|
||||
String cql = "dc.format = online and dc.type = electronic and dc.date = 2013";
|
||||
CQLParser parser = new CQLParser(cql);
|
||||
parser.parse();
|
||||
ElasticsearchFilterGenerator generator = new ElasticsearchFilterGenerator();
|
||||
parser.getCQLQuery().accept(generator);
|
||||
String json = generator.getResult().string();
|
||||
//logger.info("{} --> {}", cql, json);
|
||||
assertEquals(
|
||||
"{\"query\":{\"bool\":{\"must\":[{\"bool\":{\"must\":[{\"term\":{\"dc.format\":\"online\"}}," +
|
||||
"{\"term\":{\"dc.type\":\"electronic\"}}]}},{\"term\":{\"dc.date\":\"2013\"}}]}}}",
|
||||
json);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoost() throws Exception {
|
||||
String cql = "Jörg";
|
||||
CQLParser parser = new CQLParser(cql);
|
||||
parser.parse();
|
||||
ElasticsearchQueryGenerator generator = new ElasticsearchQueryGenerator();
|
||||
generator.setBoostParams("boost", "log2p", 2.0f, "sum");
|
||||
parser.getCQLQuery().accept(generator);
|
||||
String json = generator.getSourceResult();
|
||||
assertEquals(
|
||||
"{\"from\":0,\"size\":10,\"query\":{\"function_score\":{\"field_value_factor\":{\"field\":\"boost\"," +
|
||||
"\"modifier\":\"log2p\",\"factor\":2.0},\"boost_mode\":\"sum\"," +
|
||||
"\"query\":{\"simple_query_string\":{\"query\":\"Jörg\",\"fields\":[\"cql.allIndexes\"]," +
|
||||
"\"analyze_wildcard\":true,\"default_operator\":\"and\"}}}}}",
|
||||
json);
|
||||
}
|
||||
|
||||
private void test(String path) throws IOException {
|
||||
int count = 0;
|
||||
int ok = 0;
|
||||
int errors = 0;
|
||||
LineNumberReader lr = new LineNumberReader(new InputStreamReader(getClass().getResourceAsStream(path),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = lr.readLine()) != null) {
|
||||
if (line.trim().length() > 0 && !line.startsWith("#")) {
|
||||
try {
|
||||
int pos = line.indexOf('|');
|
||||
if (pos > 0) {
|
||||
validate(line.substring(0, pos), line.substring(pos + 1));
|
||||
ok++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
lr.close();
|
||||
assertEquals(0, errors);
|
||||
assertEquals(count, ok);
|
||||
}
|
||||
|
||||
private void validate(String cql, String expected) throws Exception {
|
||||
CQLParser parser = new CQLParser(cql);
|
||||
parser.parse();
|
||||
ElasticsearchQueryGenerator generator = new ElasticsearchQueryGenerator();
|
||||
parser.getCQLQuery().accept(generator);
|
||||
String elasticsearchQuery = generator.getSourceResult();
|
||||
assertEquals(expected, elasticsearchQuery);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for Elasticsearch CQL testing.
|
||||
*/
|
||||
package org.xbib.cql.elasticsearch;
|
4
src/test/java/org/xbib/cql/package-info.java
Normal file
4
src/test/java/org/xbib/cql/package-info.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for CQL testing.
|
||||
*/
|
||||
package org.xbib.cql;
|
|
@ -0,0 +1,22 @@
|
|||
package org.xbib.cql.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class QuotedStringTokenizerTest {
|
||||
|
||||
@Test
|
||||
public void testTokenizer() throws Exception {
|
||||
String s = "Linux is \"pinguin's best friend\", not Windows";
|
||||
QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(s);
|
||||
assertEquals("Linux", tokenizer.nextToken());
|
||||
assertEquals("is", tokenizer.nextToken());
|
||||
assertEquals("pinguin's best friend,", tokenizer.nextToken());
|
||||
assertEquals("not", tokenizer.nextToken());
|
||||
assertEquals("Windows", tokenizer.nextToken());
|
||||
}
|
||||
}
|
4
src/test/java/org/xbib/cql/util/package-info.java
Normal file
4
src/test/java/org/xbib/cql/util/package-info.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Classes for CQL utilities.
|
||||
*/
|
||||
package org.xbib.cql.util;
|
13
src/test/resources/log4j2.xml
Normal file
13
src/test/resources/log4j2.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration status="OFF">
|
||||
<appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%d{ABSOLUTE}][%-5p][%-25c][%t] %m%n"/>
|
||||
</Console>
|
||||
</appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="Console" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</configuration>
|
123
src/test/resources/org/xbib/cql/elasticsearch/queries.txt
Normal file
123
src/test/resources/org/xbib/cql/elasticsearch/queries.txt
Normal file
|
@ -0,0 +1,123 @@
|
|||
id = 8a666b7e-6597-3cfb-b478-313cc3c25011|{"from":0,"size":10,"query":{"simple_query_string":{"query":"8a666b7e-6597-3cfb-b478-313cc3c25011","fields":["id"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
unix|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
financing|{"from":0,"size":10,"query":{"simple_query_string":{"query":"financing","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"Christine Wolfinger"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"Christine Wolfinger\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"der die das"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"der die das\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
1234|{"from":0,"size":10,"query":{"simple_query_string":{"query":"1234","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"1234"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"1234\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"unix AND wolfinger"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"unix AND wolfinger\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"to be or not to be"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"to be or not to be\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"not macht erfinderisch"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"not macht erfinderisch\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"to be or not to be"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"to be or not to be\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
unix$|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix$","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"^linux"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"^linux\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
finan*|{"from":0,"size":10,"query":{"simple_query_string":{"query":"finan*","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
finan?|{"from":0,"size":10,"query":{"simple_query_string":{"query":"finan?","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
finan*ng|{"from":0,"size":10,"query":{"simple_query_string":{"query":"finan*ng","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
finan?ier?ng|{"from":0,"size":10,"query":{"simple_query_string":{"query":"finan?ier?ng","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
title = "duck"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"duck\"","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
title = "Dinosaur Systematics"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"Dinosaur Systematics\"","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
title <> linux|{"from":0,"size":10,"query":{"bool":{"must_not":{"simple_query_string":{"query":"linux","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}}}}
|
||||
cql.resultSetId = HT000011990|{"from":0,"size":10,"query":{"simple_query_string":{"query":"HT000011990","fields":["cql.resultSetId"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
cql.allRecords = 2|{"from":0,"size":10,"query":{"simple_query_string":{"query":"2","fields":["cql.allRecords"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
cql.allRecords = 1 NOT title = fish|{"from":0,"size":10,"query":{"bool":{"must_not":[{"simple_query_string":{"query":"1","fields":["cql.allRecords"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"fish","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
title any "unix linux"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix linux","fields":["title"],"analyze_wildcard":true,"default_operator":"or"}}}
|
||||
title all "unix linux"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix linux","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
title all "unix 'linux' test"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix 'linux' test","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
title all "linux \"pinguin's best friend\" unix"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"linux \"pinguin's best friend\" unix","fields":["title"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.title adj "lord of the rings"|{"from":0,"size":10,"query":{"match_phrase":{"dc.title":{"query":"lord of the rings","slop":0}}}}
|
||||
anywhere = "linux unix \"grundkurs für einsteiger\""|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"linux unix \\\"grundkurs für einsteiger\\\"\"","fields":["anywhere"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.date=2003|{"from":0,"size":10,"query":{"simple_query_string":{"query":"2003","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.date="2003"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"2003\"","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.creator=smith|{"from":0,"size":10,"query":{"simple_query_string":{"query":"smith","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.title=financing|{"from":0,"size":10,"query":{"simple_query_string":{"query":"financing","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.subject=financing|{"from":0,"size":10,"query":{"simple_query_string":{"query":"financing","fields":["dc.subject"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
"feathered dinosaur" and (yixian or jehol)|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"feathered dinosaur\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"bool":{"should":[{"simple_query_string":{"query":"yixian","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"jehol","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
(a or b) and (c or d)|{"from":0,"size":10,"query":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"a","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"b","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"should":[{"simple_query_string":{"query":"c","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"d","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
unix AND wolfinger|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"wolfinger","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
"keine angst" AND unix|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"keine angst\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.title=unix or wolfinger|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"unix","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"wolfinger","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
(dc.title = unix or dc.date = 2003) and ( dc.creator = wolfinger and dc.creator = christine or dc.creator = maier )|{"from":0,"size":10,"query":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"unix","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"2003","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"should":[{"bool":{"must":[{"simple_query_string":{"query":"wolfinger","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"christine","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"simple_query_string":{"query":"maier","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
financing AND success|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"financing","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
financing OR monetary|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"financing","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"monetary","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
financing NOT success|{"from":0,"size":10,"query":{"bool":{"must_not":[{"simple_query_string":{"query":"financing","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
(financing AND monetary) OR success|{"from":0,"size":10,"query":{"bool":{"should":[{"bool":{"must":[{"simple_query_string":{"query":"financing","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"monetary","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
financing AND (monetary OR success)|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"financing","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"bool":{"should":[{"simple_query_string":{"query":"monetary","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
"financing constraints" OR success|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"\"financing constraints\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
"financing constraints" NOT model|{"from":0,"size":10,"query":{"bool":{"must_not":[{"simple_query_string":{"query":"\"financing constraints\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"model","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
("financing constraints" AND model) OR success|{"from":0,"size":10,"query":{"bool":{"should":[{"bool":{"must":[{"simple_query_string":{"query":"\"financing constraints\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"model","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
"financing constraints" AND (model OR success)|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"financing constraints\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"bool":{"should":[{"simple_query_string":{"query":"model","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"success","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
dinosaur or bird|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"dinosaur","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"bird","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dino and "eiszeit"|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"dino","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"eiszeit\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dinosaur not reptile|{"from":0,"size":10,"query":{"bool":{"must_not":[{"simple_query_string":{"query":"dinosaur","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"reptile","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "christine" )|{"from":0,"size":10,"query":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"linux\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"must":[{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"christine\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
date = 2007-09-30 or date = "2007-09-30T12:34:56"|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"2007-09-30","fields":["date"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"2007-09-30T12:34:56\"","fields":["date"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dinosaur and bird or dinobird|{"from":0,"size":10,"query":{"bool":{"should":[{"bool":{"must":[{"simple_query_string":{"query":"dinosaur","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"bird","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"simple_query_string":{"query":"dinobird","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
(bird or dinosaur) and (feathers or scales)|{"from":0,"size":10,"query":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"bird","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"dinosaur","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"should":[{"simple_query_string":{"query":"feathers","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"scales","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
linux and creator = wolfinger|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"linux","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"wolfinger","fields":["creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.title=linux and dc.title = unix|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"linux","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"unix","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.title = unix and dc.date = 2000|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"unix","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"2000","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.title = "unix" and dc.creator = "wolfinger"|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.title = "unix" or dc.creator = "wolfinger"|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.title = "unix" and ( dc.creator = "wolfinger" or dc.creator = "meyer" )|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"bool":{"should":[{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"meyer\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
dc.title = "unix" and dc.creator = "wolfinger" and dc.creator = "christine"|{"from":0,"size":10,"query":{"bool":{"must":[{"bool":{"must":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"simple_query_string":{"query":"\"christine\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "meyer" )|{"from":0,"size":10,"query":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"linux\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"must":[{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"meyer\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
dc.title = "foo" and (dc.creator = "smith" or dc.creator = "jones")|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"foo\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"bool":{"should":[{"simple_query_string":{"query":"\"smith\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"jones\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}
|
||||
dc.creator = "smith" and dc.creator = "jones"|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"smith\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"jones\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
dc.date = 2007-09-30 or dc.date = "2007-09-30T12:34:56"|{"from":0,"size":10,"query":{"bool":{"should":[{"simple_query_string":{"query":"2007-09-30","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"2007-09-30T12:34:56\"","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}]}}}
|
||||
identifier = 0783923126590|{"from":0,"size":10,"query":{"simple_query_string":{"query":"0783923126590","fields":["identifier"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
identifier = "9783923126590"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"9783923126590\"","fields":["identifier"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
identifier = "9783923126590*"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"9783923126590*\"","fields":["identifier"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.identifier =/bib.identifierAuthority=isbn "0201563177"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"0201563177\"","fields":["bib.identifierAuthority=isbn"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.identifier =/bib.identifierAuthority=isbn "0201563177" and dc.title=unix sortby dc.date|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"\"0201563177\"","fields":["bib.identifierAuthority=isbn"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"unix","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}]}},"sort":[{"dc.date":{"unmapped_type":"string","missing":"_last"}}]}
|
||||
dc.date > 2007-09-30 and dc.date < "2007-10-30T12:34:56"|{"from":0,"size":10,"query":{"bool":{"must":[{"range":{"dc.date":{"from":"2007-09-30","include_lower":false}}},{"range":{"dc.date":{"to":"\"2007-10-30T12:34:56\"","include_upper":false}}}]}}}
|
||||
date > 2007-01-01|{"from":0,"size":10,"query":{"range":{"date":{"from":"2007-01-01","include_lower":false}}}}
|
||||
dc.date <= 2006-07-01|{"from":0,"size":10,"query":{"range":{"dc.date":{"to":"2006-07-01","include_upper":true}}}}
|
||||
dc.date >= 2005-02-31|{"from":0,"size":10,"query":{"range":{"dc.date":{"from":"2005-02-31","include_lower":true}}}}
|
||||
dc.date > 2011|{"from":0,"size":10,"query":{"range":{"dc.date":{"from":"2011","include_lower":false}}}}
|
||||
dc.date = "> 2003"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"> 2003\"","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
dc.date = "20012010"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"20012010\"","fields":["dc.date"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "meyer" ) and filter.subject = "computer"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"linux\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"must":[{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"meyer\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}},"filter":{"term":{"subject":"computer"}}}}}
|
||||
unix and filter.date > 2006-01-01|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"range":{"date":{"from":"2006-01-01","include_lower":false}}}}}}
|
||||
unix and (filter.date > 2006-01-01 and filter.date > 2007-01-01)|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"range":{"date":{"from":"2006-01-01","include_lower":false}}}}}}
|
||||
unix and dc.date within "2006 2007"|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"range":{"dc.date":{"from":"2006","to":"2007","include_lower":true,"include_upper":true}}}]}}}
|
||||
unix and dc.date within "2006-01-01 2007-01-01"|{"from":0,"size":10,"query":{"bool":{"must":[{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},{"range":{"dc.date":{"from":"2006-01-01","to":"2007-01-01","include_lower":true,"include_upper":true}}}]}}}
|
||||
unix and filter.date within "2006-01-01 2007-01-01"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"range":{"date":{"from":"2006-01-01","to":"2007-01-01","include_lower":true,"include_upper":true}}}}}}
|
||||
dc.title = "unix" and filter.creator = "wolfinger"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"creator":"wolfinger"}}}}}
|
||||
dc.title = "unix" and filter.creator = "wolfinger" or filter.creator = "meyer"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"should":{"bool":{"must":{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}}}}},"filter":{"bool":{"should":[{"term":{"creator":"wolfinger"}},{"term":{"creator":"meyer"}}]}}}}}
|
||||
dc.title = "unix" and (filter.creator = "wolfinger" and filter.subject= Computer)|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"bool":{"must":[{"term":{"creator":"wolfinger"}},{"term":{"subject":"Computer"}}]}}}}}
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "meyer" ) and filter.subject = "computer"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"bool":{"must":[{"bool":{"should":[{"simple_query_string":{"query":"\"unix\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"linux\"","fields":["dc.title"],"analyze_wildcard":true,"default_operator":"and"}}]}},{"bool":{"must":[{"simple_query_string":{"query":"\"wolfinger\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}},{"simple_query_string":{"query":"\"meyer\"","fields":["dc.creator"],"analyze_wildcard":true,"default_operator":"and"}}]}}]}}}},"filter":{"term":{"subject":"computer"}}}}}
|
||||
test and (filter.creator = "a" and filter.subject = "b")|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"bool":{"must":[{"term":{"creator":"a"}},{"term":{"subject":"b"}}]}}}}}
|
||||
test and filter.creator = "a" or filter.subject = "b"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"should":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}},"filter":{"bool":{"should":[{"term":{"creator":"a"}},{"term":{"subject":"b"}}]}}}}}
|
||||
test and filter.creator = "smith"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"creator":"smith"}}}}}
|
||||
test and filter.creator = "smith" or filter.creator = "jones"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"should":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}},"filter":{"bool":{"should":[{"term":{"creator":"smith"}},{"term":{"creator":"jones"}}]}}}}}
|
||||
test and (filter.creator = "smith" and filter.creator = "jones")|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"creator":"smith"}}}}}
|
||||
test or filter.creator = "smith" and filter.creator = "jones"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"bool":{"should":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}},"filter":{"bool":{"should":[{"term":{"creator":"jones"}},{"term":{"creator":"smith"}}]}}}}}
|
||||
test or (filter.creator = "smith" and filter.creator = "jones")|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"should":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"creator":"smith"}}}}}
|
||||
test and (filter.creator = "smith" or filter.creator = "jones")|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"bool":{"should":[{"term":{"creator":"smith"}},{"term":{"creator":"jones"}}]}}}}}
|
||||
test or (filter.creator = "smith" or filter.creator = "jones")|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"should":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"bool":{"should":[{"term":{"creator":"smith"}},{"term":{"creator":"jones"}}]}}}}}
|
||||
test and (filter.creator = "smith" or filter.creator = "jones" and filter.subject = "unix")|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"bool":{"should":[{"bool":{"must":[{"term":{"creator":"smith"}},{"term":{"subject":"unix"}}]}},{"term":{"creator":"jones"}}]}}}}}
|
||||
structure AND filter.creator="smith"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"structure","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"creator":"smith"}}}}}
|
||||
structure AND filter.subject="data"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"structure","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"subject":"data"}}}}}
|
||||
structure AND filter.date="2003"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"structure","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"date":"2003"}}}}}
|
||||
pädagogik AND filter.taxonomy="0/24/*"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"pädagogik","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"taxonomy":"0/24/"}}}}}
|
||||
pädagogik AND filter.taxonomy="0/24/313/*"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"pädagogik","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"taxonomy":"0/24/313/"}}}}}
|
||||
pädagogik AND filter.taxonomy="0/24/313/21/*"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"pädagogik","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"term":{"taxonomy":"0/24/313/21/"}}}}}
|
||||
linux and filter.creator <> "Wolfinger"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"linux","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"not":{"term":{"creator":"Wolfinger"}}}}}}
|
||||
unix and option.offset = 10 and option.length = 20|{"from":0,"size":10,"query":{"bool":{"must":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}}}
|
||||
test and option.length = 1 and option.length = 2 and option.length = 3|{"from":0,"size":10,"query":{"bool":{"must":{"bool":{"must":{"bool":{"must":{"simple_query_string":{"query":"test","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}}}}}
|
||||
unix sortby date|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},"sort":[{"date":{"unmapped_type":"string","missing":"_last"}}]}
|
||||
unix sortby date/sort.descending|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},"sort":[{"date":{"order":"desc","unmapped_type":"string","missing":"_last"}}]}
|
||||
unix sortby date/sort.descending geo/sort.ascending|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},"sort":[{"date":{"order":"desc","unmapped_type":"string","missing":"_last"}}]}
|
||||
unix sortby geo/sort.ascending/sort.unit=km/sort.lat=50.9415016174/sort.lon=6.95853996277|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},"sort":[{"geo":{"order":"asc","sort.unit":"km","sort.lat":"50.9415016174","sort.lon":"6.95853996277","unmapped_type":"string","missing":"_last"}}]}
|
||||
unix sortby geo/sort.ascending/sort.unit=km/sort.center="(50.9415016174,6.95853996277)"|{"from":0,"size":10,"query":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}},"sort":[{"geo":{"order":"asc","sort.unit":"km","sort.center":"\"(50.9415016174,6.95853996277)\"","unmapped_type":"string","missing":"_last"}}]}
|
||||
bib.namePersonal = meier|{"from":0,"size":10,"query":{"simple_query_string":{"query":"meier","fields":["bib.namePersonal"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
unix and filter.location any "DE-929 DE-107 DE-Zw1"|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"or":[{"term":{"location":"DE-929 DE-107 DE-Zw1"}}]}}}}
|
||||
unix and filter.location any "DE-929 DE-107 DE-Zw1" sortby date/sort.descending|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"filter":{"or":[{"term":{"location":"DE-929 DE-107 DE-Zw1"}}]}}},"sort":[{"date":{"order":"desc","unmapped_type":"string","missing":"_last"}}]}
|
||||
unix and option.offset = 10 and option.length = 20 and filter.location any "DE-929 DE-107 DE-Zw1" sortby date/sort.descending|{"from":0,"size":10,"query":{"filtered":{"query":{"bool":{"must":{"bool":{"must":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}}}},"filter":{"or":[{"term":{"location":"DE-929 DE-107 DE-Zw1"}}]}}},"sort":[{"date":{"order":"desc","unmapped_type":"string","missing":"_last"}}]}
|
||||
unix and facet.creator = "on"|{"from":0,"size":10,"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"aggregations":{"myfacet":"myvalue"}}
|
||||
unix and facet.creator = "off"|{"from":0,"size":10,"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"aggregations":{"myfacet":"myvalue"}}
|
||||
unix and facet.creator = "on" and facet.subject = "on" and facet.date = "off"|{"from":0,"size":10,"query":{"bool":{"must":{"bool":{"must":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}}}}}},"aggregations":{"myfacet":"myvalue"}}
|
||||
unix and facet.date = on|{"from":0,"size":10,"query":{"bool":{"must":{"simple_query_string":{"query":"unix","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}},"aggregations":{"myfacet":"myvalue"}}
|
||||
(cql.allIndexes = "")|{"from":0,"size":10,"query":{"simple_query_string":{"query":"\"\"","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
||||
cql.allIndexes all 3125294126|{"from":0,"size":10,"query":{"simple_query_string":{"query":"3125294126","fields":["cql.allIndexes"],"analyze_wildcard":true,"default_operator":"and"}}}
|
141
src/test/resources/org/xbib/cql/queries.txt
Normal file
141
src/test/resources/org/xbib/cql/queries.txt
Normal file
|
@ -0,0 +1,141 @@
|
|||
"to be or not to be"
|
||||
publicationYear < 1980
|
||||
lengthOfFemur > 2.4
|
||||
bioMass >= 100
|
||||
id = 12345678
|
||||
id = 8a666b7e-6597-3cfb-b478-313cc3c25011
|
||||
contentid = "0a1af248-7339-3b59-bc07-3a460275456f"
|
||||
isbn = "0818631678"
|
||||
title = "duck" and author = "sanderson"
|
||||
unix
|
||||
financing
|
||||
"Christine Wolfinger"
|
||||
"der die das"
|
||||
1234
|
||||
"1234"
|
||||
1.234
|
||||
"1.234"
|
||||
"unix AND wolfinger"
|
||||
"to be or not to be"
|
||||
"not macht erfinderisch"
|
||||
"to be or not to be"
|
||||
unix$
|
||||
"^linux"
|
||||
finan*
|
||||
finan?
|
||||
finan*ng
|
||||
finan?ier?ng
|
||||
title = "duck"
|
||||
title = "Dinosaur Systematics"
|
||||
title <> linux
|
||||
cql.resultSetId = HT000011990
|
||||
cql.allRecords = 2
|
||||
cql.allRecords = 1 NOT title = fish|cql.allRecords = 1 not title = fish
|
||||
title any "unix linux"
|
||||
title all "unix linux"
|
||||
title all "unix 'linux' test"
|
||||
title all "linux \"pinguin's best friend\" unix"
|
||||
dc.title adj "lord of the rings"
|
||||
anywhere = "linux unix \"grundkurs für einsteiger\""
|
||||
dc.date=2003|dc.date = 2003
|
||||
dc.date="2003"|dc.date = "2003"
|
||||
dc.creator=smith|dc.creator = smith
|
||||
dc.title=financing|dc.title = financing
|
||||
dc.subject=financing|dc.subject = financing
|
||||
"feathered dinosaur" and (yixian or jehol)
|
||||
(a or b) and (c or d)
|
||||
unix AND wolfinger|unix and wolfinger
|
||||
"keine angst" AND unix|"keine angst" and unix
|
||||
unix and 2012
|
||||
dc.title=unix or wolfinger|dc.title = unix or wolfinger
|
||||
(dc.title = unix or dc.date = 2003) and ( dc.creator = wolfinger and dc.creator = christine or dc.creator = maier )|(dc.title = unix or dc.date = 2003) and (dc.creator = wolfinger and dc.creator = christine or dc.creator = maier)
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "christine" )|(dc.title = "unix" or dc.title = "linux") and (dc.creator = "wolfinger" and dc.creator = "christine")
|
||||
financing AND success|financing and success
|
||||
financing OR monetary|financing or monetary
|
||||
financing NOT success|financing not success
|
||||
(financing AND monetary) OR success|(financing and monetary) or success
|
||||
financing AND (monetary OR success)|financing and (monetary or success)
|
||||
"financing constraints" OR success|"financing constraints" or success
|
||||
"financing constraints" NOT model|"financing constraints" not model
|
||||
("financing constraints" AND model) OR success|("financing constraints" and model) or success
|
||||
"financing constraints" AND (model OR success)|"financing constraints" and (model or success)
|
||||
dinosaur or bird
|
||||
dino and "eiszeit"
|
||||
dinosaur not reptile
|
||||
date = 2007-09-30 or date = "2007-09-30T12:34:56"
|
||||
dinosaur and bird or dinobird
|
||||
(bird or dinosaur) and (feathers or scales)
|
||||
linux and creator = wolfinger
|
||||
dc.title=linux and dc.title = unix|dc.title = linux and dc.title = unix
|
||||
dc.title = unix and dc.date = 2000
|
||||
dc.title = "unix" and dc.creator = "wolfinger"
|
||||
dc.title = "unix" or dc.creator = "wolfinger"
|
||||
dc.title = "unix" and dc.creator = "wolfinger" and dc.creator = "christine"
|
||||
dc.title = "unix" and ( dc.creator = "wolfinger" or dc.creator = "meyer" )|dc.title = "unix" and (dc.creator = "wolfinger" or dc.creator = "meyer")
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "meyer" )|(dc.title = "unix" or dc.title = "linux") and (dc.creator = "wolfinger" and dc.creator = "meyer")
|
||||
dc.title = "foo" and (dc.creator = "smith" or dc.creator = "jones")
|
||||
dc.creator = "smith" and dc.creator = "jones"
|
||||
dc.date = 2007-09-30 or dc.date = "2007-09-30T12:34:56"
|
||||
identifier = 0783923126590
|
||||
identifier = "9783923126590"
|
||||
identifier = "9783923126590*"
|
||||
dc.identifier=/bib.identifierAuthority=isbn "0201563177"|dc.identifier =/bib.identifierAuthority=isbn "0201563177"
|
||||
dc.identifier =/bib.identifierAuthority=isbn "0201563177"|dc.identifier =/bib.identifierAuthority=isbn "0201563177"
|
||||
dc.identifier =/bib.identifierAuthority=isbn "0201563177" and dc.title=unix sortby date|dc.identifier =/bib.identifierAuthority=isbn "0201563177" and dc.title = unix sortby date
|
||||
dc.date > 2007-09-30 and dc.date < "2007-10-30T12:34:56"
|
||||
date > 2007-01-01
|
||||
dc.date <= 2006-07-01
|
||||
dc.date >= 2005-02-31
|
||||
dc.date within "2006-01-01 2007-01-01"
|
||||
dc.date > 2011
|
||||
dc.date = "> 2003"
|
||||
dc.date = "20012010"
|
||||
test and filter.collection = "test"|test
|
||||
dc.title = test and filter.collection = "test"|dc.title = test
|
||||
(dc.title = "unix" or dc.title = "linux") and ( dc.creator = "wolfinger" and dc.creator = "meyer" ) and filter.subject = "computer"|(dc.title = "unix" or dc.title = "linux") and (dc.creator = "wolfinger" and dc.creator = "meyer")
|
||||
dc.title = "unix" and filter.creator = "wolfinger"|dc.title = "unix"
|
||||
dc.title = "unix" and filter.creator = "wolfinger" or filter.creator = "meyer"|dc.title = "unix"
|
||||
dc.title = "unix" and (filter.creator = "wolfinger" and filter.subject= Computer)|dc.title = "unix"
|
||||
unix and filter.date > 2006-01-01|unix
|
||||
unix and (filter.date > 2006-01-01 and filter.date > 2007-01-01)|unix
|
||||
unix and filter.date within "2006-01-01 2007-01-01"|unix
|
||||
unix and filter.collection = "info:sid/a.b.c.d:module"|unix
|
||||
unix and filter.collection = "info:sid/a.b.c.d:module" or filter.collection = "info:sid/e.f.g.h:module"|unix
|
||||
unix and (filter.collection = "info:sid/a.b.c.d:module" and filter.creator ="Wolfinger, Christine")|unix
|
||||
test and filter.collection = "test"|test
|
||||
test and (filter.creator = "a" and filter.subject = "b")|test
|
||||
test and filter.creator = "a" or filter.subject = "b"|test
|
||||
test and filter.creator = "smith"|test
|
||||
test and (filter.creator = "jones" and filter.collection = "test")|test
|
||||
test and filter.creator = "smith" or filter.creator = "jones"|test
|
||||
test and (filter.creator = "smith" and filter.creator = "jones")|test
|
||||
test or filter.creator = "smith" and filter.creator = "jones"|test
|
||||
test or (filter.creator = "smith" and filter.creator = "jones")|test
|
||||
test and (filter.creator = "smith" or filter.creator = "jones")|test
|
||||
test or (filter.creator = "smith" or filter.creator = "jones")|test
|
||||
test and (filter.creator = "smith" or filter.creator = "jones" and filter.subject = "unix")|test
|
||||
structure AND filter.creator="smith"|structure
|
||||
structure AND filter.subject="data"|structure
|
||||
structure AND filter.date="2003"|structure
|
||||
pädagogik AND filter.taxonomy="0/24/*"|pädagogik
|
||||
pädagogik AND filter.taxonomy="0/24/313/*"|pädagogik
|
||||
pädagogik AND filter.taxonomy="0/24/313/21/*"|pädagogik
|
||||
linux and filter.creator <> "Wolfinger"|linux
|
||||
unix and option.offset = 10 and option.length = 20|unix
|
||||
test and option.length = 1 and option.length = 2 and option.length = 3|test
|
||||
bib.namePersonal = meier
|
||||
unix sortby date/sort.descending
|
||||
unix sortby date/sort.descending geo/sort.ascending
|
||||
unix sortby geo/sort.ascending/sort.unit=km/sort.lat=50.9415016174/sort.lon=6.95853996277
|
||||
unix sortby geo/sort.ascending/sort.unit=km/sort.center="(50.9415016174,6.95853996277)"
|
||||
unix and filter.location any "DE-929 DE-107 DE-Zw1"|unix
|
||||
unix and filter.location any "DE-929 DE-107 DE-Zw1" sortby date/sort.descending|unix sortby date/sort.descending
|
||||
unix and option.offset = 10 and option.length = 20 and filter.location any "DE-929 DE-107 DE-Zw1" sortby date/sort.descending|unix sortby date/sort.descending
|
||||
unix and facet.dc.creator = "on"|unix
|
||||
unix and facet.dc.creator = "off"|unix
|
||||
unix and facet.dc.creator = "on" and facet.dc.subject = "on" and facet.dc.date = "off"|unix
|
||||
unix and facet.dc.date = on|unix
|
||||
unix and facet.dc.creator = "on" and facet.dc.subject = "on" and facet.dc.subject = "buckets=10"|unix
|
||||
unix and facet.dc.date = "on" and facet.dc.subject = "on" and facet.dc.subject = "buckets=20"|unix
|
||||
unix and facet.dc.creator = "on" and facet.dc.subject = "on" and facet.dc.subject = "buckets=20"|unix
|
||||
cql.allIndexes all "linux;"
|
Loading…
Reference in a new issue