Skip to main content

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:

OperadorNomeAltera
$Posicional simplesPrimeiro elemento do array que bate com o filtro
$[]Posicional todosTodos os elementos do array
$[<identifier>]Posicional filtradoTodos 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:

ParteSignificado
items.$[item].statusAtualiza o campo status dos itens filtrados
itemNome do identificador
arrayFiltersDefine 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

OperadorAtualiza quantos elementos?Precisa de arrayFilters?Exemplo
$Primeiro elemento encontradoNão"items.$.quantity"
$[]Todos os elementosNão"items.$[].status"
$[item]Todos que batem com o filtroSim"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árioUse
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$[]