OOFILE | Downloads | Purchasing | Press | Services | Company Information | Soapbox | References | F.A.Q. | HOME

OOFTST 21 - multiple field sorting.

This sample tests multi-level sorting and special Any Of and All Of searches for single-pass searching with a number of character strings.


#include "oofile.h"	// the general oofile library
#include "oofios.h"     // stream io

First we declare a new class. We will call the fields f1, f2 and f3, so trhat when we start to set sort-orders, we can see easily what order they are in. Note that we are also using 3 very different field types. The fields don't have to be the same type for multiple sorts.

DECLARE_CLASS(dbTest21)
	dbChar		f1;
	dbUlong		f2;
	dbReal		f3;  

Here we declare the class to own a sorter. This type will be explained later. We declare it as staitc as it will not belong to each instance of this class, but the table as a whole.

	static dbSorter sort123;	  
	dbTest21() : f1(20, "F1", kIndexed),
				f2("F2", kIndexed),
				f3("F3", kIndexed)
{};
	  

We declare the data entry procedures.

	void Add(const char *tf1,const unsigned long tf2,const double tf3);
	void AddTestData();
};

This first procedure is for adding a single record to our table.

void dbTest21::Add(const char *tf1,const unsigned long tf2,const double tf3)
{
	newRecord();
	f1=tf1;
	f2=tf2;
	f3=tf3;
	saveRecord();
}
  

The second procedure adds our test data. Note there are no unusual records in this test as we are not testing boundary conditions, just the basic ability of various functions.

void dbTest21::AddTestData()
{
	Add("Denton",2000,3.5);
	Add("Taylor",2000,4.5);
	Add("Taylor",3000,5.5);
	Add("Taylor",2000,3.5);
	Add("Denton",1000,1.5);
	Add("Underwood",1000,1.5);
	Add("Zaphod",5000,5.5);
}

  

We declare another class, derived from dbTable. We will use this one to test reverse sorts within our nested sorting procedure.

class dbArchive : public dbTable {   
	OOFILE_METHODS(dbArchive)    
public:  // table columns for all archives
	dbLong	Size; // in bytes
	dbDate	Date;
	dbChar	FileName;

Note that our dbSorter is not declared static this time. This indicates a slightly different way of viewing the sorter, as the default sort order for each object. The effect in this test will be the same.

	dbSorter arcSort;	  
	dbArchive() : 
		Size("Size", kIndexed),
		Date("Date Created"),
		FileName(80, "File Name", kIndexCompress)
	{

Here we pass the fields to sort by into our sorter. Note that we use an unusual notation for those familiar with the "<<" and ">>" operators. When passing fields into a sorter, we may use << to pass them in as normal, but we can also use >> to pass them in reverse order -> the line below would be equivalent to the following:

arcSort << reverse(Date) << FileName

Testing: passing in reverse sort field using operator>>

		arcSort >> Date << FileName;
	};
	void AddTestData();
};  

Here we add the test data to our other table.

void
dbArchive::AddTestData() {
	dbDate::sDefaultDateOrder = dbDate::orderMDY;

We create a stream to put all the data in and pass in  filenames as though they are in an archive list.

	ostrstream oss;
	oss << "153357\tMar 15 1996\tart/zine/real-macoy-11.hqx" << endl
		<< "288387\tMar 15 1996\tart/zine/real-macoy-12.hqx" << endl
		<< "205018\tMar 15 1996\tart/zine/real-macoy-13.hqx" << endl
		<< "448459\tMar 15 1996\tart/zine/real-macoy-14.hqx" << endl
		<< "6096\tMar 16 1996\tdev/cw LMouser.hqx" << endl
		<< "190561\tMar 16 1996\tdev/cw cw-pascal-tcl-port-10.hqx" << endl;

We cannot read from the output stream so we pass the whole stream to an input stream where it can be passed into the record (referred to by *this).

	istream is(oss.rdbuf());
	is >> *this;
}  

Now we define our static sort member. Whenever you use a static variable, you must provide a
definition in one of the source files for the compiler to allocate storage for it.

	dbSorter		 dbTest21::sort123;  
int main()
{
	cout << "OOFILE Validation Suite - Test 21\n"
		 << "This tests the string contains functions\n"
		 << "and the nested sorts\n\n";

We declare our global variables for the two tables and the sorter.

	TEST_CONNECT  theDB;
	dbTest21     	 dbTest;
	dbArchive		 Archive;
	  

This section tests a sort  bug found. Occaisionally, when the first and second fields were identical, the third field wouldn't sort.

Here, we initialise the static sort. We pass ion the fields we want it to sort by, in the order we wish them sorted. This is very similar to passing fields into a dbView.

	dbTest21::sort123 << dbTest.f1 << dbTest.f2 << dbTest.f3;    

We test to see what platform we are running under and then assign a database filename accordingly.

	#ifdef TESTING_DBASE
		#ifdef _Macintosh
			const char* kExistsName =  ":ooftst21:dbTest21.dbf";
			const char* kDatabaseName = ":ooftst21:";
		#else
			const char* kExistsName =   "dbTest21.dbf"
			const char* kDatabaseName = "";
		#endif	  
	#else
		const char* kDatabaseName = "ooftst21.db";
		const char* kExistsName = kDatabaseName;
	#endif

We check to see if this filename already exists, then open it (if it does) or create it and add test data (if it doesn't).

	if (dbConnect::fileExists(kExistsName))
		theDB.openConnection(kDatabaseName);
	else {
		theDB.newConnection(kDatabaseName);
		dbTest.AddTestData();
		Archive.AddTestData();
	}

We start by printing out the database as it exists, so we can see what the initiall order of it is (before sorting).

	cout << "Listing All Records" << endl << endl;
	cout << dbTest;

Our first operation will be to search our selection for records containing one or more of a given list of partial strings. These are passed as a delimited list to the function searchSelContainsAnyDelimited(). You must pass in the field that is to be tested and the delimiter character being used. This search should return with every record except for the one with "Zaphod".

Note: We will print the database after each test to see what has been included in the selection.

Testing: function searchSelContainsAnyDelimited()

	cout << endl << "Test 'Contains Any' Search - searching for 'ta', 'en' or 'wood'" << endl << endl;
	dbTest.searchSelContainsAnyDelimited(dbTest.f1, "ta,en,wood",',');
	cout << dbTest;

The next function tested is very similar to above, but it will only return those records that contain all of the partial strings gievn. Like the function above, you must pass in the field you wish to be searched, the delimited list of partial strings and the delimiter character used. This search will return only with the records containing "Underwood".

Testing: function searchSelContainsAllDelimited()

	cout << endl << "Test 'Contains All' Search - searching for 'wood', 'u' and 'de'" << endl << endl;
	dbTest.searchSelContainsAllDelimited(dbTest.f1, "wood,u,de",',');
	cout << dbTest;

We now undo our selection so that we can start a new set of tests with all the records.  

	dbTest.selectAll();

Now we will try a nested sort. We call the usual setSortOrder() function, but pass it the value of our static sorter (that we initialised above). This will sort the database by the fields we passed to our sorter, sorting first through fields one, then, for records whose first field is identical, the records will be sorted in order of field two. If there are records whose second field are also identical, it will sort by the third field. This specifically tests the field sort bug mentioned earlier. If we have records where all three fields are identical, they will be listed in the order they are come across in the database. We will then print the database so we can see if it has worked correctly.

Testing: nested sort using static dbSorter

	cout << endl <<"Testing Nested sort - sorting by all fields, 1 - 2 - 3" << endl << endl;
	dbTest.setSortOrder(dbTest.sort123);
	cout << dbTest;  

We now ask it to print out only the fields with the same first value. This gives the effect of seeing how it still sorts the records in the order of the other fields given. The way we actually write this expression is a bit more complex than usual -> we call a search on the table, asking for only those records with "Tay"  in the first field -> this will return only the "Taylor" records -> note that we are using our knowledge of the small database that we have created, in a real situation we wouldn't be able to do this and be convinced we'd only have records with the same first field, but then in a real situation, we shouldn't need to test if the nested sort works.

Testing: nested sort using static dbSorter on selection with same first field

	cout << endl <<"Testing Nested sort on 1st all same value - sorting by all fields, 1 - 2 - 3" << endl << endl;
	dbTest.search(dbTest.f1.startsWith("Tay"));
	cout << dbTest;  

We clear our selection again so that we can start anew.

	dbTest.selectAll();

We'll now try and change our sort order. We'll start by declaring a new dbSorter and passing in the fields in a daifferent order (2 1 3 instead of 1 2 3).

	cout << endl <<"Testing Nested sort - sorting by all fields, 2 - 1 - 3" << endl << endl;
	dbSorter sort213;
	sort213 << dbTest.f2 << dbTest.f1 << dbTest.f3;

Now we ask it to sort the database. Note that as it isn't the sorter attached to the class, we just declare the sorter and don't have to specify the table it comes from, as before.

Testing: nested sort using dbSorter

	dbTest.setSortOrder(sort213);
	cout << dbTest;  

Now we will reverse the sort order of one of the items. We will declare our sort order using a temporary dbSorter (much like we can declare a temporary dbView to pass to cout. See example of this).

Testing: nested sort using temporary dbSorter

	cout << endl << "now reversing the second sort item in the above test" << endl << endl;
	dbTest.setSortOrder(dbSorter() << dbTest.f2 << reversed(dbTest.f1) << dbTest.f3);
	cout << dbTest;

Sorting does not actually alter the database itself, merely provides a differnet (sorted) way of looking at it. If we wish, we can remove this way of looking at it temporarily to see what it looked like before, we can then reapply the sort order once more. We do this with a pair of functions: suspendSorting() and resumeSorting(). suspendSorting() temporarily allows us to see how the database was without any osrt order. resumeSorting() will return it to the sort order we have set for it. To test these function, we first set the sort order to our original sorter, with fields 1 then 2 then 3.

	cout << endl << "Testing suspended sorts and cloning selections (fields 123)" << endl;
	dbTest.setSortOrder(dbTest.sort123);

Then we suspend sorting, then print it out. The database should be seen in its original, unsorted condition

Testing: function suspendSorting()
Testing: print of sort-suspended table.

	dbTest.suspendSorting();
	cout << "Unsorted..." << endl << dbTest << endl;  

Now, we will try a search so that we find only those records with an identical first field. This will show us how it is unsorted.

Testing: search on sort-suspended table

	dbTest.search(dbTest.f1.startsWith("Tay"));
	cout << "Unsorted Taylor records..." << endl << dbTest << endl;  

Now we will see the effects of cloning the table while sorting is suspended. This new table should have the  same sort order, still suspended 

Testing: cloning sort-suspended table

	dbTest21 cloned(dbTest);  

Now we resume our sorting and print it out to see what happens.

Testing: function resumeSorting()

	dbTest.resumeSorting();
	cout << "Sorted..." << endl << dbTest << endl;

Now we have a look at our clone and see if it has changed. It hasn't had the resumeSorting() function called upon it and so it should still be suspended.

Testing: print of clone of sort-suspended table (clone still suspended)

	cout << "Unsorted clone..." << endl << cloned << endl;

Now we call resumeSorting() for our cloned table and print it out again. it should be sorted just like the other one.

Testing: function resumeSorting() on cloned sort-suspended table

	cloned.resumeSorting();
	cout << "Sorted clone..." << endl << cloned << endl;

Now we go on to test reverse sorting, within a nested sort.  We start by printing out the initial condition of the archive table.

	cout << "Now testing some additional multiple field sort bugs" << endl;
	cout << "Unsorted" << endl << Archive << endl;

Then attempt to call its sorter. The Date field should be in revrse order and it will sort by the FileName field for those records with identical Dates.

	Archive.setSortOrder(Archive.arcSort);
	cout << ">> Date << FileName" << endl << Archive << endl;  
	cout << endl <<"Test Completed" << endl;
	  
	return EXIT_SUCCESS;
}  

 

Feature index

(c) Copyright A.D. Software 1994-2000 (All Rights Reserved).
Last Updated: 9th September 2001