Escape field name with period correctly

Change period escaping mechanism during field indexing
and query building. Rather than escaping the whole field name
at the end, we separate the field name by periods and escape
each component.

BugzId:43621
diff --git a/src/mango_native_proc.erl b/src/mango_native_proc.erl
index 3e189bd..a55a3ac 100644
--- a/src/mango_native_proc.erl
+++ b/src/mango_native_proc.erl
@@ -168,8 +168,8 @@
     % We bypass make_text_field and directly call make_text_field_name
     % because the length field name is not part of the path.
     LengthFieldName = make_text_field_name(NewTAcc#tacc.path, <<"length">>),
-    EncLFN = mango_util:lucene_escape_field(LengthFieldName),
-    LengthField = [{EncLFN, <<"length">>, length(Values)}],
+    LengthField = [{escape_name_parts(LengthFieldName), <<"length">>,
+        length(Values)}],
     get_text_field_values_arr(Values, NewTAcc, LengthField);
 
 get_text_field_values(Bin, TAcc) when is_binary(Bin) ->
@@ -283,11 +283,20 @@
 
 make_text_field(TAcc, Type, Value) ->
     FieldName = make_text_field_name(TAcc#tacc.path, Type),
-    Fields = TAcc#tacc.fields,
-    case Fields == all_fields orelse lists:member(FieldName, Fields) of
+    Fields = case TAcc#tacc.fields of
+        Fields0 when is_list(Fields0) ->
+            % for field names such as "name\\.first"
+            PFields = [mango_doc:parse_field(F) || F <- Fields0],
+            [iolist_to_binary(mango_util:join(".", P)) || {ok, P} <- PFields];
+        _ ->
+            all_fields
+    end,
+    % need to convert the fieldname to binary but not escape it in order
+    % to compare with the user field names.
+    BName = iolist_to_binary(FieldName),
+    case Fields == all_fields orelse lists:member(BName, Fields) of
         true ->
-            [{mango_util:lucene_escape_field(FieldName), Type,
-            Value}];
+            [{escape_name_parts(FieldName), Type, Value}];
         false ->
             []
     end.
@@ -297,6 +306,18 @@
     make_text_field_name0(Rest, [P, ":", Type]).
 
 make_text_field_name0([], Name) ->
-    iolist_to_binary(Name);
+    Name;
 make_text_field_name0([P | Rest], Name) ->
     make_text_field_name0(Rest, [P, "." | Name]).
+
+
+escape_name_parts(Name) ->
+    EscapedName = lists:map(fun(N) ->
+        case N of
+            "." ->
+                ".";
+            Else ->
+                mango_util:lucene_escape_field(Else)
+        end
+    end, Name),
+    iolist_to_binary(EscapedName).
diff --git a/src/mango_selector_text.erl b/src/mango_selector_text.erl
index 35a0d4c..9a69853 100644
--- a/src/mango_selector_text.erl
+++ b/src/mango_selector_text.erl
@@ -171,11 +171,12 @@
 
 
 to_query({op_and, Args}) when is_list(Args) ->
-    Res = ["(", join(<<" AND ">>, lists:map(fun to_query/1, Args)), ")"],
+    Res = ["(", mango_util:join(<<" AND ">>, lists:map(fun to_query/1, Args)),
+        ")"],
     Res;
 
 to_query({op_or, Args}) when is_list(Args) ->
-    ["(", join(" OR ", lists:map(fun to_query/1, Args)), ")"];
+    ["(", mango_util:join(" OR ", lists:map(fun to_query/1, Args)), ")"];
 
 to_query({op_not, {ExistsQuery, Arg}}) when is_tuple(Arg) ->
     ["(", to_query(ExistsQuery), " AND NOT (", to_query(Arg), "))"];
@@ -191,25 +192,25 @@
 %% This needs to be resolved.
 to_query({op_field, {Name, Value}}) ->
     NameBin = iolist_to_binary(Name),
-    ["(", mango_util:lucene_escape_field(NameBin), ":", Value, ")"];
+    ["(", escape_name_parts(NameBin), ":", Value, ")"];
 
 %% This is for indexable_fields
 to_query({op_null, {Name, Value}}) ->
     NameBin = iolist_to_binary(Name),
-    ["(", mango_util:lucene_escape_field(NameBin), ":", Value, ")"];
+    ["(", escape_name_parts(NameBin), ":", Value, ")"];
 
 to_query({op_fieldname, {Name, Wildcard}}) ->
     NameBin = iolist_to_binary(Name),
-    ["($fieldnames:", mango_util:lucene_escape_field(NameBin), Wildcard, ")"];
+    ["($fieldnames:", escape_name_parts(NameBin), Wildcard, ")"];
 
 to_query({op_default, Value}) ->
     ["($default:", Value, ")"].
 
 
-join(_Sep, [Item]) ->
-    [Item];
-join(Sep, [Item | Rest]) ->
-    [Item, Sep | join(Sep, Rest)].
+escape_name_parts(Name) ->
+    {ok, ParsedNames} = mango_doc:parse_field(Name),
+    EncodedNames = [mango_util:lucene_escape_field(N) || N <- ParsedNames],
+    iolist_to_binary(mango_util:join(".", EncodedNames)).
 
 
 %% We match on fieldname and fieldname.[]
@@ -256,7 +257,9 @@
     % appened * isntead).
     Parts = [
         {op_fieldname, {[path_str(Path), ":"], "*"}},
-        {op_fieldname, {[path_str(Path), "."], "*"}}
+        % need to extract out the period because mango_doc:parse_field/1
+        % will not accept "name.", also we don't want to escape the .
+        {op_fieldname, {[path_str(Path)], ".*"}}
     ],
     {op_or, Parts}.
 
@@ -304,7 +307,7 @@
 
 
 append_sort_type(RawSortField, Selector) ->
-    EncodeField = mango_util:lucene_escape_field(RawSortField),
+    EncodeField = escape_name_parts(RawSortField),
     String = mango_util:has_suffix(EncodeField, <<"_3astring">>),
     Number = mango_util:has_suffix(EncodeField, <<"_3anumber">>),
     case {String, Number} of
diff --git a/src/mango_util.erl b/src/mango_util.erl
index f3b60b2..b1d30fb 100644
--- a/src/mango_util.erl
+++ b/src/mango_util.erl
@@ -34,7 +34,9 @@
     lucene_escape_field/1,
     lucene_escape_query_value/1,
 
-    has_suffix/2
+    has_suffix/2,
+
+    join/2
 ]).
 
 
@@ -293,3 +295,9 @@
                 false
         end
     end.
+
+
+join(_Sep, [Item]) ->
+    [Item];
+join(Sep, [Item | Rest]) ->
+    [Item, Sep | join(Sep, Rest)].