Missing field value

Problem

You want to find documents where a specific field is missing a particular field value.

Solution

You need an index that specifies the target field in the index’s terms. This index lets you search for the target field value, and by association, compute the list of documents that do not have that field value.

client.query(
  q.CreateIndex({
    name: 'People_by_degrees',
    source: q.Collection('People'),
    terms: [
      { field: ['data', 'degrees'] },
    ],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
result = client.query(
  q.create_index({
    "name": "People_by_degrees",
    "source": q.collection("People"),
    "terms": [{"field": ["data", "degrees"]}],
  })
)
print(result)
result, err := client.Query(
	f.CreateIndex(
		f.Obj{
			"name": "People_by_degrees",
			"source": f.Collection("People"),
			"terms": f.Arr{
				f.Obj{"field": f.Arr{"data", "degrees"}},
			},
		},
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
try
{
    Value result = await client.Query(
        CreateIndex(
            Obj(
                "name", "People_by_degrees",
                "source", Collection("People"),
                "terms", Arr(
                    Obj("field", Arr("data", "degrees"))
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
CreateIndex({
  name: 'People_by_degrees',
  source: Collection('People'),
  terms: [
    { field: ['data', 'degrees'] },
  ]
})
Query metrics:
  •    bytesIn:   139

  •   bytesOut:   307

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     8

  •  readBytes: 2,362

  • writeBytes: 2,767

  •  queryTime:  60ms

  •    retries:     0

This index lets us search for People documents that contain a specific degree in the degrees field. See Create documents in the Index tutorials for the definition of the People documents.

Now we can query for the difference between all documents (via the Documents function) and the documents containing a "PhD" degree, which results in the list of People documents that do not have a "PhD" degree:

client.query(
  q.Map(
    q.Paginate(
      q.Difference(
        q.Documents(q.Collection('People')),
        q.Match(q.Index('People_by_degrees'), 'PhD')
      )
    ),
    q.Lambda('ref', q.Get(q.Var('ref')))
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  data: [
    {
      ref: Ref(Collection("People"), "304065275331872256"),
      ts: 1626238093630000,
      data: {
        first: 'Tim',
        last: 'Cook',
        age: 59,
        degrees: [ 'BS', 'MBA' ],
        letter: 'G'
      }
    }
  ]
}
result = client.query(
  q.map_(
    q.lambda_("ref", q.get(q.var("ref"))),
    q.paginate(
      q.difference(
        q.documents(q.collection("People")),
        q.match(q.index("People_by_degrees"), "PhD")
      )
    )
  )
)
print(result)
{'data': [{'ref': Ref(id=304065277806510592, collection=Ref(id=People, collection=Ref(id=collections))), 'ts': 1626238095990000, 'data': {'first': 'Tim', 'last': 'Cook', 'age': 59, 'degrees': ['BS', 'MBA'], 'letter': 'G'}}]}
result, err := client.Query(
	f.Map(
		f.Paginate(
			f.Difference(
				f.Documents(f.Collection("People")),
				f.MatchTerm(f.Index("People_by_degrees"), "PhD"),
			),
		),
		f.Lambda("ref", f.Get(f.Var("ref"))),
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:[map[data:map[age:59 degrees:[BS MBA] first:Tim last:Cook letter:G] ref:{304065246510712320 0xc0001805a0 0xc0001805a0 <nil>} ts:1626238066150000]]]
try
{
    Value result = await client.Query(
        Map(
            Paginate(
                Difference(
                    Documents(Collection("People")),
                    Match(Index("People_by_degrees"), "PhD")
                )
            ),
            Lambda("ref", Get(Var("ref")))
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(data: Arr(ObjectV(ref: RefV(id = "304065227032363520", collection = RefV(id = "People", collection = RefV(id = "collections"))),ts: LongV(1626238047580000),data: ObjectV(first: StringV(Tim),last: StringV(Cook),age: LongV(59),degrees: Arr(StringV(BS), StringV(MBA)),letter: StringV(G)))))
Map(
  Paginate(
    Difference(
      Documents(Collection("People")),
      Match(Index("People_by_degrees"), "PhD")
    )
  ),
  Lambda("ref", Get(Var("ref")))
)
{
  data: [
    {
      ref: Ref(Collection("People"), "304065365448589824"),
      ts: 1626238179570000,
      data: {
        first: 'Tim',
        last: 'Cook',
        age: 59,
        degrees: [ 'BS', 'MBA' ],
        letter: 'G'
      }
    }
  ]
}
Query metrics:
  •    bytesIn:  188

  •   bytesOut:  253

  • computeOps:    1

  •    readOps:   10

  •   writeOps:    0

  •  readBytes:  882

  • writeBytes:    0

  •  queryTime: 17ms

  •    retries:    0

Discussion

When Fauna indexes a field containing an array value, one index entry per array item is created. That makes it straightforward to query for a single item in the array.

The Difference function is used to list the difference between all of the documents in the collection (via the Documents function) and the documents that have the "PhD" degree by using the index.

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!