Keystone: Array field type(s)

Created on 14 Sep 2018  Â·  11Comments  Â·  Source: keystonejs/keystone

All DB platforms we're planning to support in the near-term (eg. Mongo, pgSQL) support storing an ordered series of values in a single field. In both cases, the values stored can be of (almost) any other type supported by the data layer.

There are two way we could map this of thought on how these values should be typed:

A) Multiple array types with a different one for each underlying type ("TextArray", "NumberArray", "FileArray", .. ), eg...

keystone.createList('Post', {
  fields: {
    // .. 
    tags: { type: TextArray },
  },
});

B) A single array type which is supplied an "inner type" when configured, eg...

keystone.createList('Post', {
  fields: {
    // .. 
    tags: { type: Array, ofType: Text },
  },
});

We also need to make some decisions about how these values are accessed via GraphQL. OpenCRUD has some filters defined (field_contains, field_contains_every, field_contains_some) but is missing others (field_not_contains, field_contains_none). Issues #87 and #217 have some relevant discussion.

@JedWatson leans fairly strongly towards building separate array types initially (option "A" above) with the focus on reusing components from the "inner" type where relevant. He's suspects there are significant (enough) differences for how the interface will want to deal with a single value -vs- an array of values in the admin UI that going "fully generic" from the start won't work well.

medium 1 feature request needs-clarification needs-review

Most helpful comment

The current workaround is to create a Relationship to another list:

keystone.createList('Post', {
  fields: {
    // .. 
    tags: { type: Relationship, ref: 'Tag.posts' },
  },
});

keystone.createList('Tag', {
  fields: {
    name: { type: Text },
    posts: { type: Relationship, ref: 'Post.tags' },
  },
});

Then the graphQL query is:

query posts {
  allPosts {
    tags {
      name
    }
  }
}

And you can also look up all posts for a given tag:

query tags {
  tag(where: { id: "..." }) {
    posts {
      id
    }
  }
}

Ideally, any nested 'Array' type would support a similar GraphQL API.

All 11 comments

There's prior art for option "A" in KS4:

And arguablely..?

TextArray and CloudinaryImages are required for VO.

The current workaround is to create a Relationship to another list:

keystone.createList('Post', {
  fields: {
    // .. 
    tags: { type: Relationship, ref: 'Tag.posts' },
  },
});

keystone.createList('Tag', {
  fields: {
    name: { type: Text },
    posts: { type: Relationship, ref: 'Post.tags' },
  },
});

Then the graphQL query is:

query posts {
  allPosts {
    tags {
      name
    }
  }
}

And you can also look up all posts for a given tag:

query tags {
  tag(where: { id: "..." }) {
    posts {
      id
    }
  }
}

Ideally, any nested 'Array' type would support a similar GraphQL API.

It looks like there hasn't been any activity here in over 6 months. Sorry about that! We've flagged this issue for special attention. It wil be manually reviewed by maintainers, not automatically closed. If you have any additional information please leave us a comment. It really helps! Thank you for you contribution. :)

@jesstelford is there any way to verify that the list is ordered like an array?

I'm now including an index as part of the "array" list. The tag one in your example.

Unfortunately not; order is not guaranteed on Relationships.

I figured that. I was hoping I could sort nested fields, tags in the example below, by a field used as an index field. I still don't see a complete workaround that holds the behaviors of an array (order being one of those). Let me know if I'm missing something.

query posts {
  allPosts {
    tags {
      name
    }
  }
}

You should be able to create an intermediate list which contains the sort index:

keystone.createList('Post', {
  fields: {
    tags: { type: Relationship, ref: 'TagToPost', many: true },
  },
});

keystone.createList('Tag', {
  fields: {
    name: { type: Text },
  },
});

keystone.createList('TagToPost', {
  fields: {
    tag: { type: Relationship, ref: 'Tag' },
    post: { type: Relationship, ref: 'Post' },
    sortOrderForPost: { type: Integer },
  }
});

Then you can retrieve an ordered list like so:

query {
  allPosts {
    tags(sortBy: sortOrderForPost_ASC) {
      tag {
        name
      }
    }
  }
}

Note that the .sortOrderForPost field needs to be managed manually by you.

It looks like there hasn't been any activity here in over 6 months. Sorry about that! We've flagged this issue for special attention. It wil be manually reviewed by maintainers, not automatically closed. If you have any additional information please leave us a comment. It really helps! Thank you for you contribution. :)

This issue initially carried an implied question, basically...

Should Keystone leverage the array types supported in some underlying DBs?

Looking at this anew, I think this is the wrong question. Coming at from the user side, the kind of problem these fields would solve look pretty similar to the "repeating fields" use case, where a parent item has a one or more fields that can be entered multiple times. This is use case is described in #195 which also calls out the probably solution – a sub-list of items that is managed in the background.

Although it would _sometime_ be useful to support specific DB array features, the answer to that original question is probably "no". Building out support for nested/repeating fields should solve a lot of the same problems while also benefiting from the existing machinery around lists and avoiding deep dependancies on specific DB features. It maybe not _ideal_ for simple cases – like a single repeating number field, for example, where having a separate list behind the scenes seems like overkill – but I believe it's the best path forward. The key will be interfacing with these "nested" items in a sufficiently intuitive way, eg. adding support to edit these nested items inline.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ra-external picture ra-external  Â·  10Comments

jossmac picture jossmac  Â·  10Comments

bholloway picture bholloway  Â·  18Comments

ricardonogues picture ricardonogues  Â·  10Comments

derkweijers picture derkweijers  Â·  19Comments