aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/SearchUtil.ts37
-rw-r--r--src/client/views/collections/ParentDocumentSelector.tsx4
-rw-r--r--src/client/views/search/SearchBox.tsx16
-rw-r--r--src/client/views/search/SearchItem.scss12
-rw-r--r--src/client/views/search/SearchItem.tsx10
-rw-r--r--src/server/Search.ts12
-rw-r--r--src/server/database.ts11
-rw-r--r--src/server/index.ts7
-rw-r--r--src/server/updateProtos.ts15
9 files changed, 83 insertions, 41 deletions
diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts
index 806746496..abf1a7c32 100644
--- a/src/client/util/SearchUtil.ts
+++ b/src/client/util/SearchUtil.ts
@@ -4,30 +4,41 @@ import { Doc } from '../../new_fields/Doc';
import { Id } from '../../new_fields/FieldSymbols';
export namespace SearchUtil {
+ export type HighlightingResult = { [id: string]: { [key: string]: string[] } };
+
export interface IdSearchResult {
ids: string[];
numFound: number;
+ highlighting: HighlightingResult | undefined;
}
export interface DocSearchResult {
docs: Doc[];
numFound: number;
+ highlighting: HighlightingResult | undefined;
}
- export function Search(query: string, filterQuery: string | undefined, returnDocs: true, start?: number, count?: number): Promise<DocSearchResult>;
- export function Search(query: string, filterQuery: string | undefined, returnDocs: false, start?: number, count?: number): Promise<IdSearchResult>;
- export async function Search(query: string, filterQuery: string | undefined, returnDocs: boolean, start?: number, rows?: number) {
+ export interface SearchParams {
+ hl?: boolean;
+ "hl.fl"?: string;
+ start?: number;
+ rows?: number;
+ fq?: string;
+ }
+ export function Search(query: string, returnDocs: true, options?: SearchParams): Promise<DocSearchResult>;
+ export function Search(query: string, returnDocs: false, options?: SearchParams): Promise<IdSearchResult>;
+ export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) {
query = query || "*"; //If we just have a filter query, search for * as the query
const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), {
- qs: { query, filterQuery, start, rows },
+ qs: { ...options, q: query },
}));
if (!returnDocs) {
return result;
}
- const { ids, numFound } = result;
+ const { ids, numFound, highlighting } = result;
const docMap = await DocServer.GetRefFields(ids);
const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc);
- return { docs, numFound };
+ return { docs, numFound, highlighting };
}
export async function GetAliasesOfDocument(doc: Doc): Promise<Doc[]>;
@@ -36,31 +47,31 @@ export namespace SearchUtil {
const proto = Doc.GetProto(doc);
const protoId = proto[Id];
if (returnDocs) {
- return (await Search("", `proto_i:"${protoId}"`, returnDocs)).docs;
+ return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).docs;
} else {
- return (await Search("", `proto_i:"${protoId}"`, returnDocs)).ids;
+ return (await Search("", returnDocs, { fq: `proto_i:"${protoId}"` })).ids;
}
// return Search(`{!join from=id to=proto_i}id:${protoId}`, true);
}
export async function GetViewsOfDocument(doc: Doc): Promise<Doc[]> {
- const results = await Search("", `proto_i:"${doc[Id]}"`, true);
+ const results = await Search("", true, { fq: `proto_i:"${doc[Id]}"` });
return results.docs;
}
export async function GetContextsOfDocument(doc: Doc): Promise<{ contexts: Doc[], aliasContexts: Doc[] }> {
- const docContexts = (await Search("", `data_l:"${doc[Id]}"`, true)).docs;
+ const docContexts = (await Search("", true, { fq: `data_l:"${doc[Id]}"` })).docs;
const aliases = await GetAliasesOfDocument(doc, false);
- const aliasContexts = (await Promise.all(aliases.map(doc => Search("", `data_l:"${doc}"`, true))));
+ const aliasContexts = (await Promise.all(aliases.map(doc => Search("", true, { fq: `data_l:"${doc}"` }))));
const contexts = { contexts: docContexts, aliasContexts: [] as Doc[] };
aliasContexts.forEach(result => contexts.aliasContexts.push(...result.docs));
return contexts;
}
export async function GetContextIdsOfDocument(doc: Doc): Promise<{ contexts: string[], aliasContexts: string[] }> {
- const docContexts = (await Search("", `data_l:"${doc[Id]}"`, false)).ids;
+ const docContexts = (await Search("", false, { fq: `data_l:"${doc[Id]}"` })).ids;
const aliases = await GetAliasesOfDocument(doc, false);
- const aliasContexts = (await Promise.all(aliases.map(doc => Search("", `data_l:"${doc}"`, false))));
+ const aliasContexts = (await Promise.all(aliases.map(doc => Search("", false, { fq: `data_l:"${doc}"` }))));
const contexts = { contexts: docContexts, aliasContexts: [] as string[] };
aliasContexts.forEach(result => contexts.aliasContexts.push(...result.ids));
return contexts;
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx
index a97aa4f36..c3e55d825 100644
--- a/src/client/views/collections/ParentDocumentSelector.tsx
+++ b/src/client/views/collections/ParentDocumentSelector.tsx
@@ -23,9 +23,9 @@ export class SelectorContextMenu extends React.Component<SelectorProps> {
async fetchDocuments() {
let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document);
- const { docs } = await SearchUtil.Search("", `data_l:"${this.props.Document[Id]}"`, true);
+ const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.Document[Id]}"` });
const map: Map<Doc, Doc> = new Map;
- const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", `data_l:"${doc[Id]}"`, true).then(result => result.docs)));
+ const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index])));
docs.forEach(doc => map.delete(doc));
runInAction(() => {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d07df7e58..108492e90 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -23,7 +23,7 @@ export class SearchBox extends React.Component {
@observable private _searchString: string = "";
@observable private _resultsOpen: boolean = false;
@observable private _searchbarOpen: boolean = false;
- @observable private _results: Doc[] = [];
+ @observable private _results: [Doc, string[]][] = [];
private _resultsSet = new Set<Doc>();
@observable private _openNoResults: boolean = false;
@observable private _visibleElements: JSX.Element[] = [];
@@ -119,7 +119,7 @@ export class SearchBox extends React.Component {
}
getAllResults = async (query: string) => {
- return SearchUtil.Search(query, this.filterQuery, true, 0, 10000000);
+ return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 });
}
private get filterQuery() {
@@ -136,20 +136,22 @@ export class SearchBox extends React.Component {
}
this.lockPromise = new Promise(async res => {
while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
- this._curRequest = SearchUtil.Search(query, this.filterQuery, true, this._maxSearchIndex, 10).then(action(async (res: SearchUtil.DocSearchResult) => {
+ this._curRequest = SearchUtil.Search(query, true, { fq: this.filterQuery, start: this._maxSearchIndex, rows: 10, hl: true, "hl.fl": "*" }).then(action(async (res: SearchUtil.DocSearchResult) => {
// happens at the beginning
if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
this._numTotalResults = res.numFound;
}
+ const highlighing = res.highlighting || {};
const docs = await Promise.all(res.docs.map(doc => Cast(doc.extendsDoc, Doc, doc as any)));
let filteredDocs = FilterBox.Instance.filterDocsByType(docs);
runInAction(() => {
// this._results.push(...filteredDocs);
filteredDocs.forEach(doc => {
if (!this._resultsSet.has(doc)) {
- this._results.push(doc);
+ const highlight = highlighing[doc[Id]];
+ this._results.push([doc, highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)) : []]);
this._resultsSet.add(doc);
}
});
@@ -274,19 +276,19 @@ export class SearchBox extends React.Component {
}
else {
if (this._isSearch[i] !== "search") {
- let result: Doc | undefined = undefined;
+ let result: [Doc, string[]] | undefined = undefined;
if (i >= this._results.length) {
this.getResults(this._searchString);
if (i < this._results.length) result = this._results[i];
if (result) {
- this._visibleElements[i] = <SearchItem doc={result} key={result[Id]} />;
+ this._visibleElements[i] = <SearchItem doc={result[0]} key={result[0][Id]} highlighting={result[1]} />;
this._isSearch[i] = "search";
}
}
else {
result = this._results[i];
if (result) {
- this._visibleElements[i] = <SearchItem doc={result} key={result[Id]} />;
+ this._visibleElements[i] = <SearchItem doc={result[0]} key={result[0][Id]} highlighting={result[1]} />;
this._isSearch[i] = "search";
}
}
diff --git a/src/client/views/search/SearchItem.scss b/src/client/views/search/SearchItem.scss
index 24dd2eaa3..273d49349 100644
--- a/src/client/views/search/SearchItem.scss
+++ b/src/client/views/search/SearchItem.scss
@@ -24,11 +24,15 @@
flex-direction: row;
width: 100%;
- .search-title {
- text-transform: uppercase;
- text-align: left;
+ .search-title-container {
width: 100%;
- font-weight: bold;
+
+ .search-title {
+ text-transform: uppercase;
+ text-align: left;
+ width: 100%;
+ font-weight: bold;
+ }
}
.search-info {
diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx
index e34d101a8..26f00e03e 100644
--- a/src/client/views/search/SearchItem.tsx
+++ b/src/client/views/search/SearchItem.tsx
@@ -26,6 +26,7 @@ import { faFile } from '@fortawesome/free-solid-svg-icons';
export interface SearchItemProps {
doc: Doc;
+ highlighting: string[];
}
library.add(faCaretUp);
@@ -51,9 +52,9 @@ export class SelectorContextMenu extends React.Component<SearchItemProps> {
async fetchDocuments() {
let aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc);
- const { docs } = await SearchUtil.Search("", `data_l:"${this.props.doc[Id]}"`, true);
+ const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${this.props.doc[Id]}"` });
const map: Map<Doc, Doc> = new Map;
- const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", `data_l:"${doc[Id]}"`, true).then(result => result.docs)));
+ const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs)));
allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index])));
docs.forEach(doc => map.delete(doc));
runInAction(() => {
@@ -243,7 +244,10 @@ export class SearchItem extends React.Component<SearchItemProps> {
onClick={this.onClick} onPointerDown={this.pointerDown} >
<div className="main-search-info">
<div title="Drag as document" onPointerDown={this.onPointerDown} style={{ marginRight: "7px" }}> <FontAwesomeIcon icon="file" size="lg" /> </div>
- <div className="search-title" id="result" >{StrCast(this.props.doc.title)}</div>
+ <div className="search-title-container">
+ <div className="search-title">{StrCast(this.props.doc.title)}</div>
+ <div className="search-highlighting">Matched fields: {this.props.highlighting.join(", ")}</div>
+ </div>
<div className="search-info" style={{ width: this._useIcons ? "15%" : "400px" }}>
<div className={`icon-${this._useIcons ? "icons" : "live"}`}>
<div className="search-type" title="Click to Preview">{this.DocumentIcon}</div>
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 69e327d2d..723dc101b 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -30,20 +30,14 @@ export class Search {
}
}
- public async search(query: string, filterQuery: string = "", start: number = 0, rows: number = 10) {
+ public async search(query: any) {
try {
const searchResults = JSON.parse(await rp.get(this.url + "dash/select", {
- qs: {
- q: query,
- fq: filterQuery,
- fl: "id",
- start,
- rows,
- }
+ qs: query
}));
const { docs, numFound } = searchResults.response;
const ids = docs.map((field: any) => field.id);
- return { ids, numFound };
+ return { ids, numFound, highlighting: searchResults.highlighting };
} catch {
return { ids: [], numFound: -1 };
}
diff --git a/src/server/database.ts b/src/server/database.ts
index 29a8ffafa..7f5331998 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -140,6 +140,17 @@ export class Database {
}
}
+ public updateMany(query: any, update: any, collectionName = "newDocuments") {
+ if (this.db) {
+ const db = this.db;
+ return new Promise<mongodb.WriteOpResult>(res => db.collection(collectionName).update(query, update, (_, result) => res(result)));
+ } else {
+ return new Promise<mongodb.WriteOpResult>(res => {
+ this.onConnect.push(() => this.updateMany(query, update, collectionName).then(res));
+ });
+ }
+ }
+
public print() {
console.log("db says hi!");
}
diff --git a/src/server/index.ts b/src/server/index.ts
index 2cca7a35b..ad879093b 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -144,12 +144,13 @@ app.get("/pull", (req, res) =>
// GETTERS
app.get("/search", async (req, res) => {
- const { query, filterQuery, start, rows } = req.query;
- if (query === undefined) {
+ const solrQuery: any = {};
+ ["q", "fq", "start", "rows", "hl", "hl.fl"].forEach(key => solrQuery[key] = req.query[key]);
+ if (solrQuery.q === undefined) {
res.send([]);
return;
}
- let results = await Search.Instance.search(query, filterQuery, start, rows);
+ let results = await Search.Instance.search(solrQuery);
res.send(results);
});
diff --git a/src/server/updateProtos.ts b/src/server/updateProtos.ts
new file mode 100644
index 000000000..90490eb45
--- /dev/null
+++ b/src/server/updateProtos.ts
@@ -0,0 +1,15 @@
+import { Database } from "./database";
+
+const protos =
+ ["text", "histogram", "image", "web", "collection", "kvp",
+ "video", "audio", "pdf", "icon", "import", "linkdoc"];
+
+(async function () {
+ await Promise.all(
+ protos.map(protoId => new Promise(res => Database.Instance.update(protoId, {
+ $set: { "fields.baseProto": true }
+ }, res)))
+ );
+
+ console.log("done");
+})(); \ No newline at end of file