Clarification of Question by
daver-ga
on
18 Nov 2002 11:00 PST
The results are stored in CString that are part of CStringArray.
Typically I am caching the first 200 results, but a result set of
several thousand is common. For performance reasons, I would like to
avoid looping through the whole result set just for the count. I can
certainly provide more code if it is useful.
I've also been experimenting with another option that I'll paste below
this one. If you think that is a more viable option, or if you think
it will be a better performing solution, we can treat it as a seperate
question. If you have an even better C++ solution, I'm a careful
listener :)
bool CQueryIndex::RunQuery(CUserCache* pUserCache, WCHAR const *
pwcRestriction)
{
if(NULL == pUserCache)
return false;
pUserCache->Clear();
WCHAR const * pwcCatalog = L"JobSeeker"; // default: lookup
catalog
WCHAR const * pwcMachine = L"."; // default: local
machine
WCHAR const * pwcColumns = L"DocTitle,size"; // default
output column(s)
WCHAR const * pwcSort = 0;
const ULONG cRowsAtATime = pUserCache->RowsPerPage() + 1;
BOOL fNoQuery = FALSE; // default: execute query
// Create an ICommand object. CIMakeICommand is a shortcut for
making an
// ICommand. The ADVQUERY sample shows the OLE DB equivalent.
XInterface<ICommand> xICommand;
HRESULT hr = CIMakeICommand( xICommand.GetPPointer(), // result
1, // 1 scope
&m_dwScopeFlags, // scope
flags
&m_pwcScope, // scope path
&pwcCatalog, // catalog
&pwcMachine ); // machine
if ( FAILED( hr ) )
return false;
// Get a command tree object
XInterface<ICommandTree> xICommandTree;
hr = xICommand->QueryInterface( IID_ICommandTree,
xICommandTree.GetQIPointer() );
if ( FAILED( hr ) )
return false;
// Create an OLE DB query tree based on query parameters.
DBCOMMANDTREE * pTree;
hr = CITextToFullTreeEx( pwcRestriction, // the query itself
m_ulDialect, // query dialect
pwcColumns, // columns to return
pwcSort, // sort order, may be
0
0, // reserved
&pTree, // resulting tree
0, // no custom
properties
0, // no custom
properties
m_lcid ); // default locale
if ( FAILED( hr ) )
return false;
// If directed, don't issue the query. Parsing it was sufficient.
if ( fNoQuery )
{
xICommandTree->FreeCommandTree( &pTree );
return S_OK;
}
// Set the tree in the ICommandTree. Ownership of the tree is
transferred.
hr = xICommandTree->SetCommandTree( &pTree, DBCOMMANDREUSE_NONE,
FALSE );
if ( FAILED( hr ) )
{
xICommandTree->FreeCommandTree( &pTree );
return false;
}
// Set required properties on the ICommand
hr = SetCommandProperties( xICommand.GetPointer(),
m_fForceUseContentIndex );
if ( FAILED( hr ) )
return false;
// Execute the query. The query is complete when Execute()
returns.
hr = xICommand->Execute( 0, // no aggregating IUnknown
IID_IRowset, // IID for interface to
return
0, // no DBPARAMs
0, // no rows affected
pUserCache->xIRowset.GetIUPointer() ); //
result
if ( FAILED( hr ) )
{
// Get the real error; OLE DB permits few Execute() return codes
ERRORINFO ErrorInfo;
XInterface<IErrorInfo> xErrorInfo;
HRESULT hr2 = GetOleDBErrorInfo( xICommand.GetPointer(),
IID_ICommand,
m_lcid,
&ErrorInfo,
xErrorInfo.GetPPointer() );
// Post IErrorInfo only if we have a valid pointer to it.
if ( SUCCEEDED( hr2 ) && !xErrorInfo.IsNull() )
hr = ErrorInfo.hrError;
return false;
}
// Create an accessor, so data can be retrieved from the rowset.
hr = pUserCache->xIRowset->QueryInterface( IID_IAccessor,
pUserCache->xIAccessor.GetQIPointer() );
if ( FAILED( hr ) )
return false;
// Perry this is the function call that fires off the query.
// Count the number of output columns and make bindings for them.
ULONG cColumns = 2;
// Column iOrdinals are parallel with those passed to
CiTextToFullTree,
// so MapColumnIDs isn't necessary. These binding values for
dwPart,
// dwMemOwner, and wType are the most optimal bindings for Indexing
// Service.
XPtr<DBBINDING> xBindings( cColumns );
if ( xBindings.IsNull() )
return false; // E_OUTOFMEMORY;
memset( xBindings.Get(), 0, sizeof DBBINDING * cColumns );
for ( ULONG i = 0; i < cColumns; i++ )
{
xBindings[i].iOrdinal = 1 + i; // 1-based column number
xBindings[i].obValue = i * sizeof( PROPVARIANT * ); // offset
xBindings[i].dwPart = DBPART_VALUE; // retrieve value, not
status
xBindings[i].dwMemOwner = DBMEMOWNER_PROVIDEROWNED; // provider
owned
xBindings[i].wType = DBTYPE_VARIANT | DBTYPE_BYREF; //
VARIANT *
}
hr = pUserCache->xIAccessor->CreateAccessor( DBACCESSOR_ROWDATA,
// rowdata accessor
cColumns,
// # of columns
xBindings.Get(),
// columns
0,
// ignored
&pUserCache->hAccessor, // result
0 );
// no status
if ( FAILED( hr ) )
return false;
CString strVal;
int iCount = 0;
if( pUserCache->xData.IsNull() )
{
hr = E_OUTOFMEMORY;
pUserCache->ReleaseAccessor();
return false;
}
pUserCache->QueryStart();
pUserCache->cRowsSoFar = 0;
pUserCache->aHRow = new HROW[cRowsAtATime];
pUserCache->pgrHRows = pUserCache->aHRow;
DBCOUNTITEM cCount = pUserCache->AppendToEnd() ?
pUserCache->GetNumCache() : 1;
DBCOUNTITEM cRowsReturned = 0;
hr = pUserCache->xIRowset->GetNextRows( 0, // no
chapter
0, // no rows
to skip
cRowsAtATime, // # rows
to get
&cRowsReturned, // # rows
returned
&pUserCache->pgrHRows);
// resulting hrows
if ( FAILED( hr ) )
return false;
for ( DBCOUNTITEM iRow = 0; iRow < cRowsReturned; iRow++ )
{
HRESULT hr2 = pUserCache->xIRowset->GetData(
pUserCache->aHRow[iRow], // hrow being accessed
pUserCache->hAccessor, // accessor to use
pUserCache->xData.Get() ); // resulting data
if ( FAILED( hr2 ) )
{
hr = hr2;
return cCount > 1 ? true : false;
}
CString& str = pUserCache->GetAt(cCount);
str.Format("%S\n", pUserCache->xData[0]->pwszVal);
cCount++;
}
if ( 0 != cRowsReturned ) // Release the HROWs retrived in
GetNextRows
{
pUserCache->cRowsSoFar += cRowsReturned;
pUserCache->xIRowset->ReleaseRows( cRowsReturned, // # of rows
to release
pUserCache->aHRow, //
rows to release
0,0,0); // no options,
no refcounts, no status
}
// Check if all rows are now retrieved.
if ( DB_S_ENDOFROWSET == hr || DB_S_ROWLIMITEXCEEDED == hr )
{
hr = S_OK; // succeeded, return S_OK from DoQuery
pUserCache->ReleaseAccessor();
return true;
}
// Check if the query aborted because it was too costly.
if ( DB_S_STOPLIMITREACHED == hr )
{
hr = S_OK;
pUserCache->ReleaseAccessor();
return false;
}
if ( FAILED( hr ) )
{
pUserCache->ReleaseAccessor();
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////////
//// optional soloution for getting results from indexing services
/////
IixssoQuery query;
query.CreateDispatch("IXSSO.Query.2");
query.SetCatalog("JobSeeker");
query.SetColumns("DocTitle");
query.SetQuery("unix");
long lMax = query.GetMaxRecords();
LPDISPATCH rs = query.CreateRecordset("sequential");
At this point I'm stuck again, not knowing how to convert the
IDispatch pointer into a record set object I can use.
Thanks, you help is greatly apprecieated.