I'm looking for some opinions on improvement/technique of code in C# calling to a Neo4j database to perform a multi-faceted query looking for users. My goals were to lookup by first/last name, phone number, birthday, and e-mail address. I wanted case insensitivity, and I want the query to respect certain privacy settings that users can set (i.e. can users look me up via phone number?), and a block list.
I use regexes and some CultureInfo
code to put things into a standard format, but I feel that my code is a little bloated and could be made smaller or more optimized.
// Social Function, Non-Throwing //
// Searches for any and all users matching full name, first name, last name, e-mail, birth date, or phone number in the given search query.
private static Regex _emailRegex = new Regex("[-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+\\.[a-zA-Z]{2,4}");
private static Regex _dateRegex = new Regex(@"^((0?[13578]|10|12)(-|\/)(([1-9])|(0[1-9])|([12])([0-9]?)|(3[01]?))(-|\/)((19)([2-9])(\d{1})|(20)([01])(\d{1})|([8901])(\d{1}))|(0?[2469]|11)(-|\/)(([1-9])|(0[1-9])|([12])([0-9]?)|(3[0]?))(-|\/)((19)([2-9])(\d{1})|(20)([01])(\d{1})|([8901])(\d{1})))$");
private static Regex _phoneRegex = new Regex("[0-9]+");
public List<User> Find(string query)
{
// internal search query
List<User> results = doFind(query);
// return empty results if we found no one
if (results.Count == 0)
return results;
// remove users from results if we blocked them or they blocked us
results.RemoveAll(x => x.IsBlocked(this.Id));
return results;
}
private static List<User> doFind(string query)
{
query = query.Trim();
if (_emailRegex.IsMatch(query)) // input was an e-mail address
{
try
{
return Graph.Instance.Cypher
.Match("(user:User)")
.Where((User user) => user.Email == query)
.AndWhere("user.Privacy_AllowEMailLookup = true")
.Return(user => user.As<User>())
.Results.ToList();
}
catch { return new List<User>(); }
}
else if (_dateRegex.IsMatch(query.Replace(".","/").Replace("-","/"))) // input was a birthday
{
query = query.Replace(".","/").Replace("-","/");
DateTimeOffset dto = DateTimeOffset.Parse(query);
try
{
return Graph.Instance.Cypher
.Match("(user:User)")
.Where((User user) => user.DateOfBirth.ToString() == dto.Date.ToString())
.Return(user => user.As<User>())
.Results.ToList();
}
catch { return new List<User>(); }
}
else if (_phoneRegex.IsMatch(query.Replace("(","").Replace(")","").Replace("-","").Trim()))
{
try
{
query = query.Replace("(", "").Replace(")", "").Replace("-", "").Trim();
return Graph.Instance.Cypher
.Match("(user:User)")
.Where((User user) => user.PhoneNumber == query)
.AndWhere("user.Privacy_AllowPhoneLookup = true")
.Return(user => user.As<User>())
.Results.ToList();
}
catch { return new List<User>(); }
}
else
{
try
{
query = WebUtility.HtmlDecode(query);
query = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(query);
ICypherFluentQuery q = Graph.Instance.Cypher
.Match("(user:User)")
.Where((User user) => user.FullName == query)
.OrWhere((User user) => user.FirstName == query)
.OrWhere((User user) => user.LastName == query)
.OrWhere((User user) => user.Handle == query.ToLower())
.OrWhere((User user) => user.Id.ToString() == query);
List<string> tokens = query.Split(new Char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
if (tokens != null && tokens.Count > 0)
{
foreach (string token in tokens)
{
string t = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(token);
q = q.OrWhere((User user) => user.FullName == t)
.OrWhere((User user) => user.FirstName == t)
.OrWhere((User user) => user.LastName == t)
.OrWhere((User user) => user.Handle == t)
.OrWhere((User user) => user.Id.ToString() == t.ToLower());
}
}
return q
.ReturnDistinct(user => user.As<User>())
.Results.ToList();
}
catch (Exception ex)
{
Logger.Error(ex);
return new List<User>();
}
}
}