mongo 0.18.2 → 0.18.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +37 -28
- data/Rakefile +19 -0
- data/bin/objectid_benchmark.rb +23 -0
- data/examples/admin.rb +7 -6
- data/examples/capped.rb +3 -4
- data/examples/cursor.rb +4 -3
- data/examples/gridfs.rb +3 -2
- data/examples/index_test.rb +10 -9
- data/examples/info.rb +3 -2
- data/examples/queries.rb +3 -2
- data/examples/simple.rb +3 -2
- data/examples/strict.rb +2 -1
- data/examples/types.rb +4 -3
- data/lib/mongo.rb +29 -12
- data/lib/mongo/admin.rb +17 -2
- data/lib/mongo/collection.rb +230 -169
- data/lib/mongo/connection.rb +136 -91
- data/lib/mongo/cursor.rb +68 -40
- data/lib/mongo/db.rb +247 -123
- data/lib/mongo/{errors.rb → exceptions.rb} +6 -5
- data/lib/mongo/gridfs.rb +6 -0
- data/lib/mongo/gridfs/grid_store.rb +142 -94
- data/lib/mongo/types/binary.rb +11 -1
- data/lib/mongo/types/code.rb +6 -1
- data/lib/mongo/types/dbref.rb +7 -2
- data/lib/mongo/types/min_max_keys.rb +58 -0
- data/lib/mongo/types/objectid.rb +76 -20
- data/lib/mongo/types/regexp_of_holding.rb +5 -0
- data/lib/mongo/util/bson_ruby.rb +36 -2
- data/lib/mongo/util/byte_buffer.rb +18 -2
- data/lib/mongo/util/conversions.rb +6 -5
- data/lib/mongo/util/ordered_hash.rb +3 -1
- data/lib/mongo/util/support.rb +3 -0
- data/lib/mongo/util/xml_to_ruby.rb +7 -0
- data/test/test_bson.rb +48 -0
- data/test/test_collection.rb +13 -0
- data/test/test_connection.rb +35 -0
- data/test/test_conversions.rb +1 -1
- data/test/test_cursor.rb +37 -5
- data/test/test_db.rb +51 -2
- data/test/test_db_api.rb +4 -7
- data/test/test_grid_store.rb +10 -0
- data/test/test_objectid.rb +16 -2
- data/test/test_ordered_hash.rb +14 -0
- data/test/threading/test_threading_large_pool.rb +4 -4
- data/test/unit/db_test.rb +43 -0
- metadata +5 -7
- data/examples/benchmarks.rb +0 -42
- data/examples/blog.rb +0 -76
- data/lib/mongo/constants.rb +0 -15
- data/test/mongo-qa/_common.rb +0 -8
@@ -1,18 +1,18 @@
|
|
1
|
-
#
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
2
3
|
#
|
3
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
5
|
# you may not use this file except in compliance with the License.
|
5
6
|
# You may obtain a copy of the License at
|
6
7
|
#
|
7
|
-
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
9
|
#
|
9
10
|
# Unless required by applicable law or agreed to in writing, software
|
10
11
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
12
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
13
|
# See the License for the specific language governing permissions and
|
13
14
|
# limitations under the License.
|
14
|
-
|
15
|
-
# Exceptions raised by the MongoDB driver.
|
15
|
+
# ++
|
16
16
|
|
17
17
|
module Mongo
|
18
18
|
# Generic Mongo Ruby Driver exception class.
|
@@ -39,7 +39,8 @@ module Mongo
|
|
39
39
|
# Raised on failures in connection to the database server.
|
40
40
|
class ConnectionTimeoutError < MongoRubyError; end
|
41
41
|
|
42
|
-
# Raised when trying to insert a document that exceeds the 4MB limit
|
42
|
+
# Raised when trying to insert a document that exceeds the 4MB limit or
|
43
|
+
# when the document contains objects that can't be serialized as BSON.
|
43
44
|
class InvalidDocument < MongoDBError; end
|
44
45
|
|
45
46
|
# Raised when a database operation fails.
|
data/lib/mongo/gridfs.rb
CHANGED
@@ -20,25 +20,32 @@ require 'mongo/gridfs/chunk'
|
|
20
20
|
|
21
21
|
module GridFS
|
22
22
|
|
23
|
-
# GridStore is an IO-like
|
24
|
-
# streams of data to
|
25
|
-
# storage implementation details.
|
23
|
+
# GridStore is an IO-like class that provides input and output for
|
24
|
+
# streams of data to MongoDB.
|
26
25
|
#
|
27
|
-
#
|
26
|
+
# @example
|
28
27
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
28
|
+
# include GridFS
|
29
|
+
#
|
30
|
+
# #Store the text "Hello, world!" in the grid store.
|
31
|
+
# GridStore.open(database, 'filename', 'w') do |f|
|
32
|
+
# f.puts "Hello, world!"
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # Output "Hello, world!"
|
36
|
+
# GridStore.open(database, 'filename', 'r') do |f|
|
37
|
+
# puts f.read
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # Add text to the grid store.
|
41
|
+
# GridStore.open(database, 'filename', 'w+') do |f|
|
42
|
+
# f.puts "But wait, there's more!"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # Retrieve everything, outputting "Hello, world!\nBut wait, there's more!\n"
|
46
|
+
# GridStore.open(database, 'filename', 'r') do |f|
|
47
|
+
# puts f.read
|
48
|
+
# end
|
42
49
|
class GridStore
|
43
50
|
|
44
51
|
DEFAULT_ROOT_COLLECTION = 'fs'
|
@@ -70,80 +77,128 @@ module GridFS
|
|
70
77
|
|
71
78
|
attr_reader :md5
|
72
79
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
80
|
+
# Determine whether a given file exists in the GridStore.
|
81
|
+
#
|
82
|
+
# @param [Mongo::DB] a MongoDB database.
|
83
|
+
# @param [String] name the filename.
|
84
|
+
# @param [String] root_collection the name of the gridfs root collection.
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
def self.exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
|
88
|
+
db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
|
89
|
+
end
|
78
90
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
91
|
+
# Open a GridFS file for reading, writing, or appending. Note that
|
92
|
+
# this method must be used with a block.
|
93
|
+
#
|
94
|
+
# @param [Mongo::DB] a MongoDB database.
|
95
|
+
# @param [String] name the filename.
|
96
|
+
# @param [String] mode one of 'r', 'w', or 'w+' for reading, writing,
|
97
|
+
# and appending, respectively.
|
98
|
+
# @param [Hash] options any of the options available on
|
99
|
+
# GridStore initialization.
|
100
|
+
#
|
101
|
+
# @see GridStore#initialize.
|
102
|
+
# @see The various GridStore class methods, e.g., GridStore.open, GridStore.read etc.
|
103
|
+
def self.open(db, name, mode, options={})
|
104
|
+
gs = self.new(db, name, mode, options)
|
105
|
+
result = nil
|
106
|
+
begin
|
107
|
+
result = yield gs if block_given?
|
108
|
+
ensure
|
109
|
+
gs.close
|
88
110
|
end
|
111
|
+
result
|
112
|
+
end
|
89
113
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
114
|
+
# Read a file stored in GridFS.
|
115
|
+
#
|
116
|
+
# @param [Mongo::DB] db a MongoDB database.
|
117
|
+
# @param [String] name the name of the file.
|
118
|
+
# @param [Integer] length the number of bytes to read.
|
119
|
+
# @param [Integer] offset the number of bytes beyond the
|
120
|
+
# beginning of the file to start reading.
|
121
|
+
#
|
122
|
+
# @return [String] the file data
|
123
|
+
def self.read(db, name, length=nil, offset=nil)
|
124
|
+
GridStore.open(db, name, 'r') do |gs|
|
125
|
+
gs.seek(offset) if offset
|
126
|
+
gs.read(length)
|
95
127
|
end
|
128
|
+
end
|
96
129
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
130
|
+
# List the contents of all GridFS files stored in the given db and
|
131
|
+
# root collection.
|
132
|
+
#
|
133
|
+
# @param [Mongo::DB] db a MongoDB database.
|
134
|
+
# @param [String] root_collection the name of the root collection.
|
135
|
+
#
|
136
|
+
# @return [Array]
|
137
|
+
def self.list(db, root_collection=DEFAULT_ROOT_COLLECTION)
|
138
|
+
db.collection("#{root_collection}.files").find().map do |f|
|
139
|
+
f['filename']
|
107
140
|
end
|
141
|
+
end
|
108
142
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
143
|
+
# Get each line of data from the specified file
|
144
|
+
# as an array of strings.
|
145
|
+
#
|
146
|
+
# @param [Mongo::DB] db a MongoDB database.
|
147
|
+
# @param [String] name the filename.
|
148
|
+
# @param [String, Reg] separator
|
149
|
+
#
|
150
|
+
# @return [Array]
|
151
|
+
def self.readlines(db, name, separator=$/)
|
152
|
+
GridStore.open(db, name, 'r') do |gs|
|
153
|
+
gs.readlines(separator)
|
113
154
|
end
|
155
|
+
end
|
114
156
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
157
|
+
# Remove one for more files from the given db.
|
158
|
+
#
|
159
|
+
# @param [Mongo::Database] db a MongoDB database.
|
160
|
+
# @param [Array<String>] names the filenames to remove
|
161
|
+
#
|
162
|
+
# @return [True]
|
163
|
+
def self.unlink(db, *names)
|
164
|
+
names.each do |name|
|
165
|
+
gs = GridStore.new(db, name)
|
166
|
+
gs.send(:delete_chunks)
|
167
|
+
gs.collection.remove('_id' => gs.files_id)
|
121
168
|
end
|
169
|
+
end
|
170
|
+
class << self
|
122
171
|
alias_method :delete, :unlink
|
123
|
-
|
124
172
|
end
|
125
173
|
|
126
|
-
|
127
|
-
#
|
128
|
-
|
174
|
+
# Rename a file in this collection. Note that this method uses
|
175
|
+
# Collection#update, which means that you will not be notified
|
176
|
+
#
|
177
|
+
# @param [Mongo::DB] a MongoDB database.
|
178
|
+
# @param [String] src the name of the source file.
|
179
|
+
# @param [String] dest the name of the destination file.
|
180
|
+
# @param [String] root_collection the name of the default root collection.
|
181
|
+
def self.mv(db, src, dest, root_collection=DEFAULT_ROOT_COLLECTION)
|
182
|
+
db.collection("#{root_collection}.files").update({ :filename => src }, { '$set' => { :filename => dest } })
|
183
|
+
end
|
129
184
|
|
130
|
-
#
|
185
|
+
# Initialize a GridStore instance for reading, writing, or modifying a given file.
|
186
|
+
# Note that it's often easier to work with the various GridStore class methods (open, read, etc.).
|
131
187
|
#
|
132
|
-
#
|
133
|
-
#
|
188
|
+
# @param [Mongo::DB] db a MongoDB database.
|
189
|
+
# @param [String] name a filename.
|
190
|
+
# @param [String] mode either 'r', 'w', or 'w+' for reading, writing, or appending, respectively.
|
134
191
|
#
|
135
|
-
# :root
|
136
|
-
# DEFAULT_ROOT_COLLECTION.
|
192
|
+
# @option options [String] :root DEFAULT_ROOT_COLLECTION ('r', 'w', 'w+') the name of the root collection to use.
|
137
193
|
#
|
138
|
-
# :metadata
|
139
|
-
#
|
194
|
+
# @option options [String] :metadata ({}) (w, w+) A hash containing any data you want persisted as
|
195
|
+
# this file's metadata.
|
140
196
|
#
|
141
|
-
# :chunk_size :: (w) Sets chunk size for files opened for writing
|
142
|
-
#
|
143
|
-
# any data is written.
|
197
|
+
# @option options [Integer] :chunk_size (Chunk::DEFAULT_CHUNK_SIZE) (w) Sets chunk size for files opened for writing.
|
198
|
+
# See also GridStore#chunk_size=.
|
144
199
|
#
|
145
|
-
# :content_type
|
146
|
-
#
|
200
|
+
# @option options [String] :content_type ('text/plain') Set the content type stored as the
|
201
|
+
# file's metadata. See also GridStore#content_type=.
|
147
202
|
def initialize(db, name, mode='r', options={})
|
148
203
|
@db, @filename, @mode = db, name, mode
|
149
204
|
@root = options[:root] || DEFAULT_ROOT_COLLECTION
|
@@ -191,18 +246,26 @@ module GridFS
|
|
191
246
|
@pushback_byte = nil
|
192
247
|
end
|
193
248
|
|
249
|
+
# Get the files collection referenced by this GridStore instance.
|
250
|
+
#
|
251
|
+
# @return [Mongo::Collection]
|
194
252
|
def collection
|
195
253
|
@db.collection("#{@root}.files")
|
196
254
|
end
|
197
255
|
|
198
|
-
#
|
199
|
-
#
|
256
|
+
# Get the chunk collection referenced by this GridStore.
|
257
|
+
#
|
258
|
+
# @return [Mongo::Collection]
|
200
259
|
def chunk_collection
|
201
260
|
@db.collection("#{@root}.chunks")
|
202
261
|
end
|
203
262
|
|
204
|
-
# Change chunk size.
|
263
|
+
# Change the chunk size. This is permitted only when the file is opened for write
|
205
264
|
# and no data has yet been written.
|
265
|
+
#
|
266
|
+
# @param [Integer] size the new chunk size, in bytes.
|
267
|
+
#
|
268
|
+
# @return [Integer] the new chunk size.
|
206
269
|
def chunk_size=(size)
|
207
270
|
unless @mode[0] == ?w && @position == 0 && @upload_date == nil
|
208
271
|
raise "error: can only change chunk size if open for write and no data written."
|
@@ -210,9 +273,7 @@ module GridFS
|
|
210
273
|
@chunk_size = size
|
211
274
|
end
|
212
275
|
|
213
|
-
#---
|
214
276
|
# ================ reading ================
|
215
|
-
#+++
|
216
277
|
|
217
278
|
def getc
|
218
279
|
if @pushback_byte
|
@@ -245,17 +306,6 @@ module GridFS
|
|
245
306
|
str
|
246
307
|
end
|
247
308
|
|
248
|
-
def old_read(len=nil, buf=nil)
|
249
|
-
buf ||= ''
|
250
|
-
byte = self.getc
|
251
|
-
while byte != nil && (len == nil || len > 0)
|
252
|
-
buf << byte.chr
|
253
|
-
len -= 1 if len
|
254
|
-
byte = self.getc if (len == nil || len > 0)
|
255
|
-
end
|
256
|
-
buf
|
257
|
-
end
|
258
|
-
|
259
309
|
def read(len=nil, buf=nil)
|
260
310
|
if len
|
261
311
|
read_partial(len, buf)
|
@@ -302,9 +352,7 @@ module GridFS
|
|
302
352
|
@position -= 1
|
303
353
|
end
|
304
354
|
|
305
|
-
#---
|
306
355
|
# ================ writing ================
|
307
|
-
#+++
|
308
356
|
|
309
357
|
def putc(byte)
|
310
358
|
if @curr_chunk.pos == @chunk_size
|
@@ -344,6 +392,10 @@ module GridFS
|
|
344
392
|
|
345
393
|
def write(string)
|
346
394
|
raise "#@filename not opened for write" unless @mode[0] == ?w
|
395
|
+
# Since Ruby 1.9.1 doesn't necessarily store one character per byte.
|
396
|
+
if string.respond_to?(:force_encoding)
|
397
|
+
string.force_encoding("binary")
|
398
|
+
end
|
347
399
|
to_write = string.length
|
348
400
|
while (to_write > 0) do
|
349
401
|
if @curr_chunk && @curr_chunk.data.position == @chunk_size
|
@@ -363,9 +415,7 @@ module GridFS
|
|
363
415
|
def flush
|
364
416
|
end
|
365
417
|
|
366
|
-
#---
|
367
418
|
# ================ status ================
|
368
|
-
#+++
|
369
419
|
|
370
420
|
def eof
|
371
421
|
raise IOError.new("stream not open for reading") unless @mode[0] == ?r
|
@@ -373,9 +423,7 @@ module GridFS
|
|
373
423
|
end
|
374
424
|
alias_method :eof?, :eof
|
375
425
|
|
376
|
-
#---
|
377
426
|
# ================ positioning ================
|
378
|
-
#+++
|
379
427
|
|
380
428
|
def rewind
|
381
429
|
if @curr_chunk.chunk_number != 0
|
data/lib/mongo/types/binary.rb
CHANGED
@@ -18,7 +18,10 @@ require 'mongo/util/byte_buffer'
|
|
18
18
|
|
19
19
|
module Mongo
|
20
20
|
|
21
|
-
# An array of binary bytes with a
|
21
|
+
# An array of binary bytes with a MongoDB subtype. See the subtype
|
22
|
+
# constants for reference.
|
23
|
+
#
|
24
|
+
# Use this class when storing binary data in documents.
|
22
25
|
class Binary < ByteBuffer
|
23
26
|
|
24
27
|
SUBTYPE_BYTES = 0x02
|
@@ -29,6 +32,13 @@ module Mongo
|
|
29
32
|
# One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
|
30
33
|
attr_accessor :subtype
|
31
34
|
|
35
|
+
# Create a buffer for storing binary data in MongoDB.
|
36
|
+
#
|
37
|
+
# @param [Array] initia_data
|
38
|
+
# @param [Fixnum] one of four values specifying a BSON binary subtype. Possible values are
|
39
|
+
# SUBTYPE_BYTES, SUBTYPE_UUID, SUBTYPE_MD5, and SUBTYPE_USER_DEFINED.
|
40
|
+
#
|
41
|
+
# @see http://www.mongodb.org/display/DOCS/BSON#BSON-noteondatabinary BSON binary subtypes.
|
32
42
|
def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
|
33
43
|
super(initial_data)
|
34
44
|
@subtype = subtype
|
data/lib/mongo/types/code.rb
CHANGED
@@ -16,12 +16,17 @@
|
|
16
16
|
|
17
17
|
module Mongo
|
18
18
|
|
19
|
-
# JavaScript code to be evaluated by MongoDB
|
19
|
+
# JavaScript code to be evaluated by MongoDB.
|
20
20
|
class Code < String
|
21
21
|
|
22
22
|
# Hash mapping identifiers to their values
|
23
23
|
attr_accessor :scope
|
24
24
|
|
25
|
+
# Wrap code to be evaluated by MongoDB.
|
26
|
+
#
|
27
|
+
# @param [String] code the JavaScript code.
|
28
|
+
# @param [Hash] a document mapping identifiers to values, which
|
29
|
+
# represent the scope in which the code is to be executed.
|
25
30
|
def initialize(code, scope={})
|
26
31
|
super(code)
|
27
32
|
@scope = scope
|
data/lib/mongo/types/dbref.rb
CHANGED
@@ -16,13 +16,18 @@
|
|
16
16
|
|
17
17
|
module Mongo
|
18
18
|
|
19
|
+
# A reference to another object in a MongoDB database.
|
19
20
|
class DBRef
|
20
21
|
|
21
22
|
attr_reader :namespace, :object_id
|
22
23
|
|
24
|
+
# Create a DBRef. Use this class in conjunction with DB#dereference.
|
25
|
+
#
|
26
|
+
# @param [String] a collection name
|
27
|
+
# @param [ObjectID] an object id
|
23
28
|
def initialize(namespace, object_id)
|
24
|
-
@namespace
|
25
|
-
|
29
|
+
@namespace = namespace
|
30
|
+
@object_id = object_id
|
26
31
|
end
|
27
32
|
|
28
33
|
def to_s
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module Mongo
|
18
|
+
|
19
|
+
# A class representing the BSON MaxKey type. MaxKey will always compare greater than
|
20
|
+
# all other BSON types and values.
|
21
|
+
#
|
22
|
+
# @example Sorting (assume @numbers is a collection):
|
23
|
+
#
|
24
|
+
# >> @numbers.save({"n" => Mongo::MaxKey.new})
|
25
|
+
# >> @numbers.save({"n" => 0})
|
26
|
+
# >> @numbers.save({"n" => 5_000_000})
|
27
|
+
# >> @numbers.find.sort("n").to_a
|
28
|
+
# => [{"_id"=>4b5a050c238d3bace2000004, "n"=>0},
|
29
|
+
# {"_id"=>4b5a04e6238d3bace2000002, "n"=>5_000_000},
|
30
|
+
# {"_id"=>4b5a04ea238d3bace2000003, "n"=>#<Mongo::MaxKey:0x1014ef410>},
|
31
|
+
# ]
|
32
|
+
class MaxKey
|
33
|
+
|
34
|
+
def ==(obj)
|
35
|
+
obj.class == MaxKey
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# A class representing the BSON MinKey type. MinKey will always compare less than
|
40
|
+
# all other BSON types and values.
|
41
|
+
#
|
42
|
+
# @example Sorting (assume @numbers is a collection):
|
43
|
+
#
|
44
|
+
# >> @numbers.save({"n" => Mongo::MinKey.new})
|
45
|
+
# >> @numbers.save({"n" => -1_000_000})
|
46
|
+
# >> @numbers.save({"n" => 1_000_000})
|
47
|
+
# >> @numbers.find.sort("n").to_a
|
48
|
+
# => [{"_id"=>4b5a050c238d3bace2000004, "n"=>#<Mongo::MinKey:0x1014ef410>},
|
49
|
+
# {"_id"=>4b5a04e6238d3bace2000002, "n"=>-1_000_000},
|
50
|
+
# {"_id"=>4b5a04ea238d3bace2000003, "n"=>1_000_000},
|
51
|
+
# ]
|
52
|
+
class MinKey
|
53
|
+
|
54
|
+
def ==(obj)
|
55
|
+
obj.class == MinKey
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|