Match document id

On login I’m attempting to retrieve a list of Rankings to which the user belongs by adding a ‘userJoinedRankings’ to the loginResult @embedded type in my schema:

type User
 {  
    active : Boolean!
    username : String!
    description : String
    email : String 
    mobile : String
}

type Player
 {  rankingid : String!
    uid : String!
    rank : Int!
    challengerid : String
}

type Ranking
  { 
    active : Boolean!
    rankingname : String!
    rankingdesc : String
    rankingownerid : String!
    }

type Mutation {
  createAndOrLoginUser(active: Boolean!, username: String!, password: String!, description: String, email: String, mobile: String): String! @resolver(name: "create_andor_login_user")
  createNewPlayer(rankingid: String!, uid: String!, rank: Int!, challengerid: String): Player! @resolver(name: "create_new_player")
  createNewRanking(active: Boolean!, rankingname : String!, rankingdesc : String, rankingownerid : String!): Ranking! @resolver(name: "create_new_ranking")
  createNewUser(active: Boolean!, username : String!, password : String!, description: String, email: String, mobile: String): loginResult! @resolver(name: "create_new_user")
}

type Query {
  allUserNames: [String!]! @resolver(name: "all_user_names")
  allPlayerUIDs: [String!]! @resolver(name: "all_player_uids")
  allPlayerRanks: [Int!]! @resolver(name: "all_player_ranks")
  allPlayerChallengerUIDs: [String!]! @resolver(name: "all_player_challenger_uids")
  allPlayers: [Player] @resolver(name: "all_players")
  allRankings: [Ranking] @resolver(name: "all_rankings")
  allUsers: [User] @resolver(name: "all_users")
  loginUser(username: String!, password: String!): loginResult! @resolver(name: "login_user")
  gotPlayersByRankingId (rankingid: String!): [Player] @resolver(name: "got_players_byrankingid")
  gotRankingIdsByPlayer (uid: String!): [String] @resolver(name: "got_rankings_byplayerid")
}

type loginResult @embedded
{
  token : String
  logginUser : User
  userJoinedRankings : [String]
}

My intention is to use the document id of the User, once logged in, to query the rankingIdsByPlayer index (uid field) and return the relevant rankings as a list together with the User and a token.
The index:

{
  name: "rankingIdsByPlayer",
  unique: false,
  serialized: true,
  source: "Player",
  terms: [
    {
      field: ["data", "uid"]
    }
  ],
  values: [
    {
      field: ["data", "rankingid"]
    }
  ]
}

I will need to update the function. The closest I can get so far is:

Query(
  Lambda(
    ["username", "password"],
    Let(
      {
        match: Match(Index("unique_User_username"), Var("username")),
        user: If(Exists(Var("match")), Get(Var("match")), "false"),
        login: Login(Select("ref", Var("user")), { password: Var("password") }),
        matchRankings: Match(Index("rankingIdsByPlayer"), Var("uid")),
        userjoinrankings: Get(Var("matchRankings"))
      },
      {
        token: Select("secret", Var("login")),
        logginUser: Select("ref", Var("user")),
        userjoinrankings: Select("ref", Var("userjoinrankings"))
      }
    )
  )
)

This (correctly) gives me:
Variable 'uid' is not defined in the UI.
Is there a way for me to pass the just logged in user’s document id to matchRankings Match?
thanks …

Looks like you have identified the player who has identified with Login. What is uid (uid : String!)? Ref of Player?

@Jay-Fauna
Currently it’s a non-working place holder that represents the value I want to pass to the Index. My question is what should this value be or how should I reference a uid (user id) value to obtain a String list of rankingIds?
I currently pass a user id back to the client app. I’m hoping to use that user id immediately in the Query to obtain the list of rankingids to send back immediately on login, if that’s possible?

uid is a field in Player that is used by the index to select rankings in which that user is a player

Fauna gives you access to the Document Ref in GraphQL via the generated _id field.

But you don’t need it if you set your schema up in a more canonical way.

User, Player and Ranking should be connected using the @relation directives, rather than creating additional ids. Also, do not save the id’s directly to try to create relationships. GraphQL responds with the _id, but in the DB it stores the whole Ref, e.g. Ref(Collection('User'), '1234'). Indexes can use whole Refs as the term – use this for your own indexes, not the individual id.

On login you can return the respective User document (you’re already doing that), and then you can use GraphQL to just query for the information without creating any additional indexes. The directives will create the indexes that you need and graphQL query will use them for you.

Example

type User {  
  active: Boolean!
  username: String!
  description: String
  email: String 
  mobile: String
  playedAs: [Player!]! @relation
}

type Player {
  rank: Int!
  ranking: Ranking @relation
  challenger: User! @relation
}

type Ranking { 
  active: Boolean!
  rankingname: String!
  rankingdesc: String
  player: Player
}

side note: login is generally considered a mutation. If you are using Apollo or something like it on the client side, treating it as a mutation might make things easier to work with.

mutation {
  loginUser( ........ ) {
    token
    logginUser {
      _id
      playedAs {
        _id
        ranking {
          _id
          active
          rankingname
          rankingdesc
        }
      }
    }
  }
}

@ptpaterson
Thank you very much for your thorough response.
I updated the schema:

type User {  
  active: Boolean!
  username: String!
  description: String
  email: String 
  mobile: String
  playedAs: [Player!]! @relation
}

type Player {
  rank: Int!
  ranking: Ranking @relation
  challenger: User! @relation
}

type Ranking { 
  active: Boolean!
  rankingname: String!
  rankingdesc: String
  player: Player
}

type Mutation {
  createNewPlayer(ranking: String!, uid: String!, rank: Int!, challenger: String): Player! @resolver(name: "create_new_player")
  createNewRanking(active: Boolean!, rankingname : String!, rankingdesc : String, player : String!): Ranking! @resolver(name: "create_new_ranking")
  createNewUser(active: Boolean!, username : String!, password : String!, description: String, email: String, mobile: String, playedAs: [Player!]!): loginResult! @resolver(name: "create_new_user")
  loginUser(username: String!, password: String!): loginResult! @resolver(name: "login_user") {
    token
    logginUser {
      _id
      playedAs {
        _id
        ranking {
          _id
          active
          rankingname
          rankingdesc
        }
      }
    }
  }
}

type Query {
  allUserNames: [String!]! @resolver(name: "all_user_names")
  allPlayerUIDs: [String!]! @resolver(name: "all_player_uids")
  allPlayerRanks: [Int!]! @resolver(name: "all_player_ranks")
  allPlayerChallengerUIDs: [String!]! @resolver(name: "all_player_challenger_uids")
  allPlayers: [Player] @resolver(name: "all_players")
  allRankings: [Ranking] @resolver(name: "all_rankings")
  allUsers: [User] @resolver(name: "all_users")
  gotPlayersByRankingId (rankingid: String!): [Player] @resolver(name: "got_players_byrankingid")
}

type loginResult @embedded
{
  token : String
  logginUser : User
}

I’m using Elm on client side, incidentally.
So, if I understand correctly, this means the relation between playedAs (in User) and ranking (in Player) effectively determines (what was previously) the rankingowner (now ‘player’ in Ranking) and (what was previously) the ‘userjoinedrankings’ in User?
Currently this schema gives me (on import via the UI):

Syntax error while parsing GraphQL query. Invalid input ""login_user") {", expected Argument, FieldDefinition, Directive, Comments or Value (line 27, column 81):
  loginUser(username: String!, password: String!): loginResult! @resolver(name: 
                                              ^
"login_user") {

Do you know how I should fix this import error. I haven’t changed anything in regards to the login (it’s still username, password)?
I expect the implementation of the loginUser function will be challenging for me, however, I will give it a try once I’ve successfully uploaded the new schema … thanks again …

@ptpaterson
I updated the schema again as my first attempt (above) didn’t look right:

type User {  
  active: Boolean!
  username: String!
  description: String
  email: String 
  mobile: String
  playedAs: [Player!]! @relation
}

type Player {
  rank: Int!
  ranking: Ranking @relation
  challenger: User! @relation
}

type Ranking { 
  active: Boolean!
  rankingname: String!
  rankingdesc: String
  player: Player
}

type Mutation {
  createNewPlayer(ranking: String!, uid: String!, rank: Int!, challenger: String): Player! @resolver(name: "create_new_player")
  createNewRanking(active: Boolean!, rankingname : String!, rankingdesc : String, player : String!): Ranking! @resolver(name: "create_new_ranking")
  createNewUser(active: Boolean!, username : String!, password : String!, description: String, email: String, mobile: String, playedAs: [Player!]!): loginResult! @resolver(name: "create_new_user")
  loginUser(username: String!, password: String!): loginResult! @resolver(name: "login_user") 

type Query {
  allUserNames: [String!]! @resolver(name: "all_user_names")
  allPlayerUIDs: [String!]! @resolver(name: "all_player_uids")
  allPlayerRanks: [Int!]! @resolver(name: "all_player_ranks")
  allPlayerChallengerUIDs: [String!]! @resolver(name: "all_player_challenger_uids")
  allPlayers: [Player] @resolver(name: "all_players")
  allRankings: [Ranking] @resolver(name: "all_rankings")
  allUsers: [User] @resolver(name: "all_users")
  gotPlayersByRankingId (rankingid: String!): [Player] @resolver(name: "got_players_byrankingid")
}

type loginResult @embedded
{
    token
    logginUser {
      _id
      playedAs {
        _id
        ranking {
          _id
          active
          rankingname
          rankingdesc
        }
      }
    }
  }
}

However, this still gives me:

Syntax error while parsing GraphQL query. Invalid input ""login_user") \n\ntype Q", expected IgnoredNoComment or Value (line 27, column 81):
  loginUser(username: String!, password: String!): loginResult! @resolver(name: "login_user") 

What am I missing? thanks …

Is this a valid type? :thinking:

LoginResult is not being defined as a proper GraphQL type. types need to be specified and you cannot specify more than one depth unless you use multiple types.

My example was not specifying a type for the loginResult, I was showing an example in of a query.

I am not sure if I should be implementing this as a Query or a Mutation, in the schema or the db itself. My attempt to implement it as a type was clearly wrong (I was too focussed, in the moment, on the invalid upload problem).

Your example uses a Mutation:

mutation {
loginUser( ........ ) {
token
...

and you noted that ‘login is generally considered a mutation’ … so that appeared to be the way to go. However, your last comment was ‘I was showing an example in[sic] of a query.’

I don’t think you mean a Query in the schema since the format in your example is very different to the existing Queries, as it was for type, and you have alluded to Fauna’s ability to define functionality itself ‘and then you can use GraphQL to just query for the information without creating any additional indexes. The directives will create the indexes that you need and graphQL query will use them for you.’

That’s fine, so I then considered that you may be referring to a query in the function body definition in the db. However, it then doesn’t match the format expected by the db and I get:

'missing : after property id'

I can change that to match the syntax I was previously using:

token: Select("secret", Var("login")),

but then how does logginUser match that format given all the additional fields that aren’t defined as variables in the Let statement? In short, this doesn’t appear correct either (?).
I’m still confused on this point and am in need of further clarification if that’s possible.

  1. Should I be attempting to implement this as a Query or a Mutation, in the schema or the db itself?

  2. Is the syntax in your example already correct or is it a shorthand that requires the details to be filled in?

  3. If my problems are a result of a lack of background reading do you have any reference links that will bring me further up to speed on these specific issues?

Thank you very much for your assistance and patience …

I meant as in a generic way of sending a request. This could be either a GraphQL Query or Mutation. Sorry for the confusion. My example included the keyword mutation, though, so it is not a type but a mutation request.

To be clear, your GraphQL schema needs to be a valid GraphQL schema. Make sure that it is. To fit my example, you may try this:

type LoginResult @embedded {
    token: String!
    logginUser:  User!
}

By returning the User, the GraphQL API will let you expand it when returned by a request.

If you haven’t seen this blog, please check it out! It’s got a great example of a UserLogin Mutation.

SELF-SOLVED: I needed to query on data{}
I read the article, thanks.
Using the GraphQL API with the new mutation, I get:

{
  "data": null,
  "errors": [
    {
      "message": "Cannot query field '_id' on type 'PlayerPage'. (line 7, column 9):\n        _id\n        ^",
      "locations": [
        {
          "line": 7,
          "column": 9
        }
      ]
    },
    {
      "message": "Cannot query field 'ranking' on type 'PlayerPage'. (line 8, column 9):\n        ranking {\n        ^",
      "locations": [
        {
          "line": 8,
          "column": 9
        }
      ]
    }
  ]
}

Looking at the schema in Playground (API) I see that PlayerPage has neither ‘_id’ nor ‘ranking’, so the error makes sense. However, I do not know what the underlying problem is and what I should do to fix it?

Server side schema below for reference … thanks …

directive @embedded on OBJECT
directive @collection(name: String!) on OBJECT
directive @index(name: String!) on FIELD_DEFINITION
directive @resolver(
  name: String
  paginated: Boolean! = false
) on FIELD_DEFINITION
directive @relation(name: String) on FIELD_DEFINITION
directive @unique(index: String) on FIELD_DEFINITION
scalar Date

type loginResult {
  token: String
  logginUser: User
}

input LoginResultInput {
  token: String
  logginUser: ID
}

input LoginResultLogginUserRelation {
  create: UserInput
  connect: ID
  disconnect: Boolean
}

scalar Long

type Mutation {
  updateUser(
    id: ID!
    data: UserInput!
  ): User
  createNewUser(
    active: Boolean!
    username: String!
    password: String!
    description: String
    email: String
    mobile: String
  ): loginResult!
  createUser(data: UserInput!): User!
  updatePlayer(
    id: ID!
    data: PlayerInput!
  ): Player
  deleteRanking(id: ID!): Ranking
  createNewUserJoinedRanking(
    active: Boolean!
    rankingid: String!
    rankingname: String!
    useraddr: String!
    rankingownerid: String!
  ): UserJoinedRanking!
  updateRanking(
    id: ID!
    data: RankingInput!
  ): Ranking
  createNewRanking(
    active: Boolean!
    rankingname: String!
    rankingdesc: String
    rankingownerid: String!
  ): Ranking!
  loginUser(username: String!, password: String!): loginResult!
  createPlayer(data: PlayerInput!): Player!
  createNewPlayer(
    rankingid: String!
    uid: String!
    rank: Int!
    challengerid: String
  ): Player!
  deleteUser(id: ID!): User
  deleteUserJoinedRanking(id: ID!): UserJoinedRanking
  createAndOrLoginUser(
    active: Boolean!
    username: String!
    password: String!
    description: String
    email: String
    mobile: String
  ): String!
  createRanking(data: RankingInput!): Ranking!
  deletePlayer(id: ID!): Player
  updateUserJoinedRanking(
    id: ID!
    data: UserJoinedRankingInput!
  ): UserJoinedRanking
  createUserJoinedRanking(data: UserJoinedRankingInput!): UserJoinedRanking!
}

type Player {
  ranking: Ranking
  _id: ID!
  rank: Int!
  challenger: User!
  _ts: Long!
}

input PlayerChallengerRelation {
  create: UserInput
  connect: ID
}

input PlayerInput {
  rank: Int!
  ranking: PlayerRankingRelation
  challenger: PlayerChallengerRelation
}

type PlayerPage {
  data: [Player]!
  after: String
  before: String
}

input PlayerRankingRelation {
  create: RankingInput
  connect: ID
  disconnect: Boolean
}

type Query {
  allRankings: [Ranking]
  gotRankingIdsByPlayer(uid: String!): [String]
  findUserJoinedRankingByID(id: ID!): UserJoinedRanking
  allPlayerRanks: [Int!]!
  allUserJoinedRanking: [UserJoinedRanking]
  allUsers: [User]
  findUserByID(id: ID!): User
  allPlayerUIDs: [String!]!
  allPlayers: [Player]
  findRankingByID(id: ID!): Ranking
  allPlayerChallengerUIDs: [String!]!
  findPlayerByID(id: ID!): Player
  gotPlayersByRankingId(rankingid: String!): [Player]
  allUserNames: [String!]!
}

type Ranking {
  _id: ID!
  rankingname: String!
  player: Player
  rankingdesc: String
  active: Boolean!
  _ts: Long!
}

input RankingInput {
  active: Boolean!
  rankingname: String!
  rankingdesc: String
  player: RankingPlayerRelation
}

input RankingPlayerRelation {
  create: PlayerInput
  connect: ID
  disconnect: Boolean
}

scalar Time

type User {
  playedAs(
    _size: Int
    _cursor: String
  ): PlayerPage!
  email: String
  username: String!
  description: String
  _id: ID!
  mobile: String
  active: Boolean!
  _ts: Long!
}

input UserInput {
  active: Boolean!
  username: String!
  description: String
  email: String
  mobile: String
  playedAs: UserPlayedAsRelation
}

type UserJoinedRanking {
  rankingid: String!
  rankingownerid: String!
  _id: ID!
  rankingname: String!
  useraddr: String!
  active: Boolean!
  _ts: Long!
}

input UserJoinedRankingInput {
  active: Boolean!
  rankingid: String!
  rankingname: String!
  useraddr: String!
  rankingownerid: String!
}

input UserPlayedAsRelation {
  create: [PlayerInput]
  connect: [ID]
  disconnect: [ID]
}