2016-05-04 9 views
1

Ich manuell eine Reihe von POCOs mit Newtonsoft JsonTextWriter serialisieren und das Ergebnis als MongoDB BsonDocument speichern.Kann nicht nach Datumsfeldern in MongoDB mit C# -Treiber filtern

// 
// POCO to store in MongoDB 
public class Session 
{ 

    public DateTime? StartUTCTimestamp { get; set; } 

    public DateTime? StartTimestamp { get; set; } 

    public DateTime? EndTimestamp { get; set; } 

    public void ToJSON(ref JsonTextWriter writer) 
    { 
     Session session = this;    

     writer.WriteStartObject(); // { 

     writer.WritePropertyName("StartUTCTimestamp"); 
     writer.WriteValue(session.StartUTCTimestamp); 

     writer.WritePropertyName("StartTimestamp"); 
     writer.WriteValue(session.StartTimestamp); 

     writer.WritePropertyName("EndTimestamp"); 
     writer.WriteValue(session.EndTimestamp); 


     writer.WriteEndObject(); // } 
    } 
} 

Verfahren in einer Testanwendung, die die Daten zu importieren, verwendet wird, ruft alle Session Objekte aus einer SQL Server-Datenbank (unter Verwendung von Open-Access-ORM Telerik) die Ergebnisse in einer Liste speichern. Ich serialisiere jede Sitzung, indem ich die ToJSON() -Methode aufruft, die auf dem POCO definiert ist (siehe oben), und übergebe einen Verweis auf einen JsonTextWriter. Die resultierende JSON-Zeichenfolge wird dann mit dem C# MongoDB-Treiber in ein BsonDocument deserialisiert, das dann in Mongo gespeichert wird. (Das folgende Beispiel stammt von einer ASP.NET Web Forms-Seite, daher die Benutzersteuerelemente des Benachrichtigungsfelds).

private void LoadData() 
{ 
    DateTime startDate = new DateTime(2015,12,31,23,59,59); 
    var collection = _database.GetCollection<BsonDocument>("sessions"); 

    using (DbContext ctx = new DbContext()) 
    { 
     List<Session> sessions = ctx.Sessions.Where().ToList(); 

     foreach (Session item in sessions) 
     { 
      JsonTextWriter writer = null; 
      try 
      { 
       StringWriter sw = new StringWriter(); 
       writer = new JsonTextWriter(sw); 
       writer.CloseOutput = true; 
       item.ToJSON(ref writer); 
       String json = sw.ToString(); 

       BsonDocument doc = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(json); 

       collection.InsertOne(doc); 
       this.ucAlertMsg.Show("bg-info", "Completed without exception"); 
      } 
      catch (Exception ex) 
      { 
       while (ex.InnerException != null) { ex = ex.InnerException; } 
       this.ucAlertMsg.Show("bg-danger", ex.Message); 
      } 
      finally 
      { 
       writer.Close(); 
      } 
     } 
    } 
} 

Dies spart das Dokument ganz gut, aber ich bin nicht in der Lage effektiv die Dokumente abfragen zu, Filterung auf einem Datumsbereich. Ich glaube, basierend auf mehreren anderen Posts und Artikeln, die ich gelesen habe, dass dies möglicherweise daran liegt, dass der Wert als String statt "ISODate()" gespeichert wird.

// 
// This is what IS saved 
{ 
    "_id" : ObjectId("5729128cd9017a248cbe6284"), 
    "StartUTCTimestamp" : "2015-12-15T23:24:06", 
    "StartTimestamp" : "2015-12-15T18:24:06", 
    "EndTimestamp" : "2015-12-15T18:26:59", 
} 

// 
// Is this what I need? 
{ 
    "_id" : ObjectId("5729128cd9017a248cbe6284"), 
    "StartUTCTimestamp" : ISODate("2015-12-15T23:24:06"), 
    "StartTimestamp" : ISODate("2015-12-15T18:24:06"), 
    "EndTimestamp" : ISODate("2015-12-15T18:26:59"), 
} 

In meinem Loaddata() -Methode ich eine Reihe von Konfigurationen auf dem Textwriter versucht haben, die nach einige der Artikel, die ich gelesen haben scheint, wie es geholfen haben sollte ...

StringWriter sw    = new StringWriter(); 
writer      = new JsonTextWriter(sw); 
writer.DateFormatHandling = DateFormatHandling.IsoDateFormat; 
writer.CloseOutput   = true; 

Das Zuweisen des "IsoDateFormat" zu der "DateFormatHanding" -Einstellung des Brenners ergab keinen Unterschied. Ich habe auch versucht "MicrosoftDateFormat" und die Daten wurden noch als String gespeichert, aber das Format war anders.

Die eigentliche Frage

Also das ist alles eingerichtet ... die Frage „Wie kann ich MongoDB Dokumente nach Datum suchen“?

Mit dem C# -Treiber für MongoDB kann ich mit Linq suchen. Hier ist die Linq-Abfrage, die ich verwende.

IMongoCollection<Session> collection = database.GetCollection<Session>("sessions"); 
DateTime startDate = (this.StartDate.HasValue) ? this.StartDate.Value : DateTime.Now.AddDays(-7); 
DateTime endDate = (this.EndDate.HasValue) ? this.EndDate.Value : DateTime.Now; 
var data   = collection.Find<Session>(e => e.StartTimestamp.Value >= startDate && e.StartTimestamp.Value <= endDate).ToList(); 

Da die JSON-Karten auf eine Session POCO direkt zurück sollte ich diese Art verwenden können (?). Ich kann erfolgreich auf andere Felder in der Sitzung POCO filtern. Es sind nur die Daten, die mir passen.

Ich vermute, dass in meiner Implementierung entweder etwas nicht stimmt oder ein Versehen vorliegt oder dass die Daten, da sie als String gespeichert werden, nicht als Date (?) Verglichen werden können.

Jeder Einblick würde sehr geschätzt werden.

Danke, -G

Antwort

1

Nicht viele Menschen diesen Beitrag gesehen haben so vielleicht habe ich es schlecht markiert, aber für die eine Person, die es upvoted Ich habe kommen über, was ich mehr als ein „Umgehen“ betrachten als eine tatsächliche Lösung, aber vielleicht wird es Ihnen auch helfen.

Statt oder zusätzlich Daten im typischen String-Format zu speichern ("yyy/MM/TT HH: mm: ss"), sparte ich die Daten als Int64 Zecken. Der Parser/Schreiber erkennt diese als Zahlen und speichert sie als solche. Und Zahlen sind natürlich leicht sortiert und geordnet.

So ist das Session Modell aus dem Original-Beitrag sieht nun wie folgt ...

// 
// POCO to store in MongoDB 
public class Session 
{ 

    public DateTime? StartUTCTimestamp { get; set; } 

    public DateTime? StartTimestamp { get; set; } 

    public DateTime? EndTimestamp { get; set; } 


    // 
    // If the StartUTCDate is defined then return the number of "ticks" in the date 
    [BsonElement("StartUTCTimestampTicks")] 
    public Int64? StartUTCTimestampTicks 
    { 
     get 
     { 
      return (this.StartUTCTimestamp.HasValue) ? 
        (Int64?)this.StartUTCTimestamp.Value.Ticks : 
        null; 
     } 
    } 

    // 
    // If the StartDate is defined then return the number of "ticks" in the date 
    [BsonElement("StartTimestampTicks")] 
    public Int64? StartTimestampTicks 
    { 
     get 
     { 
      return (this.StartTimestamp.HasValue) ? 
        (Int64?)this.StartTimestamp.Value.Ticks : 
        null; 
     } 
    } 

    // 
    // If the EndDate is defined then return the number of "ticks" in the date 
    [BsonElement("EndTimestampTicks")] 
    public Int64? EndTimestampTicks 
    { 
     get 
     { 
      return (this.EndTimestamp.HasValue) ? 
        (Int64?)this.EndTimestamp.Value.Ticks : 
        null; 
     } 
    } 

    public void ToJSON(ref JsonTextWriter writer) 
    { 
     Session session = this;    

     writer.WriteStartObject(); // { 


     if (session.StartUTCTimestamp.HasValue) 
     { 
      writer.WritePropertyName("StartUTCTimestamp"); 
      writer.WriteValue(session.StartUTCTimestamp); 

      writer.WritePropertyName("StartUTCTimestampTicks"); 
      writer.WriteValue(session.StartUTCTimestampTicks); 
     } 

     if (session.StartTimestamp.HasValue) 
     { 
      writer.WritePropertyName("StartTimestamp"); 
      writer.WriteValue(session.StartTimestamp); 

      writer.WritePropertyName("StartTimestampTicks"); 
      writer.WriteValue(session.StartTimestampTicks); 
     } 

     if (session.EndTimestamp.HasValue) 
     { 
      writer.WritePropertyName("EndTimestamp"); 
      writer.WriteValue(session.EndTimestamp); 

      writer.WritePropertyName("EndTimestampTicks"); 
      writer.WriteValue(session.EndTimestampTicks); 
     } 


     writer.WriteEndObject(); // } 
    } 
} 

Ich habe Eigenschaften hinzugefügt, um die Anzahl der Ticks in jedem Zeitpunkt auszusetzen, falls definiert. Wenn nicht, geben sie einfach null zurück. Ich musste sie mit dem BsonElement Attribut schmücken, um sie für den JsonTextWriter erkennbar zu machen (Nicht sicher, warum diese es und die anderen Eigenschaften nicht benötigten), andernfalls erhielt ich einen Fehler.

Ich habe auch die ToJSON() - Methode für das Modell geändert, um zu überprüfen, ob die Daten vor der Serialisierung der Werte bereitgestellt wurden. Wenn das Datum nicht definiert ist, fügen wir das json-Element einfach nicht zum Dokument hinzu.

Das resultierende Dokument in MongoDB gespeichert jetzt aussieht ...

{ 
    "_id" : ObjectId("572b4486d9016b151846b8ed"), 
    "StartUTCTimestamp" : "2016-04-24T17:02:12", 
    "StartUTCTimestampTicks" : NumberLong(635971141320000000), 
    "StartTimestamp" : "2016-04-24T13:02:12", 
    "StartTimestampTicks" : NumberLong(635970997320000000), 
    "EndTimestamp" : "2016-04-24T13:05:16", 
    "EndTimestampTicks" : NumberLong(635970999160000000) 
} 

Warum diese Werte mit einer „NumberLong()“ Funktion Datentyp gespeichert werden, und die Daten werden nicht mit dem „isodate gespeichert() "Funktion ist mir ein Rätsel, aber ich kann jetzt Daten und Datumsbereiche basierend auf Ticks statt Datumsfolgen abfragen.

IMongoCollection<Session> collection = database.GetCollection<Session>("sessions"); 
DateTime startDate = (this.StartDate.HasValue) ? this.StartDate.Value : DateTime.Now.AddDays(-7); 
DateTime endDate = (this.EndDate.HasValue) ? this.EndDate.Value : DateTime.Now; 
var data   = collection.Find<Session>(e => e.StartTimestampTicks.Value >= startDate.Ticks && e.StartTimestampTicks.Value <= endDate.Ticks).ToList(); 

Das hat mich tatsächlich in Erwägung, die Datumsstrings überhaupt nicht zu speichern. Ich behalte sie mehr als Etiketten, weil sie lesbarer sind, wenn Sie das tatsächliche JsonDocument sehen. Aber solange die primäre Verwendung des Dokuments über eine Benutzerschnittstelle betrachtet werden soll, ist es eine triviale Angelegenheit, Ticks zu Zwecken der Anzeige in besser lesbare Datumszeichenkettenrepräsentationen umzuwandeln.

Wie dem auch sei ... Ich hoffe, dass diese Hilfe ... jemand ... :)

2

Das Datum in Ihrem Filterausdruck erstellt werden muss mit BsonValue.Create (IhrDatum);

Beispiel:

IMongoDatabase db = GetMongoDbConnection(); 
    IMongoCollection<BsonDocument> collection = db.GetCollection<BsonDocument> ("yourCollectionName"); 
    DateTime date = BsonValue.Create(DateTime.Now.Date); 
    var filter = Builders<BsonDocument>.Filter.Gt("dateFieldToFilterOn", date); 
    List<BsonDocument> bsonDocuments = await collection.Find(filter).ToListAsync(); 
Verwandte Themen