For a building security system I want to get the last swiped card time for a particular student based on their id serial number. I am using this code
var _collection = database.GetCollection<BsonDocument>("entrycard_swipetimes");
var filter = Builders<BsonDocument>.Filter.Eq("studentid", "stu-1234") & Builders<BsonDocument>.Filter.Eq("Carddetails.LastSwipeTimestamp", "2021-11-25T10:50:00.5230694Z");
var doc = _collection.Find(filter).FirstOrDefault();
This is a snippet of the json document
{
"studentid"; "stu-1234",
"Carddetails":
{
"LastSwipeTimestamp": "2021-11-25T10:50:00.5230694Z"
}
}
The code above works and I get a record, but I need to know the exact time string in advance. How can get only the last time a particular student swiped their card? How do I get the last timestamp of all the students in the system and have it paged by say 20 students at a time for the last 30 days?
Modify Filter and add a sort thusly:
var filter = Builders<BsonDocument>.Filter.Eq("studentid", "stu-1234");
var doc = _collection.Find(filter).Sort("{\"Carddetails.LastSwipeTimestamp\":-1}").FirstOrDefault();
Highly recommend that you change Carddetails.LastSwipeTimestamp to be a real datetime type instead of an ISO8601 string.
To get the last time a particular student (studentId) swiped their card:
var filter = Builders<BsonDocument>.Filter.Eq("studentid", studentId);
var doc = collection
.Find(filter)
.SortByDescending(bson => bson["Carddetails.LastSwipeTimestamp"])
.FirstOrDefault();
To get the last timestamp of all the students in the system and have it paged by pageSize students at a time for the last lastDaysCount days:
var doc = collection
.Aggregate()
.Group(new BsonDocument
{
{ "_id", "$studentid" },
{
"LastSwipeTimestamp", new BsonDocument
{
{ "$max", "$Carddetails.LastSwipeTimestamp" }
}
},
})
.Match(bson => bson["LastSwipeTimestamp"] > DateTime.Now.AddDays(-lastDaysCount))
.Skip((page - 1) * pageSize)
.Limit(pageSize)
.ToList();
But for this to work, you should store your dates as Date type.
When inserting, the offset is OK, however when retrieving the document, it's resetting to +00:00
Property:
public DateTimeOffset CreatedOn { get; set; }
Insert:
user.CreatedOn = DateTimeOffset.Now; // 01/20/2021 6:05:21 PM +05:30
col.Insert(user);
col.EnsureIndex(x => x.Username);
Find:
using (var db = _liteDbConnection.Create() as LiteDatabase)
{
var col = db.GetCollection<AuthUser>(USERS_COLLECTION);
return col.FindOne(x => x.UserId == userId);
}
user.CreatedOn becomes
01/20/2021 6:05:21 PM +00:00
Am I doing something wrong?
From the documentation
Following the BSON specification, DateTime values are stored only up to the miliseconds. All DateTime values are converted to UTC on storage and converted back to local time on retrieval.
It doesn't look like there's actual DateTimeOffset support. (Personally I think it's a terrible idea to convert to local time on retrieval, but that's a slightly different matter.) Additionally, it looks like it's not really converting to local time properly, given the incorrect offset of 0.
I would suggest avoiding using DateTimeOffset with LiteDb until it's really supported (maintaining the offset).
This happens because LiteDB stores a DateTimeOffset using value.UtcDateTime. This is unfortunate, but it can't be changed due to compatibility. However, you can override this behavior by creating a custom serializer:
BsonMapper.Global.RegisterType<DateTimeOffset>
(
serialize: obj =>
{
var doc = new BsonDocument();
doc["DateTime"] = obj.DateTime.Ticks;
doc["Offset"] = obj.Offset.Ticks;
return doc;
},
deserialize: doc => new DateTimeOffset(doc["DateTime"].AsInt64, new TimeSpan(doc["Offset"].AsInt64))
);
I'm working an an app that's Puts and Gets data to Firebase, based on Date, and Time my problem now is that users can modify it in settings and either get future data or old data I've tried using the dateTime.now however that can be modified I've also tried datetime.utcnow but it can also be modified. So my question is is there away I can work this out so users cannot modify time or even if they do the app time isn't affected.?
Sample of code stored to Firebase.
await CrossCloudFirestore.Current
.Instance
.Collection(DataPaths.NewsFeed)
.Document(guid)
.SetAsync(new {
ID = ticks + id,
ProfileImage = prefrences.profileImage,
UserName = usernames,
Title = title,
Date = DateTime.Now.ToString("dd MMM yyyy"),
Time = DateTime.UtcNow.ToString("HH:mm:ss"),
Details = details,
OrderTime = orderTime
});
Another Sample of Something I was Attempting.
//All 3 Needs sorting/fixing utc vs local/GMT time
//DateTime date = DateTime.UtcNow;
//var act = TimeZoneInfo.ConvertTime(date, TimeZoneInfo.Local);
//Application.Current.MainPage.DisplayAlert("", act.ToString(), "OK");
The solution is, that you should get the current time from an API rather than from the device.
The way to do it, is to set Date & Time to a value gotten from an API.
So I personally like this http://worldclockapi.com/api/json/est/now; copy this into your code where you would like to set your date & time, and you should be all fine.
You can edit the API URL to match your time zone; lets say you want Central Standard time, then this would be your API URL http://worldclockapi.com/api/json/cst/now.
Feel free to search for other APIs using Google.
Now, how do you set your variable to an API result value? ~ Like this:
var url = "http://worldclockapi.com/api/json/cst/now";
var client = new HttpClient();
var currentDateTime = client.Get(url);
await CrossCloudFirestore.Current
.Instance
.Collection(DataPaths.NewsFeed)
.Document(guid)
.SetAsync(new {
ID = ticks + id,
ProfileImage = prefrences.profileImage,
UserName = usernames,
Title = title,
Date = currentDateTime.ToString("dd MMM yyyy"),
Time = currentDateTime.ToString("HH:mm:ss"),
Details = details,
OrderTime = orderTime
});
I'm inserting a document to MongoDB collection using C# driver, one of the fields types vs a DateTime when i debug the application i see the server time in the "FrameTimeStamp" fields i'm passing to Mongo, this is my code:
FrameDocument frameDoc = new FrameDocument();
frameDoc.Frame = imageBA;
frameDoc.EventCodeId = 1;
frameDoc.SesionId = 1;
frameDoc.FrameTimeStamp = DateTime.Now;
frameDoc.ServerUserId = (int)toMongoDt.Rows[0]["ServerUserId"];
frameDoc.TraderId = (int)toMongoDt.Rows[0]["TraderId"];
frameDoc.ActivePick = (int)toMongoDt.Rows[0]["ActivePick"];
frameDoc.TraderName = (string)toMongoDt.Rows[0]["TraderName"];
frameDoc.ServerUserName = (string)toMongoDt.Rows[0]["ServerUserName"];
var mongoCon = "mongodb://127.0.0.1";
MongoClient client = new MongoClient(mongoCon);
var db = client.GetDatabase("Video");
var frameCollection = db.GetCollection<FrameDocument>("Frame");
frameCollection.InsertOne(frameDoc);
in the shell when I'm reading the data i see it in the following format:
2016-08-14T06:10:33.295Z and the time is not the server time, its UTC, how can i force mongo to write the DateTime as i pass it,?
as per CSHARP-185
MongoDB stores all DateTimes in UTC. Any local times you supply are
converted to UTC when stored in the database. The recommended approach
is to always convert DateTime values to UTC yourself before storing
them in the database, that way you are in full control. You can also
tell the C# driver that you want to work in LocalTime, like this:
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime Date
{ get; set; }
I am using the c# driver for mongodb and want to use an aggregation query for a web API I am creating. For my aggregation query I am concerned with a profile class that has usernames, dates and steps. I want to create a query that selects usernames and gets their total steps for a given week, in descending order by total steps. I only want to display their Username and their total steps.
When I attempt the aggregation query I am having an issue with some of my fields coming up as null. So, I believe my query is not structured correctly.
I have a “Profile” class that I am using for my data currently.
[BsonIgnoreExtraElements]
[DataContract]
public class Profile
{
[DataMember]
public string Username { get; set; }
[DataMember]
public DateTime Date { get; set; }
[DataMember]
public uint? Steps { get; set; }
}
I have created some test data with for example profiles using the following
//Test data
for (uint index = 1; index < 20; index++)
{
Profile aprofile = new Profile
{
Username = string.Format("testuser{0}", index),
Date = RandomDay(),
Steps = (index + index + index)*2
};
AddProfile(aprofile);
}
If I run the code a few times and query a particular user, I get data that is like this:
[{"Username":"testuser1","Date":"2014-07-03T00:00:00Z","Steps":6},
{"Username":"testuser1","Date":"2014-07-07T05:00:00Z","Steps":6},
{"Username":"testuser1","Date":"2014-07-17T05:00:00Z","Steps":6},
{"Username":"testuser1","Date":"2014-07-18T05:00:00Z","Steps":6}]
Then, I have a couple static methods to find the earliest date and the latest date for my aggregation query.
//gets a datetime for the earlist date and time possible for the current week
public static DateTime GetStartOfCurrentWeek()
{
int DaysToSubtract = (int)DateTime.Now.DayOfWeek;
DateTime dt = DateTime.Now.Subtract(TimeSpan.FromDays(DaysToSubtract));
return new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0, 0);
}
//gets a datetime for the latest date and time possible for the current week
public static DateTime GetEndOfCurrentWeek()
{
DateTime dt = GetStartOfCurrentWeek().AddDays(6);
return new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59, 999);
}
My attempt at an aggregation query is below.
//Here is my aggregation query to get all profiles within a given week,
public IEnumerable<Profile> GetAllProfiles()
{
DateTime StartofWeek = GetStartOfCurrentWeek();
DateTime EndofWeek = GetEndOfCurrentWeek();
var match = new BsonDocument
{{ "$match", new BsonDocument
{{ "Date", new BsonDocument {
{"$gte", StartofWeek},
{"$lt", EndofWeek}
}}}
}};
var group = new BsonDocument
{{"$group",
new BsonDocument
{{ "_id", new BsonDocument
{{"id", "$Username"},
}},
{"Steps", new BsonDocument
{{"$sum", "$Steps"}}
}}
}};
var sort = new BsonDocument
{{"$sort", new BsonDocument
{{"Steps", -1}}
}};
var pipeline = new[] {match, group, sort};
var args = new AggregateArgs { Pipeline = pipeline, OutputMode = AggregateOutputMode.Inline };
// run the aggregation query and get a list of BsonDocuments
IEnumerable<BsonDocument> documents = _profiles.Aggregate(args);
}
However, the results I am getting are showing the usernames as Null and the dates as null.
[{"Username":null,"Date":"0001-01-01T00:00:00","Steps":96},
{"Username":null,"Date":"0001-01-01T00:00:00","Steps":66},
{"Username":null,"Date":"0001-01-01T00:00:00","Steps":24}]
What do I need to do to get my array of documents so that the usernames display along with the total steps I have (which do seem to be working). I don't want the Dates in my query results. Just the Username and their total steps for that week.
I don't really understand why do you need to query your mongo with BSon. You do have your model class. Aggregation you are trying to perform will look like:
var result = collection.Aggregate()
.Match(r => r.Date >= StartofWeek && r.Date < EndofWeek)
.Group(r=>r.Username, r=> new {UserName = r.Key, Dates = r.Select(d=>d.Date), Steps = r.Sum(x=>x.Steps)})
.ToList();
Note, that you could get not one date, but array of it, therefore i aggregate it as Dates.
Reason, why you don't see Date and username is that you don't query it, in your group statement i see just sum of steps. if you really want to continue to query this way, you should add it to your group stage, something like:
var group = new BsonDocument
{
{ "_id", new BsonDocument{{"id", "$Username"},
}},
{"Steps", new BsonDocument{{"$sum", "$Steps"}}},
{ "Dates", new BsonDocument{{"$addToSet", "$Date"}} },
{ "Username", new BsonDocument{{"$first", "$Username"}} },
};
This way i can see the Username and dates in resulting BsonDocument.
But as i said already, this way to query Mongo is not the best one to do in c#
Your group stage has one flaw, and that is you didn't included the field you want out of your query.
By just changing your group stage, you should be getting the desired field.
var group = new BsonDocument
{{"$group",
new BsonDocument
{
{ "_id", new BsonDocument
{{"id", "$Username"}}
},
{ "Steps", new BsonDocument
{{"$sum", "$Steps"}}
},
{ "Username", new BsonDocument
{{"$first", "$Username"}} // since the rest of the selected object is groupped, we should give a strategy to pick a $Username, and it is safe to use $first aggregate function here
}
}
}};
And if you are not comfortable with the date in your output result just add a project stage, and reshape your output document.