Retrieve active childs in one to many association in grails - grails-2.0

I have below classes
class Audit {
Status status = Status.ACTIVE
}
class Book{
String name;
Audit audit
static embedded = ['audit']
static belongsTo = [author:Author]
}
class Author{
String name;
Audit audit
List books
static embedded = ['audit']
static hasMany = [books:Book]
}
I want to retrieve active books for the author. Can you suggest me the query for this?
I am trying to add method in Author like below
List<Book> getActiveBooks(){
}
or If there is any other way to retrieve my books.. as active books only.
Please help me out
Following are the tables with columns
Book
name, audit_status, author_id
Author
name, audit_status

Check out the CriteriaBuilder.
You can use it to search on the status of the author (see "Querying Associations" section).
Use this together with projections (see "Querying with Projections") to get a list of books.

One way of doing this is to add a namedQueries block in Author and then you can call
def activeBooks = Author.getActiveBooks().list()
Here is the Author class namedQueries
class Author {
static namedQueries = {
getActiveBooks {
books {
audit{
eq 'status', Status.ACTIVE
}
}
}
}
}
I am assuming Status is a domain object too. If so, GORM will match the id of the status object.
Please double check the syntax.

Related

Extending SELECT projection

I want to extend the documents that I receive from a SELECT clause.
Lets assume a I have a collection that stores documents in the following shape
{"foo": "yeah I am a foo", "bar": "And I am a bar"}
so that the query
SELECT * FROM f
would return the above document(s)
Now I want to add an additional property that is NOT part of the documents stored as part of the projection of the SELECT statement.
Basically I'd like to do something like using Javascript's spread operator (which is not possible in Cosmos DB)
SELECT {...*, "newprop": "oh! I am new here!"} FROM f
and which should then return document(s) like this
{"foo": "yeah I am a foo", "bar": "And I am a bar", "newprop": "oh! I am new here!"}
The one thing I DONT WANT TO DO is to repeat all the toplevel properties of my documents. So a solution in the form of
SELECT {"foo": f.foo, "bar":f.bar, "newprop": "oh! I am new here!"} FROM f
is not desired.
I also tried to get that done via a function. Which I was not able to do as I cant find out how to get the toplevel object / document handle within the SELECT clause.
I tried the following
SELECT udf.ExtendDocument(*) FROM f
SELECT udf.ExtendDocument($1) FROM f
SELECT udf.ExtendDocument(f) FROM f
SELECT udf.ExtendDocument(value) FROM f
most of which produced a syntax error
It's not possible to use SELECT *, then append columns to the projection.
One option you could explore is to add a static property and value to the class that you deserialize your data into.
For instance, you could create a class like this simple one for a person with a hardcoded property and default value. Then deserialize your query results into it with the static value added as another property with a default value.
class Person
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "pk")]
public string Pk { get; set; }
[JsonProperty(PropertyName = "firstName")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "lastName")]
public string LastName { get; set; }
public string MyStaticColumn get; set; } = "Default Value";
}
Then the code to run the query...
public static async Task QueryPerson(Container container)
{
QueryDefinition query = new QueryDefinition("select * from c");
FeedIterator<Person> resultSet = container.GetItemQueryIterator<Person>(
query, requestOptions: new QueryRequestOptions()
{
MaxConcurrency = -1
});
List<Person> results = new List<Person>();
while (resultSet.HasMoreResults)
{
FeedResponse<Person> response = await resultSet.ReadNextAsync();
foreach(var p in response)
{
results.Add(p);
}
}
}
So I found a solution.
A) Build a user defined function that does the "Extension"
function extendProjection(x) {
var result = {
//usually one want to extend the returned doc
//with some new calculated properties and not
//with a static value
newprop: calculateNewPropFromObject(x)
}
return Object.assign(result, x)
}
B) Use the user defined function in your SELECT
SELECT VALUE udf.extendProjection(c) FROM c
//it is important to use the keyword "VALUE" eitherwise
//the resulting doc will look {$1: { //the extendedprojection }}
Having described that I would recommend against this approach
Your RUs will easily tripple. The reason seems to be the usage of the JS itself and not so much what the JS engine does.
its not possible to "reuse" different registered UDFs within your JS code.
So one has to copy code snippets
"Extended Properties" are not useable in your WHERE clause
Runtime error messages returned from Cosmos DB are horrible to decipher.
The lack of any decent development environment is basically a no go.
Like #mark-brown already answered, it does not seem to be possible.
I would just like to add that likely you shouldn't do that anyway and offer a workaround arguably better than the UDF (which is costly, hard-to-maintain, does not support multiple concurrent logic versions, etc).
If you want to add extra calculations to query output based on the same entire document, then it would make more sense to do it in business layer (after querying), not data layer (CosmsosDB queries). It would also be faster (less calculations, less data to move) and cheaper (less RU).
If you want to add static data (ex: a fix string or other constants), then the same argument applies - passing it back-and-forth to cosmosDB just makes things slower and costlier. That's not the responsibility of storage.
The workaround
If the goal is to query an entire CHILD object and add only a few selected properties from other areas of documents then its best not to try to flatten the object. Just keep your storage model objects and extras side-by-side, ex:
select c.childWithAllTheFutureChildren,
c.other.location.single.value as newProp
from c
If you really-really want to add some calculation/statics to query output then you could also still use the same pattern for entire document:
SELECT c as TheRealStoredThing,
'oh! I am new here!' as theNewProp
FROM c
Yes, it does require you to have a separate model on client side for this query, but that's a good clean practice anyway. And it's much simpler than using/maintaining UDFs.

Ref<?> modify a field from object and save it when saving containing object

I am using Objectify for Google Cloud Datastore.
I am looking into Ref<?> and tried to see if I can modify a property from the object with this annotation but it doesn't seem to be saved in my datastore.
Example:
I have these classes, I'll exlude setters and getters.
#Entity
class Car {
#Id Long id;
#Load Ref<Person> driver; // Person is an #Entity
}
class Person {
#Id Long id;
String name;
}
If I do this
Car car = new Car();
car.driver = Ref.create(driverKey);
ofy().save().entity(car).now();
Car fetched = ofy().load().entity(car).now();
fetched.driver.get().setName("Pepito");
ofy().save().entity(car).now();
It won't change the name of the Person in the database.
Is there any way to achieve this?
Thanks.
References are just that - references to separate entities with separate lives. There is no cascading save. If you want to save the driver, do it explicitly.

Designing XAF Business Objects

I am developing a DevExpress XAF application that has the following business objects
Employee
Warehouse
​Applicant
Each of these entities can have one or more Email Addresses
How can I accomplish this using a single EmailAddress Business Object​?
You can introduce a base persistent class with a collection of EmailAddress objects (http://documentation.devexpress.com/#Xaf/CustomDocument2733) and then inherit your other classes from it.
As an alternative to inheritance, you can use aggregation.
However, I am not quite sure exactly what you mean by "using a single EmailAddress Business Object​", may be you do not need a collection property as I suggested above.
P.S.
To get a guaranteed and fast assistance with DevExpress products, please use the official Support Center (http://www.devexpress.com/Support/), access to which is free during the evaluation period.
" Each of these entities can have one or more Email Addresses"
Going by the statement above, what I could make out is, you got Employee as a business object and these entities can have multiple EmailIds. What I would suggest is you create EmailId as another business object with only one property EmailIdand have a 1 -to- * relationship between your Employee and EmailId business objects. Do not forget to hide your business object EmailId from navigation items. EmailId should only be visible to users creating new Employee objects and otherwise it should not be allowed to be created, independent of any relation.
I know this is a very late answer to a question, but let us know how you solved your problem. By doing this, you can help others facing a similar situation that you were in.
Thanks.
Create a email class with no link back to any other class. Then create the class that needs to have a collection of emails. Now you have two choices. You can add more than one back link to the email class with the right association for each parent class or you can create separate child (email) classes for each parent. The child will inherit from email and only add the back link to the parent class.
You can use this type; (I use XPLiteObject if you want to auto generate key you can use XPObject)
public class Person : XPLiteObject
{
public Person(Session session)
: base(session)
{
}
public override void AfterConstruction()
{
base.AfterConstruction();
}
[Association("PersonEmails", UseAssociationNameAsIntermediateTableName = true)]
public XPCollection<Email> Emails
{
get
{
return GetCollection<Email>("Emails");
}
}
}
public class Email : XPLiteObject
{
public Email(Session session)
: base(session)
{
}
public override void AfterConstruction()
{
base.AfterConstruction();
}
private Person person;
[Association]
public Person Person
{
get { return person; }
set { SetPropertyValue("Person", ref person, value); }
}
}

BreezeJs Fails to Load Metadata for EF Code First Schema with Inherited Class

Just started trying out Breeze today, long time EF user - think I found a bug in Breeze, but I may be doing something wrong - want to know which it is:
I have a simple hierarchy in EF Code First:
// For testimonials about the product line in general
public class Testimonial
{
public int Id { get; set; }
public string Text { get; set; }
}
// For testimonials specific to a single product
[Table("ProductTestimonial")]
public class ProductTestimonial : Testimonial
{
public Product Product { get; set; }
}
So just to be clear there's 2 tables here, Testimonial and ProductTestimonial, both have a PK of Id, and a Text field, and one of them also has an FK off to Product. This is just a simple way of implementing Table Per Type.
So I setup the BreezeController via WebApi:
[BreezeController]
public class EFController : ApiController
{
private readonly EFContextProvider<EfDb> _db = new EFContextProvider<EfDb>();
[HttpGet]
public string Metadata()
{
return _db.Metadata();
}
And I go to load it in breeze.js:
var manager = new breeze.EntityManager('/api/ef');
manager.executeQuery(breeze.EntityQuery.from('Product');
Kablamo. Exception. It says:
Unable to get value of the property 'propertyRef': object is null or undefined
At:
function convertFromODataEntityType(... {
. . .
var keyNamesOnServer = toArray(odataEntityType.key.propertyRef)...
Where odataEntityType.name == 'ProductTestimonial', and .key sure enough is undefined.
Which is true. Picking things apart, when I call executeQuery(), Breeze hits the Metadata call on the WebApi, which I verified calls and returns successfully. The massive JSON string returned from there includes:
{
"name": "ProductTestimonial",
"baseType": "Self.Testimonial",
"navigationProperty": {
"name": "Product",
"relationship": "Self.ProductTestimonial_Product",
"fromRole": "ProductTestimonial_Product_Source",
"toRole": "ProductTestimonial_Product_Target"
}
},
{
"name": "Testimonial",
"key": {
"propertyRef": {
"name": "Id"
}
},
So it would appear the basic issue is that the Metadata is accurately portraying ProductTestimonial as an inherited class, whose key is defined elsewhere, but Breeze.js - if I'm understanding it correctly - is naively just checking the .key property without considering superclasses. But, I could be wrong since I'm so new to Breeze.
Addendum:
I don't think it's relevant here but in case it comes up, yes I do have an IQueryable as well on the WebApi Controller:
[HttpGet]
public IQueryable<Product> Products()
{
return _db.Context.Products;
}
Also, I recognize a potential workaround here is probably to discard TPT and make full classes for every Entity with no inheritance, but I'm really slimming my example down here - there's a lot of inheritance throughout the EF model that has to stay. If it's inheritance or Breeze, Breeze is getting the axe.
Edit: As of v 1.3.1 Breeze now DOES support inheritance.
Inheritance is coming but we don't have a fixed date just yet. Please vote for the feature on the Breeze User Voice. We take these suggestions very seriously.
I don't think manager.fetchEntityByKey works with inheritance - can any of the Breeze guys confirm whether this is correct? It seems to be picking up the inherited fields from my base class, but not the fields from my derived class. I can get the full object by using entityQuery but then I have to name every field. When I do that I'm still getting parse errors on ko bindings.

NHibnernate SQL Lite in memory unit testing doesn't work the same as a physical db

I am in the process of building a "get to know NHibernate project).
My unit tests (nunit) provide the repository layer the same configuration as the normal mvc site but using "SQLiteConfiguration.Standard.InMemory()" instead of "MsSqlConfiguration.MsSql2008". My hope is that I can use fluent nhibernate and automapping exactly the same way but have a quick in memory db for tests and a real db for the app.
In the repository constructor it creates the Session object for all queries to use and also implements IDisposable which will ensure the session is dealt with correctly. For example a save article method would look like:
public void SaveArticle(Article article)
{
Session.SaveOrUpdate(article);
Session.Flush();
}
So far the MVC site all seems to work correctly, and my tests are passing. I think I have come across an issue that must be a problem with my design.
So far I have two objects:
class Article
{
public virtual int Id {get; set; }
public virtual IList<Tag> Tags {get; set; }
}
class Tag
{
public virtual int Id {get; set; }
public virtual string Text {get; set; }
}
I only want the DB to store ONE instance of each tag. So many articles could use the same tag. So I wrote this test:
[Test]
public void TestDeletingTagWillNotDeleteOthersTagV2()
{
Article article = new Article();
article.Tags.Add(new Tag { Text = "C#" });
article.Tags.Add(new Tag { Text = "VB" });
Service.SaveArticle(article);
Article article2 = new Article();
article2.Tags.Add(new Tag { Text = "C#" });
Service.SaveArticle(article2);
Guid article1Id = article.Id;
Guid article2Id = article2.Id;
article = null;
article2 = null;
Article article3 = Service.GetArticle(article1Id);
Article article4 = Service.GetArticle(article2Id);
Assert.AreEqual(2, article3.Tags.Count);
Assert.AreEqual(1, article4.Tags.Count);
Assert.AreEqual(2, Service.Tags.Count);
article3.Tags.RemoveAt(0);
Service.SaveArticle(article3);
Article article5 = Service.GetArticle(article1Id);
Article article6 = Service.GetArticle(article2Id);
Assert.AreEqual(1, article5.Tags.Count);
Assert.AreEqual(1, article6.Tags.Count);
Assert.AreEqual(2, Service.Tags.Count);
}
This test passes, but I believe it should fail. (it does when you run the MVC site i.e. the first article only has one tag, "VB" not "C#" and "VB".
I believe its because as the session is still open nhibernate is still holding onto everything and remembers what there was.
My main question is how do I test this with the in memory db? How do I ensure that after the saves and removal of a shared tag the articles still have what they should have?
The table structure it has created is, (which I feel is wrong, should have a link table):
Article:
Id (PK, uniqueid, not null)
...........
Tag
Id (PK, uniqueid, not null)
Text (nvarchar, null)
Article_id (FK, uniqueid, null)
Not sure how to use the fluent automapping to setup a link table so that one tag can be shared across multiple Articles and one article can have many tags
Believe I found how to ensure that the Tags and articles have a link table:
configuration.Mappings(m => m.AutoMappings.Add((AutoMap.AssemblyOf<Article>())
.Override<Article>(map => map.IgnoreProperty(x => x.IsPublished))
.Override<Article>(map => map.HasManyToMany(a => a.Tags).Cascade.All())
.Conventions.Add(DefaultCascade.All()))
if you only want unique tags (Text is unique) then make the text unique and use the session to get the unique instances. For NHibernate tags with the same text but different Ids are different.
// do
article.Tags.Add(session.Get<Tag>(cSharpId));
// or
article.Tags.Add(session.Query<Tag>().Where(t => t.Text == "C#"));
// instead of
article.Tags.Add(new Tag { Text = "C#" });
also micromanaging the session (Flush) will degrade performance. Your test method already does 9 - 13 db calls (depending on lazyloading settings).
Instead of Session.Flush(); in the service method at least introduce a SaveChanges() which calls Session.Flush(); so Inserts/Updates/Deletes can be batched properly.

Resources