RSS Git Download  Clone
Raw Blame History 11kB 212 lines
HTML rendered
<!-- Search Query -->
<p3xr-ng-accordion [title]="strings().page?.search?.title" accordionKey="search-query">
    <div content>
        <div class="p3xr-padding">
            @if (indexes.length === 0) {
                <div style="opacity: 0.5;">{{ strings().page?.search?.noIndex }}</div>
            }

            @if (indexes.length > 0) {
                <div style="display: flex; align-items: center; gap: 8px;">
                    <mat-form-field class="md-block" style="flex: 1;">
                        <mat-label>{{ strings().page?.search?.index }}</mat-label>
                        <mat-select [(value)]="selectedIndex" (selectionChange)="offset = 0; indexInfo = null; loadIndexInfo()">
                            @for (idx of indexes; track idx) {
                                <mat-option [value]="idx">{{ idx }}</mat-option>
                            }
                        </mat-select>
                    </mat-form-field>
                    @if (!isReadonly && selectedIndex) {
                        <button mat-mini-fab class="btn-warn" (click)="dropIndex()" [matTooltip]="strings().page?.search?.dropIndex" matTooltipPosition="above">
                            <mat-icon>delete</mat-icon>
                        </button>
                    }
                </div>

                <mat-form-field class="md-block">
                    <mat-label>{{ strings().page?.search?.query }}</mat-label>
                    <input matInput [(ngModel)]="query" (keydown.enter)="offset = 0; handleSearchEnter()" [disabled]="aiLoading" />
                </mat-form-field>

                @if (state.redisVersion().isAtLeast(8, 4)) {
                    <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
                        <mat-slide-toggle [(ngModel)]="hybridMode" color="primary">
                            {{ strings().page?.search?.hybridMode }}
                        </mat-slide-toggle>
                    </div>
                    @if (hybridMode) {
                        <div style="display: flex; gap: 8px; flex-wrap: wrap;">
                            <mat-form-field style="flex: 1; min-width: 150px;">
                                <mat-label>{{ strings().page?.search?.vectorField }}</mat-label>
                                <input matInput [(ngModel)]="vectorField" placeholder="embedding" />
                            </mat-form-field>
                            <mat-form-field style="flex: 2; min-width: 200px;">
                                <mat-label>{{ strings().page?.search?.vectorValues }}</mat-label>
                                <input matInput [(ngModel)]="vectorValues" placeholder="0.1, 0.2, 0.3, ..." />
                            </mat-form-field>
                            <mat-form-field style="width: 80px;">
                                <mat-label>Count</mat-label>
                                <input matInput type="number" [(ngModel)]="vectorCount" />
                            </mat-form-field>
                        </div>
                    }
                }

                <div style="margin-top: 8px; text-align: right;">
                    @if (isGtSm) {
                        <button mat-raised-button class="btn-primary p3xr-action-btn" type="button" (click)="handleSearchEnter()" [disabled]="aiLoading">
                            <mat-icon>search</mat-icon>
                            <span>{{ aiLoading ? (strings().label?.aiTranslating) : (strings().page?.search?.title) }}</span>
                        </button>
                    } @else {
                        <button mat-mini-fab class="btn-primary" type="button" (click)="handleSearchEnter()" [disabled]="aiLoading"
                                [matTooltip]="strings().page?.search?.title" matTooltipPosition="above">
                            <mat-icon>search</mat-icon>
                        </button>
                    }
                </div>
            }
        </div>
    </div>
</p3xr-ng-accordion>

<!-- Results -->
@if (searchDone && total === 0) {
    <br />
    <p3xr-ng-accordion [title]="(strings().page?.search?.results) + ' (0)'" accordionKey="search-results">
        <div content>
            <div style="padding: 16px; opacity: 0.5;">{{ strings().label?.noResults }}</div>
        </div>
    </p3xr-ng-accordion>
}
@if (results.length > 0 || total > 0) {
    <br />
    <p3xr-ng-accordion [title]="(strings().page?.search?.results) + ' (' + total + ')'" accordionKey="search-results">
        <div actions>
            @if (pages > 1) {
                <p3xr-ng-button (click)="pageAction('first'); $event.stopPropagation()" label="" mdIcon="skip_previous"></p3xr-ng-button>
                <p3xr-ng-button (click)="pageAction('prev'); $event.stopPropagation()" label="" mdIcon="keyboard_arrow_left"></p3xr-ng-button>
                <span style="font-size: 12px; opacity: 0.7;">{{ currentPage }} / {{ pages }}</span>
                <p3xr-ng-button (click)="pageAction('next'); $event.stopPropagation()" label="" mdIcon="keyboard_arrow_right"></p3xr-ng-button>
                <p3xr-ng-button (click)="pageAction('last'); $event.stopPropagation()" label="" mdIcon="skip_next"></p3xr-ng-button>
            }
        </div>
        <div content>
            <mat-list>
                @for (doc of results; track doc._key) {
                    <mat-list-item>
                        <div class="p3xr-settings-pair-row">
                            <div class="p3xr-settings-row-label">
                                <kbd class="p3xr-kbd p3xr-kbd-small">{{ doc._key }}</kbd>
                            </div>
                            <div class="p3xr-settings-row-value p3xr-mono" style="font-size: 12px;">
                                @for (field of getDocKeys(doc); track field) {
                                    <span>{{ field }}: {{ doc[field] }}</span>
                                    @if (!$last) { <span> · </span> }
                                }
                            </div>
                        </div>
                    </mat-list-item>
                    <mat-divider></mat-divider>
                }
            </mat-list>
        </div>
    </p3xr-ng-accordion>
}

<!-- Index Info — shown after first search -->
@if (selectedIndex && indexInfo) {
    <br />
    <p3xr-ng-accordion [title]="(strings().page?.search?.indexInfo) + ': ' + selectedIndex" accordionKey="search-index-info">
        <div actions>
            @if (!isReadonly) {
                <p3xr-ng-button
                    (click)="dropIndex(); $event.stopPropagation()"
                    [label]="strings().page?.search?.dropIndex"
                    mdIcon="delete">
                </p3xr-ng-button>
            }
        </div>
        <div content>
            @if (indexInfo) {
                <mat-list>
                    @for (key of getDocKeys(indexInfo); track key) {
                        <mat-list-item>
                            <div class="p3xr-settings-pair-row">
                                <div class="p3xr-settings-row-label">{{ key }}</div>
                                <div class="p3xr-settings-row-value p3xr-mono" style="font-size: 12px;">{{ indexInfo[key] | json }}</div>
                            </div>
                        </mat-list-item>
                        <mat-divider></mat-divider>
                    }
                </mat-list>
            }
        </div>
    </p3xr-ng-accordion>
}

<!-- Create Index -->
@if (!isReadonly) {
    <br />
    <p3xr-ng-accordion [title]="strings().page?.search?.createIndex" accordionKey="search-create-index">
        <div content>
            <div class="p3xr-padding">
                <mat-form-field class="md-block">
                    <mat-label>{{ strings().page?.search?.indexName }}</mat-label>
                    <input matInput [(ngModel)]="newIndexName" />
                </mat-form-field>

                <mat-form-field class="md-block">
                    <mat-label>{{ strings().page?.search?.prefix }}</mat-label>
                    <input matInput [(ngModel)]="newIndexPrefix" placeholder="e.g. doc:" />
                </mat-form-field>

                <div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
                    <strong>Schema</strong>
                    <button mat-mini-fab class="btn-primary" (click)="addField()" type="button">
                        <mat-icon>add</mat-icon>
                    </button>
                </div>

                @for (field of newIndexFields; track $index; let i = $index) {
                    <div class="p3xr-search-schema-row">
                        <mat-form-field style="flex: 1;">
                            <mat-label>{{ strings().page?.search?.fieldName }}</mat-label>
                            <input matInput [(ngModel)]="field.name" />
                        </mat-form-field>
                        <div class="p3xr-search-schema-type-row">
                            <mat-form-field style="width: 130px;">
                                <mat-label>{{ strings().label?.type }}</mat-label>
                                <mat-select [(value)]="field.type">
                                    <mat-option value="TEXT">TEXT</mat-option>
                                    <mat-option value="NUMERIC">NUMERIC</mat-option>
                                    <mat-option value="TAG">TAG</mat-option>
                                    <mat-option value="GEO">GEO</mat-option>
                                    <mat-option value="VECTOR">VECTOR</mat-option>
                                </mat-select>
                            </mat-form-field>
                            <button mat-mini-fab class="btn-warn" (click)="confirmRemoveField(i)" [disabled]="newIndexFields.length <= 1" type="button">
                                <mat-icon>remove</mat-icon>
                            </button>
                        </div>
                    </div>
                }

                <div style="margin-top: 8px; text-align: right;">
                    @if (isGtSm) {
                        <button mat-raised-button class="btn-accent p3xr-action-btn" type="button" (click)="createIndex()" [disabled]="!newIndexName.trim()">
                            <mat-icon>add</mat-icon>
                            <span>{{ strings().page?.search?.createIndex }}</span>
                        </button>
                    } @else {
                        <button mat-mini-fab class="btn-accent" type="button" (click)="createIndex()" [disabled]="!newIndexName.trim()"
                                [matTooltip]="strings().page?.search?.createIndex" matTooltipPosition="above">
                            <mat-icon>add</mat-icon>
                        </button>
                    }
                </div>
            </div>
        </div>
    </p3xr-ng-accordion>
}