├── README.md
├── css
├── bootstrap.css
├── favicon.ico
├── isdc-theme.css
└── logo.png
├── data.sql
├── index.php
├── sphinx
├── bin
│ ├── 1cmd.bat
│ ├── 1start.bat
│ ├── 1索引.bat
│ ├── indexer.exe
│ ├── indextool.exe
│ ├── libeay32.dll
│ ├── libiconv-2.dll
│ ├── libintl-8.dll
│ ├── libmariadb.dll
│ ├── libpq.dll
│ ├── msvcr120.dll
│ ├── searchd.exe
│ ├── sphinx.conf
│ ├── ssleay32.dll
│ └── wordbreaker.exe
├── etc
│ ├── example.sql
│ ├── sphinx-min.conf.dist
│ ├── sphinx.conf.dist
│ ├── uni.lib
│ └── unigram.txt
├── misc
│ ├── raminfo.py
│ ├── resolve.py
│ ├── search_json_pretty.php
│ ├── searchd
│ ├── suggest
│ │ ├── README
│ │ ├── suggest.conf
│ │ └── suggest.php
│ └── wordbreak.pl
└── src
│ ├── sphinxudf.c
│ ├── sphinxudf.h
│ └── udfexample.c
├── sphinxapi.php
└── test.jpg
/README.md:
--------------------------------------------------------------------------------
1 | # findpass
2 | 基于Sphinx的社工库
3 |
4 | 环境:Php + Mysql + Sphinx
5 |
6 | 
7 |
--------------------------------------------------------------------------------
/css/bootstrap.css:
--------------------------------------------------------------------------------
1 |
2 | article,
3 | aside,
4 | details,
5 | figcaption,
6 | figure,
7 | footer,
8 | header,
9 | hgroup,
10 | main,
11 | nav,
12 | section,
13 | summary {
14 | display: block;
15 | }
16 |
17 |
18 | html {
19 | font-family: sans-serif;
20 | -webkit-text-size-adjust: 100%;
21 | -ms-text-size-adjust: 100%;
22 | }
23 |
24 | body {
25 | margin: 0;
26 | }
27 |
28 | a {
29 | background: transparent;
30 | }
31 |
32 | a:focus {
33 | outline: thin dotted;
34 | }
35 |
36 | a:active,
37 | a:hover {
38 | outline: 0;
39 | }
40 |
41 | h1 {
42 | margin: 0.67em 0;
43 | font-size: 2em;
44 | }
45 |
46 | abbr[title] {
47 | border-bottom: 1px dotted;
48 | }
49 |
50 | b,
51 | strong {
52 | font-weight: bold;
53 | }
54 |
55 |
56 | hr {
57 | height: 0;
58 | -moz-box-sizing: content-box;
59 | box-sizing: content-box;
60 | }
61 |
62 | img {
63 | border: 0;
64 | }
65 |
66 | legend {
67 | padding: 0;
68 | border: 0;
69 | }
70 |
71 | button,
72 | input,
73 | select,
74 | textarea {
75 | margin: 0;
76 | font-family: inherit;
77 | font-size: 100%;
78 | }
79 |
80 | button,
81 | input {
82 | line-height: normal;
83 | }
84 |
85 | button,
86 | select {
87 | text-transform: none;
88 | }
89 |
90 | button,
91 | html input[type="button"],
92 | input[type="reset"],
93 | input[type="submit"] {
94 | cursor: pointer;
95 | -webkit-appearance: button;
96 | }
97 |
98 | button[disabled],
99 | html input[disabled] {
100 | cursor: default;
101 | }
102 |
103 | input[type="checkbox"],
104 | input[type="radio"] {
105 | padding: 0;
106 | box-sizing: border-box;
107 | }
108 |
109 | input[type="search"] {
110 | -webkit-box-sizing: content-box;
111 | -moz-box-sizing: content-box;
112 | box-sizing: content-box;
113 | -webkit-appearance: textfield;
114 | }
115 |
116 | input[type="search"]::-webkit-search-cancel-button,
117 | input[type="search"]::-webkit-search-decoration {
118 | -webkit-appearance: none;
119 | }
120 |
121 | button::-moz-focus-inner,
122 | input::-moz-focus-inner {
123 | padding: 0;
124 | border: 0;
125 | }
126 |
127 | textarea {
128 | overflow: auto;
129 | vertical-align: top;
130 | }
131 |
132 | table {
133 | border-collapse: collapse;
134 | border-spacing: 0;
135 | }
136 |
137 | @media print {
138 | * {
139 | color: #000 !important;
140 | text-shadow: none !important;
141 | background: transparent !important;
142 | box-shadow: none !important;
143 | }
144 | a,
145 | a:visited {
146 | text-decoration: underline;
147 | }
148 | a[href]:after {
149 | content: " (" attr(href) ")";
150 | }
151 | abbr[title]:after {
152 | content: " (" attr(title) ")";
153 | }
154 | a[href^="javascript:"]:after,
155 | a[href^="#"]:after {
156 | content: "";
157 | }
158 | tr,
159 | img {
160 | page-break-inside: avoid;
161 | }
162 | img {
163 | max-width: 100% !important;
164 | }
165 | @page {
166 | margin: 2cm .5cm;
167 | }
168 | p,
169 | h2,
170 | h3 {
171 | orphans: 3;
172 | widows: 3;
173 | }
174 | h2,
175 | h3 {
176 | page-break-after: avoid;
177 | }
178 | select {
179 | background: #fff !important;
180 | }
181 | .navbar {
182 | display: none;
183 | }
184 | .table td,
185 | .table th {
186 | background-color: #fff !important;
187 | }
188 | .btn > .caret,
189 | .dropup > .btn > .caret {
190 | border-top-color: #000 !important;
191 | }
192 | .label {
193 | border: 1px solid #000;
194 | }
195 | .table {
196 | border-collapse: collapse !important;
197 | }
198 | .table-bordered th,
199 | .table-bordered td {
200 | border: 1px solid #ddd !important;
201 | }
202 | }
203 |
204 | *,
205 | *:before,
206 | *:after {
207 | -webkit-box-sizing: border-box;
208 | -moz-box-sizing: border-box;
209 | box-sizing: border-box;
210 | }
211 |
212 | html {
213 | font-size: 62.5%;
214 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
215 | }
216 |
217 | body {
218 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
219 | font-size: 14px;
220 | line-height: 1.428571429;
221 | color: #333333;
222 | background-color: #ffffff;
223 | }
224 |
225 | input,
226 | button,
227 | select,
228 | textarea {
229 | font-family: inherit;
230 | font-size: inherit;
231 | line-height: inherit;
232 | }
233 |
234 | a {
235 | color: #680808;
236 | text-decoration: none;
237 | }
238 |
239 | a:hover,
240 | a:focus {
241 | color: #2a6496;
242 | text-decoration: underline;
243 | }
244 |
245 | a:focus {
246 | outline: thin dotted;
247 | outline: 5px auto -webkit-focus-ring-color;
248 | outline-offset: -2px;
249 | }
250 |
251 |
252 |
253 | p {
254 | margin: 0 0 10px;
255 | }
256 |
257 |
258 | .text-muted {
259 | color: #999999;
260 | }
261 |
262 | .text-center {
263 | text-align: center;
264 | }
265 |
266 | .container {
267 | padding-right: 15px;
268 | padding-left: 15px;
269 | margin-right: auto;
270 | margin-left: auto;
271 | }
272 |
273 | .container:before,
274 | .container:after {
275 | display: table;
276 | content: " ";
277 | }
278 |
279 | .container:after {
280 | clear: both;
281 | }
282 |
283 | .container:before,
284 | .container:after {
285 | display: table;
286 | content: " ";
287 | }
288 |
289 | .container:after {
290 | clear: both;
291 | }
292 |
293 |
294 | @media (min-width: 768px) {
295 | .container {
296 | width: 750px;
297 | }
298 | }
299 |
300 | @media (min-width: 992px) {
301 | .container {
302 | width: 970px;
303 | }
304 | }
305 |
306 | @media (min-width: 1200px) {
307 | .container {
308 | width: 1170px;
309 | }
310 | }
311 |
312 | .row {
313 | margin-right: -15px;
314 | margin-left: -15px;
315 | }
316 |
317 | .row:before,
318 | .row:after {
319 | display: table;
320 | content: " ";
321 | }
322 |
323 | .row:after {
324 | clear: both;
325 | }
326 |
327 | .row:before,
328 | .row:after {
329 | display: table;
330 | content: " ";
331 | }
332 |
333 | .row:after {
334 | clear: both;
335 | }
336 |
337 |
338 | table {
339 | max-width: 100%;
340 | background-color: transparent;
341 | }
342 |
343 | th {
344 | text-align: left;
345 | }
346 |
347 |
348 | input[type="search"] {
349 | -webkit-box-sizing: border-box;
350 | -moz-box-sizing: border-box;
351 | box-sizing: border-box;
352 | }
353 |
354 | input[type="radio"],
355 | input[type="checkbox"] {
356 | margin: 4px 0 0;
357 | margin-top: 1px \9;
358 | /* IE8-9 */
359 |
360 | line-height: normal;
361 | }
362 |
363 | input[type="file"] {
364 | display: block;
365 | }
366 |
367 | select[multiple],
368 | select[size] {
369 | height: auto;
370 | }
371 |
372 | select optgroup {
373 | font-family: inherit;
374 | font-size: inherit;
375 | font-style: inherit;
376 | }
377 |
378 | input[type="file"]:focus,
379 | input[type="radio"]:focus,
380 | input[type="checkbox"]:focus {
381 | outline: thin dotted;
382 | outline: 5px auto -webkit-focus-ring-color;
383 | outline-offset: -2px;
384 | }
385 |
386 | input[type="number"]::-webkit-outer-spin-button,
387 | input[type="number"]::-webkit-inner-spin-button {
388 | height: auto;
389 | }
390 |
391 | output {
392 | display: block;
393 | padding-top: 7px;
394 | font-size: 14px;
395 | line-height: 1.428571429;
396 | color: #555555;
397 | vertical-align: middle;
398 | }
399 |
400 | .form-control {
401 | display: block;
402 | width: 100%;
403 | height: 34px;
404 | padding: 6px 12px;
405 | font-size: 14px;
406 | line-height: 1.428571429;
407 | color: #555555;
408 | vertical-align: middle;
409 | background-color: #ffffff;
410 | background-image: none;
411 | border: 1px solid #cccccc;
412 | border-radius: 4px;
413 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
414 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
415 | -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
416 | transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
417 | }
418 |
419 | .form-control:focus {
420 | border-color: #66afe9;
421 | outline: 0;
422 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
423 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
424 | }
425 |
426 | .form-control:-moz-placeholder {
427 | color: #999999;
428 | }
429 |
430 | .form-control::-moz-placeholder {
431 | color: #999999;
432 | opacity: 1;
433 | }
434 |
435 | .form-control:-ms-input-placeholder {
436 | color: #999999;
437 | }
438 |
439 | .form-control::-webkit-input-placeholder {
440 | color: #999999;
441 | }
442 |
443 | .form-control[disabled],
444 | .form-control[readonly],
445 | fieldset[disabled] .form-control {
446 | cursor: not-allowed;
447 | background-color: #eeeeee;
448 | }
449 |
450 | textarea.form-control {
451 | height: auto;
452 | }
453 |
454 |
455 | .btn {
456 | display: inline-block;
457 | padding: 6px 12px;
458 | margin-bottom: 0;
459 | font-size: 14px;
460 | font-weight: normal;
461 | line-height: 1.428571429;
462 | text-align: center;
463 | white-space: nowrap;
464 | vertical-align: middle;
465 | cursor: pointer;
466 | background-image: none;
467 | border: 1px solid transparent;
468 | border-radius: 4px;
469 | -webkit-user-select: none;
470 | -moz-user-select: none;
471 | -ms-user-select: none;
472 | -o-user-select: none;
473 | user-select: none;
474 | }
475 |
476 |
477 |
478 | .btn-primary {
479 | color: #ffffff;
480 | background-color: #428bca;
481 | border-color: #357ebd;
482 | }
483 |
484 | .btn-primary:hover,
485 | .btn-primary:focus,
486 | .btn-primary:active,
487 | .btn-primary.active,
488 | .open .dropdown-toggle.btn-primary {
489 | color: #ffffff;
490 | background-color: #3276b1;
491 | border-color: #285e8e;
492 | }
493 |
494 | .btn-primary:active,
495 | .btn-primary.active,
496 | .open .dropdown-toggle.btn-primary {
497 | background-image: none;
498 | }
499 |
500 | .btn-primary.disabled,
501 | .btn-primary[disabled],
502 | fieldset[disabled] .btn-primary,
503 | .btn-primary.disabled:hover,
504 | .btn-primary[disabled]:hover,
505 | fieldset[disabled] .btn-primary:hover,
506 | .btn-primary.disabled:focus,
507 | .btn-primary[disabled]:focus,
508 | fieldset[disabled] .btn-primary:focus,
509 | .btn-primary.disabled:active,
510 | .btn-primary[disabled]:active,
511 | fieldset[disabled] .btn-primary:active,
512 | .btn-primary.disabled.active,
513 | .btn-primary[disabled].active,
514 | fieldset[disabled] .btn-primary.active {
515 | background-color: #428bca;
516 | border-color: #357ebd;
517 | }
518 |
519 | .btn-primary .badge {
520 | color: #428bca;
521 | background-color: #fff;
522 | }
523 |
524 |
525 | .btn-success {
526 | color: #ffffff;
527 | background-color: #5cb85c;
528 | border-color: #4cae4c;
529 | }
530 |
531 | .btn-success:hover,
532 | .btn-success:focus,
533 | .btn-success:active,
534 | .btn-success.active,
535 | .open .dropdown-toggle.btn-success {
536 | color: #ffffff;
537 | background-color: #47a447;
538 | border-color: #398439;
539 | }
540 |
541 | .btn-success:active,
542 | .btn-success.active,
543 | .open .dropdown-toggle.btn-success {
544 | background-image: none;
545 | }
546 |
547 | .btn-success.disabled,
548 | .btn-success[disabled],
549 | fieldset[disabled] .btn-success,
550 | .btn-success.disabled:hover,
551 | .btn-success[disabled]:hover,
552 | fieldset[disabled] .btn-success:hover,
553 | .btn-success.disabled:focus,
554 | .btn-success[disabled]:focus,
555 | fieldset[disabled] .btn-success:focus,
556 | .btn-success.disabled:active,
557 | .btn-success[disabled]:active,
558 | fieldset[disabled] .btn-success:active,
559 | .btn-success.disabled.active,
560 | .btn-success[disabled].active,
561 | fieldset[disabled] .btn-success.active {
562 | background-color: #5cb85c;
563 | border-color: #4cae4c;
564 | }
565 |
566 | .btn-success .badge {
567 | color: #5cb85c;
568 | background-color: #fff;
569 | }
570 |
571 |
572 |
573 |
574 | .fade {
575 | opacity: 0;
576 | -webkit-transition: opacity 0.15s linear;
577 | transition: opacity 0.15s linear;
578 | }
579 |
580 | .fade.in {
581 | opacity: 1;
582 | }
583 |
584 | .collapse {
585 | display: none;
586 | }
587 |
588 | .collapse.in {
589 | display: block;
590 | }
591 |
592 | .collapsing {
593 | position: relative;
594 | height: 0;
595 | overflow: hidden;
596 | -webkit-transition: height 0.35s ease;
597 | transition: height 0.35s ease;
598 | }
599 |
600 |
601 | /*�����*/
602 |
603 | .input-group {
604 | position: relative;
605 | display: table;
606 | border-collapse: separate;
607 | }
608 |
609 | .input-group[class*="col-"] {
610 | float: none;
611 | padding-right: 0;
612 | padding-left: 0;
613 | }
614 |
615 | .input-group .form-control {
616 | width: 100%;
617 | margin-bottom: 0;
618 | }
619 |
620 | .input-group-btn,
621 | .input-group .form-control {
622 | display: table-cell;
623 | }
624 |
625 | .input-group-btn:not(:first-child):not(:last-child),
626 | .input-group .form-control:not(:first-child):not(:last-child) {
627 | border-radius: 0;
628 | }
629 |
630 | .input-group-addon,
631 | .input-group-btn {
632 | width: 1%;
633 | white-space: nowrap;
634 | vertical-align: middle;
635 | }
636 |
637 |
638 | /*�����˵�*/
639 | .nav {
640 | padding-left: 0;
641 | margin-bottom: 0;
642 | list-style: none;
643 | }
644 | .nav > li {
645 | position: relative;
646 | display: block;
647 | }
648 | .nav > li > a {
649 | position: relative;
650 | display: block;
651 | padding: 10px 15px;
652 | }
653 |
654 | .nav > li > a:hover,
655 | .nav > li > a:focus {
656 | text-decoration: none;
657 | background-color: #eeeeee;
658 | }
659 |
660 | .nav > li > a > img {
661 | max-width: none;
662 | }
663 |
664 | .navbar {
665 | position: relative;
666 | min-height: 50px;
667 | margin-bottom: 20px;
668 | border: 1px solid transparent;
669 | }
670 |
671 | @media (min-width: 768px) {
672 | .navbar {
673 | border-radius: 4px;
674 | }
675 | }
676 |
677 | .navbar-header:before,
678 | .navbar-header:after {
679 | display: table;
680 | content: " ";
681 | }
682 |
683 | .navbar-header:after {
684 | clear: both;
685 | }
686 |
687 | .navbar-header:before,
688 | .navbar-header:after {
689 | display: table;
690 | content: " ";
691 | }
692 |
693 | .navbar-header:after {
694 | clear: both;
695 | }
696 |
697 | @media (min-width: 768px) {
698 | .navbar-header {
699 | float: left;
700 | }
701 | }
702 |
703 | .navbar-collapse {
704 | max-height: 340px;
705 | padding-right: 15px;
706 | padding-left: 15px;
707 | overflow-x: visible;
708 | border-top: 1px solid transparent;
709 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
710 | -webkit-overflow-scrolling: touch;
711 | }
712 |
713 | .navbar-collapse:before,
714 | .navbar-collapse:after {
715 | display: table;
716 | content: " ";
717 | }
718 |
719 | .navbar-collapse:after {
720 | clear: both;
721 | }
722 |
723 | .navbar-collapse:before,
724 | .navbar-collapse:after {
725 | display: table;
726 | content: " ";
727 | }
728 |
729 | .navbar-collapse:after {
730 | clear: both;
731 | }
732 |
733 | .navbar-collapse.in {
734 | overflow-y: auto;
735 | }
736 |
737 | @media (min-width: 768px) {
738 | .navbar-collapse {
739 | width: auto;
740 | border-top: 0;
741 | box-shadow: none;
742 | }
743 | .navbar-collapse.collapse {
744 | display: block !important;
745 | height: auto !important;
746 | padding-bottom: 0;
747 | overflow: visible !important;
748 | }
749 | .navbar-collapse.in {
750 | overflow-y: visible;
751 | }
752 | .navbar-fixed-top .navbar-collapse,
753 | .navbar-static-top .navbar-collapse,
754 | .navbar-fixed-bottom .navbar-collapse {
755 | padding-right: 0;
756 | padding-left: 0;
757 | }
758 | }
759 |
760 | .container > .navbar-header,
761 | .container > .navbar-collapse {
762 | margin-right: -15px;
763 | margin-left: -15px;
764 | }
765 |
766 | @media (min-width: 768px) {
767 | .container > .navbar-header,
768 | .container > .navbar-collapse {
769 | margin-right: 0;
770 | margin-left: 0;
771 | }
772 | }
773 |
774 | .navbar-static-top {
775 | z-index: 1000;
776 | border-width: 0 0 1px;
777 | }
778 |
779 | @media (min-width: 768px) {
780 | .navbar-static-top {
781 | border-radius: 0;
782 | }
783 | }
784 |
785 | .navbar-fixed-top,
786 | .navbar-fixed-bottom {
787 | position: fixed;
788 | right: 0;
789 | left: 0;
790 | z-index: 1030;
791 | }
792 |
793 | @media (min-width: 768px) {
794 | .navbar-fixed-top,
795 | .navbar-fixed-bottom {
796 | border-radius: 0;
797 | }
798 | }
799 |
800 | .navbar-fixed-top {
801 | top: 0;
802 | border-width: 0 0 1px;
803 | }
804 |
805 | .navbar-fixed-bottom {
806 | bottom: 0;
807 | margin-bottom: 0;
808 | border-width: 1px 0 0;
809 | }
810 |
811 | .navbar-brand {
812 | float: left;
813 | padding: 15px 15px;
814 | font-size: 18px;
815 | line-height: 20px;
816 | }
817 |
818 | .navbar-brand {
819 | float: left;
820 | padding: 15px 15px;
821 | font-size: 18px;
822 | line-height: 20px;
823 | }
824 |
825 | .navbar-brand:hover,
826 | .navbar-brand:focus {
827 | text-decoration: none;
828 | }
829 |
830 | @media (min-width: 768px) {
831 | .navbar > .container .navbar-brand {
832 | margin-left: -15px;
833 | }
834 | }
835 |
836 | .navbar-inverse {
837 | background-color: #222222;
838 | border-color: #080808;
839 | }
840 |
841 | .navbar-inverse .navbar-brand {
842 | color: #999999;
843 | }
844 |
845 | .navbar-inverse .navbar-brand:hover,
846 | .navbar-inverse .navbar-brand:focus {
847 | color: #ffffff;
848 | background-color: transparent;
849 | }
850 |
851 | @media (min-width: 768px) {
852 | .navbar-nav {
853 | float: left;
854 | margin: 0;
855 | }
856 | .navbar-nav > li {
857 | float: left;
858 | }
859 | .navbar-nav > li > a {
860 | padding-top: 15px;
861 | padding-bottom: 15px;
862 | }
863 | .navbar-nav.navbar-right:last-child {
864 | margin-right: -15px;
865 | }
866 | }
867 |
868 | .navbar-inverse {
869 | border-radius: 0;
870 | background: rgba(0, 0, 0, .9);
871 | border-bottom: 1px solid rgba(255, 255, 255, 0.15);
872 | min-height: 100px;
873 | padding-top: 25px;
874 | margin-bottom: 0;
875 | }
876 |
877 | .navbar-inverse .navbar-brand {
878 | font-size: 24px;
879 | color: white;
880 | padding: 0 0 0 15px;
881 | margin: 12px 0 0 0;
882 | }
883 |
884 | .navbar-inverse .navbar-brand img {
885 | margin-top: -8px;
886 | }
887 |
888 | .navbar-inverse .navbar-nav > li > a,
889 | .navbar-inverse .navbar-nav > .open ul > a {
890 | color: rgba(255, 255, 255, .4);
891 | }
892 |
893 | .navbar-inverse .navbar-nav > .active > a,
894 | .navbar-inverse .navbar-nav > .active > a:hover,
895 | .navbar-inverse .navbar-nav > .active > a:focus {
896 | color: #fff;
897 | background: none;
898 | box-shadow: none;
899 | }
900 |
901 | .navbar-inverse .navbar-nav > .open > a {
902 | background: none;
903 | color: white;
904 | box-shadow: none;
905 | }
906 |
907 | .navbar-inverse .navbar-nav > li > a:hover,
908 | .navbar-inverse .navbar-nav > li > a:focus,
909 | .navbar-inverse .navbar-nav > .open > a:hover,
910 | .navbar-inverse .navbar-nav > .open > a:focus {
911 | background: none;
912 | color: white;
913 | }
914 |
915 | .jumbotron {
916 | padding: 50px;
917 | margin-bottom: 30px;
918 | font-size: 21px;
919 | font-weight: 200;
920 | line-height: 2.1428571435;
921 | color: inherit;
922 | background-color: #FFFFFF;
923 | }
924 |
925 | .jumbotron h1,
926 | .jumbotron .h1 {
927 | line-height: 1;
928 | color: inherit;
929 | }
930 |
931 | .jumbotron p {
932 | line-height: 1.4;
933 | }
934 |
935 | .container .jumbotron {
936 | border-radius: 6px;
937 | }
938 |
939 | .jumbotron .container {
940 | max-width: 100%;
941 | }
942 |
943 | /*������*/
944 |
945 | .progress {
946 | height: 20px;
947 | margin-bottom: 20px;
948 | overflow: hidden;
949 | background-color: #f5f5f5;
950 | border-radius: 4px;
951 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
952 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
953 | }
954 |
955 | .progress-bar {
956 | float: left;
957 | width: 0;
958 | height: 100%;
959 | font-size: 12px;
960 | line-height: 20px;
961 | color: #ffffff;
962 | text-align: center;
963 | background-color: #428bca;
964 | -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
965 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
966 | -webkit-transition: width 0.6s ease;
967 | transition: width 0.6s ease;
968 | }
969 |
970 | .progress-striped .progress-bar {
971 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
972 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
973 | background-size: 40px 40px;
974 | }
975 |
976 | .progress.active .progress-bar {
977 | -webkit-animation: progress-bar-stripes 2s linear infinite;
978 | animation: progress-bar-stripes 2s linear infinite;
979 | }
980 |
981 | .progress-bar-success {
982 | background-color: #5cb85c;
983 | }
984 |
985 | .progress-striped .progress-bar-success {
986 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
987 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
988 | }
989 |
990 | .progress-bar-info {
991 | background-color: #5bc0de;
992 | }
993 |
994 | .progress-striped .progress-bar-info {
995 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
996 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
997 | }
998 |
999 | .progress-bar-warning {
1000 | background-color: #f0ad4e;
1001 | }
1002 |
1003 | .progress-striped .progress-bar-warning {
1004 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
1005 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
1006 | }
1007 |
1008 | .progress-bar-danger {
1009 | background-color: #d9534f;
1010 | }
1011 |
1012 | .progress-striped .progress-bar-danger {
1013 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
1014 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
1015 | }
1016 |
1017 | .pull-right {
1018 | float: right !important;
1019 | }
1020 | .pull-left {
1021 | float: left !important;
1022 | }
1023 | .hide {
1024 | display: none !important;
1025 | }
1026 | .show {
1027 | display: block !important;
1028 | }
1029 |
1030 | div.progress {
1031 | display: block;
1032 | height: 22px;
1033 | padding: 0;
1034 | min-width: 200px;
1035 | margin:4px 0;
1036 | background-color: #DEDEDE;
1037 | background: -moz-linear-gradient(top, #ccc, #e9e9e9);
1038 | background: -webkit-gradient(linear, left top, bottom, #ccc, #e9e9e9);
1039 | }
1040 | div.progress, div.progress span {
1041 | -moz-border-radius: 4px;
1042 | -webkit-border-radius: 4px;
1043 | border-radius: 4px;
1044 | }
1045 | div.progress span {
1046 | display: block;
1047 | /*margin-top: -10px;*/
1048 | padding: 0;
1049 | text-align:center;
1050 | /*width:0;
1051 | -moz-box-shadow:1px 0 1px rgba(0, 0, 0, 0.2);
1052 | -webkit-box-shadow:1px 0 1px rgba(0, 0, 0, 0.2);*/
1053 | box-shadow:1px 0 1px rgba(0, 0, 0, 0.2);
1054 | }
1055 | div.progress span b{
1056 | color:#8e2121;
1057 | line-height:22px;
1058 | padding-left:0;
1059 | font-size:18px;
1060 | text-shadow:0 1px 1px rgba(0, 0, 0, 0.5);
1061 | }
1062 | /*-Xia La*/
1063 | .navbar-toggle {
1064 | position: relative;
1065 | float: right;
1066 | padding: 9px 10px;
1067 | margin-top: 8px;
1068 | margin-right: 15px;
1069 | margin-bottom: 8px;
1070 | background-color: transparent;
1071 | background-image: none;
1072 | border: 1px solid transparent;
1073 | border-radius: 4px;
1074 | }
1075 |
1076 | .navbar-toggle .icon-bar {
1077 | display: block;
1078 | width: 22px;
1079 | height: 2px;
1080 | border-radius: 1px;
1081 | }
1082 |
1083 | .navbar-toggle .icon-bar + .icon-bar {
1084 | margin-top: 4px;
1085 | }
1086 |
1087 | @media (min-width: 768px) {
1088 | .navbar-toggle {
1089 | display: none;
1090 | }
1091 | }
1092 | .navbar-default .navbar-toggle .icon-bar {
1093 | background-color: #cccccc;
1094 | }
1095 | .navbar-inverse .navbar-toggle .icon-bar {
1096 | background-color: #ffffff;
1097 | }
--------------------------------------------------------------------------------
/css/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/css/favicon.ico
--------------------------------------------------------------------------------
/css/isdc-theme.css:
--------------------------------------------------------------------------------
1 |
2 | /* Header */
3 |
4 | #head {
5 | background: #181015 no-repeat;
6 | background-size: cover;
7 | min-height: 520px;
8 | text-align: center;
9 | padding-top: 240px;
10 | color: white;
11 | font-family: "Open sans", Helvetica, Arial;
12 | font-weight: 300;
13 | }
14 |
15 | #head.secondary {
16 | height: 100px;
17 | min-height: 100px;
18 | padding-top: 0px;
19 | }
20 |
21 | #head .lead {
22 | font-family: "Open sans", Helvetica, Arial;
23 | font-size: 44px;
24 | margin-bottom: 6px;
25 | color: white;
26 | line-height: 1.15em;
27 | }
28 |
29 | #head .tagline {
30 | color: rgba(255, 255, 255, 0.75);
31 | margin-bottom: 25px;
32 | }
33 |
34 | #head .tagline a {
35 | color: #fff;
36 | }
37 |
38 | #head .btn {
39 | margin-bottom: 10px;
40 | }
41 |
42 | #head .btn-default {
43 | text-shadow: none;
44 | background: transparent;
45 | color: rgba(255, 255, 255, .5);
46 | -webkit-box-shadow: inset 0px 0px 0px 3px rgba(255, 255, 255, .5);
47 | -moz-box-shadow: inset 0px 0px 0px 3px rgba(255, 255, 255, .5);
48 | box-shadow: inset 0px 0px 0px 3px rgba(255, 255, 255, .5);
49 | background: transparent;
50 | }
51 |
52 | #head .btn-default:hover,
53 | #head .btn-default:focus {
54 | color: rgba(255, 255, 255, .8);
55 | -webkit-box-shadow: inset 0px 0px 0px 3px rgba(255, 255, 255, .8);
56 | -moz-box-shadow: inset 0px 0px 0px 3px rgba(255, 255, 255, .8);
57 | box-shadow: inset 0px 0px 0px 3px rgba(255, 255, 255, .8);
58 | background: transparent;
59 | }
60 |
61 | #head .btn-default:active,
62 | #head .btn-default.active {
63 | color: #fff;
64 | -webkit-box-shadow: inset 0px 0px 0px 3px #fff;
65 | -moz-box-shadow: inset 0px 0px 0px 3px #fff;
66 | box-shadow: inset 0px 0px 0px 3px #fff;
67 | background: transparent;
68 | }
69 |
70 | @media (max-width: 767px) {
71 | #head {
72 | min-height: 420px;
73 | padding-top: 160px;
74 | }
75 | #head .lead {
76 | font-size: 34px;
77 | }
78 | }
79 | /* Autohide navbar */
80 |
81 | .slideUp {
82 | top: -100px;
83 | }
84 |
85 | .headroom {
86 | -webkit-transition: all 0.4s ease-out;
87 | -moz-transition: all 0.4s ease-out;
88 | -o-transition: all 0.4s ease-out;
89 | transition: all 0.4s ease-out;
90 | }
91 | /* Highlights (in jumbotron in most cases) */
92 |
93 | .highlight {
94 | margin-top: 40px;
95 | }
96 |
97 | .h-caption {
98 | text-align: center;
99 | }
100 |
101 | .h-caption i {
102 | display: block;
103 | font-size: 54px;
104 | color: #382526;
105 | margin-bottom: 36px;
106 | }
107 |
108 | .h-caption h4 {
109 | color: #382526;
110 | font-size: 16px;
111 | font-weight: bold;
112 | margin-bottom: 20px;
113 | }
114 |
115 | .h-body {}
116 | /* Typography */
117 |
118 | h1,
119 | h2,
120 | h3,
121 | h4,
122 | h5,
123 | h6 {
124 | font-family: "Open sans", Helvetica, Arial;
125 | }
126 |
127 | h1,
128 | .h1,
129 | h2,
130 | .h2,
131 | h3,
132 | .h3 {
133 | margin-top: 30px;
134 | }
135 |
136 | blockquote {
137 | font-style: italic;
138 | font-family: Georgia;
139 | color: #999;
140 | margin: 30px 0 30px;
141 | }
142 |
143 | label {
144 | color: #777;
145 | }
146 |
147 | .thin {
148 | font-weight: 300;
149 | }
150 |
151 | .page-title {
152 | margin-top: 20px;
153 | font-weight: 300;
154 | }
155 |
156 | .text-muted {
157 | color: #888;
158 | }
159 |
160 | .breadcrumb {
161 | background: none;
162 | padding: 0;
163 | margin: 30px 0 0px 0;
164 | }
165 |
166 | ul.list-spaces li {
167 | margin-bottom: 10px;
168 | }
169 | /* Helpers */
170 |
171 | .container-full {
172 | margin: 0 auto;
173 | width: 100%;
174 | }
175 |
176 | .top-space {
177 | margin-top: 60px;
178 | }
179 |
180 | .top-margin {
181 | margin-top: 20px;
182 | }
183 |
184 | img {
185 | max-width: 100%;
186 | }
187 |
188 | img.pull-right {
189 | margin-left: 10px;
190 | }
191 |
192 | img.pull-left {
193 | margin-right: 10px;
194 | }
195 |
196 | #map {
197 | width: 100%;
198 | height: 280px;
199 | }
200 |
201 | #social {
202 | margin-top: 50px;
203 | margin-bottom: 50px;
204 | }
205 |
206 | #social .wrapper {
207 | width: 340px;
208 | margin: 0 auto;
209 | }
210 | /* Main content block */
211 |
212 | .maincontent {}
213 | /* Footer */
214 |
215 | .footer1 {
216 | background: #232323;
217 | padding: 30px 0 0 0;
218 | font-size: 12px;
219 | color: #999;
220 | }
221 |
222 | .footer1 a {
223 | color: #ccc;
224 | }
225 |
226 | .footer1 a:hover {
227 | color: #fff;
228 | }
229 |
230 | .footer1 .widget {
231 | margin-bottom: 30px;
232 | }
233 |
234 | .footer1 .widget-title {
235 | font-size: 17px;
236 | font-weight: bold;
237 | color: #ccc;
238 | margin: 0 0 20px;
239 | }
240 |
241 | .footer1 .entry-meta {
242 | border-top: 1px solid #ccc;
243 | border-bottom: 1px solid #ccc;
244 | margin: 0 0 35px 0;
245 | padding: 2px 0;
246 | color: #888888;
247 | font-size: 12px;
248 | font-size: 0.75rem;
249 | }
250 |
251 | .footer1 .entry-meta a {
252 | color: #333333;
253 | }
254 |
255 | .footer1 .entry-meta .meta-in {
256 | border-top: 1px solid #ccc;
257 | border-bottom: 1px solid #ccc;
258 | padding: 10px 0;
259 | }
260 |
261 | .follow-me-icons {
262 | font-size: 30px;
263 | }
264 |
265 | .follow-me-icons i {
266 | float: left;
267 | margin: 0 10px 0 0;
268 | }
269 |
270 | .footer2 {
271 | background: rgba(0, 0, 0, .7);
272 | padding: 15px 0;
273 | color: #fff;
274 | font-size: 15px;
275 | }
276 |
277 | .footer2 a {
278 | color: #aaa;
279 | }
280 |
281 | .footer2 a:hover {
282 | color: #fff;
283 | }
284 |
285 | .footer2 p {
286 | margin: 0;
287 | }
288 |
289 | .widget-simplenav {
290 | margin-left: -5px;
291 | }
292 |
293 | .widget-simplenav a {
294 | margin: 0 5px;
295 | }
296 | /* Markdown */
297 |
298 | .markdown body {
299 | font-family: Helvetica, arial, sans-serif;
300 | font-size: 14px;
301 | line-height: 1.6;
302 | padding-top: 10px;
303 | padding-bottom: 10px;
304 | background-color: white;
305 | padding: 30px;
306 | }
307 |
308 | .markdown body > *:first-child {
309 | margin-top: 0 !important;
310 | }
311 |
312 | .markdown body > *:last-child {
313 | margin-bottom: 0 !important;
314 | }
315 |
316 | .markdown a {
317 | color: #4183C4;
318 | }
319 |
320 | .markdown a.absent {
321 | color: #cc0000;
322 | }
323 |
324 | .markdown a.anchor {
325 | display: block;
326 | padding-left: 30px;
327 | margin-left: -30px;
328 | cursor: pointer;
329 | position: absolute;
330 | top: 0;
331 | left: 0;
332 | bottom: 0;
333 | }
334 |
335 | .markdown h1,
336 | .markdown h2,
337 | .markdown h3,
338 | .markdown h4,
339 | .markdown h5,
340 | .markdown h6 {
341 | margin: 20px 0 10px;
342 | padding: 0;
343 | font-weight: bold;
344 | -webkit-font-smoothing: antialiased;
345 | cursor: text;
346 | position: relative;
347 | }
348 |
349 | .markdown h1:hover a.anchor,
350 | .markdown h2:hover a.anchor,
351 | .markdown h3:hover a.anchor,
352 | .markdown h4:hover a.anchor,
353 | .markdown h5:hover a.anchor,
354 | .markdown h6:hover a.anchor {
355 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==) no-repeat 10px center;
356 | text-decoration: none;
357 | }
358 |
359 | .markdown h1 tt,
360 | .markdown h1 code {
361 | font-size: inherit;
362 | }
363 |
364 | .markdown h2 tt,
365 | .markdown h2 code {
366 | font-size: inherit;
367 | }
368 |
369 | .markdown h3 tt,
370 | .markdown h3 code {
371 | font-size: inherit;
372 | }
373 |
374 | .markdown h4 tt,
375 | .markdown h4 code {
376 | font-size: inherit;
377 | }
378 |
379 | .markdown h5 tt,
380 | .markdown h5 code {
381 | font-size: inherit;
382 | }
383 |
384 | .markdown h6 tt,
385 | .markdown h6 code {
386 | font-size: inherit;
387 | }
388 |
389 | .markdown h1 {
390 | font-size: 28px;
391 | color: black;
392 | }
393 |
394 | .markdown h2 {
395 | font-size: 24px;
396 | color: black;
397 | }
398 |
399 | .markdown h3 {
400 | font-size: 18px;
401 | }
402 |
403 | .markdown h4 {
404 | font-size: 16px;
405 | }
406 |
407 | .markdown h5 {
408 | font-size: 14px;
409 | }
410 |
411 | .markdown h6 {
412 | color: #777777;
413 | font-size: 14px;
414 | }
415 |
416 | .markdown p,
417 | .markdown blockquote,
418 | .markdown ul,
419 | .markdown ol,
420 | .markdown dl,
421 | .markdown li,
422 | .markdown table,
423 | .markdown pre {
424 | margin: 15px 0;
425 | }
426 |
427 | .markdown hr {
428 | background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;
429 | border: 0 none;
430 | color: #cccccc;
431 | height: 4px;
432 | padding: 0;
433 | }
434 |
435 | .markdown body > h2:first-child {
436 | margin-top: 0;
437 | padding-top: 0;
438 | }
439 |
440 | .markdown body > h1:first-child {
441 | margin-top: 0;
442 | padding-top: 0;
443 | }
444 |
445 | .markdown body > h1:first-child + h2 {
446 | margin-top: 0;
447 | padding-top: 0;
448 | }
449 |
450 | .markdown body > h3:first-child,
451 | .markdown body > h4:first-child,
452 | .markdown body > h5:first-child,
453 | .markdown body > h6:first-child {
454 | margin-top: 0;
455 | padding-top: 0;
456 | }
457 |
458 | .markdown a:first-child h1,
459 | .markdown a:first-child h2,
460 | .markdown a:first-child h3,
461 | .markdown a:first-child h4,
462 | .markdown a:first-child h5,
463 | .markdown a:first-child h6 {
464 | margin-top: 0;
465 | padding-top: 0;
466 | }
467 |
468 | .markdown h1 p,
469 | .markdown h2 p,
470 | .markdown h3 p,
471 | .markdown h4 p,
472 | .markdown h5 p,
473 | .markdown h6 p {
474 | margin-top: 0;
475 | }
476 |
477 | .markdown li p.first {
478 | display: inline-block;
479 | }
480 |
481 | .markdown li {
482 | margin: 0;
483 | }
484 |
485 | .markdown ul,
486 | .markdown ol {
487 | padding-left: 30px;
488 | }
489 |
490 | .markdown ul:first-child,
491 | .markdown ol:first-child {
492 | margin-top: 0;
493 | }
494 |
495 | .markdown dl {
496 | padding: 0;
497 | }
498 |
499 | .markdown dl dt {
500 | font-size: 14px;
501 | font-weight: bold;
502 | font-style: italic;
503 | padding: 0;
504 | margin: 15px 0 5px;
505 | }
506 |
507 | .markdown dl dt:first-child {
508 | padding: 0;
509 | }
510 |
511 | .markdown dl dt >:first-child {
512 | margin-top: 0;
513 | }
514 |
515 | .markdown dl dt >:last-child {
516 | margin-bottom: 0;
517 | }
518 |
519 | .markdown dl dd {
520 | margin: 0 0 15px;
521 | padding: 0 15px;
522 | }
523 |
524 | .markdown dl dd >:first-child {
525 | margin-top: 0;
526 | }
527 |
528 | .markdown dl dd >:last-child {
529 | margin-bottom: 0;
530 | }
531 |
532 | .markdown blockquote {
533 | border-left: 4px solid #dddddd;
534 | padding: 0 15px;
535 | color: #777777;
536 | }
537 |
538 | .markdown blockquote >:first-child {
539 | margin-top: 0;
540 | }
541 |
542 | .markdown blockquote >:last-child {
543 | margin-bottom: 0;
544 | }
545 |
546 | .markdown table {
547 | padding: 0;
548 | border-collapse: collapse;
549 | }
550 |
551 | .markdown table tr {
552 | border-top: 1px solid #cccccc;
553 | background-color: white;
554 | margin: 0;
555 | padding: 0;
556 | }
557 |
558 | .markdown table tr:nth-child(2n) {
559 | background-color: #f8f8f8;
560 | }
561 |
562 | .markdown table tr th {
563 | font-weight: bold;
564 | border: 1px solid #cccccc;
565 | margin: 0;
566 | padding: 6px 13px;
567 | }
568 |
569 | .markdown table tr td {
570 | border: 1px solid #cccccc;
571 | margin: 0;
572 | padding: 6px 13px;
573 | }
574 |
575 | .markdown table tr th:first-child,
576 | .markdown table tr td:first-child {
577 | margin-top: 0;
578 | }
579 |
580 | .markdown table tr th:last-child,
581 | .markdown table tr td:last-child {
582 | margin-bottom: 0;
583 | }
584 |
585 | .markdown img {
586 | max-width: 100%;
587 | }
588 |
589 | .markdown span.frame {
590 | display: block;
591 | overflow: hidden;
592 | }
593 |
594 | .markdown span.frame > span {
595 | border: 1px solid #dddddd;
596 | display: block;
597 | float: left;
598 | overflow: hidden;
599 | margin: 13px 0 0;
600 | padding: 7px;
601 | width: auto;
602 | }
603 |
604 | .markdown span.frame span img {
605 | display: block;
606 | float: left;
607 | }
608 |
609 | .markdown span.frame span span {
610 | clear: both;
611 | color: #333333;
612 | display: block;
613 | padding: 5px 0 0;
614 | }
615 |
616 | .markdown span.align-center {
617 | display: block;
618 | overflow: hidden;
619 | clear: both;
620 | }
621 |
622 | .markdown span.align-center > span {
623 | display: block;
624 | overflow: hidden;
625 | margin: 13px auto 0;
626 | text-align: center;
627 | }
628 |
629 | .markdown span.align-center span img {
630 | margin: 0 auto;
631 | text-align: center;
632 | }
633 |
634 | .markdown span.align-right {
635 | display: block;
636 | overflow: hidden;
637 | clear: both;
638 | }
639 |
640 | .markdown span.align-right > span {
641 | display: block;
642 | overflow: hidden;
643 | margin: 13px 0 0;
644 | text-align: right;
645 | }
646 |
647 | .markdown span.align-right span img {
648 | margin: 0;
649 | text-align: right;
650 | }
651 |
652 | .markdown span.float-left {
653 | display: block;
654 | margin-right: 13px;
655 | overflow: hidden;
656 | float: left;
657 | }
658 |
659 | .markdown span.float-left span {
660 | margin: 13px 0 0;
661 | }
662 |
663 | .markdown span.float-right {
664 | display: block;
665 | margin-left: 13px;
666 | overflow: hidden;
667 | float: right;
668 | }
669 |
670 | .markdown span.float-right > span {
671 | display: block;
672 | overflow: hidden;
673 | margin: 13px auto 0;
674 | text-align: right;
675 | }
676 |
677 | .markdown code,
678 | .markdown tt {
679 | margin: 0 2px;
680 | padding: 2px 4px;
681 | color: #008B45;
682 | white-space: nowrap;
683 | border: 1px solid #eaeaea;
684 | background-color: #f8f8f8;
685 | border-radius: 3px;
686 | }
687 |
688 | .markdown pre code {
689 | margin: 0;
690 | padding: 0;
691 | white-space: pre;
692 | border: none;
693 | background: transparent;
694 | }
695 |
696 | .markdown .highlight pre {
697 | background-color: #f8f8f8;
698 | border: 1px solid #cccccc;
699 | font-size: 13px;
700 | line-height: 19px;
701 | overflow: auto;
702 | padding: 6px 10px;
703 | border-radius: 3px;
704 | }
705 |
706 | .markdown pre {
707 | background-color: #f8f8f8;
708 | border: 1px solid #cccccc;
709 | font-size: 13px;
710 | line-height: 19px;
711 | overflow: auto;
712 | padding: 6px 10px;
713 | border-radius: 3px;
714 | }
715 |
716 | .markdown pre code,
717 | .markdown pre tt {
718 | background-color: transparent;
719 | border: none;
720 | }
721 |
722 | .markdown sup {
723 | font-size: 0.83em;
724 | vertical-align: super;
725 | line-height: 0;
726 | }
727 |
728 | .markdown * {
729 | -webkit-print-color-adjust: exact;
730 | }
731 | /**/
732 |
733 | .jumbotron {
734 | color: inherit;
735 |
736 | padding-top: 70px;
737 | padding-bottom: 30px;
738 | margin-bottom: 0;
739 | }
740 |
741 | .container .jumbotron {
742 | -webkit-border-radius: 3px;
743 | -moz-border-radius: 3px;
744 | border-radius: 3px;
745 | padding-left: 40px;
746 | padding-right: 40px;
747 | }
748 |
749 | .jumbotron p {
750 | font-size: inherit;
751 | }
752 |
753 | .jumbotron h2,
754 | .jumbotron h3,
755 | .jumbotron h4,
756 | .jumbotron h5,
757 | .jumbotron h6 {
758 | line-height: 1.3em;
759 | }
760 |
761 | .img-rounded {
762 | -webkit-border-radius: 3px;
763 | -moz-border-radius: 3px;
764 | border-radius: 3px;
765 | }
--------------------------------------------------------------------------------
/css/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/css/logo.png
--------------------------------------------------------------------------------
/data.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat MySQL Data Transfer
3 |
4 | Date: 2019
5 | */
6 |
7 | SET FOREIGN_KEY_CHECKS=0;
8 |
9 | -- ----------------------------
10 | -- Table structure for `data`
11 | -- ----------------------------
12 | DROP TABLE IF EXISTS `data`;
13 | CREATE TABLE `data` (
14 | `id` int(30) unsigned NOT NULL AUTO_INCREMENT,
15 | `username` char(48) NOT NULL DEFAULT '',
16 | `password` char(64) NOT NULL DEFAULT '',
17 | `salt` char(48) DEFAULT '',
18 | `email` char(48) DEFAULT '',
19 | `order` char(20) DEFAULT '未知' COMMENT '来源',
20 | PRIMARY KEY (`id`),
21 | KEY `email` (`email`(20))
22 | ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=gbk;
23 |
24 | -- ----------------------------
25 | -- Records of data
26 | -- ----------------------------
27 | INSERT INTO `data` VALUES ('1', 'admin', 'admin888', '', 'admin@qq.com', 'test');
28 | INSERT INTO `data` VALUES ('2', '123456', '123456', '', '123456@qq.com', 'test');
29 | INSERT INTO `data` VALUES ('3', '111111', '111111', '', '111111@qq.com', 'test');
30 | INSERT INTO `data` VALUES ('4', '222222', '222222', '', '222222@qq.com', 'test');
31 | INSERT INTO `data` VALUES ('5', '333333', '333333', '', '333333@qq.com', 'test');
32 | INSERT INTO `data` VALUES ('6', '444444', '444444', '', '444444@qq.com', 'test');
33 | INSERT INTO `data` VALUES ('7', '555555', '555555', '', '555555@qq.com', 'test');
34 | INSERT INTO `data` VALUES ('8', '666666', '666666', '', '666666@qq.com', 'test');
35 | INSERT INTO `data` VALUES ('9', '777777', '777777', '', '777777@qq.com', 'test');
36 | INSERT INTO `data` VALUES ('10', '888888', '888888', '', '888888@qq.com', 'test');
37 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | SetServer('127.0.0.1', 9321);
18 | $cl->SetConnectTimeout(3);
19 | $cl->SetArrayResult(true);
20 | // 设置是否全文匹配
21 | if (!empty($_GET) && !empty($_GET['f'])) {
22 | $cl->SetMatchMode(SPH_MATCH_ALL);
23 | } else {
24 | $cl->SetMatchMode(SPH_MATCH_ANY);
25 | }
26 | if (!empty($_GET) && !empty($_GET['p'])) {
27 | $p = !intval(trim($_GET['p'])) == 0 ? intval(trim($_GET['p'])) - 1 : 0;
28 | $p = $p * 20;
29 | // 我在sed.conf 设置了最大返回结果数1000。但是我在生成页码的时候最多生成20页,我想能满足大部分搜索需求了。
30 | // 以下语句表示从P参数偏移开始每次返回20条。
31 | $cl->setLimits($p, 20);
32 | } else {
33 | $cl->setLimits(0, 20);
34 | }
35 | $res = $cl->Query(".$Keywords.", "*");
36 | @mysql_connect("127.0.0.1:3306", "root", "root123456"); //数据库账号密码
37 | mysql_select_db("sgkdata"); //数据库库名名
38 |
39 | if (is_array($res["matches"])) {
40 | foreach ($res["matches"] as $docinfo) {
41 | $ids = $ids . $docinfo[id] . ',';
42 | }
43 | $ids = rtrim($ids, ',');
44 | $sql = "select * from data where id in($ids)"; //注意修改表名
45 | mysql_query("set names utf8");
46 | $ret = mysql_query($sql);
47 | $num = mysql_num_rows($ret);
48 | }
49 |
50 | }
51 | ?>
52 |
53 |
54 |
55 | FindPass
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
82 |
87 |
88 |
89 |
90 |
91 |
92 |
本站现有数据量为 条~
93 |
输入你要搜索的内容:
94 |
105 |
找到与  {$Keywords}  相关的结果 {$res[total_found]} 个。用时 {$res[time]} 秒。
108 | ";
109 | echo "
110 |
111 |
112 | 用户名/账号 |
113 | 密码/密文 |
114 | Salt/Tel/QQ |
115 | 邮箱 |
116 | 来源 |
117 | ";
118 | while ($row = mysql_fetch_assoc($ret)) {
119 | echo "
" . $row['username'] . " | ";
120 | echo "" . $row['password'] . " | ";
121 | echo "" . $row['salt'] . " | ";
122 | echo "" . $row['email'] . " | ";
123 | echo "" . $row['order'] . " |
";
124 | }
125 | echo "
126 |
127 |
128 |
129 |
";
130 | } else {
131 | if (!empty($_GET) && !empty($_GET['q'])) {
132 | echo "
找不到与  {$Keywords}  相关的结果。请更换其他关键词试试。
";
133 | }
134 | }
135 | ?>
136 |
137 | 20) {
144 | $pagecount = 20;
145 | }
146 | $highlightid = !intval(trim($_GET['p'])) == 0 ? intval(trim($_GET['p'])) : 1;
147 | echo "
";
148 | for ($i = 1; $i <= $pagecount; $i++) {
149 | if ($highlightid == $i) {
150 | echo "";
151 | } else {
152 | echo "- {$i}
";
153 | }
154 | }
155 | echo "
";
156 | echo "
";
157 | }
158 | ?>
159 |
申明:
160 | 数据来自互联网,旨在找回遗忘密码,或对已泄露密码进行修改防范,请勿非法使用,否则一切后果自负。
161 |
162 |
163 |
164 |
165 |
166 |
167 |
178 |
179 |
--------------------------------------------------------------------------------
/sphinx/bin/1cmd.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/1cmd.bat
--------------------------------------------------------------------------------
/sphinx/bin/1start.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/1start.bat
--------------------------------------------------------------------------------
/sphinx/bin/1索引.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/1索引.bat
--------------------------------------------------------------------------------
/sphinx/bin/indexer.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/indexer.exe
--------------------------------------------------------------------------------
/sphinx/bin/indextool.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/indextool.exe
--------------------------------------------------------------------------------
/sphinx/bin/libeay32.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/libeay32.dll
--------------------------------------------------------------------------------
/sphinx/bin/libiconv-2.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/libiconv-2.dll
--------------------------------------------------------------------------------
/sphinx/bin/libintl-8.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/libintl-8.dll
--------------------------------------------------------------------------------
/sphinx/bin/libmariadb.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/libmariadb.dll
--------------------------------------------------------------------------------
/sphinx/bin/libpq.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/libpq.dll
--------------------------------------------------------------------------------
/sphinx/bin/msvcr120.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/msvcr120.dll
--------------------------------------------------------------------------------
/sphinx/bin/searchd.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/searchd.exe
--------------------------------------------------------------------------------
/sphinx/bin/sphinx.conf:
--------------------------------------------------------------------------------
1 | #
2 | # Minimal Sphinx configuration sample (clean, simple, functional)
3 | #
4 |
5 | source sgk
6 | {
7 | type = mysql
8 | sql_host = 127.0.0.1
9 | sql_user = root
10 | sql_pass = root123456
11 | sql_db = sgkdata
12 | sql_port = 3306
13 | sql_query = SELECT `id`,`username`,`password`,`email`,`salt`,`order` FROM data
14 | sql_query_info = SELECT * FROM data WHERE id=$id
15 | }
16 |
17 | index sgk
18 | {
19 | ondisk_dict = 1
20 | source = sgk
21 | path = D:/sgk/sphinx/var/data/find
22 | docinfo = extern
23 | #chinese_dictionary = D:/sgk/sphinx/etc/
24 | charset_type = zh_cn.utf-8
25 | #ngram_len = 1
26 | #ngram_chars = U+3000..U+2FA1F
27 | }
28 |
29 | indexer
30 | {
31 | mem_limit = 1024M
32 | }
33 |
34 | searchd
35 | {
36 | listen = 9321
37 | log = D:/sgk/sphinx/var/log/searchd.log
38 | query_log = D:/sgk/sphinx/var/log/query.log
39 | read_timeout = 5
40 | max_children = 30
41 | pid_file = D:/sgk/sphinx/var/log/searchd.pid
42 | max_matches = 1000
43 | seamless_rotate = 1
44 | preopen_indexes = 1
45 | unlink_old = 1
46 | workers = threads # for RT to work
47 | binlog_path =
48 | ondisk_dict_default = 1
49 | }
--------------------------------------------------------------------------------
/sphinx/bin/ssleay32.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/ssleay32.dll
--------------------------------------------------------------------------------
/sphinx/bin/wordbreaker.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/bin/wordbreaker.exe
--------------------------------------------------------------------------------
/sphinx/etc/example.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS test.documents;
2 | CREATE TABLE test.documents
3 | (
4 | id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
5 | group_id INTEGER NOT NULL,
6 | group_id2 INTEGER NOT NULL,
7 | date_added DATETIME NOT NULL,
8 | title VARCHAR(255) NOT NULL,
9 | content TEXT NOT NULL
10 | );
11 |
12 | REPLACE INTO test.documents ( id, group_id, group_id2, date_added, title, content ) VALUES
13 | ( 1, 1, 5, NOW(), 'test one', 'this is my test document number one. also checking search within phrases.' ),
14 | ( 2, 1, 6, NOW(), 'test two', 'this is my test document number two' ),
15 | ( 3, 2, 7, NOW(), 'another doc', 'this is another group' ),
16 | ( 4, 2, 8, NOW(), 'doc number four', 'this is to test groups' );
17 |
18 | DROP TABLE IF EXISTS test.tags;
19 | CREATE TABLE test.tags
20 | (
21 | docid INTEGER NOT NULL,
22 | tagid INTEGER NOT NULL,
23 | UNIQUE(docid,tagid)
24 | );
25 |
26 | INSERT INTO test.tags VALUES
27 | (1,1), (1,3), (1,5), (1,7),
28 | (2,6), (2,4), (2,2),
29 | (3,15),
30 | (4,7), (4,40);
31 |
--------------------------------------------------------------------------------
/sphinx/etc/sphinx-min.conf.dist:
--------------------------------------------------------------------------------
1 | #
2 | # Minimal Sphinx configuration sample (clean, simple, functional)
3 | #
4 |
5 | source src1
6 | {
7 | type = mysql
8 |
9 | sql_host = localhost
10 | sql_user = test
11 | sql_pass =
12 | sql_db = test
13 | sql_port = 3306 # optional, default is 3306
14 |
15 | sql_query = \
16 | SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content \
17 | FROM documents
18 |
19 | sql_attr_uint = group_id
20 | sql_attr_timestamp = date_added
21 | }
22 |
23 |
24 | index test1
25 | {
26 | source = src1
27 | path = /var/data/test1
28 | }
29 |
30 |
31 | index testrt
32 | {
33 | type = rt
34 | rt_mem_limit = 128M
35 |
36 | path = /var/data/testrt
37 |
38 | rt_field = title
39 | rt_field = content
40 | rt_attr_uint = gid
41 | }
42 |
43 |
44 | indexer
45 | {
46 | mem_limit = 128M
47 | }
48 |
49 |
50 | searchd
51 | {
52 | listen = 9312
53 | listen = 9306:mysql41
54 | log = /var/log/searchd.log
55 | query_log = /var/log/query.log
56 | read_timeout = 5
57 | max_children = 30
58 | pid_file = /var/log/searchd.pid
59 | seamless_rotate = 1
60 | preopen_indexes = 1
61 | unlink_old = 1
62 | workers = threads # for RT to work
63 | binlog_path = /var/data
64 | }
65 |
--------------------------------------------------------------------------------
/sphinx/etc/sphinx.conf.dist:
--------------------------------------------------------------------------------
1 | #
2 | # Sphinx configuration file sample
3 | #
4 | # WARNING! While this sample file mentions all available options,
5 | # it contains (very) short helper descriptions only. Please refer to
6 | # doc/sphinx.html for details.
7 | #
8 |
9 | #############################################################################
10 | ## data source definition
11 | #############################################################################
12 |
13 | source src1
14 | {
15 | # data source type. mandatory, no default value
16 | # known types are mysql, pgsql, mssql, xmlpipe, xmlpipe2, odbc
17 | type = mysql
18 |
19 | #####################################################################
20 | ## SQL settings (for 'mysql' and 'pgsql' types)
21 | #####################################################################
22 |
23 | # some straightforward parameters for SQL source types
24 | sql_host = localhost
25 | sql_user = test
26 | sql_pass =
27 | sql_db = test
28 | sql_port = 3306 # optional, default is 3306
29 |
30 | # UNIX socket name
31 | # optional, default is empty (reuse client library defaults)
32 | # usually '/var/lib/mysql/mysql.sock' on Linux
33 | # usually '/tmp/mysql.sock' on FreeBSD
34 | #
35 | # sql_sock = /tmp/mysql.sock
36 |
37 |
38 | # MySQL specific client connection flags
39 | # optional, default is 0
40 | #
41 | # mysql_connect_flags = 32 # enable compression
42 |
43 | # MySQL specific SSL certificate settings
44 | # optional, defaults are empty
45 | #
46 | # mysql_ssl_cert = /etc/ssl/client-cert.pem
47 | # mysql_ssl_key = /etc/ssl/client-key.pem
48 | # mysql_ssl_ca = /etc/ssl/cacert.pem
49 |
50 | # MS SQL specific Windows authentication mode flag
51 | # MUST be in sync with charset_type index-level setting
52 | # optional, default is 0
53 | #
54 | # mssql_winauth = 1 # use currently logged on user credentials
55 |
56 |
57 | # ODBC specific DSN (data source name)
58 | # mandatory for odbc source type, no default value
59 | #
60 | # odbc_dsn = DBQ=C:\data;DefaultDir=C:\data;Driver={Microsoft Text Driver (*.txt; *.csv)};
61 | # sql_query = SELECT id, data FROM documents.csv
62 |
63 |
64 | # ODBC and MS SQL specific, per-column buffer sizes
65 | # optional, default is auto-detect
66 | #
67 | # sql_column_buffers = content=12M, comments=1M
68 |
69 |
70 | # pre-query, executed before the main fetch query
71 | # multi-value, optional, default is empty list of queries
72 | #
73 | # sql_query_pre = SET NAMES utf8
74 | # sql_query_pre = SET SESSION query_cache_type=OFF
75 |
76 |
77 | # main document fetch query
78 | # mandatory, integer document ID field MUST be the first selected column
79 | sql_query = \
80 | SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content \
81 | FROM documents
82 |
83 |
84 | # joined/payload field fetch query
85 | # joined fields let you avoid (slow) JOIN and GROUP_CONCAT
86 | # payload fields let you attach custom per-keyword values (eg. for ranking)
87 | #
88 | # syntax is FIELD-NAME 'from' ( 'query' | 'payload-query' ); QUERY
89 | # joined field QUERY should return 2 columns (docid, text)
90 | # payload field QUERY should return 3 columns (docid, keyword, weight)
91 | #
92 | # REQUIRES that query results are in ascending document ID order!
93 | # multi-value, optional, default is empty list of queries
94 | #
95 | # sql_joined_field = tags from query; SELECT docid, CONCAT('tag',tagid) FROM tags ORDER BY docid ASC
96 | # sql_joined_field = wtags from payload-query; SELECT docid, tag, tagweight FROM tags ORDER BY docid ASC
97 |
98 |
99 | # file based field declaration
100 | #
101 | # content of this field is treated as a file name
102 | # and the file gets loaded and indexed in place of a field
103 | #
104 | # max file size is limited by max_file_field_buffer indexer setting
105 | # file IO errors are non-fatal and get reported as warnings
106 | #
107 | # sql_file_field = content_file_path
108 |
109 |
110 | # range query setup, query that must return min and max ID values
111 | # optional, default is empty
112 | #
113 | # sql_query will need to reference $start and $end boundaries
114 | # if using ranged query:
115 | #
116 | # sql_query = \
117 | # SELECT doc.id, doc.id AS group, doc.title, doc.data \
118 | # FROM documents doc \
119 | # WHERE id>=$start AND id<=$end
120 | #
121 | # sql_query_range = SELECT MIN(id),MAX(id) FROM documents
122 |
123 |
124 | # range query step
125 | # optional, default is 1024
126 | #
127 | # sql_range_step = 1000
128 |
129 |
130 | # unsigned integer attribute declaration
131 | # multi-value (an arbitrary number of attributes is allowed), optional
132 | # optional bit size can be specified, default is 32
133 | #
134 | # sql_attr_uint = author_id
135 | # sql_attr_uint = forum_id:9 # 9 bits for forum_id
136 | sql_attr_uint = group_id
137 |
138 | # boolean attribute declaration
139 | # multi-value (an arbitrary number of attributes is allowed), optional
140 | # equivalent to sql_attr_uint with 1-bit size
141 | #
142 | # sql_attr_bool = is_deleted
143 |
144 |
145 | # bigint attribute declaration
146 | # multi-value (an arbitrary number of attributes is allowed), optional
147 | # declares a signed (unlike uint!) 64-bit attribute
148 | #
149 | # sql_attr_bigint = my_bigint_id
150 |
151 |
152 | # UNIX timestamp attribute declaration
153 | # multi-value (an arbitrary number of attributes is allowed), optional
154 | # similar to integer, but can also be used in date functions
155 | #
156 | # sql_attr_timestamp = posted_ts
157 | # sql_attr_timestamp = last_edited_ts
158 | sql_attr_timestamp = date_added
159 |
160 |
161 | # floating point attribute declaration
162 | # multi-value (an arbitrary number of attributes is allowed), optional
163 | # values are stored in single precision, 32-bit IEEE 754 format
164 | #
165 | # sql_attr_float = lat_radians
166 | # sql_attr_float = long_radians
167 |
168 |
169 | # multi-valued attribute (MVA) attribute declaration
170 | # multi-value (an arbitrary number of attributes is allowed), optional
171 | # MVA values are variable length lists of unsigned 32-bit integers
172 | #
173 | # syntax is ATTR-TYPE ATTR-NAME 'from' SOURCE-TYPE [;QUERY] [;RANGE-QUERY]
174 | # ATTR-TYPE is 'uint' or 'timestamp'
175 | # SOURCE-TYPE is 'field', 'query', or 'ranged-query'
176 | # QUERY is SQL query used to fetch all ( docid, attrvalue ) pairs
177 | # RANGE-QUERY is SQL query used to fetch min and max ID values, similar to 'sql_query_range'
178 | #
179 | # sql_attr_multi = uint tag from query; SELECT docid, tagid FROM tags
180 | # sql_attr_multi = uint tag from ranged-query; \
181 | # SELECT docid, tagid FROM tags WHERE id>=$start AND id<=$end; \
182 | # SELECT MIN(docid), MAX(docid) FROM tags
183 |
184 |
185 | # string attribute declaration
186 | # multi-value (an arbitrary number of these is allowed), optional
187 | # lets you store and retrieve strings
188 | #
189 | # sql_attr_string = stitle
190 |
191 |
192 | # JSON attribute declaration
193 | # multi-value (an arbitrary number of these is allowed), optional
194 | # lets you store a JSON document as an (in-memory) attribute for later use
195 | #
196 | # sql_attr_json = properties
197 |
198 |
199 | # combined field plus attribute declaration (from a single column)
200 | # stores column as an attribute, but also indexes it as a full-text field
201 | #
202 | # sql_field_string = author
203 |
204 |
205 | # post-query, executed on sql_query completion
206 | # optional, default is empty
207 | #
208 | # sql_query_post =
209 |
210 |
211 | # post-index-query, executed on successful indexing completion
212 | # optional, default is empty
213 | # $maxid expands to max document ID actually fetched from DB
214 | #
215 | # sql_query_post_index = REPLACE INTO counters ( id, val ) \
216 | # VALUES ( 'max_indexed_id', $maxid )
217 |
218 |
219 | # ranged query throttling, in milliseconds
220 | # optional, default is 0 which means no delay
221 | # enforces given delay before each query step
222 | sql_ranged_throttle = 0
223 |
224 |
225 | # kill-list query, fetches the document IDs for kill-list
226 | # k-list will suppress matches from preceding indexes in the same query
227 | # optional, default is empty
228 | #
229 | # sql_query_killlist = SELECT id FROM documents WHERE edited>=@last_reindex
230 |
231 |
232 | # columns to unpack on indexer side when indexing
233 | # multi-value, optional, default is empty list
234 | #
235 | # unpack_zlib = zlib_column
236 | # unpack_mysqlcompress = compressed_column
237 | # unpack_mysqlcompress = compressed_column_2
238 |
239 |
240 | # maximum unpacked length allowed in MySQL COMPRESS() unpacker
241 | # optional, default is 16M
242 | #
243 | # unpack_mysqlcompress_maxsize = 16M
244 |
245 |
246 | # hook command to run when SQL connection succeeds
247 | # optional, default value is empty (do nothing)
248 | #
249 | # hook_connect = bash sql_connect.sh
250 |
251 |
252 | # hook command to run after (any) SQL range query
253 | # it may print out "minid maxid" (w/o quotes) to override the range
254 | # optional, default value is empty (do nothing)
255 | #
256 | # hook_query_range = bash sql_query_range.sh
257 |
258 |
259 | # hook command to run on successful indexing completion
260 | # $maxid expands to max document ID actually fetched from DB
261 | # optional, default value is empty (do nothing)
262 | #
263 | # hook_post_index = bash sql_post_index.sh $maxid
264 |
265 | #####################################################################
266 | ## xmlpipe2 settings
267 | #####################################################################
268 |
269 | # type = xmlpipe
270 |
271 | # shell command to invoke xmlpipe stream producer
272 | # mandatory
273 | #
274 | # xmlpipe_command = cat /var/test.xml
275 |
276 | # xmlpipe2 field declaration
277 | # multi-value, optional, default is empty
278 | #
279 | # xmlpipe_field = subject
280 | # xmlpipe_field = content
281 |
282 |
283 | # xmlpipe2 attribute declaration
284 | # multi-value, optional, default is empty
285 | # all xmlpipe_attr_XXX options are fully similar to sql_attr_XXX
286 | # examples:
287 | #
288 | # xmlpipe_attr_timestamp = published
289 | # xmlpipe_attr_uint = author_id
290 | # xmlpipe_attr_bool = is_enabled
291 | # xmlpipe_attr_float = latitude
292 | # xmlpipe_attr_bigint = guid
293 | # xmlpipe_attr_multi = tags
294 | # xmlpipe_attr_multi_64 = tags64
295 | # xmlpipe_attr_string = title
296 | # xmlpipe_attr_json = extra_data
297 | # xmlpipe_field_string = content
298 |
299 |
300 | # perform UTF-8 validation, and filter out incorrect codes
301 | # avoids XML parser choking on non-UTF-8 documents
302 | # optional, default is 0
303 | #
304 | # xmlpipe_fixup_utf8 = 1
305 | }
306 |
307 |
308 | # inherited source example
309 | #
310 | # all the parameters are copied from the parent source,
311 | # and may then be overridden in this source definition
312 | source src1throttled : src1
313 | {
314 | sql_ranged_throttle = 100
315 | }
316 |
317 | #############################################################################
318 | ## index definition
319 | #############################################################################
320 |
321 | # local index example
322 | #
323 | # this is an index which is stored locally in the filesystem
324 | #
325 | # all indexing-time options (such as morphology and charsets)
326 | # are configured per local index
327 | index test1
328 | {
329 | # index type
330 | # optional, default is 'plain'
331 | # known values are 'plain', 'distributed', and 'rt' (see samples below)
332 | # type = plain
333 |
334 | # document source(s) to index
335 | # multi-value, mandatory
336 | # document IDs must be globally unique across all sources
337 | source = src1
338 |
339 | # index files path and file name, without extension
340 | # mandatory, path must be writable, extensions will be auto-appended
341 | path = /var/data/test1
342 |
343 | # document attribute values (docinfo) storage mode
344 | # optional, default is 'extern'
345 | # known values are 'none', 'extern' and 'inline'
346 | docinfo = extern
347 |
348 | # dictionary type, 'crc' or 'keywords'
349 | # crc is faster to index when no substring/wildcards searches are needed
350 | # crc with substrings might be faster to search but is much slower to index
351 | # (because all substrings are pre-extracted as individual keywords)
352 | # keywords is much faster to index with substrings, and index is much (3-10x) smaller
353 | # keywords supports wildcards, crc does not, and never will
354 | # optional, default is 'keywords'
355 | dict = keywords
356 |
357 | # memory locking for cached data (.spa and .spi), to prevent swapping
358 | # optional, default is 0 (do not mlock)
359 | # requires searchd to be run from root
360 | mlock = 0
361 |
362 | # a list of morphology preprocessors to apply
363 | # optional, default is empty
364 | #
365 | # builtin preprocessors are 'none', 'stem_en', 'stem_ru', 'stem_enru',
366 | # 'soundex', and 'metaphone'; additional preprocessors available from
367 | # libstemmer are 'libstemmer_XXX', where XXX is algorithm code
368 | # (see libstemmer_c/libstemmer/modules.txt)
369 | #
370 | # morphology = stem_en, stem_ru, soundex
371 | # morphology = libstemmer_german
372 | # morphology = libstemmer_sv
373 | morphology = none
374 |
375 | # minimum word length at which to enable stemming
376 | # optional, default is 1 (stem everything)
377 | #
378 | # min_stemming_len = 1
379 |
380 |
381 | # stopword files list (space separated)
382 | # optional, default is empty
383 | # contents are plain text, charset_table and stemming are both applied
384 | #
385 | # stopwords = /var/data/stopwords.txt
386 |
387 |
388 | # wordforms file, in "mapfrom > mapto" plain text format
389 | # optional, default is empty
390 | #
391 | # wordforms = /var/data/wordforms.txt
392 |
393 |
394 | # tokenizing exceptions file
395 | # optional, default is empty
396 | #
397 | # plain text, case sensitive, space insensitive in map-from part
398 | # one "Map Several Words => ToASingleOne" entry per line
399 | #
400 | # exceptions = /var/data/exceptions.txt
401 |
402 |
403 | # embedded file size limit
404 | # optional, default is 16K
405 | #
406 | # exceptions, wordforms, and stopwords files smaller than this limit
407 | # are stored in the index; otherwise, their paths and sizes are stored
408 | #
409 | # embedded_limit = 16K
410 |
411 | # minimum indexed word length
412 | # default is 1 (index everything)
413 | min_word_len = 1
414 |
415 |
416 | # ignored characters list
417 | # optional, default value is empty
418 | #
419 | # ignore_chars = U+00AD
420 |
421 |
422 | # minimum word prefix length to index
423 | # optional, default is 0 (do not index prefixes)
424 | #
425 | # min_prefix_len = 0
426 |
427 |
428 | # minimum word infix length to index
429 | # optional, default is 0 (do not index infixes)
430 | #
431 | # min_infix_len = 0
432 |
433 |
434 | # maximum substring (prefix or infix) length to index
435 | # optional, default is 0 (do not limit substring length)
436 | #
437 | # max_substring_len = 8
438 |
439 |
440 | # list of fields to limit prefix/infix indexing to
441 | # optional, default value is empty (index all fields in prefix/infix mode)
442 | #
443 | # prefix_fields = filename
444 | # infix_fields = url, domain
445 |
446 |
447 | # expand keywords with exact forms and/or stars when searching fit indexes
448 | # search-time only, does not affect indexing, can be 0 or 1
449 | # optional, default is 0 (do not expand keywords)
450 | #
451 | # expand_keywords = 1
452 |
453 |
454 | # n-gram length to index, for CJK indexing
455 | # only supports 0 and 1 for now, other lengths to be implemented
456 | # optional, default is 0 (disable n-grams)
457 | #
458 | # ngram_len = 1
459 |
460 |
461 | # n-gram characters list, for CJK indexing
462 | # optional, default is empty
463 | #
464 | # ngram_chars = U+3000..U+2FA1F
465 |
466 |
467 | # phrase boundary characters list
468 | # optional, default is empty
469 | #
470 | # phrase_boundary = ., ?, !, U+2026 # horizontal ellipsis
471 |
472 |
473 | # phrase boundary word position increment
474 | # optional, default is 0
475 | #
476 | # phrase_boundary_step = 100
477 |
478 |
479 | # blended characters list
480 | # blended chars are indexed both as separators and valid characters
481 | # for instance, AT&T will results in 3 tokens ("at", "t", and "at&t")
482 | # optional, default is empty
483 | #
484 | # blend_chars = +, &, U+23
485 |
486 |
487 | # blended token indexing mode
488 | # a comma separated list of blended token indexing variants
489 | # known variants are trim_none, trim_head, trim_tail, trim_both, skip_pure
490 | # optional, default is trim_none
491 | #
492 | # blend_mode = trim_tail, skip_pure
493 |
494 |
495 | # whether to strip HTML tags from incoming documents
496 | # known values are 0 (do not strip) and 1 (do strip)
497 | # optional, default is 0
498 | html_strip = 0
499 |
500 | # what HTML attributes to index if stripping HTML
501 | # optional, default is empty (do not index anything)
502 | #
503 | # html_index_attrs = img=alt,title; a=title;
504 |
505 |
506 | # what HTML elements contents to strip
507 | # optional, default is empty (do not strip element contents)
508 | #
509 | # html_remove_elements = style, script
510 |
511 |
512 | # whether to preopen index data files on startup
513 | # optional, default is 0 (do not preopen), searchd-only
514 | #
515 | # preopen = 1
516 |
517 |
518 | # whether to enable in-place inversion (2x less disk, 90-95% speed)
519 | # optional, default is 0 (use separate temporary files), indexer-only
520 | #
521 | # inplace_enable = 1
522 |
523 |
524 | # in-place fine-tuning options
525 | # optional, defaults are listed below
526 | #
527 | # inplace_hit_gap = 0 # preallocated hitlist gap size
528 | # inplace_docinfo_gap = 0 # preallocated docinfo gap size
529 | # inplace_reloc_factor = 0.1 # relocation buffer size within arena
530 | # inplace_write_factor = 0.1 # write buffer size within arena
531 |
532 |
533 | # whether to index original keywords along with stemmed versions
534 | # enables "=exactform" operator to work
535 | # optional, default is 0
536 | #
537 | # index_exact_words = 1
538 |
539 |
540 | # position increment on overshort (less that min_word_len) words
541 | # optional, allowed values are 0 and 1, default is 1
542 | #
543 | # overshort_step = 1
544 |
545 |
546 | # position increment on stopword
547 | # optional, allowed values are 0 and 1, default is 1
548 | #
549 | # stopword_step = 1
550 |
551 |
552 | # hitless words list
553 | # positions for these keywords will not be stored in the index
554 | # optional, allowed values are 'all', or a list file name
555 | #
556 | # hitless_words = all
557 | # hitless_words = hitless.txt
558 |
559 |
560 | # detect and index sentence and paragraph boundaries
561 | # required for the SENTENCE and PARAGRAPH operators to work
562 | # optional, allowed values are 0 and 1, default is 0
563 | #
564 | # index_sp = 1
565 |
566 |
567 | # index zones, delimited by HTML/XML tags
568 | # a comma separated list of tags and wildcards
569 | # required for the ZONE operator to work
570 | # optional, default is empty string (do not index zones)
571 | #
572 | # index_zones = title, h*, th
573 |
574 |
575 | # index per-document and average per-index field lengths, in tokens
576 | # required for the BM25A(), BM25F() in expression ranker
577 | # optional, default is 0 (do not index field lenghts)
578 | #
579 | # index_field_lengths = 1
580 |
581 |
582 | # regular expressions (regexps) to filter the fields and queries with
583 | # gets applied to data source fields when indexing
584 | # gets applied to search queries when searching
585 | # multi-value, optional, default is empty list of regexps
586 | #
587 | # regexp_filter = \b(\d+)\" => \1inch
588 | # regexp_filter = (blue|red) => color
589 |
590 |
591 | # list of the words considered frequent with respect to bigram indexing
592 | # optional, default is empty
593 | #
594 | # bigram_freq_words = the, a, i, you, my
595 |
596 |
597 | # bigram indexing mode
598 | # known values are none, all, first_freq, both_freq
599 | # option, default is none (do not index bigrams)
600 | #
601 | # bigram_index = both_freq
602 |
603 |
604 | # snippet document file name prefix
605 | # preprended to file names when generating snippets using load_files option
606 | # WARNING, this is a prefix (not a path), trailing slash matters!
607 | # optional, default is empty
608 | #
609 | # snippets_file_prefix = /mnt/mydocs/server1
610 |
611 |
612 | # whether to apply stopwords before or after stemming
613 | # optional, default is 0 (apply stopwords after stemming)
614 | #
615 | # stopwords_unstemmed = 0
616 |
617 |
618 | # path to a global (cluster-wide) keyword IDFs file
619 | # optional, default is empty (use local IDFs)
620 | #
621 | # global_idf = /usr/local/sphinx/var/global.idf
622 | }
623 |
624 |
625 | # inherited index example
626 | #
627 | # all the parameters are copied from the parent index,
628 | # and may then be overridden in this index definition
629 | index test1stemmed : test1
630 | {
631 | path = /var/data/test1stemmed
632 | morphology = stem_en
633 | }
634 |
635 |
636 | # distributed index example
637 | #
638 | # this is a virtual index which can NOT be directly indexed,
639 | # and only contains references to other local and/or remote indexes
640 | index dist1
641 | {
642 | # 'distributed' index type MUST be specified
643 | type = distributed
644 |
645 | # local index to be searched
646 | # there can be many local indexes configured
647 | local = test1
648 | local = test1stemmed
649 |
650 | # remote agent
651 | # multiple remote agents may be specified
652 | # syntax for TCP connections is 'hostname:port:index1,[index2[,...]]'
653 | # syntax for local UNIX connections is '/path/to/socket:index1,[index2[,...]]'
654 | agent = localhost:9313:remote1
655 | agent = localhost:9314:remote2,remote3
656 | # agent = /var/run/searchd.sock:remote4
657 |
658 | # remote agent mirrors groups, aka mirrors, aka HA agents
659 | # defines 2 or more interchangeable mirrors for a given index part
660 | #
661 | # agent = server3:9312 | server4:9312 :indexchunk2
662 | # agent = server3:9312:chunk2server3 | server4:9312:chunk2server4
663 | # agent = server3:chunk2server3 | server4:chunk2server4
664 | # agent = server21|server22|server23:chunk2
665 |
666 |
667 | # blackhole remote agent, for debugging/testing
668 | # network errors and search results will be ignored
669 | #
670 | # agent_blackhole = testbox:9312:testindex1,testindex2
671 |
672 |
673 | # persistenly connected remote agent
674 | # reduces connect() pressure, requires that workers IS threads
675 | #
676 | # agent_persistent = testbox:9312:testindex1,testindex2
677 |
678 |
679 | # remote agent connection timeout, milliseconds
680 | # optional, default is 1000 ms, ie. 1 sec
681 | agent_connect_timeout = 1000
682 |
683 | # remote agent query timeout, milliseconds
684 | # optional, default is 3000 ms, ie. 3 sec
685 | agent_query_timeout = 3000
686 |
687 | # HA mirror agent strategy
688 | # optional, defaults to ??? (random mirror)
689 | # know values are nodeads, noerrors, roundrobin, nodeadstm, noerrorstm
690 | #
691 | # ha_strategy = nodeads
692 | }
693 |
694 |
695 | # realtime index example
696 | #
697 | # you can run INSERT, REPLACE, and DELETE on this index on the fly
698 | # using MySQL protocol (see 'listen' directive below)
699 | index rt
700 | {
701 | # 'rt' index type must be specified to use RT index
702 | type = rt
703 |
704 | # index files path and file name, without extension
705 | # mandatory, path must be writable, extensions will be auto-appended
706 | path = /var/data/rt
707 |
708 | # RAM chunk size limit
709 | # RT index will keep at most this much data in RAM, then flush to disk
710 | # optional, default is 128M
711 | #
712 | # rt_mem_limit = 512M
713 |
714 | # full-text field declaration
715 | # multi-value, mandatory
716 | rt_field = title
717 | rt_field = content
718 |
719 | # unsigned integer attribute declaration
720 | # multi-value (an arbitrary number of attributes is allowed), optional
721 | # declares an unsigned 32-bit attribute
722 | rt_attr_uint = gid
723 |
724 | # RT indexes currently support the following attribute types:
725 | # uint, bigint, float, timestamp, string, mva, mva64, json
726 | #
727 | # rt_attr_bigint = guid
728 | # rt_attr_float = gpa
729 | # rt_attr_timestamp = ts_added
730 | # rt_attr_string = author
731 | # rt_attr_multi = tags
732 | # rt_attr_multi_64 = tags64
733 | # rt_attr_json = extra_data
734 | }
735 |
736 | #############################################################################
737 | ## indexer settings
738 | #############################################################################
739 |
740 | indexer
741 | {
742 | # memory limit, in bytes, kiloytes (16384K) or megabytes (256M)
743 | # optional, default is 128M, max is 2047M, recommended is 256M to 1024M
744 | mem_limit = 128M
745 |
746 | # maximum IO calls per second (for I/O throttling)
747 | # optional, default is 0 (unlimited)
748 | #
749 | # max_iops = 40
750 |
751 |
752 | # maximum IO call size, bytes (for I/O throttling)
753 | # optional, default is 0 (unlimited)
754 | #
755 | # max_iosize = 1048576
756 |
757 |
758 | # maximum xmlpipe2 field length, bytes
759 | # optional, default is 2M
760 | #
761 | # max_xmlpipe2_field = 4M
762 |
763 |
764 | # write buffer size, bytes
765 | # several (currently up to 4) buffers will be allocated
766 | # write buffers are allocated in addition to mem_limit
767 | # optional, default is 1M
768 | #
769 | # write_buffer = 1M
770 |
771 |
772 | # maximum file field adaptive buffer size
773 | # optional, default is 8M, minimum is 1M
774 | #
775 | # max_file_field_buffer = 32M
776 |
777 |
778 | # how to handle IO errors in file fields
779 | # known values are 'ignore_field', 'skip_document', and 'fail_index'
780 | # optional, default is 'ignore_field'
781 | #
782 | # on_file_field_error = skip_document
783 |
784 |
785 | # lemmatizer cache size
786 | # improves the indexing time when the lemmatization is enabled
787 | # optional, default is 256K
788 | #
789 | # lemmatizer_cache = 512M
790 | }
791 |
792 | #############################################################################
793 | ## searchd settings
794 | #############################################################################
795 |
796 | searchd
797 | {
798 | # [hostname:]port[:protocol], or /unix/socket/path to listen on
799 | # known protocols are 'sphinx' (SphinxAPI) and 'mysql41' (SphinxQL)
800 | #
801 | # multi-value, multiple listen points are allowed
802 | # optional, defaults are 9312:sphinx and 9306:mysql41, as below
803 | #
804 | # listen = 127.0.0.1
805 | # listen = 192.168.0.1:9312
806 | # listen = 9312
807 | # listen = /var/run/searchd.sock
808 | listen = 9312
809 | listen = 9306:mysql41
810 |
811 | # log file, searchd run info is logged here
812 | # optional, default is 'searchd.log'
813 | log = /var/log/searchd.log
814 |
815 | # query log file, all search queries are logged here
816 | # optional, default is empty (do not log queries)
817 | query_log = /var/log/query.log
818 |
819 | # client read timeout, seconds
820 | # optional, default is 5
821 | read_timeout = 5
822 |
823 | # request timeout, seconds
824 | # optional, default is 5 minutes
825 | client_timeout = 300
826 |
827 | # maximum amount of children to fork (concurrent searches to run)
828 | # optional, default is 0 (unlimited)
829 | max_children = 30
830 |
831 | # maximum amount of persistent connections from this master to each agent host
832 | # optional, but necessary if you use agent_persistent. It is reasonable to set the value
833 | # as max_children, or less on the agent's hosts.
834 | persistent_connections_limit = 30
835 |
836 | # PID file, searchd process ID file name
837 | # mandatory
838 | pid_file = /var/log/searchd.pid
839 |
840 | # seamless rotate, prevents rotate stalls if precaching huge datasets
841 | # optional, default is 1
842 | seamless_rotate = 1
843 |
844 | # whether to forcibly preopen all indexes on startup
845 | # optional, default is 1 (preopen everything)
846 | preopen_indexes = 1
847 |
848 | # whether to unlink .old index copies on succesful rotation.
849 | # optional, default is 1 (do unlink)
850 | unlink_old = 1
851 |
852 | # attribute updates periodic flush timeout, seconds
853 | # updates will be automatically dumped to disk this frequently
854 | # optional, default is 0 (disable periodic flush)
855 | #
856 | # attr_flush_period = 900
857 |
858 |
859 | # MVA updates pool size
860 | # shared between all instances of searchd, disables attr flushes!
861 | # optional, default size is 1M
862 | mva_updates_pool = 1M
863 |
864 | # max allowed network packet size
865 | # limits both query packets from clients, and responses from agents
866 | # optional, default size is 8M
867 | max_packet_size = 8M
868 |
869 | # max allowed per-query filter count
870 | # optional, default is 256
871 | max_filters = 256
872 |
873 | # max allowed per-filter values count
874 | # optional, default is 4096
875 | max_filter_values = 4096
876 |
877 |
878 | # socket listen queue length
879 | # optional, default is 5
880 | #
881 | # listen_backlog = 5
882 |
883 |
884 | # per-keyword read buffer size
885 | # optional, default is 256K
886 | #
887 | # read_buffer = 256K
888 |
889 |
890 | # unhinted read size (currently used when reading hits)
891 | # optional, default is 32K
892 | #
893 | # read_unhinted = 32K
894 |
895 |
896 | # max allowed per-batch query count (aka multi-query count)
897 | # optional, default is 32
898 | max_batch_queries = 32
899 |
900 |
901 | # max common subtree document cache size, per-query
902 | # optional, default is 0 (disable subtree optimization)
903 | #
904 | # subtree_docs_cache = 4M
905 |
906 |
907 | # max common subtree hit cache size, per-query
908 | # optional, default is 0 (disable subtree optimization)
909 | #
910 | # subtree_hits_cache = 8M
911 |
912 |
913 | # multi-processing mode (MPM)
914 | # known values are none, fork, prefork, and threads
915 | # threads is required for RT backend to work
916 | # optional, default is threads
917 | workers = threads # for RT to work
918 |
919 |
920 | # max threads to create for searching local parts of a distributed index
921 | # optional, default is 0, which means disable multi-threaded searching
922 | # should work with all MPMs (ie. does NOT require workers=threads)
923 | #
924 | # dist_threads = 4
925 |
926 |
927 | # binlog files path; use empty string to disable binlog
928 | # optional, default is build-time configured data directory
929 | #
930 | # binlog_path = # disable logging
931 | # binlog_path = /var/data # binlog.001 etc will be created there
932 |
933 |
934 | # binlog flush/sync mode
935 | # 0 means flush and sync every second
936 | # 1 means flush and sync every transaction
937 | # 2 means flush every transaction, sync every second
938 | # optional, default is 2
939 | #
940 | # binlog_flush = 2
941 |
942 |
943 | # binlog per-file size limit
944 | # optional, default is 128M, 0 means no limit
945 | #
946 | # binlog_max_log_size = 256M
947 |
948 |
949 | # per-thread stack size, only affects workers=threads mode
950 | # optional, default is 64K
951 | #
952 | # thread_stack = 128K
953 |
954 |
955 | # per-keyword expansion limit (for dict=keywords prefix searches)
956 | # optional, default is 0 (no limit)
957 | #
958 | # expansion_limit = 1000
959 |
960 |
961 | # RT RAM chunks flush period
962 | # optional, default is 0 (no periodic flush)
963 | #
964 | # rt_flush_period = 900
965 |
966 |
967 | # query log file format
968 | # optional, known values are plain and sphinxql, default is plain
969 | #
970 | # query_log_format = sphinxql
971 |
972 |
973 | # version string returned to MySQL network protocol clients
974 | # optional, default is empty (use Sphinx version)
975 | #
976 | # mysql_version_string = 5.0.37
977 |
978 |
979 | # default server-wide collation
980 | # optional, default is libc_ci
981 | #
982 | # collation_server = utf8_general_ci
983 |
984 |
985 | # server-wide locale for libc based collations
986 | # optional, default is C
987 | #
988 | # collation_libc_locale = ru_RU.UTF-8
989 |
990 |
991 | # threaded server watchdog (only used in workers=threads mode)
992 | # optional, values are 0 and 1, default is 1 (watchdog on)
993 | #
994 | # watchdog = 1
995 |
996 |
997 | # costs for max_predicted_time model, in (imaginary) nanoseconds
998 | # optional, default is "doc=64, hit=48, skip=2048, match=64"
999 | #
1000 | # predicted_time_costs = doc=64, hit=48, skip=2048, match=64
1001 |
1002 |
1003 | # current SphinxQL state (uservars etc) serialization path
1004 | # optional, default is none (do not serialize SphinxQL state)
1005 | #
1006 | # sphinxql_state = sphinxvars.sql
1007 |
1008 |
1009 | # maximum RT merge thread IO calls per second, and per-call IO size
1010 | # useful for throttling (the background) OPTIMIZE INDEX impact
1011 | # optional, default is 0 (unlimited)
1012 | #
1013 | # rt_merge_iops = 40
1014 | # rt_merge_maxiosize = 1M
1015 |
1016 |
1017 | # interval between agent mirror pings, in milliseconds
1018 | # 0 means disable pings
1019 | # optional, default is 1000
1020 | #
1021 | # ha_ping_interval = 0
1022 |
1023 |
1024 | # agent mirror statistics window size, in seconds
1025 | # stats older than the window size (karma) are retired
1026 | # that is, they will not affect master choice of agents in any way
1027 | # optional, default is 60 seconds
1028 | #
1029 | # ha_period_karma = 60
1030 |
1031 |
1032 | # delay between preforked children restarts on rotation, in milliseconds
1033 | # optional, default is 0 (no delay)
1034 | #
1035 | # prefork_rotation_throttle = 100
1036 |
1037 |
1038 | # a prefix to prepend to the local file names when creating snippets
1039 | # with load_files and/or load_files_scatter options
1040 | # optional, default is empty
1041 | #
1042 | # snippets_file_prefix = /mnt/common/server1/
1043 | }
1044 |
1045 | #############################################################################
1046 | ## common settings
1047 | #############################################################################
1048 |
1049 | common
1050 | {
1051 |
1052 | # lemmatizer dictionaries base path
1053 | # optional, defaut is /usr/local/share (see ./configure --datadir)
1054 | #
1055 | # lemmatizer_base = /usr/local/share/sphinx/dicts
1056 |
1057 |
1058 | # how to handle syntax errors in JSON attributes
1059 | # known values are 'ignore_attr' and 'fail_index'
1060 | # optional, default is 'ignore_attr'
1061 | #
1062 | # on_json_attr_error = fail_index
1063 |
1064 |
1065 | # whether to auto-convert numeric values from strings in JSON attributes
1066 | # with auto-conversion, string value with actually numeric data
1067 | # (as in {"key":"12345"}) gets stored as a number, rather than string
1068 | # optional, allowed values are 0 and 1, default is 0 (do not convert)
1069 | #
1070 | # json_autoconv_numbers = 1
1071 |
1072 |
1073 | # whether and how to auto-convert key names in JSON attributes
1074 | # known value is 'lowercase'
1075 | # optional, default is unspecified (do nothing)
1076 | #
1077 | # json_autoconv_keynames = lowercase
1078 |
1079 |
1080 | # trusted plugin directory
1081 | # optional, default is empty (disable UDFs)
1082 | #
1083 | # plugin_dir = /usr/local/sphinx/lib
1084 |
1085 | }
1086 |
1087 | # --eof--
1088 |
--------------------------------------------------------------------------------
/sphinx/etc/uni.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/sphinx/etc/uni.lib
--------------------------------------------------------------------------------
/sphinx/misc/raminfo.py:
--------------------------------------------------------------------------------
1 | # a simple tool to examine sphinx rt index .ram file
2 |
3 | import sys, struct, os
4 |
5 |
6 | def get_dword(f):
7 | return struct.unpack('I', f.read(4))[0]
8 |
9 |
10 | def get_id(f, id64):
11 | if id64:
12 | return struct.unpack('Q', f.read(8))[0]
13 | else:
14 | return struct.unpack('I', f.read(4))[0]
15 |
16 |
17 | def skip_vec(f, entry_size):
18 | f.seek(get_dword(f) * entry_size, 1)
19 |
20 |
21 | def main(argv):
22 | if len(argv) < 2:
23 | sys.stderr.write("usage: raminfo.py ")
24 | return 1
25 |
26 | if not os.path.exists(argv[1]):
27 | sys.stderr.write("ERROR: file %r not found!" % argv[1])
28 | return 1
29 |
30 | f = open(argv[1], "rb")
31 | print "examining RAM chunk %r..." % argv[1]
32 | id64 = get_dword(f)
33 | if id64!=0:
34 | id64 = 1
35 | next_tag = get_dword(f)
36 | num_segments = get_dword(f)
37 | print "%d segments total, %d-bit ids, next free tag %d" % (num_segments, 32*(1+id64), next_tag)
38 |
39 | if num_segments>32:
40 | print "WARNING, unexpectedly many segments, displaying first 32 only"
41 |
42 | index_minid = 0
43 | index_maxid = 0
44 | total_rows = 0
45 | total_alive = 0
46 |
47 | for i in range(1, num_segments+1):
48 | off1 = f.tell()
49 | tag = get_dword(f)
50 | skip_vec(f, 1) # keywords
51 | skip_vec(f, 1) # checkpoints1
52 | skip_vec(f, 16) # checkpoints2
53 | skip_vec(f, 1) # docs
54 | skip_vec(f, 1) # hits
55 |
56 | rows = get_dword(f)
57 | alive = get_dword(f)
58 | off2 = f.tell()
59 | skip_vec(f, 4) # rows data
60 | off3 = f.tell()
61 |
62 | rowsize = (off3 - off2 - 4)/rows
63 | f.seek(off2 + 4)
64 | minid = get_id(f, id64)
65 | f.seek(off3 - rowsize)
66 | maxid = get_id(f, id64)
67 | f.seek(off3)
68 |
69 | skip_vec(f, 4*(1+id64)) # klist
70 | skip_vec(f, 1) # strings
71 | skip_vec(f, 4) # mva
72 | skip_vec(f, 8) # infixcp
73 |
74 | xrows = ""
75 | if rows!=alive:
76 | xrows = " (alive %d)" % alive
77 |
78 | if i<=32:
79 | print "seg %d at off %d, tag %d, rows %d%s at off %d, %d b/row, ids %d..%d" % (i, off1, tag, rows, xrows, off2, rowsize, minid, maxid)
80 | else:
81 | if sys.stdout.isatty() and (i % 1000)==0:
82 | sys.stdout.write("scanning segment %d of %d\r" % (i, num_segments))
83 |
84 | if index_maxid==0:
85 | index_minid = minid
86 | index_maxid = maxid
87 | else:
88 | index_minid = min(index_minid, minid)
89 | index_maxid = max(index_maxid, maxid)
90 | total_rows += rows
91 | total_alive += alive
92 |
93 | print "total %d rows (%d alive), ids %d..%d" % (total_rows, total_alive, index_minid, index_maxid)
94 |
95 | if __name__ == "__main__":
96 | sys.exit(main(sys.argv))
97 |
--------------------------------------------------------------------------------
/sphinx/misc/resolve.py:
--------------------------------------------------------------------------------
1 | #
2 | # $Id$
3 | #
4 |
5 | import sys, re
6 |
7 | if len(sys.argv)!=3:
8 | print 'Usage: python resolve.py BACKTRACE SYMBOLS'
9 | sys.exit(0)
10 |
11 | def myopen(name):
12 | if name == '-':
13 | return sys.stdin
14 | fh = open(name, 'r')
15 | if not fh:
16 | print 'FATAL: failed to open %s' % name
17 | sys.exit(1)
18 | return fh
19 |
20 | syms = []
21 | fp = myopen(sys.argv[2])
22 | for line in fp.readlines():
23 | line = line.rstrip()
24 | match = re.match('([0-9a-fA-F]+) \w ', line)
25 | if match:
26 | addr = int(match.group(1), 16)
27 | name = line[len(match.group(0)):]
28 | syms.append([addr, name])
29 | fp.close()
30 |
31 | fp = myopen(sys.argv[1])
32 | for line in fp.readlines():
33 | line = line.rstrip()
34 |
35 | # skip plain boring log entries
36 | if re.search('^\[\w+\s+\w+\s+\d+\s+\d+:\d+:\d+\.\d+ \d+\] \[\d+\] \S', line):
37 | continue
38 |
39 | # resolve symbols, if any
40 | match = re.search('\[0x([0-9a-fA-F]+)\]', line)
41 | if match:
42 | addr = int(match.group(1), 16)
43 | resolved = '???'
44 | for i in range(len(syms)-1):
45 | if syms[i][0]<=addr and addr
--------------------------------------------------------------------------------
/sphinx/misc/searchd:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Example init file for searchd
4 | #
5 | # chkconfig: 2345 55 25
6 | #
7 | # description: searchd
8 | #
9 | # USE "chkconfig --add searchd" to configure Sphinx searchd service
10 | #
11 | # by Vladimir Fedorkov Mar 1, 2006, info@astellar.com
12 | # public domain
13 |
14 | SUDO_USER=searchd
15 |
16 | BASE_PATH=/release/search
17 | PID_FILE=$BASE_PATH/searchd.pid
18 | CONFIG_FILE=$BASE_PATH/sphinx.conf
19 |
20 | EXEC_PATH=$BASE_PATH
21 | LOG_PATH=$EXEC_PATH
22 |
23 | RETVAL=0
24 | prog="searchd"
25 |
26 | do_config() {
27 | mkdir -p $EXEC_PATH
28 | mkdir $EXEC_PATH/data
29 | mkdir -p $LOG_PATH
30 | chown -R $SUDO_USER $EXEC_PATH
31 | chown -R $SUDO_USER $EXEC_PATH/$CONFIG_FILE
32 | chown -R $SUDO_USER $LOG_PATH
33 |
34 | chmod 600 $EXEC_PATH/$CONFIG_FILE
35 | chmod u+rwx $EXEC_PATH/*
36 | chmod -R u+rw,go-rwx $EXEC_PATH/data
37 | chmod -R u+rw,go-rwx $LOG_PATH
38 | }
39 |
40 | do_start() {
41 | echo "Starting $prog"
42 | sudo -u $SUDO_USER $EXEC_PATH/$prog --config $CONFIG_FILE
43 | RETVAL=$?
44 | echo
45 | return $RETVAL
46 | }
47 |
48 | do_stop() {
49 | echo "Stopping $prog"
50 | if [ -e $PID_FILE ] ; then
51 | kill -15 `cat $PID_FILE`
52 | sleep 5
53 | if [ -e $PID_FILE ] ; then
54 | kill -9 `cat $PID_FILE`
55 | fi
56 | fi
57 | RETVAL=$?
58 | echo
59 | return $RETVAL
60 | }
61 |
62 | case $* in
63 |
64 | config)
65 | do_config
66 | ;;
67 |
68 | start)
69 | do_start
70 | ;;
71 |
72 | stop)
73 | do_stop
74 | ;;
75 |
76 | *)
77 | echo "usage: $0 {start|stop|config}" >&2
78 |
79 | exit 1
80 | ;;
81 | esac
82 |
83 | exit $RETVAL
84 |
--------------------------------------------------------------------------------
/sphinx/misc/suggest/README:
--------------------------------------------------------------------------------
1 | Suggestions sample
2 | -------------------
3 |
4 | 0) What's this. This sample shows how to implement simple keyword
5 | correction suggestions (ie. "did you mean") using Sphinx.
6 |
7 | 1) Requirements. You will need Sphinx, MySQL, and PHP CLI.
8 |
9 | 2) Quickstart. (Skip first indexer command to use bundled sample.)
10 |
11 | indexer YOURINDEX --config YOURCONFIG.CONF --buildstops dict.txt 100000 --buildfreqs
12 | cat dict.txt | php suggest.php --builddict > dict.sql
13 | mysql -u root test < dict.sql
14 | indexer --config suggest.conf --all
15 | searchd --config suggest.conf
16 | php suggest.php --query sphynx
17 |
18 | --eof--
19 |
--------------------------------------------------------------------------------
/sphinx/misc/suggest/suggest.conf:
--------------------------------------------------------------------------------
1 | source suggest
2 | {
3 | type = mysql
4 | sql_host = localhost
5 | sql_user = root
6 | sql_pass =
7 | sql_db = test
8 |
9 | sql_query_pre = SET NAMES utf8
10 | sql_query = SELECT id, trigrams, freq, LENGTH(keyword) AS len, keyword FROM suggest
11 |
12 | sql_attr_uint = freq
13 | sql_attr_uint = len
14 | sql_attr_string = keyword
15 | }
16 |
17 |
18 | index suggest
19 | {
20 | source = suggest
21 | path = suggest
22 | docinfo = extern
23 | charset_type = utf-8
24 | }
25 |
26 |
27 | indexer
28 | {
29 | mem_limit = 32M
30 | }
31 |
32 |
33 | searchd
34 | {
35 | log = searchd.log
36 | query_log = query.log
37 | read_timeout = 5
38 | max_children = 30
39 | pid_file = searchd.pid
40 | max_matches = 1000
41 | seamless_rotate = 1
42 | preopen_indexes = 0
43 | unlink_old = 1
44 | }
45 |
--------------------------------------------------------------------------------
/sphinx/misc/suggest/suggest.php:
--------------------------------------------------------------------------------
1 | SetRankingMode ( SPH_RANK_WORDCOUNT );
85 | $cl->SetFilterRange ( "len", $len-$delta, $len+$delta );
86 | $cl->SetSelect ( "*, @weight+$delta-abs(len-$len) AS myrank" );
87 | $cl->SetSortMode ( SPH_SORT_EXTENDED, "myrank DESC, freq DESC" );
88 | $cl->SetArrayResult ( true );
89 |
90 | // pull top-N best trigram matches and run them through Levenshtein
91 | $cl->SetLimits ( 0, TOP_COUNT );
92 | $res = $cl->Query ( $query, "suggest" );
93 |
94 | if ( !$res || !$res["matches"] )
95 | return false;
96 |
97 | if ( SUGGEST_DEBUG )
98 | {
99 | print "--- DEBUG START ---\n";
100 |
101 | foreach ( $res["matches"] as $match )
102 | {
103 | $w = $match["attrs"]["keyword"];
104 | $myrank = @$match["attrs"]["myrank"];
105 | if ( $myrank )
106 | $myrank = ", myrank=$myrank";
107 |
108 | // FIXME? add costs?
109 | // FIXME! does not work with UTF-8.. THIS! IS!! PHP!!!
110 | $levdist = levenshtein ( $keyword, $w );
111 |
112 | print "id=$match[id], weight=$match[weight], freq={$match[attrs][freq]}{$myrank}, word=$w, levdist=$levdist\n";
113 | }
114 |
115 | print "--- DEBUG END ---\n";
116 | }
117 |
118 | // further restrict trigram matches with a sane Levenshtein distance limit
119 | foreach ( $res["matches"] as $match )
120 | {
121 | $suggested = $match["attrs"]["keyword"];
122 | if ( levenshtein ( $keyword, $suggested )<=LEVENSHTEIN_THRESHOLD )
123 | return $suggested;
124 | }
125 | return $keyword;
126 | }
127 |
128 | /// main
129 | if ( $_SERVER["argc"]<2 )
130 | {
131 | die ( "usage:\n"
132 | . "php suggest.php --builddict\treads stopwords from stdin, prints SQL dump of the dictionary to stdout\n"
133 | . "php suggest.php --query WORD\tqueries Sphinx, prints suggestion\n" );
134 | }
135 |
136 | if ( $_SERVER["argv"][1]=="--builddict" )
137 | {
138 | $in = fopen ( "php://stdin", "r" );
139 | $out = fopen ( "php://stdout", "w+" );
140 | BuildDictionarySQL ( $out, $in );
141 | }
142 |
143 | if ( $_SERVER["argv"][1]=="--query" )
144 | {
145 | mysql_connect ( "localhost", "root", "" ) or die ( "mysql_connect() failed: ".mysql_error() );
146 | mysql_select_db ( "test" ) or die ( "mysql_select_db() failed: ".mysql_error() );
147 |
148 | $keyword = $_SERVER["argv"][2];
149 | printf ( "keyword: %s\nsuggestion: %s\n", $keyword, MakeSuggestion($keyword) );
150 | }
151 |
--------------------------------------------------------------------------------
/sphinx/misc/wordbreak.pl:
--------------------------------------------------------------------------------
1 | #
2 | # prepare titles for wordbreaker frequency dictionary builder
3 | # extract and cleanup data
4 | #
5 |
6 | #
7 | # usage example:
8 | #
9 | # perl wordbreak.pl < raw.xml > titles.xml
10 | # indexer ub --buildstops titles-freq.txt 10000000 --buildfreqs
11 | #
12 | # sphinx.conf:
13 | #
14 | # source ub
15 | # {
16 | # type = xmlpipe2
17 | # xmlpipe_field = title
18 | # xmlpipe_fixup_utf8 = 1
19 | # xmlpipe_command = cat titles.xml
20 | # }
21 | #
22 | # index ub
23 | # {
24 | # dict = keywords
25 | # type = plain
26 | # source = ub
27 | # path = ub
28 | # charset_type = utf-8
29 | # html_strip = 0
30 | # charset_table = A..Z->a..z, a..z
31 | # }
32 | #
33 |
34 | $n = 1;
35 | print "\n";
36 | print "\n";
37 | while (<>)
38 | {
39 | # extract title
40 | next if (!/^\s*/);
41 | chomp;
42 |
43 | # cleanup ABC's as in World's
44 | s/[a-z]\'s\b//ig;
45 |
46 | # cleanup A.B.C. as in S.r.l. and other abbreviations
47 | s/\b([a-z]\.){2,}\b//ig;
48 |
49 | # cleanup A&B as in H&M
50 | s/\b[a-z]\&[a-z]\b//ig;
51 |
52 | # cleanup ABC.com as in google.com, brisbanetimes.com.au, etc
53 | s/\b\w+(\.(com|org|net))*\.(com|org|net|it|de|pl|co\.uk|nl|edu|eu|info|fr|ch|br|ru|at|ca|si|tv|es|gov|br|au|jp|biz|dk|il|se|cz|no)\b//ig;
54 |
55 | # print out cleaned up document
56 | print "";
57 | print;
58 | print "\n";
59 | $n++;
60 | }
61 | print "\n";
62 |
--------------------------------------------------------------------------------
/sphinx/src/sphinxudf.c:
--------------------------------------------------------------------------------
1 | //
2 | // $Id$
3 | //
4 |
5 | //
6 | // Copyright (c) 2011-2016, Andrew Aksyonoff
7 | // Copyright (c) 2011-2016, Sphinx Technologies Inc
8 | // All rights reserved
9 | //
10 | // This program is free software; you can redistribute it and/or modify
11 | // it under the terms of the GNU General Public License. You should have
12 | // received a copy of the GPL license along with this program; if you
13 | // did not, you can find it at http://www.gnu.org/
14 | //
15 |
16 | //
17 | // Sphinx UDF helpers implementation
18 | //
19 |
20 | #include "sphinxudf.h"
21 |
22 | #include
23 | #include
24 |
25 | #define SPH_UDF_MAX_FIELD_FACTORS 256
26 | #define SPH_UDF_MAX_TERM_FACTORS 2048
27 | #define SPH_UDF_MAX_FIELD_SIZE 8 ///< how many ints to store a bitmask for 256 fields?
28 |
29 | /// helper function that must be called to initialize the SPH_UDF_FACTORS structure
30 | /// before it is passed to sphinx_factors_unpack
31 | /// returns 0 on success
32 | /// returns an error code on error
33 | int sphinx_factors_init ( SPH_UDF_FACTORS * out )
34 | {
35 | if ( !out )
36 | return 1;
37 |
38 | memset ( out, 0, sizeof(SPH_UDF_FACTORS) );
39 | return 0;
40 | }
41 |
42 |
43 | /// helper function that unpacks PACKEDFACTORS() blob into SPH_UDF_FACTORS structure
44 | /// MUST be in sync with PackFactors() method in sphinxsearch.cpp
45 | /// returns 0 on success
46 | /// returns an error code on error
47 | int sphinx_factors_unpack ( const unsigned int * in, SPH_UDF_FACTORS * out )
48 | {
49 | const unsigned int * pack = in;
50 | SPH_UDF_FIELD_FACTORS * f;
51 | SPH_UDF_TERM_FACTORS * t;
52 | int i, size, fields, fields_size;
53 | unsigned int exact_hit_mask[SPH_UDF_MAX_FIELD_SIZE];
54 | unsigned int exact_order_mask[SPH_UDF_MAX_FIELD_SIZE];
55 | unsigned int exact_field_hit_mask[SPH_UDF_MAX_FIELD_SIZE];
56 | unsigned int full_field_hit_mask[SPH_UDF_MAX_FIELD_SIZE];
57 |
58 | if ( !in || !out )
59 | return 1;
60 |
61 | if ( out->field || out->term )
62 | return 1;
63 |
64 | // extract size, extract document-level factors
65 | size = *in++;
66 |
67 | out->doc_bm15 = *in++;
68 | out->doc_bm25a = *(float*)in++;
69 | out->matched_fields = *in++;
70 | out->doc_word_count = *in++;
71 | out->num_fields = *in++;
72 |
73 | // extract field-level factors
74 | if ( out->num_fields > SPH_UDF_MAX_FIELD_FACTORS )
75 | return 1;
76 |
77 | fields_size = ( out->num_fields + 31 ) / 32;
78 |
79 | for ( i=0; inum_fields > 0 )
92 | {
93 | i = out->num_fields*sizeof(SPH_UDF_FIELD_FACTORS);
94 | out->field = (SPH_UDF_FIELD_FACTORS*) malloc ( i );
95 | memset ( out->field, 0, i );
96 | }
97 |
98 | for ( i=0; inum_fields; i++ )
99 | {
100 | f = &(out->field[i]);
101 | f->hit_count = *in++;
102 |
103 | if ( f->hit_count )
104 | {
105 | f->id = *in++;
106 | f->lcs = *in++;
107 | f->word_count = *in++;
108 | f->tf_idf = *(float*)in++;
109 | f->min_idf = *(float*)in++;
110 | f->max_idf = *(float*)in++;
111 | f->sum_idf = *(float*)in++;
112 | f->min_hit_pos = (int)*in++;
113 | f->min_best_span_pos = (int)*in++;
114 | f->max_window_hits = (int)*in++;
115 | f->min_gaps = (int)*in++;
116 | f->atc = *(float*)in++;
117 | f->lccs = *in++;
118 | f->wlccs = *(float*)in++;
119 | f->exact_hit = (char)( ( exact_hit_mask [ i>>5 ] & ( 1UL<<( i&31 ) ) )!=0 );
120 | f->exact_order = (char)( ( exact_order_mask [ i>>5 ] & ( 1UL<<( i&31 ) ) )!=0 );
121 | f->exact_field_hit = (char)( ( exact_field_hit_mask [ i>>5 ] & ( 1UL<<( i&31 ) ) )!=0 );
122 | f->full_field_hit = (char)( ( full_field_hit_mask [ i>>5 ] & ( 1UL<<( i&31 ) ) )!=0 );
123 | } else
124 | {
125 | // everything else is already zeroed out by memset() above
126 | f->id = i;
127 | }
128 | }
129 |
130 | // extract term-level factors
131 | out->max_uniq_qpos = *in++;
132 | if ( out->max_uniq_qpos > SPH_UDF_MAX_TERM_FACTORS )
133 | return 1;
134 |
135 | if ( out->max_uniq_qpos > 0 )
136 | out->term = (SPH_UDF_TERM_FACTORS*) malloc ( out->max_uniq_qpos*sizeof(SPH_UDF_TERM_FACTORS) );
137 |
138 | for ( i=0; imax_uniq_qpos; i++ )
139 | {
140 | t = &(out->term[i]);
141 | t->keyword_mask = *in++;
142 | if ( t->keyword_mask )
143 | {
144 | t->id = *in++;
145 | t->tf = (int)*in++;
146 | t->idf = *(float*)in++;
147 | }
148 | }
149 |
150 | // extract field_tf factors
151 | fields = *in++;
152 | out->field_tf = (int*) malloc ( fields*sizeof(int) );
153 | memcpy ( out->field_tf, in, fields*sizeof(int) );
154 | in += fields;
155 |
156 | // do a safety check, and return
157 | return ( size!=( (int)(in-pack) * (int)sizeof(unsigned int) ) ) ? 1 : 0;
158 | }
159 |
160 |
161 | /// helper function that must be called to free the memory allocated by the sphinx_factors_unpack
162 | /// function call
163 | /// returns 0 on success
164 | /// returns an error code on error
165 | int sphinx_factors_deinit ( SPH_UDF_FACTORS * out )
166 | {
167 | if ( !out )
168 | return 1;
169 |
170 | free ( out->term );
171 | free ( out->field );
172 | free ( out->field_tf );
173 |
174 | return 0;
175 | }
176 |
177 | //////////////////////////////////////////////////////////////////////////
178 |
179 | static const unsigned int * skip_fields ( const unsigned int * in, int n )
180 | {
181 | in += 6 + ( ( in[5] + 31 ) / 32 ) * 4; // skip heading document factors and 4 exact/full masks
182 | while ( n-->0 )
183 | in += ( in[0]>0 ) ? 15 : 1; // skip 15 ints per matched field, or 1 per unmatched
184 | return in;
185 | }
186 |
187 |
188 | static const unsigned int * skip_terms ( const unsigned int * in, int n )
189 | {
190 | in += 1; // skip max_uniq_qpos
191 | while ( n-->0 )
192 | in += ( in[0]>0 ) ? 4 : 1; // skip 4 ints per matched term, or 1 per unmatched
193 | return in;
194 | }
195 |
196 |
197 | const unsigned int * sphinx_get_field_factors ( const unsigned int * in, int field )
198 | {
199 | if ( !in || field<0 || field>=(int)in[5] )
200 | return NULL; // blob[5] is num_fields, do a sanity check
201 | in = skip_fields ( in, field );
202 | if ( !in[0] )
203 | return NULL; // no hits, no fun
204 | if ( (int)in[1]!=field )
205 | return NULL; // field[1] is field_id, do a sanity check
206 | return in; // all good
207 | }
208 |
209 |
210 | const unsigned int * sphinx_get_term_factors ( const unsigned int * in, int term )
211 | {
212 | if ( !in || term<0 )
213 | return NULL;
214 | in = skip_fields ( in, in[5] ); // skip all fields
215 | if ( term>(int)in[0] )
216 | return NULL; // sanity check vs max_uniq_qpos ( qpos and terms range - [1, max_uniq_qpos]
217 | in = skip_terms ( in, term-1);
218 | if ( !in[0] )
219 | return NULL; // unmatched term
220 | if ( (int)in[1]!=term )
221 | return NULL; // term[1] is keyword_id, sanity check failed
222 | return in;
223 | }
224 |
225 |
226 | int sphinx_get_doc_factor_int ( const unsigned int * in, enum sphinx_doc_factor f )
227 | {
228 | int fields_size;
229 | switch ( f )
230 | {
231 | case SPH_DOCF_BM15: return (int)in[1];
232 | case SPH_DOCF_BM25A: return (int)in[2];
233 | case SPH_DOCF_MATCHED_FIELDS: return (int)in[3];
234 | case SPH_DOCF_DOC_WORD_COUNT: return (int)in[4];
235 | case SPH_DOCF_NUM_FIELDS: return (int)in[5];
236 | case SPH_DOCF_MAX_UNIQ_QPOS:
237 | in = skip_fields ( in, in[5] );
238 | return (int)in[0];
239 | case SPH_DOCF_EXACT_HIT_MASK: return (int)in[6];
240 | case SPH_DOCF_EXACT_ORDER_MASK: fields_size = ( (int)in[5] + 31 ) / 32; return (int)in[6+fields_size];
241 | case SPH_DOCF_EXACT_FIELD_HIT_MASK: fields_size = ( (int)in[5] + 31 ) / 32; return (int)in[6+fields_size*2];
242 | case SPH_DOCF_FULL_FIELD_HIT_MASK: fields_size = ( (int)in[5] + 31 ) / 32; return (int)in[6+fields_size*3];
243 | }
244 | return 0;
245 | }
246 |
247 | const unsigned int * sphinx_get_doc_factor_ptr ( const unsigned int * in, enum sphinx_doc_factor f )
248 | {
249 | int fields_size;
250 |
251 | if ( f==SPH_DOCF_EXACT_HIT_MASK )
252 | return in + 6;
253 |
254 | fields_size = ( (int)in[5] + 31 ) / 32;
255 | if ( f==SPH_DOCF_EXACT_ORDER_MASK )
256 | return in + 6 + fields_size;
257 |
258 | if ( f==SPH_DOCF_EXACT_FIELD_HIT_MASK )
259 | return in + 6 + fields_size*2;
260 |
261 | if ( f==SPH_DOCF_FULL_FIELD_HIT_MASK )
262 | return in + 6 + fields_size*3;
263 |
264 | return 0;
265 | }
266 |
267 | float sphinx_get_doc_factor_float ( const unsigned int * in, enum sphinx_doc_factor f )
268 | {
269 | if ( f==SPH_DOCF_BM25A )
270 | return *(float*)&in[2];
271 | else
272 | return 0.0f;
273 | }
274 |
275 |
276 | int sphinx_get_field_factor_int ( const unsigned int * in, enum sphinx_field_factor f )
277 | {
278 | if ( !in )
279 | return 0;
280 | // in[1] is id
281 | switch ( f )
282 | {
283 | case SPH_FIELDF_HIT_COUNT: return (int)in[0];
284 | case SPH_FIELDF_LCS: return (int)in[2];
285 | case SPH_FIELDF_WORD_COUNT: return (int)in[3];
286 | case SPH_FIELDF_TF_IDF: return (int)in[4];
287 | case SPH_FIELDF_MIN_IDF: return (int)in[5];
288 | case SPH_FIELDF_MAX_IDF: return (int)in[6];
289 | case SPH_FIELDF_SUM_IDF: return (int)in[7];
290 | case SPH_FIELDF_MIN_HIT_POS: return (int)in[8];
291 | case SPH_FIELDF_MIN_BEST_SPAN_POS: return (int)in[9];
292 | case SPH_FIELDF_MAX_WINDOW_HITS: return (int)in[10];
293 | case SPH_FIELDF_MIN_GAPS: return (int)in[11];
294 | case SPH_FIELDF_ATC: return (int)in[12];
295 | case SPH_FIELDF_LCCS: return (int)in[13];
296 | case SPH_FIELDF_WLCCS: return (int)in[14];
297 | }
298 | return 0;
299 | }
300 |
301 |
302 | int sphinx_get_term_factor_int ( const unsigned int * in, enum sphinx_term_factor f )
303 | {
304 | if ( !in )
305 | return 0;
306 | switch ( f )
307 | {
308 | case SPH_TERMF_KEYWORD_MASK: return (int)in[0];
309 | case SPH_TERMF_TF: return (int)in[2];
310 | case SPH_TERMF_IDF: return (int)in[3];
311 | }
312 | return 0;
313 | }
314 |
315 |
316 | float sphinx_get_field_factor_float ( const unsigned int * in, enum sphinx_field_factor f )
317 | {
318 | int r = sphinx_get_field_factor_int ( in, f );
319 | void * pvoid = &r;
320 | return *(float*)pvoid;
321 | }
322 |
323 |
324 | float sphinx_get_term_factor_float ( const unsigned int * in, enum sphinx_term_factor f )
325 | {
326 | int r = sphinx_get_term_factor_int ( in, f );
327 | void * pvoid = &r;
328 | return *(float*)pvoid;
329 | }
330 |
331 | //
332 | // $Id$
333 | //
334 |
--------------------------------------------------------------------------------
/sphinx/src/sphinxudf.h:
--------------------------------------------------------------------------------
1 | //
2 | // $Id$
3 | //
4 |
5 | //
6 | // Copyright (c) 2011-2016, Andrew Aksyonoff
7 | // Copyright (c) 2011-2016, Sphinx Technologies Inc
8 | // All rights reserved
9 | //
10 | // This program is free software; you can redistribute it and/or modify
11 | // it under the terms of the GNU General Public License. You should have
12 | // received a copy of the GPL license along with this program; if you
13 | // did not, you can find it at http://www.gnu.org/
14 | //
15 |
16 | //
17 | // Sphinx plugin interface header
18 | //
19 | // This file will be included by plugin implementations, so it should be
20 | // portable plain C, stay standalone, and change as rarely as possible.
21 | //
22 | // Refer to src/udfexample.c for a working UDF example, and refer to
23 | // doc/sphinx.html#extending-sphinx for more information on writing
24 | // plugins and UDFs.
25 | //
26 |
27 | #ifndef _sphinxudf_
28 | #define _sphinxudf_
29 |
30 | #ifdef __cplusplus
31 | extern "C" {
32 | #endif
33 |
34 | /// current udf version
35 | #define SPH_UDF_VERSION 12
36 |
37 | /// error buffer size
38 | #define SPH_UDF_ERROR_LEN 256
39 |
40 | //////////////////////////////////////////////////////////////////////////
41 | // UDF PLUGINS
42 | //////////////////////////////////////////////////////////////////////////
43 |
44 | /// UDF argument and result value types
45 | enum sphinx_udf_argtype
46 | {
47 | SPH_UDF_TYPE_UINT32 = 1, ///< unsigned 32-bit integer
48 | SPH_UDF_TYPE_UINT32SET = 2, ///< sorted set of unsigned 32-bit integers
49 | SPH_UDF_TYPE_INT64 = 3, ///< signed 64-bit integer
50 | SPH_UDF_TYPE_FLOAT = 4, ///< single-precision IEEE 754 float
51 | SPH_UDF_TYPE_STRING = 5, ///< non-ASCIIZ string, with a separately stored length
52 | SPH_UDF_TYPE_INT64SET = 6, ///< sorted set of signed 64-bit integers
53 | SPH_UDF_TYPE_FACTORS = 7, ///< packed ranking factors
54 | SPH_UDF_TYPE_JSON = 8 ///< whole json or particular field as a string
55 | };
56 |
57 | /// our malloc() replacement type
58 | /// results that are returned to searchd MUST be allocated using this replacement
59 | typedef void * sphinx_malloc_fn ( int );
60 |
61 | /// UDF call arguments
62 | typedef struct st_sphinx_udf_args
63 | {
64 | int arg_count; ///< number of arguments
65 | enum sphinx_udf_argtype * arg_types; ///< argument types
66 | char ** arg_values; ///< argument values (strings are not (!) ASCIIZ; see str_lengths below)
67 | char ** arg_names; ///< argument names (ASCIIZ argname in 'expr AS argname' case; NULL otherwise)
68 | int * str_lengths; ///< string argument lengths
69 | sphinx_malloc_fn * fn_malloc; ///< malloc() replacement to allocate returned values
70 | } SPH_UDF_ARGS;
71 |
72 | /// UDF initialization
73 | typedef struct st_sphinx_udf_init
74 | {
75 | void * func_data; ///< function data (will be passed to calls, deinit)
76 | char is_const; ///< whether a function returns a constant
77 | } SPH_UDF_INIT;
78 |
79 | /// integer return types
80 | #if defined(_MSC_VER) || defined(__WIN__)
81 | typedef __int64 sphinx_int64_t;
82 | typedef unsigned __int64 sphinx_uint64_t;
83 | #else
84 | typedef long long sphinx_int64_t;
85 | typedef unsigned long long sphinx_uint64_t;
86 | #endif
87 |
88 | //////////////////////////////////////////////////////////////////////////
89 |
90 | /// ranking factors interface, v1
91 | /// functions that unpack PACKEDFACTORS() blob into a few helper C structures
92 | /// slower because of malloc()s and copying, but easier to use
93 |
94 | /// unpacked representation of all the field-level ranking factors
95 | typedef struct st_sphinx_field_factors
96 | {
97 | unsigned int hit_count;
98 | unsigned int id;
99 | unsigned int lcs;
100 | unsigned int word_count;
101 | float tf_idf;
102 | float min_idf;
103 | float max_idf;
104 | float sum_idf;
105 | int min_hit_pos;
106 | int min_best_span_pos;
107 | char exact_hit;
108 | int max_window_hits;
109 | int min_gaps; ///< added in v.3
110 | char exact_order; ///< added in v.4
111 | float atc; ///< added in v.4
112 | int lccs; ///< added in v.5
113 | float wlccs; ///< added in v.5
114 | char exact_field_hit; ///< added in v.10
115 | char full_field_hit; ///< added in v.11
116 | } SPH_UDF_FIELD_FACTORS;
117 |
118 | /// unpacked representation of all the term-level ranking factors
119 | typedef struct st_sphinx_term_factors
120 | {
121 | unsigned int keyword_mask;
122 | unsigned int id;
123 | int tf;
124 | float idf;
125 | } SPH_UDF_TERM_FACTORS;
126 |
127 | /// unpacked representation of all the ranking factors (document, field, and term-level)
128 | typedef struct st_sphinx_factors
129 | {
130 | int doc_bm15;
131 | float doc_bm25a;
132 | unsigned int matched_fields;
133 | int doc_word_count;
134 | int num_fields;
135 | int max_uniq_qpos;
136 |
137 | SPH_UDF_FIELD_FACTORS * field;
138 | SPH_UDF_TERM_FACTORS * term;
139 | int * field_tf;
140 | } SPH_UDF_FACTORS;
141 |
142 | /// helper function that must be called to initialize the SPH_UDF_FACTORS structure
143 | /// before it is passed to sphinx_factors_unpack
144 | /// returns 0 on success
145 | /// returns an error code on error
146 | int sphinx_factors_init ( SPH_UDF_FACTORS * out );
147 |
148 | /// helper function that unpacks PACKEDFACTORS() blob into SPH_UDF_FACTORS structure
149 | /// MUST be in sync with PackFactors() method in sphinxsearch.cpp
150 | /// returns 0 on success
151 | /// returns an error code on error
152 | int sphinx_factors_unpack ( const unsigned int * in, SPH_UDF_FACTORS * out );
153 |
154 | /// helper function that must be called to free the memory allocated by the sphinx_factors_unpack
155 | /// function call
156 | /// returns 0 on success
157 | /// returns an error code on error
158 | int sphinx_factors_deinit ( SPH_UDF_FACTORS * out );
159 |
160 | //////////////////////////////////////////////////////////////////////////
161 |
162 | /// ranking factors interface, v2
163 | /// functions that access factor values directly in the PACKEDFACTORS() blob
164 | ///
165 | /// faster, as no memory allocations are guaranteed, but type-punned
166 | /// meaning that you have to call a proper get_xxx_factor_int() or xxx_float() variant
167 | /// the accessor functions themselves will NOT perform any type checking or conversions
168 | /// or in other words, sphinx_get_field_factor_int() on a float factor like min_idf is legal,
169 | /// but returns "garbage" (floating value from the blob reinterpreted as an integer)
170 |
171 | enum sphinx_doc_factor
172 | {
173 | SPH_DOCF_BM15 = 1, ///< int
174 | SPH_DOCF_BM25A = 2, ///< float
175 | SPH_DOCF_MATCHED_FIELDS = 3, ///< unsigned int
176 | SPH_DOCF_DOC_WORD_COUNT = 4, ///< int
177 | SPH_DOCF_NUM_FIELDS = 5, ///< int
178 | SPH_DOCF_MAX_UNIQ_QPOS = 6, ///< int
179 | SPH_DOCF_EXACT_HIT_MASK = 7, ///< unsigned int
180 | SPH_DOCF_EXACT_ORDER_MASK = 8, ///< v.4, unsigned int
181 | SPH_DOCF_EXACT_FIELD_HIT_MASK = 9, ///< v.10, unsigned int
182 | SPH_DOCF_FULL_FIELD_HIT_MASK = 10 ///< v.11, unsigned int
183 | };
184 |
185 | enum sphinx_field_factor
186 | {
187 | SPH_FIELDF_HIT_COUNT = 1, ///< unsigned int
188 | SPH_FIELDF_LCS = 2, ///< unsigned int
189 | SPH_FIELDF_WORD_COUNT = 3, ///< unsigned int
190 | SPH_FIELDF_TF_IDF = 4, ///< float
191 | SPH_FIELDF_MIN_IDF = 5, ///< float
192 | SPH_FIELDF_MAX_IDF = 6, ///< float
193 | SPH_FIELDF_SUM_IDF = 7, ///< float
194 | SPH_FIELDF_MIN_HIT_POS = 8, ///< int
195 | SPH_FIELDF_MIN_BEST_SPAN_POS = 9, ///< int
196 | SPH_FIELDF_MAX_WINDOW_HITS = 10, ///< int
197 | SPH_FIELDF_MIN_GAPS = 11, ///< v.3, int
198 | SPH_FIELDF_ATC = 12, ///< v.4, float
199 | SPH_FIELDF_LCCS = 13, ///< v.5, int
200 | SPH_FIELDF_WLCCS = 14 ///< v.5, float
201 | };
202 |
203 | enum sphinx_term_factor
204 | {
205 | SPH_TERMF_KEYWORD_MASK = 1, ///< unsigned int
206 | SPH_TERMF_TF = 2, ///< int
207 | SPH_TERMF_IDF = 3 ///< float
208 | };
209 |
210 | /// returns a pointer to the field factors, or NULL for a non-matched field index
211 | const unsigned int * sphinx_get_field_factors ( const unsigned int * in, int field );
212 |
213 | /// returns a pointer to the term factors, or NULL for a non-matched field index
214 | const unsigned int * sphinx_get_term_factors ( const unsigned int * in, int term );
215 |
216 | /// returns a document factor value, interpreted as integer
217 | int sphinx_get_doc_factor_int ( const unsigned int * in, enum sphinx_doc_factor f );
218 |
219 | /// returns a document factor value, interpreted as float
220 | float sphinx_get_doc_factor_float ( const unsigned int * in, enum sphinx_doc_factor f );
221 |
222 | /// returns a field factor value, interpreted as integer
223 | int sphinx_get_field_factor_int ( const unsigned int * in, enum sphinx_field_factor f );
224 |
225 | /// returns a field factor value, interpreted as float
226 | float sphinx_get_field_factor_float ( const unsigned int * in, enum sphinx_field_factor f );
227 |
228 | /// returns a term factor value, interpreted as integer
229 | int sphinx_get_term_factor_int ( const unsigned int * in, enum sphinx_term_factor f );
230 |
231 | /// returns a term factor value, interpreted as float
232 | float sphinx_get_term_factor_float ( const unsigned int * in, enum sphinx_term_factor f );
233 |
234 | /// returns a pointer to document factor value, interpreted as vector of integers
235 | const unsigned int * sphinx_get_doc_factor_ptr ( const unsigned int * in, enum sphinx_doc_factor f );
236 |
237 | //////////////////////////////////////////////////////////////////////////
238 | // RANKER PLUGINS
239 | //////////////////////////////////////////////////////////////////////////
240 |
241 | /// ranker plugin intialization info
242 | typedef struct st_plugin_rankerinfo
243 | {
244 | int num_field_weights;
245 | int * field_weights;
246 | const char * options;
247 | unsigned int payload_mask;
248 | int num_query_words;
249 | int max_qpos;
250 | } SPH_RANKER_INIT;
251 |
252 | /// a structure that represents a hit
253 | typedef struct st_plugin_hit
254 | {
255 | sphinx_uint64_t doc_id;
256 | unsigned int hit_pos;
257 | unsigned short query_pos;
258 | unsigned short node_pos;
259 | unsigned short span_length;
260 | unsigned short match_length;
261 | unsigned int weight;
262 | unsigned int query_pos_mask;
263 | } SPH_RANKER_HIT;
264 |
265 | #ifdef __cplusplus
266 | }
267 | #endif
268 |
269 | #endif // _sphinxudf_
270 |
271 | //
272 | // $Id$
273 | //
274 |
--------------------------------------------------------------------------------
/sphinx/src/udfexample.c:
--------------------------------------------------------------------------------
1 | //
2 | // $Id$
3 | //
4 |
5 | //
6 | // Sphinx UDF function example
7 | //
8 | // Linux
9 | // gcc -fPIC -shared -o udfexample.so udfexample.c
10 | // CREATE FUNCTION sequence RETURNS INT SONAME 'udfexample.so';
11 | // CREATE FUNCTION strtoint RETURNS INT SONAME 'udfexample.so';
12 | // CREATE FUNCTION avgmva RETURNS FLOAT SONAME 'udfexample.so';
13 | // CREATE FUNCTION failtest RETURNS STRING SONAME 'udfexample.so';
14 | //
15 | // Windows
16 | // cl /MTd /LD udfexample.c
17 | // CREATE FUNCTION sequence RETURNS INT SONAME 'udfexample.dll';
18 | // CREATE FUNCTION strtoint RETURNS INT SONAME 'udfexample.dll';
19 | // CREATE FUNCTION avgmva RETURNS FLOAT SONAME 'udfexample.dll';
20 | // CREATE FUNCTION failtest RETURNS STRING SONAME 'udfexample.dll';
21 | //
22 |
23 | #include "sphinxudf.h"
24 | #include
25 | #include
26 | #include
27 |
28 | #ifdef _MSC_VER
29 | #define snprintf _snprintf
30 | #define DLLEXPORT __declspec(dllexport)
31 | #else
32 | #define DLLEXPORT
33 | #endif
34 |
35 | /// UDF version control
36 | /// gets called once when the library is loaded
37 | DLLEXPORT int udfexample_ver ()
38 | {
39 | return SPH_UDF_VERSION;
40 | }
41 |
42 |
43 | /// UDF re-initialization func
44 | /// gets called on sighup (workers=prefork only)
45 | DLLEXPORT void udfexample_reinit ()
46 | {
47 | }
48 |
49 |
50 | /// UDF initialization
51 | /// gets called on every query, when query begins
52 | /// args are filled with values for a particular query
53 | DLLEXPORT int sequence_init ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
54 | {
55 | // check argument count
56 | if ( args->arg_count > 1 )
57 | {
58 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "SEQUENCE() takes either 0 or 1 arguments" );
59 | return 1;
60 | }
61 |
62 | // check argument type
63 | if ( args->arg_count && args->arg_types[0]!=SPH_UDF_TYPE_UINT32 )
64 | {
65 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "SEQUENCE() requires 1st argument to be uint" );
66 | return 1;
67 | }
68 |
69 | // allocate and init counter storage
70 | init->func_data = (void*) malloc ( sizeof(int) );
71 | if ( !init->func_data )
72 | {
73 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "malloc() failed" );
74 | return 1;
75 | }
76 | *(int*)init->func_data = 1;
77 |
78 | // all done
79 | return 0;
80 | }
81 |
82 |
83 | /// UDF deinitialization
84 | /// gets called on every query, when query ends
85 | DLLEXPORT void sequence_deinit ( SPH_UDF_INIT * init )
86 | {
87 | // deallocate storage
88 | if ( init->func_data )
89 | {
90 | free ( init->func_data );
91 | init->func_data = NULL;
92 | }
93 | }
94 |
95 |
96 | /// UDF implementation
97 | /// gets called for every row, unless optimized away
98 | DLLEXPORT sphinx_int64_t sequence ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
99 | {
100 | int res = (*(int*)init->func_data)++;
101 | if ( args->arg_count )
102 | res += *(int*)args->arg_values[0];
103 | return res;
104 | }
105 |
106 | //////////////////////////////////////////////////////////////////////////
107 |
108 | DLLEXPORT int strtoint_init ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
109 | {
110 | if ( args->arg_count!=1 || args->arg_types[0]!=SPH_UDF_TYPE_STRING )
111 | {
112 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "STRTOINT() requires 1 string argument" );
113 | return 1;
114 | }
115 | return 0;
116 | }
117 |
118 | DLLEXPORT sphinx_int64_t strtoint ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
119 | {
120 | const char * s = args->arg_values[0];
121 | int len = args->str_lengths[0], res = 0;
122 |
123 | while ( len>0 && *s>='0' && *s<='9' )
124 | {
125 | res += *s - '0';
126 | len--;
127 | }
128 |
129 | return res;
130 | }
131 |
132 | //////////////////////////////////////////////////////////////////////////
133 |
134 | DLLEXPORT int avgmva_init ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
135 | {
136 | if ( args->arg_count!=1 ||
137 | ( args->arg_types[0]!=SPH_UDF_TYPE_UINT32SET && args->arg_types[0]!=SPH_UDF_TYPE_INT64SET ) )
138 | {
139 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "AVGMVA() requires 1 MVA argument" );
140 | return 1;
141 | }
142 |
143 | // store our mva vs mva64 flag to func_data
144 | init->func_data = (void*)(int)( args->arg_types[0]==SPH_UDF_TYPE_INT64SET ? 1 : 0 );
145 | return 0;
146 | }
147 |
148 | DLLEXPORT double avgmva ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
149 | {
150 | unsigned int * mva = (unsigned int *) args->arg_values[0];
151 | double res = 0;
152 | int i, n, is64;
153 |
154 | if ( !mva )
155 | return res;
156 |
157 | // Both MVA32 and MVA64 are stored as dword (unsigned 32-bit) arrays.
158 | // The first dword stores the array length (always in dwords too), and
159 | // the next ones store the values. In pseudocode:
160 | //
161 | // unsigned int num_dwords
162 | // unsigned int data [ num_dwords ]
163 | //
164 | // With MVA32, this lets you access the values pretty naturally.
165 | //
166 | // With MVA64, however, we have to do a few tricks:
167 | // a) divide num_dwords by 2 to get the number of 64-bit elements,
168 | // b) assemble those 64-bit values from dword pairs.
169 | //
170 | // The latter is required for architectures where non-aligned
171 | // 64-bit access crashes. On Intel, we could have also done it
172 | // like this:
173 | //
174 | // int * raw_ptr = (int*) args->arg_values[0];
175 | // int mva64_count = (*raw_ptr) / 2;
176 | // sphinx_uint64_t * mva64_values = (sphinx_uint64_t*)(raw_ptr + 1);
177 |
178 | // pull "mva32 or mva64" flag (that we stored in _init) from func_data
179 | is64 = (int)(init->func_data) != 0;
180 | if ( is64 )
181 | {
182 | // handle mva64
183 | n = *mva++ / 2;
184 | for ( i=0; iarg_count != 0 )
205 | {
206 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "FAILTEST() takes 0 arguments" );
207 | return 1;
208 | }
209 |
210 | // allocate counter storage, init counter
211 | init->func_data = (void*) malloc ( sizeof(int) );
212 | if ( !init->func_data )
213 | {
214 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "malloc() failed" );
215 | return 1;
216 | }
217 | *(int*)init->func_data = 1;
218 |
219 | return 0;
220 | }
221 |
222 | DLLEXPORT char * failtest ( SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message )
223 | {
224 | int a = (*(int*)init->func_data)++;
225 |
226 | if ( a < 4 )
227 | {
228 | // IMPORTANT!
229 | // note that strings we return to Sphinx MUST be allocated using fn_malloc!
230 | char * res = args->fn_malloc ( 16 );
231 | snprintf ( res, 16, "val%d", a );
232 | return res;
233 | }
234 |
235 | // starting with UDF version 12, we can emit an error message from the main
236 | // UDF function too, and not just a 1-byte flag
237 | snprintf ( error_message, SPH_UDF_ERROR_LEN, "err%d", a );
238 | return NULL;
239 | }
240 |
241 | DLLEXPORT void failtest_deinit ( SPH_UDF_INIT * init )
242 | {
243 | // deallocate storage
244 | if ( init->func_data )
245 | {
246 | free ( init->func_data );
247 | init->func_data = NULL;
248 | }
249 | }
250 |
251 | // FIXME! add a ranker plugin example?
252 |
253 | //
254 | // $Id$
255 | //
256 |
--------------------------------------------------------------------------------
/sphinxapi.php:
--------------------------------------------------------------------------------
1 | =8 )
131 | {
132 | $v = (int)$v;
133 | return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
134 | }
135 |
136 | // x32, int
137 | if ( is_int($v) )
138 | return pack ( "NN", $v < 0 ? -1 : 0, $v );
139 |
140 | // x32, bcmath
141 | if ( function_exists("bcmul") )
142 | {
143 | if ( bccomp ( $v, 0 ) == -1 )
144 | $v = bcadd ( "18446744073709551616", $v );
145 | $h = bcdiv ( $v, "4294967296", 0 );
146 | $l = bcmod ( $v, "4294967296" );
147 | return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
148 | }
149 |
150 | // x32, no-bcmath
151 | $p = max(0, strlen($v) - 13);
152 | $lo = abs((float)substr($v, $p));
153 | $hi = abs((float)substr($v, 0, $p));
154 |
155 | $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
156 | $q = floor($m/4294967296.0);
157 | $l = $m - ($q*4294967296.0);
158 | $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
159 |
160 | if ( $v<0 )
161 | {
162 | if ( $l==0 )
163 | $h = 4294967296.0 - $h;
164 | else
165 | {
166 | $h = 4294967295.0 - $h;
167 | $l = 4294967296.0 - $l;
168 | }
169 | }
170 | return pack ( "NN", $h, $l );
171 | }
172 |
173 | /// pack 64-bit unsigned
174 | function sphPackU64 ( $v )
175 | {
176 | assert ( is_numeric($v) );
177 |
178 | // x64
179 | if ( PHP_INT_SIZE>=8 )
180 | {
181 | assert ( $v>=0 );
182 |
183 | // x64, int
184 | if ( is_int($v) )
185 | return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
186 |
187 | // x64, bcmath
188 | if ( function_exists("bcmul") )
189 | {
190 | $h = bcdiv ( $v, 4294967296, 0 );
191 | $l = bcmod ( $v, 4294967296 );
192 | return pack ( "NN", $h, $l );
193 | }
194 |
195 | // x64, no-bcmath
196 | $p = max ( 0, strlen($v) - 13 );
197 | $lo = (int)substr ( $v, $p );
198 | $hi = (int)substr ( $v, 0, $p );
199 |
200 | $m = $lo + $hi*1316134912;
201 | $l = $m % 4294967296;
202 | $h = $hi*2328 + (int)($m/4294967296);
203 |
204 | return pack ( "NN", $h, $l );
205 | }
206 |
207 | // x32, int
208 | if ( is_int($v) )
209 | return pack ( "NN", 0, $v );
210 |
211 | // x32, bcmath
212 | if ( function_exists("bcmul") )
213 | {
214 | $h = bcdiv ( $v, "4294967296", 0 );
215 | $l = bcmod ( $v, "4294967296" );
216 | return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
217 | }
218 |
219 | // x32, no-bcmath
220 | $p = max(0, strlen($v) - 13);
221 | $lo = (float)substr($v, $p);
222 | $hi = (float)substr($v, 0, $p);
223 |
224 | $m = $lo + $hi*1316134912.0;
225 | $q = floor($m / 4294967296.0);
226 | $l = $m - ($q * 4294967296.0);
227 | $h = $hi*2328.0 + $q;
228 |
229 | return pack ( "NN", $h, $l );
230 | }
231 |
232 | // unpack 64-bit unsigned
233 | function sphUnpackU64 ( $v )
234 | {
235 | list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
236 |
237 | if ( PHP_INT_SIZE>=8 )
238 | {
239 | if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
240 | if ( $lo<0 ) $lo += (1<<32);
241 |
242 | // x64, int
243 | if ( $hi<=2147483647 )
244 | return ($hi<<32) + $lo;
245 |
246 | // x64, bcmath
247 | if ( function_exists("bcmul") )
248 | return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
249 |
250 | // x64, no-bcmath
251 | $C = 100000;
252 | $h = ((int)($hi / $C) << 32) + (int)($lo / $C);
253 | $l = (($hi % $C) << 32) + ($lo % $C);
254 | if ( $l>$C )
255 | {
256 | $h += (int)($l / $C);
257 | $l = $l % $C;
258 | }
259 |
260 | if ( $h==0 )
261 | return $l;
262 | return sprintf ( "%d%05d", $h, $l );
263 | }
264 |
265 | // x32, int
266 | if ( $hi==0 )
267 | {
268 | if ( $lo>0 )
269 | return $lo;
270 | return sprintf ( "%u", $lo );
271 | }
272 |
273 | $hi = sprintf ( "%u", $hi );
274 | $lo = sprintf ( "%u", $lo );
275 |
276 | // x32, bcmath
277 | if ( function_exists("bcmul") )
278 | return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
279 |
280 | // x32, no-bcmath
281 | $hi = (float)$hi;
282 | $lo = (float)$lo;
283 |
284 | $q = floor($hi/10000000.0);
285 | $r = $hi - $q*10000000.0;
286 | $m = $lo + $r*4967296.0;
287 | $mq = floor($m/10000000.0);
288 | $l = $m - $mq*10000000.0;
289 | $h = $q*4294967296.0 + $r*429.0 + $mq;
290 |
291 | $h = sprintf ( "%.0f", $h );
292 | $l = sprintf ( "%07.0f", $l );
293 | if ( $h=="0" )
294 | return sprintf( "%.0f", (float)$l );
295 | return $h . $l;
296 | }
297 |
298 | // unpack 64-bit signed
299 | function sphUnpackI64 ( $v )
300 | {
301 | list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
302 |
303 | // x64
304 | if ( PHP_INT_SIZE>=8 )
305 | {
306 | if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
307 | if ( $lo<0 ) $lo += (1<<32);
308 |
309 | return ($hi<<32) + $lo;
310 | }
311 |
312 | // x32, int
313 | if ( $hi==0 )
314 | {
315 | if ( $lo>0 )
316 | return $lo;
317 | return sprintf ( "%u", $lo );
318 | }
319 | // x32, int
320 | elseif ( $hi==-1 )
321 | {
322 | if ( $lo<0 )
323 | return $lo;
324 | return sprintf ( "%.0f", $lo - 4294967296.0 );
325 | }
326 |
327 | $neg = "";
328 | $c = 0;
329 | if ( $hi<0 )
330 | {
331 | $hi = ~$hi;
332 | $lo = ~$lo;
333 | $c = 1;
334 | $neg = "-";
335 | }
336 |
337 | $hi = sprintf ( "%u", $hi );
338 | $lo = sprintf ( "%u", $lo );
339 |
340 | // x32, bcmath
341 | if ( function_exists("bcmul") )
342 | return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c );
343 |
344 | // x32, no-bcmath
345 | $hi = (float)$hi;
346 | $lo = (float)$lo;
347 |
348 | $q = floor($hi/10000000.0);
349 | $r = $hi - $q*10000000.0;
350 | $m = $lo + $r*4967296.0;
351 | $mq = floor($m/10000000.0);
352 | $l = $m - $mq*10000000.0 + $c;
353 | $h = $q*4294967296.0 + $r*429.0 + $mq;
354 | if ( $l==10000000 )
355 | {
356 | $l = 0;
357 | $h += 1;
358 | }
359 |
360 | $h = sprintf ( "%.0f", $h );
361 | $l = sprintf ( "%07.0f", $l );
362 | if ( $h=="0" )
363 | return $neg . sprintf( "%.0f", (float)$l );
364 | return $neg . $h . $l;
365 | }
366 |
367 |
368 | function sphFixUint ( $value )
369 | {
370 | if ( PHP_INT_SIZE>=8 )
371 | {
372 | // x64 route, workaround broken unpack() in 5.2.2+
373 | if ( $value<0 ) $value += (1<<32);
374 | return $value;
375 | }
376 | else
377 | {
378 | // x32 route, workaround php signed/unsigned braindamage
379 | return sprintf ( "%u", $value );
380 | }
381 | }
382 |
383 |
384 | /// sphinx searchd client class
385 | class SphinxClient
386 | {
387 | var $_host; ///< searchd host (default is "localhost")
388 | var $_port; ///< searchd port (default is 9312)
389 | var $_offset; ///< how many records to seek from result-set start (default is 0)
390 | var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
391 | var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
392 | var $_weights; ///< per-field weights (default is 1 for all fields)
393 | var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
394 | var $_sortby; ///< attribute to sort by (defualt is "")
395 | var $_min_id; ///< min ID to match (default is 0, which means no limit)
396 | var $_max_id; ///< max ID to match (default is 0, which means no limit)
397 | var $_filters; ///< search filters
398 | var $_groupby; ///< group-by attribute name
399 | var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with)
400 | var $_groupsort; ///< group-by sorting clause (to sort groups in result set with)
401 | var $_groupdistinct;///< group-by count-distinct attribute
402 | var $_maxmatches; ///< max matches to retrieve
403 | var $_cutoff; ///< cutoff to stop searching at (default is 0)
404 | var $_retrycount; ///< distributed retries count
405 | var $_retrydelay; ///< distributed retries delay
406 | var $_anchor; ///< geographical anchor point
407 | var $_indexweights; ///< per-index weights
408 | var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
409 | var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
410 | var $_fieldweights; ///< per-field-name weights
411 | var $_overrides; ///< per-query attribute values overrides
412 | var $_select; ///< select-list (attributes or expressions, with optional aliases)
413 |
414 | var $_error; ///< last error message
415 | var $_warning; ///< last warning message
416 | var $_connerror; ///< connection error vs remote error flag
417 |
418 | var $_reqs; ///< requests array for multi-query
419 | var $_mbenc; ///< stored mbstring encoding
420 | var $_arrayresult; ///< whether $result["matches"] should be a hash or an array
421 | var $_timeout; ///< connect timeout
422 |
423 | /////////////////////////////////////////////////////////////////////////////
424 | // common stuff
425 | /////////////////////////////////////////////////////////////////////////////
426 |
427 | /// create a new client object and fill defaults
428 | function SphinxClient ()
429 | {
430 | // per-client-object settings
431 | $this->_host = "localhost";
432 | $this->_port = 9321;
433 | $this->_path = false;
434 | $this->_socket = false;
435 |
436 | // per-query settings
437 | $this->_offset = 0;
438 | $this->_limit = 20;
439 | $this->_mode = SPH_MATCH_ALL;
440 | $this->_weights = array ();
441 | $this->_sort = SPH_SORT_RELEVANCE;
442 | $this->_sortby = "";
443 | $this->_min_id = 0;
444 | $this->_max_id = 0;
445 | $this->_filters = array ();
446 | $this->_groupby = "";
447 | $this->_groupfunc = SPH_GROUPBY_DAY;
448 | $this->_groupsort = "@group desc";
449 | $this->_groupdistinct= "";
450 | $this->_maxmatches = 1000;
451 | $this->_cutoff = 0;
452 | $this->_retrycount = 0;
453 | $this->_retrydelay = 0;
454 | $this->_anchor = array ();
455 | $this->_indexweights= array ();
456 | $this->_ranker = SPH_RANK_PROXIMITY_BM25;
457 | $this->_maxquerytime= 0;
458 | $this->_fieldweights= array();
459 | $this->_overrides = array();
460 | $this->_select = "*";
461 |
462 | $this->_error = ""; // per-reply fields (for single-query case)
463 | $this->_warning = "";
464 | $this->_connerror = false;
465 |
466 | $this->_reqs = array (); // requests storage (for multi-query case)
467 | $this->_mbenc = "";
468 | $this->_arrayresult = false;
469 | $this->_timeout = 0;
470 | }
471 |
472 | function __destruct()
473 | {
474 | if ( $this->_socket !== false )
475 | fclose ( $this->_socket );
476 | }
477 |
478 | /// get last error message (string)
479 | function GetLastError ()
480 | {
481 | return $this->_error;
482 | }
483 |
484 | /// get last warning message (string)
485 | function GetLastWarning ()
486 | {
487 | return $this->_warning;
488 | }
489 |
490 | /// get last error flag (to tell network connection errors from searchd errors or broken responses)
491 | function IsConnectError()
492 | {
493 | return $this->_connerror;
494 | }
495 |
496 | /// set searchd host name (string) and port (integer)
497 | function SetServer ( $host, $port = 0 )
498 | {
499 | assert ( is_string($host) );
500 | if ( $host[0] == '/')
501 | {
502 | $this->_path = 'unix://' . $host;
503 | return;
504 | }
505 | if ( substr ( $host, 0, 7 )=="unix://" )
506 | {
507 | $this->_path = $host;
508 | return;
509 | }
510 |
511 | assert ( is_int($port) );
512 | $this->_host = $host;
513 | $this->_port = $port;
514 | $this->_path = '';
515 |
516 | }
517 |
518 | /// set server connection timeout (0 to remove)
519 | function SetConnectTimeout ( $timeout )
520 | {
521 | assert ( is_numeric($timeout) );
522 | $this->_timeout = $timeout;
523 | }
524 |
525 |
526 | function _Send ( $handle, $data, $length )
527 | {
528 | if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length )
529 | {
530 | $this->_error = 'connection unexpectedly closed (timed out?)';
531 | $this->_connerror = true;
532 | return false;
533 | }
534 | return true;
535 | }
536 |
537 | /////////////////////////////////////////////////////////////////////////////
538 |
539 | /// enter mbstring workaround mode
540 | function _MBPush ()
541 | {
542 | $this->_mbenc = "";
543 | if ( ini_get ( "mbstring.func_overload" ) & 2 )
544 | {
545 | $this->_mbenc = mb_internal_encoding();
546 | mb_internal_encoding ( "latin1" );
547 | }
548 | }
549 |
550 | /// leave mbstring workaround mode
551 | function _MBPop ()
552 | {
553 | if ( $this->_mbenc )
554 | mb_internal_encoding ( $this->_mbenc );
555 | }
556 |
557 | /// connect to searchd server
558 | function _Connect ()
559 | {
560 | if ( $this->_socket!==false )
561 | {
562 | // we are in persistent connection mode, so we have a socket
563 | // however, need to check whether it's still alive
564 | if ( !@feof ( $this->_socket ) )
565 | return $this->_socket;
566 |
567 | // force reopen
568 | $this->_socket = false;
569 | }
570 |
571 | $errno = 0;
572 | $errstr = "";
573 | $this->_connerror = false;
574 |
575 | if ( $this->_path )
576 | {
577 | $host = $this->_path;
578 | $port = 0;
579 | }
580 | else
581 | {
582 | $host = $this->_host;
583 | $port = $this->_port;
584 | }
585 |
586 | if ( $this->_timeout<=0 )
587 | $fp = @fsockopen ( $host, $port, $errno, $errstr );
588 | else
589 | $fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout );
590 |
591 | if ( !$fp )
592 | {
593 | if ( $this->_path )
594 | $location = $this->_path;
595 | else
596 | $location = "{$this->_host}:{$this->_port}";
597 |
598 | $errstr = trim ( $errstr );
599 | $this->_error = "connection to $location failed (errno=$errno, msg=$errstr)";
600 | $this->_connerror = true;
601 | return false;
602 | }
603 |
604 | // send my version
605 | // this is a subtle part. we must do it before (!) reading back from searchd.
606 | // because otherwise under some conditions (reported on FreeBSD for instance)
607 | // TCP stack could throttle write-write-read pattern because of Nagle.
608 | if ( !$this->_Send ( $fp, pack ( "N", 1 ), 4 ) )
609 | {
610 | fclose ( $fp );
611 | $this->_error = "failed to send client protocol version";
612 | return false;
613 | }
614 |
615 | // check version
616 | list(,$v) = unpack ( "N*", fread ( $fp, 4 ) );
617 | $v = (int)$v;
618 | if ( $v<1 )
619 | {
620 | fclose ( $fp );
621 | $this->_error = "expected searchd protocol version 1+, got version '$v'";
622 | return false;
623 | }
624 |
625 | return $fp;
626 | }
627 |
628 | /// get and check response packet from searchd server
629 | function _GetResponse ( $fp, $client_ver )
630 | {
631 | $response = "";
632 | $len = 0;
633 |
634 | $header = fread ( $fp, 8 );
635 | if ( strlen($header)==8 )
636 | {
637 | list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) );
638 | $left = $len;
639 | while ( $left>0 && !feof($fp) )
640 | {
641 | $chunk = fread ( $fp, $left );
642 | if ( $chunk )
643 | {
644 | $response .= $chunk;
645 | $left -= strlen($chunk);
646 | }
647 | }
648 | }
649 | if ( $this->_socket === false )
650 | fclose ( $fp );
651 |
652 | // check response
653 | $read = strlen ( $response );
654 | if ( !$response || $read!=$len )
655 | {
656 | $this->_error = $len
657 | ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
658 | : "received zero-sized searchd response";
659 | return false;
660 | }
661 |
662 | // check status
663 | if ( $status==SEARCHD_WARNING )
664 | {
665 | list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) );
666 | $this->_warning = substr ( $response, 4, $wlen );
667 | return substr ( $response, 4+$wlen );
668 | }
669 | if ( $status==SEARCHD_ERROR )
670 | {
671 | $this->_error = "searchd error: " . substr ( $response, 4 );
672 | return false;
673 | }
674 | if ( $status==SEARCHD_RETRY )
675 | {
676 | $this->_error = "temporary searchd error: " . substr ( $response, 4 );
677 | return false;
678 | }
679 | if ( $status!=SEARCHD_OK )
680 | {
681 | $this->_error = "unknown status code '$status'";
682 | return false;
683 | }
684 |
685 | // check version
686 | if ( $ver<$client_ver )
687 | {
688 | $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work",
689 | $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff );
690 | }
691 |
692 | return $response;
693 | }
694 |
695 | /////////////////////////////////////////////////////////////////////////////
696 | // searching
697 | /////////////////////////////////////////////////////////////////////////////
698 |
699 | /// set offset and count into result set,
700 | /// and optionally set max-matches and cutoff limits
701 | function SetLimits ( $offset, $limit, $max=0, $cutoff=0 )
702 | {
703 | assert ( is_int($offset) );
704 | assert ( is_int($limit) );
705 | assert ( $offset>=0 );
706 | assert ( $limit>0 );
707 | assert ( $max>=0 );
708 | $this->_offset = $offset;
709 | $this->_limit = $limit;
710 | if ( $max>0 )
711 | $this->_maxmatches = $max;
712 | if ( $cutoff>0 )
713 | $this->_cutoff = $cutoff;
714 | }
715 |
716 | /// set maximum query time, in milliseconds, per-index
717 | /// integer, 0 means "do not limit"
718 | function SetMaxQueryTime ( $max )
719 | {
720 | assert ( is_int($max) );
721 | assert ( $max>=0 );
722 | $this->_maxquerytime = $max;
723 | }
724 |
725 | /// set matching mode
726 | function SetMatchMode ( $mode )
727 | {
728 | assert ( $mode==SPH_MATCH_ALL
729 | || $mode==SPH_MATCH_ANY
730 | || $mode==SPH_MATCH_PHRASE
731 | || $mode==SPH_MATCH_BOOLEAN
732 | || $mode==SPH_MATCH_EXTENDED
733 | || $mode==SPH_MATCH_FULLSCAN
734 | || $mode==SPH_MATCH_EXTENDED2 );
735 | $this->_mode = $mode;
736 | }
737 |
738 | /// set ranking mode
739 | function SetRankingMode ( $ranker )
740 | {
741 | assert ( $ranker>=0 && $ranker_ranker = $ranker;
743 | }
744 |
745 | /// set matches sorting mode
746 | function SetSortMode ( $mode, $sortby="" )
747 | {
748 | assert (
749 | $mode==SPH_SORT_RELEVANCE ||
750 | $mode==SPH_SORT_ATTR_DESC ||
751 | $mode==SPH_SORT_ATTR_ASC ||
752 | $mode==SPH_SORT_TIME_SEGMENTS ||
753 | $mode==SPH_SORT_EXTENDED ||
754 | $mode==SPH_SORT_EXPR );
755 | assert ( is_string($sortby) );
756 | assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 );
757 |
758 | $this->_sort = $mode;
759 | $this->_sortby = $sortby;
760 | }
761 |
762 | /// bind per-field weights by order
763 | /// DEPRECATED; use SetFieldWeights() instead
764 | function SetWeights ( $weights )
765 | {
766 | assert ( is_array($weights) );
767 | foreach ( $weights as $weight )
768 | assert ( is_int($weight) );
769 |
770 | $this->_weights = $weights;
771 | }
772 |
773 | /// bind per-field weights by name
774 | function SetFieldWeights ( $weights )
775 | {
776 | assert ( is_array($weights) );
777 | foreach ( $weights as $name=>$weight )
778 | {
779 | assert ( is_string($name) );
780 | assert ( is_int($weight) );
781 | }
782 | $this->_fieldweights = $weights;
783 | }
784 |
785 | /// bind per-index weights by name
786 | function SetIndexWeights ( $weights )
787 | {
788 | assert ( is_array($weights) );
789 | foreach ( $weights as $index=>$weight )
790 | {
791 | assert ( is_string($index) );
792 | assert ( is_int($weight) );
793 | }
794 | $this->_indexweights = $weights;
795 | }
796 |
797 | /// set IDs range to match
798 | /// only match records if document ID is beetwen $min and $max (inclusive)
799 | function SetIDRange ( $min, $max )
800 | {
801 | assert ( is_numeric($min) );
802 | assert ( is_numeric($max) );
803 | assert ( $min<=$max );
804 | $this->_min_id = $min;
805 | $this->_max_id = $max;
806 | }
807 |
808 | /// set values set filter
809 | /// only match records where $attribute value is in given set
810 | function SetFilter ( $attribute, $values, $exclude=false )
811 | {
812 | assert ( is_string($attribute) );
813 | assert ( is_array($values) );
814 | assert ( count($values) );
815 |
816 | if ( is_array($values) && count($values) )
817 | {
818 | foreach ( $values as $value )
819 | assert ( is_numeric($value) );
820 |
821 | $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
822 | }
823 | }
824 |
825 | /// set range filter
826 | /// only match records if $attribute value is beetwen $min and $max (inclusive)
827 | function SetFilterRange ( $attribute, $min, $max, $exclude=false )
828 | {
829 | assert ( is_string($attribute) );
830 | assert ( is_numeric($min) );
831 | assert ( is_numeric($max) );
832 | assert ( $min<=$max );
833 |
834 | $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
835 | }
836 |
837 | /// set float range filter
838 | /// only match records if $attribute value is beetwen $min and $max (inclusive)
839 | function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false )
840 | {
841 | assert ( is_string($attribute) );
842 | assert ( is_float($min) );
843 | assert ( is_float($max) );
844 | assert ( $min<=$max );
845 |
846 | $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
847 | }
848 |
849 | /// setup anchor point for geosphere distance calculations
850 | /// required to use @geodist in filters and sorting
851 | /// latitude and longitude must be in radians
852 | function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long )
853 | {
854 | assert ( is_string($attrlat) );
855 | assert ( is_string($attrlong) );
856 | assert ( is_float($lat) );
857 | assert ( is_float($long) );
858 |
859 | $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long );
860 | }
861 |
862 | /// set grouping attribute and function
863 | function SetGroupBy ( $attribute, $func, $groupsort="@group desc" )
864 | {
865 | assert ( is_string($attribute) );
866 | assert ( is_string($groupsort) );
867 | assert ( $func==SPH_GROUPBY_DAY
868 | || $func==SPH_GROUPBY_WEEK
869 | || $func==SPH_GROUPBY_MONTH
870 | || $func==SPH_GROUPBY_YEAR
871 | || $func==SPH_GROUPBY_ATTR
872 | || $func==SPH_GROUPBY_ATTRPAIR );
873 |
874 | $this->_groupby = $attribute;
875 | $this->_groupfunc = $func;
876 | $this->_groupsort = $groupsort;
877 | }
878 |
879 | /// set count-distinct attribute for group-by queries
880 | function SetGroupDistinct ( $attribute )
881 | {
882 | assert ( is_string($attribute) );
883 | $this->_groupdistinct = $attribute;
884 | }
885 |
886 | /// set distributed retries count and delay
887 | function SetRetries ( $count, $delay=0 )
888 | {
889 | assert ( is_int($count) && $count>=0 );
890 | assert ( is_int($delay) && $delay>=0 );
891 | $this->_retrycount = $count;
892 | $this->_retrydelay = $delay;
893 | }
894 |
895 | /// set result set format (hash or array; hash by default)
896 | /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
897 | function SetArrayResult ( $arrayresult )
898 | {
899 | assert ( is_bool($arrayresult) );
900 | $this->_arrayresult = $arrayresult;
901 | }
902 |
903 | /// set attribute values override
904 | /// there can be only one override per attribute
905 | /// $values must be a hash that maps document IDs to attribute values
906 | function SetOverride ( $attrname, $attrtype, $values )
907 | {
908 | assert ( is_string ( $attrname ) );
909 | assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) );
910 | assert ( is_array ( $values ) );
911 |
912 | $this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values );
913 | }
914 |
915 | /// set select-list (attributes or expressions), SQL-like syntax
916 | function SetSelect ( $select )
917 | {
918 | assert ( is_string ( $select ) );
919 | $this->_select = $select;
920 | }
921 |
922 | //////////////////////////////////////////////////////////////////////////////
923 |
924 | /// clear all filters (for multi-queries)
925 | function ResetFilters ()
926 | {
927 | $this->_filters = array();
928 | $this->_anchor = array();
929 | }
930 |
931 | /// clear groupby settings (for multi-queries)
932 | function ResetGroupBy ()
933 | {
934 | $this->_groupby = "";
935 | $this->_groupfunc = SPH_GROUPBY_DAY;
936 | $this->_groupsort = "@group desc";
937 | $this->_groupdistinct= "";
938 | }
939 |
940 | /// clear all attribute value overrides (for multi-queries)
941 | function ResetOverrides ()
942 | {
943 | $this->_overrides = array ();
944 | }
945 |
946 | //////////////////////////////////////////////////////////////////////////////
947 |
948 | /// connect to searchd server, run given search query through given indexes,
949 | /// and return the search results
950 | function Query ( $query, $index="*", $comment="" )
951 | {
952 | assert ( empty($this->_reqs) );
953 |
954 | $this->AddQuery ( $query, $index, $comment );
955 | $results = $this->RunQueries ();
956 | $this->_reqs = array (); // just in case it failed too early
957 |
958 | if ( !is_array($results) )
959 | return false; // probably network error; error message should be already filled
960 |
961 | $this->_error = $results[0]["error"];
962 | $this->_warning = $results[0]["warning"];
963 | if ( $results[0]["status"]==SEARCHD_ERROR )
964 | return false;
965 | else
966 | return $results[0];
967 | }
968 |
969 | /// helper to pack floats in network byte order
970 | function _PackFloat ( $f )
971 | {
972 | $t1 = pack ( "f", $f ); // machine order
973 | list(,$t2) = unpack ( "L*", $t1 ); // int in machine order
974 | return pack ( "N", $t2 );
975 | }
976 |
977 | /// add query to multi-query batch
978 | /// returns index into results array from RunQueries() call
979 | function AddQuery ( $query, $index="*", $comment="" )
980 | {
981 | // mbstring workaround
982 | $this->_MBPush ();
983 |
984 | // build request
985 | $req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits
986 | $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
987 | $req .= pack ( "N", strlen($query) ) . $query; // query itself
988 | $req .= pack ( "N", count($this->_weights) ); // weights
989 | foreach ( $this->_weights as $weight )
990 | $req .= pack ( "N", (int)$weight );
991 | $req .= pack ( "N", strlen($index) ) . $index; // indexes
992 | $req .= pack ( "N", 1 ); // id64 range marker
993 | $req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range
994 |
995 | // filters
996 | $req .= pack ( "N", count($this->_filters) );
997 | foreach ( $this->_filters as $filter )
998 | {
999 | $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
1000 | $req .= pack ( "N", $filter["type"] );
1001 | switch ( $filter["type"] )
1002 | {
1003 | case SPH_FILTER_VALUES:
1004 | $req .= pack ( "N", count($filter["values"]) );
1005 | foreach ( $filter["values"] as $value )
1006 | $req .= sphPackI64 ( $value );
1007 | break;
1008 |
1009 | case SPH_FILTER_RANGE:
1010 | $req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] );
1011 | break;
1012 |
1013 | case SPH_FILTER_FLOATRANGE:
1014 | $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
1015 | break;
1016 |
1017 | default:
1018 | assert ( 0 && "internal error: unhandled filter type" );
1019 | }
1020 | $req .= pack ( "N", $filter["exclude"] );
1021 | }
1022 |
1023 | // group-by clause, max-matches count, group-sort clause, cutoff count
1024 | $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby;
1025 | $req .= pack ( "N", $this->_maxmatches );
1026 | $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort;
1027 | $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay );
1028 | $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct;
1029 |
1030 | // anchor point
1031 | if ( empty($this->_anchor) )
1032 | {
1033 | $req .= pack ( "N", 0 );
1034 | } else
1035 | {
1036 | $a =& $this->_anchor;
1037 | $req .= pack ( "N", 1 );
1038 | $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"];
1039 | $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"];
1040 | $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] );
1041 | }
1042 |
1043 | // per-index weights
1044 | $req .= pack ( "N", count($this->_indexweights) );
1045 | foreach ( $this->_indexweights as $idx=>$weight )
1046 | $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight );
1047 |
1048 | // max query time
1049 | $req .= pack ( "N", $this->_maxquerytime );
1050 |
1051 | // per-field weights
1052 | $req .= pack ( "N", count($this->_fieldweights) );
1053 | foreach ( $this->_fieldweights as $field=>$weight )
1054 | $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight );
1055 |
1056 | // comment
1057 | $req .= pack ( "N", strlen($comment) ) . $comment;
1058 |
1059 | // attribute overrides
1060 | $req .= pack ( "N", count($this->_overrides) );
1061 | foreach ( $this->_overrides as $key => $entry )
1062 | {
1063 | $req .= pack ( "N", strlen($entry["attr"]) ) . $entry["attr"];
1064 | $req .= pack ( "NN", $entry["type"], count($entry["values"]) );
1065 | foreach ( $entry["values"] as $id=>$val )
1066 | {
1067 | assert ( is_numeric($id) );
1068 | assert ( is_numeric($val) );
1069 |
1070 | $req .= sphPackU64 ( $id );
1071 | switch ( $entry["type"] )
1072 | {
1073 | case SPH_ATTR_FLOAT: $req .= $this->_PackFloat ( $val ); break;
1074 | case SPH_ATTR_BIGINT: $req .= sphPackI64 ( $val ); break;
1075 | default: $req .= pack ( "N", $val ); break;
1076 | }
1077 | }
1078 | }
1079 |
1080 | // select-list
1081 | $req .= pack ( "N", strlen($this->_select) ) . $this->_select;
1082 |
1083 | // mbstring workaround
1084 | $this->_MBPop ();
1085 |
1086 | // store request to requests array
1087 | $this->_reqs[] = $req;
1088 | return count($this->_reqs)-1;
1089 | }
1090 |
1091 | /// connect to searchd, run queries batch, and return an array of result sets
1092 | function RunQueries ()
1093 | {
1094 | if ( empty($this->_reqs) )
1095 | {
1096 | $this->_error = "no queries defined, issue AddQuery() first";
1097 | return false;
1098 | }
1099 |
1100 | // mbstring workaround
1101 | $this->_MBPush ();
1102 |
1103 | if (!( $fp = $this->_Connect() ))
1104 | {
1105 | $this->_MBPop ();
1106 | return false;
1107 | }
1108 |
1109 | // send query, get response
1110 | $nreqs = count($this->_reqs);
1111 | $req = join ( "", $this->_reqs );
1112 | $len = 4+strlen($req);
1113 | $req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header
1114 |
1115 | if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
1116 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) )
1117 | {
1118 | $this->_MBPop ();
1119 | return false;
1120 | }
1121 |
1122 | // query sent ok; we can reset reqs now
1123 | $this->_reqs = array ();
1124 |
1125 | // parse and return response
1126 | return $this->_ParseSearchResponse ( $response, $nreqs );
1127 | }
1128 |
1129 | /// parse and return search query (or queries) response
1130 | function _ParseSearchResponse ( $response, $nreqs )
1131 | {
1132 | $p = 0; // current position
1133 | $max = strlen($response); // max position for checks, to protect against broken responses
1134 |
1135 | $results = array ();
1136 | for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ )
1137 | {
1138 | $results[] = array();
1139 | $result =& $results[$ires];
1140 |
1141 | $result["error"] = "";
1142 | $result["warning"] = "";
1143 |
1144 | // extract status
1145 | list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1146 | $result["status"] = $status;
1147 | if ( $status!=SEARCHD_OK )
1148 | {
1149 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1150 | $message = substr ( $response, $p, $len ); $p += $len;
1151 |
1152 | if ( $status==SEARCHD_WARNING )
1153 | {
1154 | $result["warning"] = $message;
1155 | } else
1156 | {
1157 | $result["error"] = $message;
1158 | continue;
1159 | }
1160 | }
1161 |
1162 | // read schema
1163 | $fields = array ();
1164 | $attrs = array ();
1165 |
1166 | list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1167 | while ( $nfields-->0 && $p<$max )
1168 | {
1169 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1170 | $fields[] = substr ( $response, $p, $len ); $p += $len;
1171 | }
1172 | $result["fields"] = $fields;
1173 |
1174 | list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1175 | while ( $nattrs-->0 && $p<$max )
1176 | {
1177 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1178 | $attr = substr ( $response, $p, $len ); $p += $len;
1179 | list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1180 | $attrs[$attr] = $type;
1181 | }
1182 | $result["attrs"] = $attrs;
1183 |
1184 | // read match count
1185 | list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1186 | list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1187 |
1188 | // read matches
1189 | $idx = -1;
1190 | while ( $count-->0 && $p<$max )
1191 | {
1192 | // index into result array
1193 | $idx++;
1194 |
1195 | // parse document id and weight
1196 | if ( $id64 )
1197 | {
1198 | $doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
1199 | list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1200 | }
1201 | else
1202 | {
1203 | list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
1204 | substr ( $response, $p, 8 ) ) );
1205 | $p += 8;
1206 | $doc = sphFixUint($doc);
1207 | }
1208 | $weight = sprintf ( "%u", $weight );
1209 |
1210 | // create match entry
1211 | if ( $this->_arrayresult )
1212 | $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight );
1213 | else
1214 | $result["matches"][$doc]["weight"] = $weight;
1215 |
1216 | // parse and create attributes
1217 | $attrvals = array ();
1218 | foreach ( $attrs as $attr=>$type )
1219 | {
1220 | // handle 64bit ints
1221 | if ( $type==SPH_ATTR_BIGINT )
1222 | {
1223 | $attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
1224 | continue;
1225 | }
1226 |
1227 | // handle floats
1228 | if ( $type==SPH_ATTR_FLOAT )
1229 | {
1230 | list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1231 | list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
1232 | $attrvals[$attr] = $fval;
1233 | continue;
1234 | }
1235 |
1236 | // handle everything else as unsigned ints
1237 | list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1238 | if ( $type & SPH_ATTR_MULTI )
1239 | {
1240 | $attrvals[$attr] = array ();
1241 | $nvalues = $val;
1242 | while ( $nvalues-->0 && $p<$max )
1243 | {
1244 | list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1245 | $attrvals[$attr][] = sphFixUint($val);
1246 | }
1247 | } else if ( $type==SPH_ATTR_STRING )
1248 | {
1249 | $attrvals[$attr] = substr ( $response, $p, $val );
1250 | $p += $val;
1251 | } else
1252 | {
1253 | $attrvals[$attr] = sphFixUint($val);
1254 | }
1255 | }
1256 |
1257 | if ( $this->_arrayresult )
1258 | $result["matches"][$idx]["attrs"] = $attrvals;
1259 | else
1260 | $result["matches"][$doc]["attrs"] = $attrvals;
1261 | }
1262 |
1263 | list ( $total, $total_found, $msecs, $words ) =
1264 | array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) );
1265 | $result["total"] = sprintf ( "%u", $total );
1266 | $result["total_found"] = sprintf ( "%u", $total_found );
1267 | $result["time"] = sprintf ( "%.3f", $msecs/1000 );
1268 | $p += 16;
1269 |
1270 | while ( $words-->0 && $p<$max )
1271 | {
1272 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1273 | $word = substr ( $response, $p, $len ); $p += $len;
1274 | list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
1275 | $result["words"][$word] = array (
1276 | "docs"=>sprintf ( "%u", $docs ),
1277 | "hits"=>sprintf ( "%u", $hits ) );
1278 | }
1279 | }
1280 |
1281 | $this->_MBPop ();
1282 | return $results;
1283 | }
1284 |
1285 | /////////////////////////////////////////////////////////////////////////////
1286 | // excerpts generation
1287 | /////////////////////////////////////////////////////////////////////////////
1288 |
1289 | /// connect to searchd server, and generate exceprts (snippets)
1290 | /// of given documents for given query. returns false on failure,
1291 | /// an array of snippets on success
1292 | function BuildExcerpts ( $docs, $index, $words, $opts=array() )
1293 | {
1294 | assert ( is_array($docs) );
1295 | assert ( is_string($index) );
1296 | assert ( is_string($words) );
1297 | assert ( is_array($opts) );
1298 |
1299 | $this->_MBPush ();
1300 |
1301 | if (!( $fp = $this->_Connect() ))
1302 | {
1303 | $this->_MBPop();
1304 | return false;
1305 | }
1306 |
1307 | /////////////////
1308 | // fixup options
1309 | /////////////////
1310 |
1311 | if ( !isset($opts["before_match"]) ) $opts["before_match"] = "";
1312 | if ( !isset($opts["after_match"]) ) $opts["after_match"] = "";
1313 | if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... ";
1314 | if ( !isset($opts["limit"]) ) $opts["limit"] = 256;
1315 | if ( !isset($opts["limit_passages"]) ) $opts["limit_passages"] = 0;
1316 | if ( !isset($opts["limit_words"]) ) $opts["limit_words"] = 0;
1317 | if ( !isset($opts["around"]) ) $opts["around"] = 5;
1318 | if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false;
1319 | if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false;
1320 | if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false;
1321 | if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false;
1322 | if ( !isset($opts["query_mode"]) ) $opts["query_mode"] = false;
1323 | if ( !isset($opts["force_all_words"]) ) $opts["force_all_words"] = false;
1324 | if ( !isset($opts["start_passage_id"]) ) $opts["start_passage_id"] = 1;
1325 | if ( !isset($opts["load_files"]) ) $opts["load_files"] = false;
1326 | if ( !isset($opts["html_strip_mode"]) ) $opts["html_strip_mode"] = "index";
1327 | if ( !isset($opts["allow_empty"]) ) $opts["allow_empty"] = false;
1328 | if ( !isset($opts["passage_boundary"]) ) $opts["passage_boundary"] = "none";
1329 | if ( !isset($opts["emit_zones"]) ) $opts["emit_zones"] = false;
1330 |
1331 | /////////////////
1332 | // build request
1333 | /////////////////
1334 |
1335 | // v.1.2 req
1336 | $flags = 1; // remove spaces
1337 | if ( $opts["exact_phrase"] ) $flags |= 2;
1338 | if ( $opts["single_passage"] ) $flags |= 4;
1339 | if ( $opts["use_boundaries"] ) $flags |= 8;
1340 | if ( $opts["weight_order"] ) $flags |= 16;
1341 | if ( $opts["query_mode"] ) $flags |= 32;
1342 | if ( $opts["force_all_words"] ) $flags |= 64;
1343 | if ( $opts["load_files"] ) $flags |= 128;
1344 | if ( $opts["allow_empty"] ) $flags |= 256;
1345 | if ( $opts["emit_zones"] ) $flags |= 512;
1346 | $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags
1347 | $req .= pack ( "N", strlen($index) ) . $index; // req index
1348 | $req .= pack ( "N", strlen($words) ) . $words; // req words
1349 |
1350 | // options
1351 | $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"];
1352 | $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"];
1353 | $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"];
1354 | $req .= pack ( "NN", (int)$opts["limit"], (int)$opts["around"] );
1355 | $req .= pack ( "NNN", (int)$opts["limit_passages"], (int)$opts["limit_words"], (int)$opts["start_passage_id"] ); // v.1.2
1356 | $req .= pack ( "N", strlen($opts["html_strip_mode"]) ) . $opts["html_strip_mode"];
1357 | $req .= pack ( "N", strlen($opts["passage_boundary"]) ) . $opts["passage_boundary"];
1358 |
1359 | // documents
1360 | $req .= pack ( "N", count($docs) );
1361 | foreach ( $docs as $doc )
1362 | {
1363 | assert ( is_string($doc) );
1364 | $req .= pack ( "N", strlen($doc) ) . $doc;
1365 | }
1366 |
1367 | ////////////////////////////
1368 | // send query, get response
1369 | ////////////////////////////
1370 |
1371 | $len = strlen($req);
1372 | $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header
1373 | if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
1374 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) )
1375 | {
1376 | $this->_MBPop ();
1377 | return false;
1378 | }
1379 |
1380 | //////////////////
1381 | // parse response
1382 | //////////////////
1383 |
1384 | $pos = 0;
1385 | $res = array ();
1386 | $rlen = strlen($response);
1387 | for ( $i=0; $i $rlen )
1393 | {
1394 | $this->_error = "incomplete reply";
1395 | $this->_MBPop ();
1396 | return false;
1397 | }
1398 | $res[] = $len ? substr ( $response, $pos, $len ) : "";
1399 | $pos += $len;
1400 | }
1401 |
1402 | $this->_MBPop ();
1403 | return $res;
1404 | }
1405 |
1406 |
1407 | /////////////////////////////////////////////////////////////////////////////
1408 | // keyword generation
1409 | /////////////////////////////////////////////////////////////////////////////
1410 |
1411 | /// connect to searchd server, and generate keyword list for a given query
1412 | /// returns false on failure,
1413 | /// an array of words on success
1414 | function BuildKeywords ( $query, $index, $hits )
1415 | {
1416 | assert ( is_string($query) );
1417 | assert ( is_string($index) );
1418 | assert ( is_bool($hits) );
1419 |
1420 | $this->_MBPush ();
1421 |
1422 | if (!( $fp = $this->_Connect() ))
1423 | {
1424 | $this->_MBPop();
1425 | return false;
1426 | }
1427 |
1428 | /////////////////
1429 | // build request
1430 | /////////////////
1431 |
1432 | // v.1.0 req
1433 | $req = pack ( "N", strlen($query) ) . $query; // req query
1434 | $req .= pack ( "N", strlen($index) ) . $index; // req index
1435 | $req .= pack ( "N", (int)$hits );
1436 |
1437 | ////////////////////////////
1438 | // send query, get response
1439 | ////////////////////////////
1440 |
1441 | $len = strlen($req);
1442 | $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header
1443 | if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
1444 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) )
1445 | {
1446 | $this->_MBPop ();
1447 | return false;
1448 | }
1449 |
1450 | //////////////////
1451 | // parse response
1452 | //////////////////
1453 |
1454 | $pos = 0;
1455 | $res = array ();
1456 | $rlen = strlen($response);
1457 | list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) );
1458 | $pos += 4;
1459 | for ( $i=0; $i<$nwords; $i++ )
1460 | {
1461 | list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
1462 | $tokenized = $len ? substr ( $response, $pos, $len ) : "";
1463 | $pos += $len;
1464 |
1465 | list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
1466 | $normalized = $len ? substr ( $response, $pos, $len ) : "";
1467 | $pos += $len;
1468 |
1469 | $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized );
1470 |
1471 | if ( $hits )
1472 | {
1473 | list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) );
1474 | $pos += 8;
1475 | $res [$i]["docs"] = $ndocs;
1476 | $res [$i]["hits"] = $nhits;
1477 | }
1478 |
1479 | if ( $pos > $rlen )
1480 | {
1481 | $this->_error = "incomplete reply";
1482 | $this->_MBPop ();
1483 | return false;
1484 | }
1485 | }
1486 |
1487 | $this->_MBPop ();
1488 | return $res;
1489 | }
1490 |
1491 | function EscapeString ( $string )
1492 | {
1493 | $from = array ( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=' );
1494 | $to = array ( '\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' );
1495 |
1496 | return str_replace ( $from, $to, $string );
1497 | }
1498 |
1499 | /////////////////////////////////////////////////////////////////////////////
1500 | // attribute updates
1501 | /////////////////////////////////////////////////////////////////////////////
1502 |
1503 | /// batch update given attributes in given rows in given indexes
1504 | /// returns amount of updated documents (0 or more) on success, or -1 on failure
1505 | function UpdateAttributes ( $index, $attrs, $values, $mva=false )
1506 | {
1507 | // verify everything
1508 | assert ( is_string($index) );
1509 | assert ( is_bool($mva) );
1510 |
1511 | assert ( is_array($attrs) );
1512 | foreach ( $attrs as $attr )
1513 | assert ( is_string($attr) );
1514 |
1515 | assert ( is_array($values) );
1516 | foreach ( $values as $id=>$entry )
1517 | {
1518 | assert ( is_numeric($id) );
1519 | assert ( is_array($entry) );
1520 | assert ( count($entry)==count($attrs) );
1521 | foreach ( $entry as $v )
1522 | {
1523 | if ( $mva )
1524 | {
1525 | assert ( is_array($v) );
1526 | foreach ( $v as $vv )
1527 | assert ( is_int($vv) );
1528 | } else
1529 | assert ( is_int($v) );
1530 | }
1531 | }
1532 |
1533 | // build request
1534 | $this->_MBPush ();
1535 | $req = pack ( "N", strlen($index) ) . $index;
1536 |
1537 | $req .= pack ( "N", count($attrs) );
1538 | foreach ( $attrs as $attr )
1539 | {
1540 | $req .= pack ( "N", strlen($attr) ) . $attr;
1541 | $req .= pack ( "N", $mva ? 1 : 0 );
1542 | }
1543 |
1544 | $req .= pack ( "N", count($values) );
1545 | foreach ( $values as $id=>$entry )
1546 | {
1547 | $req .= sphPackU64 ( $id );
1548 | foreach ( $entry as $v )
1549 | {
1550 | $req .= pack ( "N", $mva ? count($v) : $v );
1551 | if ( $mva )
1552 | foreach ( $v as $vv )
1553 | $req .= pack ( "N", $vv );
1554 | }
1555 | }
1556 |
1557 | // connect, send query, get response
1558 | if (!( $fp = $this->_Connect() ))
1559 | {
1560 | $this->_MBPop ();
1561 | return -1;
1562 | }
1563 |
1564 | $len = strlen($req);
1565 | $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
1566 | if ( !$this->_Send ( $fp, $req, $len+8 ) )
1567 | {
1568 | $this->_MBPop ();
1569 | return -1;
1570 | }
1571 |
1572 | if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
1573 | {
1574 | $this->_MBPop ();
1575 | return -1;
1576 | }
1577 |
1578 | // parse response
1579 | list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) );
1580 | $this->_MBPop ();
1581 | return $updated;
1582 | }
1583 |
1584 | /////////////////////////////////////////////////////////////////////////////
1585 | // persistent connections
1586 | /////////////////////////////////////////////////////////////////////////////
1587 |
1588 | function Open()
1589 | {
1590 | if ( $this->_socket !== false )
1591 | {
1592 | $this->_error = 'already connected';
1593 | return false;
1594 | }
1595 | if ( !$fp = $this->_Connect() )
1596 | return false;
1597 |
1598 | // command, command version = 0, body length = 4, body = 1
1599 | $req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
1600 | if ( !$this->_Send ( $fp, $req, 12 ) )
1601 | return false;
1602 |
1603 | $this->_socket = $fp;
1604 | return true;
1605 | }
1606 |
1607 | function Close()
1608 | {
1609 | if ( $this->_socket === false )
1610 | {
1611 | $this->_error = 'not connected';
1612 | return false;
1613 | }
1614 |
1615 | fclose ( $this->_socket );
1616 | $this->_socket = false;
1617 |
1618 | return true;
1619 | }
1620 |
1621 | //////////////////////////////////////////////////////////////////////////
1622 | // status
1623 | //////////////////////////////////////////////////////////////////////////
1624 |
1625 | function Status ()
1626 | {
1627 | $this->_MBPush ();
1628 | if (!( $fp = $this->_Connect() ))
1629 | {
1630 | $this->_MBPop();
1631 | return false;
1632 | }
1633 |
1634 | $req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1
1635 | if ( !( $this->_Send ( $fp, $req, 12 ) ) ||
1636 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) )
1637 | {
1638 | $this->_MBPop ();
1639 | return false;
1640 | }
1641 |
1642 | $res = substr ( $response, 4 ); // just ignore length, error handling, etc
1643 | $p = 0;
1644 | list ( $rows, $cols ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
1645 |
1646 | $res = array();
1647 | for ( $i=0; $i<$rows; $i++ )
1648 | for ( $j=0; $j<$cols; $j++ )
1649 | {
1650 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1651 | $res[$i][] = substr ( $response, $p, $len ); $p += $len;
1652 | }
1653 |
1654 | $this->_MBPop ();
1655 | return $res;
1656 | }
1657 |
1658 | //////////////////////////////////////////////////////////////////////////
1659 | // flush
1660 | //////////////////////////////////////////////////////////////////////////
1661 |
1662 | function FlushAttributes ()
1663 | {
1664 | $this->_MBPush ();
1665 | if (!( $fp = $this->_Connect() ))
1666 | {
1667 | $this->_MBPop();
1668 | return -1;
1669 | }
1670 |
1671 | $req = pack ( "nnN", SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ); // len=0
1672 | if ( !( $this->_Send ( $fp, $req, 8 ) ) ||
1673 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_FLUSHATTRS ) ) )
1674 | {
1675 | $this->_MBPop ();
1676 | return -1;
1677 | }
1678 |
1679 | $tag = -1;
1680 | if ( strlen($response)==4 )
1681 | list(,$tag) = unpack ( "N*", $response );
1682 | else
1683 | $this->_error = "unexpected response length";
1684 |
1685 | $this->_MBPop ();
1686 | return $tag;
1687 | }
1688 | }
1689 |
1690 | //
1691 | // $Id$
1692 | //
1693 |
--------------------------------------------------------------------------------
/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asphxg/findpass/5a6708bda5be5cac1837e10f7a072ed3d9e2f03d/test.jpg
--------------------------------------------------------------------------------