Getting null when trying to return only single object and not array in graphql for neo4j db - neo4j

I want to return a single object(not array).The graphql server is returning null when i try to return single object.But seems to work fine while trying to return an array.
Here is my schema example:
type Author{
authorID: String
label: String
posts: [Post]
}
type Post {
postID: String
label: String
author: Author
}
type Query {
getAuthorById (authorID: String!): Author #-> this does not work
getAuthorById (authorID: String!): [Author] #-> this works
}
But when I try to run this query "getAuthorById (authorID: String!)",I am getting the following result:
{
"data": {
"getAuthorById": {
"label": null,
"authorID": null
}
}
However,it seems to work only if i try to return an array (when I try to change the schema for type query like this):
type Query {
getAuthorById (authorID: String!): [Author]
}
Here is my resolver.js:
Query: {
getAuthorById(_, params) {
let session = driver.session();
let query = `MATCH (a:Author{ authorID: $authorID}) RETURN a ;`
return session.run(query, params)
.then( result => {
return result.records.map( record => {
return record.get("a").properties
}
)
}
)
},
}
What I need is to return a single object like this:
getAuthorById (authorID: String!): Author
// Instead of array like this-> getAuthorById (authorID: String!): [Author]
so,could someone let me know what am i doing wrong here ? All I need is to return single object and not array .... Thanks in Advance

The problem is in your resolver, specifically you are returning the result of result.records.map() from the resolver. map() evaluates to an Array (applying the inner function to each element of result in this case.
Instead you can just grab the first Record in the Result stream:
.then( result => {
return result.records[0].get("a").properties
}
)

Related

Access variables of classes when looping list of classes and getting an instance of Type

I have this Python code:
def from_json(cls, data: dict, api: Optional[Overpass] = None) -> "Result":
"""
Create a new instance and load data from json object.
:param data: JSON data returned by the Overpass API
:param api:
:return: New instance of Result object
"""
result = cls(api=api)
elem_cls: Type[Union["Area", "Node", "Relation", "Way"]]
for elem_cls in [Node, Way, Relation, Area]:
for element in data.get("elements", []):
e_type = element.get("type")
if hasattr(e_type, "lower") and e_type.lower() == elem_cls._type_value:
result.append(elem_cls.from_json(element, result=result))
return result
and I want to convert this to Dart code. This is what I've come up with:
factory Result.fromJson(Map<String, dynamic> data, {Overpass? api}) {
Result result = Result(api: api!);
Type elemCls;
for (elemCls in [Node, Way, Relation, Area]) {
for (Map<String, dynamic> element in data["elements"]) {
String eType = element["type"];
if (eType.toLowerCase() == elemCls.typeValue) {
result.append(elemCls.fromJson(element, result: result));
}
}
}
return result;
}
My problems are The getter '_typeValue' isn't defined for the type 'Type'. and The method 'fromJson' isn't defined for the type 'Type'.. This is because _typeValue and fromJson belong to the classes Node, Way, Relation and Area and not to Type which is what I get with this for loop. What do I have to change to get this working like in Python?
You could do eType.toLowerCase() == elemCls.toString().toLowerCase(). However, elem_cls.from_json(element, result=result) cannot be directly done in Dart. There is no way to generically instantiate an object with type elemCls.
What you instead could do is create a lookup table to map type names to constructors:
final _constructionTable = {
"node": Node.fromJson,
"way": Way.fromJson,
"relation": Relation.fromJson,
"area": Area.fromJson,
};
factory Result.fromJson(Map<String, dynamic> data, {Overpass? api}) {
Result result = Result(api: api!);
for (Map<String, dynamic> element in data["elements"]) {
String eType = element["type"]!;
var fromJson = _constructionTable[eType.toLowerCase()];
if (fromJson != null) {
result.append(fromJson(element, result: result));
}
}
return result;
}
Note that this also would be a bit more efficient too, and it avoids relying on the specific strings generated for the Dart types.

Can not query on ID property graphQL Neo4j Databases

I have a Neo4j database I can't do a "where" on the ID type of graphQL.
Anyone have any idea why?
thanks in advance
I have that
type DueDiligences {
ddId: ID!
counterPartyRisk: String
countryRisk: String
}
when I query on String no problem
query Query {
dueDiligences (where: { counterPartyRisk: "Hight" }){
ddId
counterPartyRisk
}
}
//response
{
"data": {
"dueDiligences": [
{
"ddId": "1",
"counterPartyRisk": "Hight"
}
]
}
}
but when query in ddId I have empty response
query Query {
dueDiligences (where: { ddId: "1" }){
ddId
counterPartyRisk
}
}
//response
{
"data": {
"dueDiligences": []
}
}
This is caused by a mismatch between the ID graphql type (string) and the, manually created, id in the database (int).
When querying the database, even if using the same name, an int will not match a string.
To fix this, either the Cypher created ids should be created as strings, or the typedefs should define id as an Int:
type DueDiligences {
ddId: Int!
counterPartyRisk: String
countryRisk: String
}

Type of Iqueryable at runtime

I have a method which returns Iqueryable result, but the result is based on an if else condition, where if condition satisfies then I will use "AssetDetails" class object ,otherwise "UserandClientdetails" object.
Here is the code:
private IQueryable<?> GetAssetDetails(ShareViewModel item)
{
...
if (type == "Video")
{
if (type == "Video")
{
return from meta in my.Assets().OfType<Model.Video>()
join content in my.Contents() on meta.ContentId equals content.ID
join channel in my.Channels() on content.ChannelId equals channel.ID
where meta.ID == item.ID
select new AssetDetails
{
ContentTitle = content.Title,
ChannelName = channel.ChannelName,
...
};
}
else
{ return from meta in my.Assets().OfType<Model.Client>()
join country in db.Countries on meta.ResellerCountry equals country.ID
where meta.ID == item.ID
select new UserAndClientDetails
{
Name = meta.ResellerName,
UserName = meta.ResellerEmail,
..
};}
So how to decide type of Iqueyable here at runtime??
So, I was able to verify that this works, so I'll go ahead and post it as an answer.
You can return IQueryable instead of the generic IQueryable<>. That will accept any IQueryable<T>. However, IQueryable, since it has no direct inner type, is very limited. So, you'll still likely need to cast to IQueryable<> at some other point in your code to get anything done:
// Piece of code where you know you are working with `IQueryable<AssetDetails>`
IQueryable<AssetDetails> assetDetails = GetAssetDetails(someItem);
That's a little dangerous, though, as you're assuming that your code is working perfectly and the right type of thing is being returned. Better would be:
try
{
var assetDetails = (IQueryable<AssetDetails>)GetAssetDetails(someItem);
// do something with `assetDetails`
}
catch (InvalidCastException)
{
// recover gracefully
}
What about using a base class ?
public abstract class BaseDetails
{
// ...
}
public class AssetDetails : BaseDetails
{
// ...
}
public class UserAndClientDetails: BaseDetails
{
// ...
}
Then you method would be like :
private IQueryable<BaseDetails> GetAssetDetails(ShareViewModel item)
{
// return either IQueryable<AssetDetails> or IQueryable<UserAndClientDetails>
}

combining criteria from params array

This example here is from the grails docs:
def emeaCriteria = {
eq "region", "EMEA"
}
def results = Airport.withCriteria {
emeaCriteria.delegate = delegate
emeaCriteria()
flights {
like "number", "BA%"
}
}
My webpage is passing back a checkbox group of ethnicities, returning the row ids. So what the server gets is:
ethnicity:[1, 4]
or if the user only picks one ethnicity:
ethnicity:4
def criteria = { params ->
//handle case where only one ethnicity is returned as just a string, not a list of strings
def list = params.ethnicty instanceof String ? [params.ethnicty] : params.ethnicity
if (list) {
inList('ethnicity', list)
}
}
I'm getting an error: java.lang.String cannot be cast to java.lang.Enum.
If I didn't have a list I think I could figure it out. The params are sending back string values, and they need to be converted to the enum class. But within the closure, how do you convert each entry into a list to the enum?
I figured it out through a combination of multiple website posts and with help from dmahapatro above.
def genderCriteria = {
if (params.gender) {
inList('gender', params.list('gender').collect { Gender.valueOf(it)} )
}
}
If a webpage passes back one or more enums (a single string or a list of strings) and you want criteria to check values from the list passed back, you have to provide a list of enum types (not strings or ints).
Here is my enum class for reference:
public enum Gender {
M('Male'),
F('Female'),
U('Unknown')
final String value
Gender(String value) {
this.value = value
}
public String toString() {
value
}
public String getKey() {
name()
}
public String getValue() {
value
}
}
And my criteria builder:
def c = MyDomain.createCriteria()
results = c.list {
genderCriteria.delegate = delegate
genderCriteria(params)
}
Even if no values are passed back for the gender field, it still works (because of the if statement in genderCriteria.
It may not be the best or cleanest solution, but it does work.

Grails controller search criteria with list

I need to do a search based on various criteria.
if a name contains the specified string.
if the name contains the specified string and a string from a list.
if the name contains one of the strings from a list.
I have the following code but it doesn't work. How bad is this code?
def taskList = Object.createCriteria().list(params) {
if (params.query) {
ilike("name", "%${params.query}%")
}
if (params.nameList) {
params.nameList.split("&").each {
or {
ilike("name", "%${it}$%")
}
}
}
The result is empty for use cases 2 & 3. What am I doing wrong? How should I do it?
Cheers
I'd use in operator for 3. case
list{
or{
if( params.query ) ilike "name", "%${params.query}%"
if( params.nameList ) 'in' 'name', params.nameList.split("&")
}
}
def taskList = Object.createCriteria().list(params) {
def names = []
if(params.nameList){
names = nameList.split("&")
}
or{
if (params.query){ilike("name", "%${params.query}%")}
and{
if (params.query){ilike("name", "%${params.query}%")}
if(names){'in'('name', names)}
}
if (names) {
names.each {name->
or {
ilike("name", "%$name$%")
}
}
}
}
}
Untested, but can you try with above. I would rather go with an HQL to achieve what you needed.

Resources