@@ -180,8 +180,7 @@ def create_analyzers(self):
180180 self .db .create_analyzer (
181181 name = "norm" ,
182182 analyzer_type = "norm" ,
183- properties = {"locale" : "en" , "accent" : False , "case" : "lower" },
184- features = [],
183+ properties = {"locale" : "en.utf-8" , "accent" : False , "case" : "lower" },
185184 )
186185
187186 def refresh_views (self ):
@@ -306,7 +305,6 @@ def create_views(self):
306305 link_definitions [view_target ] = {
307306 "analyzers" : ["identity" , "norm" ],
308307 "includeAllFields" : True ,
309- "storedValues" : [{"fields" : ["name" , "tags" , "type" ]}],
310308 "trackListPositions" : False ,
311309 }
312310
@@ -333,6 +331,20 @@ def create_views(self):
333331 except Exception :
334332 pass
335333
334+ for target in link_definitions :
335+ del link_definitions [target ]["analyzers" ]
336+ link_definitions [target ]["analyzers" ] = []
337+ link_definitions [target ]["includeAllFields" ] = False
338+ link_definitions [target ]["fields" ] = {
339+ "tags" : {"fields" : {"name" : {"analyzers" : ["identity" , "norm" ]}}},
340+ "dfiq_tags" : {"analyzers" : ["identity" , "norm" ]},
341+ "type" : {"analyzers" : ["identity" , "norm" ]},
342+ "root_type" : {"analyzers" : ["identity" , "norm" ]},
343+ "value" : {"analyzers" : ["identity" , "norm" ]},
344+ "name" : {"analyzers" : ["identity" , "norm" ]},
345+ "created" : {"analyzers" : ["identity" , "norm" ]},
346+ }
347+
336348 self .db .create_arangosearch_view (
337349 name = "all_objects_view" ,
338350 properties = {
@@ -343,6 +355,7 @@ def create_views(self):
343355 {"field" : "created" , "direction" : "desc" },
344356 {"field" : "value" , "direction" : "asc" },
345357 {"field" : "name" , "direction" : "asc" },
358+ {"field" : "tags.name" , "direction" : "asc" },
346359 ],
347360 },
348361 )
@@ -439,6 +452,7 @@ class ArangoYetiConnector(AbstractYetiConnector):
439452 """Yeti connector for an ArangoDB backend."""
440453
441454 _db = db
455+ _collection_name : str | None = None
442456
443457 def __init__ (self ):
444458 self ._arango_id = None
@@ -1016,6 +1030,7 @@ def filter(
10161030 offset: Skip this many objects when querying the DB.
10171031 count: How many objecst after `offset` to return.
10181032 sorting: A list of (order, ascending) fields to sort by.
1033+ aliases: A list of (alias, type) tuples to use for filtering.
10191034 graph_queries: A list of (name, graph, direction, field) tuples to
10201035 query the graph with.
10211036 wildcard: whether all values should be interpreted as wildcard searches.
@@ -1026,11 +1041,19 @@ def filter(
10261041 """
10271042 cls ._get_collection ()
10281043 colname = cls ._collection_name
1044+ if colname is None :
1045+ colname = "all_objects_view"
10291046 conditions = []
10301047 filter_conditions = [] # used for clauses that are not supported by arangosearch
10311048 sorts = []
10321049
10331050 using_view = False
1051+ generic_query = False
1052+
1053+ if colname == "all_objects_view" :
1054+ generic_query = True
1055+ using_view = True
1056+
10341057 if (
10351058 query_args
10361059 and colname in ("observables" , "entities" , "indicators" , "dfiq" )
@@ -1085,9 +1108,13 @@ def filter(
10851108 aql_args [f"arg{ i } _key" ] = key
10861109 elif key == "tags" :
10871110 if using_view :
1088- conditions .append (f"@arg{ i } _value ALL IN o.tags.name" )
1111+ conditions .append (
1112+ f"(FOR t in @arg{ i } _value RETURN LOWER(t)) ALL IN o.tags.name"
1113+ )
10891114 else :
1090- conditions .append (f"@arg{ i } _value ALL IN o.tags[*].name" )
1115+ conditions .append (
1116+ f"(FOR t in @arg{ i } _value RETURN LOWER(t)) ALL IN o.tags[*].name"
1117+ )
10911118 elif key in ("created" , "modified" , "tags.expires" ):
10921119 # Value is a string, we're checking the first character.
10931120 operator = value [0 ]
@@ -1114,11 +1141,16 @@ def filter(
11141141 key_conditions = [f"REGEX_TEST(o.@arg{ i } _key, @arg{ i } _value, true)" ]
11151142
11161143 for alias , alias_type in aliases :
1117- if (
1118- alias_type in {"text" , "option" }
1119- or alias_type == "list"
1120- and using_view
1121- ):
1144+ if alias == "tags" :
1145+ if using_view :
1146+ key_conditions .append (
1147+ f"ANALYZER(LIKE(o.tags.name, LOWER(@arg{ i } _value)), 'norm')"
1148+ )
1149+ else :
1150+ key_conditions .append (
1151+ f"LOWER(@arg{ i } _value) IN o.tags[*].name"
1152+ )
1153+ if alias_type in {"text" , "option" , "list" } and using_view :
11221154 if using_view and not using_regex :
11231155 key_conditions .append (
11241156 f"ANALYZER(LIKE(o.{ alias } , LOWER(@arg{ i } _value)), 'norm')"
@@ -1232,7 +1264,14 @@ def filter(
12321264 results = []
12331265 for doc in documents :
12341266 doc ["__id" ] = doc .pop ("_key" )
1235- results .append (cls .load (doc ))
1267+ if not generic_query :
1268+ results .append (cls .load (doc ))
1269+ else :
1270+ # Generic objects are not loaded, they are returned as dicts.
1271+ doc ["id" ] = doc .pop ("__id" )
1272+ del doc ["_id" ]
1273+ del doc ["_rev" ]
1274+ results .append (doc )
12361275 total = stats .get ("fullCount" , len (results ))
12371276 return results , total or 0
12381277
@@ -1321,7 +1360,10 @@ def _get_collection(cls):
13211360 Returns:
13221361 The ArangoDB collection corresponding to the object class.
13231362 """
1324- return cls ._db .collection (cls ._collection_name )
1363+ if cls ._collection_name is not None :
1364+ return cls ._db .collection (cls ._collection_name )
1365+ else :
1366+ return "all_objects_view"
13251367
13261368
13271369def tagged_observables_export (cls , args ):
0 commit comments