Main Table of Contents


Table of Contents

Queries
How to query a set
Building and using query specifications
Conditions
Boolean Operators
Precedence
Sorting the result set
Clearing a query
Stroking the query optimizer
Types, polymorphism, and queries
Query methods generated by PTXX
Conditions for embedded data or objects
Query methods for embedded C++ base types
Query methods for embedded POET Type Manager types
Query methods for embedded persistent objects
Nesting query specifications
Query methods for embedded non-persistent objects
Query methods for referenced persistent objects
Nesting query specifications
Identity queries
Query methods for sets of data or objects
Counting conditions
Counting conditions with value conditions
Counting conditions with nested query specifications
Counting conditions with identity conditions
Query methods for arrays of data or objects
Whole array conditions
Counting conditions
Counting conditions with value conditions
Counting conditions with nested query specifications
Sample queries
Example 1: Husbands of people named "Esther"
Example 2: husbands of this particular Esther
Example 3: sheiks, wives, and children
Example 4: weather data
Example 5: Bigamist queries
Example 6: Weather data (for arrays)
Example 7: Trigraphs

Queries


Queries


Intro
How to query a set
Building and using query specifications
Stroking the query optimizer
Types, polymorphism, and queries
Query methods generated by PTXX
Sample queries

A query creates a set of objects which meet certain conditions. For instance, if you have a database which contains shapes, you may want to find all of the grey circles. The following diagram illustrates the three main components of this query. The CircleAllSet is the set which is queried. Only the objects in this set will be examined. The CircleQuery tells us which circles we are interested in - in this case, the grey circles. The CircleSet is the result set; the query fills the result set with references to all the circles in the CircleAllSet which meet the conditions stated in the CircleQuery.

POET now has many ways of querying the database. This chapter describes queries, and the next two chapters cover filters and OQL. Filters are useful when you want to retrieve your results one at a time. OQL provides a very good query language which is standardized by the Object Database Management Group. In addition, the Generic Interfaces Guide contains a section on generic queries, which can be used when you have no advance knowledge of the classes in a database. There are a few other functions which also have similar functionality; if speed is essential and you just want to find an object based on its value or its identity, you may want to look at PtAllSet::FindKey() or PtObjectSet::Find(); if you just want to set a sort order without actually sorting, you may want to look at PtAllSet ::SortByIndex().


How to query a set


Intro

In POET, every query is done by asking a set to query itself. In the previous example, the query would be done by asking the CircleAllSet to examine itself to see if it contains any grey circles, and the result set would hold all grey circles.

Like all POET sets, CircleAllSet has a function called Query() which actually performs the query - in our example we did the query by calling CircleAllSet:: Query(). This function takes two parameters, the address of the query specification and the address of the result set. The first parameter, the query specification, is a CircleQuery object. The CircleQuery class is generated by PTXX, which always creates a query specification class for each persistent class. It does this because the semantics of a query depend on the attributes of the class. In our example, we are looking for grey circles; we can do this only if circles have a data member for color which can contain the value grey. If the class Circle has a member called Color which can contain the value GREY, then CircleQuery contains a member function called CircleQuery:: SetColor(), and you can specify the condition like this:

CircleQuery cq;
cq.SetColor(GREY);

In a good class design, Color is probably a member in the Shape class which is inherited by the Circle class. Therefore, PTXX derives CircleQuery from ShapeQuery to allow you to specify conditions for those data members which Circle inherits from Shape.

The second parameter is the result set. PTXX does not automatically generate a result set type for each persistent class. Use a typedef in your .HCD file to create result set types for your queries. For instance, in our example, we may want an ondemand set of circles to hold the result of our query. We can declare this type after our Circle class declaration like this:

// in CIRCLE.HCD
typedef lset<ondemand<Circle>> CircleSet;

We strongly recommend that you use ondemand sets for result sets - POET can place an ondemand reference into a set much faster than it can build objects, so queries using ondemand result sets are significantly faster. Once we have declared our result set type and run PTXX, we can create a CircleSet in our program using the type declared by the typedef. Here is the code for the query in our example:

// Create the sets and do the query
CircleAllSet allCircles(pObjBase);
CircleSet greyCircles;
CircleQuery cq;
cq.SetColor(GREY);
int err = allCircles.Query(&cq, &greyCircles);
cout << greyCircles.GetNum() << " grey circles. Err = " << err << endl;

If Circle has a Display() function, we can display the contents of the result set like this:

Circle* pCircle;
for (int i=0; greyCircles.Get(pCircle, i, PtSTART) == 0; ++i)
{
pCircle->Display();
greyCircles.Unget(pCircle);
}


Building and using query specifications


Intro
Conditions
Boolean Operators
Precedence
Sorting the result set
Clearing a query

Query specifications can be used to formulate queries which are arbitrarily complex. For instance, you may want to find all grey circles created in 1994 with a radius between one and three whose coordinates lie in a certain range which are part of a particular diagram. The rest of this chapter will show you how to formulate these kinds of queries based on the structure of your class declarations and references, but to understand this discussion, you must first know the basic building blocks of a query specification: conditions, boolean operators, precedence, and sorting conditions. These are each discussed in this section.


Conditions


Intro

In our Circle example, we said that we are looking for grey circles using CircleQuery:: SetColor() like this:

CircleQuery cq;
cq.SetColor(GREY);

Every call to SetColor() specifies one condition for the color of the circle. We just specified circles whose color is equal to grey. We could also specify circles whose color is not equal to grey by using the PtNEQ comparision operator, which stands for "is not equal to":

CircleQuery cq;
cq.SetColor(GREY, PtNEQ);

A condition consists of a value and a comparison operator, and the parameters for SetColor() allow you to specify each. The default comparison operator is PtEQ, which means "is equal to", so POET tests for equality if you do not specify a comparison operator.

The following comparison operators are available:

PtEQ Equal to
PtLT Less than
PtGT Greater than
PtGTE Greater than or equal to
PtLTE Less than or equal to
PtNEQ Not equal to


Boolean Operators


Intro

Conditions are grouped into expressions using boolean operators. The function PtQuery:: SetBoolOp() allows you to specify a boolean operator - every query specification class is derived from PtQuery and has this function. For instance, if you want circles which are grey or red, you could use the following expression:

CircleQuery cq;
cq.SetColor(GREY);
cq.SetBoolOp(PtOR);
cq.SetColor(RED);

The following boolean operators are available:

PtAnd and
PtOR or
PtXOR exclusive or
PtNOT not
PtNAND not and
PtNXOR not exclusive or

If two conditions are set without explicitly stating a boolean operator, POET combines them using a PtAND.


Precedence


Intro

POET allows you to specify the precedence for your expressions. This is sometimes essential to get the result you expect. Suppose you want to find the circles whose radius is greater than two and less than four, or which are blue. We will apply this query to the following table:

Color Radius
RED 5
GREY 2
BLUE 1
GREEN 10

There are two ways to interpret the query. The first way, which is probably what you are expecting, is to interpret it like this:

(circle.radius > 2 and circle.radius < 4) or circle.color == blue

If the query is interpreted this way, it first looks for circles whose radiuses are between two and four. There are no circles with a radius in this range. Next, the query looks for blue circles, and adds these to the result set. One object is found, the blue circle.

However, the query could also be interpreted like this:

circle.radius > 2 and (circle.radius < 4 or circle.color == blue)

If the query is interpreted like this, it starts by looking at the circles which have a radius less than four or which are blue. Both the grey and the blue circles satisfy these conditions. Next, the query restricts these circles to those which have a radius greater than two. Neither circle satisfies this condition. You see, then, that the precedence affects the results of a query. POET allows you to set precedence using PtQuery::OpenBracket(), which is equivalent to a left parenthesis in the previous examples, and PtQuery::CloseBracket(), which is equivalent to a right parenthesis. By default, POET uses the same precedence rules as C++, which means that PtAND has a higher precedence than PtOR, and POET would use the precedence shown in the first interpretation. If you want POET to use the second interpretation, you can specify the precedence like this:

CircleQuery cq;
cq.SetRadius(2, PtGT);
cq.OpenBracket();
cq.SetRadius(4, PtLT);
cq.SetBoolOp(PtOR);
cq.SetColor(BLUE);
cq.CloseBracket();


Sorting the result set


Intro

You can specify the sort order of the result set using the SortByMember() functions generated by PTXX. You can sort on multiple members - the sort orders nest from left to right. For instance, if you want to sort a set of people by last name and then by first name (as in a telephone book) then you would add these two lines:

MyQuery.SortByName( PtASCENDING );
MyQuery.SortByFirstName( PtASCENDING );

In the current version of POET you must specify sort orders before setting any conditions for the query. Enclose the rest of the query in brackets:

MyQuery.SortByName( PtASCENDING );
MyQuery.SortByFirstName( PtASCENDING );
MyQuery.OpenBracket();
MyQuery.SetName(PtString("R*"));
MyQuery.CloseBracket();

Incidentally, if you just want to use a different sort order for an AllSet, do not do this with a query. PtAllSet::SortByIndex() is much more efficient.


Clearing a query


Intro

Before you use a query specification, you should clear it using PtQuery::Clear(). If you do not do this, POET continues to accumulate your conditions, assuming that each condition is part of the same query - even if you do a query, POET does not assume that you are starting the query specification from scratch when you add the next condition.

Clearing a query specification looks like this:

MyQuery.Clear();


Stroking the query optimizer


Intro

There are often many ways that POET could process a query to get the same answer. POET uses a query optimizer to determine which of these ways is the most efficient. This query optimizer is currently somewhat dependent on the form of your query, especially if your query specifies sort orders or ranges of values.

You can speed up a query which specifies a sort order by following two simple rules: specify the sort order before specifying your search conditions, and place parentheses around the search conditions. Suppose you want to look for Persons which satisfy the condition:

(Person.Name > "M" && Person.Name < "R")
&& Person.City=="London"

The results should be sorted by name, then by first name. We can help the query optimizer by specifying our query like this:

PersonQuery MyQuery; // All conditions involve person
MyQuery.SortByName(); // Primary sort order
MyQuery.SortByFirstName(); // Secondary sort order
MyQuery.OpenBracket(); // (
MyQuery.OpenBracket(); // (
MyQuery.SetName(PtString("M"), PtGT); // Name > "M" MyQuery.SetBoolOp(PtAND); // && MyQuery.SetName(PtString("R"), PtLT); // Name < "R"
MyQuery.CloseBracket(); // )
MyQuery.SetBoolOp (PtAND); // &&
MyQuery.SetCity(PtString("London"), PtEQ); // City=="London"
MyQuery.CloseBracket(); // )

A real example for which this particular query is useful is left as an exercise for the reader.


Types, polymorphism, and queries


Intro

Query specific ations are typed, and the type is part of the semantics of the query. If we use a PersonQuery to query a set, then POET will apply the query to all persons. Suppose we have a class called Weirdo which is derived from Person. Query specification classes are derived using the same inheritance structure as the classes they apply to, so WierdoQuery is derived from PersonQuery. Since a WierdoQuery is a PersonQuery you can use it as a parameter to the Query() methods of Book sets. Consider the previous query; if we perform exactly the same query on the same set using a WeirdoQuery instead of a PersonQuery, then POET will apply the search criteria only to members of the class Weirdo:

PersonAllSet pa(&objbase);
WeirdoAllSet wa(&objbase); // Weirdo is derived from Person
WeirdoQuery wq;
wq.SetName(PtString("Smith"));
PersonQuery pq;
pq.SetName(PtString("Smith"));
// The next two declarations assume that the .HCD file contains the
// statement typedef lset<ondemand<Person>> PersonSet;
PersonSet PersonNamedSmith;
PersonSet WeirdoNamedSmith;
// The following query returns all Persons named Smith
pa.Query(&pq, &PersonNamedSmith);
// The next three queries return only Weirdos named Smith
pa.Query(&wq, &WeirdoNamedSmith); // WeirdoQuery is used
wa.Query(&wq, &WeirdoNamedSmith); // WeirdoAllSet is used
wa.Query(&pq, &WeirdoNamedSmith); // WeirdoAllSet is used


Query methods generated by PTXX


Intro
Conditions for embedded data or objects
Query methods for sets of data or objects
Query methods for arrays of data or objects

We have already seen that PTXX generates a query class and methods for conditions involving the members of each persistent class it encounters. We will now see which methods will be generated for each member of a persistent class; this depends on the type of the member (C++ base type, POET Type Manager type, persistent object, or non-persistent object). Make sure you understand these distinctions:

C++ base type int, char, float, double, or other types which are atomic in the C++ language.
POET Type Manager type PtString, PtBLOB, PtDate, PtTime
persistent object any object whose class declaration uses the persistent or _persistent keyword
non-persistent object any object whose class declaration does not use the persistent or _persistent keyword

Which query methods will be generated also depends on whether the member embedded class, a pointer, an ondemand, a set, or an array. The following declaration illustrates the combinations which are possible with POET:

persistent class Demo
{
// Base types
int EmbeddedBaseType;
cset<int> SetOfBaseType;
int ArrayOfBaseType[10];
// POET types
PtString EmbeddedPOETType;
cset<PtString> SetOfPOETType;
PtString ArrayOfPOETType[10];
// Non-persistent classes
NonPersClass EmbeddedNonPersistent;
cset<NonPersClass> SetOfNonPersistents;
NonPersClass ArrayOfNonPersistents[10];
// Persistent classes
PersClass EmbeddedPersistent;
PersClass* PointerToPersistent;
ondemand<PersClass> OnDemandToPersistent;
cset<PersClass*> SetOfPointersToPersistents;
cset<ondemand<PersClass>> SetOfOnDemandToPersistent;
PersClass ArrayOfPersistents[10];
};

The next sections will show how to formulate queries for each member type. Each of these possibilities will be illustrated with sample class declarations and the query methods that are generated for them.


Conditions for embedded data or objects


Intro
Query methods for embedded C++ base types
Query methods for embedded POET Type Manager types
Query methods for embedded persistent objects
Nesting query specifications
Query methods for embedded non-persistent objects
Query methods for referenced persistent objects
Nesting query specifications
Identity queries

Embedded data or objects are the direct members of the class. They may be C++ base types, POET Type Manager types, non-persistent objects, or persistent objects. The names of the variables are used in the headings for the next several sections; for instance, an integer is an EmbeddedBaseType. To see which query methods are generated for embedded base types, look at the heading "Embedded Base Types," below. Here is a class which illustrates the embedded types which may occur in a class:

persistent class Demo
{
int EmbeddedBaseType;
PtString EmbeddedPOETType;
NonPersClass EmbeddedNonPersistent;
PersClass EmbeddedPersistent;
};


Query methods for embedded C++ base types


Intro

Suppose your persistent class has an embedded C++ base type; e.g., an integer, a float, a long, a character, or something like that:

// in .HCD file
persistent class Demo
{
int EmbeddedBaseType;
};
typedef lset<ondemand<Demo>> DemoSet; // for declaring result sets

PTXX will generate a query specification class for Demo which contains methods for searching and sorting on that member:

class DemoQuery : public PtQuery
{
public:
int SetEmbeddedBaseType(int param, PtCmpOp co = PtEQ);
int SortByEmbeddedBaseType(PtSortOp op = PtASCENDING);
};

You use this DemoQuery to specify query conditions for the Demo class. Let's look for all Demo objects in the database whose EmbeddedBaseType is greater than 10 and less than or equal to 17. We'll also sort the result set by EmbeddedBaseType:

DemoQuery dq;
dq.SortByEmbeddedBaseType(PtASCENDING); // sort by embedded base type
dq.SetEmbeddedBaseType(10, PtGT); // greater than 10
dq.SetEmbeddedBaseType(17, PtLTE); // and less than or equal to 17
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
da.Query(&dq, &result);
cout << "Number of objects found: " << result.GetNum() << endl;


Query methods for embedded POET Type Manager types


Intro

For queries, POET Type Manager types are handled just like C++ base types. Suppose your persistent class has an embedded POET Type Manager type; e.g., a PtString, PtDate, PtTime, or PtBlob:

// in .HCD file
persistent class Demo
{
PtString EmbeddedPOETType;
};
typedef lset<ondemand<Demo>> DemoSet; // for declaring result sets

PTXX will generate a query specification class for Demo which contains methods for searching and sorting on that member:

class DemoQuery : public PtQuery
{
public:
int SetEmbeddedPOETType(PtString & param, PtCmpOp co=PtEQ);
int SortByEmbeddedPOETType(PtSortOp op = PtASCENDING);
};

You use this DemoQuery to specify query conditions for the Demo class. Our embedded type is a string; let's look for all Demo objects in the database whose EmbeddedPOETType is between "Apples" and "Bananas". We'll also sort the result set by EmbeddedPOETType:

DemoQuery dq;
dq.SortByEmbeddedPOETType(PtASCENDING);
// greater than "Apples"and less than "Bananas"
dq.SetEmbeddedPOETType(PtString("Apples"), PtGT);
dq.SetEmbeddedPOETType(PtString("Bananas"), PtLT);
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
da.Query(&dq, &result);
cout << "Number of objects found: " << result.GetNum() << endl;


Query methods for embedded persistent objects


Intro

Embedded objects are handled the same whether or not they are persistent; however, the types of parameters in the generated query classes will differ. Suppose your persistent class has an embedded persistent object:

// in .HCD file
persistent class PersClass
{
PtString AString;
};
persistent class Demo
{
PersClass EmbeddedPersistent;
};
typedef lset<ondemand<Demo>> DemoSet; // for declaring result sets

PTXX will generate a query specification class for Demo which contains methods for searching on that member:

class DemoQuery : public PtQuery
{
public:
int SetEmbeddedPersistent (PersClassQuery* param)
};


Nesting query specifications


Intro

You use this DemoQuery to specify query conditions for the Demo class. But you need some way to specify conditions for the embedded object; therefore, the parameter for SetEmbeddedPersistent() is a pointer to a query specification for the persistent object. Here is the PersClassQuery class which PTXX generated for our PersClass:

class PersClassQuery : public PtQuery
{
public:
int SetAString (PtString &param, PtCmpOp co = PtEQ);
int SortByAString (PtSortOp op = PtASCENDING);
};

Now let's look for the Demo objects whose embedded PersClass contain strings between "Apples" and "Bananas":

DemoQuery dq;
PersClassQuery pq;
// PersClass.AString >= "Apples"
// and <= "Bananas"
pq.SetAString(PtString("Apples"), PtGTE);
pq.SetAString(PtString("Bananas"), PtLTE);
// Demos containing such an EmbeddedNonPersistent dq.SetEmbeddedPersistent(&pq);
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
da.Query(&dq, &result);
cout << "Number of objects found: " << result.GetNum() << endl;


Query methods for embedded non-persistent objects


Intro

Embedded objects are handled the same whether or not they are persistent; however, the types of parameters in the generated query classes will differ. Suppose your persistent class has an embedded non-persistent object:

// in .HCD file
class NonPersClass
{
int AnInt;
};
persistent class Demo
{
NonPersClass EmbeddedNonPersistent;
};
typedef lset<ondemand<Demo>> DemoSet; // for declaring result sets

PTXX will generate a query specification class for Demo which contains methods for searching and sorting on that member:

class DemoQuery : public PtQuery
{
public:
int SetEmbeddedNonPersistent (NonPersClassQuery* param);
};

You use this DemoQuery to specify query conditions for the Demo class. But you need some way to specify conditions for the embedded object; therefore, the parameter for SetEmbeddedNonPersistent is a pointer to a query specification for the non-persistent object. Here is the NonPersClassQuery class which PTXX generated for our NonPersClass:

class NonPersClassQuery : public PtQuery
{
public:
int SetAnInt (int param, PtCmpOp co = PtEQ);
int SortByAnInt (PtSortOp op = PtASCENDING);
};

Now let's look for the Demo objects whose embedded NonPersClass contain integers between 10 and 17:

DemoQuery dq;
NonPersClassQuery npq;
npq.SetAnInt(10, PtGTE); // NonPersClass.AnInt >= 10
npq.SetAnInt(17, PtLTE); // and <= 17
dq.SetEmbeddedNonPersistent(&npq); // Demos containing such an EmbeddedNonPersistent
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
da.Query(&dq, &result);
cout << "Number of objects found: " << result.GetNum() << endl;


Query methods for referenced persistent objects


Intro

This section covers query specification methods for pointers, references, or ondemands to persistent objects. Since all three are handled very similarly we will discuss them together here.

Suppose your persistent class contains pointers, references, and ondemands to persistent objects:

// in .HCD file
persistent class PersClass
{
PtString AString;
};
persistent class Demo
{
PersClass* PointerToPersistent;
PersClass& ReferenceToPersistent;
ondemand<PersClass> OnDemandToPersistent;
};
typedef lset<ondemand<Demo>> DemoSet; // for declaring result sets

PTXX will generate a query specification class for Demo which contains methods for searching on those members:

class DemoQuery : public PtQuery
{
public:
int SetPointerToPersistent (PersClassQuery* param);
int SetPointerToPersistent (PersClass* param);
int SetReferenceToPersistent (PersClassQuery* param);
int SetReferenceToPersistent (PersClass* param);
int SetOnDemandToPersistent (PersClassQuery* param);
int SetOnDemandToPersistent (PersClassOnDemand* param);
};

The DemoQuery contains three pairs of functions: one pair for the pointer, one pair for the reference, and one pair for the ondemand. The first function in each pair is for setting query conditions based on the referenced object; the parameter for each of these methods is a pointer to a query specification for the persistent object. The second function in each pair is for identity queries.


Nesting query specifications


Intro

Three of the functions in our DemoQuery take a pointer to a PersClassQuery:

int SetPointerToPersistent (PersClassQuery* param);
int SetReferenceToPersistent (PersClassQuery* param);
int SetOnDemandToPersistent (PersClassQuery* param);

These functions are used to create nested query specifications. Here is the PersClassQuery class which PTXX generated for our PersClass:

class PersClassQuery : public PtQuery
{
public:
int SetAString (PtString &param, PtCmpOp co = PtEQ);
int SortByAString (PtSortOp op = PtASCENDING);
};

Now let's look for the Demo objects which have pointers, references, or ondemands to a PersClass which contains strings between "Apples" and "Bananas":

DemoQuery dq;
PersClassQuery pq, pq2, pq3;
// PersClass.AString >= "Apples"
// and <= "Bananas"
pq.SetAString(PtString("Apples"), PtGTE);
pq.SetAString(PtString("Bananas"), PtLTE);
// Same conditions as above
pq2.SetAString(PtString("Apples"), PtGTE);
pq2.SetAString(PtString("Bananas"), PtLTE);
// Same conditions as above
pq3.SetAString(PtString("Apples"), PtGTE);
pq3.SetAString(PtString("Bananas"), PtLTE);
// The specification for the PersClassQuery is identical
// regardless of whether we reference the object with a
// pointer, reference, or ondemand.
dq.SetPointerToPersistent(&pq);
dq.SetBoolOp(PtOR);
dq.SetReferenceToPersistent(&pq2);
dq.SetBoolOp(PtOR);
dq.SetOnDemandToPersistent(&pq3);
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
da.Query(&dq, &result);
cout << "Number of objects found: " << result.GetNum() << endl;


Identity queries


Intro

Three of the member functions for this DemoQuery take a PersClass pointer or a PersClassOnDemand pointer as a parameter:

int SetPointerToPersistent (PersClass* param);
int SetReferenceToPersistent (PersClass* param);
int SetOnDemandToPersistent (PersClassOnDemand* param);

All three are used to formulate identity queries; we would use them if we want to know which Demos contain a pointer, reference, or ondemand to a particular PersClass object. Let's suppose we have a PersClass which is called SomeParticularPersClass and we want to know which Demos contain pointers to it. We can do this with the following code:

DemoQuery dq;
dq.SetPointerToPersistent(&SomeParticularPersClass);
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
da.Query(&dq, &result);
cout << "Number of objects found: " << result.GetNum() << endl;

Note that identity queries are not generated for non-persistent objects. Since they have no persistent identity, non-persistent objects may never be used to specify identity queries.


Query methods for sets of data or objects


Intro
Counting conditions
Counting conditions with value conditions
Counting conditions with nested query specifications
Counting conditions with identity conditions

This section discusses queries based on the contents of sets in your persistent objects. These sets might contain C++ base types, POET Type manager types, non-persistent objects, pointers to persistent objects, or ondemand references to persistent objects:

persistent class Demo
{
cset<int> SetOfBaseType;
cset<PtString> SetOfPOETType;
cset<NonPersClass> SetOfNonPersistents;
cset<PersClass*> SetOfPointersToPersistents;
cset<ondemand<PersClass>> SetOfOnDemandToPersistent;
};

PTXX will generate a query specification class for Demo which contains methods for searching on its members. Searching on a set always means searching on the members of that set. Here is the query specification class for the above persistent class:

class DemoQuery
{
int SetSetOfBaseType (dword val, PtCmpOp op, int param, PtCmpOp co=PtEQ);
int SetSetOfBaseType (dword val, PtCmpOp op);
int SetSetOfPOETType(dword val, PtCmpOp op, PtString &param, PtCmpOp co=PtEQ);
int SetSetOfPOETType (dword val, PtCmpOp op);
int SetSetOfNonPersistents (dword val, PtCmpOp op, NonPersClassQuery* param = 0);
int SetSetOfPointersToPersistents(dword val, PtCmpOp op, PersClassQuery* param = 0);
int SetSetOfPointersToPersistents(dword val, PtCmpOp op, PersClass* param = 0);
int SetSetOfOnDemandToPersistent (dword val, PtCmpOp op, PersClassOnDemand* param);
int SetSetOfOnDemandToPersistent (dword val, PtCmpOp op, PersClassQuery* param = 0);
};


Counting conditions


Intro

The parameter lists for all query specification methods for sets begin with the same two parameters, which are known as counting conditions. If no further conditions are specified, then the method sets a condition on the total number of elements in the set. For instance, the following calls specify how many elements are in various sets:

DemoQuery dq;
dq.SetSetOfPOETType(10, PtGTE);// Set has at least 10 items
dq.SetSetOfPOETType(20, PtLTE);// And no more than 20 items

If the method specifies a further condition, then the first two parameters specify the number of elements in the set which meet a condition, and the rest of the parameters specify the condition which the elements should meet. The following condition looks for Demo objects with at least 10 items with the string "Apples" in the SetOfPOETType set:

dq.SetSetOfPOETType(10, PtGTE,PtString("Apples"),PtEQ);


Counting conditions with value conditions


Intro

If the set contains C++ base types or POET Type manager types, then the last two parameters of its query specification method specify the values of the data contained in the set. For instance, we could look for Demo objects which contain sets with at least 10 integers which are less than 20. The name of the set containing integers in our example is SetOfBaseType, so the corresponding method is SetSetOfBaseType():

dq.SetSetOfBaseType(10, PtGTE, 20, PtLT);

Or we could look for Demo objects with at least 10 items with the string "Apples" in the SetOfPOETType set:

dq.SetSetOfPOETType(10, PtGTE, "Apples",PtEQ);

Note: previous versions of this manual called counting conditions valence conditions. The change in terminology is intended to relieve the synaptic strain caused by gratuitous use of Latin.


Counting conditions with nested query specifications


Intro

If the last parameter of the query specification method is a pointer to a query specification then the corresponding set contains either non-persistent objects, pointers to persistent objects, or ondemand references to persistent objects. In all three cases the method is used for nested query specifications. In our example the following methods can be used for nested query specifications:

int SetSetOfNonPersistents (dword val, PtCmpOp op, NonPersClassQuery* param = 0);
int SetSetOfPointersToPersistents(dword val, PtCmpOp op, PersClassQuery* param = 0);
int SetSetOfOnDemandToPersistent (dword val, PtCmpOp op, PersClassQuery* param = 0);

To demonstrate these methods, we need a DemoQuery, a NonPersClassQuery, and a PersClassQuery:

NonPersClassQuery npq;
PersClassQuery pq;
DemoQuery dq;

Consider the following two conditions: the first specifies Demo objects whose SetOfNonPersistents contain exactly 10 items; the second specifies Demo objects with exactly 10 items which meet the conditions specified in the NonPersClassQuery:

dq.SetSetOfNonPersistents(10, PtEQ);
dq.SetSetOfNonPersistents(10, PtEQ, &npq);

Here are the corresponding statements for SetOfPersistents, which are identical except that the nested query uses a PersClassQuery instead of a NonPersClassQuery:

dq.SetSetOfPointersToPersistents(10, PtEQ);
dq.SetSetOfPointersToPersistents(10, PtEQ, &pq);

These are precisely the same parameters we would use for SetOfOnDemandToPersistent:

dq.SetSetOfOnDemandToPersistent(10, PtEQ );
dq.SetSetOfOnDemandToPersistent(10, PtEQ, &pq);


Counting conditions with identity conditions


Intro

If the last parameter of the query specification method is a pointer to a persistent object, then the corresponding set contains contains either non-persistent objects, pointers to persistent objects, or ondemand references to persistent objects. In all three cases the method is used for identity queries. In our example the following methods can be used for identity queries:

int SetSetOfPointersToPersistents(dword val, PtCmpOp op, PersClass* param = 0);
int SetSetOfOnDemandToPersistent (dword val, PtCmpOp op, PersClassOnDemand* param);

The last parameter is a pointer to the object for the identity query; the query will look for those objects whose sets contain references to this particular object. The first two parameters contain the counting conditions. Why do we specify the number of times that the object is contained in the set? In some kinds of data the same object may occur several times in a set. You may want to find roads that intersect with a particular road at least twice, or highways that cross a particular river more than twice. To demonstrate the above functions we need a DemoQuery and a pointer to a PersClass object:

NonPersClassQuery npq;
PersClass pc;
DemoQuery dq;

The following statement specifies Demo objects whose SetOfPointersToPersistents contain this particular PersClass object at least twice:

dq.SetSetOfPointersToPersistents(2, PtGTE, &pc);

We can use the following statement to find all Demo objects whose SetOfPointersToPersistents contain any references to the given object:

dq.SetSetOfPointersToPersistents(1, PtGTE, &pc);

The syntax for identity conditions is the same if the set contains ondemands to persistent objects:

dq.SetSetOfOnDemandToPersistent(2, PtGTE, &pc);
dq.SetSetOfOnDemandToPersistent(1, PtGTE, &pc);


Query methods for arrays of data or objects


Intro
Whole array conditions
Counting conditions
Counting conditions with value conditions
Counting conditions with nested query specifications

This section discusses queries based on the contents of arrays in your persistent objects. These arrays might contain C++ base types, POET Type manager types, non-persistent objects, pointers to persistent objects, or ondemand references to persistent objects:

persistent class Demo
{
int ArrayOfBaseType[10];
PtString ArrayOfPOETType[10];
NonPersClass ArrayOfNonPersistents[10];
PersClass ArrayOfPersistents[10];
};

PTXX will generate a query specification class for Demo which contains methods for searching on its members. Searching on an array always means searching on the members of that array. Here is the query specification class for the above persistent class:

class DemoQuery
{
int SetArrayOfBaseType (int param[10], PtCmpOp co = PtEQ);
int SetArrayOfBaseType (dword val, PtCmpOp op, int param, PtCmpOp co = PtEQ);
int SortByArrayOfBaseType (PtSortOp op = PtASCENDING);
int SetArrayOfPOETType (PtString param[10], PtCmpOp co = PtEQ);
int SetArrayOfPOETType (dword val, PtCmpOp op, PtString &param, PtCmpOp co = PtEQ);
int SortByArrayOfPOETType (PtSortOp op = PtASCENDING);
int SetArrayOfNonPersistents (dword val, PtCmpOp op, NonPersClassQuery* param);
int SetArrayOfPersistents (dword val, PtCmpOp op, PersClassQuery* param);
};


Whole array conditions


Intro

Query specification functions which take an array as a parameter are used for whole array conditions; the first parameter is an array which has the same type and size as the array in your object, and this condition is used to set conditions based on a whole array. Here are the corresponding methods in our above example:

int SetArrayOfBaseType (int param[10], PtCmpOp co = PtEQ);
int SetArrayOfPOETType (PtString param[10], PtCmpOp co = PtEQ);

Now let's use these methods to look for objects containing an ArrayOfBaseType array with the values 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 and an ArrayOfPOETType array with the values "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten":

int Ints[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
PtString Strings[10] = { "one", "two", "three",
"four", "five", "six", "seven", "eight", "nine", "ten"};
DemoQuery dq;
dq.SetArrayOfBaseType(Ints, PtEQ);
dq.SetBoolOp(PtAND);
dq.SetArrayOfPOETType(Strings, PtEQ);
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
ret = da.Query(&dq, &result);
cout << "Return code: " << ret;
cout << "Number of objects found: " << result.GetNum() << endl;


Counting conditions


Intro

Several of the query specification methods for arrays begin with the same two parameters:

int SetArrayOfBaseType (dword val, PtCmpOp op, int param, PtCmpOp co = PtEQ);
int SetArrayOfPOETType (dword val, PtCmpOp op, PtString &param, PtCmpOp co = PtEQ);
int SetArrayOfNonPersistents (dword val, PtCmpOp op, NonPersClassQuery* param);
int SetArrayOfPersistents (dword val, PtCmpOp op, PersClassQuery* param);

These are used to set counting conditions. The first two parameters specify the number of elements in the set which meet a condition, and the rest of the parameters specify the condition which the elements should meet. The first two methods set counting conditions with value conditions, the second set counting conditions with nested query specifications. Unlike sets, there are now queries which just test the number of elements in an array - after all, the size of an array is fixed!


Counting conditions with value conditions


Intro

If the array contains C++ base types or POET Type manager types, then the last two parameters of its query specification method specify the values of the data contained in the array. For instance, we could look for Demo objects which contain arrays with at least 10 integers which are less than 20. The name of the array containing integers in our example is ArrayOfBaseType, so the corresponding method is SetArrayOfBaseType():

int SetArrayOfBaseType (dword val, PtCmpOp op, int param, PtCmpOp co = PtEQ);

We also have an array of PtStrings called ArrayOfPOETType, and here is its counting condition:

int SetArrayOfPOETType (dword val, PtCmpOp op, PtString &param, PtCmpOp co = PtEQ);

Now let's look for Demo objects with at least 3 integers in ArrayOfBaseType that are greater than 10 or at least one string in ArrayOfPOETType which is equal to "flummery":

DemoQuery dq;
dq.SetArrayOfBaseType(3, PtGTE, 10, PtGT);
dq.SetBoolOp(PtOR);
dq.SetArrayOfPOETType(1, PtGTE, "flummery", PtEQ);
DemoAllSet da(&objbase); // objbase is an open PtBase
DemoSet result;
ret = da.Query(&dq, &result);
cout << "Return code: " << ret;
cout << "Number of objects found: " << result.GetNum() << endl;


Counting conditions with nested query specifications


Intro

If the last parameter of the query specification method is a pointer to a query specification, then the corresponding array contains either non-persistent objects, or pointers to persistent objects. In either case the method is used for nested query specifications. In our example the following methods can be used for nested query specifications:

int SetArrayOfNonPersistents (dword val, PtCmpOp op, NonPersClassQuery* param);
int SetArrayOfPersistents (dword val, PtCmpOp op, PersClassQuery* param);

To demonstrate the above functions we need a DemoQuery, a NonPersClassQuery and a PersClassQuery:

NonPersClassQuery npq;
PersClassQuery pq;
DemoQuery dq;

The following statement would search for Demo objects whose ArrayOfNonPersistents contains no more than two objects which meet the conditions specified in the NonPersistentQuery:

dq.SetArrayOfNonPersistents(2, PtLTE, &npq);

The following statement would search for Demo objects whose ArrayOfPersistents contains at least 5 objects which meet the conditions specified in the PersistentQuery:

dq.SetArrayOfPersistents(5, PtGTE, &pq);


Sample queries


Intro
Example 1: Husbands of people named "Esther"
Example 2: husbands of this particular Esther
Example 3: sheiks, wives, and children
Example 4: weather data
Example 5: Bigamist queries
Example 6: Weather data (for arrays)
Example 7: Trigraphs

At this point we have introduced a lot of concepts and terminology. A few examples may help you get used to translating English-language statements into POET queries. By now you are probably comfortable with simple queries, so these sample queries are relatively complex.


Example 1: Husbands of people named "Esther"


Intro

Suppose we want to know which people have a spouse named "Esther." This implies that a person has a spouse:

persistent class Person
{
PtString Name;
Person* Spouse;
};

Both the person and the spouse are Person objects. Our result set will contain Persons - the men with wives named "Esther". We must declare the result set type with a typedef in the .HCD file:

typedef lset<ondemand<Person>> PersonSet;

Now we need to build the query specification. Let's translate our English sentence into our C++ data structures. We want all persons for whom the following condition is true:

Spouse->Name == "Esther"

Spouse is a pointer to a persistent object, which means that the following kinds of methods will be generated:

int SetPointerToPersistent (PersClassQuery* param);
int SetPointerToPersistent (PersClass* param);

The actual names used by PTXX depend on the names in our declaration, so we must change "Spouse" to "PointerToPersistent." Because Spouse is a pointer to Person we must also change "PersClass" to "Person." Therefore, the actual methods will look like this:

int SetSpouse (PersonQuery* param);
int SetSpouse (Person* param);

The first is a nested query, the second is an identity query. In our case we want to search for spouses with a particular name, so we want a nested query. Here is the actual code:

PersonQuery husband, wife;
PersonAllSet *AllPerson = new PersonAllSet (pb);
PersonSet HusbandsOfEsthers( pb );
wife.SetName(PtString("Esther"), PtEQ); // wives named Esther husband.SetSpouse(&wife); // husbands whose wives are named Esther AllPerson->Query (&husband, &HusbandsOfEsthers);


Example 2: husbands of this particular Esther


Intro

The above query will give you all husbands whose wives are named Esther. But you might have one particular Esther, and you want to know who her husband is. In the above example this is trivial, since her Spouse pointer presumably points to her husband. If this pointer did not exist you could still find her husband using a query with an identity condition. We might also use a query to check for inconsistencies in our data, for instance, if we suspect that more than one husband may be listing the same Esther as his wife. An identity condition is a way of saying "Give me all objects that point to this object". It is called an identity query because it is based on the object's identity and not on its values.

In example 1 we showed the class declaration for Person. Spouse is a pointer to a persistent object, so PTXX generates the following query methods:

int SetSpouse (PersonQuery* param);
int SetSpouse (Person* param);

The second one is used for identity conditions. Let us assume that we have already found Esther somehow. Now we can perform our identity query:

PersonQuery husband, wife;
PersonAllSet *AllPerson = new PersonAllSet (pb);
PersonSet HusbandsOfThisEsther( pb );
Person* Esther;
// The following function is undocumented...
GetEstherOutOfThinAir(Esther);
husband.SetSpouse(Esther); // All husbands of this Esther
AllPerson->Query (&husband, &HusbandsOfThisEsther);
assert( HusbandsOfThisEsther->GetNum() <= 1);


Example 3: sheiks, wives, and children


Intro

Suppose our application permits people to have more than one wife. This would be represented using a set. Wives, in turn, may have many children. In this example we will use the following classes:

persistent class Person
{
PtString Name;
};
persistent class Child : public Person
{
};
persistent class Wife : public Person
{
lset<Child*> Children;
};
persistent class Sheik : public Person
{
lset<Wife*> Wives;
};

Now we want to ask how many sheiks have more than 5 wives. Because wives is a set of persistent objects, PTXX will generate the following kind of query method:

int SetSetOfPointersToPersistents(dword val, PtCmpOp op, PersClassQuery* param = 0);

In our case the exact name and parameters will be:

int SetWives (dword val, PtCmpOp op, WifeQuery* param = 0)

We are only interested in the number of wives, so we can omit the last parameter:

PersonAllSet pa;
SheikQuery sq;
SheikSet NouveauChic;
sq.SetWives(5, PtGTE); // at least five wives
pa.Query(&sq, &NouveauChic);

The last parameter is a WifeQuery. We can use this to construct a nested query. For instance, if we want to find all sheiks who have at least five wives who have at least five children named Fred we can use the following query:

PersonAllSet pa;
ChildQuery cq;
SheikQuery sq;
WifeQuery wq;
SheikSet NouveauChic;
wq.SetChildren(5, PtGTE); // WifeQuery: at least five children
sq.SetWives(5, PtGTE, &wq); // SheikQuery: at least five wives
pa.Query(&sq, &NouveauChic); // Sheiks with at least five wives who
// have at least five children
cq.SetName(PtString("Fred"), PtEQ); // ChildQuery: Name == Fred
wq.SetChildren(5, PtGTE, &cq); // WifeQuery: at
// least five children named Fred
sq.SetWives(5, PtGTE, &wq); // SheikQuery: at least five wives
// with at least five kids named Fred
pa.Query(&sq, &NouveauChic); // Sheiks with at least five wives
// with at least five
// children named Fred

Identity queries can also be done for sets. In our example the generated method looks like this:

int Setwives (dword val, PtCmpOp op, Wife* param);

jealous sheik wanted to make sure that no other sheik has Esther as one of his wives. He could use the following code:

PersonSet HusbandsOfThisEsther;
Wife* Esther;
GetEstherOutOfThinAir(Esther);
sq.SetWife(1, PtGTE, Esther); // At least one wife is this Esther
AllPerson->Query (&sq, &HusbandsOfThisEsther);
assert( HusbandsOfThisEsther->GetNum() <= 1);


Example 4: weather data


Intro

If your set does not contain objects, then you may want to do direct comparisons. This can be done for any C++ base type or for any other type known to the type manager.

Suppose we have an application which measures the temperature every hour during the course of a day and stores the measurements. The measurements for a day might be stored in a set:

persistent class WeatherData
{
PtDate date;
cset<float*> temperature;
cset<float*> relative_humidity;
cset<float*> wind_speed;
};
typedef lset<ondemand<WeatherData>> WeatherDataSet;

Now we may want to know on which days the temperature was at least 37 degrees or no more than 0 degrees for at least 4 hours:

WeatherDataAllSet wa;
WeatherDataQuery wq;
WeatherDataSet UnderTheWeather, OverTheWeather;
// At least 4 measurements greater than or equal to 37.0
wq.Settemperature(4, PtGTE, 37.0, PtGTE);
wa.Query(&wq, &OverTheWeather);
// At least 4 measurements less than or equal to 0.0
wq.Settemperature(4, PtGTE, 0.0, PtLTE);
wa.Query(&wq, &UnderTheWeather);


Example 5: Bigamist queries


Intro

PTXX also generates query methods for fixed-sized arrays contained within objects. If an array contains pointers to persistent objects, then its queries take the same form as they would for a set, but there is no default parameter for the query specification (since we always know how many elements are in an array, there is no need to be able to generate queries to determine the size of the array). In the following example a bigamist is analogous to our sheik, but a bigamist has an array with precisely two wives.

The code is also analogous to our sheik example:

persistent class Bigamist : public Person
{
Wife* wives[2];
};

PersonAllSet pa;
ChildQuery cq;
BigamistQuery bq;
WifeQuery wq;
BigamistSet bg;
cq.SetName(PtString("Fred"), PtEQ); // ChildQuery: Name == Fred
wq.SetChildren(5, PtGTE, &cq); // WifeQuery: at least five children named
// Fred
bq.SetWives(1, PtGTE); // BigamistQuery: at least one wife
pa.Query(&bq, &bg); // Bigamists with at least one wife who
// have at least five children named Fred

Identity queries also use the same form as for sets:

PersonSet HusbandsOfThisEsther;
Wife* Esther;
GetEstherOutOfThinAir(Esther);
bq.SetWife(1, PtGTE, Esther); // BigamistQuery: At least one wife is
// this Esther
AllPerson->Query (&bq, &HusbandsOfThisEsther);
assert( HusbandsOfThisEsther->GetNum() <= 1); // Polyandry is not allowed!


Example 6: Weather data (for arrays)


Intro

If the array contains types known to the type manager, then the above methods are not generated. Instead, one method will be generated to test for the number of elements meeting a condition and one method will be generated to compare whole arrays.

The former has the same form as direct comparisons for sets. If our weather example had the following persistent class declaration, then our application code would be identical to that given above:

// In .HCD file
persistent class WeatherData
{
PtDate date;
float temperature[24];
float relative_humidity[24];
float wind_speed[24];
};
typedef lset<ondemand<WeatherData>> WeatherDataSet;
// In .cxx file:
WeatherDataAllSet wa;
WeatherDataQuery wq;
WeatherDataSet UnderTheWeather, OverTheWeather;
// At least 4 measurements greater than or equal to 37.0
wq.Settemperature(4, PtGTE, 37.0, PtGTE);
wa.Query(&wq, &OverTheWeather);
// At least 4 measurements less than or equal to 0.0
wq.Settemperature(4, PtGTE, 0.0, PtLTE);
wa.Query(&wq, &UnderTheWeather);


Example 7: Trigraphs


Intro

Finally, you can use an entire array as the basis for a query. Array elements are compared sequentially just as characters in a string are compared in strcmp(), but the entire array is always compared- a zero does not terminate the array.

If we have this in our .HCD file:

// does anybody out there remember trigraphs?
typedef Trigraph char[3];
typedef cset<TrigraphToken*> TrigraphTokenSet;
persistent class TrigraphToken
{
Trigraph trigraph;
char char_equivalent;
int token;
};

Then we can search for tokens based on trigraphs:

TrigraphTokenSet tri;
TrigraphTokenQuery tq;
TrigraphTokenAllset ta;
Trigraph searchkey;
strncpy(searchkey,"??=", 3); // Why do trigraphs look so weird?
tq.Settrigraph(searchkey,PtEQ);
ta.Query(&tq, &tri);


Copyright (c) 1996 POET Software, Inc. All rights reserved. Reproduction in whole or in part in any form or medium without the express permission of POET Software, Inc. is prohibited.