Operadores de posição $, $[], e $[item] | MongoDB | Banco de dados
- Operadores de posição
$,$[], e$[item]| MongoDB | Banco de dados
Os operadores posicionais são usados em operações de update para alterar elementos dentro de arrays sem precisar saber exatamente o índice do item.
No MongoDB, os principais são:
| Operador | Nome | Altera |
|---|---|---|
$ | Posicional simples | Primeiro elemento do array que bate com o filtro |
$[] | Posicional todos | Todos os elementos do array |
$[<identifier>] | Posicional filtrado | Todos os elementos do array que batem com arrayFilters |
O uso clássico do operador $ é atualizar um item de um array sem conhecer a posição dele. O MongoDB usa o filtro da query para encontrar o índice correto dentro do array. Esse comportamento aparece, por exemplo, ao atualizar um item em line_items.$.quantity, onde $ representa o elemento encontrado pelo filtro.
Documento de exemplo
Vamos usar uma collection orders:
db.orders.insertOne({
_id: 1,
customer: "Adolfo",
items: [
{ sku: "A100", name: "Mouse", quantity: 1, status: "pending" },
{ sku: "B200", name: "Teclado", quantity: 2, status: "pending" },
{ sku: "C300", name: "Monitor", quantity: 1, status: "pending" }
]
})
No C#, uma model equivalente poderia ser:
public class Order
{
public int Id { get; set; }
public string Customer { get; set; } = string.Empty;
public List<OrderItem> Items { get; set; } = [];
}
public class OrderItem
{
public string Sku { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public int Quantity { get; set; }
public string Status { get; set; } = string.Empty;
}
Operador $
O operador $ atualiza o primeiro elemento do array que corresponde ao filtro.
MongoDB
db.orders.updateOne(
{
_id: 1,
"items.sku": "B200"
},
{
$set: {
"items.$.quantity": 5
}
}
)
Resultado esperado:
{
_id: 1,
customer: "Adolfo",
items: [
{ sku: "A100", name: "Mouse", quantity: 1, status: "pending" },
{ sku: "B200", name: "Teclado", quantity: 5, status: "pending" },
{ sku: "C300", name: "Monitor", quantity: 1, status: "pending" }
]
}
O campo do array precisa estar presente no filtro, por exemplo "items.sku": "B200". A documentação oficial descreve que o $ atua como placeholder para o primeiro elemento que bate com a query. (MongoDB)
C# Driver
Forma mais comum usando string path:
var filter = Builders<Order>.Filter.And(
Builders<Order>.Filter.Eq(x => x.Id, 1),
Builders<Order>.Filter.ElemMatch(x => x.Items, item => item.Sku == "B200")
);
var update = Builders<Order>.Update
.Set("items.$.quantity", 5);
await collection.UpdateOneAsync(filter, update);
Também dá para usar filtro por string:
var filter = Builders<Order>.Filter.And(
Builders<Order>.Filter.Eq("_id", 1),
Builders<Order>.Filter.Eq("items.sku", "B200")
);
var update = Builders<Order>.Update
.Set("items.$.quantity", 5);
await collection.UpdateOneAsync(filter, update);
Quando usar
Use $ quando você quer alterar apenas o primeiro item encontrado dentro do array.
Exemplo típico:
"items.$.status": "paid"
Operador $[]
O operador $[] atualiza todos os elementos do array do documento encontrado.
MongoDB
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[].status": "processed"
}
}
)
Resultado:
{
_id: 1,
customer: "Adolfo",
items: [
{ sku: "A100", name: "Mouse", quantity: 1, status: "processed" },
{ sku: "B200", name: "Teclado", quantity: 2, status: "processed" },
{ sku: "C300", name: "Monitor", quantity: 1, status: "processed" }
]
}
A documentação oficial define $[] como o operador que indica que a atualização deve modificar todos os elementos do array especificado. (MongoDB)
C# Driver
var filter = Builders<Order>.Filter.Eq(x => x.Id, 1);
var update = Builders<Order>.Update
.Set("items.$[].status", "processed");
await collection.UpdateOneAsync(filter, update);
Com $inc
db.orders.updateOne(
{ _id: 1 },
{
$inc: {
"items.$[].quantity": 1
}
}
)
C#:
var update = Builders<Order>.Update
.Inc("items.$[].quantity", 1);
await collection.UpdateOneAsync(
Builders<Order>.Filter.Eq(x => x.Id, 1),
update
);
Quando usar
Use $[] quando a alteração deve afetar todos os itens do array.
Exemplo:
"items.$[].status": "cancelled"
Operador $[<identifier>]
O operador $[<identifier>] atualiza somente os elementos que batem com uma condição definida em arrayFilters.
Ele é mais poderoso que $, porque pode atualizar vários elementos específicos dentro do array.
MongoDB
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[item].status": "approved"
}
},
{
arrayFilters: [
{ "item.quantity": { $gte: 2 } }
]
}
)
Nesse exemplo:
| Parte | Significado |
|---|---|
items.$[item].status | Atualiza o campo status dos itens filtrados |
item | Nome do identificador |
arrayFilters | Define quais elementos do array serão afetados |
"item.quantity": { $gte: 2 } | Só altera itens com quantity >= 2 |
A documentação oficial descreve $[<identifier>] como o operador usado junto com arrayFilters para identificar elementos do array que devem ser atualizados. (MongoDB)
C# Driver
var filter = Builders<Order>.Filter.Eq(x => x.Id, 1);
var update = Builders<Order>.Update
.Set("items.$[item].status", "approved");
var options = new UpdateOptions
{
ArrayFilters =
[
new BsonDocumentArrayFilterDefinition<BsonDocument>(
new BsonDocument("item.quantity", new BsonDocument("$gte", 2))
)
]
};
await collection.UpdateOneAsync(filter, update, options);
Exemplo com sku
MongoDB:
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[item].quantity": 10
}
},
{
arrayFilters: [
{ "item.sku": "B200" }
]
}
)
C#:
var filter = Builders<Order>.Filter.Eq(x => x.Id, 1);
var update = Builders<Order>.Update
.Set("items.$[item].quantity", 10);
var options = new UpdateOptions
{
ArrayFilters =
[
new BsonDocumentArrayFilterDefinition<BsonDocument>(
new BsonDocument("item.sku", "B200")
)
]
};
await collection.UpdateOneAsync(filter, update, options);
Quando usar
Use $[<identifier>] quando você quer atualizar elementos específicos por condição.
Exemplos:
"items.$[item].status": "approved"
"items.$[item].quantity": 10
"items.$[item].discount": 15
Comparação rápida
| Operador | Atualiza quantos elementos? | Precisa de arrayFilters? | Exemplo |
|---|---|---|---|
$ | Primeiro elemento encontrado | Não | "items.$.quantity" |
$[] | Todos os elementos | Não | "items.$[].status" |
$[item] | Todos que batem com o filtro | Sim | "items.$[item].status" |
Exemplo com array simples
Documento:
db.students.insertOne({
_id: 1,
name: "Carlos",
grades: [70, 80, 90]
})
Atualizar primeiro valor encontrado com $
db.students.updateOne(
{ _id: 1, grades: 80 },
{
$set: {
"grades.$": 85
}
}
)
C#:
var filter = Builders<BsonDocument>.Filter.And(
Builders<BsonDocument>.Filter.Eq("_id", 1),
Builders<BsonDocument>.Filter.Eq("grades", 80)
);
var update = Builders<BsonDocument>.Update
.Set("grades.$", 85);
await collection.UpdateOneAsync(filter, update);
Atualizar todos com $[]
db.students.updateOne(
{ _id: 1 },
{
$inc: {
"grades.$[]": 5
}
}
)
C#:
var filter = Builders<BsonDocument>.Filter.Eq("_id", 1);
var update = Builders<BsonDocument>.Update
.Inc("grades.$[]", 5);
await collection.UpdateOneAsync(filter, update);
Atualizar apenas notas maiores ou iguais a 80 com $[grade]
db.students.updateOne(
{ _id: 1 },
{
$inc: {
"grades.$[grade]": 10
}
},
{
arrayFilters: [
{ "grade": { $gte: 80 } }
]
}
)
C#:
var filter = Builders<BsonDocument>.Filter.Eq("_id", 1);
var update = Builders<BsonDocument>.Update
.Inc("grades.$[grade]", 10);
var options = new UpdateOptions
{
ArrayFilters =
[
new BsonDocumentArrayFilterDefinition<BsonDocument>(
new BsonDocument("grade", new BsonDocument("$gte", 80))
)
]
};
await collection.UpdateOneAsync(filter, update, options);
Exemplo com array de objetos
Documento:
db.students.insertOne({
_id: 1,
name: "Ana",
grades: [
{ type: "quiz", score: 80 },
{ type: "exam", score: 90 },
{ type: "homework", score: 70 }
]
})
Atualizar o primeiro elemento encontrado
db.students.updateOne(
{
_id: 1,
"grades.type": "exam"
},
{
$set: {
"grades.$.score": 95
}
}
)
C#:
var filter = Builders<BsonDocument>.Filter.And(
Builders<BsonDocument>.Filter.Eq("_id", 1),
Builders<BsonDocument>.Filter.Eq("grades.type", "exam")
);
var update = Builders<BsonDocument>.Update
.Set("grades.$.score", 95);
await collection.UpdateOneAsync(filter, update);
Atualizar todos os elementos
db.students.updateOne(
{ _id: 1 },
{
$inc: {
"grades.$[].score": 5
}
}
)
C#:
var filter = Builders<BsonDocument>.Filter.Eq("_id", 1);
var update = Builders<BsonDocument>.Update
.Inc("grades.$[].score", 5);
await collection.UpdateOneAsync(filter, update);
Atualizar elementos filtrados
db.students.updateOne(
{ _id: 1 },
{
$set: {
"grades.$[grade].score": 100
}
},
{
arrayFilters: [
{ "grade.type": "exam" }
]
}
)
C#:
var filter = Builders<BsonDocument>.Filter.Eq("_id", 1);
var update = Builders<BsonDocument>.Update
.Set("grades.$[grade].score", 100);
var options = new UpdateOptions
{
ArrayFilters =
[
new BsonDocumentArrayFilterDefinition<BsonDocument>(
new BsonDocument("grade.type", "exam")
)
]
};
await collection.UpdateOneAsync(filter, update, options);
Operadores posicionais com UpdateMany
Os operadores posicionais também funcionam com updateMany.
MongoDB
db.orders.updateMany(
{ "items.status": "pending" },
{
$set: {
"items.$[item].status": "expired"
}
},
{
arrayFilters: [
{ "item.status": "pending" }
]
}
)
C#
var filter = Builders<Order>.Filter.ElemMatch(
x => x.Items,
item => item.Status == "pending"
);
var update = Builders<Order>.Update
.Set("items.$[item].status", "expired");
var options = new UpdateOptions
{
ArrayFilters =
[
new BsonDocumentArrayFilterDefinition<BsonDocument>(
new BsonDocument("item.status", "pending")
)
]
};
await collection.UpdateManyAsync(filter, update, options);
Pegadinhas importantes
$ altera apenas o primeiro match
db.orders.updateOne(
{ _id: 1, "items.status": "pending" },
{
$set: {
"items.$.status": "processed"
}
}
)
Mesmo que existam vários itens com status: "pending", apenas o primeiro será alterado.
$[] altera tudo
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[].status": "processed"
}
}
)
Todos os itens do array serão alterados.
$[item] precisa de arrayFilters
Errado:
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[item].status": "processed"
}
}
)
Correto:
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[item].status": "processed"
}
},
{
arrayFilters: [
{ "item.status": "pending" }
]
}
)
O nome do identificador precisa bater
Errado:
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[item].status": "processed"
}
},
{
arrayFilters: [
{ "x.status": "pending" }
]
}
)
Correto:
db.orders.updateOne(
{ _id: 1 },
{
$set: {
"items.$[item].status": "processed"
}
},
{
arrayFilters: [
{ "item.status": "pending" }
]
}
)
O identificador usado em $[item] precisa ser o mesmo usado dentro de arrayFilters.
Resumo final
| Cenário | Use |
|---|---|
| Quero atualizar o primeiro item encontrado no array | $ |
| Quero atualizar todos os itens do array | $[] |
| Quero atualizar vários itens específicos do array | $[<identifier>] com arrayFilters |
Quero atualizar item por sku, id, status, etc. | $[item] |
| Quero atualizar tudo sem condição individual | $[] |