| 1 | #This Controller is in charge of the facet generation. |
|---|
| 2 | #It implements a algorithms that uses entropy to determinate the weight of each facet. |
|---|
| 3 | #It algorithms is better described in the Explorator spefication. |
|---|
| 4 | #Author: Samur Araujo |
|---|
| 5 | #Date: 25 jun 2008. |
|---|
| 6 | |
|---|
| 7 | #module where the SemanticExpression class is defined. |
|---|
| 8 | require 'query_builder' |
|---|
| 9 | #module where the query class is defined. |
|---|
| 10 | require 'query_factory' |
|---|
| 11 | class FacetsController < ApplicationController |
|---|
| 12 | #session :disabled => true |
|---|
| 13 | #The execute method evaluate a ruby expression. |
|---|
| 14 | #this method is used to invoke another method of Explorator. |
|---|
| 15 | #Basically, the UI call this method passing as the expression a |
|---|
| 16 | # call to the method facet or infer. |
|---|
| 17 | def execute |
|---|
| 18 | eval(params[:exp]) |
|---|
| 19 | end |
|---|
| 20 | def create |
|---|
| 21 | properties= eval(params[:exp]).result |
|---|
| 22 | facetgroup=FACETO::FacetGroup.new('<http://www.semanticnavigation.org/2008/faceto#' << params[:name] << '>') |
|---|
| 23 | facetgroup.rdfs::label=params[:name] |
|---|
| 24 | facetgroup.faceto::type=RDFS::Resource.new('http://www.semanticnavigation.org/2008/faceto#userdefined') |
|---|
| 25 | facetgroup.save |
|---|
| 26 | #it will hold all the facets |
|---|
| 27 | facets = Array.new |
|---|
| 28 | properties.each do |predicate| |
|---|
| 29 | if predicate.instance_of? RDF::Property |
|---|
| 30 | #create a object FACETO:Facet for each resource property and add it to the facets array. |
|---|
| 31 | #This code only creates facets that represents properties. Facets that represents expressions are not considerated here. |
|---|
| 32 | id =UUID.random_create.to_s |
|---|
| 33 | facet = FACETO::Facet.new('<http://www.semanticnavigation.org/2008/faceto#' << id << '>') |
|---|
| 34 | facet.save |
|---|
| 35 | facet.faceto::derivedTerm = predicate |
|---|
| 36 | facet.faceto::use = predicate |
|---|
| 37 | facet.save |
|---|
| 38 | #add the facet to the facet array. |
|---|
| 39 | facets << facet |
|---|
| 40 | #Sets the facets for the FacetGroup object. |
|---|
| 41 | end |
|---|
| 42 | end |
|---|
| 43 | facetgroup.faceto::facet = facets |
|---|
| 44 | end |
|---|
| 45 | #the parameter id is the ResourceSet identification in the SetsPool. |
|---|
| 46 | def facet (id) |
|---|
| 47 | #gets a ResourceSet instance in the pool. |
|---|
| 48 | @resourceset= session[:application].get(id) |
|---|
| 49 | #Gets a facet defined by the user in the repository. |
|---|
| 50 | # FACETO::FacetGroup belong to the ActiveRDF model and the vocabulary FACETO::FacetGroup was defined by the Explorator. |
|---|
| 51 | |
|---|
| 52 | # @groups=FACETO::FacetGroup.find_all() |
|---|
| 53 | # @facetgroup=FACETO::FacetGroup.find_by_rdfs::label('Group1').first |
|---|
| 54 | @groups=FACETO::FacetGroup.find_by_faceto::type(RDFS::Resource.new('http://www.semanticnavigation.org/2008/faceto#userdefined')) |
|---|
| 55 | @facetgroup=FACETO::FacetGroup.find_by_rdfs::label(params[:name]).first |
|---|
| 56 | |
|---|
| 57 | #Calculates all the facets for a set of resources. |
|---|
| 58 | entropy_by_set(@resourceset.resources) |
|---|
| 59 | #render the _facet.rhtml view |
|---|
| 60 | render :partial => "facet" |
|---|
| 61 | end |
|---|
| 62 | #Infer the facets for the specific set |
|---|
| 63 | def infer (id) |
|---|
| 64 | #gets a ResourceSet instance in the pool. |
|---|
| 65 | @resourceset= session[:application].get(id) |
|---|
| 66 | @groups=FACETO::FacetGroup.find_by_faceto::type(RDFS::Resource.new('http://www.semanticnavigation.org/2008/faceto#userdefined')) |
|---|
| 67 | |
|---|
| 68 | inference(@resourceset.resources,UUID.random_create.to_s) |
|---|
| 69 | #Calculates all the facets for a set of resources |
|---|
| 70 | entropy_by_set(@resourceset.resources) |
|---|
| 71 | #render the _facet.rhtml view |
|---|
| 72 | render :partial => "facet" |
|---|
| 73 | end |
|---|
| 74 | #The entropy_by_set method implements the facet algorithm itself. |
|---|
| 75 | #The algorithm only calculates facets that are defined as resource properties. |
|---|
| 76 | #Others kind of facets, described in the specification, are not taken into account here. |
|---|
| 77 | # Calculates the entropy for each facet. |
|---|
| 78 | def entropy_by_set(_resources) |
|---|
| 79 | puts 'FACETANDO...' |
|---|
| 80 | #store the valid facets |
|---|
| 81 | @facets = Hash.new |
|---|
| 82 | @exp=Hash.new |
|---|
| 83 | if _resources.size() ==0 |
|---|
| 84 | return |
|---|
| 85 | end |
|---|
| 86 | #detect using entropy wich facet will be valid |
|---|
| 87 | if @facetgroup == nil |
|---|
| 88 | return |
|---|
| 89 | end |
|---|
| 90 | |
|---|
| 91 | @facetgroup.all_faceto::facet.each do |facet| |
|---|
| 92 | #handles the facets that have an hierarchy of values. |
|---|
| 93 | facetroot = facet.instance_eval("faceto::level1") |
|---|
| 94 | puts facet |
|---|
| 95 | if facetroot != nil |
|---|
| 96 | facet = facetroot |
|---|
| 97 | end |
|---|
| 98 | # if $already_selected == nil |
|---|
| 99 | # $already_selected= Array.new |
|---|
| 100 | # end |
|---|
| 101 | # if facetroot != nil |
|---|
| 102 | # i=1 |
|---|
| 103 | # while $already_selected.include?( facetroot ) |
|---|
| 104 | # i+=1 |
|---|
| 105 | # facetroot = facet.instance_eval("faceto::level" + i.to_s) |
|---|
| 106 | # puts 'teste'+ i.to_s |
|---|
| 107 | # end |
|---|
| 108 | # if facetroot == nil |
|---|
| 109 | # next |
|---|
| 110 | # end |
|---|
| 111 | # $already_selected << facetroot |
|---|
| 112 | # facet = facetroot |
|---|
| 113 | # |
|---|
| 114 | # end |
|---|
| 115 | #the synonym are used when the are 2 or more object properties that describe the same values. |
|---|
| 116 | #See the definition bellow |
|---|
| 117 | # :country rdf:type faceto:Facet ; |
|---|
| 118 | # faceto:derivedTerm fn:country; |
|---|
| 119 | # faceto:synonym :brasil |
|---|
| 120 | # faceto:synonym :america |
|---|
| 121 | # faceto:use dp1:region |
|---|
| 122 | # faceto:operation 'union' |
|---|
| 123 | # :brasil faceto:word fn:region/bra; |
|---|
| 124 | # faceto:word fn:region/brasil; |
|---|
| 125 | # |
|---|
| 126 | # :america faceto:word fn:region/americas; |
|---|
| 127 | # faceto:word dp:country/americas. |
|---|
| 128 | |
|---|
| 129 | synonyms = Hash.new |
|---|
| 130 | facet.all_faceto::synonym.each{|synonym| |
|---|
| 131 | synonyms[synonym.all_word] = synonym |
|---|
| 132 | } |
|---|
| 133 | entropy = 0 |
|---|
| 134 | objects = Array.new |
|---|
| 135 | @exp[facet]= Hash.new |
|---|
| 136 | puts facet |
|---|
| 137 | prob_p=0 |
|---|
| 138 | hash_object=Hash.new |
|---|
| 139 | _resources.each do |resource| |
|---|
| 140 | next if !resource.instance_of? RDFS::Resource |
|---|
| 141 | qresult = Array.new |
|---|
| 142 | type = 'normal' #use when there are computedvalue in the facet |
|---|
| 143 | constraint=nil |
|---|
| 144 | computedValue = facet.all_faceto::computedValue |
|---|
| 145 | computedValue.each {|cvalue| |
|---|
| 146 | begin |
|---|
| 147 | type='computed' |
|---|
| 148 | #verifies if the computedvalue has a contraint expression |
|---|
| 149 | if cvalue.faceto::constraint != nil |
|---|
| 150 | #set the facet for the type contraint |
|---|
| 151 | type='constraint' |
|---|
| 152 | #all contraint computedvalue has a query expression |
|---|
| 153 | constraint=cvalue.faceto::query |
|---|
| 154 | end |
|---|
| 155 | #verifies whether the resource satisfies the constraint(faceto::contraint) in case of exists. |
|---|
| 156 | if cvalue.faceto::constraint == nil || resource.instance_eval(cvalue.faceto::constraint) == true |
|---|
| 157 | #the variable will be passed to the exp method to create the expression correctly |
|---|
| 158 | if cvalue.faceto::expressionValue != nil |
|---|
| 159 | qresult << resource.instance_eval(cvalue.faceto::expressionValue) |
|---|
| 160 | #verifies if the computed value has a queryvalue expresion. This expression is used to get the facet values. |
|---|
| 161 | elsif cvalue.faceto::queryValues != nil |
|---|
| 162 | #get the expression used to filter the elements being faceted |
|---|
| 163 | constraint=cvalue.faceto::query |
|---|
| 164 | #get all possible values for the facet. |
|---|
| 165 | qresult = qresult | eval(cvalue.faceto::queryValues) |
|---|
| 166 | else |
|---|
| 167 | #get a literal value |
|---|
| 168 | qresult << cvalue.faceto::literalValue |
|---|
| 169 | end |
|---|
| 170 | break |
|---|
| 171 | end |
|---|
| 172 | rescue |
|---|
| 173 | print "An error occurred: ",$!, "\n" |
|---|
| 174 | end |
|---|
| 175 | } |
|---|
| 176 | #verifies if the facet is a derived type. |
|---|
| 177 | if computedValue.size() == 0 |
|---|
| 178 | if facet.faceto::use != nil |
|---|
| 179 | #get the values based in the property |
|---|
| 180 | qresult = QueryFactory.new.distinct(:o).where(resource, facet.faceto::use,:o).execute |
|---|
| 181 | elsif facet.faceto::useInverse != nil |
|---|
| 182 | qresult = QueryFactory.new.distinct(:o).where(resource, facet.faceto::useInverse,:o).execute |
|---|
| 183 | end |
|---|
| 184 | end |
|---|
| 185 | |
|---|
| 186 | #property :p occurs em :s |
|---|
| 187 | if qresult.size > 0 |
|---|
| 188 | prob_p += 1 |
|---|
| 189 | end |
|---|
| 190 | #frequence that :o occurs for each :s |
|---|
| 191 | qresult.each do |o| |
|---|
| 192 | |
|---|
| 193 | #gets the default word in case where exists a table of synonyms |
|---|
| 194 | synomymswords =nil |
|---|
| 195 | if synonyms.size() > 0 |
|---|
| 196 | synonyms.keys.each{|words| |
|---|
| 197 | if words.include?(o) |
|---|
| 198 | synomymswords=words |
|---|
| 199 | o = synonyms[words].faceto::defaultWord |
|---|
| 200 | break |
|---|
| 201 | end |
|---|
| 202 | } |
|---|
| 203 | end |
|---|
| 204 | if hash_object[o] == nil |
|---|
| 205 | hash_object[o] = 1 |
|---|
| 206 | #create the query expression for this facet values |
|---|
| 207 | exp(facet, resource, o,type,synomymswords,constraint) |
|---|
| 208 | |
|---|
| 209 | else |
|---|
| 210 | hash_object[o] = hash_object[o]+1 |
|---|
| 211 | end |
|---|
| 212 | end |
|---|
| 213 | end |
|---|
| 214 | |
|---|
| 215 | # puts prob_p |
|---|
| 216 | #calculates the object occurencies |
|---|
| 217 | |
|---|
| 218 | hash_object.each_key do |object| |
|---|
| 219 | count= hash_object[object] |
|---|
| 220 | # puts object |
|---|
| 221 | |
|---|
| 222 | #calculate the object probability |
|---|
| 223 | #puts prob_p.to_f |
|---|
| 224 | #puts count |
|---|
| 225 | prob_o = count.to_f / prob_p.to_f |
|---|
| 226 | |
|---|
| 227 | if prob_o !=1 |
|---|
| 228 | objects << object |
|---|
| 229 | end |
|---|
| 230 | |
|---|
| 231 | #puts prob_o |
|---|
| 232 | #calculate the entropy based on the object probability |
|---|
| 233 | entropy = entropy + prob_o * Math.log(prob_o) |
|---|
| 234 | end |
|---|
| 235 | #calculates the objects' cardinality |
|---|
| 236 | if entropy != 0 |
|---|
| 237 | cardinalities = Array.new |
|---|
| 238 | objects.each do |object| |
|---|
| 239 | # puts object |
|---|
| 240 | h = Hash.new |
|---|
| 241 | h[object]=hash_object[object] |
|---|
| 242 | cardinalities << h |
|---|
| 243 | end |
|---|
| 244 | @facets[FACETO::Facet.new(facet)]=cardinalities |
|---|
| 245 | end |
|---|
| 246 | end |
|---|
| 247 | |
|---|
| 248 | end |
|---|
| 249 | #################################################################################### |
|---|
| 250 | #The inference method determines each possible facet, based on each resources properties |
|---|
| 251 | #The algorithm used is basd on entropy concept. |
|---|
| 252 | def inference(resources,cid) |
|---|
| 253 | #all predicates from all resources passed as parameters |
|---|
| 254 | predicates = Array.new |
|---|
| 255 | resources.each do |s| |
|---|
| 256 | predicates= predicates | Query.new.distinct(:p).where(s,:p,:o).execute |
|---|
| 257 | end |
|---|
| 258 | |
|---|
| 259 | #create a object FacetGroup for this instance of resources |
|---|
| 260 | @facetgroup=FACETO::FacetGroup.new('<http://www.semanticnavigation.org/2008/faceto#' << cid << '>') |
|---|
| 261 | @facetgroup.rdfs::label=cid |
|---|
| 262 | @facetgroup.faceto::type=RDFS::Resource.new('http://www.semanticnavigation.org/2008/faceto#infered') |
|---|
| 263 | @facetgroup.save |
|---|
| 264 | #it will hold all the facets |
|---|
| 265 | facets = Array.new |
|---|
| 266 | #create an object FACETO:Facet for each resource property and add it to the facets array. |
|---|
| 267 | #This code only creates facets that represents properties. Facets that represents expressions are not considerated here. |
|---|
| 268 | predicates.each do |predicate| |
|---|
| 269 | facet = FACETO::FacetGroup.find_by_faceto::use(predicate).first() |
|---|
| 270 | if facet == nil |
|---|
| 271 | |
|---|
| 272 | id =UUID.random_create.to_s |
|---|
| 273 | facet = FACETO::Facet.new('<http://www.semanticnavigation.org/2008/faceto#' << id << '>') |
|---|
| 274 | facet.save |
|---|
| 275 | facet.faceto::derivedTerm = predicate |
|---|
| 276 | facet.faceto::use = predicate |
|---|
| 277 | facet.save |
|---|
| 278 | end |
|---|
| 279 | #add the facet to the facet array. |
|---|
| 280 | facets << facet |
|---|
| 281 | end |
|---|
| 282 | #Sets the facets for the FacetGroup object. |
|---|
| 283 | @facetgroup.faceto::facet = facets |
|---|
| 284 | end |
|---|
| 285 | #create the expression for each facet value. |
|---|
| 286 | #the expression is different for each facet value. |
|---|
| 287 | def exp(facet, resource, value,type,words,constraint=nil,header=true,operation='intersection') |
|---|
| 288 | #it will store the expression |
|---|
| 289 | exp = '' |
|---|
| 290 | #get the value for a specific property when the value was computed. |
|---|
| 291 | if type=='computed' && constraint != nil |
|---|
| 292 | exp= '(' + constraint.gsub('resource', "RDFS::Resource.new('"+value.to_s+"')") + ',:p, :o)' |
|---|
| 293 | elsif type=='constraint' |
|---|
| 294 | exp = @resourceset.expression + "." |
|---|
| 295 | #must be a filter expression |
|---|
| 296 | exp = exp + constraint |
|---|
| 297 | return @exp[facet][value] = exp |
|---|
| 298 | else |
|---|
| 299 | properties = Array.new |
|---|
| 300 | #gets all properties used to calculated the facet value |
|---|
| 301 | properties = properties | facet.all_faceto::use |
|---|
| 302 | #iterate over each property |
|---|
| 303 | properties.each {|property| |
|---|
| 304 | if type=='computed' |
|---|
| 305 | #gets the value of this property in this resource |
|---|
| 306 | object = Query.new.distinct(:o).where(resource,property,:o).execute.to_s |
|---|
| 307 | else |
|---|
| 308 | #the value in the interface is the same as than the value that will be in the expression |
|---|
| 309 | #in this case the value was not computed but derived from a property. |
|---|
| 310 | object = value.to_s |
|---|
| 311 | end |
|---|
| 312 | if exp == '' |
|---|
| 313 | exp = "(:s,'" + property.to_s() + "', '"+ object + "')" |
|---|
| 314 | else |
|---|
| 315 | exp = exp + "."+operation+"(:s,'" + property.to_s() + "', '"+ object + "')" |
|---|
| 316 | end |
|---|
| 317 | } |
|---|
| 318 | properties = Array.new |
|---|
| 319 | properties = properties | facet.all_faceto::useInverse |
|---|
| 320 | #iterate over each property |
|---|
| 321 | properties.each {|property| |
|---|
| 322 | if type=='computed' |
|---|
| 323 | #gets the value of this property in this resource |
|---|
| 324 | object=Query.new.distinct(:o).where(:o,property,resource).execute.to_s |
|---|
| 325 | else |
|---|
| 326 | #the value in the interface is the same as than the value that will be in the expression |
|---|
| 327 | #in this case the value was not computed but derived from a property. |
|---|
| 328 | object = value.to_s |
|---|
| 329 | end |
|---|
| 330 | if exp == '' |
|---|
| 331 | exp = "('"+ object + "','" + property.to_s() + "', :s)" |
|---|
| 332 | else |
|---|
| 333 | exp = exp + "."+operation+"('"+ object + "','" + property.to_s() + "', :s)" |
|---|
| 334 | end |
|---|
| 335 | } |
|---|
| 336 | end |
|---|
| 337 | if words != nil |
|---|
| 338 | words.each {|word| |
|---|
| 339 | if word!=value |
|---|
| 340 | exp = exp + ".union"+ exp(facet, resource, word,type,nil,constraint,false) |
|---|
| 341 | end |
|---|
| 342 | } |
|---|
| 343 | end |
|---|
| 344 | if header |
|---|
| 345 | exp = "SemanticExpression.new" + exp |
|---|
| 346 | end |
|---|
| 347 | puts exp |
|---|
| 348 | @exp[facet][value] = exp |
|---|
| 349 | end |
|---|
| 350 | end |
|---|