使用Golang+GraphQL查询Neo4j时遇迭代器错误求解决
问题:Gin+GraphQL+Neo4j服务查询报错:expected iterable, but did not find one
发起GraphQL请求时收到如下错误:
{ "data": { "movieByTitle": null }, "errors": [ { "message": "User Error: expected iterable, but did not find one for field Query.movieByTitle.", "locations": [ { "line": 2, "column": 3 } ], "path": [ "movieByTitle" ] } ] }
对应的GraphQL字段代码:
var getMovieByTitle = &graphql.Field{ Type: graphql.NewList(models.MovieType), Args: graphql.FieldConfigArgument{ "title": &graphql.ArgumentConfig{ Type: graphql.String, }, }, Resolve: func(params graphql.ResolveParams) (interface{}, error) { title := params.Args["title"].(string) configuration := data.ParseConfiguration() driver, err := configuration.NewDriver() if err != nil { log.Fatal(err) } defer data.UnsafeClose(driver) session := driver.NewSession(neo4j.SessionConfig{ AccessMode: neo4j.AccessModeRead, DatabaseName: configuration.Database, }) defer data.UnsafeClose(session) movie, err := session.ReadTransaction(func(tx neo4j.Transaction) (interface{}, error) { records, err := tx.Run( `MATCH (movie:Movie {title:$title}) OPTIONAL MATCH (movie)<-[r]-(person:Person) WITH movie.title as title, collect({name:person.name, job:head(split(toLower(type(r)),'_')), role:r.roles}) as cast LIMIT 1 UNWIND cast as c RETURN title, c.name as name, c.job as job, c.role as role`, map[string]interface{}{"title": title}) if err != nil { return nil, err } var result models.Movie for records.Next() { record := records.Record() title, _ := record.Get("title") result.Title = title.(string) name, _ := record.Get("name") job, _ := record.Get("job") role, _ := record.Get("role") switch role.(type) { case []interface{}: result.Cast = append(result.Cast, models.Person{Name: name.(string), Job: job.(string), Role: data.ToStringSlice(role.([]interface{}))}) default: // handle nulls or unexpected stuff result.Cast = append(result.Cast, models.Person{Name: name.(string), Job: job.(string)}) } } return result, nil }) if movie == nil { log.Println(movie) return nil, fmt.Errorf("movie not found") } fmt.Println(movie) return movie, nil }, }
注:使用Gin运行服务器,直接用HTTP端点执行相同Cypher查询能得到预期结果,使用的是Neo4j示例电影数据集。
解决方案
错误根源是GraphQL字段类型定义与resolve返回值类型不匹配:
- 字段
Type设置为graphql.NewList(models.MovieType),表示该字段返回一个电影列表(可迭代类型) - 但resolve函数最终返回的是单个
models.Movie对象,不是切片/数组,导致GraphQL期望可迭代结构却收到单个对象,触发报错。
根据业务需求,有两种修复方式:
方式1:修改字段类型为单个电影(推荐)
因为你的Cypher查询用了LIMIT 1,本身就是查询单个电影,所以直接把字段类型改成非列表即可:
// 把原来的graphql.NewList(models.MovieType)改成models.MovieType Type: models.MovieType,
方式2:保持列表类型,修改返回值为切片
如果需要保持字段返回列表格式,将resolve的返回值包装成切片:
// 替换原来的return movie, nil return []models.Movie{movie.(models.Movie)}, nil
同时注意处理电影不存在的情况,返回空切片或者错误:
if movie == nil { log.Println(movie) // 可选:返回空切片或错误,根据业务需求 return []models.Movie{}, nil // 或者 return nil, fmt.Errorf("movie not found") }
额外优化点
resolve函数里的log.Fatal(err)会直接终止程序,建议改成返回错误:
if err != nil { // log.Fatal(err) 替换为 return nil, err }
内容的提问来源于stack exchange,提问作者Isaac Cooke
相关产品推荐
相关产品推荐

