0

There is this weird thing, I have installed the MongoDB Compass and made a aggregation query that works in the Aggregation tab but now when I use the same query in my rust web server it behaves very weirdly

Original message:

{"_id":{"$oid":"61efd41c56ffe6b1b4a15c7a"},"time":{"$date":"2022-01-25T10:42:36.175Z"},"edited_time":{"$date":"2022-01-30T14:29:54.361Z"},"changes":[],"content":"LORA","author":{"$oid":"61df3cab3087579f8767a38d"}}

Message in MongoDB compass after the query:

{
    "_id": {
        "$oid": "61efd41c56ffe6b1b4a15c7a"
    },
    "time": {
        "$date": "2022-01-25T10:42:36.175Z"
    },
    "edited_time": {
        "$date": "2021-12-17T09:55:45.856Z"
    },
    "changes": [{
        "time": {
            "$date": "2021-12-17T09:55:45.856Z"
        },
        "change": {
            "ChangedContent": "LORA"
        }
    }],
    "content": "LMAO",
    "author": {
        "$oid": "61df3cab3087579f8767a38d"
    }
}

Message after the Web Servers query:

{
    "_id": {
        "$oid": "61efd41c56ffe6b1b4a15c7a"
    },
    "time": {
        "$date": "2022-01-25T10:42:36.175Z"
    },
    "edited_time": {
        "$date": "2022-01-30T14:40:57.152Z"
    },
    "changes": {
        "$concatArrays": ["$changes", [{
            "time": {
                "$date": "2022-01-30T14:40:57.152Z"
            },
            "change": {
                "ChangedContent": "$content"
            }
        }]]
    },
    "content": "LMAO",
    "author": {
        "$oid": "61df3cab3087579f8767a38d"
    }
}

Pure query in MongoDB Compass: $set stage

{
  "changes": { $concatArrays: [ "$changes",  [ { "time": ISODate('2021-12-17T09:55:45.856+00:00'), "change": { "ChangedContent": "$content" } } ] ] },
  "edited_time": ISODate('2021-12-17T09:55:45.856+00:00'),
  "content": "LMAO",
}

Pure query in Web Server:

    let update_doc = doc! {
        "$set": {
            "changes": {
                "$concatArrays": [
                    "$changes", [
                        {
                            "time": now,
                            "change": {
                                "ChangedContent": "$content"
                            }
                        }
                    ]
                ]
            },
            "edited_time": now,
            "content": content
        }
    };

I am using update_one method, like this

 messages.update_one(message_filter, update_doc, None).await?;

I don't really understand, and this happens often, sometimes it fixes it self when I add somewhere randomly some scope in the doc eg.: { } but this time I couldn't figure it out, I had version of the query with $push but that didn't work too Is there some fault in the rust driver or am I doing something wrong, are there some rules about formatting when using rust driver that I am missing?

1 Answers1

0

The $set aggregation pipeline stage is different from the $set update operator. And the only difference that I can tell, is the pipeline stage handles $concatArrays while the update operator does not.

$set Aggregation Pipeline Stage

$set appends new fields to existing documents. You can include one or more $set stages in an aggregation operation.

To add field or fields to embedded documents (including documents in arrays) use the dot notation.

To add an element to an existing array field with $set, use with $concatArrays.

$set Update Operator

Starting in MongoDB 5.0, update operators process document fields with string-based names in lexicographic order. Fields with numeric names are processed in numeric order.

If the field does not exist, $set will add a new field with the specified value, provided that the new field does not violate a type constraint. If you specify a dotted path for a non-existent field, $set will create the embedded documents as needed to fulfill the dotted path to the field.

If you specify multiple field-value pairs, $set will update or create each field.

So if you want to update an existing document by inserting elements into an array field, use the $push update operator (potentially with $each if you're inserting multiple elements):

let update_doc = doc! {
    "$set": {
        "edited_time": now,
        "content": content
    },
    "$push": {
        "changes": {
            "time": now,
            "change": {
                "ChangedContent": "$content"
            }
        }
    }
};

Edit: I missed that $content was supposed to be mapped from the existing field as well. That is not supported by an update document, however MongoDB has support for using an aggregation pipeline to update the document. See: Update MongoDB field using value of another field So you can use the original $set just in a different way:

let update_pipeline = vec![
    doc! {
        "$set": {
            "changes": {
                "$concatArrays": [
                    "$changes", [
                        {
                            "time": now,
                            "change": {
                                "ChangedContent": "$content"
                            }
                        }
                    ]
                ]
            },
            "edited_time": now,
            "content": content
        }
    }
];

messages.update_one(message_filter, update_pipeline, None).await?;
kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • That does push a Object into that array but the ChangedContent has no value of field "content" from the message, may I ask why is that? The result: ```json { "changes": [{ "time": { "$date": "2022-01-31T14:35:31.019Z" }, "change": { "ChangedContent": "$content" } }], "content": "Loekrkkak", } ``` – Arisa Snowbell Jan 31 '22 at 14:37
  • @ArisaSnowbell my apologies, I've updated my answer – kmdreko Jan 31 '22 at 16:32
  • Oh, adding the doc in a vector changes it to a aggregation pipeline behavior, I had no idea, thank you so so so much, without you I wouldn't ever find that out. It works perfectly now, thank you so so very much you are amazing – Arisa Snowbell Jan 31 '22 at 16:50