sqldbindexdb.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2012 by the Free Pascal development team
  4. SQLDB-based index database
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit SQLDBIndexDB;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. SysUtils, fpIndexer, sqldb, db;
  16. // SQLDB Specific, cache query objects
  17. type
  18. TCachedQueryType = (cqtInsertWord, cqtGetWordID, cqtInsertFile, cqtGetFileID,
  19. cqtInsertLanguage, cqtGetLanguageID, cqtInsertMatch);
  20. Const
  21. DefaultGeneratorNames: array[TIndexTable] of string = ('GEN_WORDS','GEN_LANGUAGES', 'GEN_FILES', 'GEN_MATCHES');
  22. type
  23. { TSQLDBIndexDB }
  24. TSQLDBIndexDB = class(TSQLIndexDB)
  25. private
  26. // SQLDB specific
  27. FDB: TSQLConnection;
  28. FLastURLID: int64;
  29. FLastURL: string;
  30. FLastLanguageID: int64;
  31. FLastLanguage: string;
  32. FLastWordID: int64;
  33. FLastWord: string;
  34. FProps : Array [0..3] of UTF8String;
  35. FQueries: array [TCachedQueryType] of TSQLQuery;
  36. function GetS(AIndex: integer): UTF8String;
  37. procedure SetS(AIndex: integer; const AValue: UTF8String);
  38. Procedure EnsureDB;
  39. protected
  40. // SQLDB Specific statements
  41. procedure Execute(const sql: UTF8string; ignoreErrors: boolean = True); override;
  42. function GetLanguageID(const ALanguage: UTF8string): int64;
  43. function GetWordID(const AWord: UTF8String): int64;
  44. function GetURLID(const URL: UTF8String; ATimeStamp: TDateTime; ALanguageID: int64; DoCreate: boolean = True): int64; override;
  45. function CreateQuery(const ASQL: UTF8String): TSQLQuery;
  46. function CreateCachedQuery(QueryType: TCachedQueryType; const ASQL: UTF8String): TSQLQuery;
  47. // Connection specific, need to be overridden
  48. function CreateConnection: TSQLConnection; virtual; abstract;
  49. procedure InsertMatch(AWordID, aFileID, aLanguageID: int64; const ASearchData: TSearchWordData); virtual; abstract;
  50. function InsertWord(const AWord: UTF8String): int64; virtual; abstract;
  51. function InsertURL(const URL: UTF8String; ATimeStamp: TDateTime; ALanguageID: int64): int64; virtual; abstract;
  52. function InsertLanguage(const ALanguage: UTF8String): int64; virtual; abstract;
  53. public
  54. destructor Destroy; override;
  55. procedure Connect; override;
  56. procedure Disconnect; override;
  57. procedure CreateDB; override;
  58. procedure BeginTrans; override;
  59. procedure CommitTrans; override;
  60. procedure CompactDB; override;
  61. procedure AddSearchData(ASearchData: TSearchWordData); override;
  62. procedure FindSearchData(SearchWord: TWordParser; FPSearch: TFPSearch; SearchOptions: TSearchOptions); override;
  63. function GetAvailableWords(out aList : TUTF8StringArray; aContaining : UTF8String; Partial : TAvailableMatch) : integer;override;
  64. procedure DeleteWordsFromFile(URL: UTF8String); override;
  65. Property NativeConnection : TSQLConnection Read FDB;
  66. published
  67. property DatabasePath: UTF8String Index 0 read GetS write SetS;
  68. property UserName: UTF8String Index 1 read GetS write SetS;
  69. property Password: UTF8String Index 2 read GetS write SetS;
  70. property HostName : UTF8String Index 3 read GetS write SetS;
  71. end;
  72. implementation
  73. { TSQLDBIndexDB }
  74. function TSQLDBIndexDB.GetS(AIndex: integer): UTF8String;
  75. begin
  76. Result:=FProps[aIndex];
  77. end;
  78. procedure TSQLDBIndexDB.SetS(AIndex: integer; const AValue: UTF8String);
  79. begin
  80. FProps[aIndex]:=aValue;
  81. if Assigned(FDB) then
  82. case Aindex of
  83. 0: FDB.DatabaseName := AValue;
  84. 1: FDB.UserName := AValue;
  85. 2: FDB.Password := AValue;
  86. 3: FDB.HostName := AValue;
  87. end;
  88. end;
  89. procedure TSQLDBIndexDB.EnsureDB;
  90. begin
  91. if FDB=Nil then
  92. begin
  93. FDB:=CreateConnection;
  94. FDB.UserName:=UserName;
  95. FDB.Password:=Password;
  96. FDB.HostName:=HostName;
  97. FDB.DatabaseName:=DatabasePath;
  98. end;
  99. if FDB.Transaction = nil then
  100. FDB.Transaction := TSQLTransaction.Create(FDB);
  101. FDB.LogEvents:=LogAllEventsExtra;
  102. end;
  103. function TSQLDBIndexDB.GetLanguageID(const ALanguage: UTF8String): int64;
  104. var
  105. Q: TSQLQuery;
  106. begin
  107. if SameFileName(FLastLanguage, ALanguage) then
  108. Result := FLastLanguageID
  109. else
  110. begin
  111. Q := CreateCachedQuery(cqtGetLanguageID, GetLanguageSQL);
  112. Q.ParamByName(GetFieldName(ifLanguagesName)).AsString := ALanguage;
  113. Q.Open;
  114. try
  115. if (Q.EOF and Q.BOF) then
  116. Result := InsertLanguage(ALanguage)
  117. else
  118. Result := Q.FieldByName(GetFieldName(ifLanguagesID)).AsLargeInt;
  119. FLastLanguageID := Result;
  120. FLastLanguage := ALanguage;
  121. finally
  122. Q.Close;
  123. end;
  124. end;
  125. end;
  126. function TSQLDBIndexDB.GetWordID(const AWord: UTF8String): int64;
  127. var
  128. Q: TSQLQuery;
  129. begin
  130. if (FLastWord = AWord) then
  131. Result := FLastWordID
  132. else
  133. begin
  134. Q := CreateCachedQuery(cqtGetWordID, GetWordSQL);
  135. Q.ParamByName(GetFieldName(ifWordsWord)).AsString := AWord;
  136. Q.Open;
  137. try
  138. if (Q.EOF and Q.BOF) then
  139. Result := InsertWord(AWord)
  140. else
  141. Result := Q.FieldByName(GetFieldName(ifWordsID)).AsLargeInt;
  142. FLastWordID := Result;
  143. FLastWord := AWord;
  144. finally
  145. Q.Close;
  146. end;
  147. end;
  148. end;
  149. function TSQLDBIndexDB.CreateQuery(const ASQL: UTF8String): TSQLQuery;
  150. begin
  151. Result := TSQLQuery.Create(Self);
  152. Result.Database := Self.FDB;
  153. Result.Transaction := Self.FDB.Transaction;
  154. Result.SQL.Text := ASQL;
  155. Result.UsePrimaryKeyAsKey:=False;
  156. // Result.UniDirectional:=True;
  157. //Writeln('SQL :',ASQL);
  158. end;
  159. function TSQLDBIndexDB.GetURLID(const URL: UTF8String; ATimeStamp: TDateTime; ALanguageID: int64; DoCreate: boolean = True): int64;
  160. var
  161. Q: TSQLQuery;
  162. begin
  163. if SameFileName(FLastURL, URL) then
  164. Result := FLastURLID
  165. else
  166. begin
  167. Q := CreateCachedQuery(cqtGetFileID, GetSearchFileSQL);
  168. Q.ParamByName(GetFieldName(ifFilesURL)).AsString := URL;
  169. Q.Open;
  170. try
  171. if (Q.EOF and Q.BOF) then
  172. begin
  173. if DoCreate then
  174. Result := InsertURL(URL, ATimeStamp, ALanguageID)
  175. else
  176. Result := -1;
  177. end
  178. else
  179. Result := Q.FieldByName(GetFieldName(ifFilesID)).AsLargeInt;
  180. FLastURLID := Result;
  181. FLastURL := URL;
  182. finally
  183. Q.Close;
  184. end;
  185. end;
  186. end;
  187. function TSQLDBIndexDB.CreateCachedQuery(QueryType: TCachedQueryType; const ASQL: UTF8String): TSQLQuery;
  188. begin
  189. if FQueries[QueryType] = nil then
  190. begin
  191. FQueries[QueryType] := CreateQuery(ASQL);
  192. FQueries[QueryType].Prepare;
  193. end;
  194. Result := FQueries[QueryType];
  195. end;
  196. procedure TSQLDBIndexDB.AddSearchData(ASearchData: TSearchWordData);
  197. var
  198. WID, FID, LID: int64;
  199. begin
  200. //check if the SearchWord already is in the list
  201. LID := GetLanguageID(ASearchData.Language);
  202. FID := GetURLID(ASearchData.URL, ASearchData.FileDate, LID);
  203. WID := GetWordID(ASearchData.SearchWord);
  204. InsertMatch(WID, FID, LID, ASearchData);
  205. end;
  206. procedure TSQLDBIndexDB.FindSearchData(SearchWord: TWordParser; FPSearch: TFPSearch; SearchOptions: TSearchOptions);
  207. var
  208. Q: TSQLQuery;
  209. FN, FP, FD, FW, FC: TField;
  210. Res: TSearchWordData;
  211. S,WW : UTF8String;
  212. I,L : Integer;
  213. begin
  214. Q := CreateQuery(GetMatchSQL(SearchOptions,SearchWord,True));
  215. try
  216. WW := getFieldName(ifWordsWord);
  217. for i := 0 to SearchWord.Count - 1 do
  218. If SearchWord.Token[i].TokenType=wtWord then
  219. begin
  220. S:=SearchWord.Token[i].Value;
  221. if (Length(S)>0) and (S[1]='''') then
  222. Delete(S,1,1);
  223. L:=Length(S);
  224. if (l>0) and (S[l]='''') then
  225. Delete(S,l,1);
  226. if (soContains in Searchoptions) then
  227. S:='%'+S+'%';
  228. Q.ParamByName(WW+IntToStr(i)).AsString:=S;
  229. end;
  230. Q.Open;
  231. FN := Q.FieldByName(GetFieldName(ifFilesURL));
  232. FD := Q.FieldByName(GetFieldName(ifFilesTimeStamp));
  233. FC := Q.FieldByName(GetFieldName(ifMatchesContext));
  234. FP := Q.FieldByName(GetFieldName(ifMatchesPosition));
  235. FW := Q.FieldByName(GetFieldName(ifWordsWord));
  236. I:=0;
  237. while not Q.EOF do
  238. begin
  239. Res.FileDate := FD.AsDateTime;
  240. Res.URL := FN.AsString;
  241. Res.SearchWord := FW.AsString;
  242. Res.Position := FP.AsInteger;
  243. Res.Context:=FC.aSString;
  244. Res.Rank:=0;
  245. FPSearch.AddResult(i, Res);
  246. Inc(I);
  247. Q.Next;
  248. end;
  249. finally
  250. Q.Free;
  251. end;
  252. end;
  253. Function TSQLDBIndexDB.GetAvailableWords(out aList : TUTF8StringArray; aContaining: UTF8String; Partial: TAvailableMatch) : Integer;
  254. Var
  255. Q : TSQLQuery;
  256. begin
  257. Result:=0;
  258. Q := CreateQuery(AvailableWordsSQL(aContaining,Partial));
  259. try
  260. Q.PacketRecords:=-1;
  261. if (aContaining<>'') or (Partial<>amall) then
  262. With Q.ParamByName(SearchTermParam) do
  263. case Partial of
  264. amExact : AsString:=aContaining;
  265. amContains : AsString:='%'+aContaining+'%';
  266. amStartsWith : AsString:=aContaining+'%';
  267. end;
  268. Q.Open;
  269. SetLength(aList,Q.RecordCount);
  270. Q.First;
  271. While not Q.EOF do
  272. begin
  273. If Length(aList)<=Result then
  274. SetLength(aList,Result+100);
  275. aList[Result]:=Q.Fields[0].AsUTF8String;
  276. Inc(Result);
  277. Q.Next;
  278. end;
  279. SetLength(aList,Result);
  280. finally
  281. Q.Free;
  282. end;
  283. end;
  284. procedure TSQLDBIndexDB.DeleteWordsFromFile(URL: UTF8String);
  285. begin
  286. inherited DeleteWordsFromFile(URL);
  287. FLastURL := '';
  288. end;
  289. procedure TSQLDBIndexDB.Execute(const sql: UTF8String; ignoreErrors: boolean = True);
  290. begin
  291. if SQL = '' then
  292. exit;
  293. try
  294. FDB.ExecuteDirect(sql);
  295. except
  296. on E : exception do
  297. if not IgnoreErrors then
  298. raise
  299. else
  300. // Writeln(E.ClassName,' : ',E.Message);
  301. end;
  302. end;
  303. procedure TSQLDBIndexDB.Connect;
  304. begin
  305. EnsureDB;
  306. FDB.Connected := True;
  307. end;
  308. procedure TSQLDBIndexDB.Disconnect;
  309. Var
  310. T : TCachedQueryType;
  311. begin
  312. For T:=Low(TCachedQueryType) to High(TCachedQueryType) do
  313. FreeAndNil(FQueries[T]);
  314. FreeAndNil(FDB);
  315. end;
  316. procedure TSQLDBIndexDB.CreateDB;
  317. begin
  318. EnsureDB;
  319. FDB.CreateDB;
  320. Connect;
  321. CreateIndexerTables;
  322. end;
  323. destructor TSQLDBIndexDB.Destroy;
  324. begin
  325. Disconnect;
  326. inherited Destroy;
  327. end;
  328. procedure TSQLDBIndexDB.BeginTrans;
  329. begin
  330. FDB.Transaction.StartTransaction;
  331. end;
  332. procedure TSQLDBIndexDB.CommitTrans;
  333. begin
  334. FDB.Transaction.Commit;
  335. end;
  336. procedure TSQLDBIndexDB.CompactDB;
  337. begin
  338. //not yet implemented
  339. end;
  340. end.