How to perform AND operation in Mongodb for a query on nested array of objects - mongodb

I have the following records:
Record 1:
{
"status": "active",
"users": [
{
"name": "foo",
"status": "a"
},
{
"name": "foo",
"status": "b"
}
]
}
Record 2:
{
"status": "active",
"users": [
{
"name": "bar",
"status": "a"
},
{
"name": "foo",
"status": "b"
}
]
}
Now, I wish to return only those records in which the user.name is "foo" and the user.status is "a" - That is I need to get Record 1 back since it satisfies my requirements.
How do I do the AND operation on the array within the record to achieve this?
I tried the following query:
{
"$and": [
{
"users.name": "foo"
},
{
"users.status": "a"
}
]
}
but I get both the records - I need only the first record (since in the users array, it has an entry where the name is "foo" AND the status is "a").
Any clues?

Ok, I found the answer out here
The solution would be to have a query as follows:
{
"users": {
"$elemMatch": {
"name": "foo",
"status": "a"
}
}
}
That did the trick!

Instead of individual field values you can also use whole objects in find queries. Try:
db.collection.find( {
users: {
"name":"foo",
"status":"a"
}
});
This should return all documents where the users array includes an object (or is an object) which looks as described. But note that this doesn't work when the object has additional fields.

Related

MongoDB use array field's element to $set a new field of the document

In the database, I have documents like the following
Ticket {
"eventHistory": [
{
"event": "CREATED",
"timestamp": "aa-bb-cccc"
},
{
"event": "ASSIGNED",
"timestamp": "ii-jj-kkkk"
},
...
{
"event": "CLOSED",
"timestamp": "xx-yy-zzzz"
}
]
}
I would like to add a closedAt field to the relevant Tickets, getting the value from the eventHistory array's last element. The resultant document would look like the following
Ticket {
"eventHistory": [
{
"event": "CREATED",
"timestamp": "aa-bb-cccc"
},
{
"event": "ASSIGNED",
"timestamp": "ii-jj-kkkk"
},
...
{
"event": "CLOSED",
"timestamp": "xx-yy-zzzz"
}
],
"closedAt": "xx-yy-zzzz"
}
The following pipeline allows me to use the entire object that's present as the eventHistory array's last element.
db.collection.updateMany(
<query>,
[
"$set": {
"closedAt": {
"$arrayElemAt": [
"$eventHistory",
-1
]
}
}
]
...
)
But I want to use only the timestamp field; not the entire object.
Please help me adjust (and/or improve) the pipeline.
One option to fix your query is:
db.collection.updateMany(
<query>,
[
{
$set: {
"Ticket.closedAt": {
$last: "$Ticket.eventHistory.timestamp"
}
}
}
])
See how it works on the playground example
But note that you assume that last item is a closing one. Is this necessarily the case? Otherwise you can validate it.

How can i update record on an embedded document in a mongoDB collection?

I have a mongoDB "ratings" collection which contains the below data
/* 1 */
{
"_id": ObjectId("5f1e13936d702dc8b1aa47c8"),
"ratings": [{
"music": "PN1",
"rating": "23"
},
{
"music": "PN2",
"rating": "24"
}
],
"email": "test1#mail.com",
"username": "test1"
}
/* 2 */
{
"_id": ObjectId("5f1e13f46d702dc8b1aa47c9"),
"ratings": [{
"music": "PN3",
"rating": "45"
},
{
"music": "PN4",
"rating": "65"
}
],
"email": "test2#mail.com",
"username": "test2"
}
I want to modify the PN1 rating for username "test1" and i execute the below code
db.getCollection('ratings').update(
// query
{
"_id": ObjectId("5f1e13936d702dc8b1aa47c8"),
"email": "test1#mail.com"
},
// update
{
"ratings.rating": "8"
},
// options
{
"multi": false,
"upsert": false
);
The code works but instead of modifying just the PN1 rating for test1 user, the entire ratings list for that user was replaced the update. How can i fix this? I only want to modify the PN1 rating for the test1 user
First you'll need to specify which element of the array you want to update, e.g. one that has a music value of PN1. You can do this by adding a field to the query that matches against the ratings.music field like so:
{
"_id": ObjectId("5f1e13936d702dc8b1aa47c8"),
"email": "test1#mail.com",
"ratings.music": "PN1"
}
Now that you matched the document you want to update, you need to tell MongoDB how to update the document. An update document that changes the rating might look like this:
{
$set:
{
"ratings.$.rating": 8
}
}
This works by using the $ positional operator, which will update the first element in the ratings array that has a music field equal to PN1.

How to query based on nested object's key and value?

"session": {
"number": 123,
"words": {
"1": {
"id": 10,
"name": "Hello"
},
"2": {
"id": 13,
"name": "Hi"
},
"3": {
"id": 135,
"name": "Hey"
}
}
}
We have data in MongoDB database in the given structure which I am not able to modify.
I can get the object based on query session.number is 123, but now the challenging is we know the nested name is either Hi, Hey or Hello but the incremental key inside object words are unpredictable, is it possible to query base on the "name" field?
You can use $objectToArray and convert the words property to an array then query it however you want, you can use the following:
collection.aggregate([
{
$project: {
words: { $objectToArray: "$session.words" }
}
},
{
$match: {
'words.v': 'hi'
}
}
]);
You can read the docs for more info.

Cannot create an array of nested object using a project operation

I'm forced to use the aggregation framework and the project operation of Spring Data MongoDb.
What I'd like to do is creating an array of object as a result of a project operation.
For example, considering this intermediate aggregation result:
[
{
"content": {
"processes": [
{
"id": "101a",
"title": "delivery"
},
{
"id": "101b",
"title": "feedback"
}
]
}
}
]
What I want to obtain is this:
[
{
"results":
{
"titles": [
{
"id": "101a",
"value": "delivery"
},
{
"id": "101b",
"value": "feedback"
}
]
}
}
]
This was just an example, I don't want to simply "rename" some fields.
What I want is the possibility to create an array of objects.
If I try something like this:
projectionOperation
.and("$content.processes.id").as("results.titles.id")
.and("$content.processes.title").as("results.titles.value");
I obtain this:
[
{
"results":
{
"titles": {
"id": ["101a", "101b"]
"value": ["delivery", "feedback"]
}
}
}
}
]
With this projection the array is created, but not "in the proper position".
However, If I use the nested operator, I haven't figure out a way to specify that I want to create an array instead of an object.
With this projection:
projectionOperation.and("results.titles")
.nested(
bind("id", "process.id")
.and("value", "process.title")
);
I can create a proper nested object but not into an array:
"results.titles": {
"id": "101b",
"value": "feedback"
}
You can try below aggregation code.
ProjectionOperation po = Aggregation.project().and(
VariableOperators.mapItemsOf("content.processes").as("rt")
.andApply(
new AggregationExpression() {
#Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return new Document("id", "$$rt.id").append("value", "$$rt.title");
}
}
)
).as("result");

Criteria Morphia MongoDB

I have a collection like this:
{
"_id": {
"$oid": "53f34ef8ec10d6fa97dcc34b"
},
"editions": [
{
"number": 1,
...
},
{
"number": 2,
...
},
...
]
}
I want filter results of my query by some number.
I tried
criterias.add(query.criteria("editions.number").equal(paramNumber));
And
query.filter("editions.number =", paramNumber)
However I just received all collection, when I pass paramNumber equals 2. What I want is receive the following result:
{
"_id": {
"$oid": "53f34ef8ec10d6fa97dcc34b"
},
"editions": [
{
"number": 2,
...
}
]
}
What am I doing wrong?
You can't receive partial arrays like that. You'll get back with the full document/object or just the fields you've specified in a projection.