// Returns all entities in the EntityManager that match a given search term. // Matching logic is currently just a substring match. func FindEntities(entitySearch server.EntitySearch) webapp.UserHttpHandler { return func(w http.ResponseWriter, r *http.Request, userId int) { w.Header().Set("Content-Type", "application/json") searchString := strings.TrimSpace(strings.ToLower(strutil.LastToken(r.URL.Path, "/"))) var response map[string][]server.DisplayEntity if !strutil.IsEmpty(searchString) { searchResults := entitySearch.Find(searchString) response = map[string][]server.DisplayEntity{ "Person": searchResults[server.PersonEntity], "Org": searchResults[server.OrgEntity], "Place": searchResults[server.PlaceEntity], } } else { noEntities := []server.DisplayEntity{} response = map[string][]server.DisplayEntity{ "Person": noEntities, "Org": noEntities, "Place": noEntities, } } sendJsonResponse(response, w) } }
// PA-241/PA-198: Support for disjunctive querying. func GetAllEntityInfo(mgr *server.EntityManager) webapp.UserHttpHandler { return func(w http.ResponseWriter, r *http.Request, userId int) { w.Header().Set("Content-Type", "application/json") entityStr2entityType := map[string]server.EntityType{ "Person": server.PersonEntity, "Org": server.OrgEntity, "Place": server.PlaceEntity, } // This function calculates the 'AND' co-occurence of the conjunct expression // of the form: ["and", "<entityType>:<id>", "<entityType:id", ...] calcConjunctExpr := func(g *server.ContentBuffer, expr ConjunctiveExpr) (*server.ContentBuffer, error) { logger.Printf("Calculating co-occurences for conjuncts: %v", expr.And) for _, entity := range expr.And { logger.Printf("Filtering on %v", entity.Id) entityTypeStr, entityId, err := parseEntityStr(entity.Id) if err != nil { return nil, err } entityType, isValidEntityType := entityStr2entityType[entityTypeStr] if !isValidEntityType { return nil, errors.New(fmt.Sprintf("Unknown entity type: '%v'", entityTypeStr)) } g = g.FilterOnEntity(entityType, entityId) } return g, nil } // Parse the posted data into the filter query in JSON format. // This query is essentially a LISP expression of the form: // // (op arg1 arg2 ...) // // where op is (for now) an 'or' operator (i.e. a disjunction). // Each arg is a nested LISP conjunct expression of the form (and arg1 arg2...), // where each arg is a string representing a distinct entity of the form // "<entity type>:<entity id>" (e.g. "Person:9283742"). getHttpRequestBody(w, r, func(postedData []byte) { var stats server.EntityStats var err error var finalContentBuffer *server.ContentBuffer // Default filter query: no time range specified, and no entity filters specified. filterQuery := FilterQuery{} httpRequestContainsData := !strutil.IsEmpty(string(postedData)) if httpRequestContainsData { if err = json.Unmarshal(postedData, &filterQuery); err != nil { http.Error(w, fmt.Sprintf("User %v: Error parsing posted JSON: %v", userId, err), http.StatusBadRequest) return } } var baseContentBuffer *server.ContentBuffer if filterQuery.IsTimeRangeSpecified() { timeRange := time.Duration(filterQuery.TimeRangeInHours) * time.Hour logger.Printf("Getting content buffer for timeRange=%v", timeRange) baseContentBuffer = mgr.ContentBufferForTimeRange(timeRange) } else { logger.Printf("Getting global content buffer") baseContentBuffer = mgr.ContentBuffer() } if filterQuery.IsEntityFilterSpecified() { logger.Printf("Calculating entity co-occurrences with disjunct query: %+V", filterQuery.Or) tmpContentBuffer := baseContentBuffer finalContentBuffer = server.NewContentBuffer() // Each disjunct is itself a conjunct expression of the form (and arg1 arg2 ...) for _, conjunctiveExpr := range filterQuery.Or { tmpContentBuffer, err = calcConjunctExpr(baseContentBuffer, conjunctiveExpr) if err != nil { http.Error(w, fmt.Sprintf("User %v: Error processing conjunct expression: %v", userId, err), http.StatusBadRequest) return } finalContentBuffer = finalContentBuffer.Union(tmpContentBuffer) } stats = finalContentBuffer.CalcEntityStats() } else { // Use the global stats, since this user has no filter set. logger.Printf("No entity filter provided, so using global entity stats.") finalContentBuffer = baseContentBuffer stats = finalContentBuffer.LatestEntityStats() } topEntities := map[string][]server.Entity{ "Person": annotateEntities(mgr.ContentDAO.PersonDAO, stats.TopPersons), "Org": annotateEntities(mgr.ContentDAO.OrgDAO, stats.TopOrgs), "Place": annotateEntities(mgr.ContentDAO.PlaceDAO, stats.TopPlaces), } entityTimes, entityValues := stats.EntityTrend.Data() latestNews := make([]server.NewsArticle, 0, len(stats.LatestNews)) for _, doc := range stats.LatestNews { newsArticle := server.NewsArticle{ Document: doc, Persons: annotateEntities(mgr.ContentDAO.PersonDAO, makeEntities(finalContentBuffer.PersonGraph.EntityIdsForDocument(doc.Id).Items())), Orgs: annotateEntities(mgr.ContentDAO.OrgDAO, makeEntities(finalContentBuffer.OrgGraph.EntityIdsForDocument(doc.Id).Items())), Places: annotateEntities(mgr.ContentDAO.PlaceDAO, makeEntities(finalContentBuffer.PlaceGraph.EntityIdsForDocument(doc.Id).Items())), } latestNews = append(latestNews, newsArticle) } response := map[string]interface{}{ "EntityTrend": EntityTrend{entityTimes, entityValues}, "TopEntities": topEntities, "LatestNews": latestNews, } sendJsonResponse(response, w) }) // End getHttpRequestBody() } }