void loadThesaurus(CIndexer *indexer)
{
  CConnbas_dbox *connbas = indexer->connbas;
  time_t struct_moddate, thesaurus_moddate, cterms_moddate;

  // ----------------------- load structure and thesaurus
  char *xmlstruct;
  char **pxmlstruct = NULL;
  unsigned long  xmlstruct_length;

  char *xmlthesaurus;
  char **pxmlthesaurus = NULL;
  unsigned long  xmlthesaurus_length;

  char *xmlcterms;
  char **pxmlcterms = NULL;
  unsigned long  xmlcterms_length;

  bool struct_changed, thesaurus_changed, cterms_changed;

  std::string cstr;

  char strbuff[1000];

    // read the 3 moddates
	connbas->selectPref_moddates(&struct_moddate, &thesaurus_moddate, &cterms_moddate);

	// what has changed
	struct_changed    = indexer->firstLoad || (struct_moddate    > indexer->current_struct_moddate);
	thesaurus_changed = indexer->firstLoad || (thesaurus_moddate > indexer->current_thesaurus_moddate);
	cterms_changed    = indexer->firstLoad || (cterms_moddate    > indexer->current_cterms_moddate);

	indexer->firstLoad = false;

	if(!struct_changed && !thesaurus_changed && !cterms_changed)
	{
		// nothing changed in the prefs
		return;
	}

	// fix "scout" : when cterms change, links from structure may be corrupted
	if(cterms_changed || thesaurus_changed)
	{
		struct_changed = true;
	}

	if(struct_changed)
	{
		// the structure changed : reload
		pxmlstruct    = &xmlstruct;
	}
	if(thesaurus_changed)
	{
		// the thesaurus changed
		pxmlthesaurus = &xmlthesaurus;
	}
	if(cterms_changed)
	{
		// the cterms changed
		pxmlcterms    = &xmlcterms;
	}

	// read useful fields
	if(connbas->selectPrefs(pxmlstruct, &xmlstruct_length, pxmlthesaurus, &xmlthesaurus_length, pxmlcterms, &xmlcterms_length) != 0)
	{
		// erreur sql
		return;
	}


	// ============================ load thesaurus
	if(thesaurus_changed)
	{
		if(indexer->DocThesaurus)
		{
			xmlFreeDoc(indexer->DocThesaurus);
			indexer->DocThesaurus = NULL;
		}

		if(indexer->XPathCtx_thesaurus)
		{
			xmlXPathFreeContext(indexer->XPathCtx_thesaurus);
			indexer->XPathCtx_thesaurus = NULL;
		}

		// we have the thesaurus, load in libxml
		indexer->DocThesaurus = xmlParseMemory(xmlthesaurus, xmlthesaurus_length);
		if(indexer->DocThesaurus != NULL)
		{
			// Create xpath evaluation context
			indexer->XPathCtx_thesaurus = xmlXPathNewContext(indexer->DocThesaurus);
			if(indexer->XPathCtx_thesaurus != NULL)
			{
			}
		}
		zSyslog._log(CSyslog::LOGL_THESAURUS, CSyslog::LOGC_THESAURUS, "#%ld : thesaurus loaded", connbas->sbas_id);
	}


	// ============================ load cterms
	if(cterms_changed)
	{
		if(indexer->tStructField)
		{
			delete [] (indexer->tStructField);
			indexer->tStructField = NULL;
		}

		if(indexer->DocCterms)
		{
			xmlFreeDoc(indexer->DocCterms);
			indexer->DocCterms = NULL;
		}

		if(indexer->XPathCtx_cterms)
		{
			xmlXPathFreeContext(indexer->XPathCtx_cterms);
			indexer->XPathCtx_cterms = NULL;
		}

		if(indexer->XPathCtx_deleted)
		{
			xmlXPathFreeContext(indexer->XPathCtx_deleted);
			indexer->XPathCtx_deleted = NULL;
		}
		indexer->xmlNodePtr_deleted = NULL;

		// we have the cterms, load in libxml
		indexer->DocCterms = xmlParseMemory(xmlcterms, xmlcterms_length);
		if(indexer->DocCterms != NULL)
		{
			// Create xpath evaluation context
			indexer->XPathCtx_cterms = xmlXPathNewContext(indexer->DocCterms);
			if(indexer->XPathCtx_cterms != NULL)
			{

				xmlXPathObjectPtr  xpathObj_cterms = NULL;

				xpathObj_cterms = xmlXPathEvalExpression((const xmlChar*)("/cterms/te[@delbranch='1']"), indexer->XPathCtx_cterms);
				if(xpathObj_cterms)
				{
					if(xpathObj_cterms->nodesetval)
					{
						xmlNodeSetPtr nodes_cterms = xpathObj_cterms->nodesetval;

						if(nodes_cterms->nodeNr > 0)
						{
							xmlNodePtr node_cterms = nodes_cterms->nodeTab[0];
							indexer->XPathCtx_deleted = xmlXPathNewContext((xmlDocPtr)node_cterms);

							// in the indexer, we keep the node to the deleted
							indexer->xmlNodePtr_deleted = nodes_cterms->nodeTab[0];
						}
					}
					xmlXPathFreeObject(xpathObj_cterms);
				}

			}
		}
		indexer->ctermsChanged = false;

		zSyslog._log(CSyslog::LOGL_THESAURUS, CSyslog::LOGC_THESAURUS, "#%ld : cterms loaded", connbas->sbas_id);
	}


	// ============================ load structure
	if(struct_changed)
	{
		xmlDocPtr          doc_struct;
		xmlXPathContextPtr xpathCtx_struct;
		xmlXPathObjectPtr  xpathObj_struct;

		if(indexer->tStructField)
		{
			delete [] (indexer->tStructField);
			indexer->tStructField = NULL;
		}

		// load in libxml
		doc_struct = xmlParseMemory(xmlstruct, xmlstruct_length);
		if(doc_struct != NULL)
		{
			// Create xpath evaluation context
			xpathCtx_struct = xmlXPathNewContext(doc_struct);
			if(xpathCtx_struct != NULL)
			{
				// ----- search every fields of the structure
				// Evaluate xpath expression
				xpathObj_struct = xmlXPathEvalExpression((const xmlChar*)"/record/description/*", xpathCtx_struct);
				if(xpathObj_struct != NULL)
				{
					if(xpathObj_struct->nodesetval)
					{
						xmlNodeSetPtr nodes_struct = xpathObj_struct->nodesetval;

						indexer->nStructFields = nodes_struct->nodeNr;
						if(indexer->nStructFields > 0)
						{
							// allocate a TABLE of fields
							indexer->tStructField = new CStructField[indexer->nStructFields];
						}

						// ---- scan every nodes of the result on struct
						cstr = "/-------------------------------- Loading structure  -----\n";

						for(int i=0; i<indexer->nStructFields; i++)
						{
							xmlNodePtr node_struct = nodes_struct->nodeTab[i];

							cstr += "|  Field '"+ std::string((const char *)(node_struct->name)) +"'";

							// ---- get attribute 'type' if it exists
							indexer->tStructField[i].type = CStructField::TYPE_NONE;			// default
							xmlChar *type = (xmlChar *)"";
							if( (type = xmlGetProp(node_struct, (const xmlChar *)"type")) )
							{
								if(!isWhite(type))
								{
									if(strcmp((const char *)type, "text")==0)
										indexer->tStructField[i].type = CStructField::TYPE_TEXT;	// <... type="text"

									else if(strcmp((const char *)type, "number")==0)
										indexer->tStructField[i].type = CStructField::TYPE_INT;		// <... type="number"

									else if(strcmp((const char *)type, "float")==0)
										indexer->tStructField[i].type = CStructField::TYPE_FLOAT;	// <... type="float"

									else if(strcmp((const char *)type, "date")==0)
										indexer->tStructField[i].type = CStructField::TYPE_DATE;	// <... type="date"
								}
								snprintf(strbuff, 1000, "{ type='%s' (%d) }", type, indexer->tStructField[i].type);
								cstr += strbuff;
								xmlFree(type);
							}
							else
							{
								snprintf(strbuff, 1000, "{ type='' (%d) }", indexer->tStructField[i].type);
								cstr += strbuff;
							}
							// ---- get attribute 'index' if it exists
							indexer->tStructField[i].index = true;			// default
							xmlChar *index;
							if( (index = xmlGetProp(node_struct, (const xmlChar *)"index")) )
							{
								if(!isWhite(index))
								{
									if( isno((const char *)index) )
										indexer->tStructField[i].index = false;

								}
								xmlFree(index);
							}
							snprintf(strbuff, 1000, " { index=%d }", indexer->tStructField[i].index );
							cstr += strbuff;

							// ---- get attribute 'business' if it exists
							indexer->tStructField[i].business = false;		// default if NO attribute
							xmlChar *business;
							if( (business = xmlGetProp(node_struct, (const xmlChar *)"business")) )
							{
								indexer->tStructField[i].business = true;		// default if attribute exists
								if(!isWhite(business))
								{
									if( isno((const char *)business) )
										indexer->tStructField[i].business = false;

								}
								xmlFree(business);
							}
							snprintf(strbuff, 1000, " { business=%d }", indexer->tStructField[i].business );
							cstr += strbuff;


							// ---- get attribute 'candidates' if it exists
							indexer->tStructField[i].candidatesStrings
								= indexer->tStructField[i].candidatesDates
								= indexer->tStructField[i].candidatesIntegers
								= indexer->tStructField[i].candidatesFirstDigit
								= indexer->tStructField[i].candidatesMultiDigits
								= true;											// default if NO attribute

							xmlChar *candidates;
							if( (candidates = xmlGetProp(node_struct, (const xmlChar *)"candidates")) )
							{
								indexer->tStructField[i].candidatesStrings
									= indexer->tStructField[i].candidatesDates
									= indexer->tStructField[i].candidatesIntegers
									= indexer->tStructField[i].candidatesFirstDigit
									= indexer->tStructField[i].candidatesMultiDigits
									= false;								// default if attribute exists

								if(!isWhite(candidates))
								{
									for(char *p=(char*)candidates; *p; p++)
									{
										switch(*p)
										{
											case 'S':
											case 's':
												indexer->tStructField[i].candidatesStrings = true;
												break;
											case 'D':
											case 'd':
												indexer->tStructField[i].candidatesDates = true;
												break;
											case 'I':
											case 'i':
												indexer->tStructField[i].candidatesIntegers = true;
												break;
											case '0':
												indexer->tStructField[i].candidatesFirstDigit = true;
												break;
											case '9':
												indexer->tStructField[i].candidatesMultiDigits = true;
												break;
										}
									}

								}
								xmlFree(candidates);
							}

							// ---- get attribute 'tbranch' if it exists
							bool hastbranch = false;
							xmlChar *tbranch;
							if( (tbranch = xmlGetProp(node_struct, (const xmlChar *)"tbranch")) )
							{
								if(!isWhite(tbranch))
								{
									// dump "candidates' field attribute only if there is a tbranch
									cstr += " { candidates='";
									if(indexer->tStructField[i].candidatesStrings == true)
										cstr += "S";
									if(indexer->tStructField[i].candidatesDates == true)
										cstr += "D";
									if(indexer->tStructField[i].candidatesIntegers == true)
										cstr += "I";
									if(indexer->tStructField[i].candidatesFirstDigit == true)
										cstr += "0";
									if(indexer->tStructField[i].candidatesMultiDigits == true)
										cstr += "9";
									cstr += "'}\n";

									// --- copy the full path into the field
									indexer->tStructField[i].set("/record/description/", (const char *)(node_struct->name), (const char *)tbranch);
									xmlFree(tbranch);

									if(indexer->tStructField[i].tbranch && indexer->XPathCtx_thesaurus != NULL)
									{
										// this field has a tbranch, it's linked to the thesaurus
										// build links to the thesaurus

										snprintf(strbuff, 1000, "|    searching tbranch ' %s ' in thesaurus \n", indexer->tStructField[i].tbranch);
										cstr += strbuff;

										xmlXPathObjectPtr  xpathObj_thesaurus = NULL;
										xpathObj_thesaurus = xmlXPathEvalExpression((const xmlChar*)(indexer->tStructField[i].tbranch), indexer->XPathCtx_thesaurus);
										if(xpathObj_thesaurus != NULL)
										{
											if(xpathObj_thesaurus->nodesetval)
											{
												xmlNodeSetPtr nodes_thesaurus = xpathObj_thesaurus->nodesetval;

												snprintf(strbuff, 1000, "|    -> found %d node%s \n", nodes_thesaurus->nodeNr, (nodes_thesaurus->nodeNr==1 ? "s":""));
												cstr += strbuff;

												if(nodes_thesaurus->nodeNr > 0)
												{
													hastbranch = true;

													// in this field, allocate an array of xpathcontext
													indexer->tStructField[i].tXPathCtxThesaurus = new xmlXPathContextPtr[nodes_thesaurus->nodeNr];
													// in this field, allocate an array of nodes
													indexer->tStructField[i].tNodesThesaurus = new xmlNodePtr[nodes_thesaurus->nodeNr];

													if(indexer->tStructField[i].tXPathCtxThesaurus && indexer->tStructField[i].tNodesThesaurus)
													{
														indexer->tStructField[i].nXPathCtxThesaurus = nodes_thesaurus->nodeNr;
														indexer->tStructField[i].nNodesThesaurus    = nodes_thesaurus->nodeNr;
														for(int j=0; j<nodes_thesaurus->nodeNr; j++)
														{
															xmlNodePtr node_thesaurus = nodes_thesaurus->nodeTab[j];
															indexer->tStructField[i].tXPathCtxThesaurus[j] = xmlXPathNewContext((xmlDocPtr)node_thesaurus);
															indexer->tStructField[i].tNodesThesaurus[j]    = node_thesaurus;
														}
													}
												}
											}
											xmlXPathFreeObject(xpathObj_thesaurus);
										}
									}

									if(indexer->tStructField[i].cbranch && indexer->XPathCtx_cterms != NULL)
									{
										// build a link to cterms
										snprintf(strbuff, 1000, "|    searching cbranch ' %s ' in cterms \n", indexer->tStructField[i].cbranch);
										cstr += strbuff;

										// check if cterms has a branch '...field='..zfname..'...
										xmlXPathObjectPtr  xpathObj_cterms = NULL;

										xpathObj_cterms = xmlXPathEvalExpression((const xmlChar*)(indexer->tStructField[i].cbranch), indexer->XPathCtx_cterms);
										if(xpathObj_cterms != NULL)
										{
											if(!xpathObj_cterms->nodesetval || xpathObj_cterms->nodesetval->nodeNr == 0)
											{
												// the branch does not exists, create it

												cstr += "|    -> node not found, creating \n";

												xmlNodePtr root = xmlDocGetRootElement(indexer->DocCterms);

												// get nextid
												xmlChar *nextid;
												if( (nextid = xmlGetProp(root, (const xmlChar *)"nextid")) )
												{
													int l = strlen((const char *)nextid);
													if(l > 32)
														l = 32;
													xmlNodePtr te;
													if((te = xmlNewChild(root, NULL, (const xmlChar*)"te", NULL)) != NULL)
													{
														char ibuff[33];

														// prop 'id'
														ibuff[0] = 'C';
														memcpy(ibuff+1, nextid, l+1);
														xmlSetProp(te, (const xmlChar*)"id", (const xmlChar *)ibuff);

														// prop 'field'
														xmlSetProp(te, (const xmlChar*)"field", (const xmlChar *)(indexer->tStructField[i].name));

														// prop 'nextid'
														xmlSetProp(te, (const xmlChar*)"nextid", (const xmlChar *)"0");

														// inc nextid
														sprintf(ibuff, "%d", atoi((const char *)nextid) + 1);
														xmlSetProp(root, (const xmlChar*)"nextid", (const xmlChar *)ibuff );

														// put a xpathcontext into the field
														indexer->tStructField[i].xmlNodeCterms = te;
														indexer->tStructField[i].XPathCtxCterms = xmlXPathNewContext((xmlDocPtr)te);
													}
													xmlFree(nextid);

													time(&cterms_moddate);
												}
											}
											else
											{
												xmlNodeSetPtr nodes_cterms = xpathObj_cterms->nodesetval;

												snprintf(strbuff, 1000, "|    -> found %d node%s (keeping the first) \n", nodes_cterms->nodeNr, (nodes_cterms->nodeNr==1 ? "s":""));
												cstr += strbuff;

												// in the field, keep the first xpathcontext
												indexer->tStructField[i].xmlNodeCterms = nodes_cterms->nodeTab[0];
												indexer->tStructField[i].XPathCtxCterms = xmlXPathNewContext((xmlDocPtr)(nodes_cterms->nodeTab[0]));
											}

											xmlXPathFreeObject(xpathObj_cterms);
										}
									}
								}
								else
								{
									//  'tbranch' is white
									cstr += "'\n";
									indexer->tStructField[i].set("/record/description/", (const char *)(node_struct->name), NULL);
								}
							}
							else
							{
								// no 'tbranch' attribute
								cstr += "'\n";
								indexer->tStructField[i].set("/record/description/", (const char *)(node_struct->name), NULL);
							}
						} // FIN : boucle sur les nodes du result sur struc

						cstr += "\\-------------------------------- structure loaded  ------\n";

					} // FIN : if(xpathObj_struct->nodesetval)

					xmlXPathFreeObject(xpathObj_struct);

				} // FIN : if(xpathObj_struct != NULL)

				xmlXPathFreeContext(xpathCtx_struct);

			} // FIN : if(xpathCtx_struct != NULL)
		}
	}

	zSyslog._log(CSyslog::LOGL_INFO, CSyslog::LOGC_STRUCTURE, (TCHAR *)(cstr.c_str()) );

	cstr.clear();

	// ------------------ end loading structure
	indexer->current_struct_moddate = struct_moddate;
	indexer->current_thesaurus_moddate = thesaurus_moddate;
	indexer->current_cterms_moddate = cterms_moddate;
}
Example #2
0
void loadThesaurus(CIndexer *indexer)
{
  CConnbas_dbox *connbas = indexer->connbas;
  time_t struct_moddate, thesaurus_moddate, cterms_moddate;

  // ----------------------- load structure and thesaurus
  char *xmlstruct;
  char **pxmlstruct = NULL;
  unsigned long  xmlstruct_length;

  char *xmlthesaurus;
  char **pxmlthesaurus = NULL;
  unsigned long  xmlthesaurus_length;

  char *xmlcterms;
  char **pxmlcterms = NULL;
  unsigned long  xmlcterms_length;

  bool struct_changed, thesaurus_changed, cterms_changed;

  extern int debug_flag;
//printf("loadThesaurus ? \n");
	// read the 3 moddates
	connbas->selectPref_moddates(&struct_moddate, &thesaurus_moddate, &cterms_moddate);

	// what has changed
	struct_changed    = indexer->firstLoad || (struct_moddate    > indexer->current_struct_moddate);
	thesaurus_changed = indexer->firstLoad || (thesaurus_moddate > indexer->current_thesaurus_moddate);
	cterms_changed    = indexer->firstLoad || (cterms_moddate    > indexer->current_cterms_moddate);

	indexer->firstLoad = false;	

	if(!struct_changed && !thesaurus_changed && !cterms_changed)
	{
		// nothing changed in the prefs
		return;
	}

	if(struct_changed)
	{
		// the structure changed : reload
		pxmlstruct    = &xmlstruct;
	}
	if(thesaurus_changed)
	{
		// the thesaurus changed
		pxmlthesaurus = &xmlthesaurus;
	}
	if(cterms_changed)
	{
		// the cterms changed
		pxmlcterms    = &xmlcterms;
	}

	// read useful fields
	if(connbas->selectPrefs(pxmlstruct, &xmlstruct_length, pxmlthesaurus, &xmlthesaurus_length, pxmlcterms, &xmlcterms_length) != 0)
	{
		// erreur sql
		return;
	}


	// ============================ load thesaurus
	if(thesaurus_changed)
	{
		if(indexer->DocThesaurus)
		{
			xmlFreeDoc(indexer->DocThesaurus);
			indexer->DocThesaurus = NULL;
		}

		if(indexer->XPathCtx_thesaurus)
		{
			xmlXPathFreeContext(indexer->XPathCtx_thesaurus);
			indexer->XPathCtx_thesaurus = NULL;
		}

		// we have the thesaurus, load in libxml
		indexer->DocThesaurus = xmlParseMemory(xmlthesaurus, xmlthesaurus_length);
		if(indexer->DocThesaurus != NULL)
		{
			// Create xpath evaluation context 
			indexer->XPathCtx_thesaurus = xmlXPathNewContext(indexer->DocThesaurus);
			if(indexer->XPathCtx_thesaurus != NULL)
			{
			}
		}
		zSyslog.log(CSyslog::LOGL_INFO, CSyslog::LOGC_THESAURUS, "#%ld : thesaurus loaded", connbas->sbas_id);
	}


	// ============================ load cterms
	if(cterms_changed)
	{
		if(indexer->DocCterms)
		{
			xmlFreeDoc(indexer->DocCterms);
			indexer->DocCterms = NULL;
		}

		if(indexer->XPathCtx_cterms)
		{
			xmlXPathFreeContext(indexer->XPathCtx_cterms);
			indexer->XPathCtx_cterms = NULL;
		}

		if(indexer->XPathCtx_deleted)
		{
			xmlXPathFreeContext(indexer->XPathCtx_deleted);
			indexer->XPathCtx_deleted = NULL;
		}
		indexer->xmlNodePtr_deleted = NULL;

		// we have the cterms, load in libxml
		indexer->DocCterms = xmlParseMemory(xmlcterms, xmlcterms_length);
		if(indexer->DocCterms != NULL)
		{
			// Create xpath evaluation context 
			indexer->XPathCtx_cterms = xmlXPathNewContext(indexer->DocCterms);
			if(indexer->XPathCtx_cterms != NULL)
			{

				xmlXPathObjectPtr  xpathObj_cterms = NULL; 

//				zSyslog.log(CSyslog::LOG_DEBUG, "|    searching tbranch ' /cterms/te[@delbranch='1'] ' in cterms");
				xpathObj_cterms = xmlXPathEvalExpression((const xmlChar*)("/cterms/te[@delbranch='1']"), indexer->XPathCtx_cterms);
				if(xpathObj_cterms)
				{
					if(xpathObj_cterms->nodesetval)
					{
						xmlNodeSetPtr nodes_cterms = xpathObj_cterms->nodesetval;

						if(nodes_cterms->nodeNr > 0)
						{
//							zSyslog.log(CSyslog::LOG_DEBUG, "|    -> found %d nodes (keeping the first)", nodes_cterms->nodeNr);
							xmlNodePtr node_cterms = nodes_cterms->nodeTab[0];
							indexer->XPathCtx_deleted = xmlXPathNewContext((xmlDocPtr)node_cterms);

							// in the indexer, we keep the node to the deleted
							indexer->xmlNodePtr_deleted = nodes_cterms->nodeTab[0];
						}
						else
						{
//							zSyslog.log(CSyslog::LOG_DEBUG, "|    -> found 0 node");
						}
					}
					xmlXPathFreeObject(xpathObj_cterms);
				}

			}
		}
		indexer->ctermsChanged = false;
		zSyslog.log(CSyslog::LOGL_DEBUG, CSyslog::LOGC_THESAURUS, "#%ld : cterms loaded", connbas->sbas_id);
	}


// printf(" 0 ------------------------\n");
	// ============================ load structure
	if(struct_changed)
	{
// printf(" 1 ------------------------\n");
		xmlDocPtr          doc_struct;
		xmlXPathContextPtr xpathCtx_struct; 
		xmlXPathObjectPtr  xpathObj_struct;

		if(indexer->tStructField)
		{
			delete [] (indexer->tStructField);
			indexer->tStructField = NULL;
		}

		// load in libxml
		doc_struct = xmlParseMemory(xmlstruct, xmlstruct_length);
		if(doc_struct != NULL)
		{
// printf(" 2 ------------------------\n");
			// Create xpath evaluation context
			xpathCtx_struct = xmlXPathNewContext(doc_struct);
			if(xpathCtx_struct != NULL)
			{
// printf(" 3 ------------------------\n");
				// ----- search every fields of the structure
				// Evaluate xpath expression 
				xpathObj_struct = xmlXPathEvalExpression((const xmlChar*)"/record/description/*", xpathCtx_struct);
				if(xpathObj_struct != NULL)
				{
// printf(" 4 ------------------------\n");
					if(xpathObj_struct->nodesetval)
					{
// printf(" 5 ------------------------\n");
						xmlNodeSetPtr nodes_struct = xpathObj_struct->nodesetval;

						indexer->nStructFields = nodes_struct->nodeNr;
						if(indexer->nStructFields > 0)
						{
							// allocate a TABLE of fields
							indexer->tStructField = new CStructField[indexer->nStructFields];
						}

						// ---- scan every nodes of the result on struct
						if(debug_flag)
							printf("/-------------------------------- Loading structure  -----\n");

						for(int i=0; i<indexer->nStructFields; i++)
						{
							xmlNodePtr node_struct = nodes_struct->nodeTab[i];

							if(debug_flag)
								printf("|  Field '%s' ", node_struct->name);

							// ---- get attribute 'type' if it exists
							xmlChar *type;
							if( (type = xmlGetProp(node_struct, (const xmlChar *)"type")) )
							{
								if(strcmp((const char *)type, "text")==0)
									indexer->tStructField[i].type = CStructField::TYPE_TEXT;	// <... type="text"

								else if(strcmp((const char *)type, "number")==0)
									indexer->tStructField[i].type = CStructField::TYPE_INT;		// <... type="number"

								else if(strcmp((const char *)type, "float")==0)
									indexer->tStructField[i].type = CStructField::TYPE_FLOAT;	// <... type="float"

								else if(strcmp((const char *)type, "date")==0)
									indexer->tStructField[i].type = CStructField::TYPE_DATE;	// <... type="date"

								if(debug_flag)
									printf(" { type='%s' (%d) }", type, indexer->tStructField[i].type );

								xmlFree(type);
							}

							// ---- get attribute 'escape' if it exists
							xmlChar *escape;
							if( (escape = xmlGetProp(node_struct, (const xmlChar *)"escape")) )
							{
								if(debug_flag)
									printf(" { escape='%s' }", escape );

								xmlFree(escape);
							}

							// ---- get attribute 'index' if it exists
							xmlChar *index;
							if( (index = xmlGetProp(node_struct, (const xmlChar *)"index")) )
							{
								if( isno((const char *)index) )
									indexer->tStructField[i].index = false;

								if(debug_flag)
									printf(" { index=%d }", indexer->tStructField[i].index );

								xmlFree(index);
							}

							if(debug_flag)
								putchar('\n');

							// ---- get attribute 'tbranch' if it exists
							xmlChar *tbranch;
							if( (tbranch = xmlGetProp(node_struct, (const xmlChar *)"tbranch")) )
							{
								// --- copy the full path into the field
								indexer->tStructField[i].set("/record/description/", (const char *)(node_struct->name), (const char *)tbranch);
								xmlFree(tbranch);
							}
							else
							{
								// no 'tbranch' attribute
								indexer->tStructField[i].set("/record/description/", (const char *)(node_struct->name), NULL);
							}

							// ---- get attribute 'candidates' if it exists
							xmlChar *candidates;
							if( (candidates = xmlGetProp(node_struct, (const xmlChar *)"candidates")) )
							{
								indexer->tStructField[i].candidatesStrings
									= indexer->tStructField[i].candidatesDates
									= indexer->tStructField[i].candidatesIntegers
									= indexer->tStructField[i].candidatesFirstDigit
									= indexer->tStructField[i].candidatesMultiDigits
									= false;
								for(char *p=(char*)candidates; *p; p++)
								{
									switch(*p)
									{
										case 'S':
										case 's':
											indexer->tStructField[i].candidatesStrings = true;
											break;
										case 'D':
										case 'd':
											indexer->tStructField[i].candidatesDates = true;
											break;
										case 'I':
										case 'i':
											indexer->tStructField[i].candidatesIntegers = true;
											break;
										case '0':
											indexer->tStructField[i].candidatesFirstDigit = true;
											break;
										case '9':
											indexer->tStructField[i].candidatesMultiDigits = true;
											break;
									}
								}
								xmlFree(candidates);
							}

							//	if (nodes_struct->nodeTab[i]->type != XML_NAMESPACE_DECL)
							//		nodes_struct->nodeTab[i] = NULL;

						} // FIN : boucle sur les nodes du result sur struc


						if(debug_flag)
							printf("\\-------------------------------- structure loaded  ------\n");


					} // FIN : if(xpathObj_struct->nodesetval)

					xmlXPathFreeObject(xpathObj_struct);

				} // FIN : if(xpathObj_struct != NULL)

				xmlXPathFreeContext(xpathCtx_struct); 

			} // FIN : if(xpathCtx_struct != NULL)
		}
	}


	// search branches pointed by the tbranch into the thesaurus

	if(debug_flag)
		printf("/-------------------------------- Linking fields to thesaurus  ----- \n");

	for(int i=0; i<indexer->nStructFields; i++)
	{
		if(indexer->tStructField[i].tbranch)
		{
			// this field has a tbranch, it's linked to the thesaurus
			if(debug_flag)
					printf("|  Field '%s'\n", indexer->tStructField[i].name);

			if(indexer->XPathCtx_thesaurus != NULL)
			{
				// build links to the thesaurus
				if(debug_flag)
					printf("|    searching tbranch ' %s ' in thesaurus \n", indexer->tStructField[i].tbranch);
									
				xmlXPathObjectPtr  xpathObj_thesaurus = NULL; 
				xpathObj_thesaurus = xmlXPathEvalExpression((const xmlChar*)(indexer->tStructField[i].tbranch), indexer->XPathCtx_thesaurus);
				if(xpathObj_thesaurus != NULL)
				{
					if(xpathObj_thesaurus->nodesetval)
					{
						xmlNodeSetPtr nodes_thesaurus = xpathObj_thesaurus->nodesetval;

						if(debug_flag)
							printf("|    -> found %d nodes \n", nodes_thesaurus->nodeNr);

						if(nodes_thesaurus->nodeNr > 0)
						{
							// in this field, allocate an array of xpathcontext
							indexer->tStructField[i].tXPathCtxThesaurus = new xmlXPathContextPtr[nodes_thesaurus->nodeNr];
							// in this field, allocate an array of nodes
							indexer->tStructField[i].tNodesThesaurus = new xmlNodePtr[nodes_thesaurus->nodeNr];

							if(indexer->tStructField[i].tXPathCtxThesaurus && indexer->tStructField[i].tNodesThesaurus)
							{
								indexer->tStructField[i].nXPathCtxThesaurus = nodes_thesaurus->nodeNr;
								indexer->tStructField[i].nNodesThesaurus    = nodes_thesaurus->nodeNr;
								for(int j=0; j<nodes_thesaurus->nodeNr; j++)
								{
									xmlNodePtr node_thesaurus = nodes_thesaurus->nodeTab[j];
									indexer->tStructField[i].tXPathCtxThesaurus[j] = xmlXPathNewContext((xmlDocPtr)node_thesaurus);
									indexer->tStructField[i].tNodesThesaurus[j]    = node_thesaurus;
								}
							}
						}
					}
					xmlXPathFreeObject(xpathObj_thesaurus);
				}
			}
		}

		if(indexer->tStructField[i].cbranch)
		{
			// this field has a cbranch: it's linked to cterms

			if(indexer->XPathCtx_cterms != NULL)
			{
				// build a link to cterms
				if(debug_flag)
					printf("|    searching cbranch ' %s ' in cterms \n", indexer->tStructField[i].cbranch);
									
				
				// check if cterms has a branch '...field='..zfname..'...
				xmlXPathObjectPtr  xpathObj_cterms = NULL; 

				xpathObj_cterms = xmlXPathEvalExpression((const xmlChar*)(indexer->tStructField[i].cbranch), indexer->XPathCtx_cterms);
				if(xpathObj_cterms != NULL)
				{
					if(!xpathObj_cterms->nodesetval || xpathObj_cterms->nodesetval->nodeNr == 0)
					{
						// the branch does not exists, create it

						if(debug_flag)
							printf("|    -> nodes not found, creating \n");

						xmlNodePtr root = xmlDocGetRootElement(indexer->DocCterms);

						// get nextid
						xmlChar *nextid;
						if( (nextid = xmlGetProp(root, (const xmlChar *)"nextid")) )
						{
							int l = strlen((const char *)nextid);
							if(l > 32)
								l = 32;
							xmlNodePtr te;
							if((te = xmlNewChild(root, NULL, (const xmlChar*)"te", NULL)) != NULL)
							{
								char ibuff[33];

								// prop 'id'
								ibuff[0] = 'C';
								memcpy(ibuff+1, nextid, l+1);
								xmlSetProp(te, (const xmlChar*)"id", (const xmlChar *)ibuff);

								// prop 'field'
								xmlSetProp(te, (const xmlChar*)"field", (const xmlChar *)(indexer->tStructField[i].name));

								// prop 'nextid'
								xmlSetProp(te, (const xmlChar*)"nextid", (const xmlChar *)"0");

								// inc nextid
								sprintf(ibuff, "%d", atoi((const char *)nextid) + 1);
								xmlSetProp(root, (const xmlChar*)"nextid", (const xmlChar *)ibuff );

								// put a xpathcontext into the field
								indexer->tStructField[i].xmlNodeCterms = te;
								indexer->tStructField[i].XPathCtxCterms = xmlXPathNewContext((xmlDocPtr)te);
							}
							xmlFree(nextid);

							time(&cterms_moddate);
						}
					}
					else
					{
						xmlNodeSetPtr nodes_cterms = xpathObj_cterms->nodesetval;

						if(debug_flag)
							printf("|    -> found %d nodes (keeping the first) \n", nodes_cterms->nodeNr);

						// in the field, keep the first xpathcontext
						indexer->tStructField[i].xmlNodeCterms = nodes_cterms->nodeTab[0];
						indexer->tStructField[i].XPathCtxCterms = xmlXPathNewContext((xmlDocPtr)(nodes_cterms->nodeTab[0]));
					}

					xmlXPathFreeObject(xpathObj_cterms);
				}
			}
		}
	}
	if(debug_flag)
		printf("\\-------------------------------- fields linked to thesaurus  ------ \n");



	// ------------------ end loading structure
	indexer->current_struct_moddate = struct_moddate;
	indexer->current_thesaurus_moddate = thesaurus_moddate;
	indexer->current_cterms_moddate = cterms_moddate;
}