Quick.Linq.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 Kike Pérez
  3. Unit : Quick.Linq
  4. Description : Arrays and Generic Lists Linq functions
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 04/04/2019
  8. Modified : 01/12/2019
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Linq;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. SysUtils,
  26. Generics.Collections,
  27. Generics.Defaults,
  28. RTTI,
  29. Quick.RTTI.Utils,
  30. Quick.Expression,
  31. Quick.Commons,
  32. Quick.Value,
  33. {$IFDEF FPC}
  34. Quick.Value.RTTI,
  35. {$ENDIF}
  36. Quick.Arrays;
  37. type
  38. TOrderDirection = (odAscending, odDescending);
  39. TLinqExpression<T : class> = class(TExpression)
  40. private
  41. fPredicate : TPredicate<T>;
  42. public
  43. constructor Create(aPredicate : TPredicate<T>);
  44. function Validate(aValue : TObject) : Boolean; override;
  45. end;
  46. ILinqQuery<T> = interface
  47. ['{16B68C0B-EA38-488A-99D9-BAD1E8560E8E}']
  48. function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
  49. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  50. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  51. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  52. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  53. function SelectFirst : T;
  54. function SelectLast : T;
  55. function SelectTop(aLimit : Integer) : TxArray<T>;
  56. function Select : TxArray<T>; overload;
  57. function Select(const aPropertyName : string) : TFlexArray; overload;
  58. function Count : Integer;
  59. function Update(const aFields : array of string; aValues : array of const) : Integer;
  60. function Delete : Integer;
  61. end;
  62. TLinqQuery<T : class> = class(TInterfacedObject,ILinqQuery<T>)
  63. private type
  64. arrayOfT = array of T;
  65. private
  66. fWhereClause : TExpression;
  67. fOrderBy : TArray<string>;
  68. fOrderDirection : TOrderDirection;
  69. //fPList : Pointer;
  70. fList : arrayOfT;
  71. function FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  72. procedure DoOrderBy(vArray : ArrayOfT);
  73. function Compare(const aPropertyName : string; L, R : T) : Integer;
  74. procedure Clear;
  75. public
  76. {$IFNDEF FPC}
  77. constructor Create(aObjectList : TObjectList<T>); overload;
  78. {$ENDIF}
  79. constructor Create(aList : TList<T>); overload;
  80. constructor Create(aXArray : TxArray<T>); overload;
  81. constructor Create(aArray : TArray<T>); overload;
  82. destructor Destroy; override;
  83. function Where(const aWhereClause : string; aWhereParams : array of const) : ILinqQuery<T>; overload;
  84. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  85. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  86. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  87. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  88. function SelectFirst : T;
  89. function SelectLast : T;
  90. function SelectTop(aLimit : Integer) : TxArray<T>;
  91. function Select : TxArray<T>; overload;
  92. function Select(const aPropertyName : string) : TFlexArray; overload;
  93. function Count : Integer;
  94. function Update(const aFields : array of string; aValues : array of const) : Integer;
  95. function Delete : Integer;
  96. end;
  97. TLinq<T : class> = class
  98. public
  99. {$IFNDEF FPC}
  100. class function From(aObjectList : TObjectList<T>) : ILinqQuery<T>; overload;
  101. {$ENDIF}
  102. class function From(aArray : TArray<T>) : ILinqQuery<T>; overload;
  103. class function From(aXArray : TXArray<T>) : ILinqQuery<T>; overload;
  104. end;
  105. ELinqNotValidExpression = class(Exception);
  106. ELinqError = class(Exception);
  107. implementation
  108. { TLinqQuery<T> }
  109. procedure TLinqQuery<T>.Clear;
  110. begin
  111. SetLength(fOrderBy,0);
  112. end;
  113. constructor TLinqQuery<T>.Create(aArray: TArray<T>);
  114. begin
  115. Clear;
  116. fList := aArray;
  117. end;
  118. {$IFNDEF FPC}
  119. constructor TLinqQuery<T>.Create(aObjectList: TObjectList<T>);
  120. begin
  121. Clear;
  122. //Create(aObjectList.List);
  123. //fPList := Pointer(aObjectList.List);
  124. //fList := arrayOfT(fPList);
  125. fList := aObjectList.List;
  126. end;
  127. {$ENDIF}
  128. constructor TLinqQuery<T>.Create(aXArray: TxArray<T>);
  129. begin
  130. Clear;
  131. fList := aXArray;
  132. end;
  133. constructor TLinqQuery<T>.Create(aList: TList<T>);
  134. begin
  135. Clear;
  136. fList := aList.ToArray;
  137. end;
  138. function TLinqQuery<T>.Compare(const aPropertyName: string; L, R: T): Integer;
  139. var
  140. valueL : TValue;
  141. valueR : TValue;
  142. begin
  143. Result := 0;
  144. valueL := TRTTI.GetPathValue(L,aPropertyName);
  145. valueR := TRTTI.GetPathValue(R,aPropertyName);
  146. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(1)
  147. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(-1);
  148. case valueL.Kind of
  149. {$IFNDEF FPC}
  150. tkString,
  151. {$ENDIF}
  152. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  153. tkInteger, tkInt64 :
  154. begin
  155. if valueL.AsInteger > valueR.AsInteger then Result := 1
  156. else if valueL.AsInteger < valueR.AsInteger then Result := -1;
  157. end;
  158. tkFloat :
  159. begin
  160. if valueL.AsExtended > valueR.AsExtended then Result := 1
  161. else if valueL.AsExtended < valueR.AsExtended then Result := -1;
  162. end;
  163. end;
  164. end;
  165. function TLinqQuery<T>.Count: Integer;
  166. var
  167. i : Integer;
  168. begin
  169. Result := 0;
  170. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  171. for i := High(fList) downto Low(fList) do
  172. begin
  173. if fWhereClause.Validate(fList[i]) then
  174. begin
  175. Inc(Result);
  176. end;
  177. end;
  178. end;
  179. function TLinqQuery<T>.Delete: Integer;
  180. var
  181. i : Integer;
  182. begin
  183. Result := 0;
  184. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  185. for i := High(fList) downto Low(fList) do
  186. begin
  187. if fWhereClause.Validate(fList[i]) then
  188. begin
  189. TObject(fList[i]).Free;
  190. //System.Delete(fList,i,1);
  191. Inc(Result);
  192. end;
  193. end;
  194. end;
  195. destructor TLinqQuery<T>.Destroy;
  196. begin
  197. if Assigned(fWhereClause) then fWhereClause.Free;
  198. inherited;
  199. end;
  200. procedure TLinqQuery<T>.DoOrderBy(vArray : ArrayOfT);
  201. begin
  202. if High(fOrderBy) < 0 then Exit;
  203. {$IFNDEF FPC}
  204. TArray.Sort<T>(vArray, TComparer<T>.Construct(
  205. function (const A, B: T): Integer
  206. var
  207. field : string;
  208. begin
  209. for field in fOrderBy do
  210. begin
  211. Result := Compare(field,A,B);
  212. if Result <> 0 then Break;
  213. end;
  214. if fOrderDirection = TOrderDirection.odDescending then Result := Result * -1;
  215. end)
  216. );
  217. {$ENDIF}
  218. end;
  219. function TLinqQuery<T>.OrderBy(const aFieldNames: string): ILinqQuery<T>;
  220. begin
  221. Result := Self;
  222. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  223. fOrderBy := aFieldNames.Split([',']);
  224. fOrderDirection := TOrderDirection.odAscending;
  225. end;
  226. function TLinqQuery<T>.OrderByDescending(const aFieldNames: string): ILinqQuery<T>;
  227. begin
  228. Result := Self;
  229. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  230. fOrderBy := aFieldNames.Split([',']);
  231. fOrderDirection := TOrderDirection.odDescending;
  232. end;
  233. function TLinqQuery<T>.Select(const aPropertyName: string): TFlexArray;
  234. var
  235. obj : T;
  236. value : TFlexValue;
  237. begin
  238. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  239. for obj in fList do
  240. begin
  241. if fWhereClause.Validate(obj) then
  242. begin
  243. //value := TRTTI.GetProperty(obj,aPropertyName);
  244. {$IFNDEF FPC}
  245. value := TRTTI.GetPathValue(obj,aPropertyName).AsVariant;
  246. {$ELSE}
  247. value.AsTValue := TRTTI.GetPathValue(obj,aPropertyName);
  248. {$ENDIF}
  249. Result.Add(value);
  250. end;
  251. end;
  252. end;
  253. function TLinqQuery<T>.Select: TxArray<T>;
  254. var
  255. obj : T;
  256. begin
  257. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  258. for obj in fList do
  259. begin
  260. if fWhereClause.Validate(obj) then Result.Add(obj);
  261. end;
  262. DoOrderBy(Result);
  263. end;
  264. function TLinqQuery<T>.SelectFirst: T;
  265. var
  266. obj : T;
  267. begin
  268. {$IFNDEF FPC}
  269. Result := nil;
  270. {$ENDIF}
  271. DoOrderBy(fList);
  272. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  273. for obj in fList do
  274. begin
  275. if fWhereClause.Validate(obj) then Exit(obj);
  276. end;
  277. end;
  278. function TLinqQuery<T>.SelectLast: T;
  279. var
  280. obj : T;
  281. begin
  282. {$IFNDEF FPC}
  283. Result := nil;
  284. {$ENDIF}
  285. DoOrderBy(fList);
  286. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  287. for obj in fList do
  288. begin
  289. if fWhereClause.Validate(obj) then Result := obj;
  290. end;
  291. end;
  292. function TLinqQuery<T>.SelectTop(aLimit: Integer): TxArray<T>;
  293. var
  294. obj : T;
  295. i : Integer;
  296. begin
  297. DoOrderBy(fList);
  298. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  299. i := 0;
  300. for obj in fList do
  301. begin
  302. if fWhereClause.Validate(obj) then
  303. begin
  304. Result.Add(obj);
  305. Inc(i);
  306. if i > aLimit then Exit;
  307. end;
  308. end;
  309. end;
  310. function TLinqQuery<T>.Update(const aFields: array of string; aValues: array of const): Integer;
  311. var
  312. obj : T;
  313. i : Integer;
  314. {$IFDEF FPC}
  315. value : TValue;
  316. {$ENDIF}
  317. begin
  318. Result := 0;
  319. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  320. for obj in fList do
  321. begin
  322. {$IFNDEF FPC}
  323. if obj = nil then continue;
  324. {$ELSE}
  325. if Pointer(obj) = nil then continue;
  326. {$ENDIF}
  327. if fWhereClause.Validate(obj) then
  328. begin
  329. for i := Low(aFields) to High(aFields) do
  330. begin
  331. if not TRTTI.PropertyExists(TypeInfo(T),aFields[i]) then raise ELinqError.CreateFmt('Linq update field "%s" not found in obj',[aFields[i]]);
  332. try
  333. {$IFNDEF FPC}
  334. TRTTI.SetPropertyValue(obj,aFields[i],aValues[i]);
  335. {$ELSE}
  336. case aValues[i].VType of
  337. vtString : value := string(aValues[i].VString^);
  338. vtChar : value := string(aValues[i].VChar);
  339. {$IFDEF MSWINDOWS}
  340. vtAnsiString : value := AnsiString(aValues[i].VAnsiString);
  341. vtWideString : value := WideString(aValues[i].VWideString);
  342. {$ENDIF}
  343. {$IFDEF UNICODE}
  344. vtUnicodeString: AsString := string(aValues[i].VUnicodeString);
  345. {$ENDIF UNICODE}
  346. vtInteger : value := aValues[i].VInteger;
  347. vtInt64 : value := aValues[i].VInt64^;
  348. vtExtended : value := aValues[i].VExtended^;
  349. vtBoolean : value := aValues[i].VBoolean;
  350. else raise Exception.Create('DataType not supported by Linq update');
  351. end;
  352. TRTTI.SetPropertyValue(obj,aFields[i],value);
  353. {$ENDIF}
  354. except
  355. on E : Exception do raise ELinqError.CreateFmt('Linq update error: %s',[e.Message]);
  356. end;
  357. end;
  358. Inc(Result);
  359. end;
  360. end;
  361. end;
  362. function TLinqQuery<T>.FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  363. var
  364. i : Integer;
  365. begin
  366. Result := aWhereClause;
  367. if aWhereClause = '' then
  368. begin
  369. Result := '1 = 1';
  370. Exit;
  371. end;
  372. for i := 0 to aWhereClause.CountChar('?') - 1 do
  373. begin
  374. case aWhereParams[i].VType of
  375. vtInteger : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInteger),[]);
  376. vtInt64 : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInt64^),[]);
  377. vtExtended : Result := StringReplace(Result,'?',FloatToStr(aWhereParams[i].VExtended^),[]);
  378. vtBoolean : Result := StringReplace(Result,'?',BoolToStr(aWhereParams[i].VBoolean),[]);
  379. vtAnsiString : Result := StringReplace(Result,'?',string(aWhereParams[i].VAnsiString),[]);
  380. vtWideString : Result := StringReplace(Result,'?',string(aWhereParams[i].VWideString^),[]);
  381. {$IFNDEF NEXTGEN}
  382. vtString : Result := StringReplace(Result,'?',aWhereParams[i].VString^,[]);
  383. {$ENDIF}
  384. vtChar : Result := StringReplace(Result,'?',aWhereParams[i].VChar,[]);
  385. vtPChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VPChar),[]);
  386. else Result := StringReplace(Result,'?', DbQuotedStr(string(aWhereParams[i].VUnicodeString)),[]);
  387. end;
  388. end;
  389. end;
  390. function TLinqQuery<T>.Where(const aWhereClause: string; aWhereParams: array of const): ILinqQuery<T>;
  391. begin
  392. Result := Where(FormatParams(aWhereClause,aWhereParams));
  393. end;
  394. function TLinqQuery<T>.Where(const aWhereClause: string): ILinqQuery<T>;
  395. begin
  396. Result := Self;
  397. try
  398. fWhereClause := TExpressionParser.Parse(aWhereClause);
  399. except
  400. on E : Exception do raise ELinqNotValidExpression.Create(e.Message);
  401. end;
  402. end;
  403. function TLinqQuery<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
  404. begin
  405. Result := Self;
  406. fWhereClause := TLinqExpression<T>.Create(aPredicate);
  407. end;
  408. { TLinq }
  409. {$IFNDEF FPC}
  410. class function TLinq<T>.From(aObjectList: TObjectList<T>): ILinqQuery<T>;
  411. begin
  412. Result := TLinqQuery<T>.Create(aObjectList);
  413. end;
  414. {$ENDIF}
  415. class function TLinq<T>.From(aArray: TArray<T>): ILinqQuery<T>;
  416. begin
  417. Result := TLinqQuery<T>.Create(aArray);
  418. end;
  419. class function TLinq<T>.From(aXArray : TXArray<T>) : ILinqQuery<T>;
  420. begin
  421. Result := TLinqQuery<T>.Create(aXArray);
  422. end;
  423. { TLinqExpression<T> }
  424. constructor TLinqExpression<T>.Create(aPredicate: TPredicate<T>);
  425. begin
  426. fPredicate := aPredicate;
  427. end;
  428. function TLinqExpression<T>.Validate(aValue : TObject) : Boolean;
  429. begin
  430. Result := fPredicate(aValue as T);
  431. end;
  432. end.