I am consuming a webservice for getting the data and i am success fully getting back the data
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{ String username = txtblock4.Text.Trim();
String hash = txtblock8.Text.Trim();
client.UploadStringAsync(new
Uri("http://www.picturelove.mobi/picturelove3/getmessages.php?loginType=N&email=" +
username + "&hash=" + hash), "Post");
client.UploadStringCompleted += new
UploadStringCompletedEventHandler(client_UploadStringCompleted);
}
I am parsing the xml response like below with two functions save message data and generate message data i am gettin the data in a list.
void client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Error != null)
txtblock10.Text = e.Error.Message.Trim();
else
txtblock10.Text = e.Result.Trim();
String XmlString = txtblock10.Text.Trim();
using (XmlReader reader = XmlReader.Create(new StringReader(XmlString)))
{
while (reader.ReadToFollowing("all_messages"))
{
while (reader.Read())
{
try
{
reader.ReadToFollowing("id");
string id = reader.ReadElementContentAsString();
reader.MoveToAttribute("from");
string n_from = reader.ReadElementContentAsString();
reader.MoveToAttribute("to");
string n_to = reader.ReadElementContentAsString();
reader.MoveToAttribute("time");
string n_time = reader.ReadElementContentAsString();
reader.MoveToAttribute("sub");
string n_sub = reader.ReadElementContentAsString();
reader.MoveToAttribute("ct");
string n_ct = reader.ReadElementContentAsString();
reader.MoveToAttribute("txt");
string n_txt = reader.ReadElementContentAsString();
reader.MoveToAttribute("msg_image");
string n_image = reader.ReadElementContentAsString();
reader.MoveToAttribute("gender");
string n_gender = reader.ReadElementContentAsString();
reader.MoveToAttribute("name");
string n_name = reader.ReadElementContentAsString();
reader.MoveToAttribute("avatar");
string n_avatar = reader.ReadElementContentAsString();
ObservableCollection<SampleData> dataSource = new ObservableCollection<SampleData>();
dataSource.Add(new SampleData() { Name = txtblock11.Text, Text = txtblock12.Text,
Time= txtblock13.Text, Picture = txtblock9.Text });
// listBox.Items.Add(new SampleData() { Name = txtblock11.Text, Text = txtblock8.Text,
Time = txtblock5.Text, Picture = txtblock12.Text });
SaveMessageData(new SampleData() { Name = txtblock11.Text, Text = txtblock12.Text, Time
= txtblock13.Text, Picture = txtblock9.Text });
// listBox1.ItemsSource =
this.GenerateMessageData();
}
catch
{
//MessageBox.Show("No New Messages For You", "No Message", MessageBoxButton.OK);
break;
}
}
}
}
}
}
public class SampleData
{
public string Name { get; set; }
public string Text { get; set; }
public string Time { get; set; }
public string Picture { get; set; }
}
public void SaveMessageData()
{
using (var isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var isoStream = new IsolatedStorageFileStream("MyTextfile.txt", FileMode.Append,
isoStorage))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<SampleData>));
using (XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
List<SampleData> data = new List<SampleData>();
foreach (SampleData obj in Listbox.Items)
{
data.Add(obj);
}
data.Add(msg);
if (data != null)
serializer.Serialize(xmlWriter, data);
}
}
}
}
}
public void GenerateMessageData()
{
List<SampleData> data;// = new List<SampleData>();
try
{
using (IsolatedStorageFile myIsolatedStorage =
IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("MyTextfile.txt",
FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<SampleData>));
data = (List<SampleData>)serializer.Deserialize(stream);
this.Listbox.ItemsSource = data;
return data;
}
}
}
catch (Exception exp)
{
MessageBox.Show("No New Messages For You", "No Message", MessageBoxButton.OK);
}
return null;
}
But, The real problem is if there are two set of data(two messages) if i'm getting both are showing in the same list. How to manipulate or iterate multiple lists if there are multiple data?
If you need to merge two lists and there may be items which exist in both but you only want to appear in the merged list once, the easiest solution is to simply check if it's already in the list before adding it.
Related
I'm struggling to write a test class for visualforce email template that I have created as per business requirements but I don't know who to cover "get' methods in the test class.. I spent long hours doing research on google unfortunately I haven't found anything helpful.
I tried many ways to cover 'get' method in test class like below
myClass cls = new myClass;
cls.getMethod();
cls.getProperty = 'test';
but it's giving me the error message "List index out of bounds: 0" because I'm calling another method in getter and that methods takes list of records
Controller Class:
public class JobChangesEmail_ComplianceTeam_Cls {
public string jobId {get; set;}
// public string jUrl{get; }
// public string clientUrl {get; }
// public string endclientUrl {get;}
public static list<job__c> jobs;
public JobChangesEmail_ComplianceTeam_Cls()
{
jobs = [select j.Id,j.name,j.other_location__c,j.account__c,j.account__r.id, j.account__r.name,j.Job_Start_Date__c,j.Job_End_Date__c,j.Job_Status__c,j.account__r.Spreadsheet_SOPs__c,
j.account__r.Facility_SOPs__c,j.End_Client__c,End_Client__r.id,j.End_Client__r.name,j.End_Client__r.Spreadsheet_SOPs__c, j.End_Client__r.Facility_SOPs__c from Job__c j where Id=:jobId];
system.debug('-------------------------------------jobId '+jobId);
}
public list<job__c> getjbs()
{
jobs = [select j.Id,j.name,j.other_location__c,j.account__c, j.account__r.name,j.Job_Start_Date__c,j.Job_End_Date__c,j.Job_Status__c,j.account__r.Spreadsheet_SOPs__c,
j.account__r.Facility_SOPs__c,j.End_Client__c,j.End_Client__r.name,j.End_Client__r.Spreadsheet_SOPs__c, j.End_Client__r.Facility_SOPs__c from Job__c j where Id=:jobId];
system.debug('-------------------------------------jobId '+jobId);
return jobs;
}
Public static string generatejobUrl(list<job__c> jobs)
{
string jurl = System.URL.getSalesforceBaseUrl().toExternalForm() + '/'+jobs[0].Id;
return jurl;
}
Public static string generateclientUrl(list<job__c> jobs)
{
string cUrl = System.URL.getSalesforceBaseUrl().toExternalForm()+'/'+jobs[0].account__r.id;
return cUrl;
}
Public static string generateEndclientUrl(list<job__c> jobs)
{
string endCUrl = System.URL.getSalesforceBaseUrl().toExternalForm()+'/'+jobs[0].end_client__r.id;
return endCUrl;
}
public String getjUrl()
{
string jbUrl;
list<job__c> jobs = [select Id,name from Job__c where Id=:jobId];
jbUrl= System.URL.getSalesforceBaseUrl().toExternalForm() + '/'+jobs[0].Id;
return jbUrl;
}
public string clientUrl
{
get { return generateclientUrl(jobs); }
}
public string endclientUrl
{
get { return generateEndclientUrl(jobs); }
}
}
Test class I have written so far:
#isTest(seeAllData= true)
Public class JobChangesEmail_ComplianceTeam_Cls_test {
#isTest
public static void createJob()
{
string clienturl;
string endClienturl;
string joburl;
list<job__c> jobs = new list<job__c>();
Account a = new account(name='ghghghg',type='Other', Account_Status__c='Active',phone='9090909090');
insert a;
Contact contact = new Contact(FirstName='John',LastName='Adams',email='test#gamil.com',phone='14243443', accountid = a.id);
insert contact;
// Opportunity o = [Select id, Name, CloseDate ,Location__c ,AccountId from opportunity limit 1];
Job__c job = new Job__c();
job.Name = 'NewJob'; // Set to match test record
job.X_KOL__c = false; // Set to match test record
job.DE_Job_Type__c = null; // Set to match test record
job.Job_Name_Subject__c = 'TestJob';
job.Job_Start_Date__c = Date.today();
job.End_Client__c = a.Id;
job.Other_Location__c = 'SOS'; // Set to match test record
job.PM__c = 'Amber Anderson';
job.Booked_By__c = UserInfo.getUserId();
job.Account_Manager_COE__c = UserInfo.getUserId();
job.Account__c = a.Id;
job.Contact__c = contact.Id;
job.Respondent_Type__c = 'Acne';
job.Job_Status__c = 'Tentative';
job.Job_Qualification__c = 'Qualitative';
job.Recruitment_Method__c = 'Client List';
job.On_Site_Recruits__c = 5;
job.Off_Site_Recruits__c = 5;
job.London_Project_Number__c = 'Valyria';
job.Recruiting_Begin_Date__c = Date.today();
jobs.add(job);
if(jobs.size()>0)
{
insert jobs;
}
test.startTest();
clienturl = JobChangesEmail_ComplianceTeam_Cls.generateclientUrl(jobs);
endClienturl = JobChangesEmail_ComplianceTeam_Cls.generateEndclientUrl(jobs);
joburl = JobChangesEmail_ComplianceTeam_Cls.generatejobUrl(jobs);
Test.stopTest();
string cUrl = System.URL.getSalesforceBaseUrl().toExternalForm()+'/'+jobs[0].account__r.id;
string Eurl = System.URL.getSalesforceBaseUrl().toExternalForm()+'/'+jobs[0].end_client__r.id;
string jUrl = System.URL.getSalesforceBaseUrl().toExternalForm() + '/'+jobs[0].Id;
system.assertEquals(clienturl, cUrl);
system.assertEquals(endClienturl, Eurl);
system.assertEquals(joburl, jUrl);
JobChangesEmail_ComplianceTeam_Cls cls = new JobChangesEmail_ComplianceTeam_Cls();
cls.getjbs();
string id = cls.jobId;
id = [select id from job__c limit 1].id;
system.assertNotEquals(id,job.id);
}
}
above test class is not covering get methods or properties
I have an app that allows users to log in via facebook, once user enters their credentials - My api request saves the user onto the database and auto-generates a user token(This is unique to each user). In order to display user specific details once user logs in - the token needs to be referenced. I am trying to get this token to the PCL project but it returns null just for the token. When I tried passing another string like name, it passes the correct value. Any help will be much appreciated.Thanks
FacebookRender in droid:
public class FacebookRender : PageRenderer
{
public FacebookRender()
{
CustomerService customerService = new CustomerService();
String error;
var activity = this.Context as Activity;
var auth = new OAuth2Authenticator(
clientId: "",
scope: "",
authorizeUrl: new Uri("https://www.facebook.com/dialog/oauth/"),
redirectUrl: new Uri("https://www.facebook.com/connect/login_success.html")
);
auth.Completed += async (sender, eventArgs) =>
{
try
{
if (eventArgs.IsAuthenticated)
{
await AccountStore.Create().SaveAsync(eventArgs.Account, "FacebookProviderKey");
var accessToken = eventArgs.Account.Properties["access_token"].ToString();
var expiresIn = Convert.ToDouble(eventArgs.Account.Properties["expires_in"]);
var expiryDate = DateTime.Now + TimeSpan.FromSeconds(expiresIn);
var request = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me?fields=email,first_name,last_name,gender,picture"), null, eventArgs.Account);
var response = await request.GetResponseAsync();
var obj = JObject.Parse(response.GetResponseText());
var id = obj["id"].ToString().Replace("\"", "");
var name = obj["first_name"].ToString().Replace("\"", "");
var surname = obj["last_name"].ToString().Replace("\"", "");
var gender = obj["gender"].ToString().Replace("\"", "");
//var email = obj["email"].ToString().Replace("\"", "");
Customer.Customers cust = new Customer.Customers();
cust.Credentials = new Customer.Credentials();
cust.Name = name;
cust.Surname = surname;
cust.Email = "";
cust.MobilePhone = "";
cust.DOB = DateTime.Now;
cust.Number = "";
cust.City = "";
cust.Region = "";
cust.Country = "";
cust.DeviceToken = "sample";
cust.Credentials.SecretKey = "";
await customerService.AddCustomer(cust);
App.SaveToken(cust.Credentials.Token); - **//This is where I am passing the token**
App.NavigateToProfile(string.Format(name + surname));
}
else
{
App.NavigateToProfile("Invalid Login");
}
}
catch(Exception ex)
{
error = ex.Message;
}
};
activity.StartActivity(auth.GetUI(activity));
}
App.cs
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
public static void NavigateToProfile(string message)
{
App.Current.MainPage = (new Profile(message));
}
static string _Token;
public static string Token
{
get { return _Token; }
}
public static void SaveToken(string token)
{
_Token = token;
}
AboutPage.cs - I am passing the token in a label just to see if it's passing
public partial class About : ContentPage
{
private Label _lbltoken;
public About()
{
//InitializeComponent();
Appearing += (object s, EventArgs a) => {
_lbltoken.Text = App.Token;
};
string tk = App.Token;
_lbltoken = new Label()
{
FontSize = 20,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Text = tk,
};
var stack = new StackLayout
{
VerticalOptions = LayoutOptions.StartAndExpand,
Children = { _lbltoken },
};
Content = stack;
}
}
You can use the MessagingCenter.
Messages may be sent as a result like a button click, a system event or some other incident. Subscribers might be listening in order to change the appearance of the user interface, save data or trigger some other operation.
More Info
I don't really now if its good idea use static fields in App class. Xamarin access all fields with service locator, App.Current.[property] I will suggest you try to change these fields to public
string _Token;
public string Token
{
get { return _Token; }
}
public void SaveToken(string token)
{
_Token = token;
}
and use it with App.Current.SaveToken(token) or App.Current.Token
I need to read objects and save them in array. I did that on c# but can't figure out how to do that on actionscript.
c# example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TestingWSDLLOad.ServiceReference2;
namespace TestingWSDLLOad
{
class Program
{
static void Main(string[] args)
{
ServiceReference2.Service1Client testas = new ServiceReference2.Service1Client();
SortedList<int, PlayListItem> playList = new SortedList<int, PlayListItem>();
int cc = 0;
foreach (var resultas in testas.GetPlayList(394570))
{
PlayListItem ss = new PlayListItem(resultas.Id, resultas.VideoId, resultas.Path);
playList.Add(cc, ss);
cc++;
}
Console.WriteLine(playList[0].Id);
Console.ReadKey();
}
}
public class PlayListItem
{
public int VideoId { get; private set; }
public string Path { get; private set; }
public int Id { get; private set; }
public PlayListItem(int id, int videoId, string path)
{
Id = id;
VideoId = videoId;
Path = path;
}
}
}
I know how to get a simple result from wsdl using actionscript, but don't know how to get objects with parameteres and save them.
Service has a method GetPlaylist(int value) which returns an array of objects (id, videoId, path). How to handle this and save them ?
Here is my as3:
package {
public class data extends CasparTemplate {
var flvControl:FLVPlayback;
var refreshTimer:Timer;
var videoList:Array;
var videoNew:Array;
var videoMaxIds:Array;
var videoNewMaxIds:Array;
var videoIndex:uint;
var videoIdFrom:uint;
var loopAtEnd:Boolean;
var _playListItems:Array;
var _playList:PlayListItem;
var gotNewPlaylist:Boolean;
var webService:WebService;
var serviceOperation:AbstractOperation;
public function data()
{
_playListItems = new Array();
flvControl = new FLVPlayback();
videoNew = new Array();
videoNewMaxIds = new Array();
videoIndex = 0;
videoIdFrom = videoMaxIds[videoIndex];
loopAtEnd = true;
gotNewPlaylist = false;
refreshTimer = new Timer(20000);
refreshTimer.addEventListener(TimerEvent.TIMER, getNewPlaylist);
refreshTimer.start();
flvControl.addEventListener(VideoEvent.COMPLETE, completeHandler);
flvControl.addEventListener(VideoEvent.STATE_CHANGE, vidState);
flvControl.setSize(720, 576);
flvControl.visible = true;
//addChild(flvControl);
var url:String = "http://xxx/yyy.svc?wsdl";
webService = new WebService();
webService.loadWSDL(url);
webService.addEventListener(LoadEvent.LOAD, BuildServiceRequest);
}
function BuildServiceRequest(evt:LoadEvent):void
{
webService.removeEventListener(LoadEvent.LOAD, BuildServiceRequest);
//serviceOperation.addEventListener(ResultEvent.RESULT, displayResult);
for (var resultas in webService.getOperation("GetPlaylist(394575)"))
{
trace(resultas.Id);
}
//serviceOperation = webService.getOperation("GetPlaylist");
//serviceOperation.arguments[{videoId: "394575"}];
}
private function displayResult(e:ResultEvent):void
{
trace("sss");
}
// Handle the video completion (load the next video)
function completeHandler(event:VideoEvent):void
{
if (gotNewPlaylist)
{
videoList = videoNew;
videoMaxIds = videoNewMaxIds;
videoNew = null;
videoNewMaxIds = null;
gotNewPlaylist = false;
videoIndex = 0;
} else
videoIndex++;
nextVideo();
}
private function vidState(e:VideoEvent):void {
var flvPlayer:FLVPlayback = e.currentTarget as FLVPlayback;
if (flvPlayer.state==VideoState.CONNECTION_ERROR) {
trace("FLVPlayer Connection Error! -> path : "+flvPlayer.source);
videoIndex++;
nextVideo();
} else if (flvPlayer.state==VideoState.DISCONNECTED) {
videoIndex++;
nextVideo();
}
}
function nextVideo():void
{
trace("Video List:"+videoList.toString());
if( videoIndex == videoList.length ){
if( loopAtEnd )
{
videoIndex = 0;
} else { return; }
}
flvControl.source = videoList[videoIndex];
if (videoIdFrom < videoMaxIds[videoIndex])
videoIdFrom = videoMaxIds[videoIndex];
trace(videoIdFrom);
}
}
}
internal class PlayListItem
{
private var _videoId:int;
private var _path:String;
private var _id:int;
public function get VideoId():int { return _videoId; }
public function get Path():String { return _path; }
public function get Id():int { return _id; }
public function set VideoId(setValue:int):void { _videoId = setValue };
public function set Path(setValue:String):void { _path = setValue };
public function set Id(setValue:int):void { _id = setValue };
public function PlayListItem(id:int, videoId:int, path:String)
{
_videoId = videoId;
_id = id;
_path = path;
}// end function
}
I think you were on the right track with your commented-out code. Be aware that the getOperation() will return an AbstractOperation, which in my mind is simply a pointer to the remote function. You can set arguments on the object, or simply pass the arguments when you call send(). I know some people have had issues with the argument property approach, so simply passing your arguments in the send function may be the smart way to go.
The following replace BuildServiceRequest and displayResult
private function BuildServiceRequest(evt:LoadEvent):void {
webService.removeEventListener(LoadEvent.LOAD, BuildServiceRequest);
serviceOperation.addEventListener(ResultEvent.RESULT, displayResult);
serviceOperation = webService.getOperation("GetPlaylist");
serviceOperation.send(394575);
}
private function displayResult(e:ResultEvent):void {
// Store the token as our array.
_playListItems = e.token;
var msg:String;
// Loop through the array
for each (var entry:Object in _playListItems) {
msg = "";
// For every key:value pair, compose a message to trace
for (var key:String in entry) {
msg += key + ":" + entry[key] + " ";
}
trace(msg);
}
}
I'm having an issue while saving my list items to isolated storage.(Windows Phone 7)
Here is how I defined my list class; I'm using data binding
public class CTransaction
{
public String Date1 { get; set; }
public String Amount1 { get; set; }
public String Type1 { get; set; }
public String Time1 { get; set; }
public String Dis1 { get; set; }
public String Def1 { get; set; }
public CTransaction(String date1, String amount1, String type1, String time1, String dis1, String def1)
{
this.Date1 = date1;
this.Amount1 = amount1;
this.Time1 = time1;
this.Dis1 = dis1;
this.Def1 = def1;
switch (type1)
{
case "FR":
this.Type1 = "Images/a.png";
break;
case "TA":
this.Type1 = "Images/b.png";
break;
case "DA":
this.Type1 = "Images/c.png";
break;
case "CC":
this.Type1 = "Images/mount.png";
break;
}
}
}
Here is how I insert the items to my list; I'm assigning something to the fields and then I'm inserting this into my list like;
List<CTransaction> ctransactionList = new List<CTransaction>();
//some list item filling operations here***
ctransactionList.Insert(0, new CTransaction(DefaultDate, DefaultAmount, RandomType, Times, Dist, Defin));//This one inserts at the top of my list
CTransactionList.ItemsSource = null;
CTransactionList.ItemsSource = ctransactionList; //These two operations refreshes my list
!!Yes, you see how I insert and create my list. Everything is OK. now I want to save ctransactionList
Here is How I save it;(by clicking a button)
var store = IsolatedStorageFile.GetUserStoreForApplication();
if (store.FileExists("saver"))
{
store.DeleteFile("saver");
}
using (var stream = new IsolatedStorageFileStream("saver", FileMode.OpenOrCreate, FileAccess.Write, store))
{
var serializer = new XmlSerializer(typeof(List<CTransaction>));
serializer.Serialize(stream, ctransactionList);
}
Here is How I load it (By clicking another button)
var store = IsolatedStorageFile.GetUserStoreForApplication();
if (store.FileExists("saver"))
{
using (var stream = new IsolatedStorageFileStream("saver", FileMode.OpenOrCreate, FileAccess.Read, store))
{
var reader = new StreamReader(stream);
if (!reader.EndOfStream)
{
var serializer = new XmlSerializer(typeof(List<CTransaction>));
ctransactionList = (List<CTransaction>)serializer.Deserialize(reader);
}
}
}
While debugging, saving operation seems consistent and not raising any error, but while loading; this is the raised error at this line;
ctransactionList = (List<CTransaction>)serializer.Deserialize(reader);
ERROR: There is an error in XML document (3, 4).
If you can help me, I'd really appreciate it.
Thanks in advance
Try this to make your class serializable:
[DataContract]
public class CTransaction
{
[DataMember]
public String Date1 { get; set; }
[DataMember]
public String Amount1 { get; set; }
[DataMember]
public String Type1 { get; set; }
[DataMember]
public String Time1 { get; set; }
[DataMember]
public String Dis1 { get; set; }
[DataMember]
public String Def1 { get; set; }
public CTransaction(String date1, String amount1, String type1, String time1, String dis1, String def1)
{
//your code
}
}
then change the load and save methods to:
public List<CTransaction> LoadFromIsolatedStorage()
{
List<CTransaction> ret = new List<CTransaction>();
try
{
var store = IsolatedStorageFile.GetUserStoreForApplication();
if (store.FileExists("saver"))
{
using (var stream = new IsolatedStorageFileStream("saver", FileMode.OpenOrCreate, FileAccess.Read, store))
{
if (stream.Length > 0)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(List<CTransaction>));
ret = dcs.ReadObject(stream) as List<CTransaction>;
}
}
}
}
catch (Exception ex)
{
// handle exception
}
}
public void SaveToIsolatedStorage(List<CTransaction> listToSave)
{
var store = IsolatedStorageFile.GetUserStoreForApplication();
if (store.FileExists("saver"))
{
store.DeleteFile("saver");
}
using (var stream = new IsolatedStorageFileStream("saver", FileMode.OpenOrCreate, FileAccess.Write, store))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(List<CTransaction>));
dcs.WriteObject(stream, ctransactionList);
}
}
Sorry for this lengthy query, I decided to add the whole test so that it will be easier for even newbies to help me with this total brain-melt.
The using directives are:
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Client.Indexes;
Please leave feedback if I'm too lengthy, but what could possibly go wrong if I add a complete test?
[TestFixture]
public class ClicksByScoreAndCardTest
{
private IDocumentStore _documentStore;
[SetUp]
public void SetUp()
{
_documentStore = new EmbeddableDocumentStore {RunInMemory = true}.Initialize();
_documentStore.DatabaseCommands.DisableAllCaching();
IndexCreation.CreateIndexes(typeof (ClicksBySearchAndProductCode).Assembly, _documentStore);
}
[TearDown]
public void TearDown()
{
_documentStore.Dispose();
}
[Test]
public void ShouldCountTotalLeadsMatchingPreference()
{
var userFirst = new User {Id = "users/134"};
var userSecond = new User {Id = "users/135"};
var searchFirst = new Search(userFirst)
{
Id = "searches/24",
VisitId = "visits/63"
};
searchFirst.Result = new Result();
searchFirst.Result.Rows = new List<Row>(
new[]
{
new Row {ProductCode = "CreditCards/123", Score = 6},
new Row {ProductCode = "CreditCards/124", Score = 4}
});
var searchSecond = new Search(userSecond)
{
Id = "searches/25",
VisitId = "visits/64"
};
searchSecond.Result = new Result();
searchSecond.Result.Rows = new List<Row>(
new[]
{
new Row {ProductCode = "CreditCards/122", Score = 9},
new Row {ProductCode = "CreditCards/124", Score = 4}
});
var searches = new List<Search>
{
searchFirst,
searchSecond
};
var click = new Click
{
VisitId = "visits/64",
ProductCode = "CreditCards/122",
SearchId = "searches/25"
};
using (var session = _documentStore.OpenSession())
{
foreach (var search in searches)
{
session.Store(search);
}
session.Store(click);
session.SaveChanges();
}
IList<ClicksBySearchAndProductCode.MapReduceResult> clicksBySearchAndProductCode = null;
using (var session = _documentStore.OpenSession())
{
clicksBySearchAndProductCode = session.Query<ClicksBySearchAndProductCode.MapReduceResult>(ClicksBySearchAndProductCode.INDEX_NAME)
.Customize(x => x.WaitForNonStaleResults()).ToArray();
}
Assert.That(clicksBySearchAndProductCode.Count, Is.EqualTo(4));
var mapReduce = clicksBySearchAndProductCode
.First(x => x.SearchId.Equals("searches/25")
&& x.ProductCode.Equals("CreditCards/122"));
Assert.That(mapReduce.Clicks,
Is.EqualTo(1));
}
}
public class ClicksBySearchAndProductCode :
AbstractMultiMapIndexCreationTask
<ClicksBySearchAndProductCode.MapReduceResult>
{
public const string INDEX_NAME = "ClicksBySearchAndProductCode";
public override string IndexName
{
get { return INDEX_NAME; }
}
public class MapReduceResult
{
public string SearchId { get; set; }
public string ProductCode { get; set; }
public string Score { get; set; }
public int Clicks { get; set; }
}
public ClicksBySearchAndProductCode()
{
AddMap<Search>(
searches =>
from search in searches
from row in search.Result.Rows
select new
{
SearchId = search.Id,
ProductCode = row.ProductCode,
Score = row.Score.ToString(),
Clicks = 0
});
AddMap<Click>(
clicks =>
from click in clicks
select new
{
SearchId = click.SearchId,
ProductCode = click.ProductCode,
Score = (string)null,
Clicks = 1
});
Reduce =
results =>
from result in results
group result by
new { SearchId = result.SearchId, ProductCode = result.ProductCode }
into g
select
new
{
SearchId = g.Key.SearchId,
ProductCode = g.Key.ProductCode,
Score = g.First(x => x.Score != null).Score,
Clicks = g.Sum(x => x.Clicks)
};
}
}
public class User
{
public string Id { get; set; }
}
public class Search
{
public string Id { get; set; }
public string VisitId { get; set; }
public User User { get; set; }
private Result _result = new Result();
public Result Result
{
get { return _result; }
set { _result = value; }
}
public Search(User user)
{
User = user;
}
}
public class Result
{
private IList<Row> _rows = new List<Row>();
public IList<Row> Rows
{
get { return _rows; }
set { _rows = value; }
}
}
public class Row
{
public string ProductCode { get; set; }
public int Score { get; set; }
}
public class Click
{
public string VisitId { get; set; }
public string SearchId { get; set; }
public string ProductCode { get; set; }
}
My problem here is that I expect Count to be one in that specific test, but it just doesn't seem to add the Clicks in the Click map and the result is 0 clicks. I'm totally confused, and I'm sure that there is a really simple solution to my problem, but I just can't find it..
..hope there is a week-end warrior out there who can take me under his wings.
Yes, it was a brain-melt, for me non-trivial, but still. The proper reduce should look like this:
Reduce =
results =>
from result in results
group result by
new { SearchId = result.SearchId, ProductCode = result.ProductCode }
into g
select
new
{
SearchId = g.Key.SearchId,
ProductCode = g.Key.ProductCode,
Score = g.Select(x=>x.Score).FirstOrDefault(),
Clicks = g.Sum(x => x.Clicks)
};
Not all Maps had the Score set to a non-null-value, and therefore my original version had a problem with:
Score = g.First(x => x.Score != null).Score
Mental note, use:
Score = g.Select(x=>x.Score).FirstOrDefault()
Don't use:
Score = g.First(x => x.Score != null).Score