Search Part III: Contextual Search

Contextual search and optimizations

by rocketegg0

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>
<div ng-controller="MyCtrl">
    <h2>Part 3: Contextual Search</h2>
    <div>Search: <input type="text" ng-model="textIn"></input></div>
    <div>Context (#)<input type="number" ng-model="context" value=1></input></div>
    <div>
        Typeahead Results: 
        <table>
            <tr>
                <th>Search Result:</th>
                <th>Context</th>
            </tr>
            <tr ng-repeat="word in typeahead(textIn)">
                <td>
                {{word.value}}:{{word.position}}
                </td>
                <td><em>
                    {{dict.slice(word.position - context, word.position + context).join(' ')}}</em>
                </td>
            </tr>
        </table>
    </div>
</div>

CSS

h2 {
    font-size: 150%;
}

table {
    margin-top: 10px;
    border: 1px solid #ccc;
    width: 100%;
}

table th {
    font-weight: bold;
}

em {
    font-style: italic;
}

JavaScript

var myApp = angular.module('myApp',[]);

myApp.controller('MyCtrl', function($scope, $q) {
    
    // Compute NGrams
    var computeNGrams = function(dict) {    
        $scope.dict = dict.split(' ');
        return $scope.dict.reduce(ngramize, {});
    };
    
    // NGramize a word
    function ngramize(hash, word, index) {
        for (var i = 3; i < word.length+1; i++) {
            var s = word.substring(i - 3, i);
            if (!hash.hasOwnProperty(s)) {
                hash[s] = [new Word(word, index)];
            } else {
                //todo, still eliminate dupes
                hash[s].push(new Word(word, index));
            }
        }
        return hash;
    }
    
    var merge = function(arrays) {
        var returnArray = [];
        if (!arrays) return [];
        var base = arrays.pop();
        var intersected = arrays.reduce(intersect, base);
        return intersected;
    };
    
    $scope.typeahead = function(textIn) {
        if (!textIn || textIn.length < 3) return [];
        var ngrams = Object.keys(ngramize({}, textIn, 0));
        return flatten(merge(ngrams.map(function(ngram) {
          return $scope.hash[ngram] || [];        
        })));
    };
    
    $scope.hash = computeNGrams(dictionary);
    $scope.context = 3;
});

// Word: has a string word and an int position
var Word = function(value, position) {    
    var self = this;
  	this.value = value;
  	this.position = position;
    this.equals = function(word2) {
        return self.value == word2.value && self.position == word2.position;
    };
};

// Note: We can optimize this by using keeping arrays sorted
var intersect = function(array1, array2) {
    var intersection = [];
	array1.forEach(function(element1) {
        array2.forEach(function(element2) {
            if (element1.equals(element2)) {
                intersection.push(element1);
            }
        });
    });
    return intersection;
};

// Some Array Helper Methods
var flatten = function(arrays)...