private static XElement GetItemByIdFormatted(EntityInfo ent_info, XElement format) { XElement result = new XElement("record", new XAttribute("id", ent_info.LastId), ent_info.TypeId == null ? null : new XAttribute("type", ent_info.TypeId), // Сэкономил запрос на тип null); // Множество точек рассширения дерева List<ExtendInfo> extend_points = new List<ExtendInfo>(); extend_points.Add(new ExtendInfo() { ent = ent_info, format = format, record = result }); int level = 1; // для эксперимента по ограничению уровня раскрытия while (extend_points.Count > 0) { // Набор всех идентификаторов, по которым надо найти обратные записи List<string> forinverse = new List<string>(); //List<string> forinverse_special = new List<string>(); // for experiment List<string> fordirect = new List<string>(); List<ExtendInfo> extend_points_new = new List<ExtendInfo>(); // формирование множества идентификаторов обратных типов (null - нужны все типы) string[] inverse_types = null; var inverse_recs = extend_points .SelectMany(ep => ep.format.Elements("inverse").Elements("record")) .ToArray(); if (inverse_recs.All(inv => inv.Attribute("type") != null)) { inverse_types = inverse_recs.Select(inv => inv.Attribute("type").Value) .Distinct() .ToArray(); } foreach (var ep in extend_points) { EntityInfo entity = ep.ent; XElement ep_format = ep.format; XElement toinclude = ep.record; // Формирование множества forinverseset if (ep_format.Elements("inverse").Any()) { forinverse.Add(ep.ent.LastId); } // Обработаем нужные поля string[] fieldnames = ep_format.Elements("field") .Select(f => f.Attribute("prop").Value) .ToArray(); if (entity.RecordElements != null) foreach (PredicateInfo field in entity.RecordElements.Where(pi => !pi.IsObjectProperty)) { if (!fieldnames.Contains(field.Predicate)) continue; toinclude.Add(new XElement("field", new XAttribute("prop", field.Predicate), field.Lang == null ? null : new XAttribute(ONames.xmllang, field.Lang), field.Value)); } // обработаем нужные прямые ссылки string[] directnames = ep_format.Elements("direct") .Select(f => f.Attribute("prop").Value) .ToArray(); if (entity.RecordElements != null) foreach (PredicateInfo field in entity.RecordElements.Where(pi => pi.IsObjectProperty)) { if (!directnames.Contains(field.Predicate)) continue; fordirect.Add(field.Value); } } // Теперь сделаем запрос на записи, список которых сформирован в forinverse string[] inv_set = forinverse.Distinct().ToArray(); var query = DbRecords.AsQueryable<EntityInfo>() .Where(r => r.ExternalLinks.ContainsAny(inv_set)).Where(r => !r.IsRemoved); if (inverse_types != null) query = query .Where(r => r.TypeId == null || inverse_types.Contains(r.TypeId)); EntityInfo[] inv_entities = inv_set.Length == 0 ? new EntityInfo[0] : query.ToArray(); // Теперь сделаем запрос на записи, список которых сформирован в fordirect string[] dir_set = fordirect.Distinct().ToArray(); EntityInfo[] dir_entities = dir_set.Length == 0 ? new EntityInfo[0] : DbRecords.AsQueryable<EntityInfo>() .Where(r => dir_set.Contains(r.LastId)).Where(r => !r.IsRemoved) .ToArray(); // Теперь надо проанализировать каждую из точек рассширения foreach (var ep in extend_points) { string id = ep.ent.LastId; // Анализ обратных отношений foreach (XElement f_inverse in ep.format.Elements("inverse")) { string prop = f_inverse.Attribute("prop").Value; //var inv_props = inv_entities // .SelectMany(en => en.RecordElements.Select(re => new { ent = en, ele = re })) // .Where(pair => pair.ele.IsObjectProperty && // pair.ele.Predicate == prop && // pair.ele.Value == id); // Думаю, двойной цикл будет эффективнее foreach (var en in inv_entities) { foreach (var pr in en.RecordElements) { // проверяем на вид свойства, его имя и то, что ссылка на нужный объект if (pr.IsObjectProperty && pr.Predicate == prop && pr.Value == id) { // Опеределяем тип удаленной записи string inv_t_id = en.TypeId; // Определяем нужный подформат XElement f_innerrecord = f_inverse.Elements("record") .FirstOrDefault(r => { var t_att = r.Attribute("type"); return t_att == null || t_att.Value == inv_t_id; // Здесь надо использовать иерархию типов }); if (f_innerrecord != null) { XElement innerrecord = new XElement("record", new XAttribute("id", en.LastId), new XAttribute("type", inv_t_id)); ep.record.Add(new XElement("inverse", new XAttribute("prop", prop), innerrecord)); ExtendInfo inner_ei = new ExtendInfo() { ent = en, format = f_innerrecord, record = innerrecord }; extend_points_new.Add(inner_ei); } } } //XElement innerXrecord = new XElement("record", new XAttribute( //ep.record.Add(new XElement("inverse", new X } } // Анализ формата прямых ссылок foreach (XElement f_direct in ep.format.Elements("direct")) { string prop = f_direct.Attribute("prop").Value; PredicateInfo pi = ep.ent.RecordElements .FirstOrDefault(re => re.IsObjectProperty && re.Predicate == prop); if (pi == null) continue; // Возможно, надо ставнивать не с LastId, а со всеми слитыми EntityInfo en_direct = dir_entities.FirstOrDefault(de => de.LastId == pi.Value); if (en_direct == null) continue; XElement f_innerrecord = f_direct.Elements("record") .FirstOrDefault(r => { var t_att = r.Attribute("type"); return t_att == null || t_att.Value == en_direct.TypeId; // Здесь надо использовать иерархию типов }); if (f_innerrecord == null) continue; XElement innerRecord = new XElement("record", new XAttribute("id", en_direct.LastId), new XAttribute("type", en_direct.TypeId == null ? "" : en_direct.TypeId)); ep.record.Add(new XElement("direct", new XAttribute("prop", prop), innerRecord)); extend_points_new.Add(new ExtendInfo() { ent = en_direct, format = f_innerrecord, record = innerRecord }); } } extend_points = extend_points_new; //extend_points = level > 2 ? new List<ExtendInfo>() : extend_points_new; level++; } return result; }
public void IncludeXElement(XElement el) { DateTime modificationTime_new = DateTime.MinValue; XAttribute mt = el.Attribute(ONames.AttModificationTime); if (mt != null) DateTime.TryParse(mt.Value, out modificationTime_new); modificationTime_new = modificationTime_new.ToUniversalTime(); string id = el.Attribute(sema2012m.ONames.rdfabout).Value; sema2012m.ResInfo ri = table_ri[id]; string[] link_arr = el.Elements() .Select(p => p.Attribute(sema2012m.ONames.rdfresource)) .Where(res_att => res_att != null) .Select(res_att => { // идентификатор берется или из table_ri или оригинальный var v = res_att.Value; sema2012m.ResInfo r; if (!table_ri.TryGetValue(v, out r)) return v; return r.id; }) .ToArray(); PredicateInfo[] recordElements = el.Elements() .Select(pred => { var resource_att = pred.Attribute(sema2012m.ONames.rdfresource); if (resource_att == null) { var lang_att = pred.Attribute(sema2012m.ONames.xmllang); return new PredicateInfo() { IsObjectProperty = false, Predicate = pred.Name.NamespaceName + pred.Name.LocalName, Value = pred.Value, Lang = lang_att == null ? null : lang_att.Value }; } else { sema2012m.ResInfo r; string res_id = resource_att.Value; if (table_ri.TryGetValue(res_id, out r)) res_id = r.id; return new PredicateInfo() { IsObjectProperty = true, Predicate = pred.Name.NamespaceName + pred.Name.LocalName, Value = res_id }; } }).ToArray(); string[] firstWords = el.Elements(ONames.xNameDatatypeProperty) .Select(nm => { string name = nm.Value; if (string.IsNullOrEmpty(name)) return ""; string[] parts = name.Split(' ', ','); return parts[0].ToLower(); }).ToArray(); //string[] externalLinks = el.Elements() string[] externalLinks = recordElements .Where(re => re.IsObjectProperty) .Select(re => re.Value) .ToArray(); EntityInfo ei = new EntityInfo() { TypeId = el.Name.NamespaceName + el.Name.LocalName, LastId = id, // вычислен MergedIds = ri.merged_ids.ToArray(), // вычислен IsRemoved = ri.removed, // вычислен RecordElements = recordElements, FirstWords = firstWords, ExternalLinks = externalLinks, TimeStamp = modificationTime_new }; buff.Add(ei); }
private static IEnumerable<EntityInfo> GetInverseRecordsById(EntityInfo en) { var entities = DbRecords.AsQueryable<EntityInfo>() .Where(r => r.ExternalLinks.Contains(en.LastId)); return entities; }
public static void LoadXFlow(IEnumerable<XElement> xflow) { foreach (XElement el in xflow) { if (el.Name == sema2012m.ONames.TagDelete) { string delete_id = el.Attribute(sema2012m.ONames.AttItem_id).Value; // Поиск записи для delete_id EntityInfo delete_en = _records.AsQueryable<EntityInfo>() .FirstOrDefault(en => en.MergedIds.Contains(delete_id)); if (delete_en == null) { // Завести новую запись delete_en = new EntityInfo() { LastId = delete_id, // на всякий случай... IsRemoved = true, MergedIds = new string[] { delete_id } }; _records.Insert(delete_en); } else { // добавим в MergedIds //delete_en.MergedIds = delete_en.MergedIds // .Concat(new string[] { delete_id }).ToArray(); if (!delete_en.IsRemoved) // если уже уничтожен, то ничего делать не надо { delete_en.IsRemoved = true; delete_en.RecordElements = null; delete_en.ExternalLinks = null; // Надо еще вставить проверку того, что уничтожается оригинал записи _records.Save(delete_en); } } } else if (el.Name == sema2012m.ONames.TagSubstitute) { // Поиск записей для old_id и new_id string old_id = el.Attribute(sema2012m.ONames.AttOld_id).Value; EntityInfo old_en = _records.AsQueryable<EntityInfo>() .FirstOrDefault(en => en.MergedIds.Contains(old_id)); string new_id = el.Attribute(sema2012m.ONames.AttNew_id).Value; EntityInfo new_en = _records.AsQueryable<EntityInfo>() .FirstOrDefault(en => en.MergedIds.Contains(new_id)); // Предыдущие два запроса можно объединить в один, только тогда придется разбираться что есть что if (old_en == null && new_en == null) { // Вообще нет записей // заводим одну с общим списком слитых идентификаторов, оригиналом определяем новый идентификатор EntityInfo en = new EntityInfo() { MergedIds = new string[] { old_id, new_id }, LastId = new_id }; _records.Insert(en); } else if (old_en == null || new_en == null) { // Есть какая-то одна // добавляем в нее другой идентификатор и, в случае, если отсутствует новый, LastId определяем как новый идентификатор EntityInfo en = old_en == null ? new_en : old_en; en.MergedIds = en.MergedIds.Concat(new string[] { old_en == null ? old_id : new_id}).ToArray(); if (old_en != null) { // надо скорректировать обратные ссылки на эту сущность string corrected_id = en.LastId; CorrectExternalLinks(corrected_id, new_id); // а теперь заменить на новую en.LastId = new_id; } _records.Save(en); } else if (old_en.Id == new_en.Id) { // Есть обе записи, но они совпадают - слитие произведено, ничего не делаем } else { // Есть две записи и они не совпадают // в новую добавляем в merged старый идентификатор, старую уничтожаем new_en.MergedIds = new_en.MergedIds.Concat(old_en.MergedIds).Distinct().ToArray(); // кроме того, если старая уничтожена, то транслируем это в новую (Это не очевидно...) //if (old_en.IsRemoved) new_en.IsRemoved = true; _records.Save(new_en); CorrectExternalLinks(old_en.LastId, new_en.LastId); var query = Query.EQ("_id", old_en.Id); _records.Remove(query); } } else { // это запись var aboutatt = el.Attribute(sema2012m.ONames.rdfabout); if (aboutatt == null) continue; string id = aboutatt.Value; // Выявляем временную отметку DateTime modificationTime_new = DateTime.MinValue; XAttribute mt = el.Attribute(sema2012m.ONames.AttModificationTime); if (mt != null) DateTime.TryParse(mt.Value, out modificationTime_new); modificationTime_new = modificationTime_new.ToUniversalTime(); // Проверка на наличие мзаписи с таким же идентификатором EntityInfo existant_en = _records.AsQueryable<EntityInfo>() .FirstOrDefault(en => en.MergedIds.Contains(id)); if (existant_en != null && existant_en.TypeId != null && (existant_en.LastId != id || (existant_en.LastId == id && existant_en.TimeStamp > modificationTime_new)) ) { // Есть полная мзапись и она лучше, чем пришедшая // Ничего делать не надо } else { // Есть еще два варианта, в обоих придется создавать новую мзапись и уничтожать старую, надо следить за LastIs и MergedIds // Устанавливаем дефолтные значения: массива слитых идентификаторов, последнего идентификатора и признака IsRemoved string[] merged_arr = new string[] { id }; // Это для случая, когда нет мзаписи string lid = id; // Это для случая, когда нет мзаписи bool ir = false; // Это для случая, когда нет мзаписи if (existant_en != null) { // мзапись есть, но она неполная или хуже пришедшей записи merged_arr = existant_en.MergedIds; lid = existant_en.LastId; ir = existant_en.IsRemoved; // саму запись уничтожить var query = Query.EQ("_id", existant_en.Id); _records.Remove(query); // LastId-идентификатция не меняется НЕ надо корректировать обратные ссылки } else { // LastId совпадает с id - не надо корректировать обратные ссылки } // Штатный случай: новая запись, но с накопленным массивом слитых // Запросим текущие LastId объектных ссылок string[] link_arr = el.Elements().Select(p => p.Attribute(ONames.rdfresource)) .Where(res_att => res_att != null).Select(res_att => res_att.Value).ToArray(); var ext_ents = _records.AsQueryable<EntityInfo>() .Where(en => en.MergedIds.ContainsAny(link_arr)) .Select(en => new { last = en.LastId, merged = en.MergedIds }) .ToArray(); PredicateInfo[] recordElements = el.Elements() .Select(pred => { var resource_att = pred.Attribute(sema2012m.ONames.rdfresource); if (resource_att == null) { var lang_att = pred.Attribute(sema2012m.ONames.xmllang); return new PredicateInfo() { IsObjectProperty = false, Predicate = pred.Name.NamespaceName + pred.Name.LocalName, Value = pred.Value, Lang = lang_att == null ? null : lang_att.Value }; } else { var candidate = ext_ents.FirstOrDefault(en => en.merged.Contains(resource_att.Value)); string corrected_resource = candidate == null ? resource_att.Value : candidate.last; return new PredicateInfo() { IsObjectProperty = true, Predicate = pred.Name.NamespaceName + pred.Name.LocalName, Value = corrected_resource, }; } }).ToArray(); string[] firstWords = el.Elements(ONames.xNameDatatypeProperty) .Select(nm => { string name = nm.Value; if (string.IsNullOrEmpty(name)) return ""; string[] parts = name.Split(' ', ','); return parts[0].ToLower(); }).ToArray(); //string[] externalLinks = el.Elements() // .Where(pred => pred.Attribute(ONames.rdfresource) != null) // .Select(pred => pred.Attribute(ONames.rdfresource).Value) // .ToArray(); string[] externalLinks = recordElements .Where(re => re.IsObjectProperty) .Select(re => re.Value) .ToArray(); EntityInfo ei = new EntityInfo() { TypeId = el.Name.NamespaceName + el.Name.LocalName, LastId = lid, // вычислен MergedIds = merged_arr, // вычислен IsRemoved = ir, // вычислен RecordElements = recordElements, FirstWords = firstWords, ExternalLinks = externalLinks, TimeStamp = modificationTime_new }; _records.Insert<EntityInfo>(ei); } } } }