I want to traverse a graph using match, but stop the traversal when a certain node is not connected to another specific node.
To clarify, I have the following setup:
Setup of graph
My goal is to perform the following query:
For a wall, give me all linked/deep properties of that wall that are part of the same commit as the wall, but only if the full path from property to wall is part of commit
This means that, starting from commit #21:0, I go to wall #30:0, for which I should get 0 properties (note that property #34:0 is not linked to commit #21:0!), while starting from commit #22:0, would go to wall #29:0, for which I should get all 4 properties. Basically I want to stop the traversal of the MATCH whenever a node is hit which is not connected to the specified commit node.
I tried a MATCH command, with a while in which I check if there is a shortestPath from the commit to the $currentMatch. However, this doesn't seem to work, I get error that Vertex ID cannot be NULL.
My query is like:
select expand(ret) from (
MATCH {class: V, as: commit, where:(#rid = #21:0)},
{as: commit}.out("commitlink"){as: wall, where:(#class INSTANCEOF "Wall")},
{as: wall}.out("E"){as: props, where:(#class = "Property"), while:(shortestPath($matched.commit, $currentMatch, "OUT","commitlink").size() > 0)}
return items as ret)
... but note that this query gives a the "Vertex id can not be null" error.
I seem to misunderstand the availability of $currentMatch in the while part of the command.
EDIT:
This seems to do the job:
select expand($properties) LET
$commit = (SELECT FROM #22:0),
$wall = (SELECT FROM (SELECT expand(out("commitlink")) FROM $commit) WHERE
#class INSTANCEOF "Wall"),
$properties = (SELECT FROM (TRAVERSE OUT("E") FROM $wall WHILE (shortestPath($commit, #rid, "OUT", "commitlink").size() > 0)) WHERE #class INSTANCEOF "Property")
But is there a drawback in using traverse queries instead of MATCH?
I don't know if I get what you want but what I understood is that your query should return all the property that are directly connected to the commit vertex without a Wall in between, am I right?
If this is what you want, try this:
MATCH {class: Property, as: props}.in("commitlink"){as: commit, where:(#rid = #19:0)} return props
this is my schema:
this is the result:
Hoope it helps
Regards
Related
context
I have Users collection and Recipe collection, ManyToMany relation between them
I'm new in this framework, wondering how can I do the following query:
count users with at least one recipe
count users without any recipes
I have found loadRelationCountAndMap is very useful in counting how many recipes a user has, but I can't seem to filter the total response according to this property.
I have tried this:
const users_without_recipes = await getRepository(User)
.createQueryBuilder('user')
.addSelect(['user.createdAt', 'user.email'])
.loadRelationCountAndMap('user.recipes_count', 'user.recipes')
.where('user.recipes_count = :count', {count: 0})
.getManyAndCount();
also tried to use postgres array_count but not sure how to integrate it with the typeORM framework
and help is very appreciated
You can do this with subqueries I think.
Something like this in SQL:
SELECT *
// ... other stuff
WHERE user.id IN (
SELECT u.id
FROM user u JOIN recipie r USING(id)
GROUP BY u.id
HAVING COUNT(*) > 10
)
Or in TypeORM:
// ...
.where(qb => {
const subQuery = qb.subQuery()
.select("u.id")
.from(User, "u")
// join users and recipies
.groupBy("u.id")
.having("COUNT(*) > :count", { count: 10 })
.getQuery();
return "user.id IN " + subQuery;
});
//...
I have stumbled upon this exact problem myself. I have fond a couple of sources that could help you find the solution. Your underlying problem is more general. If you turn on logging, you will probably see an error message something like:
Error column "user"."recipes_count" does not exist
The problem is that you are trying to use an alias. This question deals with this problem:
Using an Alias in a WHERE clause
I hope you are more successful then me, but I decided to use a workaround after a log trial and error. I am sure there is a better way. If you manage to find it, please let me know.
(If you want to get back the list of user entities, without the user.recipes_count property, include the last .map as well.)
const users_without_recipes = await getRepository(User)
.createQueryBuilder('user')
.loadRelationCountAndMap('user.recipes_count', 'user.recipes')
.getMany();
return users_without_recipes
.filter((user) => user['user.recipes_count'] === 0)
.map(({recipes_count, ...otherProperties}) => otherProperties);
I can't perform a wildcard-query on an embedded-list property of vertex (or edge).
For example:
Assume we have a Person class with a multi-value property named Nicknames and one instance of it:
{
"#type": "d",
"#rid": "#317:0",
"#version": 1,
"#class": "Person",
"Nicknames": [
"zito",
"ziton",
"zitoni"
]
}
then,
Select FROM Person WHERE Nicknames like "zit%"
returns empty result-set, while:
Select FROM Person WHERE Nicknames ="zito" returns 1 item correctly.
There's a NOTUNIQUE_HASH_INDEX index on the field Nicknames.
I've tried many ways (contains, index-query...) with no luck :(
I'm probably missing something basic.
I know is not an ideal solution what i'm going to write but, to stay stuck with your requirement of "query by wildcard" this is the only way that worked for me, as AVK stated is a better idea work with a Lucene index, but with the standard implementation i was unable to let it work, now here what i've done:
Use studio to create a javascript function with 2 parameter with name "array" and "rule", lets name the function "wildcardSearch"
past this code in the body of the function (is just simple javascript change it if it dosent do the job) :
for(i=0; i<array.length ; i++){
rule= rule.split("*").join(".*");
rule= rule.split("*").join(".*");
rule= "^" + ruleValue + "$";
var regex = new RegExp(rule);
if (regex.test(array[i]))
return true;
}
return false;
Remember to save the fucntion
now you can query:
Select from Person where wildcardSearch(nicknames,'zit*')=true
CONSIDERATIONS: is a brute force method, but show how "funny" can be play around with the "stored procedure" in OrientDb so i've decided to share it anyway, if performance are your main goal this things is not for you, it scan all the class and do the loop on the array to apply the regex. An Index is a way better solution, or change your db with a different data structure.
You can try this:
select from Person where Nicknames containstext 'zit'
Hope that helps
I am trying to insert a vertex with orientjs(previously oriento) query builder. My class has a link type property pointing to another class.
I know I can get it to work with a raw query string but I would love to use the query builder.
Here is what I've tried so far :
db.insert()
.into('VertexClassName')
.set({"prop":"value", "linkProperty":"33:1289287"})
db.insert()
.into('VertexClassName')
.set({"prop":"value", "linkProperty":"#33:1289287"})
I get the following error :
Error on saving record in cluster #13
Am I setting properties in the right way ?
Could the error be related to somtehing else ?
I have sucessfully ran an insert query in the cluster #13 with a raw query string in the studio...
According to the official documentation it seems that the problem might be at the end of your statement
db.insert().into('VertexClassName')
.set({"prop":"value", "linkProperty":"33:1289287"}).one()
.then(function (data) {
// callback
});
Check if your code works adding one() to the pipe line
EDITED: I found this method in orientjs.
db.create('VERTEX', 'V')
.set({
key: 'value',
foo: 'bar'
})
.one()
.then(function (vertex) {
console.log('created vertex', vertex);
});
When using Tinkerpop API they recommend using createVertex instead of insert, because createVertex is intended for graphs and insert for Documents... Could you try with the create() method instead?
I am using SQL and it worked.
sql = "INSERT INTO Station set linked = (select from LinkedClass where LinkedProb = 'value'), prop = 'value'"
OrientVertex vertex = new OrientVertex();
vertex = graph.command(new OCommandSQL(sql)).execute();
I don't think that's possible unless you've added a proper field with the right type 'Link' in your schema. (which I rarely do).
Now instead of having the right 'link' type inserted you can do the opposite, store is as a String, and leverage the query functions to use it correctly:
db.insert().into('table').set({prop: '#15:14'}).one();
And it will be converted as String (which is a bit sad) but then you can use that in your queries:
SELECT eval(prop) FROM table;
And it will be 'eval'-ed to a Node RecordID that you can directly use and call functions like expand() on.
For example:
SELECT name FROM (SELECT expand(eval(prop)) FROM table);
Will eval the node stored in the insert(), grab the node, expand it and collect its name property.
Is there a way to create with ArangoDB an Edge with REST API without knowing the Vertex ids? With a query to find the vertexs and link them?
Like this with OrientDB: create edge Uses from (select from Module where name = 'm2') to (select from Project where name = 'p1')
I don't want to query via REST the two vertex before, and after create the Edge. I don't want to use Foxx also.
Perhaps with AQL?
Thanks.
Yes, it is doable with a single AQL query:
LET from = (FOR doc IN Module FILTER doc.name == 'm2' RETURN doc._id)
LET to = (FOR doc IN Project FILTER doc.name == 'p1' RETURN doc._id)
INSERT {
_from: from[0],
_to: to[0],
/* insert other edge attributes here as needed */
someOtherAttribute: "someValue"
}
INTO nameOfEdgeCollection
I'm trying to insert patterns (nodes and edges) using merge. Using the demo movies graph, I'm sending the following cypher query: the movie exists, I'd like to create the User node and the edge in one query.
MERGE (top:Movie { title:'Top Gun' })<-[:viewed]-(user:User {Name:'Pierre'})
ON CREATE SET user.created = timestamp()
ON MATCH SET user.lastSeen = timestamp()
RETURN user,top;
"MERGE needs at least some part of the pattern to already be known. Please provide values for one of: user, top"
Actually, top exits, I can't figure out what's wrong in my query. Thanks for your help.
Pierre
Would this work?
MATCH (top:Movie { title:'Top Gun' })
MERGE (top)<-[:viewed]-(user:User {Name:'Pierre'})
ON CREATE SET user.created = timestamp()
ON MATCH SET user.lastSeen = timestamp()
RETURN user,top;
or this for creates:
MERGE (top:Movie { title:'Top Gun' })
MERGE (user:User {Name:'Pierre'})
ON CREATE SET user.created = timestamp()
ON MATCH SET user.lastSeen = timestamp()
MERGE (top)<-[:viewed]-(user)
RETURN user,top;