General Query Framework, an updated proposal

Derek Atkins warlord@MIT.EDU
29 Jan 2002 23:14:04 -0500


--=-=-=

Ok, here is an updated set of headers.  I've flushed out the types a
bit more as per your suggestions, and I've flushed out the model as
well.

There are now three registries:
        1) core data types
        2) gnucash objects' data parameters
        3) gnucash objects' "parent" objects

The third registry basically allows you to register functions that
basically say: "given a split, return the account" and then perform
queries against the account object.

When you build a query term, you basically supply an object_type,
parameter_name, comparitor, predicate_data, and query_operator.  When
you run the query, you tell the query what kind of object you're
looking for and it will iterate over those objects.  For each term it
uses registry 3 to convert from the requested object to the querried
object, registry 2 to lookup the core type and access function, and
registry 1 to look up the match_predicate function.  In this way
you can build a query that says:

        Return all splits where account->account_name equals regex (name)

In this example:
 - "split" is the requested object,
 - "account" is the querried object,
 - "account_name" is the parameter (of type QUERYCORE_STRING),
 - "equals" is the comparitor,
 - "regex (name)" is the predicate_data (for type QUERYCORE_STRING)

I _think_ that this API can fully implement the existing Query API.
Please send me comments.

Thanks,

-derek


--=-=-=
Content-Disposition: attachment; filename=QueryNew.h
Content-Description: Top-Level Query API

/*
 * QueryNew.h -- API for finding Gnucash objects
 * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
 *
 */

#ifndef GNC_QUERYNEW_H
#define GNC_QUERYNEW_H

/* A Query */
typedef struct querynew_s QueryNew;

/* Query Term Operators, for combining Query Terms */
typedef enum {
  QUERY_AND=1,
  QUERY_OR,
  QUERY_NAND,
  QUERY_NOR,
  QUERY_XOR
} QueryOp;


/* Standard Query Term comparitors, for how to process a query term.
 * Note that not all core types implement all comparitors
 */
typedef enum {
  COMPARE_LT = 1,
  COMPARE_LTE,
  COMPARE_EQUAL,
  COMPARE_GT,
  COMPARE_GTE
} query_compare_t;


/* Types of Gnucash Queriable Objects  (const char *) */
#define QUERY_TYPE_SPLIT	"split"
#define QUERY_TYPE_ACCOUNT	"account"
#define QUERY_TYPE_TRANSACTION	"transaction"


/* Type of Query Core Objects (String, Date, Numeric, GUID, etc. */
typedef const char * QueryCoreType;

/*
 * List of known core query types... 
 * Each core query type defines it's set of optional "comparitor qualifiers".
 */
#define QUERYCORE_STRING	"string"
typedef enum {
  STRING_MATCH_CASEINSENSITIVE	= 1,
  STRING_MATCH_REGEXP
} string_match_t;

#define QUERYCORE_DATE		"date"
typedef enum {
  DATE_MATCH_ROUNDED = 1
} date_match_t;

#define QUERYCORE_NUMERIC	"numeric"
typedef enum {
  NUMERIC_MATCH_ABS = 1,
  NUMERIC_MATCH_NEG_ONLY,
  NUMERIC_MATCH_POS_ONLY
} numeric_match_t;

#define QUERYCORE_GUID		"guid"
typedef enum {
  GUID_MATCH_ALL = 1,
  GUID_MATCH_ANY
  GUID_MATCH_NONE
} guid_match_t;

#define QUERYCORE_INT64		"gint64"
#define QUERYCORE_DOUBLE	"double"
#define QUERYCORE_BOOLEAN	"boolean"
#define QUERYCORE_KVP		"kvp"

/* A CHAR type is for a RECNCell */
#define QUERYCORE_CHAR		"character"


/* Basic API Functions */

/* This is the general function that adds a new Query Term to a query.
 * It will find the 'obj_type' object of the search item and compare
 * the 'param_name' parameter to the predicate data via the comparitor.
 *
 * For example:
 *
 * acct_name_pred_data = make_string_pred_data(STRING_MATCH_CASEINSENSITIVE,
 *					       account_name);
 * gncQueryAddTerm (query, QUERY_TYPE_ACCOUNT, QUERY_ACCOUNT_NAME,
 *		    COMPARE_EQUAL, acct_name_pred_data, QUERY_AND);
 */
void gncQueryAddTerm (QueryNew *query,
		      const char *obj_type, const char *param_name,
		      query_compare_t comparitor, gpointer pred_data,
		      QueryOp op);


/* Run the query:
 *
 * ex: gncQueryRun (query, QUERY_TYPE_SPLIT);
 */
GList * gncQueryRun (QueryNew *query, const char *obj_type);

#endif /* GNC_QUERYNEW_H */

--=-=-=
Content-Disposition: attachment; filename=QueryCore.h
Content-Description: Core Data Type registration

/*
 * QueryCore.h -- API for providing core Query data types
 * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
 *
 */

#ifndef GNC_QUERYCORE_H
#define GNC_QUERYCORE_H

#include "QueryNew.h"
#include "QueryObject.h"	/* for QueryAccess */

/* 
 * An arbitrary Query Predicate.  Given the gnucash object and the
 * particular parameter get-function (obtained from the registry by
 * the Query internals), compare the object's parameter to the
 * predicate data given the match_options
 */
typedef int (*QueryPredicate) (gpointer object,
			       QueryAccess get_fcn,
			       query_compare_t how,
			       gint match_options,
			       gpointer pdata);

/* A callback for how to destroy a query predicate's pdata */
typedef void (*QueryPredDataFree) (gpointer pdata);


/* This function registers a new Core Object with the QueryNew
 * subsystem.  It maps the "core_name" object to the given
 * query_predicate and predicate_data_free functions.
 */
void gncQueryRegisterCoreObject (char const *core_name,
				 QueryPredicate pred,
				 QueryPredDataFree pd_free);


/* An example:
 *
 * gncQueryRegisterCoreObject (QUERYCORE_STRING, string_match_predicate,
 *			       string_free_pdata);
 */


#endif /* GNC_QUERYCORE_H */

--=-=-=
Content-Disposition: attachment; filename=QueryObject.h
Content-Description: Gnucash Object registration

/*
 * QueryObject.h -- API for registering queriable Gnucash objects
 * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>
 *
 */

#ifndef GNC_QUERYOBJECT_H
#define GNC_QUERYOBJECT_H

#include "QueryNew.h"

/* Define an arbitrary function pointer for access functions.  This is
 * because C doesn't have templates, so we just cast a lot.  Real
 * functions must be of the form:
 * 
 * <param_type> function (object_type *obj);
 */
typedef void (*QueryAccess)(gpointer);

/* This structure is for each queriable parameter in an object */
typedef struct query_object_def {
  const char *	param_name;
  QueryCoreType	param_type;
  QueryAccess	param_getfcn;
} QueryObjectDef;

/* This function registers a new Gnucash Object with the QueryNew
 * subsystem.  It registers the "obj_name" object which provides the
 * "methods" set of queriable parameters.  The "methods" array must
 * be a NULL-terminated array.
 */
void gncQueryRegisterObject (const char * obj_name,
			     const QueryObjectDef *methods[]);

/* An example:
 *
 * #define MY_QUERY_OBJ_MEMO	"memo"
 * #define MY_QUERY_OBJ_VALUE	"value"
 * #define MY_QUERY_OBJ_DATE	"date"
 *
 * static QueryObjectDef myQueryObject[] = {
 * { MY_QUERY_OBJ_MEMO, QUERYCORE_STRING, myMemoGetter },
 * { MY_QUERY_OBJ_VALUE, QUERYCORE_NUMERIC, myValueGetter },
 * { MY_QUERY_OBJ_DATE, QUERYCORE_DATE, myDateGetter },
 * NULL };
 *
 * gncQueryRegisterObject ("myObjectName", &myQueryObject);
 */

/* This function-type will convert from one object-type to another */
typedef gpointer (*QueryConvert)(gpointer);

typedef struct query_convert_def {
  const char *	object_name;
  QueryConvert	object_getfcn;
} QueryConvertDef;

/* This function registers a new Gnucash Object and provides a
 * set of methods to obtain other Gnucash objects from this one.
 * The methods array much be NULL-terminated.
 */
void gncQueryRegisterConversion (const char *obj_name,
				 const QueryConvertDef *methods[]);

/* An example:
 *
 * static QueryConvertDef myQueryObjects[] = {
 * { QUERY_TYPE_ACCOUNT, myAccountGetter },
 * { QUERY_TYPE_TRANSACTION, myTransactionGetter },
 * NULL };
 *
 * gncQueryRegisterConversion ("myObjectName", &myQueryObjects);
 */

#endif /* GNC_QUERYOBJECT_H */

--=-=-=



-- 
       Derek Atkins, SB '93 MIT EE, SM '95 MIT Media Laboratory
       Member, MIT Student Information Processing Board  (SIPB)
       URL: http://web.mit.edu/warlord/    PP-ASEL-IA     N1NWH
       warlord@MIT.EDU                        PGP key available

--=-=-=--