1
votes

I am trying to create a static index for the following sample class:

public class Board {
...other assorted fields
List<dynamic> Messages {get; set;}
internal Board() {Messages = new List<dynamic>();}
}

The index is to filter boards which have messages which are a older than a certain date. The aim is to perform an "update" operation on messages which are due today, update their content, and persist them back. The index is needed to avoid traversing all the messages for a board for all clients as that may be computationally expensive. Messages is a list of message types which inherit from a base class which contains a property ExpiryDate.

Trying to create an index like follows results in an "An expression tree may not contain a dynamic operation" error. I know that the dynamic type does not play well with Linq queries hence the need to use LuceneQueries instead of Query() in RavenDB. Is there any way to make this index work with dynamic properties? Thanks!

 public class ScanBoardMessagesIndex : AbstractIndexCreationTask<Board>
  {
    public ScanBoardMessagesIndex () {
      Map = boards => from board in boards
                     where board.Messages.Any(msg => ((MessageItem) msg).ExpiryDate <= DateTime.UtcNow.Date)
                     select board;
    }
  }

EDIT:

I ran into a raven serialization issue because the metadata clr-type of existing Board documents was set to a class namespace which was not valid anymore. I am doing a migration project so I went ahead and first issued a patch to change the metadata clr-type of the existing documents before migrating them to the new data structure which uses a base/abstract class for list of Messages instead of type dynamic.

1

1 Answers

1
votes

A Map/Reduce index seems more appropriate for the given requirements. Effectively, you want to be able to query boards by the oldest expiry date of messages in the board. This is an aggregating operation, exactly what Map/Reduce was designed to solve. Also, using a base class for messages will allow you to define the index without resorting to the lower level IndexDefinition:

public class Message
{
    public DateTime ExpiryDate { get; set; }
}

public class Board
{
    public string Id { get; set; }
    public List<Message> Messages { get; set; }
}

public class OldestExpiryDateMessageInBoard : AbstractIndexCreationTask<Board, OldestExpiryDateMessageInBoard.Result>
{
    class Result
    {
        public string BoardId { get; set; }
        public DateTime OldestExpiryDate { get; set; }
    }

    public OldestExpiryDateMessageInBoard()
    {
        this.Map = boards => from board in boards
                             from message in board.Messages
                             select new
                             {
                                 BoardId = board.Id,
                                 OldestExpiryDate = message.ExpiryDate
                             };

        this.Reduce = results => from result in results
                                 group result by result.BoardId into g
                                 select new
                                 {
                                     BoardId = g.Key,
                                     OldestExpiryDate = g.Min(x => x.OldestExpiryDate)
                                 };
    }
}

You can then query this index with Lucene syntax.