/**
 * Example for TEXT queries using LUCENE-based indexing of people's resumes.
 *
 * Note that to be able to do so you have to add FULLTEXT index for the 'resume'
 * field of the Person type. See config for details.
 */
void DoTextQuery()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    typedef std::vector< CacheEntry<int64_t, Person> > ResVector;

    //  Query for all people with "Master" in their resumes.
    ResVector masters;
    cache.Query(TextQuery(PERSON_TYPE, "Master")).GetAll(masters);

    // Query for all people with "Bachelor" in their resumes.
    ResVector bachelors;
    cache.Query(TextQuery(PERSON_TYPE, "Bachelor")).GetAll(bachelors);

    std::cout << "Following people have 'Master' in their resumes: " << std::endl;

    // Printing first result set.
    for (ResVector::const_iterator i = masters.begin(); i != masters.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;

    std::cout << "Following people have 'Bachelor' in their resumes: " << std::endl;

    // Printing second result set.
    for (ResVector::const_iterator i = bachelors.begin(); i != bachelors.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;
}
/**
 * Example for SQL queries to calculate average salary for a specific organization.
 *
 * Note that SQL Fields Query can only be performed using fields that have been
 * listed in "QueryEntity" been of the config.
 */
void DoSqlQueryWithAggregation()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    // Calculate average of salary of all persons in ApacheIgnite.
    // Note that we also join on Organization cache as well.
    std::string sql(
        "select avg(salary) "
        "from Person, \"Organization\".Organization as org "
        "where Person.orgId = org._key "
        "and lower(org.name) = lower(?)");

    SqlFieldsQuery qry(sql);

    qry.AddArgument<std::string>("ApacheIgnite");

    QueryFieldsCursor cursor = cache.Query(qry);

    // Calculate average salary for a specific organization.
    std::cout << "Average salary for 'ApacheIgnite' employees: " << std::endl;

    while (cursor.HasNext())
        std::cout << cursor.GetNext().GetNext<double>() << std::endl;

    std::cout << std::endl;
}
/**
 * Example for SQL-based fields queries that return only required
 * fields instead of whole key-value pairs.
 *
 * Note that SQL Fields Query can only be performed using fields that have been
 * listed in "QueryEntity" been of the config.
 */
void DoSqlFieldsQueryWithJoin()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    // Execute query to get names of all employees.
    std::string sql(
        "select concat(firstName, ' ', lastName), org.name "
        "from Person, \"Organization\".Organization as org "
        "where Person.orgId = org._key");

    QueryFieldsCursor cursor = cache.Query(SqlFieldsQuery(sql));

    // Print persons' names and organizations' names.
    std::cout << "Names of all employees and organizations they belong to: " << std::endl;

    // In this particular case each row will have two elements with full name
    // of an employees and their organization.
    while (cursor.HasNext())
    {
        QueryFieldsRow row = cursor.GetNext();

        std::cout << row.GetNext<std::string>() << ", ";
        std::cout << row.GetNext<std::string>() << std::endl;
    }

    std::cout << std::endl;
}
/**
 * Example for SQL queries based on all employees working for a specific
 * organization (query uses distributed join).
 */
void DoSqlQueryWithDistributedJoin()
{
    typedef std::vector< CacheEntry<int64_t, Person> > ResVector;

    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    // SQL clause query which joins on 2 types to select people for a specific organization.
    std::string joinSql(
        "from Person, \"Organization\".Organization as org "
        "where Person.orgId = org._key "
        "and lower(org.name) = lower(?)");

    SqlQuery qry("Person", joinSql);

    qry.AddArgument<std::string>("ApacheIgnite");

    // Enable distributed joins for query.
    qry.SetDistributedJoins(true);

    // Execute queries for find employees for different organizations.
    ResVector igniters;
    cache.Query(qry).GetAll(igniters);

    // Printing first result set.
    std::cout << "Following people are 'ApacheIgnite' employees (distributed join): " << std::endl;

    for (ResVector::const_iterator i = igniters.begin(); i != igniters.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;

    qry.ClearArguments();

    qry.AddArgument<std::string>("Other");

    ResVector others;
    cache.Query(qry).GetAll(others);

    // Printing second result set.
    std::cout << "Following people are 'Other' employees (distributed join): " << std::endl;

    for (ResVector::const_iterator i = others.begin(); i != others.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;
}
/**
 * Example for SQL queries based on salary ranges.
 *
 * Note that SQL Query can only be performed using fields that have been
 * listed in "QueryEntity" been of the config.
 */
void DoSqlQuery()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    // SQL clause which selects salaries based on range.
    std::string sql("salary > ? and salary <= ?");

    SqlQuery qry(PERSON_TYPE, sql);

    typedef std::vector< CacheEntry<int64_t, Person> > ResVector;

    // Execute queries for salary range 0 - 1000.
    std::cout << "People with salaries between 0 and 1000 (queried with SQL query): " << std::endl;

    qry.AddArgument(0);
    qry.AddArgument(1000);

    ResVector res;
    cache.Query(qry).GetAll(res);

    for (ResVector::const_iterator i = res.begin(); i != res.end(); ++i)
            std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;

    qry.ClearArguments();

    // Execute queries for salary range 1000 - 2000.
    std::cout << "People with salaries between 1000 and 2000 (queried with SQL query): " << std::endl;

    qry.AddArgument(1000);
    qry.AddArgument(2000);

    res.clear();

    cache.Query(qry).GetAll(res);

    for (ResVector::const_iterator i = res.begin(); i != res.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;
}
/**
 * Example for SQL queries based on all employees working for a specific organization.
 *
 * Note that SQL Query can only be performed using fields that have been
 * listed in "QueryEntity" been of the config.
 */
void DoSqlQueryWithJoin()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    typedef std::vector< CacheEntry<int64_t, Person> > ResVector;

    // SQL clause query which joins on 2 types to select people for a specific organization.
    std::string sql(
        "from Person, \"Organization\".Organization as org "
        "where Person.orgId = org._key "
        "and lower(org.name) = lower(?)");

    SqlQuery qry(PERSON_TYPE, sql);

    // Execute queries for find employees for different organizations.
    std::cout << "Following people are 'ApacheIgnite' employees: " << std::endl;

    qry.AddArgument<std::string>("ApacheIgnite");

    ResVector res;
    cache.Query(qry).GetAll(res);

    for (ResVector::const_iterator i = res.begin(); i != res.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;

    std::cout << "Following people are 'Other' employees: " << std::endl;

    qry.ClearArguments();

    qry.AddArgument<std::string>("Other");

    res.clear();
    cache.Query(qry).GetAll(res);

    for (ResVector::const_iterator i = res.begin(); i != res.end(); ++i)
        std::cout << i->GetKey() << " : " << i->GetValue().ToString() << std::endl;

    std::cout << std::endl;
}
/**
 * Example for SQL-based fields queries that return only required fields instead
 * of whole key-value pairs.
 *
 * Note that SQL Fields Query can only be performed using fields that have been
 * listed in "QueryEntity" been of the config.
 */
void DoSqlFieldsQuery()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    // Execute query to get names of all employees.
    QueryFieldsCursor cursor = cache.Query(SqlFieldsQuery(
        "select concat(firstName, ' ', lastName) from Person"));

    // Print names.
    std::cout << "Names of all employees: " << std::endl;

    // In this particular case each row will have one element with full name of an employees.
    while (cursor.HasNext())
        std::cout << cursor.GetNext().GetNext<std::string>() << std::endl;

    std::cout << std::endl;
}
/**
 * Example for scan query based on a predicate.
 */
void DoScanQuery()
{
    Cache<int64_t, Person> cache = Ignition::Get().GetCache<int64_t, Person>(PERSON_CACHE);

    ScanQuery scan;

    typedef std::vector< CacheEntry<int64_t, Person> > ResVector;

    ResVector res;
    cache.Query(scan).GetAll(res);

    // Execute queries for salary ranges.
    std::cout << "People with salaries between 0 and 1000 (queried with SCAN query): " << std::endl;

    for (ResVector::const_iterator i = res.begin(); i != res.end(); ++i)
    {
        Person person(i->GetValue());

        if (person.salary <= 1000)
            std::cout << i->GetKey() << " : " << person.ToString() << std::endl;
    }

    std::cout << std::endl;
}