I do not have too much expierence in Grails, so maybe I do not understand hasMany and belongsTo relations in GORM.
Say, I have 2 classes Parent.groovy and Child.groovy
class Parent {
String name
List childen = new ArrayList()
static hasMany = [children: Child]
}
class Child {
String name
static belongsTo = [parent: Parent]
}
Person person1 = new Person(name: "Person1")
Child child1 = new Child(name: "child1")
Child child2 = new Child(name: "child2")
person1.addToChildren(child1).save(flush: true)
person1.addToChildren(child2).save(flush: true)
Person person2 = new Person(name: "Person2").save(flush: true)
Now I want to change a parent of a child
child1.parent = parent2 // no effect
child1.save(flush: true)
In controller it is possible
Child child1 = Child.get(1)
bindData(child1, [parent: [id: 2]])
child1.save(flush: true)
But now there is null in movie1.children, in DB I can see that parent_id has changed to 2
Note: In Active Record (Rails) it is easy
child1.parent_id = 2
Maybe I do not need to use such relation if I want to change parent?
Maybe there is another way to do it?
After some investigation I understood why there is a null in collection.
At the very beginning there are such values in Child table
id | name | parent_id | parent_idx
1 | child1 | 1 | 0
2 | child2 | 1 | 1
After
bindData(child1, [parent: [id: 2]])
We have
id | name | parent_id | parent_idx
1 | child1 | 2 | 0
2 | child2 | 1 | 1
So now parent2 has child1 with parent_idx 0 - it is ok. But parent1 has child2 with parent_idx = 1 (without 0). So we changed parent_id value without changing parent_idx.
I wish there was a possibility to change index in collection too.
So the conclusion is:
I we want to change parent we should not use List collection, so we will not have idx column and no problems
Related
TableA
| where GuidId == "123"
| where Desc has_any ("processor")
| join kind=leftouter TableB on
$left.SubId == $right.SubId,
$left.ProductName == $right.Name,
$left.GuidId == $right.GuidId
| distinct SubId, PriceTags, ResourceType, ProductName, Name
ProductName is in lower case and Name is in camel case. How to bring ProductName and Name to same case in Join condition.
Thanks
something like:
| extend Name=tolower(Name)
TableA
| where GuidId == "123"
| where Desc has_any ("processor")
| join kind=leftouter (TableB | extend Name=tolower(Name)) on $left.SubId == $right.SubId, $left.ProductName==$right.Name, $left.GuidId==$right.GuidId
|distinct SubId, PriceTags, ResourceType, ProductName, Name
extend Name=tolower(Name)
You'll need to 'normalize' the values before the join.
Ideally you'll do this before ingestion, or at ingestion time (using an update policy).
Given the current non-normalized values, you can do it at query time (performance would be sub-optimal):
TableA
| where GuidId == "123"
| where Desc has "processor"
| join kind=leftouter (
TableB
| extend Name = tolower(Name)
) on
$left.SubId == $right.SubId,
$left.ProductName == $right.Name,
$left.GuidId == $right.GuidId
| distinct SubId, PriceTags, ResourceType, ProductName, Name
I have a table like this:
+----+-------+------------------+
| ID | Name | TipoContenedorID |
+----+-------+------------------+
| 1 | first | 10 |
| 2 | two | 9 |
| 3 | three | 10 |
+----+-------+------------------+
So depending of "TipoContenedorID" I get the name of another table with anonymous type like this:
var vehiculo = _pService.Listar(x => x.TipoContenedor.CatalogosID.Equals("TCO"), includeProperties: "TipoContenedor").Select(x => new
{
x.TipoContenedor.ID,
x.TipoContenedor.Nombre
});
Problem is I just want to receive value one time. Actually I get:
TipoContenedor.Nombre = firstname
TipoContenedor.Nombre= secondname
TipoContenedor.Nombre = firstname
So I'm getting the first name twice. How do I distinct that TipoContenedorID if repeated just pass it? Regards
Add .Distinct()
var vehiculo = _pService
.Listar(x => x.TipoContenedor.CatalogosID.Equals("TCO"), includeProperties: "TipoContenedor")
.Select(x => new
{
x.TipoContenedor.ID,
x.TipoContenedor.Nombre
})
.Distinct();
How would I write a changelog.groovy for the grails database migration plugin that would insert rows into a table if a row doesn't already exist for a range of ids? For example.
cool_stuff table has
id | some_other_id |
The cool_stuff table is populated with data. Given a range of cool_stuff ids, 1 - 2000, I would like to:
Iterate through the ids, querying the cool_stuff table to see if the combination of cool_stuff id and some_other_id = 2 exists
If it doesn't exist, insert a row with the cool_stuff id and some_other_id = 2
Threre are recoreds on "cool_stuff" table already.
You need conbination of a record that "cool_stuff.id" and "some_other_id == 2"
so, do you want like following?
table of "cool_stuff"
FROM:
id | some_other_id
----|---------------
1 | 2
2 | 1
3 | 2
4 | 1
TO:
id | some_other_id
----|---------------
1 | 2
2 | 1
3 | 2
4 | 1
2 | 2
4 | 2
Is this right??
I would like to do like following if i do that.
databaseChangeLog = {
changeSet(author: "koji", id: "123456789") {
grailsChange {
change {
CoolStuff.list().findAll {
it.someOtherId != 2
}.each{
// save new instance
new CoolStuf(id: it.id, someOtherId:2).save(flush:true)
}
}
}
}
}
I have created a domain class as given below, which contains an int and list of Integer properties.
class User {
int UserId
List<Integer> UserFriendsId
static constraints = {
}
User() {
this.UserId = 21
this.UserFriendsId=[1,2,3]
}
}
The table generated for this domain class while saving is as follows
mysql> select * from user;
+----+---------+---------------------+
| id | version | UserId |
+----+---------+---------------------+
| 1 | 0 | 21 |
| 2 | 0 | 21 |
| 3 | 0 | 21 |
+----+---------+---------------------+
3 rows in set (0.00 sec)
column for userFriendsId (ie: for list of integers) is not generated in this table user.
so how can solve this issue or can add list of integer in grails domain class.
The UserFriendsId List should be mapped as a GORM basic collection type and not simply be a list in the User domain class:
class User {
int userId
static hasMany = [userFriendsIds: Integer]
static mapping = {
userFriendsIds joinTable: [name: "user_id", column: "friend_id", type: Integer]
}
static constraints = {
}
User() {
}
}
Why not just make UserFriendsId a comma separated String?
class User {
int UserId
String UserFriendsId
static constraints = {
}
User() {
this.UserId = 21
this.UserFriendsId = "1,2,3"
}
}
Then:
for (userId in UserFriendsId.get(21).split(','))
{
println userId.toInteger()
/// Or do whatever ...
}
I am developing a GRAILS application (I'm new to GRAILS and inherited the project from a previous developer). I'm slowly getting a small grasp for how GRAILS operates and the use of DOMAIN classes, hibernate etc. The MySQL db is hosted on Amazon and we're using ElasticCache.
Do any of you more knowledgeable folks know how I can go about converting the following SQL statement into domain classes and query criteria.
if(params?.searchterm) {
def searchTerms = params.searchterm.trim().split( ',' )
def resultLimit = params.resultlimit?: 1000
def addDomain = ''
if (params?.domainname){
addDomain = " and url like '%${params.domainname}%' "
}
def theSearchTermsSQL = ""
/*
* create c.name rlike condition for each search term
*
*/
searchTerms.each{
aSearchTerm ->
if( theSearchTermsSQL != '' ){
theSearchTermsSQL += ' or '
}
theSearchTermsSQL += "cname rlike '[[:<:]]" + aSearchTerm.trim() + "[[:>:]]'"
}
/*
* build query
*
*/
def getUrlsQuery = "select
u.url as url,
c.name as cname,
t.weight as tweight
from
(category c, target t, url_meta_data u )
where
(" + theSearchTermsSQL + ")
and
t.category_id = c.id
and t.url_meta_data_id = u.id
and u.ugc_flag != 1 " + addDomain + "
order by tweight desc
limit " + resultLimit.toLong()
/*
* run query
*
*/
Sql sqlInstance = new Sql( dataSource )
def resultsList = sqlInstance.rows( getUrlsQuery )
}
The tables are as follows (dummy data):
[Category]
id | name
-----------
1 | small car
2 | bike
3 | truck
4 | train
5 | plane
6 | large car
7 | caravan
[Target]
id | cid | weight | url_meta_data_id
----------------------------------------
1 | 1 | 56 | 1
2 | 1 | 76 | 2
3 | 3 | 34 | 3
4 | 2 | 98 | 4
5 | 1 | 11 | 5
6 | 3 | 31 | 7
7 | 5 | 12 | 8
8 | 4 | 82 | 6
[url_meta_data]
id | url | ugc_flag
---------------------------------------------
1 | http://www.example.com/foo/1 | 0
2 | http://www.example.com/foo/2 | 0
3 | http://www.example.com/foo/3 | 1
4 | http://www.example.com/foo/4 | 0
5 | http://www.example.com/foo/5 | 1
6 | http://www.example.com/foo/6 | 1
7 | http://www.example.com/foo/7 | 1
8 | http://www.example.com/foo/8 | 0
domain classes
class Category {
static hasMany = [targets: Target]
static mapping = {
cache true
cache usage: 'read-only'
targetConditions cache : true
}
String name
String source
}
class Target {
static belongsTo = [urlMetaData: UrlMetaData, category: Category]
static mapping = {
cache true
cache usage: 'read-only'
}
int weight
}
class UrlMetaData {
String url
String ugcFlag
static hasMany = [targets: Target ]
static mapping = {
cache true
cache usage: 'read-only'
}
static transients = ['domainName']
String getDomainName() {
return HostnameHelper.getBaseDomain(url)
}
}
Basically, a url from url_meta_data can be associated to many categories. So in essence what I'm trying to achieve should be a relatively basic operation...to return all the urls for the search-term 'car', their weight(i.e importance) and where the ugc_flag is not 1(i.e the url is not user-generated content). There are 100K + of records in the db and these are imported from a third-party provider. Note that all the URLs do belong to my client - not doing anything dodgy here.
Note the rlike I've used in the query - I was originally using ilike %searchterm% but that would find categories where searchterm is part of a larger word, for example 'caravan') - unfortunately though the rlike is not going to return anything if the user requests 'cars'.
I edited the code - as Igor pointed out the strange inclusion originally of 'domainName'. This is an optional parameter passed that allows the user to filter for urls of only a certain domain (e.g. 'example.com')
I'd create an empty list of given domain objects,
loop over the resultsList, construct a domain object from each row and add it to a list of those objects. Then return that list from controller to view. Is that what you're looking for?
1) If it's a Grails application developed from a scratch (rather than based on a legacy database structure) then you probably should already have domain classes Category, Target, UrlMetaData (otherwise you'll have to create them manually or with db-reverse-engineer plugin)
2) I assume Target has a field Category category and Category has a field UrlMetaData urlMetaData
3) The way to go is probably http://grails.org/doc/2.1.0/ref/Domain%20Classes/createCriteria.html and I'll try to outline the basics for your particular case
4) Not sure what theDomain means - might be a code smell, as well as accepting rlike arguments from the client side
5) The following code hasn't been tested at all - in particular I'm not sure how disjunction inside of a nested criteria works or not. But this might be suitable a starting point; logging sql queries should help with making it work ( How to log SQL statements in Grails )
def c = Target.createCriteria() //create criteria on Target
def resultsList = c.list(max: resultLimit.toLong()) { //list all matched entities up to resultLimit results
category { //nested criteria for category
//the following 'if' statement and its body is plain Groovy code rather than part of DSL that translates to Hibernate Criteria
if (searchTerms) { //do the following only if searchTerms list is not empty
or { // one of several conditions
for (st in searchTerms) { // not a part of DSL - plain Groovy loop
rlike('name', st.trim())) //add a disjunction element
}
}
}
urlMetaData { //nested criteria for metadata
ne('ugcFlag', 1) //ugcFlag not equal 1
}
}
order('weight', 'desc') //order by weight
}
Possibly the or restriction works better when written explicitly
if (searchTerms) {
def r = Restrictions.disjunction()
for (st in searchTerms) {
r.add(new LikeExpression('name', st.trim()))
}
instance.add(r) //'instance' is an injected property
}
Cheers,
Igor Sinev