Search
mash.search · Platform
mash.search finds files two different ways, and the difference matters. execute asks the search index, which understands words, phrases and file contents, and may lag a little behind recent changes. byField asks the database for files whose custom field has a value. It’s exact and sees a value the instant it’s written. Pick execute to find by what a file says. Pick byField to find by what a file has been marked with.
How it works
Section titled “How it works”execute(where, query) returns a result set. where is a share (name or id) or a dictionary of options such as {share: "Finance", contents: true, rpp: 20, page: 2}. query uses the same search syntax as the search box. The set has total (all matches), page, per_page, last_page, and holds up to one page of results. Loop over it, or ask count() for how many are on this page.
A result is a summary, not a file. It has name, path, size, is_dir, mimetype, modified, share, and highlight (a snippet around the match). Open the real thing with mash.files.get(hit.share, hit.path). Ask for contents: true in the options if you want the text of each hit, which is slower.
Search answers as someone. A script run by a user searches as that user, so results are limited to what they may see. An administrator’s script searches unrestricted. Pass a user as the third argument to search as someone else (a script run by a user can’t).
byField(share, field, value) returns a result set of rows. Each row has path, value and updated_at. A string value matches exactly, a list matches any of them, and no value means “has any value”. It returns up to 200 rows by default (the rpp option, at most 1000), ordered by path. lastPath is the cursor for the next batch, passed back as the after option.
escape makes text safe in a query. Put a name like Smith (Ltd) in a query and the brackets mean something to the search. mash.search.escape("Smith (Ltd)") backslashes them first.
Common tasks
Section titled “Common tasks”Find invoices that mention a customer, and open each:
set hits to mash.search.execute("Finance", "invoice AND \"Acme Ltd\"")printline "{{hits.total}} matches"each hits as hit printline "{{hit.name}}: {{hit.highlight}}"endSearch for text a person typed, without letting it change the query:
set typed to "Smith (Ltd) & Co"printline mash.search.escape(typed)Smith \(Ltd\) \& CoWork through everything waiting for review, whatever state the index is in:
set batch to mash.search.byField("Orders", "Status", "Pending")each batch as row set doc to mash.files.get("Orders", row.path) doc.fields.set("Status", "Done")endWalk a large share page by page:
set options to {rpp: 500}while true set batch to mash.search.byField("Orders", "Customer", null, options) if count(batch) is 0 break end each batch as row printline row.path end set options["after"] to batch.lastPathendWatch out for
Section titled “Watch out for”- Test for no results with
count(), neverif batch. A result set is always “true”. - The index can be behind. A file uploaded or a field set a second ago may not be in
executeyet. UsebyFieldfor state you’ve just written. byFieldonly sees files that have a value. “Files with this field empty” can’t be asked. Give each file a starting value, then look for it.- The state flip is your cursor. In a loop that sets
PendingtoDone, the next call no longer finds the finished files, so you don’t needafter. Useafterfor a set that doesn’t change. - Search results aren’t File objects.
hit.fieldsdoesn’t exist. Open the file first.
See also
Section titled “See also”Custom fields, mash.fields for searching across shares, and the File type.
Methods
Section titled “Methods”byField
Section titled “byField”search.byField(share: Share, field: mixed, value?: mixed, options?: array = []) → FieldResultSet
Enumerate files in a share by custom-field value — straight from the database, no Solr. Exact and read-your-writes: a value written by fields.set (or the web UI, or Captur) is visible here immediately, making this the right primitive for workflow state machines where search.execute() is eventually consistent.
Value matching: string = equality, array = IN, null/omitted = “field has any value”. Only files WITH a value are enumerable — rows exist only for set values, so “files missing the field” is unanswerable here by design. Prefer positive state machines (set status=pending at discovery, enumerate pending, flip to done after processing).
Each row’s path is share-relative, as mash.files.get(share, path) takes it. Its value is the field’s value, or for a multivalue field the list of every value it holds on that file.
Options: rpp batch size (default 200, capped at 1000) after keyset cursor — return paths > after (use result.lastPath) withValues include each row’s value (default true) user user (or guid) to enforce field read permission for; omitted = system context, unrestricted (mirrors fields[…])
Iteration idioms (test emptiness with count()):
Drain pattern: the state flip IS the cursor while true batch = mash.search.byField(share, “status”, “pending”) if count(batch) == 0 break end each batch as r # … flip to done … end end
Section titled “Drain pattern: the state flip IS the cursor while true batch = mash.search.byField(share, “status”, “pending”) if count(batch) == 0 break end each batch as r # … flip to done … end end”Static set: keyset pagination opts = {“rpp”: 1000} opts[“after”] = batch.lastPath next = mash.search.byField(share, “f”, null, opts)
Section titled “Static set: keyset pagination opts = {“rpp”: 1000} opts[“after”] = batch.lastPath next = mash.search.byField(share, “f”, null, opts)”escape
Section titled “escape”search.escape(str: string) → string
Escape special Lucene/Solr characters in a query string.
execute
Section titled “execute”search.execute(config: mixed, query: string, user?: User) → SearchResultSet
Execute a search query.