sqldbindexdb.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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 aWord='Just' then
  131. Writeln('here');
  132. if (FLastWord = AWord) then
  133. Result := FLastWordID
  134. else
  135. begin
  136. Q := CreateCachedQuery(cqtGetWordID, GetWordSQL);
  137. Q.ParamByName(GetFieldName(ifWordsWord)).AsString := AWord;
  138. Q.Open;
  139. try
  140. if (Q.EOF and Q.BOF) then
  141. Result := InsertWord(AWord)
  142. else
  143. Result := Q.FieldByName(GetFieldName(ifWordsID)).AsLargeInt;
  144. FLastWordID := Result;
  145. FLastWord := AWord;
  146. finally
  147. Q.Close;
  148. end;
  149. end;
  150. end;
  151. function TSQLDBIndexDB.CreateQuery(const ASQL: UTF8String): TSQLQuery;
  152. begin
  153. Result := TSQLQuery.Create(Self);
  154. Result.Database := Self.FDB;
  155. Result.Transaction := Self.FDB.Transaction;
  156. Result.SQL.Text := ASQL;
  157. Result.UsePrimaryKeyAsKey:=False;
  158. // Result.UniDirectional:=True;
  159. //Writeln('SQL :',ASQL);
  160. end;
  161. function TSQLDBIndexDB.GetURLID(const URL: UTF8String; ATimeStamp: TDateTime; ALanguageID: int64; DoCreate: boolean = True): int64;
  162. var
  163. Q: TSQLQuery;
  164. begin
  165. if SameFileName(FLastURL, URL) then
  166. Result := FLastURLID
  167. else
  168. begin
  169. Q := CreateCachedQuery(cqtGetFileID, GetSearchFileSQL);
  170. Q.ParamByName(GetFieldName(ifFilesURL)).AsString := URL;
  171. Q.Open;
  172. try
  173. if (Q.EOF and Q.BOF) then
  174. begin
  175. if DoCreate then
  176. Result := InsertURL(URL, ATimeStamp, ALanguageID)
  177. else
  178. Result := -1;
  179. end
  180. else
  181. Result := Q.FieldByName(GetFieldName(ifFilesID)).AsLargeInt;
  182. FLastURLID := Result;
  183. FLastURL := URL;
  184. finally
  185. Q.Close;
  186. end;
  187. end;
  188. end;
  189. function TSQLDBIndexDB.CreateCachedQuery(QueryType: TCachedQueryType; const ASQL: UTF8String): TSQLQuery;
  190. begin
  191. if FQueries[QueryType] = nil then
  192. begin
  193. FQueries[QueryType] := CreateQuery(ASQL);
  194. FQueries[QueryType].Prepare;
  195. end;
  196. Result := FQueries[QueryType];
  197. end;
  198. procedure TSQLDBIndexDB.AddSearchData(ASearchData: TSearchWordData);
  199. var
  200. WID, FID, LID: int64;
  201. begin
  202. //check if the SearchWord already is in the list
  203. LID := GetLanguageID(ASearchData.Language);
  204. FID := GetURLID(ASearchData.URL, ASearchData.FileDate, LID);
  205. WID := GetWordID(ASearchData.SearchWord);
  206. InsertMatch(WID, FID, LID, ASearchData);
  207. end;
  208. procedure TSQLDBIndexDB.FindSearchData(SearchWord: TWordParser; FPSearch: TFPSearch; SearchOptions: TSearchOptions);
  209. var
  210. Q: TSQLQuery;
  211. FN, FP, FD, FW, FC: TField;
  212. Res: TSearchWordData;
  213. S,WW : UTF8String;
  214. I,L : Integer;
  215. begin
  216. Q := CreateQuery(GetMatchSQL(SearchOptions,SearchWord,True));
  217. try
  218. WW := getFieldName(ifWordsWord);
  219. for i := 0 to SearchWord.Count - 1 do
  220. If SearchWord.Token[i].TokenType=wtWord then
  221. begin
  222. S:=SearchWord.Token[i].Value;
  223. if (Length(S)>0) and (S[1]='''') then
  224. Delete(S,1,1);
  225. L:=Length(S);
  226. if (l>0) and (S[l]='''') then
  227. Delete(S,l,1);
  228. if (soContains in Searchoptions) then
  229. S:='%'+S+'%';
  230. Q.ParamByName(WW+IntToStr(i)).AsString:=S;
  231. end;
  232. Q.Open;
  233. FN := Q.FieldByName(GetFieldName(ifFilesURL));
  234. FD := Q.FieldByName(GetFieldName(ifFilesTimeStamp));
  235. FC := Q.FieldByName(GetFieldName(ifMatchesContext));
  236. FP := Q.FieldByName(GetFieldName(ifMatchesPosition));
  237. FW := Q.FieldByName(GetFieldName(ifWordsWord));
  238. I:=0;
  239. while not Q.EOF do
  240. begin
  241. Res.FileDate := FD.AsDateTime;
  242. Res.URL := FN.AsString;
  243. Res.SearchWord := FW.AsString;
  244. Res.Position := FP.AsInteger;
  245. Res.Context:=FC.aSString;
  246. Res.Rank:=0;
  247. FPSearch.AddResult(i, Res);
  248. Inc(I);
  249. Q.Next;
  250. end;
  251. finally
  252. Q.Free;
  253. end;
  254. end;
  255. Function TSQLDBIndexDB.GetAvailableWords(out aList : TUTF8StringArray; aContaining: UTF8String; Partial: TAvailableMatch) : Integer;
  256. Var
  257. Q : TSQLQuery;
  258. begin
  259. Result:=0;
  260. Q := CreateQuery(AvailableWordsSQL(aContaining,Partial));
  261. try
  262. Q.PacketRecords:=-1;
  263. if (aContaining<>'') or (Partial<>amall) then
  264. With Q.ParamByName(SearchTermParam) do
  265. case Partial of
  266. amExact : AsString:=aContaining;
  267. amContains : AsString:='%'+aContaining+'%';
  268. amStartsWith : AsString:=aContaining+'%';
  269. end;
  270. Q.Open;
  271. SetLength(aList,Q.RecordCount);
  272. Q.First;
  273. While not Q.EOF do
  274. begin
  275. If Length(aList)<=Result then
  276. SetLength(aList,Result+100);
  277. aList[Result]:=Q.Fields[0].AsUTF8String;
  278. Inc(Result);
  279. Q.Next;
  280. end;
  281. SetLength(aList,Result);
  282. finally
  283. Q.Free;
  284. end;
  285. end;
  286. procedure TSQLDBIndexDB.DeleteWordsFromFile(URL: UTF8String);
  287. begin
  288. inherited DeleteWordsFromFile(URL);
  289. FLastURL := '';
  290. end;
  291. procedure TSQLDBIndexDB.Execute(const sql: UTF8String; ignoreErrors: boolean = True);
  292. begin
  293. if SQL = '' then
  294. exit;
  295. try
  296. FDB.ExecuteDirect(sql);
  297. except
  298. on E : exception do
  299. if not IgnoreErrors then
  300. raise
  301. else
  302. // Writeln(E.ClassName,' : ',E.Message);
  303. end;
  304. end;
  305. procedure TSQLDBIndexDB.Connect;
  306. begin
  307. EnsureDB;
  308. FDB.Connected := True;
  309. end;
  310. procedure TSQLDBIndexDB.Disconnect;
  311. Var
  312. T : TCachedQueryType;
  313. begin
  314. For T:=Low(TCachedQueryType) to High(TCachedQueryType) do
  315. FreeAndNil(FQueries[T]);
  316. FreeAndNil(FDB);
  317. end;
  318. procedure TSQLDBIndexDB.CreateDB;
  319. begin
  320. EnsureDB;
  321. FDB.CreateDB;
  322. Connect;
  323. CreateIndexerTables;
  324. end;
  325. destructor TSQLDBIndexDB.Destroy;
  326. begin
  327. Disconnect;
  328. inherited Destroy;
  329. end;
  330. procedure TSQLDBIndexDB.BeginTrans;
  331. begin
  332. FDB.Transaction.StartTransaction;
  333. end;
  334. procedure TSQLDBIndexDB.CommitTrans;
  335. Var
  336. T : TCachedQueryType;
  337. begin
  338. For T:=Low(TCachedQueryType) to High(TCachedQueryType) do
  339. FreeAndNil(FQueries[T]);
  340. FDB.Transaction.Commit;
  341. end;
  342. procedure TSQLDBIndexDB.CompactDB;
  343. begin
  344. //not yet implemented
  345. end;
  346. end.