PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/activate-docs/query.md

http://github.com/fwbrasil/activate
Markdown | 375 lines | 270 code | 105 blank | 0 comment | 0 complexity | 9196da9d6604fa17934cf50dfd18590a MD5 | raw file
Possible License(s): LGPL-2.1
  1. # QUERY #
  2. Activate queries are consistent, even with entities created in the current transaction, so a new entity can be returned in a query. This is achieved by the LiveCache, which implements in memory the behavior of a database.
  3. **Important:**
  4. - Remember to use queries inside transactions.
  5. - Use only attributes accessors inside a query. If you use a method that isnt an accessor, Activate can produce a wrong result.
  6. Given this entities:
  7. ``` scala
  8. trait Person extends Entity {
  9. var name: String
  10. }
  11. class NaturalPerson(var name: String, var motherName: String) extends Person
  12. class LegalPerson(var name: String, var director: NaturalPerson) extends Person
  13. ```
  14. There are some forms of query:
  15. ## BYID ##
  16. If you have the entity id, this method is the best way to recover the instance. It has better performance. If the entity is already in memory, Activate can avoid a database trip.
  17. ``` scala
  18. byId[Person](personId)
  19. ```
  20. The method return is an Option[Person].
  21. ## ALL ##
  22. Get all persons:
  23. ``` scala
  24. all[Person]
  25. ```
  26. Note that the queries can be made about abstract entities (trait and abstract class).
  27. ## SELECT WHERE ##
  28. Get all natural persons with the name John and mother name Mary.
  29. ``` scala
  30. select[NaturalPerson] where(_.name :== "John", _.motherName :== "Mary")
  31. ```
  32. ## OPERATORS ##
  33. Activate query operators are preceded by a :.
  34. The available operators are :**||**, :**&&**, :**==**, :**!=**, :**<**, :**>**, :**<=**, :**>=**, **isNull** and **isNotNull**.
  35. You have to use parenthesis to Scala recognize the implicit conversions to Query. Example:
  36. Get all natural persons with the name John or mother name Mary.
  37. ``` scala
  38. select[NaturalPerson] where(person => (person.name :== "John") :|| (person.motherName :== "Mary"))
  39. ```
  40. ## COLLECTION OPERATORS ##
  41. It is possible to query collections using the **in** and **notInt** operators. Examples:
  42. ``` scala
  43. query {
  44. (e: MyEntity) => where(e.age.in(List(40))) select (e)
  45. }
  46. select[MyEntity].where(_.age notIn List(20, 30))
  47. ```
  48. ## MATCH OPERATORS ##
  49. Activate supports queries that matches strings by using the operators **like** and **regexp**.
  50. Natural persons that the name start with John:
  51. ``` scala
  52. select[NaturalPerson] where(_.name like "John*")
  53. ```
  54. Use * to indicate any sequence of characters and ? to indicate one any character.
  55. Natural persons with numbers on name:
  56. ``` scala
  57. select[NaturalPerson] where(_.name regexp "^.*[0-9].*$")
  58. ```
  59. Activate relies on the storage regex engine, so its possible that the query result varies according to the current storage. For memory and prevayler storages, java regex is used.
  60. ## COMPLETE QUERY FORM ##
  61. The all and select where queries are a simplified form of the complete query:
  62. ``` scala
  63. query {
  64. (person: Person) => where(person.name :== "John") select (person)
  65. }
  66. ```
  67. It is also possible to define an empty where:
  68. ``` scala
  69. query {
  70. (person: Person) => where() select (person)
  71. }
  72. ```
  73. ## STRING CASE MANIPULATION ##
  74. It is possible to use toUpperCase(string) and toLowerCase(string) as query functions:
  75. ``` scala
  76. query {
  77. (person: Person) => where(toUpperCase(person.name) :== "JOHN") select (person)
  78. }
  79. ```
  80. ## NESTED PROPERTIES ##
  81. Queries using more than one entity or nested properties:
  82. ``` scala
  83. query {
  84. (company: LegalPerson, director: NaturalPerson) => where(company.director :== director) select (company, director)
  85. }
  86. query {
  87. (company: LegalPerson) => where(company.director.name :== "John2") select (company)
  88. }
  89. ```
  90. NOTE: Queries involving more than one entity or nested properties are not supported by MongoStorage.
  91. ## ORDERED QUERY ##
  92. The complete query form supports the order by clause:
  93. ``` scala
  94. query {
  95. (entity: MyEntity) =>
  96. where(entity.attribute like "A*") select (entity) orderBy (entity.attribute)
  97. }
  98. ```
  99. If the entity attribute doesnt support ordering, a compile error with be thrown. Its possible have multiple order by criterias and/or define directions:
  100. ``` scala
  101. query {
  102. (entity: MyEntity) =>
  103. where(entity.attribute1 like "A*") select (entity) orderBy (entity.attribute1 asc, entity.attribute2 desc)
  104. }
  105. ```
  106. ## LIMITED QUERY ##
  107. It is possible to limit the number of returned rows in a query by adding the limit clause after the orderBy:
  108. ``` scala
  109. query {
  110. (entity: MyEntity) =>
  111. where(entity.attribute like "A*") select (entity) orderBy (entity.attribute) limit(10)
  112. }
  113. ```
  114. It is possible to define an offset:
  115. ``` scala
  116. query {
  117. (entity: MyEntity) =>
  118. where(entity.attribute like "A*") select (entity) orderBy (entity.attribute) limit(10) offset(20)
  119. }
  120. ```
  121. ## PAGINATED QUERY ##
  122. Paginated query example:
  123. ``` scala
  124. paginatedQuery {
  125. (entity: MyEntity) =>
  126. where(entity.attribute like "A*") select (entity) orderBy (entity.attribute)
  127. }.navigator(100)
  128. ```
  129. Note that the query must be ordered to be paginated. It returns a Pagination instance. The navigator method call produces a navigable pagination interface (PaginationNavigator) using the parameter as the page size.
  130. Methods and values of **PaginationNavigator**:
  131. - **val numberOfResults: Int**
  132. Total number of lines returned by the query.
  133. - **val numberOfPages: Int**
  134. Number of pages determined by the page size.
  135. - **def hasNext: Boolean**
  136. Indicates if the navigator has a next page.
  137. - **def next: List[S]**
  138. Go to next page and return it.
  139. - **def page(number: Int): List[S]**
  140. Go to the specified page and return it.
  141. - **def currentPage: List[S]**
  142. Return the current page elements.
  143. - **def firstPage: List[S]**
  144. Go to the first page and return it.
  145. - **def lastPage: List[S]**
  146. Go to the last page and return it.
  147. ## QUERY PARSE CACHE ##
  148. For the complete query form, Activate maintains a parse cache. With this functionality if a query is already parsed before, it is bounded to the current parameters, avoiding to parse again.
  149. This cache doesnt works with all and select where. If you have a query that will run too many times, prefer to use the complete query form.
  150. ## DYNAMIC QUERIES ##
  151. The default query method uses the query parse cache, so it is not possible to define dynamic queries that changes its structure according to the parameters.
  152. Dynamic queries skip the parse cache, so it is possible to define queries like this example:
  153. ``` scala
  154. def someQuery(asc: Boolean) =
  155. dynamicQuery {
  156. (e: MyEntity) =>
  157. where() select (e) orderBy {
  158. if (asc)
  159. e.name asc
  160. else
  161. e.name desc
  162. }
  163. }
  164. ```
  165. ## CACHED QUERIES ##
  166. The cachedQuery is a mechanism to maintain a in-memory cache of the query results. The cache is consistent with new and modified entities by transactions in the same VM. It is possible to enable the optimistic locking with read validation so the cache can be updated for modified entities in other VMs too. For now, the cache cant be updated automatically for new entities created in other VMs. This limitation should be resolved on the 1.5 version.
  167. The cache just maintain the entities IDs, so there arent strong references to the entities.
  168. Example of a cachedQuery:
  169. ``` scala
  170. cachedQuery {
  171. (person: Person) => where(person.name :== "John")
  172. }
  173. ```
  174. There isnt a select clause. Cached queries can be used only to select the entity instances.
  175. The cache is initialized lazily for each set of query input parameters.
  176. ## EAGER QUERIES ##
  177. By default, Activate queries are lazy and just load the entity id. When needed, the entity values are loaded from the database.
  178. To force the entity properties loading it is possible to use eager queries. For example:
  179. ``` scala
  180. query {
  181. (person: Person) => where(person.name :== "John") select(e.eager)
  182. }
  183. ```
  184. The eager keyword can be used only for entities inside the select clause.
  185. It is also possible to use it for nested properties:
  186. ``` scala
  187. query {
  188. (person: Person) => where(person.name :== "John") select(e.contact.eager)
  189. }
  190. ```
  191. To eager load relations, it is necessary to do explicit joins. For example:
  192. ``` scala
  193. query {
  194. (person: Person, contact: Contact) => where(person.contact :== contact) select(e.eager, contact.eager)
  195. }
  196. ```
  197. ## ASYNC QUERIES ##
  198. Activate provides async variations of each query form. The query syntax and operators are the same from non-async queries. Examples:
  199. ``` scala
  200. asyncById[Person](personId)
  201. asyncSelect[NaturalPerson] where(_.name :== "John", _.motherName :== "Mary")
  202. asyncQuery {
  203. (person: Person) => where(person.name :== "John") select (person)
  204. }
  205. ```
  206. Async queries must be executed inside a asyncTransactionalChain block that provides a TransactionalExecutionContext (the ctx parameter):
  207. ``` scala
  208. val aFuture: Future[List[Person]] =
  209. asyncTransactionalChain { implicit ctx =>
  210. asyncAll[Person]
  211. }
  212. ```
  213. It is possible to compose the future using map operations:
  214. ``` scala
  215. val aFuture: Future[Unit] =
  216. asyncTransactionalChain { implicit ctx =>
  217. asyncAll[Person].map {
  218. persons => persons.foreach(_.delete)
  219. }
  220. }
  221. ```
  222. ## ASYNC PAGINATED QUERIES ##
  223. There is a async variation for paginated queries too:
  224. ``` scala
  225. val aFuture: Future[AsyncPaginationNavigator[String]] =
  226. asyncPaginatedQuery {
  227. (entity: MyEntity) =>
  228. where(entity.attribute like "A*") select (entity) orderBy (entity.attribute)
  229. }.navigator(100)
  230. ```
  231. Methods and values of **AsyncPaginationNavigator**:
  232. - **val numberOfResults: Int**
  233. Total number of lines returned by the query.
  234. - **val numberOfPages: Int**
  235. Number of pages determined by the page size.
  236. - **def hasNext: Boolean**
  237. Indicates if the navigator has a next page.
  238. - **def next: Future[List[S]]**
  239. Go to next page and return it.
  240. - **def page(number: Int): Future[List[S]]**
  241. Go to the specified page and return it.
  242. - **def currentPage: Future[List[S]]**
  243. Return the current page elements.
  244. - **def firstPage: Future[List[S]]**
  245. Go to the first page and return it.
  246. - **def lastPage: Future[List[S]]**
  247. Go to the last page and return it.
  248. ## SLICK QUERIES ##
  249. To use slick lifted embedding queries, add the **activate-slick** module to the project dependencies.
  250. The slick operations are provided by extending the persistence context from the SlickQueryContext:
  251. ``` scala
  252. import net.fwbrasil.activate.slick.SlickQueryContext
  253. object myContext extends ActivateContext with SlickQueryContext {
  254. val storage = ...
  255. }
  256. ```
  257. A query example:
  258. ``` scala
  259. SlickQuery[MyEntity].filter(_.attribute.col === someValue).execute
  260. ```
  261. Activate can avoid the necessity to declare the slick Table objects, but for each usage of an entity property, it is necessary to add the .col, so Activate can create the correspondent Slick column definition.
  262. The execute call integrates with the persistence context storage to perform the query and return the results.
  263. Refer to the [slick](http://slick.typesafe.com/) documentation to more information on how to construct slick lifted embedding queries.
  264. Another more complex query:
  265. ``` scala
  266. (for {
  267. c <- SlickQuery[Coffee]
  268. s <- SlickQuery[Supplier].sortBy(_.id.col) if c.supplier.col === s.col
  269. } yield (c.name.col)).sortBy(_.asc).take(2)
  270. ```