1111# the License.
1212
1313defmodule MangoDatabase do
14+ @ moduledoc false
1415 def has_text_service ( ) do
1516 resp = Couch . get ( "/" )
1617 "search" in resp . body [ "features" ]
@@ -29,7 +30,7 @@ defmodule MangoDatabase do
2930 end
3031 end
3132
32- defp create ( db , opts ) do
33+ def create ( db , opts \\ [ ] ) do
3334 partitioned = Keyword . get ( opts , :partitioned , false )
3435 Couch . put ( "/#{ db } ?partitioned=#{ partitioned } " )
3536 end
@@ -38,42 +39,160 @@ defmodule MangoDatabase do
3839 Couch . delete ( "/#{ db } " )
3940 end
4041
42+ def save_doc ( db , doc , opts \\ [ ] ) do
43+ MangoDatabase . save_docs ( db , [ doc ] , opts )
44+ end
45+
46+ def save_docs_with_conflicts ( db , docs ) do
47+ body = % { "docs" => docs , "new_edits" => false }
48+ Couch . post ( "/#{ db } /_bulk_docs" , body: body )
49+ end
50+
51+ # If a certain keyword like sort or field is passed in the options,
52+ # then it is added to the request body.
53+ defp put_if_set ( map , key , opts , opts_key ) do
54+ if Keyword . has_key? ( opts , opts_key ) do
55+ Map . put ( map , key , opts [ opts_key ] )
56+ else
57+ map
58+ end
59+ end
60+
4161 # TODO: make this use batches if necessary
42- def save_docs ( db , docs ) do
43- resp = Couch . post ( "/#{ db } /_bulk_docs" , body: % { "docs" => docs } )
62+ def save_docs ( db , docs , opts \\ [ ] ) do
63+ query = % { }
64+ |> put_if_set ( "w" , opts , :w )
65+
66+ result = Couch . post ( "/#{ db } /_bulk_docs" , body: % { "docs" => docs } , query: query )
67+ zipped_docs = Enum . zip ( docs , result . body )
68+
69+ # This returns the doc list including _id and _rev values
70+ resp = Enum . map ( zipped_docs , fn { doc , result } ->
71+ doc
72+ |> Map . put ( "_id" , result [ "id" ] )
73+ |> Map . put ( "_rev" , result [ "rev" ] )
74+ end )
75+
76+ # _bulk_docs sometimes returns errors in the body and this is captured here
77+ errors = Enum . filter ( result . body , fn r ->
78+ Map . has_key? ( r , "error" )
79+ end )
80+ if errors == [ ] do
81+ resp
82+ else
83+ { :error , errors }
84+ end
4485 end
4586
46- def create_index ( db , fields , name ) do
47- Couch . post ( "/#{ db } /_index" , body: % {
48- "index" => % { "fields" => fields } ,
49- "name" => name ,
50- "ddoc" => name ,
87+ def create_index ( db , fields , options \\ [ ] ) do
88+ index = % {
89+ "fields" => fields ,
90+ }
91+ |> put_if_set ( "selector" , options , :selector )
92+ |> put_if_set ( "partial_filter_selector" , options , :partial_filter_selector )
93+
94+ body = % {
95+ "index" => index ,
5196 "type" => "json" ,
5297 "w" => 3
53- } )
98+ }
99+ |> put_if_set ( "type" , options , :idx_type )
100+ |> put_if_set ( "name" , options , :name )
101+ |> put_if_set ( "ddoc" , options , :ddoc )
102+
103+ resp = Couch . post ( "/#{ db } /_index" , body: body )
104+ if resp . status_code == 200 do
105+ { :ok , resp . body [ "result" ] == "created" }
106+ else
107+ { :error , resp }
108+ end
54109 end
55110
56- def create_text_index ( db ) do
57- Couch . post ( "/#{ db } /_index" , body: % {
58- "index" => % { } ,
59- "type" => "text" ,
111+ def create_text_index ( db , options \\ [ ] ) do
112+ index = % { }
113+ |> put_if_set ( "default_analyzer" , options , :analyzer )
114+ |> put_if_set ( "default_field" , options , :default_field )
115+ |> put_if_set ( "index_array_lengths" , options , :index_array_lengths )
116+ |> put_if_set ( "selector" , options , :selector )
117+ |> put_if_set ( "partial_filter_selector" , options , :partial_filter_selector )
118+ |> put_if_set ( "fields" , options , :fields )
119+
120+ body = % {
121+ "index" => index ,
122+ "type" => Keyword . get ( options , :idx_type , "text" ) ,
60123 "w" => 3
61- } )
124+ }
125+ |> put_if_set ( "name" , options , :name )
126+ |> put_if_set ( "ddoc" , options , :ddoc )
127+
128+ resp = Couch . post ( "/#{ db } /_index" , body: body )
129+
130+ if resp . status_code == 200 do
131+ { :ok , resp . body [ "result" ] == "created" }
132+ else
133+ { :error , resp }
134+ end
135+ end
136+
137+ def list_indexes ( db , opts \\ [ ] ) do
138+ limit = Keyword . get ( opts , :limit )
139+ skip = Keyword . get ( opts , :skip )
140+ query =
141+ [ limit: limit , skip: skip ]
142+ |> Enum . filter ( fn { _k , v } -> not is_nil ( v ) end )
143+ |> Enum . map_join ( "&" , fn { k , v } -> "#{ k } =#{ v } " end )
144+
145+ path =
146+ if query == "" do
147+ "/#{ db } /_index"
148+ else
149+ "/#{ db } /_index?#{ query } "
150+ end
151+ resp = Couch . get ( path )
152+
153+ if resp . status_code == 200 do
154+ { :ok , resp . body [ "indexes" ] }
155+ else
156+ { :error , resp }
157+ end
62158 end
63159
64- # TODO: port more options from src/mango/test/mango.py `def find(...)`
65160 def find ( db , selector , opts \\ [ ] ) do
66- defaults = [ use_index: nil , skip: 0 , limit: 25 , r: 1 , conflicts: false ]
161+ defaults = [
162+ use_index: nil ,
163+ skip: 0 ,
164+ limit: 25 ,
165+ r: 1 ,
166+ conflicts: false ,
167+ explain: false ,
168+ return_raw: false
169+ ]
67170 options = Keyword . merge ( defaults , opts )
68171
69- resp = Couch . post ( "/#{ db } /_find" , body: % {
172+ path =
173+ case options [ :explain ] do
174+ true -> "/#{ db } /_explain"
175+ _ -> "/#{ db } /_find"
176+ end
177+
178+ resp = Couch . post ( path , body: % {
70179 "selector" => selector ,
71180 "use_index" => options [ :use_index ] ,
72181 "skip" => options [ :skip ] ,
73182 "limit" => options [ :limit ] ,
74183 "r" => options [ :r ] ,
75184 "conflicts" => options [ :conflicts ]
76- } )
77- resp . body [ "docs" ]
185+ }
186+ |> put_if_set ( "sort" , options , :sort )
187+ |> put_if_set ( "fields" , options , :fields )
188+ |> put_if_set ( "execution_stats" , options , :executionStats )
189+ |> put_if_set ( "allow_fallback" , options , :allow_fallback )
190+ )
191+
192+ case { ( options [ :explain ] or options [ :return_raw ] ) , resp . status_code } do
193+ { false , 200 } -> { :ok , resp . body [ "docs" ] }
194+ { true , 200 } -> { :ok , resp . body }
195+ _ -> { :error , resp }
196+ end
78197 end
79198end
0 commit comments