GORM criteria group by in subquery - mysql

Let's assume the following Grails domains:
Owner {
String name
static hasMany [cars: Cars]
}
Car {
Date inspectionDate
}
I want to be able to search for Owners through Criteria, with the following rule: Most recent Car by inspectionDate in Owner's cars list being lower than *given date*.
As an example, I want to apply the following code to a select query in GORM:
queryResultsList = allOwnersList.filter { owner ->
owner.cars.min{ car -> car.inspectionDate }.inspectionDate < myDate
}
I need to achieve it using Criteria because I am already filtering Owners on other fields.
The whole given code is used as an example, some parts of the original code has been ommited, and source code is not about cars and owners.
As on first thought I assumed I needed a subquery in SQL to retrieve my data as I expected, I tried the following:
Owner.createCriteria().list {
// [...] some filters on other fields
cars {
lt('inspectionDate', params.inspectionDate)
'in'('inspectionDate', new grails.gorm.DetachedCriteria(Owner).list {
projections {
cars {
min('inspectionDate')
}
}
})
}
}
I also tried to add groupProperty in different places in the projection, ending with MissingPropertyException.
I am using Grails 2.2.4

After a few days testing solutions, I've come to the following one:
Owner.createCriteria().list {
// [...] some filters on other fields
cars {
lt('inspectionDate', params.inspectionDate)
'in'('inspectionDate', Owner.createCriteria().list {
projections {
cars {
min('inspectionDate')
}
groupProperty 'id'
}
// this allows to only get first property, which is inspectionDate
}.collect { it[0] })
}
}
However, I still feel like doing things wrong. In particular, the collect part looks like a code smell.
I am posting this because it does what I need, but any help with finding a good solution will be appreciated.

Related

How to get all relation table with subquery

I need to make a query where I can get "Place" table, but I have to exclude from that query all "Place" that are already part of the "LocationGroupPlace" table.
I have a class called Place and other called LocationGroupPlace
each LocationGroupPlace can contain many Places
these are the relations
in Place.php file
public function locationGroupPlace()
{
return $this->hasMany(LocationGroupPlace::class, 'place_id');
}
in LocationGroupPlace.php file
public function place(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Place::class);
}
I am trying to retrieve 5 items from "Place" that do not exist in "LocationGroupPlace", that is, the IDs of "Place" are not in the table of "LocationGroupPlace", for example these IDs
I'm trying to get the first 5 Place with all the table from LocationGroupPlace with a subquery (for test my subquery)
public function getUsedPlacesForGroups()
{
return Place::with(['locationGroupPlace' => function ($query){
$query->get();
}])->limit(5)->get();
}
but I can't get LocationGroupPlace, the relations section is empty
Note: locationGroupPlace is the function in Place
How can I get all LocationGroupPlace? my final idea is to exclude all "Places" included in "LocationGroupPlace"
If you want the places that doesn't exist in LocationGroupPlace then you can use doesntHave
$places= Place::doesntHave('locationGroupPlace')->limit(5)->get();
See Querying Relationship Absence

Apollo-boost caching issue

I'm experiencing an issue with apollo client caching, and I'm not sure I understand why. I'm building an angular 6 app, using apollo-boost, all is working well. I have a scenario now where my a graphql query takes an id (of a user) and a filter string (used to filter records on the backend). The angular component code looks like:
ngOnInit() {
this.filter$.subscribe(filterValue => {
this.route.params.subscribe(this.getAppointments.bind(this, filterValue));
});
}
The getAppointments function looks like:
getAppointments(filter: string, params: {id: string}) {
this.artistAppointmentBookGQL.watch({artistId: user.artist._id, filter}).valueChanges
.pipe(
map(results => {
// THIS ALWAYS RUNS WHEN THE FILTER CHANGES
// HOWEVER THE RESULTS ARE ALWAYS THE LAST QUERY RUN
// IF THE FILTER HAS BEEN RUN BEFORE
console.log(user.artist._id, filter, results.data.artist.appointmentBook);
return results.data.artist.appointmentBook;
}));
}
The graphql query:
query artistAppointmentBook($artistId: ID!, $filter: String) {
artist(id: $artistId, appointmentType: $filter) {
_id
appointmentBook {
_id
created_at
firstName
lastName
date
price
stripe {
charge {
id
amount
}
}
}
}
The main issue:
I have 4 different possible filter values (all, unconfirmed, confirmed, paid). When I run these queries with each filter value, it works as expected, and I get back the proper result sets from the apollo server. However, as soon as I run the same query twice, I only get back the result of whatever the last query was, and no network call is made, presumably because it's using a cached version.
Shouldn't the cache be based on the variable inputs? It seems to run fine the first time I run with different variables, but as soon as one gets duplicated I only get back whatever the last call yielded. Thanks for any help!
This gif demonstrates the issue:
Figured out my issue. As it should, apollo is caching the artist record because it has an _id and a type. The filter was being passed into artist query, when it should have been passed in at the appointmentBook level. I updated my schema to accept the filter param and then passed it in there instead of into the artist query.
Originally I had:
query artistAppointmentBook($artistId: ID!, $filter: String) {
artist(id: $artistId, appointmentType: $filter) {
_id
appointmentBook {
_id
created_at
firstName
lastName
date
price
stripe {
charge {
id
amount
}
}
}
}
Which needed to be changed to:
query artistAppointmentBook($artistId: ID!, $filter: String) {
artist(id: $artistId) {
_id
appointmentBook(filter: $filter) {
_id
created_at
firstName
lastName
date
price
stripe {
charge {
id
amount
}
}
}
}
After this update, the queries are cached properly.

CouchDB / Cloudant get another object using reference

We are using two objects: Employee and Company
In Employee object contains a reference to Company (idcompany). How can I get the Employee object with the Company fields too? (Using a view or search index)
Example:
employee
{
"name":"test",
"idcompany":"4a70356d1a99260f3b9fd565a10e5ece",
"objecttype":"employee"
}
company
{
"id":"4a70356d1a99260f3b9fd565a10e5ece",
"name":"test",
"objecttype":"company"
}
You can try the Join With Views proposal
You can define view with the following map function
function (doc) {
if (doc.idcompany) {
emit(doc._id, {"_id":doc.idcompany});
}
}
Then retrieve the view values using the include_docs=true param. You will get the key that you defined in the emit function, joined with the company document.

reactivemongo - merging two BSONDocuments

I am looking for the most efficient and easy way to merge two BSON Documents. In case of collisions I have already handlers, for example if both documents include Integer, I will sum that, if a string also, if array then will add elements of the other one, etc.
However due to BSONDocument immutable nature it is almost impossible to do something with it. What would be the easiest and fastest way to do merging?
I need to merge the following for example:
{
"2013": {
"09": {
value: 23
}
}
}
{
"2013": {
"09": {
value: 13
},
"08": {
value: 1
}
}
}
And the final document would be:
{
"2013": {
"09": {
value: 36
},
"08": {
value: 1
}
}
}
There is a method in BSONDocument.add, however it doesn't check uniqueness, it means I would have at the end 2 BSON documents with "2013" as a root key, etc.
Thank you!
If I understand you inquiry, you are looking to aggregate field data via composite id. MongoDB has a fairly slick aggregate framework. Part of that framework is the $group pipeline aggregate keyword. This will allow you to specify and _id to group by which could be defined as a field or a document as in your example, as well as perform aggregation using accumulators such as $sum.
Here is a link to the manual for the operators you will probably need to use.
http://docs.mongodb.org/manual/reference/operator/aggregation/group/
Also, please remove the "merge" tag from your original inquiry to reduce confusion. Many MongoDB drivers include a Merge function as part of the BsonDocument representation as a way to consolidate two BsonDocuments into a single BsonDocument linearly or via element overwrites and it has no relation to aggregation.
Hope this helps.
ndh

Best way to perform filtering on view data in couchbase

What would be the best way to filter records from a view based on tags in couchbase? I mean I understand that I can extract tags from the documents and use them as the keys using the map functionality in the view. But is there a way to select records from the view based on whether the record's key (which can be multiple values) contains a tag?
Example: I have a transaction that has the following tags (tag 1, tag2, tag3, tag4) and each view record (from the emit function) looks like this
emit ([tag1,tag2,tag3,tag4], null)
What I want to get is all the records that have tag2.
Thanks for any help.
Two suggestions... If the only purpose of the view is to find records with a particular tag, you could simply emit a view row for each tag.
function (doc) {
if (doc.tags) {
for(var idx in doc.tags) {
emit(doc.tags[idx], null);
}
}
}
This map function would give you rows as follows:
{"total_rows":13,"rows":[
{"id":"foo_doc_1","key":"a","value":null},
{"id":"foo_doc_3","key":"a","value":null},
{"id":"foo_doc_1","key":"b","value":null},
{"id":"foo_doc_2","key":"b","value":null},
{"id":"foo_doc_3","key":"b","value":null},
{"id":"foo_doc_1","key":"c","value":null},
{"id":"foo_doc_2","key":"c","value":null},
{"id":"foo_doc_1","key":"d","value":null},
{"id":"foo_doc_4","key":"d","value":null},
{"id":"foo_doc_4","key":"e","value":null}
]
}
When you query by key (e.g., key="a") you'll get the results for that key. If however, your concern is "give me all documents where the second tag is 'xyz'" then you could emit the index as well.
function (doc) {
if (doc.tags) {
for(var idx in doc.tags) {
emit([parseInt(idx), doc.tags[idx]], null);
}
}
}
In this example, the rows would like like:
{"total_rows":13,"rows":[
{"id":"foo_doc_1","key":[0,"a"],"value":null},
{"id":"foo_doc_3","key":[0,"a"],"value":null},
{"id":"foo_doc_2","key":[0,"b"],"value":null},
{"id":"foo_doc_4","key":[0,"d"],"value":null},
{"id":"foo_doc_1","key":[1,"b"],"value":null},
{"id":"foo_doc_3","key":[1,"b"],"value":null},
{"id":"foo_doc_2","key":[1,"c"],"value":null},
{"id":"foo_doc_4","key":[1,"e"],"value":null},
{"id":"foo_doc_1","key":[2,"c"],"value":null},
{"id":"foo_doc_4","key":[2,"f"],"value":null}
]
}
You would then query with an array key to get a key at a particular position (e.g., key=[1, "b"]).