You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 20:24:55