private void outputAccount(int i, AStringBuilder sb, ref bool firstEl) { var a = store.Accounts[i]; if (firstEl) { firstEl = false; } else { sb.Append(','); } // id (always present) sb.Append("{\"id\":").Append(Mapper.IntIdToExtId(i)).Append(','); // email sb.Append("\"email\":"); if (a.Email == null) { sb.Append("null"); } else { sb.Append('"'); store.emailFromBuffer(a.Email, sb); sb.Append("\","); } // fname if (a.FNameIdx > 0) { sb.Append("\"fname\":\"").Append(store.Fnames[a.FNameIdx].AName).Append("\","); } // sname if (a.SNameIdx != 0) { sb.Append("\"sname\":\"").Append(store.Snames[a.SNameIdx].AName).Append("\","); } // status sb.Append("\"status\":\""); if (a.IsFree()) { sb.Append(DtoAccount.s_Free); } else if (a.IsTaken()) { sb.Append(DtoAccount.s_Taken); } else { sb.Append(DtoAccount.s_Complicated); } sb.Append("\"}"); }
/******************************** Helpers **********************************/ // sync! returns the reply length private int sendReply(HttpCtx ctx, int statusCode) { ctx.StatusCode = statusCode; switch (statusCode) { case 200: // 200 OK with optional body Array.Copy(Status200, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status200.Length); AStringBuilder.ComposeInt(ctx.ResponseBodyLength, ctx.Buffer, ctx.ResponseStart + LENGTH_POS); break; case 201: case 202: // empty jsons if (statusCode == 201) { Array.Copy(Status201, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status201.Length); } else { Array.Copy(Status202, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status202.Length); } Array.Copy(emptyJsonLength, 0, ctx.Buffer, ctx.ResponseStart + LENGTH_POS, emptyJsonLength.Length); Array.Copy(emptyJson, 0, ctx.Buffer, ctx.ResponseBodyStart, emptyJson.Length); ctx.ResponseBodyLength = 2; break; case 211: // empty accounts ctx.StatusCode = 200; Array.Copy(Status200, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status200.Length); Array.Copy(emptyAccountsLength, 0, ctx.Buffer, ctx.ResponseStart + LENGTH_POS, emptyAccountsLength.Length); Array.Copy(emptyAccounts, 0, ctx.Buffer, ctx.ResponseBodyStart, emptyAccounts.Length); ctx.ResponseBodyLength = emptyAccounts.Length; break; case 212: // empty groups ctx.StatusCode = 200; Array.Copy(Status200, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status200.Length); Array.Copy(emptyGroupsLength, 0, ctx.Buffer, ctx.ResponseStart + LENGTH_POS, emptyGroupsLength.Length); Array.Copy(emptyGroups, 0, ctx.Buffer, ctx.ResponseBodyStart, emptyGroups.Length); ctx.ResponseBodyLength = emptyGroups.Length; break; case 400: // 400 bad request, empty body Array.Copy(Status400, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status400.Length); Array.Copy(zeroLength, 0, ctx.Buffer, ctx.ResponseStart + LENGTH_POS, zeroLength.Length); ctx.ResponseBodyLength = 0; break; case 404: // 404 not found, empty body Array.Copy(Status404, 0, ctx.Buffer, ctx.ResponseStart + STATUS_POS, Status404.Length); Array.Copy(zeroLength, 0, ctx.Buffer, ctx.ResponseStart + LENGTH_POS, zeroLength.Length); ctx.ResponseBodyLength = 0; break; } var responseLength = responseHeaders.Length + ctx.ResponseBodyLength; //ctx.Complete(responseLength); return(responseLength); }
public AStringBuilder emailFromBuffer(byte[] buffer, AStringBuilder sb) { var domain = Domains[buffer[buffer.Length - 1]].Name; for (int i = 0; i < buffer.Length - 1; i++) { sb.Append((char)(buffer[i])); } sb.Append('@'); sb.Append(domain); return(sb); }
private void composeResponse(HttpCtx ctx, List <GroupItem> groupList, int limit) { // find and compose the response var sb = new AStringBuilder(ctx.Buffer, ctx.ResponseBodyStart); sb.Append("{\"groups\":["); bool firstGroup = true; foreach (var g in groupList) { if (firstGroup) { firstGroup = false; } else { sb.Append(','); } // count - always there sb.Append("{\"count\":").Append(g.Count); // sex if (g.sex > 0) { sb.Append(",\"sex\":\"").Append(g.ASex).Append('"'); } if (g.status > 0) { sb.Append(",\"status\":\"").Append(g.AStatus).Append('"'); } if (g.city > 0) { sb.Append(",\"city\":\"").Append(g.ACity).Append('"'); } if (g.country > 0) { sb.Append(",\"country\":\"").Append(g.ACountry).Append('"'); } if (g.interest > 0) { sb.Append(",\"interests\":\"").Append(g.AInterest).Append('"'); } sb.Append('}'); if (--limit == 0) { break; // enough } } sb.Append("]}"); ctx.ResponseBodyLength = sb.Count; }
// synchronously process the request, fill up responseBuffer, and return statusCode public int Process(HttpCtx ctx, int id) { var startTime = Stats.Watch.Elapsed; var limit = 0; if (!Mapper.ExtIdToIntId(id, out id)) { return(404); // no mapping found } var acct = store.Accounts[id]; if (acct.IsEmpty()) { return(404); // no such user } var flags = new SuggestQueryMask(); var cityIdx = 0; var countryIdx = 0; bool empty = false; foreach (var query in ctx.Params) { var value = query.Value; if (value.IsEmpty) { return(400); } if (query.Key == "query_id") { } // ignore else if (query.Key == "limit") { if (!value.TryToInt(out limit)) { return(400); } } else if (query.Key == "country") { if (value.IsEmpty) { return(400); } if (store.Countries.TryGetValue(value, out IRange countryInd)) { countryIdx = countryInd.Index; } else { empty = true; } flags |= SuggestQueryMask.Country; } else if (query.Key == "city") { if (value.IsEmpty) { return(400); } if (store.Cities.TryGetValue(value, out var cityBm)) { cityIdx = cityBm.Index; } else { empty = true; } flags |= SuggestQueryMask.City; } else // all other parameters are invalid { return(400); } } if (limit <= 0) { return(400); } if (empty || acct.LikesCount == 0) // shortcut { return(211); // empty accounts } // first, create the list of suggesters and find their similarity value var acctLikes = acct.GetLikes(store); if (!Pool <List <(int id, double sim)> > .TryGet(out var suggesters)) { suggesters = new List <(int id, double sim)>(4096); } for (int i = 0; i < acctLikes.Count; i++) { var q = store.Accounts[acctLikes[i].GetId()]; foreach (var suggester in q.GetLikedBy(store)) { var a = store.Accounts[suggester]; if (suggester != id && // can't suggest to myself a.IsMale() == acct.IsMale() && // suggester must be same gender (cityIdx == 0 || cityIdx == a.CityIdx) && // from the specified city (countryIdx == 0 || countryIdx == a.CountryIdx)) // from the specified country { #if false long key = id < suggester ? (id + ((long)suggester << 32)) : (suggester + ((long)id << 32)); if (!store.CachedSim.TryGetValue(key, out var similarity)) { // if it's a new suggester, calculate his similarity similarity = calcSimilarity(acctLikes, a.GetLikes(store)); store.CachedSim.Add(key, similarity); } #else // if it's a new suggester, calculate his similarity var similarity = calcSimilarity(acctLikes, a.GetLikes(store)); //store.CachedSim.Add(key, similarity); #endif if (similarity > 0) { suggesters.Add((suggester, similarity)); } } } } // suggesters is sorted by id, now, sort it by descending similarity then by ascending ids suggesters.Sort((x, y) => y.sim.CompareTo(x.sim)); // compose the response var sb = new AStringBuilder(ctx.Buffer, ctx.ResponseBodyStart); sb.Append("{\"accounts\":["); bool firstEl = true; // form the list of suggestions var suggestions = new List <int>(limit); var lastSuggester = 0; foreach (var kv in suggesters) { if (kv.id == lastSuggester) { continue; } else { lastSuggester = kv.id; } var a = store.Accounts[kv.id]; var aLikes = a.GetLikes(store); // now, add suggested likes that were not yet liked by acct for (int k = aLikes.Count - 1; k >= 0; k--) { var l0 = aLikes[k]; if (l0.GetId() == id) { continue; } var j1 = Array.BinarySearch(store.Likes, acct.LikesIdx, acct.LikesCount, l0, Like.CompareById); if (j1 < 0) // suggest only not already liked accounts { if (!suggestions.Contains(l0.GetId())) { suggestions.Add(l0.GetId()); outputAccount(l0.GetId(), sb, ref firstEl); if (suggestions.Count == limit) { break; } } } } if (suggestions.Count >= limit) { break; } } suggesters.Clear(); Pool <List <(int, double)> > .Release(suggesters); // finalize the output sb.Append("]}"); ctx.ResponseBodyLength = sb.Count; var stopTime = Stats.Watch.Elapsed; ctx.ContextType = "GetSuggest"; return(200); }
private void composeResults(HttpCtx ctx, List <int> ids, FilterQueryMask flags, int limit) { // find and compose the response var sb = new AStringBuilder(ctx.Buffer, ctx.ResponseBodyStart); sb.Append("{\"accounts\":["); bool firstEl = true; foreach (int id in ids) { var acct = store.Accounts[id]; if (firstEl) { firstEl = false; } else { sb.Append(','); } // id (always present) sb.Append("{\"id\":").Append(Mapper.IntIdToExtId(id)).Append(','); // sex if (flags.HasFlag(FilterQueryMask.Sex_eq)) { sb.Append(store.Male[id] ? "\"sex\":\"m\"," : "\"sex\":\"f\","); } // status if (flags.HasFlag(FilterQueryMask.Status_eq)) { sb.Append("\"status\":\""); if (store.Free[id]) { sb.Append(DtoAccount.s_Free); } else if (store.Taken[id]) { sb.Append(DtoAccount.s_Taken); } else { sb.Append(DtoAccount.s_Complicated); } sb.Append("\","); } // fname if (acct.FNameIdx > 0 && ( flags.HasFlag(FilterQueryMask.Fname_eq) || flags.HasFlag(FilterQueryMask.Fname_any) || flags.HasFlag(FilterQueryMask.Fname_null))) { sb.Append("\"fname\":\"").Append(store.Fnames[acct.FNameIdx].AName).Append("\","); } // sname if (acct.SNameIdx != 0 && ( flags.HasFlag(FilterQueryMask.Sname_eq) || flags.HasFlag(FilterQueryMask.Sname_starts) || flags.HasFlag(FilterQueryMask.Sname_null))) { sb.Append("\"sname\":\"").Append(store.Snames[acct.SNameIdx].AName).Append("\","); } // phone if (acct.Phone != null && ( flags.HasFlag(FilterQueryMask.Phone_code) || flags.HasFlag(FilterQueryMask.Phone_null))) { sb.Append("\"phone\":\"").Append(acct.Phone).Append("\","); } // country if (acct.CountryIdx > 0 && ( flags.HasFlag(FilterQueryMask.Country_eq) || flags.HasFlag(FilterQueryMask.Country_null))) { sb.Append("\"country\":\"").Append(store.Countries[acct.CountryIdx].AName).Append("\","); } // city if (acct.CityIdx > 0 && ( flags.HasFlag(FilterQueryMask.City_eq) || flags.HasFlag(FilterQueryMask.City_any) || flags.HasFlag(FilterQueryMask.City_null))) { sb.Append("\"city\":\"").Append(store.Cities[acct.CityIdx].AName).Append("\","); } // birth if (flags.HasFlag(FilterQueryMask.Birth_ltgt) || flags.HasFlag(FilterQueryMask.Birth_year)) { sb.Append("\"birth\":").Append(acct.Birth).Append(','); } // premium if (flags.HasFlag(FilterQueryMask.Premium_now) || flags.HasFlag(FilterQueryMask.Premium_null)) { if (acct.PStart != 0 || acct.PFinish != 0) { sb.Append("\"premium\":{\"start\":").Append(acct.PStart).Append(",\"finish\":").Append(acct.PFinish).Append("},"); } } // email sb.Append("\"email\":"); if (acct.Email == null) { sb.Append("null"); } else { sb.Append('"'); store.emailFromBuffer(acct.Email, sb); sb.Append('"'); } // interests and likes are not required sb.Append('}'); if (--limit <= 0) { break; } } sb.Append("]}"); ctx.ResponseBodyLength = sb.Count; }
// synchronously process the request, fill up responseBuffer, and return statusCode public int Process(HttpCtx ctx, int id) { var limit = 0; if (!Mapper.ExtIdToIntId(id, out id)) { return(404); // no mapping exists } var finder = new Finder(store.All); finder.AndBitmap(store.PremiumFreeMale); // placeholder, to be replaced for each category var flags = new RecommendQueryMask(); var startTime = Stats.Watch.Elapsed; foreach (var query in ctx.Params) { var value = query.Value; if (value.IsEmpty) { return(400); } if (query.Key == "query_id") { } // ignore else if (query.Key == "limit") { if (!value.TryToInt(out limit)) { return(400); } } else if (query.Key == "country") { if (value.IsEmpty) { return(400); } if (store.Countries.TryGetValue(value, out IRange countryInd)) { finder.AndBitmap(countryInd as BitMap); } else { finder.AndBitmap(null); } flags |= RecommendQueryMask.Country; } else if (query.Key == "city") { if (value.IsEmpty) { return(400); } if (store.Cities.TryGetValue(value, out var cityBm)) { finder.AndBitmap(cityBm as BitMap); } else { finder.AndBitmap(null); } flags |= RecommendQueryMask.City; } else // all other parameters are invalid { return(400); } } if (limit <= 0) { return(400); } var acct = store.Accounts[id]; if (acct.IsEmpty()) { return(404); // no such user } if (acct.InterestMask.Count == 0) { return(211); // no interests => zero compatibility } if (finder.DefaultBitmap == null || acct.InterestMask.Count == 0) // shortcut { return(211); // empty accounts } #if false finder.AddCondition(i => acct.InterestMask.Any(store.Accounts[i].InterestMask), 0); #else for (int i = 1; i < BitMap96.MAX_BITS; i++) { if (acct.InterestMask.IsSet(i)) { finder.OrBitmap(0, store.Interests[i] as BitMap); } } #endif var findContext = new FindContext(store, finder, acct, limit); for (int category = 1; category <= 6; category++) { if (findContext.Select(category) >= limit) { break; } } // compose the response var sb = new AStringBuilder(ctx.Buffer, ctx.ResponseBodyStart); sb.Append("{\"accounts\":["); bool firstEl = true; // pick first limit users from selected foreach (var kv in findContext.Selected) { var i = kv.Value; var a = store.Accounts[i]; if (firstEl) { firstEl = false; } else { sb.Append(','); } // id (always present) sb.Append("{\"id\":").Append(Mapper.IntIdToExtId(i)).Append(','); // email sb.Append("\"email\":"); if (a.Email == null) { sb.Append("null"); } else { sb.Append('"'); store.emailFromBuffer(a.Email, sb); sb.Append("\","); } // status sb.Append("\"status\":\""); if (a.IsFree()) { sb.Append(DtoAccount.s_Free); } else if (a.IsTaken()) { sb.Append(DtoAccount.s_Taken); } else { sb.Append(DtoAccount.s_Complicated); } sb.Append("\","); // fname if (a.FNameIdx > 0) { sb.Append("\"fname\":\"").Append(store.Fnames[a.FNameIdx].AName).Append("\","); } // sname if (a.SNameIdx > 0) { sb.Append("\"sname\":\"").Append(store.Snames[a.SNameIdx].AName).Append("\","); } // premium if (store.PremiumYes[i]) { sb.Append("\"premium\":{\"start\":").Append(a.PStart).Append(",\"finish\":").Append(a.PFinish).Append("},"); } // birth sb.Append("\"birth\":").Append(a.Birth).Append('}'); } ; // finalize the output sb.Append("]}"); ctx.ResponseBodyLength = sb.Count; var stopTime = Stats.Watch.Elapsed; ctx.ContextType = "GetRecommend"; return(200); }