#Indexes on fields within nested objects

7 messages · Page 1 of 1 (latest)

royal marsh
#

Have there been any community discussions or questions about the perspectives of indexes on the nested object fields in Convex? E.g., I have a column foo: v.object({bar: v.string()}, and want to have an index on "foo.bar" strings.

wary stagBOT
#

Hi @royal marsh. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Yes, this has come up a few times, and the short answer is: yes, you can index fields inside nested objects using dot-separated paths.

From the docs:

  • Database indexes: when defining an index, “to specify a field on a nested document, use a dot-separated path like properties.name.” [Indexes]
  • Vector indexes: “You can specify vector and filter fields on nested documents by using a dot-separated path like properties.name.” [Vector search]
  • Full‑text search indexes: same pattern for searchField and filterFields. [Text search]

So for your example:

export default defineSchema({
  myTable: defineTable({
    foo: v.object({ bar: v.string() }),
  }).index("by_foo_bar", ["foo.bar"]),
});

This is a supported and documented pattern.

There’s also a Discord thread on “custom fields” where staff explicitly confirm: “You can index on a nested document” and link to the same indexes doc. [Custom fields thread]

If you need to query it:

const rows = await ctx.db
  .query("myTable")
  .withIndex("by_foo_bar", q => q.eq("foo.bar", someString))
  .collect();

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@royal marsh Kapa.ai is still learning and improving, please let me know how I did by reacting below.

royal marsh
#

@wary stag What's the behaviour of such indexes when the nested object is absent ? field is assumed to be undefined?

wary stagBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, the field is treated as undefined when the nested object (or the nested field) is absent.

The docs state that for filters and index queries, missing fields are considered undefined, and you can even query them that way:

You can use undefined in filters and index queries, and it will match documents that do not have the field. i.e.
.withIndex("by_a", q => q.eq("a", undefined)) matches {} and {b: 1}, but not {a: 1} or {a: null, b: 1}. [Working with undefined]

This applies equally to nested paths like "foo.bar": if foo is missing or doesn’t have bar, then "foo.bar" is treated as undefined for the purposes of index ordering and comparisons. The same section also notes Convex’s ordering:

In Convex's ordering scheme, undefined < null < all other values. [Working with undefined]

So:

  • Docs where foo.bar is missing will behave as if foo.bar === undefined.
  • You can explicitly query for “no value” with .eq("foo.bar", undefined).
  • You can query for “has some value” with something like .gt("foo.bar", undefined) or .gte("foo.bar", null as any) if needed.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: