1use crate::prelude::*;
4use toasty_core::{
5 driver::Operation,
6 stmt::{Expr, ExprSet, Statement},
7};
8
9#[driver_test(scenario(crate::scenarios::two_models))]
10pub async fn query_index_eq(t: &mut Test) -> Result<()> {
11 let mut db = setup(t).await;
12
13 for name in ["one", "two", "three"] {
15 User::create().name(name).exec(&mut db).await?;
16 }
17
18 let users = User::filter_by_name("one").exec(&mut db).await?;
19
20 assert_eq!(1, users.len());
21 assert_eq!("one", users[0].name);
22
23 User::create().name("one").exec(&mut db).await?;
26
27 let users = User::filter_by_name("one").exec(&mut db).await?;
28
29 assert_eq!(2, users.len());
30 assert!(users.iter().all(|u| u.name == "one"));
31 Ok(())
32}
33
34#[driver_test]
35pub async fn query_partition_key_string_eq(test: &mut Test) -> Result<()> {
36 #[derive(Debug, toasty::Model)]
37 #[key(partition = league, local = name)]
38 struct Team {
39 league: String,
40
41 name: String,
42
43 founded: i64,
44 }
45
46 let mut db = test.setup_db(models!(Team)).await;
47
48 for (league, name, founded) in [
50 ("MLS", "Portland Timbers", 2009),
51 ("MLS", "Seattle Sounders FC", 2007),
52 ("MLS", "Vancouver Whitecaps FC", 2009),
53 ("MLS", "Los Angeles Football Club", 2014),
54 ("MLS", "San Jose Earthquakes", 1994),
55 ("MLS", "LA Galaxy", 1994),
56 ("EPL", "Arsenal", 1886),
57 ("EPL", "Chelsea", 1905),
58 ("EPL", "Manchester United", 1878),
59 ("EPL", "Tottenham", 1882),
60 ("La Liga", "FC Barcelona", 1899),
61 ("La Liga", "Girona FC", 1930),
62 ("La Liga", "Real Madrid", 1902),
63 ("La Liga", "Atlético Madrid", 1903),
64 ]
65 .into_iter()
66 {
67 Team::create()
68 .league(league)
69 .name(name)
70 .founded(founded)
71 .exec(&mut db)
72 .await?;
73 }
74
75 let teams = Team::filter(Team::fields().league().eq("EPL"))
77 .exec(&mut db)
78 .await?;
79
80 let mut names = teams.iter().map(|team| &team.name).collect::<Vec<_>>();
81 names.sort();
82
83 assert_eq!(
84 names,
85 ["Arsenal", "Chelsea", "Manchester United", "Tottenham"]
86 );
87
88 let teams = Team::filter(
90 Team::fields()
91 .league()
92 .eq("MLS")
93 .and(Team::fields().name().eq("Portland Timbers")),
94 )
95 .exec(&mut db)
96 .await?;
97
98 let mut names = teams.iter().map(|team| &team.name).collect::<Vec<_>>();
99 names.sort();
100
101 assert_eq!(names, ["Portland Timbers"]);
102
103 let teams = Team::filter(
105 Team::fields()
106 .league()
107 .eq("MLS")
108 .and(Team::fields().founded().eq(2009)),
109 )
110 .exec(&mut db)
111 .await?;
112
113 let mut names = teams.iter().map(|team| &team.name).collect::<Vec<_>>();
114 names.sort();
115
116 assert_eq!(names, ["Portland Timbers", "Vancouver Whitecaps FC"]);
117
118 let teams = Team::filter(
120 Team::fields()
121 .league()
122 .eq("MLS")
123 .and(Team::fields().founded().eq(2009))
124 .and(Team::fields().name().eq("Portland Timbers")),
125 )
126 .exec(&mut db)
127 .await?;
128
129 assert_eq!(1, teams.len());
130 assert!(teams.iter().all(|team| team.founded == 2009));
131
132 let mut names = teams.iter().map(|team| &team.name).collect::<Vec<_>>();
133 names.sort();
134
135 assert_eq!(names, ["Portland Timbers"]);
136
137 let teams = Team::filter(
139 Team::fields()
140 .league()
141 .eq("MLS")
142 .and(Team::fields().founded().eq(2009))
143 .and(Team::fields().name().eq("LA Galaxy")),
144 )
145 .exec(&mut db)
146 .await?;
147
148 assert!(teams.is_empty());
149 Ok(())
150}
151
152#[driver_test]
153pub async fn query_local_key_cmp(test: &mut Test) -> Result<()> {
154 #[derive(Debug, toasty::Model)]
155 #[key(partition = kind, local = timestamp)]
156 struct Event {
157 kind: String,
158
159 timestamp: i64,
160 }
161
162 let mut db = test.setup_db(models!(Event)).await;
163
164 for (kind, ts) in [
166 ("info", 0),
167 ("warn", 1),
168 ("info", 2),
169 ("warn", 3),
170 ("info", 4),
171 ("warn", 5),
172 ("info", 6),
173 ("warn", 7),
174 ("info", 8),
175 ("warn", 9),
176 ("info", 10),
177 ("warn", 11),
178 ("info", 12),
179 ("warn", 13),
180 ("info", 14),
181 ("warn", 15),
182 ("info", 16),
183 ("warn", 17),
184 ("info", 18),
185 ("warn", 19),
186 ] {
187 Event::create()
188 .kind(kind)
189 .timestamp(ts)
190 .exec(&mut db)
191 .await?;
192 }
193
194 let events: Vec<_> = Event::filter_by_kind("info")
195 .filter(Event::fields().timestamp().ne(10))
196 .exec(&mut db)
197 .await?;
198
199 assert_eq_unordered!(
200 events.iter().map(|event| event.timestamp),
201 [&0, &2, &4, &6, &8, &12, &14, &16, &18,]
202 );
203
204 let events: Vec<_> = Event::filter_by_kind("info")
205 .filter(Event::fields().timestamp().gt(10))
206 .exec(&mut db)
207 .await?;
208
209 assert_eq_unordered!(
210 events.iter().map(|event| event.timestamp),
211 [&12, &14, &16, &18,]
212 );
213
214 let events: Vec<_> = Event::filter_by_kind("info")
215 .filter(Event::fields().timestamp().ge(10))
216 .exec(&mut db)
217 .await?;
218
219 assert_eq_unordered!(
220 events.iter().map(|event| event.timestamp),
221 [&10, &12, &14, &16, &18,]
222 );
223
224 let events: Vec<_> = Event::filter_by_kind("info")
225 .filter(Event::fields().timestamp().lt(10))
226 .exec(&mut db)
227 .await?;
228
229 assert_eq_unordered!(
230 events.iter().map(|event| event.timestamp),
231 [&0, &2, &4, &6, &8]
232 );
233
234 let events: Vec<_> = Event::filter_by_kind("info")
235 .filter(Event::fields().timestamp().le(10))
236 .exec(&mut db)
237 .await?;
238
239 assert_eq_unordered!(
240 events.iter().map(|event| event.timestamp),
241 [&0, &2, &4, &6, &8, &10]
242 );
243 Ok(())
244}
245
246#[driver_test(scenario(crate::scenarios::user_with_age))]
247pub async fn set_filter_overwrites(test: &mut Test) -> Result<()> {
248 let mut db = setup(test).await;
249
250 toasty::create!(User::[
251 { name: "Alice", age: 30 },
252 { name: "Bob", age: 20 },
253 ])
254 .exec(&mut db)
255 .await?;
256
257 use toasty::stmt::{List, Query};
260 let mut q = Query::<List<User>>::all().filter(User::fields().name().eq("Alice"));
261 q.set_filter(User::fields().name().eq("Bob"));
262
263 let users = q.exec(&mut db).await?;
264
265 assert_eq!(1, users.len());
266 assert_eq!("Bob", users[0].name);
267 Ok(())
268}
269
270#[driver_test(scenario(crate::scenarios::user_with_age))]
271pub async fn query_or_basic(test: &mut Test) -> Result<()> {
272 let mut db = setup(test).await;
273 let _name_column = db.schema().table_for(User::id()).columns[1].id;
274 let _age_column = db.schema().table_for(User::id()).columns[2].id;
275
276 for (name, age) in [("Alice", 25), ("Bob", 30), ("Charlie", 35), ("Diana", 40)] {
278 User::create().name(name).age(age).exec(&mut db).await?;
279 }
280
281 test.log().clear();
283
284 let result = User::filter(
286 User::fields()
287 .name()
288 .eq("Alice")
289 .or(User::fields().age().eq(35)),
290 )
291 .exec(&mut db)
292 .await;
293
294 if test.capability().sql() {
295 let users = result?;
296 assert_eq!(2, users.len());
297 let mut names: Vec<_> = users.iter().map(|u| u.name.as_str()).collect();
298 names.sort();
299 assert_eq!(names, ["Alice", "Charlie"]);
300
301 let (op, _) = test.log().pop();
303
304 assert_struct!(&op, Operation::QuerySql({
305 stmt: Statement::Query({
306 body: ExprSet::Select({
307 filter.expr: Some(Expr::Or({
308 })),
324 }),
325 }),
326 }));
327 } else if test.capability().scan {
328 let users = result?;
329 assert_eq!(2, users.len());
330 let mut names: Vec<_> = users.iter().map(|u| u.name.as_str()).collect();
331 names.sort();
332 assert_eq!(names, ["Alice", "Charlie"]);
333 } else {
334 assert!(
335 result.is_err(),
336 "Expected error for OR query without key condition on non-SQL database"
337 );
338 }
339 Ok(())
340}
341
342#[driver_test(scenario(crate::scenarios::user_with_age))]
343pub async fn query_or_multiple(test: &mut Test) -> Result<()> {
344 let mut db = setup(test).await;
345
346 for (name, age) in [("Alice", 25), ("Bob", 30), ("Charlie", 35), ("Diana", 40)] {
348 User::create().name(name).age(age).exec(&mut db).await?;
349 }
350
351 let result = User::filter(
353 User::fields()
354 .name()
355 .eq("Alice")
356 .or(User::fields().age().eq(35))
357 .or(User::fields().age().eq(40)),
358 )
359 .exec(&mut db)
360 .await;
361
362 if test.capability().sql() || test.capability().scan {
363 let users = result?;
364 assert_eq!(3, users.len());
365 let mut names: Vec<_> = users.iter().map(|u| u.name.as_str()).collect();
366 names.sort();
367 assert_eq!(names, ["Alice", "Charlie", "Diana"]);
368 } else {
369 assert!(
370 result.is_err(),
371 "Expected error for OR query without key condition on non-SQL database"
372 );
373 }
374 Ok(())
375}
376
377#[driver_test(scenario(crate::scenarios::user_with_active))]
378pub async fn query_or_and_combined(test: &mut Test) -> Result<()> {
379 let mut db = setup(test).await;
380
381 for (name, age, active) in [
383 ("Alice", 25, true),
384 ("Bob", 30, false),
385 ("Charlie", 35, true),
386 ("Diana", 40, false),
387 ("Eve", 25, false),
388 ] {
389 User::create()
390 .name(name)
391 .age(age)
392 .active(active)
393 .exec(&mut db)
394 .await?;
395 }
396
397 let result = User::filter(
399 User::fields()
400 .name()
401 .eq("Alice")
402 .or(User::fields().age().eq(35))
403 .and(User::fields().active().eq(true)),
404 )
405 .exec(&mut db)
406 .await;
407
408 if test.capability().sql() || test.capability().scan {
409 let users = result?;
410 assert_eq!(2, users.len());
411 let mut names: Vec<_> = users.iter().map(|u| u.name.as_str()).collect();
412 names.sort();
413 assert_eq!(names, ["Alice", "Charlie"]);
414 } else {
415 assert!(
416 result.is_err(),
417 "Expected error for OR query without key condition on non-SQL database"
418 );
419 }
420 Ok(())
421}
422
423#[driver_test]
424pub async fn query_boolean_filters_with_index(test: &mut Test) -> Result<()> {
425 #[derive(Debug, toasty::Model)]
426 #[key(partition = team, local = name)]
427 struct Player {
428 team: String,
429
430 name: String,
431
432 #[allow(dead_code)]
433 position: String,
434
435 #[allow(dead_code)]
436 number: i64,
437 }
438
439 let mut db = test.setup_db(models!(Player)).await;
440
441 for (team, name, position, number) in [
443 ("Timbers", "Diego Valeri", "Midfielder", 8),
444 ("Timbers", "Darlington Nagbe", "Midfielder", 6),
445 ("Timbers", "Diego Chara", "Midfielder", 21),
446 ("Timbers", "Fanendo Adi", "Forward", 9),
447 ("Timbers", "Adam Kwarasey", "Goalkeeper", 1),
448 ("Sounders", "Clint Dempsey", "Forward", 2),
449 ("Sounders", "Obafemi Martins", "Forward", 9),
450 ("Sounders", "Osvaldo Alonso", "Midfielder", 6),
451 ] {
452 Player::create()
453 .team(team)
454 .name(name)
455 .position(position)
456 .number(number)
457 .exec(&mut db)
458 .await?;
459 }
460
461 let players = Player::filter(
464 Player::fields().team().eq("Timbers").and(
465 Player::fields()
466 .position()
467 .eq("Forward")
468 .or(Player::fields().position().eq("Goalkeeper")),
469 ),
470 )
471 .exec(&mut db)
472 .await?;
473
474 assert_eq!(2, players.len());
475 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
476 names.sort();
477 assert_eq!(names, ["Adam Kwarasey", "Fanendo Adi"]);
478
479 let players = Player::filter(
481 Player::fields().team().eq("Timbers").and(
482 Player::fields()
483 .number()
484 .eq(8)
485 .or(Player::fields().number().eq(21))
486 .or(Player::fields().number().eq(9)),
487 ),
488 )
489 .exec(&mut db)
490 .await?;
491
492 assert_eq!(3, players.len());
493 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
494 names.sort();
495 assert_eq!(names, ["Diego Chara", "Diego Valeri", "Fanendo Adi"]);
496
497 let players = Player::filter(
499 Player::fields().team().eq("Timbers").and(
500 Player::fields()
501 .number()
502 .gt(20)
503 .or(Player::fields().number().lt(2)),
504 ),
505 )
506 .exec(&mut db)
507 .await?;
508 assert_eq!(2, players.len());
509 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
510 names.sort();
511 assert_eq!(names, ["Adam Kwarasey", "Diego Chara"]);
512
513 let players = Player::filter(
515 Player::fields()
516 .team()
517 .eq("Timbers")
518 .and(Player::fields().position().eq("Midfielder").not()),
519 )
520 .exec(&mut db)
521 .await?;
522 assert_eq!(2, players.len());
523 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
524 names.sort();
525 assert_eq!(names, ["Adam Kwarasey", "Fanendo Adi"]);
526
527 let players = Player::filter(
529 Player::fields()
530 .team()
531 .eq("Timbers")
532 .and(Player::fields().number().gt(8).not()),
533 )
534 .exec(&mut db)
535 .await?;
536 assert_eq!(3, players.len());
537 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
538 names.sort();
539 assert_eq!(names, ["Adam Kwarasey", "Darlington Nagbe", "Diego Valeri"]);
540
541 let players = Player::filter(
543 Player::fields()
544 .team()
545 .eq("Timbers")
546 .and(!Player::fields().position().eq("Midfielder")),
547 )
548 .exec(&mut db)
549 .await?;
550 assert_eq!(2, players.len());
551 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
552 names.sort();
553 assert_eq!(names, ["Adam Kwarasey", "Fanendo Adi"]);
554
555 let players = Player::filter(
557 Player::fields().team().eq("Timbers").and(
558 !(Player::fields()
559 .number()
560 .gt(8)
561 .or(Player::fields().position().eq("Goalkeeper"))),
562 ),
563 )
564 .exec(&mut db)
565 .await?;
566 assert_eq!(2, players.len());
567 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
568 names.sort();
569 assert_eq!(names, ["Darlington Nagbe", "Diego Valeri"]);
570 Ok(())
571}
572
573#[driver_test]
574pub async fn query_or_on_keys(test: &mut Test) -> Result<()> {
575 #[derive(Debug, toasty::Model)]
581 #[key(partition = team, local = name)]
582 struct Player {
583 team: String,
584
585 name: String,
586
587 #[allow(dead_code)]
588 position: String,
589 }
590
591 let mut db = test.setup_db(models!(Player)).await;
592
593 for (team, name, position) in [
594 ("Timbers", "Diego Valeri", "Midfielder"),
595 ("Timbers", "Fanendo Adi", "Forward"),
596 ("Sounders", "Clint Dempsey", "Forward"),
597 ("Sounders", "Osvaldo Alonso", "Midfielder"),
598 ("Galaxy", "Robbie Keane", "Forward"),
599 ] {
600 Player::create()
601 .team(team)
602 .name(name)
603 .position(position)
604 .exec(&mut db)
605 .await?;
606 }
607
608 let players = Player::filter(
610 Player::fields()
611 .team()
612 .eq("Timbers")
613 .or(Player::fields().team().eq("Sounders")),
614 )
615 .exec(&mut db)
616 .await?;
617
618 assert_eq!(4, players.len());
619 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
620 names.sort();
621 assert_eq!(
622 names,
623 [
624 "Clint Dempsey",
625 "Diego Valeri",
626 "Fanendo Adi",
627 "Osvaldo Alonso"
628 ]
629 );
630
631 let players = Player::filter(
634 Player::fields()
635 .team()
636 .eq("Timbers")
637 .and(Player::fields().name().eq("Diego Valeri"))
638 .or(Player::fields()
639 .team()
640 .eq("Sounders")
641 .and(Player::fields().name().eq("Clint Dempsey"))),
642 )
643 .exec(&mut db)
644 .await?;
645
646 assert_eq!(2, players.len());
647 let mut names: Vec<_> = players.iter().map(|p| p.name.as_str()).collect();
648 names.sort();
649 assert_eq!(names, ["Clint Dempsey", "Diego Valeri"]);
650 Ok(())
651}
652
653#[driver_test(requires(scan), scenario(crate::scenarios::user_with_active))]
654pub async fn query_arbitrary_constraint(test: &mut Test) -> Result<()> {
655 let mut db = setup(test).await;
656
657 for (kind, ts) in [
659 ("info", 0),
660 ("warn", 1),
661 ("info", 2),
662 ("warn", 3),
663 ("info", 4),
664 ("warn", 5),
665 ("info", 6),
666 ("warn", 7),
667 ("info", 8),
668 ("warn", 9),
669 ("info", 10),
670 ("warn", 11),
671 ("info", 12),
672 ("warn", 13),
673 ("info", 14),
674 ("warn", 15),
675 ("info", 16),
676 ("warn", 17),
677 ("info", 18),
678 ("warn", 19),
679 ] {
680 User::create()
681 .name(kind)
682 .age(ts)
683 .active(true)
684 .exec(&mut db)
685 .await?;
686 }
687
688 let events: Vec<_> = User::filter(User::fields().age().gt(12))
689 .exec(&mut db)
690 .await?;
691
692 assert_eq_unordered!(
693 events.iter().map(|event| event.age),
694 [&13, &14, &15, &16, &17, &18, &19,]
695 );
696
697 let events: Vec<_> = User::filter(
698 User::fields()
699 .age()
700 .gt(12)
701 .and(User::fields().name().ne("info")),
702 )
703 .exec(&mut db)
704 .await?;
705
706 assert!(events.iter().all(|event| event.name != "info"));
707
708 assert_eq_unordered!(events.iter().map(|event| event.age), [&13, &15, &17, &19,]);
709
710 let events: Vec<_> = User::filter(
711 User::fields()
712 .name()
713 .eq("info")
714 .and(User::fields().age().ne(10)),
715 )
716 .exec(&mut db)
717 .await?;
718
719 assert_eq_unordered!(
720 events.iter().map(|event| event.age),
721 [&0, &2, &4, &6, &8, &12, &14, &16, &18,]
722 );
723
724 let events: Vec<_> = User::filter(
725 User::fields()
726 .name()
727 .eq("info")
728 .and(User::fields().age().gt(10)),
729 )
730 .exec(&mut db)
731 .await?;
732
733 assert_eq_unordered!(events.iter().map(|event| event.age), [&12, &14, &16, &18,]);
734
735 let events: Vec<_> = User::filter(
736 User::fields()
737 .name()
738 .eq("info")
739 .and(User::fields().age().ge(10)),
740 )
741 .exec(&mut db)
742 .await?;
743
744 assert_eq_unordered!(
745 events.iter().map(|event| event.age),
746 [&10, &12, &14, &16, &18,]
747 );
748
749 let events: Vec<_> = User::filter(
750 User::fields()
751 .name()
752 .eq("info")
753 .and(User::fields().age().lt(10)),
754 )
755 .exec(&mut db)
756 .await?;
757
758 assert_eq_unordered!(events.iter().map(|event| event.age), [&0, &2, &4, &6, &8]);
759
760 let events: Vec<_> = User::filter(
761 User::fields()
762 .name()
763 .eq("info")
764 .and(User::fields().age().le(10)),
765 )
766 .exec(&mut db)
767 .await?;
768
769 assert_eq_unordered!(
770 events.iter().map(|event| event.age),
771 [&0, &2, &4, &6, &8, &10]
772 );
773 Ok(())
774}
775
776#[driver_test(scenario(crate::scenarios::user_with_age))]
777pub async fn query_not_basic(test: &mut Test) -> Result<()> {
778 let mut db = setup(test).await;
779
780 for (name, age) in [("Alice", 25), ("Bob", 30), ("Charlie", 35), ("Diana", 40)] {
782 User::create().name(name).age(age).exec(&mut db).await?;
783 }
784
785 test.log().clear();
787
788 let result = User::filter(User::fields().name().eq("Alice").not())
790 .exec(&mut db)
791 .await;
792
793 if test.capability().sql() || test.capability().scan {
794 let users = result?;
795 assert_eq!(3, users.len());
796 let mut names: Vec<_> = users.iter().map(|u| u.name.as_str()).collect();
797 names.sort();
798 assert_eq!(names, ["Bob", "Charlie", "Diana"]);
799 } else {
800 assert!(
801 result.is_err(),
802 "Expected error for NOT query without key condition on non-SQL database"
803 );
804 }
805 Ok(())
806}
807
808#[driver_test(scenario(crate::scenarios::user_with_active))]
809pub async fn query_not_and_combined(test: &mut Test) -> Result<()> {
810 let mut db = setup(test).await;
811
812 for (name, age, active) in [
814 ("Alice", 25, true),
815 ("Bob", 30, false),
816 ("Charlie", 35, true),
817 ("Diana", 40, false),
818 ("Eve", 25, false),
819 ] {
820 User::create()
821 .name(name)
822 .age(age)
823 .active(active)
824 .exec(&mut db)
825 .await?;
826 }
827
828 let result = User::filter(
831 User::fields()
832 .active()
833 .eq(true)
834 .and(User::fields().age().eq(25).not()),
835 )
836 .exec(&mut db)
837 .await;
838
839 if test.capability().sql() || test.capability().scan {
840 let users = result?;
841 assert_eq!(1, users.len());
842 assert_eq!("Charlie", users[0].name);
843 } else {
844 assert!(
845 result.is_err(),
846 "Expected error for NOT query without key condition on non-SQL database"
847 );
848 }
849 Ok(())
850}
851
852#[driver_test(scenario(crate::scenarios::user_with_age))]
853pub async fn query_not_or_combined(test: &mut Test) -> Result<()> {
854 let mut db = setup(test).await;
855
856 for (name, age) in [("Alice", 25), ("Bob", 30), ("Charlie", 35), ("Diana", 40)] {
858 User::create().name(name).age(age).exec(&mut db).await?;
859 }
860
861 let result = User::filter(
864 User::fields()
865 .name()
866 .eq("Alice")
867 .or(User::fields().name().eq("Bob"))
868 .not(),
869 )
870 .exec(&mut db)
871 .await;
872
873 if test.capability().sql() || test.capability().scan {
874 let users = result?;
875 assert_eq!(2, users.len());
876 let mut names: Vec<_> = users.iter().map(|u| u.name.as_str()).collect();
877 names.sort();
878 assert_eq!(names, ["Charlie", "Diana"]);
879 } else {
880 assert!(
881 result.is_err(),
882 "Expected error for NOT query without key condition on non-SQL database"
883 );
884 }
885 Ok(())
886}