[CSHARP-1550] Wrong behavior for bulk Upsert().UpdateOne filtering with "created_on" Created: 30/Jan/16  Updated: 05/Apr/19  Resolved: 31/Jan/16

Status: Closed
Project: C# Driver
Component/s: Operations
Affects Version/s: 1.9.2
Fix Version/s: None

Type: Task Priority: Major - P3
Reporter: Zeioth Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: Bug, driver, question
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

MongoDB 3.2.1 under Ubuntu 15.10


Attachments: PNG File bu.png    
Issue Links:
Duplicate

 Description   

Hi, I think there's a problem in .Upsert().UpdateOne().
When you filter documents created today, using "created_on", it will always insert a new document instead of update the desired field.

Short code:
http://pastebin.com/jT0cpdGg



 Comments   
Comment by Zeioth [ 01/Feb/16 ]

Thank you Craid and Robert, I posted the solution in stack overflow so everyone can benefit from your knowledge. Regards.
http://stackoverflow.com/questions/35122191/filter-by-date-using-an-idobject/35140698#35140698

Comment by Craig Wilson [ 01/Feb/16 ]

Hi Zeioth,

It's best to keep jira tickets to a single topic only. It seems your last comment is unrelated to the topic. In addition, we generally reserve jira tickets for bugs and feature requests. Questions are better asked at the mongodb-user forums.

To answer your question, there is a constructor for an ObjectId that takes a DateTime. Simply pass 0 for the remaining parameters.

Craig

Comment by Zeioth [ 01/Feb/16 ]

Hi Robert, thank you for your answer.
This mongoDB query, filter documents by date using an idObject field.

db.myCollection.find({_id:{$gt: ObjectId(Math.floor((new Date('1990/10/10'))/1000).toString(16) + "0000000000000000"), 
                           $lt:  ObjectId(Math.floor((new Date('2011/10/10'))/1000).toString(16) + "0000000000000000")}})

What I'm trying to achieve is the same result using the C# driver. How can I convert a date to idObject? There's already any method to achieve this?
Code extracted from: http://stackoverflow.com/a/13594408/2010764

Comment by Robert Stam [ 31/Jan/16 ]

I've reproduced this, but the problem isn't with the driver but rather with the query itself.

Let's look at this using the shell to see exactly what is going on.

Let's begin by verifying that the "test" collection is empty:

> db.test.find()
>

Then let's execute one update command and see what's going on. While you are using the bulk insert API in your code, it's easier to look at just one update. The others are doing the same thing:

> var query = {
... created_on : { $gte : ISODate("2016-01-31"), $lt :ISODate("2016-02-01") },
... url : "a"
... }
> var update = { $set : { isTopApp : true } }
> db.runCommand({
... update : "test",
... updates : [ { q : query, u : update, upsert : true } ],
... ordered : true
... })
{
        "ok" : 1,
        "nModified" : 0,
        "n" : 1,
        "upserted" : [
                {
                        "index" : 0,
                        "_id" : ObjectId("56ae7af886f97e23d9117a95")
                }
        ]
}
>

And let's see what got inserted into the collection:

> db.test.find()
{ "_id" : ObjectId("56ae7af886f97e23d9117a95"), "url" : "a", "isTopApp" : true }
>

The important thing to notice here is that your upserted document does NOT have a created_on field. But you are assuming that it does.

When an upsert is executed the query is used to initialize the new document to be inserted, and then the update statements are applied against that. But the only parts of the query that can be used to initialize the new document are the parts of the query that compare a field against a constant value. In your query, that's the "url" field, so we see that the upserted document contains a "url" field. But the query on "created_on" is a range query, so it is not used to initialize the new document, so we see that the upserted document does not contain a created_on field.

You are probably going to need to change your schema so that the "created_on" field (or some new field with whatever name you want) contains only the Date component and not the Time component of when the document was created. Then alter your query to be:

var query = { created_on : ISODate("2016-01-31"), url : "a" }

So that the created_on field is compared to a constant value that can be used if necessary to initialize the new document when an upsert occurs.

Finally, be very careful when using DateTime to always use UTC values in queries and documents.

So instead of

DateTime.Today

write this instead

DateTime.UtcNow.Date

I'm going to go ahead and close this CSHARP ticket as "Works as designed" since the issue is not actually with the driver.

Comment by Zeioth [ 30/Jan/16 ]

Screenshot of the same code.

Generated at Wed Feb 07 21:39:57 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.