├── .gitignore ├── .travis.yml ├── README.md ├── gpl.txt ├── pom.xml └── src ├── main ├── assemblies │ └── plugin.xml ├── java │ └── com │ │ └── graphaware │ │ └── es │ │ └── gas │ │ ├── GraphAidedSearch.java │ │ ├── GraphAidedSearchFilter.java │ │ ├── GraphAidedSearchModule.java │ │ ├── GraphAidedSearchPlugin.java │ │ ├── annotation │ │ ├── SearchBooster.java │ │ └── SearchFilter.java │ │ ├── booster │ │ ├── SearchResultBooster.java │ │ ├── SearchResultCypherBooster.java │ │ ├── SearchResultExternalBooster.java │ │ └── SearchResultNeo4jBooster.java │ │ ├── cypher │ │ ├── CypherBoltHttpEndPoint.java │ │ ├── CypherEndPoint.java │ │ ├── CypherEndPointBuilder.java │ │ ├── CypherHttpEndPoint.java │ │ ├── CypherResult.java │ │ ├── CypherSettingsReader.java │ │ └── ResultRow.java │ │ ├── domain │ │ ├── ClauseConstants.java │ │ ├── ExternalResult.java │ │ └── IndexInfo.java │ │ ├── filter │ │ ├── SearchResultCypherFilter.java │ │ └── SearchResultFilter.java │ │ ├── modifier │ │ ├── PrivilegedSearchResultModifier.java │ │ └── SearchResultModifier.java │ │ ├── util │ │ ├── Instantiator.java │ │ ├── NumberUtil.java │ │ ├── ParamUtil.java │ │ ├── PluginClassLoader.java │ │ └── UrlUtil.java │ │ └── wrap │ │ ├── ActionListenerWrapper.java │ │ ├── CannotWrapException.java │ │ ├── GraphAidedSearchActionListenerWrapper.java │ │ └── WrappingActionListener.java └── plugin-metadata │ ├── plugin-descriptor.properties │ └── plugin-security.policy └── test ├── java └── com │ └── graphaware │ └── es │ └── gas │ ├── EmbeddedGraphDatabaseServerTest.java │ ├── GraphAidedSearchFilterTest.java │ ├── GraphAidedSearchIntegrationBoltTest.java │ ├── GraphAidedSearchIntegrationTest.java │ ├── GraphAidedSearchNeo4jIntegrationTest.java │ ├── GraphAidedSearchTest.java │ ├── JestMsgResult.java │ ├── MockServerTest.java │ ├── booster │ ├── GraphBoosterTest.java │ ├── SearchResultCypherBoltBoosterTest.java │ ├── SearchResultCypherBoosterTest.java │ └── SearchResultNeo4jBoosterTest.java │ ├── domain │ ├── CypherEndpointBoltTest.java │ ├── CypherEndpointTest.java │ ├── CypherEndpointUnitTest.java │ ├── ExternalResultTest.java │ ├── ExternalResultUnitTest.java │ └── TestIndexInfo.java │ ├── filter │ ├── GraphFilterTest.java │ └── SearchResultCypherFilterTest.java │ ├── stubs │ ├── CypherSearchResultTestBooster.java │ ├── CypherSearchResultTestFilter.java │ └── SearchResultTestBooster.java │ └── util │ ├── ClauseConstantsTest.java │ ├── NumberUtilTest.java │ ├── TestHttpClient.java │ └── UrlUtilTest.java └── resources ├── demo-data-reduced.cyp ├── demo-data.cyp ├── graphgen-test-data.cyp ├── neo4j-elasticsearch-reco.properties ├── neo4j-elasticsearch.properties └── neo4j-server-es.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Project ### 4 | data/ 5 | 6 | ### Intellij ### 7 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 8 | 9 | *.iml 10 | 11 | ## Directory-based project format: 12 | .idea/ 13 | # if you remove the above rule, at least ignore the following: 14 | 15 | # User-specific stuff: 16 | # .idea/workspace.xml 17 | # .idea/tasks.xml 18 | # .idea/dictionaries 19 | 20 | # Sensitive or high-churn files: 21 | # .idea/dataSources.ids 22 | # .idea/dataSources.xml 23 | # .idea/sqlDataSources.xml 24 | # .idea/dynamic.xml 25 | # .idea/uiDesigner.xml 26 | 27 | # Gradle: 28 | # .idea/gradle.xml 29 | # .idea/libraries 30 | 31 | # Mongo Explorer plugin: 32 | # .idea/mongoSettings.xml 33 | 34 | ## File-based project format: 35 | *.ipr 36 | *.iws 37 | 38 | ## Plugin-specific files: 39 | 40 | # IntelliJ 41 | /out/ 42 | 43 | # mpeltonen/sbt-idea plugin 44 | .idea_modules/ 45 | 46 | # JIRA plugin 47 | atlassian-ide-plugin.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | 54 | 55 | ### OSX ### 56 | .DS_Store 57 | .AppleDouble 58 | .LSOverride 59 | 60 | # Icon must end with two \r 61 | Icon 62 | 63 | 64 | # Thumbnails 65 | ._* 66 | 67 | # Files that might appear in the root of a volume 68 | .DocumentRevisions-V100 69 | .fseventsd 70 | .Spotlight-V100 71 | .TemporaryItems 72 | .Trashes 73 | .VolumeIcon.icns 74 | 75 | # Directories potentially created on remote AFP share 76 | .AppleDB 77 | .AppleDesktop 78 | Network Trash Folder 79 | Temporary Items 80 | .apdisk 81 | 82 | 83 | ### Maven ### 84 | target/ 85 | pom.xml.tag 86 | pom.xml.releaseBackup 87 | pom.xml.versionsBackup 88 | pom.xml.next 89 | release.properties 90 | dependency-reduced-pom.xml 91 | buildNumber.properties 92 | 93 | 94 | ### Java ### 95 | *.class 96 | 97 | # Mobile Tools for Java (J2ME) 98 | .mtj.tmp/ 99 | 100 | # Package Files # 101 | *.jar 102 | *.war 103 | *.ear 104 | 105 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 106 | hs_err_pid* 107 | 108 | /extlib/ 109 | /neo4j-home/ 110 | nb-configuration.xml 111 | /data/ 112 | .classpath 113 | .project 114 | .settings/ 115 | neo4jextlib 116 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | before_install: 4 | - sudo apt-get update > /dev/null 5 | 6 | jdk: 7 | - oraclejdk8 8 | - oraclejdk7 9 | 10 | branches: 11 | only: 12 | - master 13 | - 2.2 14 | 15 | after_success: 16 | - echo "ossrh\${env.OSSRH_USER}\${env.OSSRH_PASS}" > ~/settings.xml 17 | - mvn deploy --settings ~/settings.xml -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 4.0.0 21 | 22 | com.graphaware.es 23 | graph-aided-search 24 | 2.4.4.4-SNAPSHOT 25 | 26 | 27 | 1.7 28 | UTF-8 29 | 2.4.4 30 | 1.1.0-M04 31 | com.graphaware.es.gas.GraphAidedSearchPlugin 32 | 1.7 33 | 1.7 34 | 35 | 36 | Graph-Aided Search 37 | GraphAware Elasticsearch Plugin for Integration with Neo4j 38 | http://graphaware.com 39 | 40 | 41 | 42 | GNU General Public License, version 3 43 | http://www.gnu.org/licenses/gpl-3.0.txt 44 | repo 45 | 46 | 47 | 48 | 49 | Graph Aware Limited 50 | http://graphaware.com 51 | 52 | 53 | 54 | scm:git:git@github.com:graphaware/graph-aided-search.git 55 | scm:git:git@github.com:graphaware/graph-aided-search.git 56 | git@github.com:graphaware/graph-aided-search.git 57 | HEAD 58 | 59 | 60 | 61 | 62 | release 63 | 64 | 65 | performRelease 66 | true 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-gpg-plugin 74 | 1.6 75 | 76 | 77 | sign-artifacts 78 | verify 79 | 80 | sign 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | doclint-java8-disable 90 | 91 | [1.8,) 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-javadoc-plugin 99 | 100 | -Xdoclint:none 101 | 102 | 103 | 104 | 105 | 106 | 107 | intellij-javadoc-fix 108 | 109 | 110 | 111 | maven-javadoc-plugin 112 | 2.10.3 113 | 114 | ${java.home}/../bin/javadoc 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | alenegro 125 | Alessandro Negro 126 | alessandro@graphaware.com 127 | 128 | 129 | ikwattro 130 | Christophe Willemsen 131 | christophe@graphaware.com 132 | 133 | 134 | bachmanm 135 | Michal Bachman 136 | graph-aided-search@graphaware.com 137 | 138 | 139 | 140 | 141 | https://travis-ci.org/graphaware/graph-aided-search 142 | Travis CI 143 | 144 | 145 | 2015 146 | 147 | 148 | GitHub 149 | https://github.com/graphaware/graph-aided-search/issues 150 | 151 | 152 | 153 | 154 | 155 | org.elasticsearch 156 | elasticsearch 157 | ${elasticsearch.version} 158 | provided 159 | 160 | 161 | 162 | com.sun.jersey 163 | jersey-client 164 | 1.19 165 | 166 | 167 | com.sun.jersey 168 | jersey-json 169 | 1.19 170 | 171 | 172 | org.codehaus.jackson 173 | jackson-jaxrs 174 | 1.9.13 175 | 176 | 177 | com.google.guava 178 | guava 179 | 18.0 180 | 181 | 182 | org.reflections 183 | reflections 184 | 0.9.9 185 | 186 | 187 | 188 | javax.mail 189 | javax.mail-api 190 | 1.5.5 191 | 192 | 193 | 194 | org.neo4j.driver 195 | neo4j-java-driver 196 | ${bolt.version} 197 | 198 | 199 | 200 | 201 | 202 | 203 | org.codelibs 204 | elasticsearch-cluster-runner 205 | 2.3.0.0 206 | test 207 | 208 | 209 | junit 210 | junit 211 | 4.11 212 | test 213 | 214 | 215 | log4j 216 | log4j 217 | 1.2.17 218 | test 219 | 220 | 221 | io.searchbox 222 | jest 223 | 2.0.1 224 | test 225 | 226 | 227 | com.graphaware.integration.neo4j 228 | neo4j-tests-integration 229 | 3.0.6.1 230 | test 231 | 232 | 233 | org.apache.httpcomponents 234 | httpclient 235 | 4.5.1 236 | test 237 | 238 | 239 | org.springframework 240 | spring-core 241 | 4.2.3.RELEASE 242 | test 243 | 244 | 245 | org.mock-server 246 | mockserver-netty 247 | 3.10.4 248 | test 249 | 250 | 251 | 252 | 253 | 254 | 255 | org.apache.maven.plugins 256 | maven-release-plugin 257 | 2.5.3 258 | 259 | true 260 | false 261 | release 262 | deploy 263 | 264 | 265 | 266 | maven-assembly-plugin 267 | 2.6 268 | 269 | false 270 | ${project.build.directory}/releases/ 271 | 272 | ${basedir}/src/main/assemblies/plugin.xml 273 | 274 | 275 | 276 | 277 | package 278 | 279 | single 280 | 281 | 282 | 283 | 284 | 285 | org.apache.maven.plugins 286 | maven-compiler-plugin 287 | 3.5.1 288 | 289 | ${java.version} 290 | ${java.version} 291 | ${project.build.sourceEncoding} 292 | 293 | 294 | 295 | org.apache.maven.plugins 296 | maven-deploy-plugin 297 | 2.8.2 298 | 299 | 10 300 | 301 | 302 | 303 | org.sonatype.plugins 304 | nexus-staging-maven-plugin 305 | 1.6.7 306 | true 307 | 308 | ossrh 309 | https://oss.sonatype.org/ 310 | true 311 | 312 | 313 | 314 | org.apache.maven.plugins 315 | maven-source-plugin 316 | 3.0.0 317 | 318 | 319 | attach-sources 320 | 321 | jar-no-fork 322 | 323 | 324 | 325 | 326 | 327 | org.apache.maven.plugins 328 | maven-javadoc-plugin 329 | 2.10.3 330 | 331 | true 332 | 333 | 334 | 335 | attach-javadocs 336 | 337 | jar 338 | 339 | 340 | 341 | aggregate 342 | 343 | aggregate 344 | 345 | site 346 | 347 | 348 | 349 | 350 | maven-dependency-plugin 351 | 352 | 353 | copy-model 354 | compile 355 | 356 | copy 357 | 358 | 359 | true 360 | false 361 | true 362 | 363 | 364 | com.graphaware.integration.neo4j 365 | neo4j-tests-integration 366 | 3.0.6.1 367 | shaded 368 | true 369 | 370 | 371 | log4j 372 | log4j 373 | 1.2.17 374 | false 375 | 376 | 377 | com.sun.jersey 378 | jersey-core 379 | 1.19 380 | 381 | 382 | ${project.basedir}/neo4jextlib 383 | 384 | 385 | 386 | 387 | 388 | maven-clean-plugin 389 | 3.0.0 390 | 391 | 392 | 393 | ${project.basedir}/neo4jextlib 394 | 395 | 396 | 397 | 398 | 399 | org.apache.maven.plugins 400 | maven-surefire-plugin 401 | 2.19.1 402 | 403 | -Xmx2048m -XX:MaxPermSize=512m 404 | 405 | ${project.basedir}/neo4jextlib 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | -------------------------------------------------------------------------------- /src/main/assemblies/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | plugin 20 | 21 | zip 22 | 23 | false 24 | 25 | 26 | ${basedir}/src/main/plugin-metadata/plugin-descriptor.properties 27 | 28 | true 29 | 30 | 31 | ${basedir}/src/main/plugin-metadata/plugin-security.policy 32 | 33 | true 34 | 35 | 36 | 37 | 38 | / 39 | true 40 | true 41 | 42 | com.google.guava:guava 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/GraphAidedSearch.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2013-2016 GraphAware 4 | * 5 | * This file is part of the GraphAware Framework. 6 | * 7 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 8 | * the GNU General Public License as published by the Free Software Foundation, either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 12 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. You should have received a copy of 14 | * the GNU General Public License along with this program. If not, see 15 | * . 16 | */ 17 | package com.graphaware.es.gas; 18 | 19 | import com.graphaware.es.gas.wrap.ActionListenerWrapper; 20 | import com.graphaware.es.gas.wrap.GraphAidedSearchActionListenerWrapper; 21 | import org.elasticsearch.action.support.ActionFilter; 22 | import org.elasticsearch.action.support.ActionFilters; 23 | import org.elasticsearch.client.Client; 24 | import org.elasticsearch.cluster.ClusterService; 25 | import org.elasticsearch.common.component.AbstractLifecycleComponent; 26 | import org.elasticsearch.common.inject.Inject; 27 | import org.elasticsearch.common.settings.Settings; 28 | import org.elasticsearch.script.ScriptService; 29 | import org.elasticsearch.threadpool.ThreadPool; 30 | 31 | public class GraphAidedSearch extends AbstractLifecycleComponent { 32 | 33 | private final ActionListenerWrapper wrapper; 34 | private final ActionFilters filters; 35 | 36 | @Inject 37 | public GraphAidedSearch(final Settings settings, final Client client, final ClusterService clusterService, final ScriptService scriptService, final ThreadPool threadPool, final ActionFilters filters) { 38 | super(settings); 39 | 40 | this.filters = filters; 41 | this.wrapper = new GraphAidedSearchActionListenerWrapper(settings, clusterService, client); 42 | 43 | initializeFilters(); 44 | } 45 | 46 | private void initializeFilters() { 47 | for (final ActionFilter filter : filters.filters()) { 48 | if (filter instanceof GraphAidedSearchFilter) { 49 | ((GraphAidedSearchFilter) filter).setWrapper(wrapper); 50 | if (logger.isDebugEnabled()) { 51 | logger.debug("Set GraphAidedSearch to " + filter); 52 | } 53 | } 54 | } 55 | } 56 | 57 | @Override 58 | protected void doStart() { 59 | 60 | } 61 | 62 | @Override 63 | protected void doStop() { 64 | 65 | } 66 | 67 | @Override 68 | protected void doClose() { 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/GraphAidedSearchFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas; 17 | 18 | import com.graphaware.es.gas.wrap.ActionListenerWrapper; 19 | import com.graphaware.es.gas.wrap.CannotWrapException; 20 | import org.elasticsearch.action.ActionListener; 21 | import org.elasticsearch.action.ActionRequest; 22 | import org.elasticsearch.action.ActionResponse; 23 | import org.elasticsearch.action.search.SearchAction; 24 | import org.elasticsearch.action.search.SearchRequest; 25 | import org.elasticsearch.action.support.ActionFilter; 26 | import org.elasticsearch.action.support.ActionFilterChain; 27 | import org.elasticsearch.common.component.AbstractComponent; 28 | import org.elasticsearch.common.inject.Inject; 29 | import org.elasticsearch.common.logging.ESLogger; 30 | import org.elasticsearch.common.logging.Loggers; 31 | import org.elasticsearch.common.settings.Settings; 32 | import org.elasticsearch.tasks.Task; 33 | 34 | public class GraphAidedSearchFilter extends AbstractComponent implements ActionFilter { 35 | 36 | private static final int DEFAULT_FILTER_ORDER = 10; 37 | 38 | private static final String FILTER_ORDER_KEY_NAME = "indices.graphaware.filter.order"; 39 | 40 | protected final ESLogger logger; 41 | 42 | private final int order; 43 | 44 | private ActionListenerWrapper wrapper; 45 | 46 | @Inject 47 | public GraphAidedSearchFilter(final Settings settings) { 48 | super(settings); 49 | logger = Loggers.getLogger(GraphAidedSearchFilter.class.getName(), settings); 50 | order = settings.getAsInt(FILTER_ORDER_KEY_NAME, DEFAULT_FILTER_ORDER); 51 | } 52 | 53 | public void setWrapper(ActionListenerWrapper wrapper) { 54 | this.wrapper = wrapper; 55 | } 56 | 57 | @Override 58 | public int order() { 59 | return order; 60 | } 61 | 62 | @Override 63 | public void apply(Task task, String action, ActionRequest request, ActionListener listener, ActionFilterChain chain) { 64 | if (SearchAction.INSTANCE.name().equals(action)) { 65 | try { 66 | listener = wrapper.wrap((SearchRequest) request, listener); 67 | } catch (CannotWrapException e) { 68 | //that's OK, will use the original unwrapped one and perform no Graph-Aided Search 69 | } 70 | } 71 | 72 | chain.proceed(task, action, request, listener); 73 | } 74 | 75 | @Override 76 | public void apply(final String action, final ActionResponse response, final ActionListener listener, final ActionFilterChain chain) { 77 | chain.proceed(action, response, listener); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/GraphAidedSearchModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas; 17 | 18 | import org.elasticsearch.common.inject.AbstractModule; 19 | 20 | public class GraphAidedSearchModule extends AbstractModule { 21 | 22 | @Override 23 | protected void configure() { 24 | bind(GraphAidedSearch.class).asEagerSingleton(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/GraphAidedSearchPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas; 17 | 18 | import org.elasticsearch.action.ActionModule; 19 | import org.elasticsearch.cluster.ClusterModule; 20 | import org.elasticsearch.cluster.settings.Validator; 21 | import org.elasticsearch.common.inject.Module; 22 | import org.elasticsearch.plugins.Plugin; 23 | 24 | import java.util.Collection; 25 | import java.util.Collections; 26 | 27 | import static com.graphaware.es.gas.wrap.GraphAidedSearchActionListenerWrapper.*; 28 | 29 | public class GraphAidedSearchPlugin extends Plugin { 30 | 31 | @Override 32 | public String name() { 33 | return "GraphAidedSearchPlugin"; 34 | } 35 | 36 | @Override 37 | public String description() { 38 | return "GraphAware Graph-Aided Search Plugin for Neo4j."; 39 | } 40 | 41 | public void onModule(final ActionModule module) { 42 | module.registerFilter(GraphAidedSearchFilter.class); 43 | } 44 | 45 | public void onModule(final ClusterModule module) { 46 | module.registerIndexDynamicSetting(INDEX_GA_ES_NEO4J_ENABLED, Validator.BOOLEAN); 47 | module.registerIndexDynamicSetting(INDEX_GA_ES_NEO4J_HOST, Validator.EMPTY); 48 | module.registerIndexDynamicSetting(INDEX_GA_ES_NEO4J_USER, Validator.EMPTY); 49 | module.registerIndexDynamicSetting(INDEX_GA_ES_NEO4J_PWD, Validator.EMPTY); 50 | } 51 | 52 | @Override 53 | public Collection nodeModules() { 54 | return Collections.singleton(new GraphAidedSearchModule()); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/annotation/SearchBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.annotation; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | @Retention(RetentionPolicy.RUNTIME) 24 | @Target(ElementType.TYPE) 25 | public @interface SearchBooster { 26 | 27 | String name(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/annotation/SearchFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.annotation; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | @Retention(RetentionPolicy.RUNTIME) 24 | @Target(ElementType.TYPE) 25 | public @interface SearchFilter { 26 | 27 | String name(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/booster/SearchResultBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.booster; 17 | 18 | import com.graphaware.es.gas.modifier.SearchResultModifier; 19 | 20 | public interface SearchResultBooster extends SearchResultModifier { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/booster/SearchResultCypherBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.booster; 17 | 18 | import com.graphaware.es.gas.annotation.SearchBooster; 19 | import com.graphaware.es.gas.cypher.CypherEndPoint; 20 | import com.graphaware.es.gas.cypher.CypherEndPointBuilder; 21 | import com.graphaware.es.gas.cypher.CypherResult; 22 | import com.graphaware.es.gas.cypher.ResultRow; 23 | import com.graphaware.es.gas.domain.ExternalResult; 24 | import com.graphaware.es.gas.domain.IndexInfo; 25 | import com.graphaware.es.gas.util.NumberUtil; 26 | import com.graphaware.es.gas.util.UrlUtil; 27 | import org.elasticsearch.common.logging.ESLogger; 28 | import org.elasticsearch.common.logging.Loggers; 29 | import org.elasticsearch.common.settings.Settings; 30 | 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | import java.util.Set; 34 | 35 | import static com.graphaware.es.gas.domain.ClauseConstants.*; 36 | import static com.graphaware.es.gas.util.ParamUtil.extractParameter; 37 | 38 | @SearchBooster(name = "SearchResultCypherBooster") 39 | public class SearchResultCypherBooster extends SearchResultExternalBooster { 40 | 41 | private final ESLogger logger; 42 | private CypherEndPoint cypherEndPoint; 43 | 44 | private String cypherQuery; 45 | private String scoreResultName; 46 | private String idResultName; 47 | 48 | public SearchResultCypherBooster(Settings settings, IndexInfo indexInfo) { 49 | super(settings, indexInfo); 50 | this.logger = Loggers.getLogger(IndexInfo.INDEX_LOGGER_NAME, settings); 51 | } 52 | 53 | @Override 54 | protected void extendedParseRequest(Map extParams) { 55 | cypherQuery = extractParameter(QUERY, extParams); 56 | scoreResultName = extractParameter(SCORE_NAME, extParams, DEFAULT_SCORE_RESULT_NAME); 57 | idResultName = extractParameter(IDENTIFIER, extParams, DEFAULT_ID_RESULT_NAME); 58 | String protocol = extParams.containsKey(PROTOCOL) ? String.valueOf(extParams.get(PROTOCOL)) : DEFAULT_PROTOCOL; 59 | cypherEndPoint = createCypherEndPoint(protocol, getSettings()); 60 | } 61 | 62 | @Override 63 | protected Map externalDoReorder(Set keySet) { 64 | logger.debug("Query cypher for: " + keySet); 65 | return getExternalResults(keySet); 66 | } 67 | 68 | protected Map getExternalResults(Set keySet) { 69 | CypherResult externalResult = cypherEndPoint.executeCypher(cypherQuery, getParameters(keySet)); 70 | Map results = new HashMap<>(); 71 | for (ResultRow resultRow : externalResult.getRows()) { 72 | checkResultRow(resultRow); 73 | results.put(String.valueOf(resultRow.get(getIdResultName())), new ExternalResult(String.valueOf(resultRow.get(getIdResultName())), NumberUtil.getFloat(resultRow.get(getScoreResultName())))); 74 | } 75 | 76 | return results; 77 | } 78 | 79 | public HashMap getParameters(Set resultKeySet) { 80 | HashMap parameters = new HashMap<>(); 81 | parameters.put("ids", resultKeySet); 82 | 83 | return parameters; 84 | } 85 | 86 | public String getEndpoint(String serverUrl) { 87 | return UrlUtil.buildUrlFromParts(serverUrl); 88 | } 89 | 90 | public String getScoreResultName() { 91 | return null != scoreResultName ? scoreResultName : DEFAULT_SCORE_RESULT_NAME; 92 | } 93 | 94 | public String getIdResultName() { 95 | return null != idResultName ? idResultName : DEFAULT_ID_RESULT_NAME; 96 | } 97 | 98 | protected void checkResultRow(ResultRow resultRow) { 99 | if (!resultRow.getValues().containsKey(getIdResultName())) { 100 | dispatchInvalidResultException(getIdResultName()); 101 | } 102 | if (!resultRow.getValues().containsKey(getScoreResultName())) { 103 | dispatchInvalidResultException(getScoreResultName()); 104 | } 105 | } 106 | 107 | private void dispatchInvalidResultException(String missingKey) { 108 | throw new RuntimeException(String.format("The cypher query result must contain the %s column name", missingKey)); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/booster/SearchResultExternalBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.booster; 17 | 18 | import com.graphaware.es.gas.cypher.CypherSettingsReader; 19 | import com.graphaware.es.gas.domain.ExternalResult; 20 | import com.graphaware.es.gas.domain.IndexInfo; 21 | import com.graphaware.es.gas.util.NumberUtil; 22 | import org.elasticsearch.common.settings.Settings; 23 | import org.elasticsearch.search.internal.InternalSearchHit; 24 | import org.elasticsearch.search.internal.InternalSearchHits; 25 | 26 | import java.util.*; 27 | 28 | import static com.graphaware.es.gas.domain.ClauseConstants.*; 29 | import static com.graphaware.es.gas.util.ParamUtil.*; 30 | import static com.graphaware.es.gas.wrap.GraphAidedSearchActionListenerWrapper.*; 31 | 32 | public abstract class SearchResultExternalBooster extends CypherSettingsReader implements SearchResultBooster { 33 | 34 | protected static final String DEFAULT_SCORE_OPERATOR = MULTIPLY; 35 | protected static final String DEFAULT_PROTOCOL = "http"; 36 | static final String DEFAULT_SCORE_RESULT_NAME = "score"; 37 | static final String DEFAULT_ID_RESULT_NAME = "id"; 38 | 39 | 40 | private int maxResultSize = -1; 41 | 42 | private int size; 43 | private int from; 44 | 45 | protected String composeScoreOperator; 46 | 47 | public SearchResultExternalBooster(Settings settings, IndexInfo indexSettings) { 48 | super(settings, indexSettings); 49 | } 50 | 51 | @Override 52 | public final void parseRequest(Map sourceAsMap) { 53 | size = NumberUtil.getInt(sourceAsMap.get(SIZE), 10); 54 | from = NumberUtil.getInt(sourceAsMap.get(FROM), 0); 55 | 56 | Map extParams = (Map) sourceAsMap.get(GAS_BOOSTER_CLAUSE); 57 | if (extParams != null) { 58 | maxResultSize = NumberUtil.getInt(extParams.get(MAX_RESULT_SIZE), getMaxResultWindow()); 59 | composeScoreOperator = extractParameter(OPERATOR, extParams, DEFAULT_SCORE_OPERATOR); 60 | extendedParseRequest(extParams); 61 | validateOperator(); 62 | } 63 | if (maxResultSize > 0) { 64 | sourceAsMap.put(SIZE, maxResultSize); 65 | } 66 | sourceAsMap.put(FROM, 0); 67 | } 68 | 69 | @Override 70 | public InternalSearchHits modify(final InternalSearchHits hits) { 71 | final InternalSearchHit[] searchHits = hits.internalHits(); 72 | Map hitMap = new HashMap<>(); 73 | for (InternalSearchHit hit : searchHits) { 74 | hitMap.put(hit.getId(), hit); 75 | } 76 | int totalHitsSize = hitMap.keySet().size(); 77 | Map remoteScore = externalDoReorder(hitMap.keySet()); 78 | final int arraySize = (size + from) < searchHits.length ? size 79 | : (searchHits.length - from) > 0 ? (searchHits.length - from) : 0; 80 | if (arraySize == 0) { 81 | return new InternalSearchHits(new InternalSearchHit[0], 0, 0); 82 | } 83 | 84 | final int totalSize = arraySize + from; 85 | List newSearchHits = new ArrayList<>(totalSize); 86 | float maxScore = -1; 87 | for (Map.Entry item : hitMap.entrySet()) { 88 | ExternalResult remoteResult = remoteScore.get(item.getKey()); 89 | if (remoteResult != null) { 90 | float newScore = composeScore(item.getValue().score(), remoteResult.getScore()); 91 | if (maxScore < newScore) { 92 | maxScore = newScore; 93 | } 94 | item.getValue().score(newScore); 95 | } 96 | int k = 0; 97 | while (newSearchHits.size() > 0 98 | && k < newSearchHits.size() 99 | && newSearchHits.get(k) != null 100 | && newSearchHits.get(k).score() > item.getValue().score() 101 | && k < totalSize) { 102 | k++; 103 | } 104 | if (k < totalSize) { 105 | newSearchHits.add(k, item.getValue()); 106 | } 107 | if (newSearchHits.size() > totalSize) { 108 | newSearchHits.remove(totalSize); 109 | } 110 | } 111 | if (from > 0) { 112 | int k = 0; 113 | while (k < from) { 114 | newSearchHits.remove(0); 115 | k++; 116 | } 117 | } 118 | return new InternalSearchHits(newSearchHits.toArray(new InternalSearchHit[arraySize]), totalHitsSize, 119 | maxScore); 120 | } 121 | 122 | protected float composeScore(float esScore, float extScore) { 123 | switch (getComposeScoreOperator()) { 124 | case MULTIPLY: 125 | return esScore * extScore; 126 | case DIVIDE: 127 | return esScore / extScore; 128 | case PLUS: 129 | return esScore + extScore; 130 | case MINUS: 131 | return esScore - extScore; 132 | case REPLACE: 133 | return extScore; 134 | default: 135 | return esScore; 136 | } 137 | 138 | } 139 | 140 | public int getSize() { 141 | return size; 142 | } 143 | 144 | public int getFrom() { 145 | return from; 146 | } 147 | 148 | public int getMaxResultSize() { 149 | return maxResultSize; 150 | } 151 | 152 | protected abstract Map externalDoReorder(Set keySet); 153 | 154 | protected void extendedParseRequest(Map extParams) { 155 | 156 | } 157 | 158 | protected void validateOperator() { 159 | Set validOperators = new HashSet<>(); 160 | validOperators.add(MULTIPLY); 161 | validOperators.add(PLUS); 162 | validOperators.add(MINUS); 163 | validOperators.add(DIVIDE); 164 | validOperators.add(REPLACE); 165 | 166 | String operator = getComposeScoreOperator(); 167 | 168 | if (!validOperators.contains(operator)) { 169 | throw new IllegalArgumentException("Operator \"" + operator + "\" is not valid"); 170 | } 171 | } 172 | 173 | protected String getComposeScoreOperator() { 174 | return composeScoreOperator != null ? composeScoreOperator : DEFAULT_SCORE_OPERATOR; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/booster/SearchResultNeo4jBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.booster; 17 | 18 | import com.graphaware.es.gas.annotation.SearchBooster; 19 | import com.graphaware.es.gas.domain.ExternalResult; 20 | import com.graphaware.es.gas.domain.IndexInfo; 21 | import com.graphaware.es.gas.util.UrlUtil; 22 | import com.sun.jersey.api.client.Client; 23 | import com.sun.jersey.api.client.ClientResponse; 24 | import com.sun.jersey.api.client.GenericType; 25 | import com.sun.jersey.api.client.WebResource; 26 | import com.sun.jersey.api.client.config.ClientConfig; 27 | import com.sun.jersey.api.client.config.DefaultClientConfig; 28 | import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 29 | import org.elasticsearch.common.logging.ESLogger; 30 | import org.elasticsearch.common.logging.Loggers; 31 | import org.elasticsearch.common.settings.Settings; 32 | 33 | import javax.ws.rs.core.MediaType; 34 | import java.util.HashMap; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.Set; 38 | 39 | import com.sun.jersey.api.client.ClientHandlerException; 40 | import com.sun.jersey.api.client.UniformInterfaceException; 41 | import com.sun.jersey.core.util.MultivaluedMapImpl; 42 | import javax.ws.rs.core.HttpHeaders; 43 | import javax.ws.rs.core.MultivaluedMap; 44 | 45 | import static com.graphaware.es.gas.domain.ClauseConstants.*; 46 | 47 | @SearchBooster(name = "SearchResultNeo4jBooster") 48 | public class SearchResultNeo4jBooster extends SearchResultExternalBooster { 49 | 50 | private static final String DEFAULT_KEY_PROPERTY = "uuid"; 51 | private static final String DEFAULT_REST_ENDPOINT = "/graphaware/recommendation/filter"; 52 | 53 | private String boosterEndpoint = null; 54 | private final ESLogger logger; 55 | private String targetId; 56 | private String keyProperty; 57 | 58 | public SearchResultNeo4jBooster(Settings settings, IndexInfo indexSettings) { 59 | super(settings, indexSettings); 60 | this.logger = Loggers.getLogger(IndexInfo.INDEX_LOGGER_NAME, settings); 61 | } 62 | 63 | @Override 64 | protected Map externalDoReorder(Set keySet) { 65 | if (logger.isDebugEnabled()) { 66 | logger.debug("External Neo4j booster for : " + keySet); 67 | logger.debug("Call: " + getEndpoint()); 68 | } 69 | return getReorderedResults(getExternalResults(keySet)); 70 | 71 | } 72 | 73 | public Map getReorderedResults(List externalResults) { 74 | HashMap results = new HashMap<>(); 75 | for (ExternalResult item : externalResults) { 76 | results.put(item.getObjectId(), item); 77 | } 78 | 79 | return results; 80 | } 81 | 82 | public List getExternalResults(Set keySet) { 83 | 84 | ClientConfig cfg = new DefaultClientConfig(); 85 | cfg.getClasses().add(JacksonJsonProvider.class); 86 | WebResource resource = Client.create(cfg).resource(getEndpoint()); 87 | WebResource.Builder resBuilder = resource.accept(MediaType.APPLICATION_JSON); 88 | setHeader(resBuilder); 89 | ClientResponse response = null; 90 | List externalResults = null; 91 | 92 | try { 93 | response = resBuilder.post(ClientResponse.class, getParameters(keySet)); 94 | GenericType> type = new GenericType>() { 95 | }; 96 | externalResults = response.getEntity(type); 97 | } 98 | catch (UniformInterfaceException | ClientHandlerException ex) { 99 | throw new RuntimeException("Error while connecting to neo4j host", ex); 100 | } 101 | finally { 102 | if (response != null) 103 | response.close(); 104 | } 105 | if (externalResults == null) { 106 | logger.error("Null results from neo4j endpoint"); 107 | throw new RuntimeException("Null results from neo4j endpoint. No results returned"); 108 | } 109 | return externalResults; 110 | } 111 | 112 | private void setHeader(WebResource.Builder resBuilder) { 113 | Map headers = new HashMap<>(); 114 | if (null != getNeo4jPassword()) { 115 | headers.put(HttpHeaders.AUTHORIZATION, UrlUtil.getAuthorizationHeaderValue(getNeo4jUsername(), getNeo4jPassword())); 116 | } 117 | for (String k : headers.keySet()) { 118 | resBuilder.header(k, headers.get(k)); 119 | } 120 | } 121 | 122 | @Override 123 | protected void extendedParseRequest(Map extParams) { 124 | targetId = extParams.get(TARGET); 125 | keyProperty = extParams.get(KEY_PROPERTY) != null ? extParams.get(KEY_PROPERTY) : DEFAULT_KEY_PROPERTY; 126 | boosterEndpoint = extParams.get(NEO4J_ENDPOINT); 127 | } 128 | 129 | public MultivaluedMap getParameters(Set keySet) { 130 | MultivaluedMap param = new MultivaluedMapImpl(); 131 | param.add("limit", String.valueOf(Integer.MAX_VALUE)); 132 | param.add("from", String.valueOf(getFrom())); 133 | param.add("keyProperty", getKeyProperty()); 134 | param.add("ids", implodeKeySet(keySet)); 135 | return param; 136 | } 137 | 138 | protected String getTargetId() { 139 | return targetId; 140 | } 141 | 142 | public String getKeyProperty() { 143 | return keyProperty; 144 | } 145 | 146 | public String getEndpoint() { 147 | String boosterUrl = null != boosterEndpoint ? boosterEndpoint : DEFAULT_REST_ENDPOINT; 148 | 149 | return UrlUtil.buildUrlFromParts(getNeo4jHost(), boosterUrl, targetId); 150 | } 151 | 152 | public String implodeKeySet(Set keySet) { 153 | boolean isFirst = true; 154 | String ids = ""; 155 | for (String id : keySet) { 156 | if (!isFirst) { 157 | ids = ids.concat(","); 158 | } 159 | isFirst = false; 160 | ids = ids.concat(id); 161 | } 162 | 163 | return ids; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/CypherBoltHttpEndPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import java.util.HashMap; 19 | 20 | import org.elasticsearch.common.settings.Settings; 21 | import org.neo4j.driver.v1.AuthTokens; 22 | import org.neo4j.driver.v1.Config; 23 | import static org.neo4j.driver.v1.Config.EncryptionLevel.NONE; 24 | import org.neo4j.driver.v1.Driver; 25 | import org.neo4j.driver.v1.GraphDatabase; 26 | import org.neo4j.driver.v1.Record; 27 | import org.neo4j.driver.v1.Session; 28 | import org.neo4j.driver.v1.StatementResult; 29 | import org.neo4j.driver.v1.Value; 30 | import org.neo4j.driver.v1.util.Pair; 31 | 32 | public class CypherBoltHttpEndPoint extends CypherEndPoint { 33 | 34 | private boolean encryption = true; 35 | 36 | CypherBoltHttpEndPoint(Settings settings, String neo4jUrl, String neo4jUsername, String neo4jPassword, boolean encryption) { 37 | super(settings, neo4jUrl, neo4jUsername, neo4jPassword); 38 | this.encryption = encryption; 39 | } 40 | 41 | public CypherResult executeCypher(String cypherQuery) { 42 | return executeCypher(cypherQuery, new HashMap()); 43 | } 44 | 45 | @Override 46 | public CypherResult executeCypher(String cypherQuery, HashMap parameters) { 47 | try { 48 | Driver driver; 49 | if (encryption) { 50 | if (getNeo4jUsername() != null) { 51 | driver = GraphDatabase.driver(getNeo4jHost(), AuthTokens.basic(getNeo4jUsername(), getNeo4jPassword())); 52 | } else { 53 | driver = GraphDatabase.driver(getNeo4jHost()); 54 | } 55 | } else { 56 | if (getNeo4jUsername() != null) { 57 | driver = GraphDatabase.driver(getNeo4jHost(), AuthTokens.basic(getNeo4jUsername(), getNeo4jPassword()), Config.build().withEncryptionLevel(NONE).toConfig()); 58 | } else { 59 | driver = GraphDatabase.driver(getNeo4jHost(), Config.build().withEncryptionLevel(NONE).toConfig()); 60 | } 61 | } 62 | Session session = driver.session(); 63 | StatementResult response = session.run(cypherQuery, parameters); 64 | return buildResult(response); 65 | } catch (Exception ex) { 66 | throw new RuntimeException(ex); 67 | } 68 | } 69 | 70 | private CypherResult buildResult(StatementResult response) { 71 | CypherResult result = new CypherResult(); 72 | while (response.hasNext()) { 73 | Record record = response.next(); 74 | ResultRow resultRow = new ResultRow(); 75 | for (Pair fieldInRecord : record.fields()) { 76 | resultRow.add(fieldInRecord.key(), fieldInRecord.value()); 77 | } 78 | result.addRow(resultRow); 79 | } 80 | return result; 81 | } 82 | 83 | @Override 84 | public CypherResult executeCypher(HashMap headers, String query, HashMap parameters) { 85 | return executeCypher(query, parameters); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/CypherEndPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import com.graphaware.es.gas.domain.IndexInfo; 19 | import java.util.HashMap; 20 | import org.elasticsearch.common.logging.ESLogger; 21 | import org.elasticsearch.common.logging.Loggers; 22 | import org.elasticsearch.common.settings.Settings; 23 | 24 | public abstract class CypherEndPoint { 25 | 26 | protected final ESLogger logger; 27 | private final String neo4jHost; 28 | private final String neo4jPassword; 29 | private final String neo4jUsername; 30 | 31 | 32 | public CypherEndPoint(Settings settings, String neo4jHost) { 33 | this(settings, neo4jHost, null, null); 34 | } 35 | 36 | public CypherEndPoint(Settings settings, String neo4jHost, String neo4jUsername, String neo4jPassword) { 37 | this.neo4jHost = neo4jHost; 38 | this.neo4jUsername = neo4jUsername; 39 | this.neo4jPassword = neo4jPassword; 40 | if (settings != null) { 41 | this.logger = Loggers.getLogger(IndexInfo.INDEX_LOGGER_NAME, settings); 42 | } else { 43 | this.logger = Loggers.getLogger(IndexInfo.INDEX_LOGGER_NAME, Settings.EMPTY); 44 | } 45 | } 46 | 47 | public String getNeo4jPassword() { 48 | return neo4jPassword; 49 | } 50 | 51 | public String getNeo4jUsername() { 52 | return neo4jUsername; 53 | } 54 | 55 | public String getNeo4jHost() { 56 | return neo4jHost; 57 | } 58 | 59 | public abstract CypherResult executeCypher(String query, HashMap parameters); 60 | 61 | public abstract CypherResult executeCypher(HashMap headers, String query, HashMap parameters); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/CypherEndPointBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import org.elasticsearch.common.settings.Settings; 19 | 20 | public class CypherEndPointBuilder { 21 | 22 | private Settings settings; 23 | private String neo4jHost; 24 | private String neo4jBoltHost; 25 | private final CypherEndPointType protocol; 26 | private String neo4jUsername; 27 | private String neo4jPassword; 28 | private boolean encryption = true; 29 | 30 | public CypherEndPointBuilder(CypherEndPointType protocol) { 31 | this.protocol = protocol; 32 | } 33 | 34 | public CypherEndPointBuilder(String protocol) { 35 | this.protocol = CypherEndPointType.getEnum(protocol); 36 | } 37 | 38 | 39 | public CypherEndPointBuilder settings(Settings settings) { 40 | this.settings = settings; 41 | return this; 42 | } 43 | 44 | public CypherEndPointBuilder neo4jHostname(String neo4jHost) { 45 | this.neo4jHost = neo4jHost; 46 | return this; 47 | } 48 | 49 | public CypherEndPointBuilder neo4jBoltHostname(String neo4jBoltHost) { 50 | this.neo4jBoltHost = neo4jBoltHost; 51 | return this; 52 | } 53 | 54 | public CypherEndPointBuilder username(String neo4jUsername) { 55 | this.neo4jUsername = neo4jUsername; 56 | return this; 57 | } 58 | 59 | public CypherEndPointBuilder password(String neo4jPassword) { 60 | this.neo4jPassword = neo4jPassword; 61 | return this; 62 | } 63 | 64 | public CypherEndPointBuilder encryption(boolean encryption) { 65 | this.encryption = encryption; 66 | return this; 67 | } 68 | 69 | public CypherEndPoint build() { 70 | checkNeo4jHost(); 71 | switch (protocol) { 72 | case HTTP: 73 | return new CypherHttpEndPoint(settings, 74 | neo4jHost, 75 | neo4jUsername, 76 | neo4jPassword 77 | ); 78 | case BOLT: 79 | return new CypherBoltHttpEndPoint(settings, 80 | neo4jBoltHost, 81 | neo4jUsername, 82 | neo4jPassword, 83 | encryption); 84 | } 85 | throw new RuntimeException("Type " + protocol + " not supported"); 86 | } 87 | 88 | private void checkNeo4jHost() { 89 | if (neo4jHost == null) 90 | throw new RuntimeException("No neo4j hosts specified for http connection in the index settings"); 91 | } 92 | 93 | public enum CypherEndPointType { 94 | HTTP("http"), 95 | BOLT("bolt"); 96 | 97 | String name; 98 | 99 | CypherEndPointType(String name) { 100 | this.name = name; 101 | } 102 | 103 | public String getName() { 104 | return name; 105 | } 106 | 107 | public static CypherEndPointType getEnum(String value) { 108 | if (HTTP.getName().equalsIgnoreCase(value)) { 109 | return HTTP; 110 | } else if (BOLT.getName().equalsIgnoreCase(value)) { 111 | return BOLT; 112 | } 113 | throw new RuntimeException("Type " + value + " not supported"); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/CypherHttpEndPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import com.graphaware.es.gas.util.UrlUtil; 19 | import com.sun.jersey.api.client.Client; 20 | import com.sun.jersey.api.client.ClientResponse; 21 | import com.sun.jersey.api.client.GenericType; 22 | import com.sun.jersey.api.client.WebResource; 23 | import com.sun.jersey.api.client.config.ClientConfig; 24 | import com.sun.jersey.api.client.config.DefaultClientConfig; 25 | import java.io.IOException; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | import javax.ws.rs.core.HttpHeaders; 30 | import javax.ws.rs.core.MediaType; 31 | import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 32 | import org.codehaus.jackson.map.ObjectMapper; 33 | import org.elasticsearch.common.settings.Settings; 34 | 35 | public class CypherHttpEndPoint extends CypherEndPoint { 36 | 37 | private static final String CYPHER_ENDPOINT = "/db/data/transaction/commit"; 38 | private static final String CYPHER_RESPONSE_RESULTS_FIELD = "results"; 39 | private static final String CYPHER_RESPONSE_DATA_FIELD = "data"; 40 | private static final String CYPHER_RESPONSE_COLUMNS_FIELD = "columns"; 41 | private static final String CYPHER_RESPONSE_ERRORS_FIELD = "errors"; 42 | private static final String CYPHER_RESPONSE_ROW_FIELD = "row"; 43 | 44 | private final ClientConfig cfg; 45 | private final StringBuilder stringBuilder; 46 | private final ObjectMapper mapper; 47 | 48 | public CypherHttpEndPoint(Settings settings, String neo4jUrl, String neo4jUsername, String neo4jPassword) { 49 | super(settings, neo4jUrl, neo4jUsername, neo4jPassword); 50 | cfg = new DefaultClientConfig(); 51 | cfg.getClasses().add(JacksonJsonProvider.class); 52 | stringBuilder = new StringBuilder(); 53 | mapper = new ObjectMapper(); 54 | } 55 | 56 | public String buildCypherQuery(String cypherQuery) { 57 | return buildCypherQuery(cypherQuery, new HashMap()); 58 | } 59 | 60 | @Override 61 | public CypherResult executeCypher(String query, HashMap parameters) { 62 | HashMap headers = new HashMap<>(); 63 | 64 | return executeCypher(headers, query, parameters); 65 | } 66 | 67 | @Override 68 | public CypherResult executeCypher(HashMap headers, String query, HashMap parameters) { 69 | String jsonBody = buildCypherQuery(query, parameters); 70 | String cypherEndpoint = UrlUtil.buildUrlFromParts(getNeo4jHost(), CYPHER_ENDPOINT); 71 | Map response = post(cypherEndpoint, headers, jsonBody); 72 | checkErrors(response); 73 | 74 | return buildCypherResult(response); 75 | } 76 | 77 | public CypherResult buildCypherResult(Map response) { 78 | Map res = (Map) ((List) response.get(CYPHER_RESPONSE_RESULTS_FIELD)).get(0); 79 | List rows = (List) res.get(CYPHER_RESPONSE_DATA_FIELD); 80 | List columns = (List) res.get(CYPHER_RESPONSE_COLUMNS_FIELD); 81 | int k = 0; 82 | Map columnsMap = new HashMap<>(); 83 | for (String c : columns) { 84 | columnsMap.put(c, k); 85 | ++k; 86 | } 87 | 88 | CypherResult result = new CypherResult(); 89 | for (Map r : rows) { 90 | ResultRow resultRow = new ResultRow(); 91 | List row = (List) r.get(CYPHER_RESPONSE_ROW_FIELD); 92 | for (String key : columns) { 93 | resultRow.add(key, row.get(columnsMap.get(key))); 94 | } 95 | result.addRow(resultRow); 96 | } 97 | 98 | return result; 99 | } 100 | 101 | public String buildCypherQuery(String cypherQuery, Map parameters) { 102 | try { 103 | stringBuilder.append("{\"statements\" : ["); 104 | stringBuilder.append("{\"statement\" : \"").append(cypherQuery).append("\""); 105 | if (parameters.size() > 0) { 106 | stringBuilder.append(",").append("\"parameters\":"); 107 | stringBuilder.append(mapper.writeValueAsString(parameters)); 108 | } 109 | stringBuilder.append("}]}"); 110 | 111 | return stringBuilder.toString(); 112 | } catch (IOException e) { 113 | throw new RuntimeException("Unable to build the Cypher query : " + e.getMessage()); 114 | } 115 | } 116 | 117 | public Map post(String url, HashMap headers, String json) { 118 | if (!headers.containsKey(HttpHeaders.AUTHORIZATION) && null != getNeo4jPassword()) { 119 | headers.put(HttpHeaders.AUTHORIZATION, UrlUtil.getAuthorizationHeaderValue(getNeo4jUsername(), getNeo4jPassword())); 120 | } 121 | WebResource resource = Client.create(cfg).resource(url); 122 | WebResource.Builder builder = resource.accept(MediaType.APPLICATION_JSON) 123 | .type(MediaType.APPLICATION_JSON) 124 | .entity(json); 125 | for (String k : headers.keySet()) { 126 | builder.header(k, headers.get(k)); 127 | } 128 | ClientResponse response = null; 129 | Map results = null; 130 | try { 131 | response = builder.post(ClientResponse.class); 132 | GenericType> type = new GenericType>() { 133 | }; 134 | results = response.getEntity(type); 135 | } finally { 136 | if (response != null) 137 | response.close(); 138 | } 139 | 140 | if (logger.isDebugEnabled()) { 141 | try { 142 | ObjectMapper oWrapper = ObjectMapper.class.newInstance(); 143 | logger.debug(oWrapper.writeValueAsString(results)); 144 | } catch (InstantiationException | IllegalAccessException | IOException e) { 145 | // 146 | } 147 | } 148 | if (results == null) { 149 | logger.error("Null results from cypher endpoint for json:\n" + json); 150 | throw new RuntimeException("Cypher Execution Error. No results returned"); 151 | } 152 | 153 | return results; 154 | } 155 | 156 | private void checkErrors(Map results) { 157 | @SuppressWarnings("unchecked") 158 | List> errors = (List) results.get(CYPHER_RESPONSE_ERRORS_FIELD); 159 | if (errors.size() > 0) { 160 | throw new RuntimeException("Cypher Execution Error, message is : " + errors.get(0).toString()); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/CypherResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | public class CypherResult { 22 | 23 | private List rows = new ArrayList<>(); 24 | 25 | public void addRow(ResultRow resultRow) { 26 | rows.add(resultRow); 27 | } 28 | 29 | public List getRows() { 30 | return rows; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/CypherSettingsReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import com.graphaware.es.gas.domain.IndexInfo; 19 | import org.elasticsearch.common.settings.Settings; 20 | 21 | public class CypherSettingsReader { 22 | private final String neo4jHost; 23 | private final String neo4jBoltHost; 24 | private final String neo4jUsername; 25 | private final String neo4jPassword; 26 | private final int maxResultWindow; 27 | private final Settings settings; 28 | private final boolean secureBolt; 29 | 30 | public CypherSettingsReader(Settings settings, IndexInfo indexSettings) { 31 | this.settings = settings; 32 | this.neo4jHost = indexSettings.getNeo4jHost(); 33 | this.neo4jBoltHost = indexSettings.getNeo4jBoltHost(); 34 | this.neo4jUsername = indexSettings.getNeo4jUsername(); 35 | this.neo4jPassword = indexSettings.getNeo4jPassword(); 36 | this.maxResultWindow = indexSettings.getMaxResultWindow(); 37 | this.secureBolt = indexSettings.isSecureBolt(); 38 | } 39 | 40 | protected CypherEndPoint createCypherEndPoint(String protocol, Settings settings) { 41 | return new CypherEndPointBuilder(protocol) 42 | .settings(settings) 43 | .neo4jHostname(getNeo4jHost()) 44 | .neo4jBoltHostname(getNeo4jBoltHost()) 45 | .username(getNeo4jUsername()) 46 | .password(getNeo4jPassword()) 47 | .encryption(isSecureBolt()) 48 | .build(); 49 | } 50 | 51 | public String getNeo4jHost() { 52 | return neo4jHost; 53 | } 54 | 55 | public String getNeo4jUsername() { 56 | return neo4jUsername; 57 | } 58 | 59 | public String getNeo4jPassword() { 60 | return neo4jPassword; 61 | } 62 | 63 | public int getMaxResultWindow() { 64 | return maxResultWindow; 65 | } 66 | 67 | public Settings getSettings() { 68 | return settings; 69 | } 70 | 71 | public boolean isSecureBolt() { 72 | return secureBolt; 73 | } 74 | 75 | public String getNeo4jBoltHost() { 76 | return neo4jBoltHost; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/cypher/ResultRow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.cypher; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | public class ResultRow { 22 | 23 | private Map items = new HashMap<>(); 24 | 25 | public void add(String columnKey, Object item) { 26 | items.put(columnKey, item); 27 | } 28 | 29 | public Object get(String columnKey) { 30 | return items.get(columnKey); 31 | } 32 | 33 | public Map getValues() { 34 | return items; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/domain/ClauseConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.domain; 18 | 19 | public final class ClauseConstants { 20 | 21 | public static final String QUERY = "query"; 22 | public static final String SCORE_NAME = "scoreName"; 23 | public static final String IDENTIFIER = "identifier"; 24 | public static final String PROTOCOL = "protocol"; 25 | 26 | 27 | public static final String SIZE = "size"; 28 | public static final String FROM = "from"; 29 | public static final String MAX_RESULT_SIZE = "maxResultSize"; 30 | public static final String OPERATOR = "operator"; 31 | public static final String MULTIPLY = "*"; 32 | public static final String DIVIDE = "/"; 33 | public static final String PLUS = "+"; 34 | public static final String MINUS = "-"; 35 | public static final String REPLACE = "replace"; 36 | 37 | public static final String TARGET = "target"; 38 | public static final String KEY_PROPERTY = "keyProperty"; 39 | public static final String NEO4J_ENDPOINT = "neo4j.endpoint"; 40 | public static final String LIMIT = "limit"; 41 | public static final String IDS = "ids"; 42 | 43 | public static final String EXCLUDE = "exclude"; 44 | public static final String TRUE = "true"; 45 | public static final String QUERY_BINARY = "query_binary"; 46 | public static final String NAME = "name"; 47 | 48 | private ClauseConstants() { 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/domain/ExternalResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.domain; 17 | 18 | public class ExternalResult { 19 | 20 | private long nodeId; 21 | private String objectId; 22 | //private String item; 23 | private float score; 24 | 25 | public ExternalResult() { 26 | 27 | } 28 | public ExternalResult(String objectId, float score) { 29 | this.objectId = objectId; 30 | this.score = score; 31 | } 32 | 33 | public long getNodeId() { 34 | return nodeId; 35 | } 36 | 37 | public void setNodeId(long nodeId) { 38 | this.nodeId = nodeId; 39 | } 40 | 41 | public String getObjectId() { 42 | return objectId; 43 | } 44 | 45 | public void setObjectId(String uuid) { 46 | this.objectId = uuid; 47 | } 48 | 49 | // public String getItem() { 50 | // return item; 51 | // } 52 | // 53 | // public void setItem(String item) { 54 | // this.item = item; 55 | // } 56 | 57 | public float getScore() { 58 | return score; 59 | } 60 | 61 | public void setScore(float score) { 62 | this.score = score; 63 | } 64 | 65 | ///todo this is not used anywhere: 66 | // class Neo4JResultComparator implements Comparator { 67 | // 68 | // @Override 69 | // public int compare(ExternalResult o1, ExternalResult o2) { 70 | // if (o1.score < o2.score) { 71 | // return -1; 72 | // } 73 | // if (o1.score > o2.score) { 74 | // return 1; 75 | // } 76 | // return 0; 77 | // } 78 | // 79 | // } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/domain/IndexInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.domain; 17 | 18 | public class IndexInfo { 19 | 20 | public static final String INDEX_LOGGER_NAME = "index.graph-aided-search"; 21 | public final static IndexInfo NO_SCRIPT_INFO = new IndexInfo(); 22 | 23 | private final String neo4jHost; 24 | private final String neo4jBoltHost; 25 | private String neo4jUsername; 26 | private String neo4jPwd; 27 | private final boolean enabled; 28 | private final int maxResultWindow; 29 | private boolean secureBolt = true; 30 | 31 | IndexInfo() { 32 | this.neo4jHost = "http://localhost:7474"; 33 | this.neo4jBoltHost = null; 34 | this.enabled = false; 35 | this.maxResultWindow = 0; 36 | } 37 | 38 | public IndexInfo(final String hostname, final String username, final String password, boolean enabled, int maxResultWindow) { 39 | this(hostname, null, username, password, enabled, maxResultWindow); 40 | } 41 | 42 | public IndexInfo(final String hostname, final String boltHostname, final String username, final String password, boolean enabled, int maxResultWindow, boolean secureBolt) { 43 | this(hostname, boltHostname, username, password, enabled, maxResultWindow); 44 | this.secureBolt = secureBolt; 45 | } 46 | public IndexInfo(final String hostname, final String boltHostname, final String username, final String password, boolean enabled, int maxResultWindow) { 47 | this(hostname, boltHostname, enabled, maxResultWindow); 48 | this.neo4jUsername = username; 49 | this.neo4jPwd = password; 50 | } 51 | 52 | public IndexInfo(final String hostname, boolean enabled, int maxResultWindow) { 53 | this(hostname, null, enabled, maxResultWindow); 54 | } 55 | 56 | public IndexInfo(final String hostname, final String boltHostname, boolean enabled, int maxResultWindow) { 57 | this.neo4jHost = hostname; 58 | this.neo4jBoltHost = boltHostname; 59 | this.enabled = enabled; 60 | this.maxResultWindow = maxResultWindow; 61 | } 62 | 63 | public String getNeo4jHost() { 64 | return neo4jHost; 65 | } 66 | 67 | public boolean isEnabled() { 68 | return enabled; 69 | } 70 | 71 | public int getMaxResultWindow() { 72 | return maxResultWindow; 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "ScriptInfo [neo4jHost=" + neo4jHost 78 | + ", enabled=" + enabled 79 | + ", maxResultWindow=" + maxResultWindow 80 | + ", neo4jUsername=" + neo4jUsername 81 | + ", neo4jPwd=" + neo4jPwd 82 | + "]"; 83 | } 84 | 85 | public String getNeo4jUsername() { 86 | return neo4jUsername; 87 | } 88 | 89 | public String getNeo4jPassword() { 90 | return neo4jPwd; 91 | } 92 | 93 | public String getNeo4jBoltHost() { 94 | return neo4jBoltHost; 95 | } 96 | 97 | public boolean isSecureBolt() { 98 | return secureBolt; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/filter/SearchResultCypherFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.filter; 17 | 18 | import com.graphaware.es.gas.annotation.SearchFilter; 19 | import com.graphaware.es.gas.cypher.CypherEndPoint; 20 | import com.graphaware.es.gas.cypher.CypherResult; 21 | import com.graphaware.es.gas.cypher.CypherSettingsReader; 22 | import com.graphaware.es.gas.cypher.ResultRow; 23 | import com.graphaware.es.gas.domain.IndexInfo; 24 | import com.graphaware.es.gas.util.NumberUtil; 25 | import org.elasticsearch.common.settings.Settings; 26 | import org.elasticsearch.search.internal.InternalSearchHit; 27 | import org.elasticsearch.search.internal.InternalSearchHits; 28 | 29 | import java.util.HashMap; 30 | import java.util.HashSet; 31 | import java.util.Map; 32 | import java.util.Set; 33 | import java.util.logging.Level; 34 | import java.util.logging.Logger; 35 | 36 | import static com.graphaware.es.gas.domain.ClauseConstants.*; 37 | import static com.graphaware.es.gas.wrap.GraphAidedSearchActionListenerWrapper.GAS_FILTER_CLAUSE; 38 | 39 | @SearchFilter(name = "SearchResultCypherFilter") 40 | public class SearchResultCypherFilter extends CypherSettingsReader implements SearchResultFilter { 41 | 42 | private static final Logger logger = Logger.getLogger(SearchResultCypherFilter.class.getName()); 43 | 44 | private static final String DEFAULT_ID_RESULT_NAME = "id"; 45 | private static final String ID_RESULT_NAME_KEY = "identifier"; 46 | private static final int DEFAULT_RESULT_SIZE = 10; 47 | private static final int DEFAULT_FROM_VALUE = 0; 48 | protected static final String DEFAULT_PROTOCOL = "http"; 49 | 50 | private int maxResultSize = -1; 51 | private CypherEndPoint cypherEndPoint; 52 | 53 | private int size; 54 | private int from; 55 | private String cypherQuery; 56 | private boolean shouldExclude = true; 57 | private String idResultName; 58 | 59 | public SearchResultCypherFilter(Settings settings, IndexInfo indexSettings) { 60 | super(settings, indexSettings); 61 | } 62 | 63 | @Override 64 | public void parseRequest(Map sourceAsMap) { 65 | size = NumberUtil.getInt(sourceAsMap.get(SIZE), DEFAULT_RESULT_SIZE); 66 | from = NumberUtil.getInt(sourceAsMap.get(FROM), DEFAULT_FROM_VALUE); 67 | 68 | HashMap extParams = (HashMap) sourceAsMap.get(GAS_FILTER_CLAUSE); 69 | if (extParams != null) { 70 | cypherQuery = (String) extParams.get(QUERY); 71 | maxResultSize = NumberUtil.getInt(extParams.get(MAX_RESULT_SIZE), getMaxResultWindow()); 72 | shouldExclude = extParams.containsKey(EXCLUDE) && String.valueOf(extParams.get(EXCLUDE)).equalsIgnoreCase(TRUE); 73 | idResultName = extParams.containsKey(ID_RESULT_NAME_KEY) ? String.valueOf(extParams.get(ID_RESULT_NAME_KEY)) : null; 74 | String protocol = extParams.containsKey(PROTOCOL) ? String.valueOf(extParams.get(PROTOCOL)) : DEFAULT_PROTOCOL; 75 | cypherEndPoint = createCypherEndPoint(protocol, getSettings()); 76 | } 77 | if (maxResultSize > 0) { 78 | sourceAsMap.put(SIZE, maxResultSize); 79 | } 80 | if (null == cypherQuery) { 81 | throw new RuntimeException("The Query Parameter is required in gas-filter"); 82 | } 83 | } 84 | 85 | @Override 86 | public InternalSearchHits modify(final InternalSearchHits hits) { 87 | Set remoteFilter = getFilteredItems(); 88 | final InternalSearchHit[] searchHits = hits.internalHits(); 89 | Map hitMap = new HashMap<>(); 90 | for (InternalSearchHit hit : searchHits) { 91 | hitMap.put(hit.getId(), hit); 92 | } 93 | 94 | InternalSearchHit[] tmpSearchHits = new InternalSearchHit[hitMap.size()]; 95 | int k = 0; 96 | float maxScore = -1; 97 | for (Map.Entry item : hitMap.entrySet()) { 98 | if ((shouldExclude && !remoteFilter.contains(item.getKey())) 99 | || (!shouldExclude && remoteFilter.contains(item.getKey()))) { 100 | tmpSearchHits[k] = item.getValue(); 101 | k++; 102 | float score = item.getValue().getScore(); 103 | if (maxScore < score) { 104 | maxScore = score; 105 | } 106 | } 107 | } 108 | int totalSize = k; 109 | 110 | logger.log(Level.FINE, "k <= reorderSize: {0}", (k <= size)); 111 | 112 | final int arraySize = (size + from) < k ? size 113 | : (k - from) > 0 ? (k - from) : 0; 114 | if (arraySize == 0) { 115 | return new InternalSearchHits(new InternalSearchHit[0], 0, 0); 116 | } 117 | 118 | InternalSearchHit[] newSearchHits = new InternalSearchHit[arraySize]; 119 | k = 0; 120 | for (int i = from; i < arraySize + from; i++) { 121 | InternalSearchHit newId = tmpSearchHits[i]; 122 | if (newId == null) { 123 | break; 124 | } 125 | newSearchHits[k++] = newId; 126 | } 127 | return new InternalSearchHits(newSearchHits, totalSize, 128 | hits.maxScore()); 129 | } 130 | 131 | protected Set getFilteredItems() { 132 | CypherResult result = getCypherResult(); 133 | Set filteredItems = new HashSet<>(); 134 | 135 | for (ResultRow resultRow : result.getRows()) { 136 | filteredItems.add(getFilteredItem(resultRow)); 137 | } 138 | 139 | return filteredItems; 140 | } 141 | 142 | protected CypherResult getCypherResult() { 143 | return cypherEndPoint.executeCypher(cypherQuery, new HashMap()); 144 | } 145 | 146 | protected String getFilteredItem(ResultRow resultRow) { 147 | if (!resultRow.getValues().containsKey(getIdResultName())) { 148 | throw new RuntimeException("The cypher query result must contain the " + getIdResultName() + " column name"); 149 | } 150 | 151 | return getIdentifier(resultRow.get(getIdResultName())); 152 | } 153 | 154 | public int getSize() { 155 | return size; 156 | } 157 | 158 | public int getFrom() { 159 | return from; 160 | } 161 | 162 | public String getIdResultName() { 163 | return null != idResultName ? idResultName : DEFAULT_ID_RESULT_NAME; 164 | } 165 | 166 | private static String getIdentifier(Object objectId) { 167 | if (objectId instanceof String) { 168 | return (String) objectId; 169 | } 170 | return String.valueOf(objectId); 171 | } 172 | 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/filter/SearchResultFilter.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2013-2016 GraphAware 4 | * 5 | * This file is part of the GraphAware Framework. 6 | * 7 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 8 | * the GNU General Public License as published by the Free Software Foundation, either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 12 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. You should have received a copy of 14 | * the GNU General Public License along with this program. If not, see 15 | * . 16 | */ 17 | package com.graphaware.es.gas.filter; 18 | 19 | import com.graphaware.es.gas.modifier.SearchResultModifier; 20 | 21 | public interface SearchResultFilter extends SearchResultModifier { 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/modifier/PrivilegedSearchResultModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.modifier; 18 | 19 | import org.elasticsearch.search.internal.InternalSearchHits; 20 | 21 | import java.security.AccessController; 22 | import java.security.PrivilegedAction; 23 | import java.util.Map; 24 | 25 | public class PrivilegedSearchResultModifier implements SearchResultModifier { 26 | 27 | private final SearchResultModifier delegate; 28 | 29 | public PrivilegedSearchResultModifier(SearchResultModifier delegate) { 30 | this.delegate = delegate; 31 | } 32 | 33 | @Override 34 | public InternalSearchHits modify(final InternalSearchHits hits) { 35 | return AccessController.doPrivileged(new PrivilegedAction() { 36 | @Override 37 | public InternalSearchHits run() { 38 | return delegate.modify(hits); 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | public void parseRequest(Map sourceAsMap) { 45 | delegate.parseRequest(sourceAsMap); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/modifier/SearchResultModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.modifier; 18 | 19 | import org.elasticsearch.search.internal.InternalSearchHits; 20 | 21 | import java.util.Map; 22 | 23 | public interface SearchResultModifier { 24 | 25 | InternalSearchHits modify(final InternalSearchHits hits); 26 | 27 | void parseRequest(Map sourceAsMap); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/util/Instantiator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.util; 18 | 19 | import com.graphaware.es.gas.domain.IndexInfo; 20 | import com.graphaware.es.gas.modifier.SearchResultModifier; 21 | import org.elasticsearch.common.logging.ESLogger; 22 | import org.elasticsearch.common.logging.Loggers; 23 | import org.elasticsearch.common.settings.Settings; 24 | 25 | import java.lang.annotation.Annotation; 26 | import java.lang.reflect.Constructor; 27 | import java.lang.reflect.InvocationTargetException; 28 | import java.lang.reflect.Method; 29 | import java.security.AccessController; 30 | import java.security.PrivilegedAction; 31 | import java.util.Collection; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | import java.util.concurrent.ConcurrentHashMap; 35 | 36 | import static com.graphaware.es.gas.domain.ClauseConstants.NAME; 37 | 38 | public class Instantiator { 39 | 40 | private final ESLogger logger; 41 | private final Settings settings; 42 | 43 | private final Map, Map> classCache = new ConcurrentHashMap<>(); 44 | 45 | public Instantiator(Settings settings) { 46 | this.logger = Loggers.getLogger(getClass(), settings); 47 | this.settings = settings; 48 | } 49 | 50 | 51 | @SuppressWarnings("unchecked") 52 | public T instantiate(String clause, Map source, IndexInfo indexInfo, final Class clazz, final Class annotationClass) { 53 | Map params = (Map) source.get(clause); 54 | if (params == null) { 55 | return null; 56 | } 57 | 58 | String name = params.get(NAME); 59 | if (name == null || name.length() < 1) { 60 | return null; 61 | } 62 | 63 | T result = instantiatePrivileged(name, indexInfo, clazz, annotationClass); 64 | 65 | if (result != null) { 66 | result.parseRequest(source); 67 | } else { 68 | logger.warn("No {} found with name {}", clazz.getName(), name); 69 | } 70 | 71 | source.remove(clause); 72 | return result; 73 | } 74 | 75 | private T instantiatePrivileged(final String name, final IndexInfo indexSettings, final Class clazz, final Class annotationClass) { 76 | return AccessController.doPrivileged(new PrivilegedAction() { 77 | public T run() { 78 | return instantiate(name, indexSettings, clazz, annotationClass); 79 | } 80 | }); 81 | } 82 | 83 | private T instantiate(String name, IndexInfo indexSettings, Class clazz, Class annotationClass) { 84 | Class cls = loadCachedClasses(clazz, annotationClass).get(name.toLowerCase()); 85 | 86 | if (cls == null) { 87 | return null; 88 | } 89 | 90 | try { 91 | try { 92 | Constructor constructor = cls.getConstructor(Settings.class, IndexInfo.class); 93 | return constructor.newInstance(settings, indexSettings); 94 | } catch (NoSuchMethodException ex) { 95 | logger.warn("No constructor with settings for class {}. Using default", cls.getName()); 96 | return cls.newInstance(); 97 | } 98 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException | SecurityException ex) { 99 | logger.error("Error while initializing new {}", cls.getName(), ex); 100 | } 101 | 102 | return null; 103 | } 104 | 105 | private Map> loadCachedClasses(Class clazz, Class annotationClass) { 106 | @SuppressWarnings("unchecked") 107 | Map> cachedMap = (Map>) classCache.get(clazz); 108 | 109 | if (cachedMap == null) { 110 | cachedMap = loadClasses(clazz, annotationClass); 111 | classCache.put(clazz, cachedMap); 112 | } 113 | 114 | return cachedMap; 115 | } 116 | 117 | private Map> loadClasses(Class clazz, Class annotationClass) { 118 | Collection> classes = PluginClassLoader.loadClass(clazz, annotationClass).values(); 119 | 120 | Map> result = new HashMap<>(); 121 | 122 | for (Class cls : classes) { 123 | Annotation annotation = cls.getAnnotation(annotationClass); 124 | try { 125 | Method nameMethod = annotationClass.getDeclaredMethod("name"); 126 | result.put(((String) nameMethod.invoke(annotation)).toLowerCase(), cls); 127 | } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { 128 | throw new RuntimeException(e); 129 | } 130 | } 131 | 132 | return result; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/util/NumberUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.util; 17 | 18 | public final class NumberUtil { 19 | 20 | public static int getInt(final Object value, final int defaultValue) { 21 | if (value instanceof Number) { 22 | return ((Number) value).intValue(); 23 | } else if (value instanceof String) { 24 | return Integer.parseInt(value.toString()); 25 | } 26 | return defaultValue; 27 | } 28 | 29 | public static float getFloat(final Object value) { 30 | if (value instanceof Number) { 31 | return ((Number) value).floatValue(); 32 | } else if (value instanceof String) { 33 | return Float.parseFloat(value.toString()); 34 | } 35 | 36 | throw new RuntimeException("Unable to parse float from value"); 37 | } 38 | 39 | private NumberUtil() { 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/util/ParamUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.util; 18 | 19 | import java.util.Map; 20 | 21 | public final class ParamUtil { 22 | 23 | public static T extractParameter(String name, Map params) { 24 | T value = params.get(name); 25 | 26 | if (value == null) { 27 | throw new IllegalStateException("The " + name + " parameter must not be null"); 28 | } 29 | 30 | return value; 31 | } 32 | 33 | public static T extractParameter(String name, Map params, T defaultValue) { 34 | T value = params.get(name); 35 | return value != null ? value : defaultValue; 36 | } 37 | 38 | private ParamUtil() { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/util/PluginClassLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.util; 17 | 18 | import java.lang.annotation.Annotation; 19 | import java.security.AccessController; 20 | import java.security.PrivilegedAction; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.Set; 24 | import org.reflections.Reflections; 25 | 26 | public final class PluginClassLoader { 27 | 28 | private static Reflections reflections; 29 | 30 | public static Map> loadClass(Class type, Class annotation) { 31 | return loadClassByAnnotation(type, annotation); 32 | } 33 | 34 | private static Map> loadClassByAnnotation(Class type, Class annotation) { 35 | if (reflections == null) { 36 | loadReflections("com.graphaware.es.gas"); 37 | } 38 | Map> loader = new HashMap<>(); 39 | Set> providers = reflections.getTypesAnnotatedWith(annotation); 40 | for (Class item : providers) { 41 | loader.put(item.getName(), (Class) item); 42 | } 43 | return loader; 44 | } 45 | 46 | private static void loadReflections(final String packagePath) { 47 | AccessController.doPrivileged(new PrivilegedAction() { 48 | public Void run() { 49 | reflections = new Reflections(packagePath); 50 | return null; // nothing to return 51 | } 52 | }); 53 | } 54 | 55 | private PluginClassLoader() { 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/util/UrlUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.util; 18 | 19 | import com.google.common.io.BaseEncoding; 20 | 21 | public class UrlUtil { 22 | 23 | public static String buildUrlFromParts(String... parts) { 24 | boolean isFirst = true; 25 | String url = ""; 26 | for (String part : parts) { 27 | while (part.endsWith("/")) { 28 | part = part.substring(0, part.length()-1); 29 | } 30 | while (part.startsWith("/")) { 31 | part = part.substring(1, part.length()); 32 | } 33 | if (!isFirst) { 34 | url += "/"; 35 | } 36 | url += part; 37 | isFirst = false; 38 | } 39 | 40 | return url; 41 | } 42 | 43 | private UrlUtil() { 44 | } 45 | 46 | public static String getAuthorizationHeaderValue(String username, String password) { 47 | String value = username + ":" + password; 48 | 49 | return "Basic " + BaseEncoding.base64().encode(value.getBytes()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/wrap/ActionListenerWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.wrap; 18 | 19 | import org.elasticsearch.action.ActionListener; 20 | import org.elasticsearch.action.search.SearchRequest; 21 | import org.elasticsearch.common.xcontent.StatusToXContent; 22 | 23 | public interface ActionListenerWrapper { 24 | 25 | ActionListener wrap(final SearchRequest request, final ActionListener listener) throws CannotWrapException; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/wrap/CannotWrapException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.wrap; 18 | 19 | public class CannotWrapException extends Exception { 20 | 21 | public CannotWrapException(String message) { 22 | super(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/wrap/GraphAidedSearchActionListenerWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.wrap; 17 | 18 | import com.google.common.cache.Cache; 19 | import com.google.common.cache.CacheBuilder; 20 | import com.graphaware.es.gas.annotation.SearchBooster; 21 | import com.graphaware.es.gas.annotation.SearchFilter; 22 | import com.graphaware.es.gas.booster.SearchResultBooster; 23 | import com.graphaware.es.gas.domain.IndexInfo; 24 | import com.graphaware.es.gas.filter.SearchResultFilter; 25 | import com.graphaware.es.gas.modifier.PrivilegedSearchResultModifier; 26 | import com.graphaware.es.gas.modifier.SearchResultModifier; 27 | import com.graphaware.es.gas.util.Instantiator; 28 | import com.graphaware.es.gas.util.NumberUtil; 29 | import org.elasticsearch.action.ActionListener; 30 | import org.elasticsearch.action.search.SearchRequest; 31 | import org.elasticsearch.action.search.SearchResponse; 32 | import org.elasticsearch.client.Client; 33 | import org.elasticsearch.client.Requests; 34 | import org.elasticsearch.cluster.ClusterService; 35 | import org.elasticsearch.cluster.metadata.AliasOrIndex; 36 | import org.elasticsearch.cluster.metadata.IndexMetaData; 37 | import org.elasticsearch.cluster.metadata.MetaData; 38 | import org.elasticsearch.common.bytes.BytesReference; 39 | import org.elasticsearch.common.logging.ESLogger; 40 | import org.elasticsearch.common.logging.Loggers; 41 | import org.elasticsearch.common.settings.Settings; 42 | import org.elasticsearch.common.xcontent.XContentBuilder; 43 | import org.elasticsearch.common.xcontent.XContentFactory; 44 | import org.elasticsearch.search.lookup.SourceLookup; 45 | 46 | import java.io.IOException; 47 | import java.util.LinkedList; 48 | import java.util.List; 49 | import java.util.Map; 50 | import java.util.concurrent.Callable; 51 | import java.util.concurrent.TimeUnit; 52 | 53 | import static com.graphaware.es.gas.domain.ClauseConstants.*; 54 | 55 | public class GraphAidedSearchActionListenerWrapper implements ActionListenerWrapper { 56 | 57 | public static final String INDEX_GA_ES_NEO4J_ENABLED = "index.gas.enable"; 58 | public static final String INDEX_MAX_RESULT_WINDOW = "max_result_window"; 59 | public static final String INDEX_GA_ES_NEO4J_HOST = "index.gas.neo4j.hostname"; 60 | public static final String INDEX_GA_ES_NEO4J_USER = "index.gas.neo4j.user"; 61 | public static final String INDEX_GA_ES_NEO4J_PWD = "index.gas.neo4j.password"; 62 | public static final String INDEX_GA_ES_NEO4J_BOLT_HOST = "index.gas.neo4j.boltHostname"; 63 | public static final String INDEX_GA_ES_NEO4J_BOLT_SECURE = "index.gas.neo4j.bolt.secure"; 64 | 65 | // 66 | public static final String GAS_REQUEST = "_gas"; 67 | public static final String GAS_BOOSTER_CLAUSE = "gas-booster"; 68 | public static final String GAS_FILTER_CLAUSE = "gas-filter"; 69 | // 70 | public static final int DEFAULT_MAX_RESULT_WINDOW = 10000; 71 | 72 | private final ESLogger logger; 73 | private final Settings settings; 74 | private final Instantiator instantiator; 75 | 76 | private final ClusterService clusterService; 77 | private final Cache scriptInfoCache; 78 | //private final Client client; 79 | 80 | public GraphAidedSearchActionListenerWrapper(Settings settings, ClusterService clusterService, Client client) { 81 | this.logger = Loggers.getLogger(getClass(), settings); 82 | this.settings = settings; 83 | this.instantiator = new Instantiator(settings); 84 | 85 | this.clusterService = clusterService; 86 | //this.client = client; 87 | this.scriptInfoCache = CacheBuilder.newBuilder().concurrencyLevel(16).expireAfterAccess(120, TimeUnit.SECONDS).build(); 88 | } 89 | 90 | @Override 91 | public ActionListener wrap(SearchRequest request, ActionListener listener) throws CannotWrapException { 92 | checkCorrectType(request); 93 | checkScroll(request); 94 | checkNotAlreadyWrapped(request); 95 | checkSource(request); 96 | checkIndex(request); 97 | 98 | final long startTime = System.nanoTime(); 99 | 100 | final Map source = SourceLookup.sourceAsMap(request.source()); 101 | 102 | warnIfQueryBinary(source); 103 | final int size = NumberUtil.getInt(source.get(SIZE), 10); 104 | final int from = NumberUtil.getInt(source.get(FROM), 0); 105 | checkSizeAndFrom(size, from); 106 | 107 | final IndexInfo scriptInfo = getScriptInfo(request.indices()[0]); 108 | 109 | List modifiers = produceModifiers(scriptInfo, source); 110 | 111 | request.source(buildBytes(source)); 112 | 113 | return createActionListener(request, listener, source, size, from, new WrappingActionListener(listener, startTime, modifiers, scriptInfo, settings)); 114 | } 115 | 116 | private void checkCorrectType(SearchRequest request) throws CannotWrapException { 117 | switch (request.searchType()) { 118 | case DFS_QUERY_AND_FETCH: 119 | case QUERY_AND_FETCH: 120 | case QUERY_THEN_FETCH: 121 | return; 122 | default: 123 | throw new CannotWrapException("Incorrect type: " + request.searchType()); 124 | } 125 | } 126 | 127 | private void checkScroll(SearchRequest request) throws CannotWrapException { 128 | if (request.scroll() != null) { 129 | throw new CannotWrapException("Has scroll"); 130 | } 131 | } 132 | 133 | private void checkNotAlreadyWrapped(SearchRequest request) throws CannotWrapException { 134 | //Necessary to avoid infinite loop 135 | if (Boolean.FALSE.equals(request.getHeader(GAS_REQUEST))) { 136 | throw new CannotWrapException("Already wrapped"); 137 | } 138 | } 139 | 140 | private void checkSource(SearchRequest request) throws CannotWrapException { 141 | BytesReference source = request.source(); 142 | if (source == null) { 143 | source = request.extraSource(); 144 | if (source == null) { 145 | throw new CannotWrapException("No source"); 146 | } 147 | } 148 | } 149 | 150 | private void checkIndex(SearchRequest request) throws CannotWrapException { 151 | final String[] indices = request.indices(); 152 | if (indices == null || indices.length != 1) { 153 | throw new CannotWrapException("Does not have a single index"); 154 | } 155 | } 156 | 157 | private void warnIfQueryBinary(Map sourceAsMap) { 158 | if (sourceAsMap.get(QUERY_BINARY) != null) { 159 | String query = new String((byte[]) sourceAsMap.get(QUERY_BINARY)); 160 | logger.warn("Binary query not supported: \n" + query); 161 | } 162 | } 163 | 164 | private void checkSizeAndFrom(int size, int from) throws CannotWrapException { 165 | if (size < 0 || from < 0) { 166 | throw new CannotWrapException("Negative size or from"); 167 | } 168 | } 169 | 170 | private List produceModifiers(IndexInfo scriptInfo, Map source) throws CannotWrapException { 171 | List modifiers = new LinkedList<>(); 172 | 173 | SearchResultBooster booster = instantiator.instantiate(GAS_BOOSTER_CLAUSE, source, scriptInfo, SearchResultBooster.class, SearchBooster.class); 174 | if (booster != null) { 175 | modifiers.add(new PrivilegedSearchResultModifier(booster)); 176 | } 177 | 178 | SearchResultFilter filter = instantiator.instantiate(GAS_FILTER_CLAUSE, source, scriptInfo, SearchResultFilter.class, SearchFilter.class); 179 | if (filter != null) { 180 | modifiers.add(new PrivilegedSearchResultModifier(filter)); 181 | } 182 | 183 | if (modifiers.isEmpty()) { 184 | throw new CannotWrapException("No modifiers"); 185 | } 186 | 187 | return modifiers; 188 | } 189 | 190 | private BytesReference buildBytes(Map source) { 191 | final XContentBuilder builder; 192 | 193 | try { 194 | builder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE); 195 | builder.map(source); 196 | } catch (IOException e) { 197 | throw new RuntimeException(e); 198 | } 199 | 200 | return builder.bytes(); 201 | } 202 | 203 | private ActionListener createActionListener(final SearchRequest request, final ActionListener listener, final Map source, final int size, final int from, final ActionListener searchResponseListener) { 204 | return new ActionListener() { 205 | @Override 206 | public void onResponse(SearchResponse response) { 207 | searchResponseListener.onResponse(response); 208 | } 209 | 210 | @Override 211 | public void onFailure(Throwable t) { 212 | searchResponseListener.onFailure(t); 213 | } 214 | }; 215 | } 216 | 217 | private IndexInfo getScriptInfo(final String index) { 218 | try { 219 | return scriptInfoCache.get(index, new Callable() { 220 | @Override 221 | public IndexInfo call() throws Exception { 222 | final MetaData metaData = clusterService.state().getMetaData(); 223 | AliasOrIndex aliasOrIndex = metaData.getAliasAndIndexLookup().get(index); 224 | if (aliasOrIndex == null) { 225 | return IndexInfo.NO_SCRIPT_INFO; 226 | } 227 | Settings indexSettings = null; 228 | for (IndexMetaData indexMD : aliasOrIndex.getIndices()) { 229 | final Settings scriptSettings = indexMD.getSettings(); 230 | final String script = scriptSettings.get(INDEX_GA_ES_NEO4J_HOST); 231 | if (script != null && script.length() > 0) { 232 | indexSettings = scriptSettings; 233 | } 234 | } 235 | 236 | if (indexSettings == null) { 237 | return IndexInfo.NO_SCRIPT_INFO; 238 | } 239 | 240 | return new IndexInfo(indexSettings.get(INDEX_GA_ES_NEO4J_HOST), 241 | indexSettings.get(INDEX_GA_ES_NEO4J_BOLT_HOST), 242 | indexSettings.get(INDEX_GA_ES_NEO4J_USER), 243 | indexSettings.get(INDEX_GA_ES_NEO4J_PWD), 244 | indexSettings.getAsBoolean(INDEX_GA_ES_NEO4J_ENABLED, false), 245 | indexSettings.getAsInt(INDEX_MAX_RESULT_WINDOW, 246 | DEFAULT_MAX_RESULT_WINDOW), 247 | indexSettings.getAsBoolean(INDEX_GA_ES_NEO4J_BOLT_SECURE, true)); 248 | } 249 | }); 250 | } catch (final Exception e) { 251 | logger.warn("Failed to load ScriptInfo for {}.", e, index); 252 | return null; 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/com/graphaware/es/gas/wrap/WrappingActionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.wrap; 18 | 19 | import com.graphaware.es.gas.domain.IndexInfo; 20 | import com.graphaware.es.gas.modifier.SearchResultModifier; 21 | import org.elasticsearch.Version; 22 | import org.elasticsearch.action.ActionListener; 23 | import org.elasticsearch.action.search.SearchResponse; 24 | import org.elasticsearch.action.search.ShardSearchFailure; 25 | import org.elasticsearch.common.io.stream.BytesStreamOutput; 26 | import org.elasticsearch.common.logging.ESLogger; 27 | import org.elasticsearch.common.logging.Loggers; 28 | import org.elasticsearch.common.settings.Settings; 29 | import org.elasticsearch.search.aggregations.InternalAggregations; 30 | import org.elasticsearch.search.internal.InternalSearchHits; 31 | import org.elasticsearch.search.internal.InternalSearchResponse; 32 | import org.elasticsearch.search.profile.InternalProfileShardResults; 33 | import org.elasticsearch.search.suggest.Suggest; 34 | import org.elasticsearch.transport.netty.ChannelBufferStreamInput; 35 | 36 | import java.io.IOException; 37 | import java.util.Arrays; 38 | import java.util.Comparator; 39 | import java.util.List; 40 | import java.util.Map; 41 | 42 | import static org.elasticsearch.action.search.ShardSearchFailure.readShardSearchFailure; 43 | import org.elasticsearch.search.internal.InternalSearchHit; 44 | import static org.elasticsearch.search.internal.InternalSearchHits.readSearchHits; 45 | import static org.elasticsearch.search.internal.InternalSearchHits.readSearchHits; 46 | 47 | public class WrappingActionListener implements ActionListener { 48 | 49 | private final ESLogger logger; 50 | private final ActionListener wrapped; 51 | private final long startTime; 52 | private final List modifiers; 53 | private final IndexInfo indexInfo; 54 | 55 | public WrappingActionListener(ActionListener wrapped, long startTime, List modifiers, IndexInfo indexInfo, Settings settings) { 56 | this.logger = Loggers.getLogger(getClass(), settings); 57 | this.wrapped = wrapped; 58 | this.startTime = startTime; 59 | this.modifiers = modifiers; 60 | this.indexInfo = indexInfo; 61 | } 62 | 63 | @Override 64 | public void onResponse(final SearchResponse response) { 65 | if (response.getHits().getTotalHits() == 0 || !indexInfo.isEnabled()) { 66 | wrapped.onResponse(response); 67 | return; 68 | } 69 | 70 | if (logger.isDebugEnabled()) { 71 | logger.debug("Boosting results: {}", response); 72 | } 73 | 74 | try { 75 | wrapped.onResponse(handleResponse(response, startTime, modifiers)); 76 | } catch (final Exception e) { 77 | if (logger.isDebugEnabled()) { 78 | logger.debug("Failed to parse a search response.", e); 79 | } 80 | throw new RuntimeException("Failed to parse a search response.", e); 81 | } 82 | } 83 | 84 | @Override 85 | public void onFailure(final Throwable e) { 86 | wrapped.onFailure(e); 87 | } 88 | 89 | private SearchResponse handleResponse(final SearchResponse response, final long startTime, final List modifiers) throws IOException { 90 | BytesStreamOutput out = new BytesStreamOutput(); 91 | response.writeTo(out); 92 | ChannelBufferStreamInput in = new ChannelBufferStreamInput(out.bytes().toChannelBuffer()); 93 | 94 | Map headers = readHeaders(in); 95 | InternalSearchHits hits = modifyHits(modifiers, readHits(in)); 96 | InternalAggregations aggregations = readAggregations(in); 97 | Suggest suggest = readSuggestions(in); 98 | Boolean timedOut = in.readBoolean(); 99 | Boolean terminatedEarly = in.readOptionalBoolean(); 100 | InternalProfileShardResults profileResults = readInternalProfileShardResults(in); 101 | InternalSearchResponse internalResponse = new InternalSearchResponse(hits, aggregations, suggest, profileResults, timedOut, terminatedEarly); 102 | SearchResponse newResponse = createNewResponse(startTime, in, internalResponse); 103 | 104 | copyHeaders(headers, newResponse); 105 | 106 | logTime(response, startTime); 107 | 108 | return newResponse; 109 | } 110 | 111 | private Map readHeaders(ChannelBufferStreamInput in) throws IOException { 112 | if (logger.isDebugEnabled()) { 113 | logger.debug("Reading headers..."); 114 | } 115 | 116 | Map headers = null; 117 | if (in.readBoolean()) { 118 | headers = in.readMap(); 119 | } 120 | return headers; 121 | } 122 | 123 | private InternalSearchHits readHits(ChannelBufferStreamInput in) throws IOException { 124 | if (logger.isDebugEnabled()) { 125 | logger.debug("Reading hits..."); 126 | } 127 | 128 | return readSearchHits(in); 129 | } 130 | 131 | private InternalSearchHits modifyHits(List modifiers, InternalSearchHits hits) { 132 | for (final SearchResultModifier modifier : modifiers) { 133 | hits = modifier.modify(hits); 134 | } 135 | InternalSearchHit[] searchHits = sortResults(hits); 136 | return new InternalSearchHits(searchHits, hits.getTotalHits(), 137 | hits.maxScore()); 138 | } 139 | 140 | private InternalSearchHit[] sortResults(InternalSearchHits hits) { 141 | final InternalSearchHit[] searchHits = hits.internalHits(); 142 | Arrays.sort(searchHits, new Comparator() { 143 | @Override 144 | public int compare(InternalSearchHit o1, InternalSearchHit o2) { 145 | if (o1 == null && o2 != null) 146 | return -1; 147 | else if (o1 != null && o2 == null) 148 | return 1; 149 | else if (o1 == null && o2 == null) 150 | return 0; 151 | else if (o1.getScore() > o2.getScore()) 152 | return -1; 153 | else if (o1.getScore() < o2.getScore()) 154 | return 1; 155 | else 156 | return 0; 157 | } 158 | }); 159 | return searchHits; 160 | } 161 | 162 | private InternalAggregations readAggregations(ChannelBufferStreamInput in) throws IOException { 163 | if (logger.isDebugEnabled()) { 164 | logger.debug("Reading aggregations..."); 165 | } 166 | 167 | InternalAggregations aggregations = null; 168 | if (in.readBoolean()) { 169 | aggregations = InternalAggregations.readAggregations(in); 170 | } 171 | return aggregations; 172 | } 173 | 174 | private Suggest readSuggestions(ChannelBufferStreamInput in) throws IOException { 175 | if (logger.isDebugEnabled()) { 176 | logger.debug("Reading suggest..."); 177 | } 178 | 179 | Suggest suggest = null; 180 | if (in.readBoolean()) { 181 | suggest = Suggest.readSuggest(Suggest.Fields.SUGGEST, in); 182 | } 183 | return suggest; 184 | } 185 | 186 | private InternalProfileShardResults readInternalProfileShardResults(ChannelBufferStreamInput in) throws IOException { 187 | 188 | InternalProfileShardResults profileResults; 189 | 190 | if (in.getVersion().onOrAfter(Version.V_2_2_0) && in.readBoolean()) { 191 | profileResults = new InternalProfileShardResults(in); 192 | } else { 193 | profileResults = null; 194 | } 195 | return profileResults; 196 | 197 | //return new InternalSearchResponse(hits, aggregations, suggest, profileResults, timedOut, terminatedEarly); 198 | } 199 | 200 | private SearchResponse createNewResponse(long startTime, ChannelBufferStreamInput in, InternalSearchResponse internalResponse) throws IOException { 201 | int totalShards = in.readVInt(); 202 | int successfulShards = in.readVInt(); 203 | int size = in.readVInt(); 204 | 205 | ShardSearchFailure[] shardFailures; 206 | if (size == 0) { 207 | shardFailures = ShardSearchFailure.EMPTY_ARRAY; 208 | } else { 209 | shardFailures = new ShardSearchFailure[size]; 210 | for (int i = 0; i < shardFailures.length; i++) { 211 | shardFailures[i] = readShardSearchFailure(in); 212 | } 213 | } 214 | 215 | final String scrollId = in.readOptionalString(); 216 | 217 | if (logger.isDebugEnabled()) { 218 | logger.debug("Creating new SearchResponse..."); 219 | } 220 | 221 | return new SearchResponse(internalResponse, scrollId, totalShards, successfulShards, (System.nanoTime() - startTime) / 1000000, shardFailures); 222 | } 223 | 224 | private void copyHeaders(Map headers, SearchResponse newResponse) { 225 | if (headers != null) { 226 | for (Map.Entry entry : headers.entrySet()) { 227 | newResponse.putHeader(entry.getKey(), entry.getValue()); 228 | } 229 | } 230 | } 231 | 232 | private void logTime(SearchResponse response, long startTime) { 233 | if (logger.isDebugEnabled()) { 234 | long tookInMillis = (System.nanoTime() - startTime) / 1000000; 235 | logger.debug("Rewriting overhead time: {} - {} = {}ms", tookInMillis, response.getTookInMillis(), tookInMillis - response.getTookInMillis()); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/main/plugin-metadata/plugin-descriptor.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2013-2016 GraphAware 3 | # 4 | # This file is part of the GraphAware Framework. 5 | # 6 | # GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | # the GNU General Public License as published by the Free Software Foundation, either 8 | # version 3 of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | # See the GNU General Public License for more details. You should have received a copy of 13 | # the GNU General Public License along with this program. If not, see 14 | # . 15 | # 16 | 17 | description=This plugin allow to boosting and filtering results based on neo4j graph databases 18 | version=${project.version} 19 | name=graph-aided-search 20 | jvm=true 21 | classname=${elasticsearch.plugin.classname} 22 | elasticsearch.version=${elasticsearch.version} 23 | java.version=${maven.compiler.target} 24 | isolated=true 25 | -------------------------------------------------------------------------------- /src/main/plugin-metadata/plugin-security.policy: -------------------------------------------------------------------------------- 1 | grant { 2 | //permission org.elasticsearch.script.ClassPermission "java.lang.*"; // allow class` 3 | //permission java.lang.RuntimePermission "createClassLoader"; 4 | permission java.lang.RuntimePermission "getClassLoader"; 5 | permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; 6 | //permission java.lang.RuntimePermission "accessDeclaredMembers"; 7 | //permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect"; 8 | //permission java.lang.RuntimePermission "closeClassLoader"; 9 | //permission org.elasticsearch.script.ClassPermission "<>"; 10 | }; -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/EmbeddedGraphDatabaseServerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.graphaware.es.gas; 7 | 8 | import com.graphaware.integration.neo4j.test.EmbeddedGraphDatabaseServer; 9 | import org.junit.After; 10 | import org.junit.AfterClass; 11 | import org.junit.Before; 12 | import org.junit.BeforeClass; 13 | import org.junit.Test; 14 | 15 | /** 16 | * 17 | * @author ale 18 | */ 19 | public class EmbeddedGraphDatabaseServerTest { 20 | private EmbeddedGraphDatabaseServer server; 21 | 22 | public EmbeddedGraphDatabaseServerTest() { 23 | } 24 | 25 | @BeforeClass 26 | public static void setUpClass() { 27 | } 28 | 29 | @AfterClass 30 | public static void tearDownClass() { 31 | } 32 | 33 | @Before 34 | public void setUp() { 35 | server = new EmbeddedGraphDatabaseServer(); 36 | server.start(); 37 | } 38 | 39 | @After 40 | public void tearDown() { 41 | server.stop(); 42 | } 43 | 44 | @Test 45 | public void someTest() { 46 | System.out.print("But a bp here"); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/GraphAidedSearchFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas; 2 | 3 | import org.elasticsearch.common.settings.Settings; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | public class GraphAidedSearchFilterTest { 9 | 10 | @Test 11 | public void testDefaultFilterOrder() { 12 | Settings settings = Settings.EMPTY; 13 | GraphAidedSearchFilter filter = new GraphAidedSearchFilter(settings); 14 | assertEquals(10, filter.order()); 15 | } 16 | 17 | @Test 18 | public void testCustomFilterOrder() { 19 | Settings settings = Settings.builder() 20 | .put("indices.graphaware.filter.order", 25) 21 | .build(); 22 | 23 | GraphAidedSearchFilter filter = new GraphAidedSearchFilter(settings); 24 | assertEquals(25, filter.order()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/GraphAidedSearchIntegrationBoltTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas; 17 | 18 | import com.graphaware.es.gas.cypher.CypherEndPoint; 19 | import com.graphaware.es.gas.cypher.CypherEndPointBuilder; 20 | import io.searchbox.core.Search; 21 | import io.searchbox.core.SearchResult; 22 | import org.junit.Test; 23 | 24 | import java.io.IOException; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | import static com.graphaware.es.gas.wrap.GraphAidedSearchActionListenerWrapper.*; 29 | import com.graphaware.integration.neo4j.test.EmbeddedGraphDatabaseServer; 30 | import org.elasticsearch.common.settings.Settings; 31 | import static org.junit.Assert.assertEquals; 32 | 33 | public class GraphAidedSearchIntegrationBoltTest extends GraphAidedSearchTest { 34 | 35 | private static final String INDEX_NAME = "test-index"; 36 | private static final String DISABLED_INDEX_NAME = "disabled-test-index"; 37 | private static final String TYPE_NAME = "test_data"; 38 | protected static final String BOLT_URL = "bolt://localhost:7687"; 39 | protected static final String HTTP_URL = "http://localhost:7474"; 40 | 41 | private CypherEndPoint cypherEndPoint; 42 | 43 | @Override 44 | public void setUp() throws Exception { 45 | super.setUp(); 46 | createIndices(); 47 | createData(); 48 | 49 | } 50 | 51 | protected void createNeo4jServer() throws IOException { 52 | neo4jServer = new EmbeddedGraphDatabaseServer(); 53 | Map serverParams = new HashMap<>(); 54 | serverParams.put("dbms.connector.0.enabled", true); 55 | neo4jServer.start(serverParams); 56 | cypherEndPoint = new CypherEndPointBuilder(CypherEndPointBuilder.CypherEndPointType.BOLT) 57 | .neo4jHostname(HTTP_URL) 58 | .neo4jBoltHostname(BOLT_URL) 59 | .settings(Settings.EMPTY) 60 | .encryption(false) 61 | .build(); 62 | } 63 | 64 | @Override 65 | protected HashMap clusterSettings() { 66 | HashMap settings = new HashMap<>(); 67 | settings.put("script.search", "on"); 68 | settings.put("http.cors.enabled", true); 69 | settings.put("http.cors.allow-origin", "*"); 70 | settings.put("index.number_of_shards", 3); 71 | settings.put("index.number_of_replicas", 0); 72 | settings.put("discovery.zen.ping.unicast.hosts", "localhost:9301-9310"); 73 | settings.put("plugin.types", "com.graphaware.es.gas.GraphAidedSearchPlugin"); 74 | settings.put("index.unassigned.node_left.delayed_timeout", "0"); 75 | 76 | return settings; 77 | } 78 | 79 | @Test 80 | public void testCypherFilterWithGraphAndBolt() throws IOException { 81 | executeCypher("UNWIND range(0, 100) as x CREATE (n) SET n.id = x"); 82 | String query = "{" 83 | + " \"query\": {" 84 | + " \"bool\": {" 85 | + " \"should\": [" 86 | + " {" 87 | + " \"match\": {" 88 | + " \"message\": \"test 1\"" 89 | + " }" 90 | + " }" 91 | + " ]" 92 | + " }" 93 | + " }" 94 | + " ,\"gas-filter\" :{" 95 | + " \"name\": \"SearchResultCypherFilter\"," 96 | + " \"query\": \"MATCH (n) RETURN n.id as id\"," 97 | + " \"exclude\": false," 98 | + " \"protocol\": \"bolt\"" 99 | + " }" 100 | + "}"; 101 | 102 | Search search = new Search.Builder(query) 103 | // multiple index or types can be added. 104 | .addIndex(INDEX_NAME) 105 | .addType(TYPE_NAME) 106 | .build(); 107 | SearchResult result = jestClient.execute(search); 108 | 109 | assertEquals(100, result.getTotal().intValue()); 110 | } 111 | 112 | private void createData() throws IOException { 113 | for (int i = 0; i < 100; ++i) { 114 | index(INDEX_NAME, TYPE_NAME, String.valueOf(i), document(String.valueOf(i))); 115 | } 116 | refresh(); 117 | assertHitCount(INDEX_NAME, TYPE_NAME, 100); 118 | } 119 | 120 | private void createIndices() { 121 | Map settings1 = new HashMap<>(); 122 | settings1.put(INDEX_GA_ES_NEO4J_ENABLED, true); 123 | settings1.put(INDEX_GA_ES_NEO4J_HOST, HTTP_URL); 124 | settings1.put(INDEX_GA_ES_NEO4J_USER, NEO4J_USER); 125 | settings1.put(INDEX_GA_ES_NEO4J_PWD, NEO4J_PASSWORD); 126 | settings1.put(INDEX_GA_ES_NEO4J_BOLT_HOST, BOLT_URL); 127 | createIndex(INDEX_NAME, settings1); 128 | 129 | Map settings2 = new HashMap<>(); 130 | settings2.put(INDEX_GA_ES_NEO4J_ENABLED, false); 131 | createIndex(DISABLED_INDEX_NAME, settings2); 132 | } 133 | 134 | private HashMap document(String id) { 135 | HashMap doc = new HashMap<>(); 136 | doc.put("id", id); 137 | doc.put("message", "test " + id); 138 | doc.put("counter", Integer.valueOf(id)); 139 | 140 | return doc; 141 | } 142 | 143 | protected String executeCypher(String query) { 144 | cypherEndPoint.executeCypher(query, new HashMap()); 145 | return ""; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/GraphAidedSearchTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas; 18 | 19 | import com.graphaware.es.gas.util.TestHttpClient; 20 | import com.graphaware.integration.neo4j.test.EmbeddedGraphDatabaseServer; 21 | import io.searchbox.client.JestClient; 22 | import io.searchbox.client.JestClientFactory; 23 | import io.searchbox.client.config.HttpClientConfig; 24 | import org.apache.commons.codec.binary.Base64; 25 | import org.codehaus.jackson.map.ObjectMapper; 26 | import org.codelibs.elasticsearch.runner.ElasticsearchClusterRunner; 27 | import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; 28 | import org.elasticsearch.action.index.IndexResponse; 29 | import org.elasticsearch.client.Client; 30 | import org.elasticsearch.common.settings.Settings; 31 | import org.junit.After; 32 | import org.junit.Before; 33 | import org.springframework.core.io.ClassPathResource; 34 | 35 | import java.io.IOException; 36 | import java.util.Collections; 37 | import java.util.HashMap; 38 | import java.util.Map; 39 | 40 | import static org.codelibs.elasticsearch.runner.ElasticsearchClusterRunner.newConfigs; 41 | import static org.junit.Assert.assertEquals; 42 | import static org.junit.Assert.assertTrue; 43 | 44 | public abstract class GraphAidedSearchTest { 45 | 46 | private final String DEFAULT_CLUSTER_NAME = "graph-aided-search-cluster"; 47 | protected static final String NEO4J_SERVER_URL = "http://localhost:7474"; 48 | protected static final String NEO4J_USER = "neo4j"; 49 | protected static final String NEO4J_PASSWORD = "password"; 50 | 51 | protected ElasticsearchClusterRunner runner; 52 | 53 | protected EmbeddedGraphDatabaseServer neo4jServer; 54 | 55 | protected JestClient jestClient; 56 | 57 | protected ObjectMapper objectMapper; 58 | 59 | protected TestHttpClient httpClient; 60 | 61 | @Before 62 | public void setUp() throws Exception{ 63 | httpClient = new TestHttpClient(); 64 | objectMapper = new ObjectMapper(); 65 | createCluster(); 66 | createJestClient(); 67 | createNeo4jServer(); 68 | } 69 | 70 | protected void createCluster() { 71 | runner = new ElasticsearchClusterRunner(); 72 | runner.onBuild(new ElasticsearchClusterRunner.Builder() { 73 | @Override 74 | public void build(final int number, final Settings.Builder settingsBuilder) { 75 | for (String key : clusterSettings().keySet()) { 76 | settingsBuilder.put(key, clusterSettings().get(key)); 77 | } 78 | } 79 | }).build(newConfigs().numOfNode(numberOfNodes()).clusterName(clusterName())); 80 | runner.ensureGreen(); 81 | } 82 | 83 | protected void createJestClient() { 84 | JestClientFactory factory = new JestClientFactory(); 85 | factory.setHttpClientConfig(new HttpClientConfig.Builder(getConnection()) 86 | .multiThreaded(true) 87 | .readTimeout(10000000) 88 | .build()); 89 | jestClient = factory.getObject(); 90 | } 91 | 92 | protected void createNeo4jServer() throws IOException { 93 | neo4jServer = new EmbeddedGraphDatabaseServer(); 94 | neo4jServer.start(Collections.singletonMap("cypherPath", new ClassPathResource(cypherFile()).getFile().getAbsolutePath())); 95 | changePassword(); 96 | } 97 | 98 | protected String cypherFile() { 99 | return "graphgen-test-data.cyp"; 100 | } 101 | 102 | protected CreateIndexResponse createIndex(String indexName) { 103 | return createIndex(indexName, new HashMap()); 104 | } 105 | 106 | protected CreateIndexResponse createIndex(String indexName, Map settings) { 107 | Settings.Builder builder = Settings.builder(); 108 | for (String k : settings.keySet()) { 109 | builder.put(k, settings.get(k)); 110 | } 111 | CreateIndexResponse createIndexResponse = runner 112 | .createIndex(indexName, builder.build()); 113 | assertTrue(createIndexResponse.isAcknowledged()); 114 | return createIndexResponse; 115 | } 116 | 117 | protected int numberOfNodes() { 118 | return 1; 119 | } 120 | 121 | protected String clusterName() { 122 | return DEFAULT_CLUSTER_NAME; 123 | } 124 | 125 | protected HashMap clusterSettings() { 126 | return new HashMap<>(); 127 | } 128 | 129 | protected String clientHost() { 130 | return "localhost"; 131 | } 132 | 133 | protected int clientPort() { 134 | return 9201; 135 | } 136 | 137 | private String getConnection() { 138 | return String.format("http://%s:%d", clientHost(), clientPort()); 139 | } 140 | 141 | protected IndexResponse index(String indexName, String type, String id, HashMap fields) throws IOException { 142 | return index(indexName, type, id, objectMapper.writeValueAsString(fields)); 143 | } 144 | 145 | protected IndexResponse index(String indexName, String type, String id, String source) { 146 | IndexResponse indexResponse = runner.insert(indexName, type, id, source); 147 | assertTrue(indexResponse.isCreated()); 148 | 149 | return indexResponse; 150 | } 151 | 152 | protected void assertHitCount(String indexName, String typeName, int expected) { 153 | assertEquals(expected, runner.client().prepareSearch(indexName).setTypes(typeName).setSize(0).get().getHits().getTotalHits()); 154 | } 155 | 156 | protected final void refresh() { 157 | runner.refresh(); 158 | } 159 | 160 | protected final Client client() { 161 | return runner.client(); 162 | } 163 | 164 | protected void emptyDB() { 165 | httpClient.executeCypher(NEO4J_SERVER_URL, getAuthorizationHeaders(NEO4J_PASSWORD), "MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE r,n"); 166 | } 167 | 168 | protected void changePassword() { 169 | String json = "{\"password\":\"" + NEO4J_PASSWORD + "\"}"; 170 | try { 171 | httpClient.post(NEO4J_SERVER_URL + "/user/neo4j/password", json, getAuthorizationHeaders("neo4j"), 200); 172 | } catch (AssertionError e) { 173 | // password was already changed in a previous test and the dbms auth directory is already existing 174 | } 175 | } 176 | 177 | protected HashMap getAuthorizationHeaders(String password) { 178 | HashMap headers = new HashMap<>(); 179 | try { 180 | String credentials = "neo4j:" + password; 181 | headers.put("Authorization", "Basic " + Base64.encodeBase64String(credentials.getBytes("UTF-8"))); 182 | } catch (Exception e) { 183 | e.printStackTrace(); 184 | } 185 | 186 | return headers; 187 | } 188 | 189 | protected String executeCypher(String query) { 190 | return httpClient.executeCypher(NEO4J_SERVER_URL, getAuthorizationHeaders(NEO4J_PASSWORD), query); 191 | } 192 | 193 | @After 194 | public void tearDown() { 195 | runner.close(); 196 | runner.clean(); 197 | neo4jServer.stop(); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/JestMsgResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas; 17 | 18 | import io.searchbox.annotations.JestId; 19 | 20 | public class JestMsgResult 21 | { 22 | 23 | @JestId 24 | private String documentId; 25 | 26 | private String message; 27 | 28 | public String getDocumentId() 29 | { 30 | return documentId; 31 | } 32 | public String getMsg() 33 | { 34 | return message; 35 | } 36 | public void setMessage(String name) 37 | { 38 | this.message = name; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/MockServerTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas; 2 | 3 | import com.graphaware.es.gas.domain.ExternalResult; 4 | import com.sun.jersey.api.client.Client; 5 | import com.sun.jersey.api.client.ClientResponse; 6 | import com.sun.jersey.api.client.GenericType; 7 | import com.sun.jersey.api.client.WebResource; 8 | import com.sun.jersey.api.client.config.ClientConfig; 9 | import com.sun.jersey.api.client.config.DefaultClientConfig; 10 | import com.sun.jersey.core.util.MultivaluedMapImpl; 11 | import org.junit.Test; 12 | 13 | import java.util.*; 14 | import javax.ws.rs.core.HttpHeaders; 15 | import javax.ws.rs.core.MediaType; 16 | import javax.ws.rs.core.MultivaluedMap; 17 | import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 18 | import org.junit.After; 19 | 20 | import static org.junit.Assert.*; 21 | import org.junit.Before; 22 | import org.mockserver.model.Header; 23 | import org.mockserver.integration.ClientAndServer; 24 | import static org.mockserver.integration.ClientAndServer.startClientAndServer; 25 | import static org.mockserver.model.HttpRequest.request; 26 | import static org.mockserver.model.HttpResponse.response; 27 | 28 | public class MockServerTest { 29 | 30 | private ClientAndServer mockServer; 31 | 32 | @Before 33 | public void startMockServer() { 34 | mockServer = startClientAndServer(1080); 35 | } 36 | 37 | @After 38 | public void stopProxy() { 39 | mockServer.stop(); 40 | } 41 | 42 | private static final String LS = System.getProperty("line.separator"); 43 | 44 | @Test 45 | public void testGetExternalResults() { 46 | mockServer 47 | .when( 48 | request() 49 | .withPath("/reco/12") 50 | ) 51 | .respond(response() 52 | .withHeaders( 53 | new Header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) 54 | ) 55 | .withBody("" + 56 | "[" + LS + 57 | " {" + LS + 58 | " \"nodeId\": 1," + LS + 59 | " \"objectId\": \"270\"," + LS + 60 | " \"score\": 3" + LS + 61 | " }," + LS + 62 | " {" + LS + 63 | " \"nodeId\": 2," + LS + 64 | " \"objectId\": \"123\"," + LS + 65 | " \"score\": 3" + LS + 66 | " }," + LS + 67 | " {" + LS + 68 | " \"nodeId\": 3," + LS + 69 | " \"objectId\": \"456\"," + LS + 70 | " \"score\": 3" + LS + 71 | " }" + LS + 72 | "]") 73 | ); 74 | ClientConfig cfg = new DefaultClientConfig(); 75 | cfg.getClasses().add(JacksonJsonProvider.class); 76 | MultivaluedMap param = new MultivaluedMapImpl(); 77 | param.add("limit", String.valueOf(Integer.MAX_VALUE)); 78 | param.add("from", String.valueOf(0)); 79 | param.add("keyProperty", "objectId"); 80 | param.add("ids", "123,456"); 81 | WebResource resource = Client.create(cfg).resource("http://localhost:1080/reco/12"); 82 | ClientResponse response = resource 83 | .accept(MediaType.APPLICATION_JSON) 84 | .post(ClientResponse.class, param); 85 | GenericType> type = new GenericType>() { 86 | }; 87 | List externalResults = response.getEntity(type); 88 | // String entity = response.getEntity(String.class); 89 | assertNotNull(externalResults); 90 | assertEquals(externalResults.size(), 3); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/booster/GraphBoosterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.booster; 18 | 19 | import com.graphaware.es.gas.domain.IndexInfo; 20 | import com.graphaware.es.gas.domain.TestIndexInfo; 21 | import com.graphaware.es.gas.stubs.CypherSearchResultTestBooster; 22 | import org.elasticsearch.common.settings.Settings; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | import java.util.HashMap; 27 | 28 | import static org.junit.Assert.*; 29 | 30 | public class GraphBoosterTest { 31 | 32 | private SearchResultExternalBooster booster; 33 | 34 | @Before 35 | public void setUp() { 36 | Settings.Builder builder = Settings.builder(); 37 | Settings settings = builder.build(); 38 | IndexInfo indexInfo = TestIndexInfo.newInstance(); 39 | booster = new CypherSearchResultTestBooster(settings, indexInfo); 40 | } 41 | 42 | @Test 43 | public void testComposeScoreIsSetByDefault() { 44 | assertEquals("*", booster.getComposeScoreOperator()); 45 | } 46 | 47 | @Test 48 | public void testComposerScoreOperatorCanBeCustomized() throws Exception{ 49 | 50 | booster.parseRequest(getBoosterSourceMap("+")); 51 | assertEquals("+", booster.getComposeScoreOperator()); 52 | 53 | booster.parseRequest(getBoosterSourceMap("-")); 54 | assertEquals("-", booster.getComposeScoreOperator()); 55 | 56 | booster.parseRequest(getBoosterSourceMap("/")); 57 | assertEquals("/", booster.getComposeScoreOperator()); 58 | 59 | booster.parseRequest(getBoosterSourceMap("*")); 60 | assertEquals("*", booster.getComposeScoreOperator()); 61 | 62 | booster.parseRequest(getBoosterSourceMap("replace")); 63 | assertEquals("replace", booster.getComposeScoreOperator()); 64 | 65 | try { 66 | booster.parseRequest(getBoosterSourceMap("_")); 67 | assertTrue(false); // exception should be thrown 68 | } catch (Exception e) { 69 | assertTrue(e.getMessage().contains("Operator")); 70 | } 71 | } 72 | 73 | @Test 74 | public void testExceptionIsThrownWhenQueryParameterIsMissing() { 75 | HashMap sourceMap = new HashMap<>(); 76 | HashMap externalParameters = new HashMap<>(); 77 | sourceMap.put("gas-booster", externalParameters); 78 | try { 79 | booster.parseRequest(sourceMap); 80 | assertTrue(false); // If we come here then we have a bug 81 | } catch (Exception e) { 82 | assertTrue(e.getMessage().contains("query parameter must not be null")); 83 | } 84 | } 85 | 86 | private HashMap getBoosterSourceMap(String operator) { 87 | HashMap sourceMap = new HashMap<>(); 88 | HashMap externalParameters = new HashMap<>(); 89 | externalParameters.put("query", "MATCH (n) RETURN n"); 90 | externalParameters.put("operator", operator); 91 | sourceMap.put("gas-booster", externalParameters); 92 | 93 | return sourceMap; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/booster/SearchResultCypherBoltBoosterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.graphaware.es.gas.booster; 7 | 8 | import com.graphaware.es.gas.cypher.CypherEndPoint; 9 | import com.graphaware.es.gas.cypher.CypherEndPointBuilder; 10 | import com.graphaware.es.gas.cypher.CypherResult; 11 | import com.graphaware.es.gas.domain.IndexInfo; 12 | import com.graphaware.integration.neo4j.test.EmbeddedGraphDatabaseServer; 13 | import java.io.IOException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import org.elasticsearch.common.settings.Settings; 17 | 18 | /** 19 | * 20 | * @author ale 21 | */ 22 | public class SearchResultCypherBoltBoosterTest extends SearchResultCypherBoosterTest { 23 | 24 | private CypherEndPoint cypherEndPoint; 25 | protected static final String BOLT_URL = "bolt://localhost:7687"; 26 | protected static final String HTTP_URL = "http://localhost:7474"; 27 | 28 | protected void createNeo4jServer() throws IOException { 29 | neo4jServer = new EmbeddedGraphDatabaseServer(); 30 | Map serverParams = new HashMap<>(); 31 | serverParams.put("dbms.connector.0.enabled", true); 32 | neo4jServer.start(serverParams); 33 | cypherEndPoint = new CypherEndPointBuilder(CypherEndPointBuilder.CypherEndPointType.BOLT) 34 | .neo4jHostname(HTTP_URL) 35 | .neo4jBoltHostname(BOLT_URL) 36 | .settings(Settings.EMPTY) 37 | .encryption(false) 38 | .build(); 39 | } 40 | 41 | protected HashMap getDefaultMap(String query) { 42 | HashMap map = new HashMap<>(); 43 | HashMap gasFilter = new HashMap<>(); 44 | gasFilter.put("query", query); 45 | gasFilter.put("protocol", "bolt"); 46 | map.put("gas-booster", gasFilter); 47 | 48 | return map; 49 | } 50 | 51 | protected SearchResultCypherBooster getBooster() { 52 | Settings.Builder builder = Settings.builder(); 53 | IndexInfo indexInfo = new IndexInfo(HTTP_URL, BOLT_URL, NEO4J_USER, NEO4J_PASSWORD, true, 0, false); 54 | 55 | return new SearchResultCypherBooster(builder.build(), indexInfo); 56 | } 57 | 58 | protected String executeCypher(String query) { 59 | CypherResult res = cypherEndPoint.executeCypher(query, new HashMap()); 60 | return "OK"; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/booster/SearchResultCypherBoosterTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.booster; 2 | 3 | import com.graphaware.es.gas.GraphAidedSearchTest; 4 | import com.graphaware.es.gas.domain.IndexInfo; 5 | 6 | import static com.graphaware.es.gas.booster.SearchResultExternalBooster.DEFAULT_ID_RESULT_NAME; 7 | import static com.graphaware.es.gas.booster.SearchResultExternalBooster.DEFAULT_SCORE_RESULT_NAME; 8 | 9 | import com.graphaware.es.gas.domain.ExternalResult; 10 | import org.elasticsearch.common.settings.Settings; 11 | import org.junit.Test; 12 | 13 | import java.util.HashMap; 14 | import java.util.HashSet; 15 | import java.util.Map; 16 | import java.util.Set; 17 | 18 | import static org.junit.Assert.*; 19 | 20 | public class SearchResultCypherBoosterTest extends GraphAidedSearchTest { 21 | 22 | @Test 23 | public void testDefaultBoosterSettings() { 24 | SearchResultCypherBooster booster = getBooster(); 25 | Map request = new HashMap<>(); 26 | request.put("query", "MATCH (n) RETURN n"); 27 | booster.extendedParseRequest(request); 28 | assertEquals(DEFAULT_SCORE_RESULT_NAME, booster.getScoreResultName()); 29 | assertEquals(DEFAULT_ID_RESULT_NAME, booster.getIdResultName()); 30 | } 31 | 32 | @Test 33 | public void testGetExternalResultsWhenKeySetIsEmpty() { 34 | Set keySet = new HashSet<>(); 35 | SearchResultCypherBooster booster = getBooster(); 36 | booster.extendedParseRequest((Map) getDefaultMap().get("gas-booster")); 37 | Map externalResults = booster.getExternalResults(keySet); 38 | assertEquals(0, externalResults.size()); 39 | } 40 | 41 | @Test 42 | public void testExternalResultsAreReturned() { 43 | executeCypher("UNWIND range(1,10) as x CREATE (n:Test) SET n.id = x"); 44 | Set keySet = new HashSet<>(); 45 | for (int i = 1; i <= 10; ++i) { 46 | keySet.add(String.valueOf(i)); 47 | } 48 | SearchResultCypherBooster booster = getBooster(); 49 | booster.extendedParseRequest((Map) getDefaultMap("MATCH (n:Test) RETURN n.id as id, 10 as score").get("gas-booster")); 50 | Map externalResults = booster.getExternalResults(keySet); 51 | assertEquals(10, externalResults.size()); 52 | assertEquals(10.0f, externalResults.get("3").getScore(), 0); 53 | } 54 | 55 | @Test 56 | public void testExternalResultsAreReturnedWithCustomIdentifierAndScoreName() { 57 | executeCypher("UNWIND range(1,10) as x CREATE (n:Test) SET n.id = x"); 58 | Set keySet = new HashSet<>(); 59 | for (int i = 1; i <= 10; ++i) { 60 | keySet.add(String.valueOf(i)); 61 | } 62 | SearchResultCypherBooster booster = getBooster(); 63 | Map map = (Map) getDefaultMap("MATCH (n:Test) RETURN n.id as uuid, 15 as freq").get("gas-booster"); 64 | map.put("identifier", "uuid"); 65 | map.put("scoreName", "freq"); 66 | booster.extendedParseRequest(map); 67 | Map externalResults = booster.getExternalResults(keySet); 68 | assertEquals(10, externalResults.size()); 69 | assertEquals(15.0f, externalResults.get("1").getScore(), 0); 70 | } 71 | 72 | @Test 73 | public void testExceptionIsThrownWhenScoreResultNameIsNotReturned() { 74 | executeCypher("UNWIND range(1,10) as x CREATE (n:Test) SET n.id = x"); 75 | Set keySet = new HashSet<>(); 76 | for (int i = 1; i < 10; ++i) { 77 | keySet.add(String.valueOf(i)); 78 | } 79 | SearchResultCypherBooster booster = getBooster(); 80 | booster.extendedParseRequest((Map) getDefaultMap().get("gas-booster")); 81 | try { 82 | Map externalResults = booster.getExternalResults(keySet); 83 | assertEquals(1, 2); // If we're here it's a bug 84 | } catch (RuntimeException e) { 85 | assertTrue(e.getMessage().contains("The cypher query result must contain the score column name")); 86 | } 87 | } 88 | 89 | 90 | 91 | @Test 92 | public void testExceptionIsThrownWhenIdResultNameIsNotPresent() { 93 | executeCypher("UNWIND range(1,10) as x CREATE (n:Test) SET n.id = x"); 94 | Set keySet = new HashSet<>(); 95 | for (int i = 1; i < 10; ++i) { 96 | keySet.add(String.valueOf(i)); 97 | } 98 | SearchResultCypherBooster booster = getBooster(); 99 | booster.extendedParseRequest((Map) getDefaultMap("MATCH (n:Test) RETURN id(n)").get("gas-booster")); 100 | try { 101 | Map externalResults = booster.getExternalResults(keySet); 102 | assertEquals(1, 2); // If we're here it's a bug 103 | } catch (RuntimeException e) { 104 | assertTrue(e.getMessage().contains("The cypher query result must contain the id column name")); 105 | } 106 | } 107 | 108 | @Test 109 | public void testGetParameters() { 110 | Set keySet = new HashSet<>(); 111 | keySet.add("1"); 112 | keySet.add("3"); 113 | keySet.add("5"); 114 | HashMap parameters = getBooster().getParameters(keySet); 115 | assertTrue(parameters.containsKey("ids")); 116 | Set ids = (Set) parameters.get("ids"); 117 | assertEquals(3, ids.size()); 118 | assertTrue(ids.contains("1")); 119 | assertTrue(ids.contains("3")); 120 | assertTrue(ids.contains("5")); 121 | } 122 | 123 | private HashMap getDefaultMap() { 124 | return getDefaultMap("MATCH (n:Test) RETURN n.id as id"); 125 | } 126 | 127 | private HashMap getDefaultMap(String query) { 128 | HashMap map = new HashMap<>(); 129 | HashMap gasFilter = new HashMap<>(); 130 | gasFilter.put("query", query); 131 | map.put("gas-booster", gasFilter); 132 | 133 | return map; 134 | } 135 | 136 | private SearchResultCypherBooster getBooster() { 137 | Settings.Builder builder = Settings.builder(); 138 | IndexInfo indexInfo = new IndexInfo(NEO4J_SERVER_URL, NEO4J_USER, NEO4J_PASSWORD, true, 0); 139 | 140 | return new SearchResultCypherBooster(builder.build(), indexInfo); 141 | } 142 | 143 | @Test 144 | public void testExternalResultsIdsUsage() { 145 | executeCypher("UNWIND range(1,10) as x CREATE (n:Test) SET n.id = toString(x)"); 146 | Set keySet = new HashSet<>(); 147 | for (int i = 1; i <= 2; ++i) { 148 | keySet.add(String.valueOf(i)); 149 | } 150 | SearchResultCypherBooster booster = getBooster(); 151 | Map map = (Map) getDefaultMap("MATCH (n:Test) WHERE n.id in {ids} RETURN n.id as uuid, 15 as freq").get("gas-booster"); 152 | map.put("identifier", "uuid"); 153 | map.put("scoreName", "freq"); 154 | booster.extendedParseRequest(map); 155 | Map externalResults = booster.getExternalResults(keySet); 156 | assertEquals(2, externalResults.size()); 157 | assertEquals(15.0f, externalResults.get("1").getScore(), 0); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/booster/SearchResultNeo4jBoosterTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.booster; 2 | 3 | import com.graphaware.es.gas.domain.IndexInfo; 4 | import com.graphaware.es.gas.annotation.SearchBooster; 5 | import com.graphaware.es.gas.domain.ClauseConstants; 6 | import com.graphaware.es.gas.domain.ExternalResult; 7 | import org.elasticsearch.common.settings.Settings; 8 | import org.junit.Test; 9 | 10 | import java.util.*; 11 | import javax.ws.rs.core.HttpHeaders; 12 | import javax.ws.rs.core.MediaType; 13 | import org.junit.After; 14 | 15 | import static org.junit.Assert.*; 16 | import org.junit.Before; 17 | import org.mockserver.model.Header; 18 | import org.mockserver.integration.ClientAndServer; 19 | import static org.mockserver.integration.ClientAndServer.startClientAndServer; 20 | import static org.mockserver.model.HttpRequest.request; 21 | import static org.mockserver.model.HttpResponse.response; 22 | 23 | public class SearchResultNeo4jBoosterTest { 24 | 25 | private ClientAndServer mockServer; 26 | 27 | @Before 28 | public void startMockServer() { 29 | mockServer = startClientAndServer(1080); 30 | } 31 | 32 | @After 33 | public void stopProxy() { 34 | mockServer.stop(); 35 | } 36 | 37 | @Test 38 | public void testNewInstance() { 39 | SearchResultNeo4jBooster booster = getBooster(); 40 | assertTrue(booster instanceof SearchResultExternalBooster); 41 | } 42 | 43 | @Test 44 | public void testExtendedParseRequest() { 45 | HashMap externalParams = new HashMap<>(); 46 | externalParams.put(ClauseConstants.KEY_PROPERTY, "objectId"); 47 | externalParams.put(ClauseConstants.TARGET, "12"); 48 | externalParams.put(ClauseConstants.NEO4J_ENDPOINT, "reco/"); 49 | SearchResultNeo4jBooster booster = getBooster(); 50 | booster.extendedParseRequest(externalParams); 51 | assertEquals("objectId", booster.getKeyProperty()); 52 | assertEquals("12", booster.getTargetId()); 53 | assertEquals("http://localhost:7474/", booster.getNeo4jHost()); 54 | assertEquals("http://localhost:7474/reco/12", booster.getEndpoint()); 55 | } 56 | 57 | @Test 58 | public void testGetReorderedResults() { 59 | List externalResults = new ArrayList<>(); 60 | ExternalResult result1 = new ExternalResult("123", 10.0f); 61 | ExternalResult result2 = new ExternalResult("456", 20.0f); 62 | externalResults.add(result1); 63 | externalResults.add(result2); 64 | Map results = getBooster().getReorderedResults(externalResults); 65 | assertTrue(results.containsKey("123")); 66 | assertTrue(results.containsKey("456")); 67 | assertEquals(10.0f, results.get("123").getScore(), 0); 68 | } 69 | 70 | @Test 71 | public void testExternalDoReorder() { 72 | HashMap externalParams = new HashMap<>(); 73 | externalParams.put(ClauseConstants.KEY_PROPERTY, "objectId"); 74 | externalParams.put(ClauseConstants.TARGET, "12"); 75 | externalParams.put(ClauseConstants.NEO4J_ENDPOINT, "reco/"); 76 | SearchResultNeo4jBooster testBooster = getTestBooster(); 77 | testBooster.extendedParseRequest(externalParams); 78 | Set keySet = new HashSet<>(); 79 | keySet.add("123"); 80 | keySet.add("456"); 81 | Map results = testBooster.externalDoReorder(keySet); 82 | assertTrue(results.containsKey("123")); 83 | assertTrue(results.containsKey("456")); 84 | assertEquals(123.0f, results.get("123").getScore(), 0); 85 | } 86 | 87 | private static final String LS = System.getProperty("line.separator"); 88 | 89 | @Test 90 | public void testGetExternalResults() { 91 | mockServer 92 | .when( 93 | request() 94 | .withPath("/reco/12") 95 | ) 96 | .respond(response() 97 | .withHeaders( 98 | new Header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) 99 | ) 100 | .withBody("" + 101 | "[" + LS + 102 | " {" + LS + 103 | " \"nodeId\": 1," + LS + 104 | " \"objectId\": \"270\"," + LS + 105 | " \"score\": 3.4" + LS + 106 | " }," + LS + 107 | " {" + LS + 108 | " \"nodeId\": 2," + LS + 109 | " \"objectId\": \"123\"," + LS + 110 | " \"score\": 3.5" + LS + 111 | " }," + LS + 112 | " {" + LS + 113 | " \"nodeId\": 3," + LS + 114 | " \"objectId\": \"456\"," + LS + 115 | " \"score\": 3.6" + LS + 116 | " }" + LS + 117 | "]") 118 | ); 119 | HashMap externalParams = new HashMap<>(); 120 | externalParams.put(ClauseConstants.KEY_PROPERTY, "objectId"); 121 | externalParams.put(ClauseConstants.TARGET, "12"); 122 | externalParams.put(ClauseConstants.NEO4J_ENDPOINT, "reco/"); 123 | SearchResultNeo4jBooster testBooster = getMockBooster(); 124 | testBooster.extendedParseRequest(externalParams); 125 | Set keySet = new HashSet<>(); 126 | keySet.add("123"); 127 | keySet.add("456"); 128 | Map results = testBooster.externalDoReorder(keySet); 129 | assertTrue(results.containsKey("123")); 130 | assertTrue(results.containsKey("456")); 131 | assertEquals(3.5f, results.get("123").getScore(), 0); 132 | } 133 | 134 | @Test 135 | public void testRestUrlBuilder() { 136 | SearchResultNeo4jBooster booster = getBooster(); 137 | HashMap externalParams = new HashMap<>(); 138 | externalParams.put(ClauseConstants.KEY_PROPERTY, "objectId"); 139 | externalParams.put(ClauseConstants.TARGET, "12"); 140 | externalParams.put(ClauseConstants.NEO4J_ENDPOINT, "reco/"); 141 | booster.extendedParseRequest(externalParams); 142 | assertEquals("http://localhost:7474/reco/12", booster.getEndpoint()); 143 | externalParams.remove(ClauseConstants.NEO4J_ENDPOINT); 144 | booster.extendedParseRequest(externalParams); 145 | assertEquals("http://localhost:7474/graphaware/recommendation/filter/12", booster.getEndpoint()); 146 | } 147 | 148 | @Test 149 | public void testBuildParameters() { 150 | Set keySet = new HashSet<>(); 151 | keySet.add("1"); 152 | keySet.add("2"); 153 | keySet.add("33"); 154 | Map parameters = getBooster().getParameters(keySet); 155 | assertTrue(parameters.containsKey(ClauseConstants.LIMIT)); 156 | assertTrue(parameters.containsKey(ClauseConstants.FROM)); 157 | assertTrue(parameters.containsKey(ClauseConstants.KEY_PROPERTY)); 158 | assertTrue(parameters.containsKey(ClauseConstants.IDS)); 159 | //assertEquals(getBooster().implodeKeySet(keySet), parameters.get(Constants.IDS)); 160 | } 161 | 162 | @Test 163 | public void testImplodeKeySet() { 164 | Set keySet = new HashSet<>(); 165 | keySet.add("1"); 166 | keySet.add("2"); 167 | keySet.add("33"); 168 | String imploded = getBooster().implodeKeySet(keySet); 169 | assertEquals(6, imploded.length()); 170 | // @todo result is not 1,2,33 but 2,1,33. Is it safe to rely on the order ? 171 | } 172 | 173 | private HashMap getDefaultMap() { 174 | HashMap map = new HashMap<>(); 175 | HashMap gasFilter = new HashMap<>(); 176 | gasFilter.put("query", "MATCH (n) RETURN n"); 177 | map.put("gas-filter", gasFilter); 178 | 179 | return map; 180 | } 181 | 182 | private SearchResultNeo4jBooster getBooster() { 183 | Settings.Builder builder = Settings.builder(); 184 | IndexInfo indexInfo = new IndexInfo("http://localhost:7474/", true, 10); 185 | 186 | return new SearchResultNeo4jBooster(builder.build(), indexInfo); 187 | } 188 | 189 | private SearchResultNeo4jBooster getTestBooster() { 190 | Settings.Builder builder = Settings.builder(); 191 | IndexInfo indexInfo = new IndexInfo("http://localhost:7474/", true, 10); 192 | 193 | return new SearchResultNeo4jBoostertest(builder.build(), indexInfo); 194 | } 195 | 196 | private SearchResultNeo4jBooster getMockBooster() { 197 | Settings.Builder builder = Settings.builder(); 198 | IndexInfo indexInfo = new IndexInfo("http://localhost:1080/", true, 10); 199 | 200 | return new SearchResultNeo4jBooster(builder.build(), indexInfo); 201 | } 202 | 203 | @SearchBooster(name = "SearchResultNeo4jBoostertest") 204 | class SearchResultNeo4jBoostertest extends SearchResultNeo4jBooster { 205 | 206 | public SearchResultNeo4jBoostertest(Settings settings, IndexInfo indexSettings) { 207 | super(settings, indexSettings); 208 | } 209 | 210 | public List getExternalResults(Set keySet) { 211 | List externalResults = new ArrayList<>(); 212 | for (String key : keySet) { 213 | ExternalResult res = new ExternalResult(key, Float.parseFloat(key)); 214 | externalResults.add(res); 215 | } 216 | return externalResults; 217 | } 218 | 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/domain/CypherEndpointBoltTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.domain; 2 | 3 | import com.graphaware.es.gas.cypher.CypherEndPoint; 4 | import com.graphaware.es.gas.cypher.CypherEndPointBuilder; 5 | import com.graphaware.es.gas.cypher.CypherResult; 6 | import com.graphaware.es.gas.cypher.ResultRow; 7 | import com.graphaware.integration.neo4j.test.EmbeddedGraphDatabaseServer; 8 | import java.io.File; 9 | import org.elasticsearch.common.settings.Settings; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | import static org.junit.Assert.*; 18 | import static java.lang.System.getProperty; 19 | 20 | public class CypherEndpointBoltTest { 21 | 22 | 23 | private CypherEndPoint cypherEndPoint; 24 | 25 | protected EmbeddedGraphDatabaseServer server; 26 | protected static final String BOLT_URL = "bolt://localhost:7687"; 27 | protected static final String HTTP_URL = "http://localhost:7474"; 28 | 29 | private static final File DEFAULT_KNOWN_HOSTS = new File(getProperty("user.home"), 30 | ".neo4j" + File.separator + "known_hosts"); 31 | 32 | @Before 33 | public void setUp() { 34 | server = new EmbeddedGraphDatabaseServer(); 35 | Map serverParams = new HashMap<>(); 36 | serverParams.put("dbms.connector.0.enabled", true); 37 | server.start(serverParams); 38 | cypherEndPoint = new CypherEndPointBuilder(CypherEndPointBuilder.CypherEndPointType.BOLT) 39 | .neo4jHostname(HTTP_URL) 40 | .neo4jBoltHostname(BOLT_URL) 41 | .settings(Settings.EMPTY) 42 | .encryption(false) 43 | .build(); 44 | } 45 | 46 | @After 47 | public void clear() { 48 | deleteDefaultKnownCertFileIfExists(); 49 | } 50 | 51 | public static void deleteDefaultKnownCertFileIfExists() { 52 | if (DEFAULT_KNOWN_HOSTS.exists()) { 53 | DEFAULT_KNOWN_HOSTS.delete(); 54 | } 55 | } 56 | 57 | @Test 58 | public void testExecuteCypher() throws Exception { 59 | HashMap params = new HashMap<>(); 60 | cypherEndPoint.executeCypher("UNWIND range(1, 10) as x CREATE (n:Test) SET n.id = x", params); 61 | String query = "MATCH (n) RETURN n.id as id"; 62 | CypherResult result = cypherEndPoint.executeCypher(query, params); 63 | assertEquals(10, result.getRows().size()); 64 | int i = 0; 65 | for (ResultRow resultRow : result.getRows()) { 66 | assertTrue(resultRow.getValues().containsKey("id")); 67 | assertEquals(String.valueOf(++i), String.valueOf(resultRow.getValues().get("id"))); 68 | } 69 | } 70 | 71 | @After 72 | public void tearDown() { 73 | server.stop(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/domain/CypherEndpointTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.domain; 2 | 3 | import com.graphaware.es.gas.cypher.CypherEndPoint; 4 | import com.graphaware.es.gas.cypher.CypherEndPointBuilder; 5 | import com.graphaware.es.gas.cypher.CypherResult; 6 | import com.graphaware.es.gas.cypher.ResultRow; 7 | import com.graphaware.es.gas.util.TestHttpClient; 8 | import com.graphaware.integration.neo4j.test.EmbeddedGraphDatabaseServer; 9 | import org.apache.commons.codec.binary.Base64; 10 | import org.codehaus.jackson.map.ObjectMapper; 11 | import org.junit.After; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import org.elasticsearch.common.settings.Settings; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | public class CypherEndpointTest { 23 | 24 | private CypherEndPoint cypherEndPoint; 25 | 26 | private EmbeddedGraphDatabaseServer server; 27 | 28 | private TestHttpClient httpClient; 29 | 30 | private static final String NEO4J_SERVER_URL = "http://localhost:7474"; 31 | private static final String NEO4J_CUSTOM_PASSWORD = "password"; 32 | private static final String NEO4J_CUSTOM_USER = "neo4j"; 33 | 34 | @Before 35 | public void setUp() { 36 | cypherEndPoint = new CypherEndPointBuilder("http") 37 | .settings(Settings.builder().build()) 38 | .neo4jHostname(NEO4J_SERVER_URL) 39 | .password(NEO4J_CUSTOM_PASSWORD) 40 | .username(NEO4J_CUSTOM_USER) 41 | .build(); 42 | server = new EmbeddedGraphDatabaseServer(); 43 | server.start(); 44 | httpClient = new TestHttpClient(); 45 | changePassword(); 46 | } 47 | 48 | @Test 49 | public void testExecuteCypher() throws Exception { 50 | httpClient.executeCypher(NEO4J_SERVER_URL, getHeaders(NEO4J_CUSTOM_USER, NEO4J_CUSTOM_PASSWORD), "UNWIND range(1, 10) as x CREATE (n:Test) SET n.id = x"); 51 | String query = "MATCH (n) RETURN n.id as id"; 52 | HashMap params = new HashMap<>(); 53 | CypherResult result = cypherEndPoint.executeCypher(getHeaders(NEO4J_CUSTOM_USER, NEO4J_CUSTOM_PASSWORD), query, params); 54 | assertEquals(10, result.getRows().size()); 55 | int i = 0; 56 | for (ResultRow resultRow : result.getRows()) { 57 | assertTrue(resultRow.getValues().containsKey("id")); 58 | assertEquals(++i, resultRow.getValues().get("id")); 59 | } 60 | } 61 | 62 | private void changePassword() { 63 | String json = "{\"password\":\"" + NEO4J_CUSTOM_PASSWORD + "\"}"; 64 | try { 65 | httpClient.post(NEO4J_SERVER_URL + "/user/neo4j/password", json, getHeaders(NEO4J_CUSTOM_USER, "neo4j"), 200); 66 | } catch (AssertionError e) { 67 | // password was already changed in a previous test and the dbms auth directory is already existing 68 | } 69 | } 70 | 71 | private HashMap getHeaders(String user, String password) { 72 | HashMap headers = new HashMap<>(); 73 | try { 74 | String credentials = user + ":" + password; 75 | headers.put("Authorization", "Basic " + Base64.encodeBase64String(credentials.getBytes("UTF-8"))); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } 79 | 80 | return headers; 81 | } 82 | 83 | private String getJsonBody(String query) throws Exception { 84 | HashMap body = new HashMap<>(); 85 | HashMap statement = new HashMap<>(); 86 | List> statements = new ArrayList<>(); 87 | statement.put("statement", query); 88 | statements.add(statement); 89 | body.put("statements", statements); 90 | 91 | return ObjectMapper.class.newInstance().writeValueAsString(body); 92 | } 93 | 94 | @After 95 | public void tearDown() { 96 | server.stop(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/domain/CypherEndpointUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.domain; 2 | 3 | import com.graphaware.es.gas.cypher.CypherEndPoint; 4 | import com.graphaware.es.gas.cypher.CypherEndPointBuilder; 5 | import com.graphaware.es.gas.cypher.CypherHttpEndPoint; 6 | import org.elasticsearch.common.settings.Settings; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | 16 | public class CypherEndpointUnitTest { 17 | 18 | private CypherEndPoint cypherEndPoint; 19 | 20 | private static final String NEO4J_SERVER_URL = "http://localhost:7474"; 21 | private static final String NEO4J_CUSTOM_PASSWORD = "password"; 22 | private static final String NEO4J_CUSTOM_USER = "neo4j"; 23 | 24 | @Before 25 | public void setUp() { 26 | cypherEndPoint = new CypherEndPointBuilder("http") 27 | .settings(Settings.builder().build()) 28 | .neo4jHostname(NEO4J_SERVER_URL) 29 | .password(NEO4J_CUSTOM_PASSWORD) 30 | .username(NEO4J_CUSTOM_USER) 31 | .build(); 32 | } 33 | 34 | @Test 35 | public void testBuildCypherQuery() { 36 | String query = "MATCH (n) RETURN n"; 37 | String json = ((CypherHttpEndPoint)cypherEndPoint).buildCypherQuery(query); 38 | assertEquals("{\"statements\" : [{\"statement\" : \"MATCH (n) RETURN n\"}]}", json); 39 | } 40 | 41 | @Test 42 | public void testBuildQueryWithParameters() { 43 | String query = "MATCH (n) WHERE id(n) IN {ids}"; 44 | HashMap parameters = new HashMap<>(); 45 | List ids = new ArrayList<>(); 46 | ids.add(1); 47 | ids.add(2); 48 | ids.add(3); 49 | parameters.put("ids", ids); 50 | String json = ((CypherHttpEndPoint)cypherEndPoint).buildCypherQuery(query, parameters); 51 | assertEquals("{\"statements\" : [{\"statement\" : \"MATCH (n) WHERE id(n) IN {ids}\",\"parameters\":{\"ids\":[1,2,3]}}]}", json); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/domain/ExternalResultTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.domain; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class ExternalResultTest { 8 | 9 | @Test 10 | public void testNewInstance() { 11 | ExternalResult result = new ExternalResult("123", 1.25f); 12 | assertEquals("123", result.getObjectId()); 13 | assertEquals(1.25f, result.getScore(), 0); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/domain/ExternalResultUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.domain; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class ExternalResultUnitTest { 8 | 9 | @Test 10 | public void testNewInstance() { 11 | ExternalResult externalResult = new ExternalResult("123", 23.8f); 12 | assertEquals("123", externalResult.getObjectId()); 13 | assertEquals(23.8f, externalResult.getScore(), 0); 14 | assertNotNull(externalResult.getObjectId()); 15 | } 16 | 17 | @Test 18 | public void testMethods() { 19 | ExternalResult externalResult = new ExternalResult("123", 12.0f); 20 | externalResult.setNodeId(1L); 21 | externalResult.setScore(15.23f); 22 | externalResult.setObjectId("dd-23-ee"); 23 | assertEquals(1L, externalResult.getNodeId()); 24 | assertEquals(15.23f, externalResult.getScore(), 0); 25 | assertEquals("dd-23-ee", externalResult.getObjectId()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/domain/TestIndexInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.domain; 18 | 19 | public class TestIndexInfo extends IndexInfo { 20 | 21 | public static IndexInfo newInstance() { 22 | return new IndexInfo(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/filter/GraphFilterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.filter; 18 | 19 | import com.graphaware.es.gas.domain.IndexInfo; 20 | import com.graphaware.es.gas.domain.TestIndexInfo; 21 | import org.elasticsearch.common.settings.Settings; 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | 25 | import java.util.HashMap; 26 | 27 | import static org.junit.Assert.*; 28 | 29 | public class GraphFilterTest { 30 | 31 | private SearchResultCypherFilter filter; 32 | 33 | @Before 34 | public void setUp() { 35 | Settings.Builder builder = Settings.builder(); 36 | IndexInfo indexInfo = TestIndexInfo.newInstance(); 37 | filter = new SearchResultCypherFilter(builder.build(), indexInfo); 38 | } 39 | 40 | @Test 41 | public void testQueryParameterIsMandatory() { 42 | HashMap sourceMap = new HashMap<>(); 43 | sourceMap.put("gas-filter", new HashMap()); 44 | 45 | try { 46 | filter.parseRequest(sourceMap); 47 | fail(); 48 | } catch (Exception e) { 49 | assertTrue(e.getMessage().contains("Query Parameter is required")); 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/filter/SearchResultCypherFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.filter; 2 | 3 | import com.graphaware.es.gas.domain.IndexInfo; 4 | import com.graphaware.es.gas.domain.TestIndexInfo; 5 | import org.elasticsearch.common.settings.Settings; 6 | import org.junit.Test; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | public class SearchResultCypherFilterTest { 14 | 15 | @Test 16 | public void testSizeIsParsedFromRequest() { 17 | SearchResultCypherFilter filter = getFilter(); 18 | HashMap map = getDefaultMap(); 19 | map.put("size", 25); 20 | filter.parseRequest(map); 21 | assertEquals(25, filter.getSize()); 22 | } 23 | 24 | @Test 25 | public void testSizeHasDefault() { 26 | SearchResultCypherFilter filter = getFilter(); 27 | HashMap map = getDefaultMap(); 28 | filter.parseRequest(map); 29 | assertEquals(10, filter.getSize()); 30 | } 31 | 32 | @Test 33 | public void testFromIsParsedFromRequest() { 34 | SearchResultCypherFilter filter = getFilter(); 35 | HashMap map = getDefaultMap(); 36 | map.put("from", 100); 37 | filter.parseRequest(map); 38 | assertEquals(100, filter.getFrom()); 39 | } 40 | 41 | @Test 42 | public void testFromHasDefault() { 43 | SearchResultCypherFilter filter = getFilter(); 44 | HashMap map = getDefaultMap(); 45 | filter.parseRequest(map); 46 | assertEquals(0, filter.getFrom()); 47 | } 48 | 49 | @Test 50 | public void testIdResultNameHasDefault() { 51 | SearchResultCypherFilter filter = getFilter(); 52 | assertEquals("id", filter.getIdResultName()); 53 | } 54 | 55 | @Test 56 | public void testIdResultNameCanBeCustomized() { 57 | SearchResultCypherFilter filter = getFilter(); 58 | Map map = getDefaultMap(); 59 | Map gasFilter = (Map) map.get("gas-filter"); 60 | gasFilter.put("identifier", "uuid"); 61 | filter.parseRequest(map); 62 | assertEquals("uuid", filter.getIdResultName()); 63 | } 64 | 65 | private HashMap getDefaultMap() { 66 | HashMap map = new HashMap<>(); 67 | HashMap gasFilter = new HashMap<>(); 68 | gasFilter.put("query", "MATCH (n) RETURN n"); 69 | map.put("gas-filter", gasFilter); 70 | 71 | return map; 72 | } 73 | 74 | private SearchResultCypherFilter getFilter() { 75 | Settings.Builder builder = Settings.builder(); 76 | IndexInfo indexInfo = TestIndexInfo.newInstance(); 77 | 78 | return new SearchResultCypherFilter(builder.build(), indexInfo); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/stubs/CypherSearchResultTestBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | 17 | package com.graphaware.es.gas.stubs; 18 | 19 | import com.graphaware.es.gas.annotation.SearchBooster; 20 | import com.graphaware.es.gas.booster.SearchResultCypherBooster; 21 | import com.graphaware.es.gas.domain.ExternalResult; 22 | import com.graphaware.es.gas.domain.IndexInfo; 23 | import org.elasticsearch.common.settings.Settings; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import java.util.Set; 28 | 29 | @SearchBooster(name = "CypherSearchResultTestBooster") 30 | public class CypherSearchResultTestBooster extends SearchResultCypherBooster { 31 | 32 | public CypherSearchResultTestBooster(Settings settings, IndexInfo indexInfo) { 33 | super(settings, indexInfo); 34 | } 35 | 36 | @Override 37 | protected Map getExternalResults(Set keySet) { 38 | 39 | Map results = new HashMap<>(); 40 | for (int i = 0; i < 1000; ++i) { 41 | results.put(String.valueOf(i), new ExternalResult(String.valueOf(i), 1000.0f*i)); 42 | } 43 | return results; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/stubs/CypherSearchResultTestFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.stubs; 17 | 18 | import com.graphaware.es.gas.annotation.SearchFilter; 19 | import com.graphaware.es.gas.domain.IndexInfo; 20 | import com.graphaware.es.gas.filter.SearchResultCypherFilter; 21 | 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | import org.elasticsearch.common.settings.Settings; 26 | 27 | @SearchFilter(name = "CypherSearchResultTestFilter") 28 | public class CypherSearchResultTestFilter extends SearchResultCypherFilter { 29 | 30 | public CypherSearchResultTestFilter(Settings settings, IndexInfo indexSettings) { 31 | super(settings, indexSettings); 32 | } 33 | 34 | @Override 35 | protected Set getFilteredItems() { 36 | Set result = new HashSet<>(); 37 | for (int i = 1; i <= 1000; i++) { 38 | if (i%3 == 0) { 39 | result.add(String.valueOf(i)); 40 | } 41 | } 42 | return result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/stubs/SearchResultTestBooster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 GraphAware 3 | * 4 | * This file is part of the GraphAware Framework. 5 | * 6 | * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | * the GNU General Public License as published by the Free Software Foundation, either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | * See the GNU General Public License for more details. You should have received a copy of 13 | * the GNU General Public License along with this program. If not, see 14 | * . 15 | */ 16 | package com.graphaware.es.gas.stubs; 17 | 18 | import com.graphaware.es.gas.annotation.SearchBooster; 19 | import com.graphaware.es.gas.domain.ExternalResult; 20 | import com.graphaware.es.gas.domain.IndexInfo; 21 | import com.graphaware.es.gas.booster.SearchResultExternalBooster; 22 | 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | import java.util.Set; 26 | 27 | import org.elasticsearch.common.settings.Settings; 28 | 29 | @SearchBooster(name = "SearchResultTestBooster") 30 | public class SearchResultTestBooster extends SearchResultExternalBooster { 31 | 32 | 33 | public SearchResultTestBooster(Settings settings, IndexInfo indexSettings) { 34 | super(settings, indexSettings); 35 | } 36 | 37 | @Override 38 | protected Map externalDoReorder(Set keySet) { 39 | Map result = new HashMap<>(); 40 | for (String id : keySet) { 41 | result.put(id, new ExternalResult(id, Integer.parseInt(id) * 1000)); 42 | } 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/util/ClauseConstantsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.graphaware.es.gas.util; 7 | 8 | import com.graphaware.es.gas.domain.ClauseConstants; 9 | 10 | import static org.junit.Assert.*; 11 | import java.lang.reflect.*; 12 | import org.junit.Test; 13 | 14 | public class ClauseConstantsTest { 15 | 16 | @Test 17 | public void testConstants() { 18 | try { 19 | assertUtilityClassWellDefined(ClauseConstants.class); 20 | } catch (NoSuchMethodException ex) { 21 | fail("NoSuchMethodException" + ex.getMessage()); 22 | } catch (InvocationTargetException ex) { 23 | fail("InvocationTargetException" + ex.getMessage()); 24 | } catch (InstantiationException ex) { 25 | fail("InstantiationException" + ex.getMessage()); 26 | } catch (IllegalAccessException ex) { 27 | fail("IllegalAccessException" + ex.getMessage()); 28 | } 29 | } 30 | 31 | public static void assertUtilityClassWellDefined(Class clazz) 32 | throws NoSuchMethodException, InvocationTargetException, 33 | InstantiationException, IllegalAccessException { 34 | assertTrue("class must be public", 35 | Modifier.isPublic(clazz.getModifiers())); 36 | assertTrue("class must be final", 37 | Modifier.isFinal(clazz.getModifiers())); 38 | assertEquals("There must be only one constructor", 1, 39 | clazz.getDeclaredConstructors().length); 40 | final Constructor constructor = clazz.getDeclaredConstructor(); 41 | if (constructor.isAccessible() || !Modifier.isPrivate(constructor.getModifiers())) { 42 | fail("constructor is not private"); 43 | } 44 | constructor.setAccessible(true); 45 | constructor.newInstance(); 46 | constructor.setAccessible(false); 47 | for (final Method method : clazz.getMethods()) { 48 | if (!Modifier.isStatic(method.getModifiers()) 49 | && method.getDeclaringClass().equals(clazz)) { 50 | fail("there exists a non-static method:" + method); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/util/NumberUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.util; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class NumberUtilTest { 8 | 9 | private static final int DEFAULT_VALUE = 10; 10 | 11 | @Test 12 | public void testDefaultValueIsReturned() { 13 | assertEquals(DEFAULT_VALUE, NumberUtil.getInt(null, DEFAULT_VALUE)); 14 | } 15 | 16 | @Test 17 | public void testIntegersAreReturnedWhenIntegersGiven() { 18 | assertEquals(10, NumberUtil.getInt(10, DEFAULT_VALUE)); 19 | } 20 | 21 | @Test 22 | public void testIntegerIsReturnedWhenFloatIsGiven() { 23 | assertEquals(12, NumberUtil.getInt(12.04f, DEFAULT_VALUE)); 24 | } 25 | 26 | @Test 27 | public void testIntegerIsReturnedWhenStringIsGiven() { 28 | assertEquals(110, NumberUtil.getInt("110", DEFAULT_VALUE)); 29 | } 30 | 31 | @Test 32 | public void testIntegerIsReturnedAsFloat() { 33 | assertEquals(10.0f, NumberUtil.getFloat(10), 0); 34 | } 35 | 36 | @Test 37 | public void testFloatIsReturnedAsFloat() { 38 | assertEquals(12.375f, NumberUtil.getFloat(12.375f), 0); 39 | } 40 | 41 | @Test 42 | public void testStringIsReturnedAsFloat() { 43 | assertEquals(12.678f, NumberUtil.getFloat("12.678"), 0); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/util/TestHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.util; 2 | 3 | import org.apache.http.HttpResponse; 4 | import org.apache.http.HttpStatus; 5 | import org.apache.http.client.ResponseHandler; 6 | import org.apache.http.client.methods.*; 7 | import org.apache.http.entity.ContentType; 8 | import org.apache.http.entity.StringEntity; 9 | import org.apache.http.impl.client.CloseableHttpClient; 10 | import org.apache.http.impl.client.HttpClientBuilder; 11 | import org.apache.http.params.BasicHttpParams; 12 | import org.apache.http.params.HttpParams; 13 | import org.apache.http.util.EntityUtils; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.IOException; 18 | import java.util.Collections; 19 | import java.util.Map; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | import static org.junit.Assert.fail; 23 | 24 | /** 25 | * Http client for testing APIs. 26 | */ 27 | public class TestHttpClient { 28 | 29 | private static final Logger LOG = LoggerFactory.getLogger(TestHttpClient.class); 30 | 31 | private final CloseableHttpClient client; 32 | 33 | /** 34 | * Create a default client. 35 | */ 36 | public TestHttpClient() { 37 | this(HttpClientBuilder.create()); 38 | } 39 | 40 | /** 41 | * Create a client from the provided builder. 42 | * 43 | * @param builder builder. 44 | */ 45 | public TestHttpClient(HttpClientBuilder builder) { 46 | this.client = builder.build(); 47 | } 48 | 49 | /** 50 | * Issue an HTTP GET and assert the response status code. 51 | * 52 | * @param url to GET. 53 | * @param expectedStatusCode expected status code. 54 | * @return the body of the response. 55 | */ 56 | public String get(String url, final int expectedStatusCode) { 57 | return get(url, Collections.emptyMap(), expectedStatusCode); 58 | } 59 | 60 | /** 61 | * Issue an HTTP GET and assert the response status code. 62 | * 63 | * @param url to GET. 64 | * @param headers request headers as map. 65 | * @param expectedStatusCode expected status code. 66 | * @return the body of the response. 67 | */ 68 | public String get(String url, Map headers, final int expectedStatusCode) { 69 | HttpGet method = new HttpGet(url); 70 | 71 | setHeaders(method, headers); 72 | 73 | return method(method, expectedStatusCode); 74 | } 75 | 76 | /** 77 | * Issue an HTTP POST with empty body and assert the response status code. 78 | * 79 | * @param url to POST to. 80 | * @param expectedStatusCode expected status code. 81 | * @return the body of the response. 82 | */ 83 | public String post(String url, final int expectedStatusCode) { 84 | return post(url, null, expectedStatusCode); 85 | } 86 | 87 | /** 88 | * Issue an HTTP POST and assert the response status code. 89 | * 90 | * @param url to POST to. 91 | * @param json request body. 92 | * @param expectedStatusCode expected status code. 93 | * @return the body of the response. 94 | */ 95 | public String post(String url, String json, final int expectedStatusCode) { 96 | return post(url, json, Collections.emptyMap(), expectedStatusCode); 97 | } 98 | 99 | /** 100 | * Issue an HTTP POST and assert the response status code. 101 | * 102 | * @param url to POST to. 103 | * @param json request body. 104 | * @param headers request headers as map. 105 | * @param expectedStatusCode expected status code. 106 | * @return the body of the response. 107 | */ 108 | public String post(String url, String json, Map headers, final int expectedStatusCode) { 109 | return post(url, json, headers, Collections.emptyMap(), expectedStatusCode); 110 | } 111 | 112 | /** 113 | * Issue an HTTP POST and assert the response status code. 114 | * 115 | * @param url to POST to. 116 | * @param json request body. 117 | * @param headers request headers as map. 118 | * @param params request parameters as map. 119 | * @param expectedStatusCode expected status code. 120 | * @return the body of the response. 121 | */ 122 | public String post(String url, String json, Map headers, Map params, final int expectedStatusCode) { 123 | HttpPost post = new HttpPost(url); 124 | if (json != null) { 125 | post.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); 126 | } 127 | 128 | setHeaders(post, headers); 129 | setParams(post, params); 130 | 131 | return method(post, expectedStatusCode); 132 | } 133 | 134 | /** 135 | * Issue an HTTP PUT with empty body and assert the response status code. 136 | * 137 | * @param url to PUT to. 138 | * @param expectedStatusCode expected status code. 139 | * @return the body of the response. 140 | */ 141 | public String put(String url, final int expectedStatusCode) { 142 | return put(url, null, expectedStatusCode); 143 | } 144 | 145 | /** 146 | * Issue an HTTP PUT and assert the response status code. 147 | * 148 | * @param url to POST to. 149 | * @param json request body. 150 | * @param expectedStatusCode expected status code. 151 | * @return the body of the response. 152 | */ 153 | public String put(String url, String json, final int expectedStatusCode) { 154 | return put(url, json, Collections.emptyMap(), expectedStatusCode); 155 | } 156 | 157 | /** 158 | * Issue an HTTP PUT and assert the response status code. 159 | * 160 | * @param url to POST to. 161 | * @param json request body. 162 | * @param headers request headers as map. 163 | * @param expectedStatusCode expected status code. 164 | * @return the body of the response. 165 | */ 166 | public String put(String url, String json, Map headers, final int expectedStatusCode) { 167 | HttpPut put = new HttpPut(url); 168 | if (json != null) { 169 | put.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); 170 | } 171 | 172 | setHeaders(put, headers); 173 | 174 | return method(put, expectedStatusCode); 175 | } 176 | 177 | /** 178 | * Issue an HTTP DELETE and assert the response status code. 179 | * 180 | * @param url to DELETE. 181 | * @param expectedStatusCode expected status code. 182 | * @return the body of the response. 183 | */ 184 | public String delete(String url, final int expectedStatusCode) { 185 | return delete(url, Collections.emptyMap(), expectedStatusCode); 186 | } 187 | 188 | /** 189 | * Issue an HTTP DELETE and assert the response status code. 190 | * 191 | * @param url to DELETE. 192 | * @param headers request headers as map. 193 | * @param expectedStatusCode expected status code. 194 | * @return the body of the response. 195 | */ 196 | public String delete(String url, Map headers, final int expectedStatusCode) { 197 | HttpDelete method = new HttpDelete(url); 198 | 199 | setHeaders(method, headers); 200 | 201 | return method(method, expectedStatusCode); 202 | } 203 | 204 | /** 205 | * Issue a HTTP call and assert the response status code. 206 | * 207 | * @param method HTTP method. 208 | * @param expectedStatusCode expected status code. 209 | * @return the body of the response. 210 | */ 211 | public String method(HttpRequestBase method, final int expectedStatusCode) { 212 | try { 213 | String result = client.execute(method, responseHandler(expectedStatusCode)); 214 | 215 | LOG.debug("HTTP " + method.getMethod() + " returned: " + result); 216 | 217 | return result; 218 | 219 | } catch (IOException e) { 220 | fail(e.getMessage()); 221 | return null; 222 | } 223 | } 224 | 225 | /** 226 | * Execute a set of cypher statements against a database in a single transaction. 227 | * 228 | * @param serverUrl URL of the database server. 229 | * @param cypherStatements to execute. 230 | * @return body of the server response. 231 | */ 232 | public String executeCypher(String serverUrl, String... cypherStatements) { 233 | return executeCypher(serverUrl, Collections.emptyMap(), cypherStatements); 234 | } 235 | 236 | /** 237 | * Execute a set of cypher statements against a database in a single transaction. 238 | * 239 | * @param serverUrl URL of the database server. 240 | * @param headers request headers as map. 241 | * @param cypherStatements to execute. 242 | * @return body of the server response. 243 | */ 244 | public String executeCypher(String serverUrl, Map headers, String... cypherStatements) { 245 | StringBuilder stringBuilder = new StringBuilder("{\"statements\" : ["); 246 | for (String statement : cypherStatements) { 247 | stringBuilder.append("{\"statement\" : \"").append(statement).append("\"}").append(","); 248 | } 249 | stringBuilder.deleteCharAt(stringBuilder.length() - 1); 250 | 251 | stringBuilder.append("]}"); 252 | 253 | while (serverUrl.endsWith("/")) { 254 | serverUrl = serverUrl.substring(0, serverUrl.length() - 1); 255 | } 256 | 257 | return post(serverUrl + "/db/data/transaction/commit", stringBuilder.toString(), headers, HttpStatus.SC_OK); 258 | } 259 | 260 | protected void setHeaders(HttpRequestBase method, Map headers) { 261 | if (headers == null) { 262 | return; 263 | } 264 | 265 | for (Map.Entry headerEntry : headers.entrySet()) { 266 | method.setHeader(headerEntry.getKey(), headerEntry.getValue()); 267 | } 268 | } 269 | 270 | protected void setParams(HttpRequestBase method, Map params) { 271 | if (params == null) { 272 | return; 273 | } 274 | 275 | HttpParams httpParams = new BasicHttpParams(); 276 | for (Map.Entry paramEntry : params.entrySet()) { 277 | httpParams.setParameter(paramEntry.getKey(), paramEntry.getValue()); 278 | } 279 | method.setParams(httpParams); 280 | } 281 | 282 | /** 283 | * Create a response handler. By default, it returns the body of the response and verifies expected status code 284 | * using JUnit assert. 285 | * 286 | * @param expectedStatusCode expected status code. 287 | * @return response body. Can be null of the response has no body. 288 | */ 289 | protected ResponseHandler responseHandler(final int expectedStatusCode) { 290 | return new ResponseHandler() { 291 | @Override 292 | public String handleResponse(final HttpResponse response) throws IOException { 293 | String body = null; 294 | if (response.getEntity() != null) { 295 | body = EntityUtils.toString(response.getEntity()); 296 | } 297 | assertEquals("Expected and actual status codes don't match. Response body: " + body, expectedStatusCode, response.getStatusLine().getStatusCode()); 298 | return body; 299 | } 300 | }; 301 | } 302 | 303 | public void close() { 304 | try { 305 | client.close(); 306 | } catch (IOException e) { 307 | throw new RuntimeException(e); 308 | } 309 | } 310 | } -------------------------------------------------------------------------------- /src/test/java/com/graphaware/es/gas/util/UrlUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.graphaware.es.gas.util; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | public class UrlUtilTest { 8 | 9 | @Test 10 | public void buildEndpointTest() { 11 | String neo4jHost = "http://localhost:7474/"; 12 | String boosterEndpoint = "/reco/engine//"; 13 | String recoId = "15"; 14 | String endpoint = UrlUtil.buildUrlFromParts(neo4jHost, boosterEndpoint, recoId); 15 | assertEquals("http://localhost:7474/reco/engine/15", endpoint); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/resources/neo4j-elasticsearch-reco.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2013-2016 GraphAware 3 | # 4 | # This file is part of the GraphAware Framework. 5 | # 6 | # GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | # the GNU General Public License as published by the Free Software Foundation, either 8 | # version 3 of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | # See the GNU General Public License for more details. You should have received a copy of 13 | # the GNU General Public License along with this program. If not, see 14 | # . 15 | # 16 | 17 | # GraphAware Config 18 | com.graphaware.runtime.enabled=true 19 | 20 | com.graphaware.module.UIDM.1=com.graphaware.module.uuid.UuidBootstrapper 21 | 22 | com.graphaware.module.ES.2=com.graphaware.module.es.ElasticSearchModuleBootstrapper 23 | com.graphaware.module.ES.uri=localhost 24 | com.graphaware.module.ES.port=9201 25 | com.graphaware.module.ES.index=neo4jes 26 | 27 | #com.graphaware.module.reco.2=com.graphaware.reco.neo4j.module.RecommendationModuleBootstrapper 28 | #com.graphaware.module.reco.node=hasLabel('Person') 29 | #com.graphaware.module.reco.engine=com.graphaware.elasticsearch.reco.demo.engine.RecruitingRecoEngine 30 | #com.graphaware.module.reco.maxRecommendations=5 -------------------------------------------------------------------------------- /src/test/resources/neo4j-elasticsearch.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2013-2016 GraphAware 3 | # 4 | # This file is part of the GraphAware Framework. 5 | # 6 | # GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | # the GNU General Public License as published by the Free Software Foundation, either 8 | # version 3 of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | # See the GNU General Public License for more details. You should have received a copy of 13 | # the GNU General Public License along with this program. If not, see 14 | # . 15 | # 16 | 17 | # GraphAware Config 18 | com.graphaware.runtime.enabled=true 19 | com.graphaware.module.ES.2=com.graphaware.module.es.ElasticSearchModuleBootstrapper 20 | com.graphaware.module.ES.uri=localhost 21 | com.graphaware.module.ES.port=9201 22 | com.graphaware.module.ES.index=neo4jes 23 | com.graphaware.module.ES.keyProperty=uuid 24 | 25 | 26 | com.graphaware.module.UIDM.1=com.graphaware.module.uuid.UuidBootstrapper 27 | com.graphaware.module.UIDM.uuidProperty=uuid 28 | com.graphaware.module.UIDM.uuidIndex=nodeIndex 29 | #com.graphaware.module.UIDM.node=hasLabel('Person') || hasLabel('Company') -------------------------------------------------------------------------------- /src/test/resources/neo4j-server-es.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2013-2016 GraphAware 3 | # 4 | # This file is part of the GraphAware Framework. 5 | # 6 | # GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of 7 | # the GNU General Public License as published by the Free Software Foundation, either 8 | # version 3 of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 11 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | # See the GNU General Public License for more details. You should have received a copy of 13 | # the GNU General Public License along with this program. If not, see 14 | # . 15 | # 16 | 17 | org.neo4j.server.database.location=data/graph.db 18 | dbms.security.auth_enabled=false 19 | org.neo4j.server.webserver.port=7575 20 | --------------------------------------------------------------------------------