Beispiel #1
0
        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("\"}");
        }
Beispiel #2
0
        /******************************** 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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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;
        }
Beispiel #5
0
        // 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);
        }
Beispiel #6
0
        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;
        }
Beispiel #7
0
        // 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);
        }