com.amalto.core.query.StorageFullTextTest.java Source code

Java tutorial

Introduction

Here is the source code for com.amalto.core.query.StorageFullTextTest.java

Source

/*
 * Copyright (C) 2006-2016 Talend Inc. - www.talend.com
 * 
 * This source code is available under agreement available at
 * %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
 * 
 * You should have received a copy of the agreement along with this program; if not, write to Talend SA 9 rue Pages
 * 92150 Suresnes, France
 */

package com.amalto.core.query;

import static com.amalto.core.query.user.UserQueryBuilder.*;
import static com.amalto.core.query.user.UserStagingQueryBuilder.*;

import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.talend.mdm.commmon.metadata.ComplexTypeMetadata;

import com.amalto.core.query.optimization.ConfigurableContainsOptimizer;
import com.amalto.core.query.user.BinaryLogicOperator;
import com.amalto.core.query.user.Compare;
import com.amalto.core.query.user.Condition;
import com.amalto.core.query.user.Expression;
import com.amalto.core.query.user.FieldFullText;
import com.amalto.core.query.user.OrderBy;
import com.amalto.core.query.user.Predicate;
import com.amalto.core.query.user.Select;
import com.amalto.core.query.user.Split;
import com.amalto.core.query.user.StringConstant;
import com.amalto.core.query.user.UserQueryBuilder;
import com.amalto.core.query.user.UserQueryHelper;
import com.amalto.core.storage.FullTextResultsWriter;
import com.amalto.core.storage.Storage;
import com.amalto.core.storage.StorageResults;
import com.amalto.core.storage.datasource.DataSource;
import com.amalto.core.storage.datasource.DataSourceDefinition;
import com.amalto.core.storage.datasource.RDBMSDataSource;
import com.amalto.core.storage.exception.FullTextQueryCompositeKeyException;
import com.amalto.core.storage.hibernate.HibernateStorage;
import com.amalto.core.storage.record.DataRecord;
import com.amalto.core.storage.record.DataRecordReader;
import com.amalto.core.storage.record.DataRecordWriter;
import com.amalto.core.storage.record.ViewSearchResultsWriter;
import com.amalto.core.storage.record.XmlStringDataRecordReader;
import com.amalto.xmlserver.interfaces.IWhereItem;
import com.amalto.xmlserver.interfaces.WhereAnd;
import com.amalto.xmlserver.interfaces.WhereCondition;

@SuppressWarnings("nls")
public class StorageFullTextTest extends StorageTestCase {

    private static Logger LOG = Logger.getLogger(StorageFullTextTest.class);
    private static ComplexTypeMetadata a1 = repository.getComplexType("a1");
    private static ComplexTypeMetadata a2 = repository.getComplexType("a2");
    private static ComplexTypeMetadata a3 = repository.getComplexType("a3");

    static {
        initStorage(DATASOURCE_FULLTEXT);
        try {
            populateData();
        } catch (Exception e) {
            LOG.error("Populate Date failed");
        }
    }

    private static void populateData() throws Exception {
        try {
            clean();
        } catch (Exception e) {
            // ignore
        }
        DataRecordReader<String> factory = new XmlStringDataRecordReader();
        List<DataRecord> allRecords = new LinkedList<DataRecord>();
        allRecords.add(factory.read(repository, productFamily, "<ProductFamily>\n" + "    <Id>1</Id>\n"
                + "    <Name>ProductFamily1</Name>\n" + "</ProductFamily>"));
        allRecords.add(factory.read(repository, productFamily, "<ProductFamily>\n" + "    <Id>2</Id>\n"
                + "    <Name>ProductFamily2</Name>\n" + "</ProductFamily>"));
        allRecords.add(factory.read(repository, productFamily, "<ProductFamily>\n" + "    <Id>3</Id>\n"
                + "    <Name>ProductFamily3</Name>\n" + "</ProductFamily>"));
        allRecords.add(factory.read(repository, productFamily,
                "<ProductFamily>\n" + "    <Id>4</Id>\n" + "    <Name>test_name4</Name>\n" + "</ProductFamily>"));
        allRecords.add(factory.read(repository, product,
                "<Product>\n" + "    <Id>1</Id>\n" + "    <Name>talend</Name>\n"
                        + "    <ShortDescription>Short description word</ShortDescription>\n"
                        + "    <LongDescription>Long description</LongDescription>\n" + "    <Price>10</Price>\n"
                        + "    <Features>\n" + "        <Sizes>\n" + "            <Size>Small</Size>\n"
                        + "            <Size>Medium</Size>\n" + "            <Size>Large</Size>\n"
                        + "        </Sizes>\n" + "        <Colors>\n" + "            <Color>Blue</Color>\n"
                        + "            <Color>Red</Color>\n" + "        </Colors>\n" + "    </Features>\n"
                        + "    <Status>Pending</Status>\n" + "    <Supplier>[1]</Supplier>\n" + "</Product>"));
        allRecords.add(factory.read(repository, product,
                "<Product>\n" + "    <Id>2</Id>\n" + "    <Name>Renault car</Name>\n"
                        + "    <ShortDescription>A car</ShortDescription>\n"
                        + "    <LongDescription>Long description 2</LongDescription>\n" + "    <Price>10</Price>\n"
                        + "    <Features>\n" + "        <Sizes>\n" + "            <Size>Large</Size>\n"
                        + "        <Size>Large</Size></Sizes>\n" + "        <Colors>\n"
                        + "            <Color>Blue 2</Color>\n" + "            <Color>Blue 1</Color>\n"
                        + "            <Color>Klein blue2</Color>\n" + "        </Colors>\n" + "    </Features>\n"
                        + "    <Family>[1]</Family>\n" + "    <Status>Pending</Status>\n"
                        + "    <Supplier>[2]</Supplier>\n" + "    <Supplier>[1]</Supplier>\n" + "</Product>"));
        allRecords.add(factory.read(repository, supplier,
                "<Supplier>\n" + "    <Id>1</Id>\n" + "    <SupplierName>Renault</SupplierName>\n" + "    <Contact>"
                        + "        <Name>Jean Voiture</Name>\n" + "        <Phone>33123456789</Phone>\n"
                        + "        <Email>test@test.org</Email>\n" + "    </Contact>\n" + "</Supplier>"));
        allRecords.add(factory.read(repository, supplier,
                "<Supplier>\n" + "    <Id>2</Id>\n" + "    <SupplierName>Starbucks Talend</SupplierName>\n"
                        + "    <Contact>" + "        <Name>Jean Cafe</Name>\n"
                        + "        <Phone>33234567890</Phone>\n" + "        <Email>test@testfactory.org</Email>\n"
                        + "    </Contact>\n" + "</Supplier>"));
        allRecords.add(factory.read(repository, supplier,
                "<Supplier>\n" + "    <Id>3</Id>\n" + "    <SupplierName>Talend</SupplierName>\n" + "    <Contact>"
                        + "        <Name>Jean Paul</Name>\n" + "        <Phone>33234567890</Phone>\n"
                        + "        <Email>test@talend.com</Email>\n" + "    </Contact>\n" + "</Supplier>"));
        allRecords.add(factory.read(repository, supplier,
                "<Supplier>\n" + "    <Id>4</Id>\n" + "    <SupplierName>IdSoftware</SupplierName>\n"
                        + "    <Contact>" + "        <Name>John Carmack</Name>\n"
                        + "        <Phone>123456789</Phone>\n" + "        <Email></Email>\n" + "    </Contact>\n"
                        + "</Supplier>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>1</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France</name></Country>"));
        allRecords.add(factory.read(repository, country,
                "<Country><id>2</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France1</name><notes><note>Country note</note><comment>repeatable comment 1</comment><comment>Repeatable comment 2</comment></notes></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>3</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France2</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>4</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France3</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>5</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France4</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>6</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France5</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>7</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France6</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>8</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France7</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>9</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France8</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>10</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France9</name></Country>"));

        allRecords.add(factory.read(repository, country,
                "<Country><id>11</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France10</name></Country>"));

        allRecords.add(factory.read(repository, countryLong,
                "<CountryLong><id>1</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France1</name></CountryLong>"));

        allRecords.add(factory.read(repository, countryLong,
                "<CountryLong><id>2</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France2</name></CountryLong>"));

        allRecords.add(factory.read(repository, countryLong,
                "<CountryLong><id>3</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France3</name></CountryLong>"));

        allRecords.add(factory.read(repository, countryShort,
                "<CountryShort><id>1</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France1</name></CountryShort>"));

        allRecords.add(factory.read(repository, countryShort,
                "<CountryShort><id>2</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France2</name></CountryShort>"));

        allRecords.add(factory.read(repository, countryShort,
                "<CountryShort><id>3</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France3</name></CountryShort>"));
        allRecords.add(factory.read(repository, countryShort,
                "<CountryShort><id>4</id><creationDate>2010-10-10</creationDate><creationTime>2010-10-10T00:00:01</creationTime><name>France3</name></CountryShort>"));

        allRecords.add(factory.read(repository, person,
                "<Person><id>1</id><score>130000.00</score><lastname>Dupond</lastname><resume>[EN:my splendid resume, splendid isn't it][FR:mon magnifique resume, n'est ce pas ?]</resume><middlename>John</middlename><firstname>Julien</firstname><addresses></addresses><age>10</age><Status>Employee</Status><Available>true</Available></Person>"));
        allRecords.add(factory.read(repository, person,
                "<Person><id>2</id><score>170000.00</score><lastname>Dupont</lastname><middlename>John</middlename><firstname>Robert-Julien</firstname><addresses></addresses><age>20</age><Status>Customer</Status><Available>false</Available></Person>"));
        allRecords.add(factory.read(repository, person,
                "<Person><id>3</id><score>200000.00</score><lastname>Leblanc</lastname><middlename>John</middlename><firstname>Juste</firstname><addresses></addresses><age>30</age><Status>Friend</Status></Person>"));
        allRecords.add(factory.read(repository, person,
                "<Person><id>4</id><score>200000.00</score><lastname>Leblanc</lastname><middlename>John</middlename><firstname>Julien</firstname><age>30</age><Status>Friend</Status></Person>"));
        allRecords.add(factory.read(repository, person,
                "<Person><id>1234</id><firstname>quan</firstname><middlename>kevin</middlename><lastname>cui</lastname><resume>[EN:Hello [World:]][FR:bonjour :le][ZH:ni Hao]</resume><age>22</age><score>100</score><Available></Available><Status>Customer</Status></Person>"));

        allRecords.add(factory.read(repository, fullTextSearchEntityA,
                "<FullTextSearchEntityA><Id>id1</Id><Name>name1</Name><Address><AddressName>address1</AddressName><City><CityName>city1</CityName></City></Address></FullTextSearchEntityA>"));
        allRecords.add(factory.read(repository, a2,
                "<a2><subelement>1</subelement><subelement1>10</subelement1><b3>String b3</b3><b4>String b4</b4></a2>"));
        allRecords.add(factory.read(repository, a1,
                "<a1><subelement>1</subelement><subelement1>11</subelement1><b1>String b1</b1><b2>[1][10]</b2></a1>"));
        allRecords.add(factory.read(repository, a2,
                "<a2><subelement>1</subelement><subelement1>2</subelement1><b3>String b3</b3><b4>String b4</b4></a2>"));
        allRecords.add(factory.read(repository, a3, "<a3><id>3</id><name>hamdi</name><a2>[1][2]</a2></a3>"));
        allRecords.add(factory.read(repository, store, "<Store><Id>Upper Case Id</Id><Name>name1</Name></Store>"));
        allRecords.add(factory.read(repository, store, "<Store><Id>lower case id</Id><Name>name2</Name></Store>"));
        allRecords.add(factory.read(repository, store, "<Store><Id>ab</Id><Name>name3</Name></Store>"));
        allRecords.add(factory.read(repository, store, "<Store><Id>ab&amp;cd</Id><Name>name4</Name></Store>"));
        allRecords.add(factory.read(repository, store, "<Store><Id>AB&amp;CD</Id><Name>name5</Name></Store>"));
        allRecords.add(factory.read(repository, store,
                "<Store><Id>One@#$Two%&amp;=Three;,.Four</Id><Name>name6</Name></Store>"));

        allRecords.add(factory.read(repository, employee,
                "<Employee><name>employee 1</name><age>11</age><jobTitle>jobTitle 11</jobTitle></Employee>"));
        allRecords.add(factory.read(repository, employee,
                "<Employee><name>employee 2</name><age>22</age><jobTitle>jobTitle 22</jobTitle></Employee>"));
        allRecords.add(factory.read(repository, employee,
                "<Employee><name>employee 3</name><age>33</age><jobTitle>jobTitle 33</jobTitle></Employee>"));
        allRecords.add(factory.read(repository, persons, "<Persons><name>person 1</name><age>11</age></Persons>"));
        allRecords.add(factory.read(repository, persons, "<Persons><name>person 2</name><age>22</age></Persons>"));
        allRecords.add(factory.read(repository, persons, "<Persons><name>person 3</name><age>33</age></Persons>"));
        allRecords.add(factory.read(repository, manager,
                "<Manager><name>manager 1</name><age>11</age><jobTitle>jobTitle 11</jobTitle><dept>dept 1</dept></Manager>"));
        allRecords.add(factory.read(repository, manager,
                "<Manager><name>manager 2</name><age>22</age><jobTitle>jobTitle 22</jobTitle><dept>dept 2</dept></Manager>"));
        allRecords.add(factory.read(repository, manager,
                "<Manager><name>manager 3</name><age>33</age><jobTitle>jobTitle 33</jobTitle><dept>dept 3</dept></Manager>"));

        try {
            storage.begin();
            storage.update(allRecords);
            storage.commit();
        } catch (Exception e) {
            storage.rollback();
            throw new RuntimeException(e);
        } finally {
            storage.end();
        }
    }

    public void tearDown() throws Exception {
        super.tearDown();
    }

    public static void clean() throws Exception {
        storage.begin();
        {
            UserQueryBuilder qb = from(productFamily);
            storage.delete(qb.getSelect());

            qb = from(supplier);
            storage.delete(qb.getSelect());

            qb = from(product);
            storage.delete(qb.getSelect());

            qb = from(country);
            storage.delete(qb.getSelect());

            qb = from(countryLong);
            storage.delete(qb.getSelect());

            qb = from(countryShort);
            storage.delete(qb.getSelect());

            qb = from(person);
            storage.delete(qb.getSelect());

            qb = from(fullTextSearchEntityA);
            storage.delete(qb.getSelect());

            qb = from(a1);
            storage.delete(qb.getSelect());

            qb = from(a2);
            storage.delete(qb.getSelect());

            qb = from(a3);
            storage.delete(qb.getSelect());

            qb = from(store);
            storage.delete(qb.getSelect());
        }
        storage.commit();
        storage.end();
    }

    public void setUp() throws Exception {
        super.setUp();
    }

    public void testNumericQueryMix() throws Exception {
        UserQueryBuilder qb = from(product).where(fullText("2"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            for (DataRecord result : results) {
                LOG.info("result = " + result);
            }
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testMatchesInSameInstance() throws Exception {
        UserQueryBuilder qb = from(country).where(fullText("2010"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            for (DataRecord result : results) {
                LOG.info("result = " + result);
            }
            assertEquals(11, results.getCount());
        } finally {
            results.close();
        }

        qb = from(product).where(fullText("Large"));
        results = storage.fetch(qb.getSelect());
        try {
            for (DataRecord result : results) {
                LOG.info("result = " + result);
            }
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearch() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault"));

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            for (DataRecord result : results) {
                LOG.info("result = " + result);
            }
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchOrderBy() throws Exception {
        UserQueryBuilder qb = from(supplier).select(supplier.getField("Id")).where(fullText("Talend"))
                .orderBy(supplier.getField("Id"), OrderBy.Direction.ASC);

        StorageResults records = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, records.getCount());
            int currentId = -1;
            for (DataRecord record : records) {
                Integer id = Integer.parseInt((String) record.get("Id"));
                assertTrue(id > currentId);
                currentId = id;
            }
        } finally {
            records.close();
        }

        qb = from(supplier).select(supplier.getField("Id")).where(fullText("Talend"))
                .orderBy(supplier.getField("Id"), OrderBy.Direction.DESC);

        records = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, records.getCount());
            int currentId = Integer.MAX_VALUE;
            for (DataRecord record : records) {
                Integer id = Integer.parseInt((String) record.get("Id"));
                assertTrue(id < currentId);
                currentId = id;
            }
        } finally {
            records.close();
        }
    }

    public void testSimpleSearchOrderByWithContainsCondition() throws Exception {
        // Order by "Id" field
        UserQueryBuilder qb = from(productFamily).where(contains(productFamily.getField("Name"), "Product"))
                .orderBy(productFamily.getField("Id"), OrderBy.Direction.DESC);

        StorageResults records = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, records.getCount());
            int currentId = Integer.MAX_VALUE;
            for (DataRecord record : records) {
                Integer id = Integer.parseInt((String) record.get("Id"));
                assertTrue(id < currentId);
                currentId = id;
            }
        } finally {
            records.close();
        }
        // Order by "Name" field
        qb = from(productFamily).where(contains(productFamily.getField("Name"), "Product"))
                .orderBy(productFamily.getField("Name"), OrderBy.Direction.DESC);

        records = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, records.getCount());
            int currentId = Integer.MAX_VALUE;
            for (DataRecord record : records) {
                Integer id = Integer.parseInt((String) record.get("Id"));
                assertTrue(id < currentId);
                currentId = id;
            }
        } finally {
            records.close();
        }
    }

    public void testMultipleTypesSearch() throws Exception {
        UserQueryBuilder qb = from(supplier).and(product).where(fullText("Renault"));

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testDateSearch() throws Exception {
        UserQueryBuilder qb = from(country).where(fullText("2010")); // Default StandardAnalyzer will split text
                                                                     // "2010-10-12" into "2010", "10", "12"

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(11, results.getCount());
        } finally {
            results.close();
        }
    }

    /**
     * TMDM-8970 : exception occured when launching this test before fix test for id of int type
     * 
     * @throws Exception
     */
    public void testFullSearchCountry() throws Exception {
        UserQueryBuilder qb = from(country).where(fullText("France"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            // debug code for unstable case
            for (DataRecord dataRecord : results) {
                assertTrue(String.valueOf(dataRecord.get("name")).startsWith("France"));
            }
            assertEquals(11, results.getCount());
        } finally {
            results.close();
        }
    }

    /**
     * TMDM-8970 : exception occured when launching this test before fix test for id of long type
     * 
     * @throws Exception
     */
    public void testFullSearchCountryLong() throws Exception {
        UserQueryBuilder qb = from(countryLong).where(fullText("F"));
        qb.limit(2);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    /**
     * TMDM-8970 : exception occured when launching this test before fix test for id of short type
     * 
     * @throws Exception
     */
    public void testFullSearchCountryShort() throws Exception {
        UserQueryBuilder qb = from(countryShort).where(fullText("F"));
        qb.limit(2);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testCollectionSearch() throws Exception {
        UserQueryBuilder qb = from(product).where(fullText("Blue"));

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithCondition() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault"))
                .where(eq(supplier.getField("Contact/Name"), "Jean Voiture"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(supplier).where(fullText("Renault")).where(eq(supplier.getField("Id"), "1"));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(supplier).where(fullText("Renault")).where(eq(supplier.getField("Id"), "2"));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithWildcard() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("*"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(4, results.getCount());
            int actualCount = 0;
            for (DataRecord result : results) {
                actualCount++;
            }
            assertEquals(4, actualCount);
        } finally {
            results.close();
        }
        //
        qb = from(supplier).where(fullText("**"));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(4, results.getCount());
            int actualCount = 0;
            for (DataRecord result : results) {
                actualCount++;
            }
            assertEquals(4, actualCount);
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithWildcardOnTypes() throws Exception {
        UserQueryBuilder qb = from(supplier).and(product).where(fullText("*"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(6, results.getCount());
            int actualCount = 0;
            for (DataRecord result : results) {
                actualCount++;
            }
            assertEquals(6, actualCount);
        } finally {
            results.close();
        }
        //
        qb = from(supplier).and(product).where(fullText("**"));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(6, results.getCount());
            int actualCount = 0;
            for (DataRecord result : results) {
                actualCount++;
            }
            assertEquals(6, actualCount);
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithSpace() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText(" "));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(4, results.getCount());
        } finally {
            results.close();
        }

        qb = from(supplier).where(fullText("     "));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(4, results.getCount());
        } finally {
            results.close();
        }

        qb = from(supplier).and(product).where(fullText("     "));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(6, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithContainsCondition() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault"))
                .where(contains(supplier.getField("Contact/Name"), "Jean"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(supplier).where(fullText("Talend")).where(contains(supplier.getField("Contact/Name"), "Jean"));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithContainsConditionAndNot() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault")).where(
                and(contains(supplier.getField("Contact/Name"), "Jean"), not(eq(supplier.getField("Id"), "0"))));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotSame(0, result.get("Id"));
            }
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithGreaterThanCondition() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault")).where(gt(supplier.getField("Id"), "1"));
        try {
            storage.fetch(qb.getSelect());
            fail("Expected: not supported.");
        } catch (Exception e) {
            // Expected.
        }

        qb = from(supplier).where(fullText("Renault")).where(gte(supplier.getField("Id"), "1"));
        try {
            storage.fetch(qb.getSelect());
            fail("Expected: not supported.");
        } catch (Exception e) {
            // Expected.
        }

        qb = from(supplier).where(fullText("Jean")).where(gte(supplier.getField("Id"), "2"));
        try {
            storage.fetch(qb.getSelect());
            fail("Expected: not supported.");
        } catch (Exception e) {
            // Expected.
        }
    }

    public void testSimpleSearchWithContainsAndIsEmptyNullCondition() throws Exception {
        UserQueryBuilder qb = from(supplier).where(and(contains(supplier.getField("Id"), "4"),
                or(isEmpty(supplier.getField("Contact/Email")), isNull(supplier.getField("Contact/Email")))));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(supplier).where(and(contains(supplier.getField("Contact/Name"), "John"),
                or(isNull(supplier.getField("Contact/Email")), isEmpty(supplier.getField("Contact/Email")))));
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithLessThanCondition() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault")).where(lt(supplier.getField("Id"), "1"));
        try {
            storage.fetch(qb.getSelect());
            fail("Expected: not supported.");
        } catch (Exception e) {
            // Expected.
        }

        qb = from(supplier).where(fullText("Renault")).where(lte(supplier.getField("Id"), "1"));
        try {
            storage.fetch(qb.getSelect());
            fail("Expected: not supported.");
        } catch (Exception e) {
            // Expected.
        }
    }

    public void testSimpleSearchWithJoin() throws Exception {
        UserQueryBuilder qb = from(product).and(productFamily).selectId(product)
                .select(productFamily.getField("Name")).where(fullText("Renault")).join(product.getField("Family"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotNull(result.get("Id"));
                assertEquals("", result.get("Name"));
            }
        } finally {
            results.close();
        }

        qb = from(product).and(productFamily).selectId(product).select(productFamily.getField("Name"))
                .where(fullText("Renault")).join(product.getField("Family")).limit(20);

        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotNull(result.get("Id"));
                assertEquals("", result.get("Name"));
            }
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithProjection() throws Exception {
        UserQueryBuilder qb = from(supplier).select(supplier.getField("Contact/Name")).where(fullText("Renault"));

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertEquals(1, result.getSetFields().size());
                assertNotNull(result.get("Name"));
            }
        } finally {
            results.close();
        }
    }

    public void testSimpleSearchWithProjectionAlias() throws Exception {
        UserQueryBuilder qb = from(supplier).select(alias(supplier.getField("Contact/Name"), "element"))
                .where(fullText("Renault"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertEquals(1, result.getSetFields().size());
                assertNotNull(result.get("element"));
            }
        } finally {
            results.close();
        }

        qb = from(supplier).select(alias(supplier.getField("Contact/Name"), "element")).where(fullText("Renault"))
                .start(0).limit(20);
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertEquals(1, result.getSetFields().size());
                assertNotNull(result.get("element"));
            }
        } finally {
            results.close();
        }
    }

    public void testFKSearchWithProjection() throws Exception {
        UserQueryBuilder qb = from(product).select(product.getField("Family")).where(fullText("car"));

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            ViewSearchResultsWriter writer = new ViewSearchResultsWriter();
            StringWriter resultWriter = new StringWriter();
            for (DataRecord result : results) {
                writer.write(result, resultWriter);
            }
            assertEquals("<result xmlns:metadata=\"http://www.talend.com/mdm/metadata\" "
                    + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" + "\t<Family>[1]</Family>\n"
                    + "</result>", resultWriter.toString());
        } finally {
            results.close();
        }
    }

    public void testMultipleTypesSearchWithCondition() throws Exception {
        UserQueryBuilder qb = from(supplier).where(fullText("Renault"))
                .where(eq(supplier.getField("Contact/Name"), "Jean Voiture"));

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testFullTextSuggestion() throws Exception {
        try {
            storage.getFullTextSuggestion("Ren", Storage.FullTextSuggestion.START, 3);
            fail("Expected due to Lucene version being used.");
        } catch (Exception e) {
            // Expected.
        }
    }

    public void testFullTextAlternative() throws Exception {
        try {
            storage.getFullTextSuggestion("strabuks", Storage.FullTextSuggestion.ALTERNATE, 3);
            fail("Expected due to Lucene version being used");
        } catch (Exception e) {
            // Expected
        }

    }

    public void testFullTextResultsFormat() throws Exception {
        UserQueryBuilder qb = from(product).where(fullText("Renault"));

        StorageResults results = null;
        try {
            results = storage.fetch(qb.getSelect());
            assertEquals(1, results.getCount());

            DataRecordWriter writer = new FullTextResultsWriter("Renault");
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            for (DataRecord result : results) {
                writer.write(result, output);
            }
            assertTrue(output.toString().contains("<b>Renault car</b>"));
        } finally {
            if (results != null) {
                results.close();
            }
        }
    }

    public void testFullTextResultsInNestedFormat() throws Exception {
        UserQueryBuilder qb = from(product).where(fullText("Klein"));

        StorageResults results = null;
        try {
            results = storage.fetch(qb.getSelect());
            assertEquals(1, results.getCount());

            DataRecordWriter writer = new FullTextResultsWriter("Klein");
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            for (DataRecord result : results) {
                writer.write(result, output);
            }
            assertTrue(output.toString().contains("<b>Klein blue2</b>"));
        } finally {
            if (results != null) {
                results.close();
            }
        }
    }

    public void testNoFullText() throws Exception {
        Storage storage = new HibernateStorage("noFullText");
        try {
            storage.init(getDatasource("RDBMS-1-NO-FT"));
            storage.prepare(repository, Collections.<Expression>emptySet(), false, false);
            UserQueryBuilder qb = from(product).where(fullText("Test"));

            try {
                storage.fetch(qb.getSelect());
                fail("Full text is not enabled");
            } catch (Exception e) {
                assertEquals("Storage 'noFullText(MASTER)' is not configured to support full text queries.",
                        e.getCause().getMessage());
            }
        } finally {
            storage.close();
        }
    }

    public void testSimpleWithoutFkValueSearch() throws Exception {
        UserQueryBuilder qb = from(product).select(product.getField("Family")).where(fullText("talend")).limit(20);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertEquals("", result.get("Family"));
            }
        } finally {
            results.close();
        }
    }

    public void testFullTestWithCompositeKeySearch() throws Exception {

        try {
            UserQueryBuilder qb = from(a1).selectId(a1).select(a1.getField("b1")).select(a1.getField("b2"))
                    .where(fullText("String")).limit(20);
            storage.fetch(qb.getSelect());
            fail();
        } catch (RuntimeException runtimeException) {
            if (FullTextQueryCompositeKeyException.class.isInstance(runtimeException.getCause())) {
                assertEquals("a1", runtimeException.getCause().getMessage());
            } else {
                throw runtimeException;
            }
        }
    }

    public void testFieldFullText() throws Exception {
        UserQueryBuilder qb = from(product).where(fullText(product.getField("ShortDescription"), "description"))
                .limit(20);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(product).where(fullText(product.getField("ShortDescription"), "long")).limit(20);
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testFullTextAndRangeTimeQuery() throws Exception {
        // Original case not working due to a issue in Lucene, where some collectors cannot accept out-of-order scoring.
        // should be fixed in Lucene 5.0, https://issues.apache.org/jira/browse/LUCENE-6179
        UserQueryBuilder qb = from(product)
                .where(or(fullText(product.getField("ShortDescription"), "description"),
                        and(gte(timestamp(), "0"), lte(timestamp(), String.valueOf(System.currentTimeMillis())))))
                .limit(20);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testTaskIdProjection() throws Exception {
        UserQueryBuilder qb = from(product).select(alias(taskId(), "taskId")).select(alias(error(), "error"))
                .where(fullText(product.getField("ShortDescription"), "description"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNull(result.get("taskId"));
                assertNull(result.get("error"));
            }
        } finally {
            results.close();
        }

        qb = from(product).select(alias(taskId(), "taskId")).select(alias(error(), "error"))
                .where(fullText(product.getField("ShortDescription"), "description")).limit(20);
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNull(result.get("taskId"));
                assertNull(result.get("error"));
            }
        } finally {
            results.close();
        }
    }

    public void testSearchOnContainedType() throws Exception {
        UserQueryBuilder qb = from(product).where(fullText(product.getField("Features"), "klein"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                Object object = result.get("Features/Colors/Color");
                assertTrue(object instanceof List);
                assertEquals(3, ((List) object).size());
                assertEquals("Klein blue2", ((List) object).get(2));
            }
        } finally {
            results.close();
        }

        qb = UserQueryBuilder.from(product);
        String fieldName = "Product/Features";
        IWhereItem item = new WhereAnd(Arrays.<IWhereItem>asList(
                new WhereCondition(fieldName, WhereCondition.FULLTEXTSEARCH, "klein", WhereCondition.NO_OPERATOR)));
        Condition condition = UserQueryHelper.buildCondition(qb, item, repository);
        Expression normalizedCondition = condition.normalize();
        assertTrue(normalizedCondition instanceof BinaryLogicOperator);
        assertTrue(((BinaryLogicOperator) normalizedCondition).getLeft() instanceof FieldFullText);
        qb = qb.where(condition);
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                Object object = result.get("Features/Colors/Color");
                assertTrue(object instanceof List);
                assertEquals(3, ((List) object).size());
                assertEquals("Klein blue2", ((List) object).get(2));
            }
        } finally {
            results.close();
        }
    }

    public void testTimeStampProjectionNoAlias() throws Exception {
        // TMDM-7737: Test metadata field projection *with* paging in query.
        UserQueryBuilder qb = from(product).select(timestamp())
                .where(fullText(product.getField("Features"), "klein")).start(0).limit(20);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testIdFieldContainUpperCaseKeyWordSearch() throws Exception {
        UserQueryBuilder qb = from(store).selectId(store).where(contains(store.getField("Id"), "case"));
        storage.begin();
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("case"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(2, results.getCount());
        } finally {
            results.close();
        }
    }

    // TMDM-8798 FK constraint warning when creating a fk on the fly with special character
    public void testIdFieldContainSpecialCharacter() throws Exception {
        UserQueryBuilder qb = from(store).selectId(store).where(contains(store.getField("Id"), "ab&cd"));
        storage.begin();
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(contains(store.getField("Id"), "ab"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(contains(store.getField("Id"), "AB&CD"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(contains(store.getField("Id"), "one"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(contains(store.getField("Id"), "two"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(contains(store.getField("Id"), "three"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(contains(store.getField("Id"), "four"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("ab&cd"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("ab"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("AB&CD"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(3, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("one"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("two"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("three"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }

        qb = from(store).selectId(store).where(fullText("four"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testGenerateIdFetchSize() throws Exception {
        // Test "stream resultset"
        RDBMSDataSource dataSource = new RDBMSDataSource("TestDataSource", "MySQL", "", "", "", 0, 0, "", "", false,
                "update", false, new HashMap(), "", "", null, "", "", "", false);
        Configuration configuration = new Configuration();
        configuration.setProperty(Environment.STATEMENT_FETCH_SIZE, "1000");
        HibernateStorage storage = new HibernateStorage("HibernateStorage");
        storage.init(getDatasource("RDBMS-1-NO-FT"));
        storage.prepare(repository, Collections.<Expression>emptySet(), false, false);

        Class storageClass = storage.getClass();
        Field dataSourceField = storageClass.getDeclaredField("dataSource");
        dataSourceField.setAccessible(true);
        dataSourceField.set(storage, dataSource);

        Field configurationField = storageClass.getDeclaredField("configuration");
        configurationField.setAccessible(true);
        configurationField.set(storage, configuration);

        Method generateIdFetchSizeMethod = storageClass.getDeclaredMethod("generateIdFetchSize", null);
        generateIdFetchSizeMethod.setAccessible(true);
        assertEquals(Integer.MIN_VALUE, generateIdFetchSizeMethod.invoke(storage, null));

        // Test config batch size
        dataSource = new RDBMSDataSource("TestDataSource", "H2", "", "", "", 0, 0, "", "", false, "update", false,
                new HashMap(), "", "", null, "", "", "", false);
        storage = new HibernateStorage("HibernateStorage");
        storage.init(getDatasource("RDBMS-1-NO-FT"));
        storage.prepare(repository, Collections.<Expression>emptySet(), false, false);
        storageClass = storage.getClass();

        dataSourceField = storageClass.getDeclaredField("dataSource");
        dataSourceField.setAccessible(true);
        dataSourceField.set(storage, dataSource);

        configurationField = storageClass.getDeclaredField("configuration");
        configurationField.setAccessible(true);
        configurationField.set(storage, configuration);
        assertEquals(1000, generateIdFetchSizeMethod.invoke(storage, null));

        // Test default batch size
        configuration = new Configuration();
        configurationField = storageClass.getDeclaredField("configuration");
        configurationField.setAccessible(true);
        configurationField.set(storage, configuration);
        assertEquals(500, generateIdFetchSizeMethod.invoke(storage, null));
    }

    public void testFieldQueryWhenHavingCompositeFK() throws Exception {

        UserQueryBuilder qb = from(a3).select(a3.getField("id")).select(a3.getField("a2"))
                .where(fullText(a3.getField("name"), "hamdi")).limit(20);
        StorageResults results = storage.fetch(qb.getSelect());
        Exception exception = null;
        try {
            assertEquals(1, results.getCount());
            DataRecord result = results.iterator().next();
            Object b2 = result.get(a3.getField("a2"));
            assertTrue(b2 instanceof Object[]);
            assertEquals("1", ((Object[]) b2)[0]);
            assertEquals("2", ((Object[]) b2)[1]);
        } catch (Exception e) {
            exception = e;
        } finally {
            results.close();
        }
        assertNull(exception);
    }

    public void testSearchOnMultiLingualType() throws Exception {
        UserQueryBuilder qb = from(person).select(person.getField("id"))
                .where(contains(person.getField("resume"), " world "));
        storage.begin();
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotNull(result.get("id"));
                assertEquals("1234", result.get("id"));
            }
        } finally {
            results.close();
        }

        qb = from(person).selectId(person).where(contains(person.getField("resume"), " bon "));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotNull(result.get("id"));
                assertEquals("1234", result.get("id"));
            }
        } finally {
            results.close();
        }

        qb = from(person).selectId(person).where(contains(person.getField("resume"), " le "));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotNull(result.get("id"));
                assertEquals("1234", result.get("id"));
            }
        } finally {
            results.close();
        }

        qb = from(person).selectId(person).where(contains(person.getField("resume"), "hao"));
        storage.begin();
        results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                assertNotNull(result.get("id"));
                assertEquals("1234", result.get("id"));
            }
        } finally {
            results.close();
        }
    }

    public void testFullTextOnRepeatable() throws Exception {
        UserQueryBuilder qb = UserQueryBuilder.from(country).where(fullText("repeatable"));
        StorageResults results = storage.fetch(qb.getSelect());
        for (DataRecord result : results) {
            assertEquals("Country", result.getType().getName());
            assertEquals(2, result.get("id"));
        }
    }

    public void testFullText() throws Exception {
        UserQueryBuilder qb = UserQueryBuilder.from(country).where(fullText("note"));
        StorageResults results = storage.fetch(qb.getSelect());
        for (DataRecord result : results) {
            assertEquals("Country", result.getType().getName());
            assertEquals(2, result.get("id"));
        }

        qb = UserQueryBuilder.from(fullTextSearchEntityA).where(fullText("city"));
        results = storage.fetch(qb.getSelect());
        assertEquals(1, results.getCount());
        for (DataRecord result : results) {
            assertEquals("id1", result.get("Id"));
        }
    }

    public void testFullTextWithMultiKeywords() throws Exception {
        UserQueryBuilder qb = UserQueryBuilder.from(supplier)
                .where(contains(supplier.getField("SupplierName"), "Star Id"));
        StorageResults results = storage.fetch(qb.getSelect());
        assertEquals(2, results.getCount());
        for (DataRecord result : results) {
            if ("2".equals(result.get("Id"))) {
                assertEquals("Starbucks Talend", result.get("SupplierName"));
            }
            if ("4".equals(result.get("Id"))) {
                assertEquals("IdSoftware", result.get("SupplierName"));
            }
        }
    }

    public void testTypeSplitWithPaging() throws Exception {
        // Build expected results
        UserQueryBuilder qb = UserQueryBuilder.from(person).and(product).where(fullText("Julien"));
        List<String> expected = new LinkedList<String>();
        StorageResults records = storage.fetch(qb.getSelect());
        int count = records.getCount();
        int size = records.getSize();
        for (DataRecord record : records) {
            expected.add(String.valueOf(record.get("id")));
        }
        // Ensures split behavior is same as no split
        StorageResults split = Split.fetchAndMerge(storage, qb.getSelect());
        for (DataRecord record : split) {
            assertTrue(expected.remove(String.valueOf(record.get("id"))));
        }
        assertEquals(count, split.getCount());
        assertEquals(size, split.getSize());
    }

    public void testTypeSplit() throws Exception {
        // Build expected results
        UserQueryBuilder qb = UserQueryBuilder.from(person).and(product).where(fullText("Julien"));
        List<DataRecord> expected = new LinkedList<DataRecord>();
        StorageResults records = storage.fetch(qb.getSelect());
        int count = records.getCount();
        int size = records.getSize();
        for (DataRecord record : records) {
            expected.add(record);
        }
        // Ensures split behavior is same as no split
        StorageResults split = Split.fetchAndMerge(storage, qb.getSelect());
        int i = 0;
        for (DataRecord record : split) {
            assertEquals(record, expected.get(i++));
        }
        assertEquals(count, split.getCount());
        assertEquals(size, split.getSize());
    }

    public void testContainsWithUnderscoreInSearchWords3() throws Exception {
        UserQueryBuilder qb = from(productFamily).where(contains(productFamily.getField("Name"), "te*t_nam*"));

        Select select = qb.getSelect();
        assertNotNull(select);
        Condition condition = select.getCondition();
        assertNotNull(condition);
        assertTrue(condition instanceof Compare);
        Compare compareCondition = (Compare) condition;
        Expression right = compareCondition.getRight();
        assertTrue(right instanceof StringConstant);
        assertEquals("te*t_nam*", ((StringConstant) right).getValue());

        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertEquals(1, results.getCount());
        } finally {
            results.close();
        }
    }

    public void testManyFieldUsingAndContainsCondition() throws Exception {
        UserQueryBuilder qb = from(product)
                .where(contains(product.getField("Features/Sizes/Size"), "ValueDoesNotExist"));
        StorageResults results = storage.fetch(qb.getSelect());
        assertEquals(0, results.getCount());

        qb = from(product).where(and(eq(product.getField("Id"), "1"),
                contains(product.getField("Features/Sizes/Size"), "ValueDoesNotExist")));
        results = storage.fetch(qb.getSelect());
        assertEquals(0, results.getCount());

        qb = from(product).where(contains(product.getField("Features/Sizes/Size"), "large"));
        results = storage.fetch(qb.getSelect());
        assertEquals(2, results.getCount());
    }

    public void testContainsWithReservedCharacters() throws Exception {
        DataSourceDefinition definition = getDatasource(DATASOURCE_FULLTEXT);
        DataSource datasource = definition.getMaster();
        assertTrue(datasource instanceof RDBMSDataSource);
        RDBMSDataSource rdbmsDataSource = (RDBMSDataSource) datasource;
        TestRDBMSDataSource testDataSource = new TestRDBMSDataSource(rdbmsDataSource);
        testDataSource.setCaseSensitiveSearch(false);
        testDataSource.setSupportFullText(true);
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.FULL_TEXT);
        ConfigurableContainsOptimizer optimizer = new ConfigurableContainsOptimizer(testDataSource);
        // Only '-' should disable contains optimization
        UserQueryBuilder qb = UserQueryBuilder.from(person).where(contains(person.getField("id"), "-"));
        Select copy = qb.getSelect().copy();
        optimizer.optimize(copy);
        assertFalse(copy.getCondition() instanceof FieldFullText);
        StorageResults records = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, records.getCount());
        } finally {
            records.close();
        }
        // Only '/' should disable contains optimization
        qb = UserQueryBuilder.from(person).where(contains(person.getField("id"), "/"));
        copy = qb.getSelect().copy();
        optimizer.optimize(copy);
        assertFalse(copy.getCondition() instanceof FieldFullText);
        records = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, records.getCount());
        } finally {
            records.close();
        }
        // Contains optimization should be disabled for next query
        qb = UserQueryBuilder.from(person).where(contains(person.getField("id"), "1-1"));
        copy = qb.getSelect().copy();
        optimizer.optimize(copy);
        assertFalse(copy.getCondition() instanceof FieldFullText);
        records = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, records.getCount());
        } finally {
            records.close();
        }
        // Contains optimization should be enabled
        qb = UserQueryBuilder.from(person).where(contains(person.getField("id"), "_"));
        copy = qb.getSelect().copy();
        optimizer.optimize(copy);
        assertTrue(copy.getCondition() instanceof FieldFullText);
        records = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, records.getCount());
        } finally {
            records.close();
        }
        // Contains optimization should also be enabled
        qb = UserQueryBuilder.from(person).where(contains(person.getField("id"), "1_1"));
        copy = qb.getSelect().copy();
        optimizer.optimize(copy);
        assertTrue(copy.getCondition() instanceof FieldFullText);
        records = storage.fetch(qb.getSelect());
        try {
            assertEquals(0, records.getCount());
        } finally {
            records.close();
        }
    }

    public void testContainsOptimization() throws Exception {
        DataSourceDefinition definition = getDatasource(DATASOURCE_FULLTEXT);
        DataSource datasource = definition.getMaster();
        assertTrue(datasource instanceof RDBMSDataSource);
        RDBMSDataSource rdbmsDataSource = (RDBMSDataSource) datasource;
        TestRDBMSDataSource testDataSource = new TestRDBMSDataSource(rdbmsDataSource);
        testDataSource.setCaseSensitiveSearch(false);
        testDataSource.setSupportFullText(true);
        ConfigurableContainsOptimizer optimizer = new ConfigurableContainsOptimizer(testDataSource);
        // Default optimization
        UserQueryBuilder qb = UserQueryBuilder.from(person)
                .where(contains(person.getField("knownAddresses/knownAddress/Notes/Note"), "test note"));
        Select select = qb.getSelect();
        assertTrue(select.getCondition() instanceof Compare);
        assertTrue(((Compare) select.getCondition()).getPredicate() == Predicate.CONTAINS);
        // LIKE optimization
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.LIKE);
        qb = UserQueryBuilder.from(person)
                .where(contains(person.getField("knownAddresses/knownAddress/Notes/Note"), "test note"));
        select = qb.getSelect();
        optimizer.optimize(select);
        assertTrue(select.getCondition() instanceof Compare);
        assertTrue(((Compare) select.getCondition()).getPredicate() == Predicate.CONTAINS);
        // DISABLED optimization
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.DISABLED);
        qb = UserQueryBuilder.from(person)
                .where(contains(person.getField("knownAddresses/knownAddress/Notes/Note"), "test note"));
        select = qb.getSelect();
        try {
            optimizer.optimize(select);
            fail("Contains use is disabled.");
        } catch (Exception e) {
            // Expected
        }
        // FULL TEXT optimization
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.FULL_TEXT);
        qb = UserQueryBuilder.from(person)
                .where(contains(person.getField("knownAddresses/knownAddress/Notes/Note"), "test note"));
        select = qb.getSelect();
        optimizer.optimize(select);
        assertTrue(select.getCondition() instanceof FieldFullText);
        assertEquals("test note", ((FieldFullText) select.getCondition()).getValue());
    }

    public void testContainsOptimizationOnReusableTypes() throws Exception {
        DataSourceDefinition definition = getDatasource(DATASOURCE_FULLTEXT);
        DataSource datasource = definition.getMaster();
        assertTrue(datasource instanceof RDBMSDataSource);
        RDBMSDataSource rdbmsDataSource = (RDBMSDataSource) datasource;
        TestRDBMSDataSource testDataSource = new TestRDBMSDataSource(rdbmsDataSource);
        testDataSource.setCaseSensitiveSearch(false);
        testDataSource.setSupportFullText(true);
        ConfigurableContainsOptimizer optimizer = new ConfigurableContainsOptimizer(testDataSource);
        // Default optimization
        UserQueryBuilder qb = UserQueryBuilder.from(customer)
                .where(contains(customer.getField("address1/Street"), "test note"));
        Select select = qb.getSelect();
        assertTrue(select.getCondition() instanceof Compare);
        assertTrue(((Compare) select.getCondition()).getPredicate() == Predicate.CONTAINS);
        // LIKE optimization
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.LIKE);
        qb = UserQueryBuilder.from(customer).where(contains(customer.getField("address1/Street"), "test note"));
        select = qb.getSelect();
        optimizer.optimize(select);
        assertTrue(select.getCondition() instanceof Compare);
        assertTrue(((Compare) select.getCondition()).getPredicate() == Predicate.CONTAINS);
        // DISABLED optimization
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.DISABLED);
        qb = UserQueryBuilder.from(customer).where(contains(customer.getField("address1/Street"), "test note"));
        select = qb.getSelect();
        try {
            optimizer.optimize(select);
            fail("Contains use is disabled.");
        } catch (Exception e) {
            // Expected
        }
        // FULL TEXT optimization
        testDataSource.setOptimization(RDBMSDataSource.ContainsOptimization.FULL_TEXT);
        qb = UserQueryBuilder.from(customer).where(contains(customer.getField("address1/Street"), "test note"));
        select = qb.getSelect();
        optimizer.optimize(select);
        assertTrue(select.getCondition() instanceof Compare);
        assertTrue(((Compare) select.getCondition()).getPredicate() == Predicate.CONTAINS);
    }

    public void testContainsSentenceSearch() throws Exception {
        UserQueryBuilder qb = from(supplier).where(contains(supplier.getField("Contact/Name"), "'jean cafe'"));
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            assertTrue(results.getSize() == 1);
            for (DataRecord result : results) {
                assertEquals("2", result.get("Id"));
            }
        } finally {
            results.close();
        }

        qb = from(product)
                .where(and(eq(product.getField("Id"), "2"), contains(product.getField("Name"), "'Renault car'")))
                .where(fullText("car"));
        results = storage.fetch(qb.getSelect());
        try {
            assertTrue(results.getSize() == 1);
            for (DataRecord result : results) {
                assertEquals("A car", result.get("ShortDescription"));
            }
        } finally {
            results.close();
        }
    }

    public void testFullTextSearchOnComplexType() throws Exception {
        UserQueryBuilder qb = from(persons).where(fullText("1"));
        qb.limit(5);
        StorageResults results = storage.fetch(qb.getSelect());
        try {
            int recordCount = 0;
            assertEquals(3, results.getCount());
            for (DataRecord result : results) {
                if (result != null) {
                    if ("person 1".equals(result.get("name")) || "employee 1".equals(result.get("name"))
                            || "manager 1".equals(result.get("name"))) {
                        recordCount++;
                    }
                }
            }
            assertEquals(3, recordCount);
        } finally {
            results.close();
        }

        qb = from(employee).where(fullText("1"));
        results = storage.fetch(qb.getSelect());
        try {
            int recordCount = 0;
            assertEquals(2, results.getCount());
            for (DataRecord result : results) {
                if (result != null) {
                    if ("employee 1".equals(result.get("name")) || "manager 1".equals(result.get("name"))) {
                        recordCount++;
                    }
                }
            }
            assertEquals(2, recordCount);
        } finally {
            results.close();
        }

        qb = from(manager).where(fullText("1"));
        results = storage.fetch(qb.getSelect());
        try {
            int recordCount = 0;
            assertEquals(1, results.getCount());
            for (DataRecord result : results) {
                if (result != null) {
                    if ("manager 1".equals(result.get("name"))) {
                        recordCount++;
                    }
                }
            }
            assertEquals(1, recordCount);
        } finally {
            results.close();
        }
    }

    private static class TestRDBMSDataSource extends RDBMSDataSource {

        private ContainsOptimization optimization;

        private boolean supportFullText;

        private boolean isCaseSensitiveSearch;

        public TestRDBMSDataSource(RDBMSDataSource rdbmsDataSource) {
            super(rdbmsDataSource);
        }

        @Override
        public boolean supportFullText() {
            return supportFullText;
        }

        private void setSupportFullText(boolean supportFullText) {
            this.supportFullText = supportFullText;
        }

        @Override
        public boolean isCaseSensitiveSearch() {
            return isCaseSensitiveSearch;
        }

        public void setCaseSensitiveSearch(boolean caseSensitiveSearch) {
            isCaseSensitiveSearch = caseSensitiveSearch;
        }

        @Override
        public ContainsOptimization getContainsOptimization() {
            return optimization;
        }

        public void setOptimization(ContainsOptimization optimization) {
            this.optimization = optimization;
        }
    }
}