So imagine the following:
Company A is owned by Company B { intermediate = true }
which is owned by Company C { intermediate = false }
which is owned by Company D { intermediate = false }
Given that I'm at "Company A" I want to get to "Company C" without returning "Company D"
(I want the first Company that isn't a intermediate.)
There's also the following scenario:
Company Foo is owned by Company Bar { intermediate = false }
Company Foo is owned by Company Baz { intermediate = true }
Company Baz is owned by Company Das { intermediate = false }
In this case, it should return both "Company Bar" and "Company Das" because they're both owners of "Company Foo".
This should work:
MATCH p = (:Company {name: "Foo"})-[:IS_OWNED_BY*]->(first:Company {intermediate: false})
WHERE NONE(n IN NODES(p)[1..-1] WHERE NOT n.intermediate)
RETURN first
The WHERE clause tests that none of the nodes in between the start and end nodes have a false intermediate value.
Note: If there can be many long IS_OWNED_BY paths, you should consider putting a reasonable upper bound on the variable-length relationship pattern to avoid taking too long or running out of memory. For example, [:IS_OWNED_BY*..6].
Related
I want to search from my domain class with ElasticSearch, but it's a nested query and the match, that I want, is three levels deep.
Only the domain class that I'm searching from is set as root, all others are set as components and not root for searchable configuration.
When I do the search it gives me matches, but the count is wrong and there are totally wrong matches inside there.
I should get 4000 matches, but I get 20000 matches and I have no clue why it doesn't work as expected.
When I started fiddling around, I changed the mappings between my domain classes and it then gave me correct matches, but the thing is that I do not want to do this kind of changes in my prod environment.
I'm going to give two examples. The first one shows my domain classes' relations currently (the one that doesn't work) and the other one is with the change, that made the search work correctly.
Not correctly working example
class A {
String id
String name
B b
static searchable = {
b component: true
}
}
class B {
String id
String name
C c
static searchable = {
root false
c component: true
}
}
class C {
String id
String name
static belongsTo = [d: D]
static searchable = {
root false
d component: true
}
}
class D {
String id
String name
static searchable = {
root false
}
}
Correctly working example
class A {
String id
String name
B b
static searchable = {
b component: true
}
}
class B {
String id
String name
C c
static searchable = {
root false
c component: true
}
}
class C {
String id
String name
D d
static searchable = {
root false
d component: true
}
}
class D {
String id
String name
static searchable = {
root false
}
}
As you can see the only difference in relations is that in the first example class D belongs to class C and in the second example class D is just a field in class C. In the database D is referenced in C exactly the same in both cases.
The search closure that I created looks like:
bool {
must {
nested {
path = "b"
query {
nested {
path = "b.c"
query {
path = "b.c.d"
query {
bool {
must {
match("b.c.d.id": "142342342342")
}
}
}
}
}
}
}
}
}
Do I really have to change my relations for the domain classes to make the search work or am I just doing the search wrong?
What could cause the issue in general?
EDIT
ElasticSearch mapping for the field "d" inside "c", is exactly the same in both cases:
"c":{
"type":"nested",
"properties":{
"class":{
"type":"string"
},
"dateCreated":{
"type":"date",
"format":"strict_date_optional_time||epoch_millis",
"include_in_all":true
},
"d":{
"type":"nested",
"properties":{
"class":{
"type":"string"
},
"id":{
"type":"string"
},
"name":{
"type":"string",
"term_vector":"with_positions_offsets",
"include_in_all":true
}
}
}
EDIT 2
So it seems that the problem was not rooted in the mappings, but rather that when doing a search with at least three levels of nested objects, then ElasticSearch wasn't able to correctly find the matches with the word match. I got it working with match_phrase.
So important lesson, when searching through for example ids and you your query has multi level nesting and you want an exact match, then one should use match_phrase!
I suggest to use match_phrase over match.
match_phrase query will analyze the input if analyzers are defined for the queried field and find documents matching the following criteria :
all the terms must appear in the field
they must have the same order as the input value
I have an app with the following entities:
Topic:
class Topic {
UUID id
String description
String name
boolean visibility = true
// Relation
static hasMany = [tests:Test]
...
}
Test:
class Test {
UUID id
boolean active = true
String description
...
static hasMany = [evaluationsTest: Evaluation]
static belongsTo = [topic: Topic, catalog: Catalog]
}
When I show all visible topics to the user I request the query:
def visibleTopics = Topic.findAllByVisibility(true, [sort:"name", order:"asc"])
This query returns me for example: [['English'], ['Spanish']]. Then, I can show the full information about each topic to the user.
But I also want to indicate to the user the number of active test in each visible topic.
For example:
English topic has 2 active test.
Spanish topic has a total of 2 test. One is active and the other is not.
German topic has not any active test.
Then I need a query whose result is: def activeTotalEachTopic = [[2],[1],[0]] and I can pass the activeTotalEachTopic variable to the view (.gsp).
Solution:
From the first query where I can obtain all visible topics, I get the number of active test.
def visibleTopics = Topic.findAllByVisibility(true, [sort:"name", order:"asc"])
def numberActiveTest = []
activeTopics.each { topic ->
def result = Test.findAllByTopicAndActive(topic, true).size()
numberActiveTest.push(result)
}
And I pass to the view both variables.
render view: 'home', model: [activeTopics: activeTopics, numberActiveTest: numberActiveTest]
What you are missing is grouping so that you get the count per group, rather than a total count.
You also need to change the join type from the default inner join to an outer join in order for topics without an active test to return 0. However, a side-effect of this is that it changes how association properties are referenced due to the alias that's created by the join. Something like this:
import static org.hibernate.sql.JoinType.*
def activeTotalEachTopic = Topic.createCriteria().list() {
createAlias('tests', 't', LEFT_OUTER_JOIN)
eq 'visibility', true
or {
eq 't.active', true
isNull 't.id'
}
projections {
groupProperty 'name'
count()
}
order ("name", "asc")
}
Now, another issue to address is that the output would be something like this due to the grouping: [['English', 2],['Spanish', 1],['German', 0]]. So what you can do is collect the second item in each sub-list:
activeTotalEachTopic*.getAt(1)
// Which is the equivalent of...
activeTotalEachTopic.collect { it[1] }
I have a domain class named Logging which stores an id of another domain class: Organization
The structure of both domains is provided:
class Logging {
Date dateCreated
long user_id
long organization_id
String memberCode
static constraints = {
user_id(nullable: false)
organization_id(nullable: false)
memberCode(nullable: true)
}
}
class Organization {
Type type
String name
String memberCode
User manager
String collateralAutoEmails
boolean isBlocked = true
static constraints = {
name(blank: false, unique: true)
manager(nullable: true)
memberCode(nullable: true)
collateralAutoEmails(nullable: true)
}
static mapping = {
manager(lazy: false)
}
}
User enters several parameters: dateCreated, the memberCode and the name of the organization. I need to select all elements from the Logging domain matching these criterias.
The tricky part for me is writing the query for the name of the organisation parameter.
According to the search rules I should check whether organization.name field contains data entered by user as a substring(case insensetive) and select the corresponding element from the Logging domain.
The two domains are not mapped directly and I can't join those tables.I have tried different approaches but still haven't found the solution.
Here you go
Logging.executeQuery("Select l from Logging l, Organization o where l.organization_id = o.id and o.dateCreated = :dateCreated and o.memberCode = :memberCode and o.name = :name", [dateCreated: dateCreated, memberCode: memberCode, name: name])
Try something like this:
Organization.executeQuery("select o from Organization o, Logging l where o.name like = :orgName AND o.id=l.organization_id", [orgName : orgName ])
I didn't tried it, if it works then more search options can be added on the query, and also % can be added on the parameter, in order to enhance the search.
My domain contains a one-to-many relationship like this:
class A {
B b
}
class B {
String name
}
I want to create a criteria query on A which will look for A to have the B object with the given name. It may return multiple entries. So... compare a given string with the "name" field from B and return the list of entries of type A for which B matches the name.
Thx!
Unless I am missing something that should be pretty easy:
def instanceList = A.withCriteria {
b {
eq('name','whatever')
}
}
I have 2 domain classes
class A {
....
static hasMany = [bs:B]
}
class B {
int code
....
}
How can I list the number of ocurrences of all codes in B in all the A table?
Something like
b.each { thisb ->
int ocurrences = A.bs.findAll{it == thisb}.size()
...
}
Thanks
I think the reason that I'm a little confused by this question is that technically it's actually a many-to-many relationship, not really a one-to-many. Grails will create a join table ("a_b") for this relationship (because B doesn't have a belongsTo relationship to A).
The way you have your A domain constructed the hasMany relationship is a set, so B will only appear a single time in the "bs" collection. So, I believe, all you're asking is how many As have a B.
If thats true, you can use HQL to answer your question (you can also use criteria builders, but I prefer hql). Here's an example (using the build-test-data plugin to construct objects with buildLazy and adding a String name to A):
def a1 = A.buildLazy(name: "one")
def a2 = A.buildLazy(name: "two")
def a3 = A.buildLazy(name: "three")
def a4 = A.buildLazy(name: "four")
def b1 = B.buildLazy(code: 888)
def b2 = B.buildLazy(code: 999)
a1.addToBs(b1)
a2.addToBs(b1)
a3.addToBs(b1)
a4.addToBs(b1)
a1.addToBs(b2)
println "Number of As that have each B = " +
A.executeQuery("select count(b), b.code from A as a join a.bs as b group by b.code")
println "Number of As with a specific B = " +
A.executeQuery("select count(*) from A as a join a.bs as b where b = :b", [b: b1])
results in:
Number of As that have each B = [[1, 999], [4, 888]]
Number of As with a specific B = [4]