Quick.Linq.pas 14 KB

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