Mongodb query to match based on 2 fields - c#

Let me give a example first,
{
$match: {
$or:[
{'sender':1, 'recipient':2},
{'sender':2, 'recipient':1},
{'sender':1, 'recipient':3},
{'sender':7, 'recipient':2},
{'sender':7, 'recipient':3} //goes on may be 20 or 30
]
}
}
I am trying to fetch data based on the sender and the recipient. If the sender and recipient falls in this combination of categories, I pick only that data.
From the above example I can say that the combination of sender:7 and recipient:1 is not valid, only the combination of sender:7 and recipient:2 or recipient:3 is valid.
Is there any way to simplify the above mentioned query in C#?

If you want to write c# queries i would suggest using a repository for this it would make things much easier for you and you can write some entity framework style queries
https://github.com/alexandre-spieser/mongodb-generic-repository
public class TestMongoRepository: BaseMongoRepository, IEmailMongoRepository
{
public TestMongoRepository(string connectionString, string databaseName) : base(connectionString, databaseName)
{
}
//public MongoRepository<T> Create<T>() where T:IEntity
//{
// return new MongoRepository<T>();
//}
}
}
public class Data: Document
{
public string Sender { get; set; }
public string Receiver{ get; set; }
}
then you could query using the following syntaxt
var _yourRepository=new TestMongoRepository("connectionstring","database");
class Combination{
public int Sender{get;set;}
public int Receiver {get;set;}
}
var combinations=new List<Combination>{
new Combination{Sender=1, Receiver=5},
// add your other comibnaitons here
}
var data= _yourRepository.GetAll<Data>(e =>combinations.Any(c=>c.Sender=e.Sender && c.Receiver=e.Receiver) );

Related

How do I correctly use the SendBulkTemplatedEmailRequest from SES in the AWS-SDK-NET?

I am attempting to use the AmazonSimpleEmailService client via the AWS-SDK for .Net, to send a SendBulkTempatedEmailRequest. I have implemented a dedicated handler for actually building the request and making the SendBulkTemplatedEmailAsync call. It is not working as I expect. I think there is a bug with how the request object is serialized and passed to the API.
Here is some sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using Newtonsoft.Json;
namespace Sample.AWS.SES
{
public class SendEmailService
{
private readonly IAmazonSimpleEmailService _sesClient;
public SendEmailService(IAmazonSimpleEmailService sesClient)
{
_sesClient = sesClient;
}
public async Task<string> SendBulkEmailAsync(SesOrderCreatedBulkTemplate data)
{
var result = string.Empty;
var request = new SendBulkTemplatedEmailRequest
{
Template = data.Template,
ConfigurationSetName = data.ConfigurationSet,
DefaultTemplateData = JsonConvert.SerializeObject(data.DefaultTemplateData),
Source = data.Source,
Destinations = data.Destinations
.Select(d => new BulkEmailDestination
{
Destination = new Destination
{
ToAddresses = d.ToAddresses.ToList(),
},
ReplacementTemplateData = string.Empty
})
.ToList(),
ReplyToAddresses = data.ReplyToAddresses.ToList()
};
try
{
var resp = await _sesClient.SendBulkTemplatedEmailAsync(request);
}
catch (Exception ex)
{
var msgEx = new Exception("Error sending message to SES.", ex);
throw msgEx;
}
return result;
}
public class SesOrderCreatedBulkTemplate
{
public string Source { get; set; }
public string Template { get; set; }
public string ConfigurationSet { get; set; }
public IEnumerable<Destination> Destinations { get; set; }
public MyTemplateData DefaultTemplateData { get; set; }
public IEnumerable<string> ReplyToAddresses { get; set; }
public string ReturnPath { get; set; } = string.Empty;
}
public class DestinationObj
{
public IEnumerable<string> ToAddresses { get; set; }
public MyTemplateData ReplacementTemplateData { get; set; }
public DestinationObj() {}
}
public class MyTemplateData
{
public List<Person> Tenants { get; set; }
}
public class Person
{
public string PersonName { get; set; }
public List<object> PersonData { get; set; }
}
}
}
The properties for SourceArn, TemplateArn and ReturnPathArn are omitted on purpose. According the SES documentation, the SDK wraps the low-level functionality of the Amazon SES API with higher-level data types and function calls that take care of the details for you. When I view the API documentation for sending bulk email, the ARN properties are all list as not required. When I look at the some CLI examples, it is the same. When I look at the documentation for the SDK for .Net v3, it is ambiguous (not marked as required or optional).
Because the SDK supposed to wrap the low-level functionality of the API, I do not believe the ARN values are required (neither the API nor the CLI require them). However, when I attempt to actually use the request object created in the code snippet, I get an error that says InvalidTemplateData.
If I serialize the request object to JSON, then remove the 3 ARN fields from the string, I can use either the API or the CLI to successfully send the message.
In addition to not specifying a value for the ARN's, I have tried (for all 3 ARN values):
specificArn = string.empty;
specificArn = new {};
specificArn = "";
I have also tried explicitly newing-up the object separate from initializing the properties:
var request = new SendBulkTemplatedEmailRequest();, and then individually populating the properties.
If I don't initialize the ARN values, I get an error about NoneType vs StringType when the send method is called. The variations on string initialization that I tried result in InvalidTemplateData errors.
Note, I do know ARN values for Source and ReturnPath. I do not have an ARN value for the template we use. Supposedly, using the CLI, when you create a template you should receive a response back that includes the ARN for the template. I get no response from the CLI when I create a template, but it does get created every time I try. The describe-template CLI command is not valid when you specify SES and responds with an error if I don't specify the workspace (whatever you call the SES space) value.
Does anyone have a suggestion on how to solve this?
From the provided code it's hard to say what you pass into API.
This is how I send bulk emails:
SES configuration
create a template (taken from AWS SES docs) and save it to a file - my-template.json
{
"Template": {
"TemplateName": "my-template",
"SubjectPart": "Greetings, {{name}}!",
"HtmlPart": "<h1>Hello {{name}},</h1><p>Your favorite animal is {{favoriteanimal}}.</p>",
"TextPart": "Dear {{name}},\r\nYour favorite animal is {{favoriteanimal}}."
}
}
create a template via CLI aws ses create-template --cli-input-json file://my-template.json
SES .NET SDK API
async Task SendAsync(string[] receivers)
{
var destinations = receivers
.Select(receiver => new BulkEmailDestination
{
Destination = new Destination(new List<string> { receiver }),
ReplacementTemplateData = ToJson(receiver, "Doggy")
})
.ToList();
var bulkTemplate = new SendBulkTemplatedEmailRequest
{
Source = "your-email#gmail.com", // your email you bulk send from
Template = "my-template", // your template name
DefaultTemplateData = ToJson("<not set>", "<not set>"),
Destinations = destinations
};
await _client.SendBulkTemplatedEmailAsync(bulkTemplate);
}
// Create replacement data by serializing Dictionary
string ToJson(string name, string favoriteanimal)
=> JsonSerializer.Serialize(new Dictionary<string, string>
{
{ "name", name },
{ "favoriteanimal", favoriteanimal }
});

EF CF - List<T> of Primitive Type

I have the following:
public class Broadcast {
public int NumUsersToMessage { get; set; }
public int NumMessagesQueued { get; set; }
public string DbUsersMessaged { get; set; }
public int NumMessagesSent {
get {
return UsersMessaged.Count();
}
}
public List<int> UsersMessaged {
get {
return DbUsersMessaged == null ? new List<int>() : DbUsersMessaged.Split(',').Select(Int32.Parse).ToList();
}
set {
DbUsersMessaged = value != null ? String.Join(",", value) : null;
}
}
}
My goal here is to only ever access DbUsersMessaged through UsersMessaged. I'm attempting to do broadcast.UsersMessaged.Add(2), however since this is not an assignment, I can't get the property to behave as I like. Instead, I have to do this:
tempList = broadcast.UsersMessaged();
tempList.Add(2);
broadcast.UsersMessaged = tempList;
db.SaveChanges();
Which is obviously unwieldy. I'm considering making an AddReassign extension method but I want to know - what's the standard practice here for supporting Lists of primitive types? It looks like even with the extension method, my best shot looks like this:
broadcast.UsersMessaged = broadcast.UsersMessaged.AddReassign(2) // yuck!
Before anyone asks - we've intentionally denormalized this for performance reasons.
If you don't care about performance, you can create own list:
public class MyList : IList<int>
{
private List<int> underlyingList;
private Broadcast entity;
public MyList(Broadcast entity)
{
this.entity = entity;
this.underlyingList = entity.DbUsersMessaged?.Split(",") ?? new List<int>();
}
public void Add(int i)
{
this.underlyingList.Add(i);
this.entity.DbUsersMessaged = String.Join(",", underylingList);
}
// other interface memebers impl
}
Then
MyList list;
public IList<int> UsersMessaged {
get {
return myList ?? (myList = new MyList(this));
}
}
Of course it is only sample.
I recommend you to have a look at this: Entity Framework 5 - Looking for Central Point to Execute Custom Code after Entity is Loaded from Database
And then convert from string to list, and then use Saving Changes event to convert back into the string construction when saving.
Then, for performance, maybe you want to use byte[] rather than a string for storing the data in the database.

RavenDB - stream index query results in exception

We're currently trying to use the Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(IQueryable<T> query, CancellationToken token = null), running into some issues.
Our document look something like:
public class Entity
{
public string Id { get; set; }
public DateTime Created { get; set; }
public Geolocation Geolocation { get; set; }
public string Description { get; set; }
public IList<string> SubEntities { get; set; }
public Entity()
{
this.Id = Guid.NewGuid().ToString();
this.Created = DateTime.UtcNow;
}
}
In combination we've a view model, which is also the model were indexing:
public class EntityViewModel
{
public string Id { get; set; }
public DateTime Created { get; set; }
public Geolocation Geolocation { get; set; }
public string Description { get; set; }
public IList<SubEntity> SubEntities { get; set; }
}
And ofcourse, the index, with the resulttype inheriting from the viewmodel, to enable that SubEntities are mapped and output correctly, while enabling the addition of searchfeatures such as fulltext etc.:
public class EntityWithSubentitiesIndex : AbstractIndexCreationTask<Entity, EntityWithSubentitiesIndex.Result>
{
public class Result : EntityViewModel
{
public string Fulltext { get; set; }
}
public EntityWithSubentitiesIndex ()
{
Map = entities => from entity in entities
select new
{
Id = entity.Id,
Created = entity.Created,
Geolocation = entity.Geolocation,
SubEntities = entity.SubEntities.Select(x => LoadDocument<SubEntity>(x)),
Fulltext = new[]
{
entity.Description
}.Concat(entity.SubEntities.Select(x => LoadDocument<SubEntity>(x).Name)),
__ = SpatialGenerate("__geolokation", entity.Geolocation.Lat, entity.Geolocation.Lon)
};
Index(x => x.Created.Date, FieldIndexing.Analyzed);
Index(x => x.Fulltext, FieldIndexing.Analyzed);
Spatial("__geolokation", x => x.Cartesian.BoundingBoxIndex());
}
}
Finally we're querying like this:
var query = _ravenSession.Query<EntityWithSubentitiesIndex.Result, EntityWithSubentitiesIndex>()
.Customize(c =>
{
if (filter.Boundary == null) return;
var wkt = filter.Boundary.GenerateWkt().Result;
if (!string.IsNullOrWhiteSpace(wkt))
{
c.RelatesToShape("__geolokation", wkt, SpatialRelation.Within);
}
})
.AsQueryable();
// (...) and several other filters here, removed for clarity
var enumerator = await _ravenSession.Advanced.StreamAsync(query);
var list = new List<EntityViewModel>();
while (await enumerator.MoveNextAsync())
{
list.Add(enumerator.Current.Document);
}
When doing so we're getting the following exception:
System.InvalidOperationException: The query results type is 'Entity'
but you expected to get results of type 'Result'. If you want to
return a projection, you should use
.ProjectFromIndexFieldsInto() (for Query) or
.SelectFields() (for DocumentQuery) before calling to
.ToList().
According to the documentation, the Streaming API should support streaming via an index, and querying via an IQueryable at once.
How can this be fixed, while still using an index, and the streaming API, to:
Prevent having to page through the normal query, to work around the default pagesize
Prevent having to load the subentities one at a time when querying
Thanks in advance!
Try to use:
.As<Entity>()
(or .OfType<Entity>()) in your query. That should work in the regular stream.
This is a simple streaming query using "TestIndex" that is an index over an entity Test and I'm using a TestIndex.Result to look like your query. Note that this is actually not what the query will return, it's only there so you can write typed queries (ie. .Where(x => x.SomethingMapped == something))
var queryable = session.Query<TestIndex.Result, TestIndex>()
.Customize(c =>
{
//do stuff
})
.As<Test>();
var enumerator = session.Advanced.Stream(queryable);
while (enumerator.MoveNext())
{
var entity = enumerator.Current.Document;
}
If you instead want to retrieve the values from the index and not the actual entity being indexed you have to store those as fields and then project them into a "view model" that matches your mapped properties. This can be done by using .ProjectFromIndexFieldsInto<T>() in your query. All the stored fields from the index will be mapped to the model you specify.
Hope this helps (and makes sense)!
Edit: Updated with a, for me, working example of the Streaming API used with ProjectFromIndexFieldsInto<T>() that returns more than 128 records.
using (var session = store.OpenAsyncSession())
{
var queryable = session.Query<Customers_ByName.QueryModel, Customers_ByName>()
.Customize(c =>
{
//just to do some customization to look more like OP's query
c.RandomOrdering();
})
.ProjectFromIndexFieldsInto<CustomerViewModel>();
var enumerator = await session.Advanced.StreamAsync(queryable);
var customerViewModels = new List<CustomerViewModel>();
while (await enumerator.MoveNextAsync())
{
customerViewModels.Add(enumerator.Current.Document);
}
Console.WriteLine(customerViewModels.Count); //in my case 504
}
The above code works great for me. The index has one property mapped (name) and that property is stored. This is running the latest stable build (3.0.3800).
As #nicolai-heilbuth stated in the comments to #jens-pettersson's answer, it seems to be a bug in the RavenDB client libraries from version 3 onwards.
Bug report filed here: http://issues.hibernatingrhinos.com/issue/RavenDB-3916

One Table, Several Views, one C# Class

I have a single c# class defined as
class HighScore
{
public int Id { get; set; }
[DataMember(Name = "PlayerName")]
public string PlayerName { get; set; }
[DataMember(Name = "PlayerCountry")]
public int PlayerCountry { get; set; }
[DataMember(Name = "PlayerTime")]
public double PlayerTime { get; set; }
[DataMember(Name = "PlayerBadge")]
public int PlayerBadge { get; set; }
}
And a table in my Azure mobile services SQL database that contains several records of this type. I have a number of views
select * from tellingthetime.HighScore where PlayerBadge=0
where the PlayerBadge is a number from 0 to 4. I also have a number of read scripts added to my Mobile Service that query the view and return the appropriate rows.
function read(query, user, request) {
mssql.query("select * from OneStarBadgeLeaderBoard", {
success: function(results) {
console.log(results);
request.respond(statusCodes.OK, results);
}
});
}
The above script is called OneStarBadgeLeaderBoard, but my class is called HighScore. The code below I call to Get the underlying table.
private IMobileServiceTable<HighScore> HighScoreTable = App.MobileService.GetTable<HighScore>();
Without creating a different class name, the defintions are all the same as the SQL returned data is the same, for each read script, how do I make this work so I can call any read script, which queries the appropriate view to retrieve the values I need?
Hope that make sense.
Many thanks,
Jason.
P.S. Of course, I could read the entire table and query it with LINQ on the client, but that will increase the amount of data to download.
Got this from Josh Twist
function read(query, user, request) {
var dispatch = {
op1 : operation1,
op2 : operation2,
}
if (request.parameters.operation && dispatch.hasOwnProperty(request.parameters.operation)) {
dispatch[request.parameters.operation](query, user, request);
return;
}
else
{
// default path for execution
request.execute();
}
}
function operation1(query, user, request) {
request.respond(200, "this result is from operation1");
}
function operation2(query, user, request) {
request.respond(200, "this result is from operation2");
}
http://www.thejoyofcode.com/Dispatching_to_different_query_functions_in_Mobile_Services.aspx
Also this code sends a filtered OData request that only retrieves the required records. Got it from the ToDo list Azure Mobile Service Tutorials
private async void RefreshTodoItems()
{
// This code refreshes the entries in the list view by querying the TodoItems table.
// The query excludes completed TodoItems
var results = await todoTable
.Where(todoItem => todoItem.Complete == false)
.ToListAsync();
items = new ObservableCollection<TodoItem>(results);
ListItems.ItemsSource = items;
}

C# limit length of a string

I have the following class:
public class VendorClass {
public int VendorID { get; set; }
public string VendorName { get; set; }
}
The fields above match fields in the database table.
In the case of say VendorName, how do I give it a field width ?
VendorName maps to a field in the database which is varchar(15)
You can't limit the length of the string but you can use properties with backing fields to achieve the desired result :
public class VendorClass
{
public int VendorID { get; set; }
private string _vendorName;
public string VendorName
{
get { return _vendorName; }
set
{
if (value.Length > 15)
{
_vendorName = value.Substring(0,15);
} else {
_vendorName = value;
}
}
}
}
Strings in C# have almost-arbitrary length.
When loading from your database, it will automatically accommodate the actual string length. When saving to the database, your business logic, data layer or ORM (as appropriate) will need to ensure the proper maximum length.
A string can't have a set length in C#. You will have to handle the db length through some other mechanism like validation. Can't really tell you more without more details.
I would question why you would do this in c# code. However this link has a couple of ways around this. I suppose either truncation or taking a subsring is the best option. You could also make sure that the UI (or the model-view) takes care of details such as this.
I am not sure exactly what you are asking, but if you want to know the maximum length of a string, this question can help you.
If you want to limit the number of characters entered, I would suggest that you use server-side validation and/or client-side validation.
I just met a problem like what you described and found a way to create a limited length's string. Maybe a little inflexible but concise when there are only finite varchar length definitions in database.
Firstly introduce some basic classes:
public class Length16
{
public static int StringLength { get => 16; }
}
public class Length8
{
public static int StringLength { get => 8; }
}
public class Length15
{
public static int StringLength { get => 15; }
}
public class LimitedLengthString<T>
{
private string _sValue;
public LimitedLengthString(string sNewValue)
{
_sValue = sNewValue;
}
public static implicit operator LimitedLengthString<T>(string sNewValue)
{
var prop = typeof(T).GetProperty("StringLength");
int iLength = (int)prop.GetValue(null);
if (sNewValue.Length > iLength)
{
throw new Exception($"New string is too long! Allowed length {iLength}.");
}
return new LimitedLengthString<T>(sNewValue);
}
public static implicit operator string(LimitedLengthString<T> sSource)
{
return sSource.ToString();
}
public override string ToString()
{
return _sValue;
}
}
public class AutoTruncatedString<T>
{
private string _sValue;
public AutoTruncatedString(string sNewValue)
{
_sValue = sNewValue;
}
public static implicit operator AutoTruncatedString<T>(string sNewValue)
{
var prop = typeof(T).GetProperty("StringLength");
int iLength = (int)prop.GetValue(null);
return new AutoTruncatedString<T>(sNewValue.Substring(0, iLength));
}
public static implicit operator string(AutoTruncatedString<T> sSource)
{
return sSource.ToString();
}
public override string ToString()
{
return _sValue;
}
}
Use them like this:
LimitedLengthString<Length8> sLimitedLength8;
sLimitedLength8 = "asdfgasdfg"; // will error out
AutoTruncatedString<Length8> sAutoTruncated8;
sAutoTruncated8 = "asdfgasdfg"; // will be truncated
sLimitedLength8 will throw an error if you try to assign a string longer than 8 and sAutoTruncated8 will truncate the string you assign to it.
For you, you can define the VendorName this way:
public LimitedLengthString<Length15> VendorName { get; set; }
Hope this could help you.

Categories

Resources