org.nuxeo.elasticsearch.test.nxql.TestNxqlConversion.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.elasticsearch.test.nxql.TestNxqlConversion.java

Source

/*
 * (C) Copyright 2014-2016 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Nuxeo
 */

package org.nuxeo.elasticsearch.test.nxql;

import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import org.apache.commons.lang.SystemUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.work.api.WorkManager;
import org.nuxeo.elasticsearch.api.ElasticSearchAdmin;
import org.nuxeo.elasticsearch.api.ElasticSearchIndexing;
import org.nuxeo.elasticsearch.api.ElasticSearchService;
import org.nuxeo.elasticsearch.query.NxQueryBuilder;
import org.nuxeo.elasticsearch.query.NxqlQueryConverter;
import org.nuxeo.elasticsearch.test.RepositoryElasticSearchFeature;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.test.runner.Features;
import org.nuxeo.runtime.test.runner.FeaturesRunner;
import org.nuxeo.runtime.test.runner.LocalDeploy;
import org.nuxeo.runtime.transaction.TransactionHelper;

/**
 * Test that NXQL can be used to generate ES queries
 *
 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
 */
@RunWith(FeaturesRunner.class)
@Features({ RepositoryElasticSearchFeature.class })
@LocalDeploy({ "org.nuxeo.elasticsearch.core:elasticsearch-test-contrib.xml" })
public class TestNxqlConversion {

    private static final String IDX_NAME = "nxutest";

    private static final String TYPE_NAME = "doc";

    @Inject
    protected CoreSession session;

    @Inject
    protected ElasticSearchService ess;

    @Inject
    protected ElasticSearchAdmin esa;

    @Inject
    protected ElasticSearchIndexing esi;

    protected void buildDocs() throws Exception {
        for (int i = 0; i < 10; i++) {
            String name = "doc" + i;
            DocumentModel doc = session.createDocumentModel("/", name, "File");
            doc.setPropertyValue("dc:title", "File" + i);
            doc.setPropertyValue("dc:nature", "Nature" + i);
            doc.setPropertyValue("dc:rights", "Rights" + i % 2);
            doc = session.createDocument(doc);
        }
        TransactionHelper.commitOrRollbackTransaction();
        // wait for async jobs
        WorkManager wm = Framework.getLocalService(WorkManager.class);
        Assert.assertTrue(wm.awaitCompletion(20, TimeUnit.SECONDS));
        Assert.assertEquals(0, esa.getPendingWorkerCount());

        esa.refresh();

        TransactionHelper.startTransaction();

    }

    @Test
    public void testQuery() throws Exception {

        buildDocs();

        SearchResponse searchResponse = esa.getClient().prepareSearch(IDX_NAME).setTypes(TYPE_NAME)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(QueryBuilders.queryStringQuery(" dc\\:nature:\"Nature1\" AND dc\\:title:\"File1\""))
                .setFrom(0).setSize(60).execute().actionGet();
        Assert.assertEquals(1, searchResponse.getHits().getTotalHits());

        searchResponse = esa.getClient().prepareSearch(IDX_NAME).setTypes(TYPE_NAME)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(QueryBuilders.queryStringQuery(" dc\\:nature:\"Nature2\" AND dc\\:title:\"File1\""))
                .setFrom(0).setSize(60).execute().actionGet();
        Assert.assertEquals(0, searchResponse.getHits().getTotalHits());

        searchResponse = esa.getClient().prepareSearch(IDX_NAME).setTypes(TYPE_NAME)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(QueryBuilders.queryStringQuery(" NOT " + "dc\\:nature:\"Nature2\"")).setFrom(0)
                .setSize(60).execute().actionGet();
        Assert.assertEquals(9, searchResponse.getHits().getTotalHits());

        checkNXQL("select * from Document where dc:nature='Nature2' and dc:title='File2'", 1);
        checkNXQL("select * from Document where dc:nature='Nature2' and dc:title='File1'", 0);
        checkNXQL("select * from Document where dc:nature='Nature2' or dc:title='File1'", 2);
    }

    @Test
    public void testQueryLimits() throws Exception {
        buildDocs();

        // limit does not change the total size, only the returned number of docs
        DocumentModelList docs = ess.query(new NxQueryBuilder(session).nxql("select * from Document").limit(1));
        Assert.assertEquals(10, docs.totalSize());
        Assert.assertEquals(1, docs.size());
        // default is 10
        docs = ess.query(new NxQueryBuilder(session).nxql("select * from Document"));
        Assert.assertEquals(10, docs.totalSize());
        Assert.assertEquals(10, docs.size());
        // only interested about totalSize
        docs = ess.query(new NxQueryBuilder(session).nxql("select * from Document").limit(0));
        Assert.assertEquals(10, docs.totalSize());
        Assert.assertEquals(0, docs.size());
    }

    @Test
    public void testQueryWithSpecialCharacters() throws Exception {
        // special character should not raise syntax error
        String specialChars = "^..*+ - && || ! ( ) { } [ ] )^ \" (~ * ? : \\ / \\t$";
        checkNXQL("select * from Document where dc:title = '" + specialChars + "'", 0);
        checkNXQL("select * from Document where ecm:fulltext.dc:title = '" + specialChars + "'", 0);
        checkNXQL("select * from Document where dc:title LIKE '" + specialChars + "'", 0);
        checkNXQL("select * from Document where dc:title IN ('" + specialChars + "')", 0);
        checkNXQL("select * from Document where dc:title STARTSWITH '" + specialChars + "'", 0);
    }

    protected void checkNXQL(String nxql, int expectedNumberOfHis) {
        // System.out.println(NXQLQueryConverter.toESQueryString(nxql));
        DocumentModelList docs = ess.query(new NxQueryBuilder(session).nxql(nxql).limit(0));
        Assert.assertEquals(expectedNumberOfHis, docs.totalSize());
    }

    @Test
    public void testConverterSelect() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match_all\" : { }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from File, Document").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match_all\" : { }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from File").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : {\n" + //
                "      \"match_all\" : { }\n" + //
                "    },\n" + //
                "    \"filter\" : {\n" + //
                "      \"terms\" : {\n" + //
                "        \"ecm:primaryType\" : [ \"File\" ]\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from File, Note").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : {\n" + //
                "      \"match_all\" : { }\n" + //
                "    },\n" + //
                "    \"filter\" : {\n" + //
                "      \"terms\" : {\n" + //
                "        \"ecm:primaryType\" : [ \"File\", \"Note\" ]\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterEQUALS() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1=1").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"f1\" : \"1\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 != 1").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f1\" : \"1\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 <> 1").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f1\" : \"1\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

    }

    @Test
    public void testConverterIN() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 IN (1)").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"terms\" : {\n" + //
                "        \"f1\" : [ \"1\" ]\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 NOT IN (1, '2', 3)").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"terms\" : {\n" + //
                "            \"f1\" : [ \"1\", \"2\", \"3\" ]\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterLIKE() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 LIKE 'foo%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match\" : {\n" + //
                "    \"f1\" : {\n" + //
                "      \"query\" : \"foo\",\n" + //
                "      \"type\" : \"phrase_prefix\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 LIKE '%Foo%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1\" : \"*Foo*\"\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 NOT LIKE 'Foo%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"match\" : {\n" + //
                "            \"f1\" : {\n" + //
                "              \"query\" : \"Foo\",\n" + //
                "              \"type\" : \"phrase_prefix\"\n" + //
                "            }\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        // invalid input
        NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 LIKE '(foo.*$#@^'").toString();
    }

    @Test
    public void testConverterLIKEWildcard() throws Exception {
        String es;
        es = NxqlQueryConverter.toESQueryBuilder("SELECT * FROM Document WHERE f1 LIKE '%foo'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1\" : \"*foo\"\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("SELECT * FROM Document WHERE f1 LIKE '_foo'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1\" : \"?foo\"\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("SELECT * FROM Document WHERE f1 LIKE '?foo'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1\" : \"\\\\?foo\"\n" // backslash escaped for JSON
                + "  }\n" + //
                "}", es);
        // * is also accepted as a wildcard (compat)
        es = NxqlQueryConverter.toESQueryBuilder("SELECT * FROM Document WHERE f1 LIKE '*foo'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1\" : \"*foo\"\n" + //
                "  }\n" + //
                "}", es);
        // NXQL escaping
        es = NxqlQueryConverter.toESQueryBuilder("SELECT * FROM Document WHERE f1 LIKE 'foo\\_bar\\%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1\" : \"foo_bar%\"\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterILIKE() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 ILIKE 'Foo%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match\" : {\n" + //
                "    \"f1.lowercase\" : {\n" + //
                "      \"query\" : \"foo\",\n" + //
                "      \"type\" : \"phrase_prefix\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 ILIKE '%Foo%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"f1.lowercase\" : \"*foo*\"\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 NOT ILIKE 'Foo%'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"match\" : {\n" + //
                "            \"f1.lowercase\" : {\n" + //
                "              \"query\" : \"foo\",\n" + //
                "              \"type\" : \"phrase_prefix\"\n" + //
                "            }\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterIsNULL() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 IS NULL").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"exists\" : {\n" + //
                "            \"field\" : \"f1\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 IS NOT NULL").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"exists\" : {\n" + //
                "        \"field\" : \"f1\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterBETWEEN() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 BETWEEN 1 AND 2")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"range\" : {\n" + //
                "        \"f1\" : {\n" + //
                "          \"from\" : \"1\",\n" + //
                "          \"to\" : \"2\",\n" + //
                "          \"include_lower\" : true,\n" + //
                "          \"include_upper\" : true\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1 NOT BETWEEN 1 AND 2").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"range\" : {\n" + //
                "            \"f1\" : {\n" + //
                "              \"from\" : \"1\",\n" + //
                "              \"to\" : \"2\",\n" + //
                "              \"include_lower\" : true,\n" + //
                "              \"include_upper\" : true\n" + //
                "            }\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterSTARTSWITH() throws Exception {
        String es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where ecm:path STARTSWITH '/the/path'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"ecm:path.children\" : \"/the/path\"\n" + //
                "          }\n" + //
                "        },\n" + //
                "        \"must_not\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"ecm:path\" : \"/the/path\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where ecm:path STARTSWITH '/'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"exists\" : {\n" + //
                "        \"field\" : \"ecm:path.children\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where ecm:path STARTSWITH '/the/path/'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"ecm:path.children\" : \"/the/path\"\n" + //
                "          }\n" + //
                "        },\n" + //
                "        \"must_not\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"ecm:path\" : \"/the/path/\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        // for other field than ecm:path we want to match the root
        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where dc:coverage STARTSWITH 'Europe/France'").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"dc:coverage.children\" : \"Europe/France\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterAncestorId() throws Exception {
        String es = NxqlQueryConverter
                .toESQueryBuilder(
                        "select * from Document where ecm:ancestorId = 'c5904f77-299a-411e-8477-81d3102a81f9'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"exists\" : {\n" + //
                "        \"field\" : \"ancestorid-without-session\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where ecm:ancestorId != 'c5904f77-299a-411e-8477-81d3102a81f9'", session)
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"exists\" : {\n" + //
                "            \"field\" : \"ancestorid-not-found\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterIsVersion() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where ecm:isVersion = 1")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"ecm:isVersion\" : \"1\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        String es2 = NxqlQueryConverter.toESQueryBuilder("select * from Document where ecm:isCheckedInVersion = 1")
                .toString();
        assertEqualsEvenUnderWindows(es, es2);
    }

    @Test
    public void testConverterFulltext() throws Exception {
        // Given a search on a fulltext field
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where ecm:fulltext='+foo -bar'")
                .toString();
        // then we have a simple query text and not a filter
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"simple_query_string\" : {\n" + //
                "    \"query\" : \"+foo -bar\",\n" + //
                "    \"fields\" : [ \"_all\" ],\n" + //
                "    \"analyzer\" : \"fulltext\",\n" + //
                "    \"default_operator\" : \"and\"\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where ecm:fulltext_someindex LIKE '+foo -bar'")
                .toString();
        // don't handle nxql fulltext index definition, match to _all field
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"simple_query_string\" : {\n" + //
                "    \"query\" : \"+foo -bar\",\n" + //
                "    \"fields\" : [ \"_all\" ],\n" + //
                "    \"analyzer\" : \"fulltext\",\n" + //
                "    \"default_operator\" : \"and\"\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where ecm:fulltext.dc:title!='+foo -bar'")
                .toString();
        // request on field match field.fulltext
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"simple_query_string\" : {\n" + //
                "            \"query\" : \"+foo -bar\",\n" + //
                "            \"fields\" : [ \"dc:title.fulltext\" ],\n" + //
                "            \"analyzer\" : \"fulltext\",\n" + //
                "            \"default_operator\" : \"and\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterFulltextElasticsearchPrefix() throws Exception {
        // Given a search on a fulltext field with the
        // elasticsearch-specific prefix
        String es = NxqlQueryConverter.toESQueryBuilder("SELECT * FROM Document WHERE ecm:fulltext = 'es: foo bar'")
                .toString();
        // then we have a simple query text and not a filter
        // and we have the OR operator
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"simple_query_string\" : {\n" + //
                "    \"query\" : \"foo bar\",\n" + //
                "    \"fields\" : [ \"_all\" ],\n" + //
                "    \"analyzer\" : \"fulltext\",\n" + //
                "    \"default_operator\" : \"or\"\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterWhereCombination() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1=1 AND f2=2").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : [ {\n" + //
                "      \"constant_score\" : {\n" + //
                "        \"filter\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f1\" : \"1\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }, {\n" + //
                "      \"constant_score\" : {\n" + //
                "        \"filter\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f2\" : \"2\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    } ]\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1=1 OR f2=2").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"should\" : [ {\n" + //
                "      \"constant_score\" : {\n" + //
                "        \"filter\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f1\" : \"1\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }, {\n" + //
                "      \"constant_score\" : {\n" + //
                "        \"filter\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f2\" : \"2\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    } ]\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1=1 AND f2=2 AND f3=3").toString();
        // Assert.assertEquals("foo", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1=1 OR f2=2 OR f3=3").toString();
        // Assert.assertEquals("foo", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where f1=1 OR f2 LIKE 'foo' OR f3=3")
                .toString();
        // Assert.assertEquals("foo", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where (f1=1 OR f2=2) AND f3=3").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : [ {\n" + //
                "      \"bool\" : {\n" + //
                "        \"should\" : [ {\n" + //
                "          \"constant_score\" : {\n" + //
                "            \"filter\" : {\n" + //
                "              \"term\" : {\n" + //
                "                \"f1\" : \"1\"\n" + //
                "              }\n" + //
                "            }\n" + //
                "          }\n" + //
                "        }, {\n" + //
                "          \"constant_score\" : {\n" + //
                "            \"filter\" : {\n" + //
                "              \"term\" : {\n" + //
                "                \"f2\" : \"2\"\n" + //
                "              }\n" + //
                "            }\n" + //
                "          }\n" + //
                "        } ]\n" + //
                "      }\n" + //
                "    }, {\n" + //
                "      \"constant_score\" : {\n" + //
                "        \"filter\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f3\" : \"3\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    } ]\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterComplex() throws Exception {
        String es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where (f1 LIKE '1%' OR f2 LIKE '2%') AND f3=3")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : [ {\n" + //
                "      \"bool\" : {\n" + //
                "        \"should\" : [ {\n" + //
                "          \"match\" : {\n" + //
                "            \"f1\" : {\n" + //
                "              \"query\" : \"1\",\n" + //
                "              \"type\" : \"phrase_prefix\"\n" + //
                "            }\n" + //
                "          }\n" + //
                "        }, {\n" + //
                "          \"match\" : {\n" + //
                "            \"f2\" : {\n" + //
                "              \"query\" : \"2\",\n" + //
                "              \"type\" : \"phrase_prefix\"\n" + //
                "            }\n" + //
                "          }\n" + //
                "        } ]\n" + //
                "      }\n" + //
                "    }, {\n" + //
                "      \"constant_score\" : {\n" + //
                "        \"filter\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"f3\" : \"3\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    } ]\n" + //
                "  }\n" + //
                "}", es);
        // Assert.assertEquals("foo", es);
        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where ecm:fulltext='foo bar' AND ecm:path STARTSWITH '/foo/bar' OR ecm:path='/foo/'")
                .toString();
        // Assert.assertEquals("foo", es);

        es = NxqlQueryConverter
                .toESQueryBuilder(
                        "select * from File, Note, Workspace where f1 IN ('foo', 'bar', 'foo') AND NOT f2>=3")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must\" : [ {\n" + //
                "          \"constant_score\" : {\n" + //
                "            \"filter\" : {\n" + //
                "              \"terms\" : {\n" + //
                "                \"f1\" : [ \"foo\", \"bar\", \"foo\" ]\n" + //
                "              }\n" + //
                "            }\n" + //
                "          }\n" + //
                "        }, {\n" + //
                "          \"bool\" : {\n" + //
                "            \"must_not\" : {\n" + //
                "              \"constant_score\" : {\n" + //
                "                \"filter\" : {\n" + //
                "                  \"range\" : {\n" + //
                "                    \"f2\" : {\n" + //
                "                      \"from\" : \"3\",\n" + //
                "                      \"to\" : null,\n" + //
                "                      \"include_lower\" : true,\n" + //
                "                      \"include_upper\" : true\n" + //
                "                    }\n" + //
                "                  }\n" + //
                "                }\n" + //
                "              }\n" + //
                "            }\n" + //
                "          }\n" + //
                "        } ]\n" + //
                "      }\n" + //
                "    },\n" + //
                "    \"filter\" : {\n" + //
                "      \"terms\" : {\n" + //
                "        \"ecm:primaryType\" : [ \"File\", \"Note\", \"Workspace\" ]\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConverterWhereWithoutSelect() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("f1=1").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"f1\" : \"1\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder(null).toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match_all\" : { }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match_all\" : { }\n" + //
                "}", es);
    }

    @Test
    public void testConvertComplexProperties() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where file:content/name = 'foo'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"file:content.name\" : \"foo\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConvertComplexListProperties() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where dc:subjects/* = 'foo'")
                .toString();
        // this is supported and match any element of the list
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"dc:subjects\" : \"foo\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where files:files/*/file/length=123")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"files:files.file.length\" : \"123\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

    }

    @Test
    public void testConvertComplexListPropertiesUnsupported() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder("select * from Document where dc:subjects/3 = 'foo'")
                .toString();
        // This is not supported and generate query that is going to match nothing
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"dc:subjects.3\" : \"foo\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where dc:subjects/*1 = 'foo'").toString();
        // This is not supported and generate query that is going to match nothing
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"dc:subjects1\" : \"foo\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where files:files/*1/file/length=123")
                .toString();
        // This is not supported and generate query that is going to match nothing
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"files:files1.file.length\" : \"123\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

    }

    @Test
    public void testOrderByFromNxql() throws Exception {
        NxQueryBuilder qb = new NxQueryBuilder(session).nxql("name='foo' ORDER BY name DESC");
        String es = qb.makeQuery().toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"name\" : \"foo\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        Assert.assertEquals(1, qb.getSortInfos().size());
        Assert.assertEquals("SortInfo [sortColumn=name, sortAscending=false]", qb.getSortInfos().get(0).toString());
    }

    @Test
    public void testOrderByWithComplexProperties() throws Exception {
        NxQueryBuilder qb = new NxQueryBuilder(session).nxql("SELECT * FROM File ORDER BY file:content/name DESC");
        String es = qb.makeQuery().toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"bool\" : {\n" + //
                "    \"must\" : {\n" + //
                "      \"match_all\" : { }\n" + //
                "    },\n" + //
                "    \"filter\" : {\n" + //
                "      \"terms\" : {\n" + //
                "        \"ecm:primaryType\" : [ \"File\" ]\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        Assert.assertEquals(1, qb.getSortInfos().size());
        Assert.assertEquals("SortInfo [sortColumn=file:content.name, sortAscending=false]",
                qb.getSortInfos().get(0).toString());
    }

    @Test
    public void testConvertHint() throws Exception {
        String es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: INDEX(some:field) */ dc:title = 'foo'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"term\" : {\n" + //
                "        \"some:field\" : \"foo\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: INDEX(some:field) */ dc:title != 'foo'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"bool\" : {\n" + //
                "        \"must_not\" : {\n" + //
                "          \"term\" : {\n" + //
                "            \"some:field\" : \"foo\"\n" + //
                "          }\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
    }

    @Test
    public void testConvertHintOperator() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: INDEX(some:field) ANALYZER(my_analyzer) OPERATOR(match) */ dc:subjects = 'foo'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + "  \"match\" : {\n" + //
                "    \"some:field\" : {\n" + //
                "      \"query\" : \"foo\",\n" + //
                "      \"type\" : \"boolean\",\n" + //
                "      \"analyzer\" : \"my_analyzer\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: OPERATOR(match_phrase) */ dc:title = 'foo'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match\" : {\n" + //
                "    \"dc:title\" : {\n" + //
                "      \"query\" : \"foo\",\n" + //
                "      \"type\" : \"phrase\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: OPERATOR(match_phrase_prefix) */ dc:title = 'this is a test'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match\" : {\n" + //
                "    \"dc:title\" : {\n" + //
                "      \"query\" : \"this is a test\",\n" + //
                "      \"type\" : \"phrase_prefix\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);
        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: INDEX(dc:title^3,dc:description) OPERATOR(multi_match) */ dc:title = 'this is a test'")
                .toString();
        // fields are not ordered
        assertIn(es, "{\n" + //
                "  \"multi_match\" : {\n" + //
                "    \"query\" : \"this is a test\",\n" + //
                "    \"fields\" : [ \"dc:title^3\", \"dc:description\" ]\n" + //
                "  }\n" + //
                "}", "{\n" + //
                        "  \"multi_match\" : {\n" + //
                        "    \"query\" : \"this is a test\",\n" + //
                        "    \"fields\" : [ \"dc:description\", \"dc:title^3\" ]\n" + //
                        "  }\n" + //
                        "}");

        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: OPERATOR(regex) */ dc:title = 's.*y'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"regexp\" : {\n" + //
                "    \"dc:title\" : {\n" + //
                "      \"value\" : \"s.*y\",\n" + //
                "      \"flags_value\" : 65535\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: OPERATOR(fuzzy) */ dc:title = 'ki'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"fuzzy\" : {\n" + //
                "    \"dc:title\" : {\n" + //
                "      \"value\" : \"ki\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: OPERATOR(wildcard) */ dc:title = 'ki*y'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"dc:title\" : \"ki*y\"\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: OPERATOR(simple_query_string) */ dc:title = '\"fried eggs\" +(eggplant | potato) -frittata'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"simple_query_string\" : {\n" + //
                "    \"query\" : \"\\\"fried eggs\\\" +(eggplant | potato) -frittata\",\n" + //
                "    \"fields\" : [ \"dc:title\" ]\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: INDEX(dc:title,dc:description) ANALYZER(fulltext) OPERATOR(query_string) */ dc:title = 'this AND that OR thus'")
                .toString();
        // fields are not ordered
        assertIn(es, "{\n" + //
                "  \"query_string\" : {\n" + //
                "    \"query\" : \"this AND that OR thus\",\n" + //
                "    \"fields\" : [ \"dc:title\", \"dc:description\" ],\n" + //
                "    \"analyzer\" : \"fulltext\"\n" + //
                "  }\n" + //
                "}", "{\n" + //
                        "  \"query_string\" : {\n" + //
                        "    \"query\" : \"this AND that OR thus\",\n" + //
                        "    \"fields\" : [ \"dc:description\", \"dc:title\" ],\n" + //
                        "    \"analyzer\" : \"fulltext\"\n" + //
                        "  }\n" + //
                        "}");

        es = NxqlQueryConverter
                .toESQueryBuilder(
                        "select * from Document where /*+ES: OPERATOR(common) */ dc:title = 'this is bonsai cool'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"common\" : {\n" + //
                "    \"dc:title\" : {\n" + //
                "      \"query\" : \"this is bonsai cool\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

    }

    @Test
    public void testConvertHintLike() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: INDEX(some:field) ANALYZER(my_analyzer) */ dc:subjects LIKE 'foo*'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"match\" : {\n" + //
                "    \"some:field\" : {\n" + //
                "      \"query\" : \"foo\",\n" + //
                "      \"type\" : \"phrase_prefix\",\n" + //
                "      \"analyzer\" : \"my_analyzer\"\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter
                .toESQueryBuilder(
                        "select * from Document where /*+ES: INDEX(some:field) */ dc:subjects LIKE '%foo%'")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"wildcard\" : {\n" + //
                "    \"some:field\" : \"*foo*\"\n" + //
                "  }\n" + //
                "}", es);

    }

    @Test
    public void testConvertHintFulltext() throws Exception {
        // search on title and description, boost title
        String es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: INDEX(dc:title.fulltext^3,dc:description.fulltext) */ ecm:fulltext = 'foo'")
                .toString();
        // fields are not ordered
        assertIn(es, "{\n" + //
                "  \"simple_query_string\" : {\n" + //
                "    \"query\" : \"foo\",\n" + //
                "    \"fields\" : [ \"dc:title.fulltext^3\", \"dc:description.fulltext\" ],\n" + //
                "    \"analyzer\" : \"fulltext\",\n" + //
                "    \"default_operator\" : \"and\"\n" + //
                "  }\n" + //
                "}", "{\n" + //
                        "  \"simple_query_string\" : {\n" + //
                        "    \"query\" : \"foo\",\n" + //
                        "    \"fields\" : [ \"dc:description.fulltext\", \"dc:title.fulltext^3\" ],\n" + //
                        "    \"analyzer\" : \"fulltext\",\n" + //
                        "    \"default_operator\" : \"and\"\n" + //
                        "  }\n" + //
                        "}");
    }

    protected void assertEqualsEvenUnderWindows(String expected, String actual) {
        expected = normalizeString(expected);
        actual = normalizeString(actual);
        Assert.assertEquals(expected, actual);
    }

    private String normalizeString(String str) {
        if (SystemUtils.IS_OS_WINDOWS) {
            str = str.trim();
            str = str.replace("\n", "");
            str = str.replace("\r", "");
        }
        return str;
    }

    protected void assertIn(String actual, String... expected) {
        actual = normalizeString(actual);
        for (String exp : expected) {
            exp = normalizeString(exp);
            if (exp.equals(actual)) {
                return;
            }
        }
        // fail
        Assert.assertEquals(expected[0], actual);
    }

    @Test
    public void testConvertHintGeo() throws Exception {
        String es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: OPERATOR(geo_bounding_box) */ osm:location IN ('40.73, -74.1', '40.01, -71.12')")
                .toString();
        String response = "{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"geo_bbox\" : {\n" + //
                "        \"osm:location\" : {\n" + //
                "          \"top_left\" : [ -74.1, 40.01 ],\n" + //
                "          \"bottom_right\" : [ -71.12, 40.73 ]\n" + //
                "        }\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}";
        assertEqualsEvenUnderWindows(response, es);
        es = NxqlQueryConverter.toESQueryBuilder(
                "select * from Document where /*+ES: OPERATOR(geo_bounding_box) */ osm:location IN ('dr5r9y', 'drj7tee')")
                .toString();
        // we can not do this because lat and lon are not rounded to match the input
        // assertTruEqualsEvenUnderWindows(response, es);
        Assert.assertTrue(es.contains("geo_bbox"));
        Assert.assertTrue(es.contains("bottom_right"));

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where /*+ES: OPERATOR(geo_distance) */ "
                + "osm:location IN ('40.73, -74.1', '20km')").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"geo_distance\" : {\n" + //
                "        \"osm:location\" : [ -74.1, 40.73 ],\n" + //
                "        \"distance\" : \"20km\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: OPERATOR(geo_distance_range) */"
                        + "osm:location IN ('40.73, -74.1', '500m', '20km')")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"geo_distance_range\" : {\n" + //
                "        \"osm:location\" : [ -74.1, 40.73 ],\n" + //
                "        \"from\" : \"500m\",\n" + //
                "        \"to\" : \"20km\",\n" + //
                "        \"include_lower\" : true,\n" + //
                "        \"include_upper\" : true\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter
                .toESQueryBuilder("select * from Document where /*+ES: OPERATOR(geo_distance_range) */"
                        + "osm:location IN ('40.73, -74.1', '500m', '20km')")
                .toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"geo_distance_range\" : {\n" + //
                "        \"osm:location\" : [ -74.1, 40.73 ],\n" + //
                "        \"from\" : \"500m\",\n" + //
                "        \"to\" : \"20km\",\n" + //
                "        \"include_lower\" : true,\n" + //
                "        \"include_upper\" : true\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where /*+ES: OPERATOR(geo_hash_cell) */"
                + "osm:location IN ('40.73, -74.1', '2')").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"geohash_cell\" : {\n" + //
                "        \"precision\" : 10,\n" + //
                "        \"osm:location\" : \"dr5r9ydj2y73\"\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

        es = NxqlQueryConverter.toESQueryBuilder("select * from Document where /*+ES: OPERATOR(geo_shape) */"
                + "osm:location IN ('FRA', 'countries', 'shapes', 'location')").toString();
        assertEqualsEvenUnderWindows("{\n" + //
                "  \"constant_score\" : {\n" + //
                "    \"filter\" : {\n" + //
                "      \"geo_shape\" : {\n" + //
                "        \"osm:location\" : {\n" + //
                "          \"indexed_shape\" : {\n" + //
                "            \"id\" : \"FRA\",\n" + //
                "            \"type\" : \"countries\",\n" + //
                "            \"index\" : \"shapes\",\n" + //
                "            \"path\" : \"location\"\n" + //
                "          },\n" + //
                "          \"relation\" : \"within\"\n" + //
                "        },\n" + //
                "        \"_name\" : null\n" + //
                "      }\n" + //
                "    }\n" + //
                "  }\n" + //
                "}", es);

    }
}