Заполнить HashSet UNID'ами всех документов базы

Тема в разделе "Lotus + Java + LS2J", создана пользователем Zeka, 27 дек 2012.

  1. Zeka

    Zeka Well-Known Member

    Регистрация:
    1 сен 2009
    Сообщения:
    219
    Симпатии:
    0
    В базе есть 100 тысяч документов. Надо УНИДы всех документов положить в Set. Как это сделать максимально быстро?

    Код (Text):
            DocumentCollection dc = session.getCurrentDatabase().getAllDocuments();
    Document doc = dc.getFirstDocument();
    while (doc != null) {
    set.add(doc.getUniversalID());
    Document tmp = doc;
    doc = dc.getNextDocument(doc);
    tmp.recycle();
    }
    Занимает ~50 секунд.

    Код (Text):
            ViewEntryCollection ec = session.getCurrentDatabase().getView("AllByUNID").getAllEntries();
    ViewEntry en = ec.getFirstEntry();
    while (en != null) {
    set.add(en.getUniversalID());
    ViewEntry tmp = en;
    en = ec.getNextEntry(en);
    tmp.recycle();
    }
    Заинимает ~120 секунд.

    Как бы это дело оптимизировать?
     
  2. oshmianski

    oshmianski Достойный программист
    Lotus team

    Регистрация:
    25 апр 2012
    Сообщения:
    512
    Симпатии:
    13
    Для 8.5.2 и выше вроде...
    Код (C++):
    nav = view.createViewNav();
    nav.setBufferMaxEntries(400);
    nav.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);
    ve = nav.getFirst();
    while (ve != null) {
    set.add(ve.getUniversalID());

    tmpentry = nav.getNext();
    ve.recycle();
    ve = tmpentry;
    }
     
  3. lmike

    lmike нет, пердело совершенство
    Команда форума Lotus team

    Регистрация:
    27 авг 2008
    Сообщения:
    6.073
    Симпатии:
    299
    а вот интересно еще попробовать через спецвью, с юнидом как колонка и брать его
     
  4. oshmianski

    oshmianski Достойный программист
    Lotus team

    Регистрация:
    25 апр 2012
    Сообщения:
    512
    Симпатии:
    13
    еще можно попробовать на Notes C API (если не обязательно java).
    смотри NIFReadEntries и READ_MASK_NOTEID - это самый быстый способ, который я знаю.

    проверил:

    результаты на картинке. производилось считывание noteID и запись в файл (без записи будет еще быстрее).

    поиск на C API - это получение представления (всегда одинакова, я так понял).
    поиск на LS - это построение навигатора (здесь могут быть погрешности, связанные с кешем).

    как видно, чтение данных на С API быстрее.
     

    Вложения:

  5. Zeka

    Zeka Well-Known Member

    Регистрация:
    1 сен 2009
    Сообщения:
    219
    Симпатии:
    0
    Спасибо за идею. Если сделать view.setAutoUpdate(false), то с навигатором пройтись по всем докам можно за ~12 секунд. Всё же лучше, чем 50 :)

    Добавлено:
    Через @DbColumn? Не получится. Ограничение - 32кб....
     
  6. lmike

    lmike нет, пердело совершенство
    Команда форума Lotus team

    Регистрация:
    27 авг 2008
    Сообщения:
    6.073
    Симпатии:
    299
    нет - просто вьюшку созадать с юнидами, но получать юнид не через entry.GetUniversalID, а через entry.Columnvalues(0)


    Добавлено: вот тока за ускорение ничё не могу сказать, в теори должно брать из индекса, но юнид оно тоже может брать из кэшей
     
  7. oshmianski

    oshmianski Достойный программист
    Lotus team

    Регистрация:
    25 апр 2012
    Сообщения:
    512
    Симпатии:
    13
    пожалуйста.

    по другому выставить

    nav.setBufferMaxEntries(400);
    nav.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);

    и не получится


    Добавлено:
    а мне свегда казалось, что наоборот, GetUniversalID работает быстрее, чем ColumnValues(i).
    возможно стоит проверить работу GetUniversalID, GetNoteID и ColumnValues.

    ставлю на GetNoteID.
     
  8. Zeka

    Zeka Well-Known Member

    Регистрация:
    1 сен 2009
    Сообщения:
    219
    Симпатии:
    0
    entry.GetUniversalID и entry.Columnvalues(0) по времени одинаково получилось.

    Подозреваю GetNoteID тоже самое будет...
     
  9. garrick

    garrick Lotus team
    Lotus team

    Регистрация:
    26 окт 2009
    Сообщения:
    770
    Симпатии:
    50
    Если уже есть вьюшка "AllByUNID", пройтись по ней от первого документа до последнего, не получая коллекцию? Я думаю, значительное время тратится именно на формирование коллекции.
     
  10. lmike

    lmike нет, пердело совершенство
    Команда форума Lotus team

    Регистрация:
    27 авг 2008
    Сообщения:
    6.073
    Симпатии:
    299
    на getallentries врядли тратится много времени, но таки да - можно получить энтрис и сразу
    здесь другой момент, если собираемся писать не просто тест/одноразовый код, то более универсальным будет использование коллекций
     
  11. VladSh

    VladSh начинающий
    Lotus team

    Регистрация:
    11 дек 2009
    Сообщения:
    1.251
    Симпатии:
    2
    По моему, универсально - написать классы-итераторы для каждого варианта (NotesDocumentCollection, NotesViewEntryCollection, NotesViewNavigator), а внутрь их вбрасывать объекты классов, в которых реализованы определённые алгоритмы обработки.
     
  12. garrick

    garrick Lotus team
    Lotus team

    Регистрация:
    26 окт 2009
    Сообщения:
    770
    Симпатии:
    50
    Сегодня довелось проверить, правда не Entries, а Documets. Первый столбец во вьюхе содержит "1" специально для выбора всех документов. Во вьюхе ~130 тыс. документов. View.getAllDocumentsByKey("1", true) выполняется ококло 10 минут.
     
  13. VladSh

    VladSh начинающий
    Lotus team

    Регистрация:
    11 дек 2009
    Сообщения:
    1.251
    Симпатии:
    2
    garrick
    А если ентрисы попробовать? Ведь для эксперимента надо заменить лишь название метода)
     
  14. garrick

    garrick Lotus team
    Lotus team

    Регистрация:
    26 окт 2009
    Сообщения:
    770
    Симпатии:
    50
    С энтрисами раз в десять быстрее :(

    Код (Text):
    16.01.2013 12:42:23 Поиск документов для обработки...
    16.01.2013 12:42:50 Найдено документов для обработки 125353
    но надо было документы
     
  15. VladSh

    VladSh начинающий
    Lotus team

    Регистрация:
    11 дек 2009
    Сообщения:
    1.251
    Симпатии:
    2
    Тогда и я навалю кода по поводу:
    <div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">PDocumentBase.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import lotus.domino.Document;

    /**
    * Базовый класс для всех классов обработки документов;
    * предназначен для вбрасывания в классы-итераторы,
    */
    public abstract class PDocumentBase {

    /**
    * Прототип метода, содержащий основной алгоритм работы с документом
    * @param oParameter - любой объект для передачи параметром
    * @return
    */
    protected boolean processDocument(Object oParameter) {
    return false;
    }

    protected boolean processDocument(Document nd) {
    return false;
    }

    }[/CODE]
    <div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">IteratorBase.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import lotus.domino.Document;

    /**
    * Базовый класс-итератор;
    * предназначен для организации перебора документов,
    * вне зависимости от контейнеров их содержащих
    * Некоторые свойства и метода остались от LS, возможно н.б. выпилить
    */
    abstract class IteratorBase extends PDocumentBase {
    private String sActionText = "обработано документов"; //по умолчанию
    //выводить ли в статусбар процент выполнения
    protected boolean bOutputProgress;
    //выводить ли в статусбар общую инфу по завершению процесса
    protected boolean bOutputResult = true;
    //общее количество элементов
    protected long Count = -1; // по умолчанию "не инициализировано"
    //текущее значение счётчика (всего)
    protected long lCounter;
    //текущее значение счётчика (выполнено с положительным результатом)
    protected long lProcessed;

    /**
    * Конструктор
    * Для случаев, когда объект-итератор не известен вначале,
    * а определяется в ходе выполнения (поиск по базе и т.п.)
    */
    public IteratorBase() {
    }

    /**
    * @param sActionText the sActionText to set
    */
    public void setActionText(String sActionText) {
    this.sActionText = sActionText;
    }

    /**
    * @return the sActionText
    */
    public String getActionText() {
    return sActionText;
    }

    /**
    * Задание "выводить или нет" процент выполнения по ходу работы
    */
    public void outputProgress(boolean bOutputProgress) {
    this.bOutputProgress = bOutputProgress;
    }

    /**
    * Задание "выводить или нет" результат выполнения
    */
    public void outputResult(boolean bOutputResult) {
    this.bOutputResult = bOutputResult;
    }

    /**
    * Возвращает количество обработанных документов;
    * по данному значению можно косвенно судить о выполнении задания, возложенного на класс
    */
    public long isProcessed() {
    return this.lProcessed;
    }

    /**
    * Метод содержит основную логику - перебор документов в цикле;
    * @param oPDocument - вбрасываемый объект,
    * метод processDocument() которго будет вызываться в цикле
    * @return Результат как boolean
    */
    public boolean run(PDocumentBase oPDocument) {
    return false;
    }

    /**
    * Метод без параметра будет вызывать собственный processDocument(),
    * т.е. реализованый внутри себя
    * @return Результат как boolean
    */
    public boolean run() {
    return run(this);
    }

    /**
    * Метод обработки конкретного документа; переопределяется в классах-наследниках
    * При необходимости расширить логику внутри цикла также переопределяем этот метод
    */
    protected boolean processDocument(Document nd) {
    return false;
    }

    /**
    * Вывод (на печать) информации о текущей позиции (возможно %) обработки
    */
    protected void outputProgressInfo() {
    this.lProcessed += 1;
    if (this.bOutputProgress) {
    //Print StrConv(this.sActionText, 3) + ": " & Fix(this.lCounter * 100 / this.Count) & "%";
    }
    }

    /**
    * Вывод (на печать) информации об окончании обработки
    * Отдельной функцией, по причинам:
    * - метод run м.б. переопределён и сообщение выдано позже;
    * - сама процедура вывода результата м.б. переопределена
    */
    protected void outputResultInfo() {
    //Print "Процесс завершён. Всего " + this.sActionText + ": " & this.lCounter & ".";
    }


    protected void finalize() {
    try {
    super.finalize();
    }
    catch (Throwable e) {}
    }
    }[/CODE]<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">IteratorDocumentCollection.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import lotus.domino.Document;
    import lotus.domino.DocumentCollection;
    import lotus.domino.NotesException;

    /**
    * Базовый класс-итератор для перебора коллекции документов
    * раньше назывался PDocumentCollection
    */
    public class IteratorDocumentCollection extends IteratorBase {
    protected DocumentCollection ndc;

    /**
    * Конструктор
    * Для случаев, когда коллекция известна с самого начала
    * @param collection
    */
    public IteratorDocumentCollection(DocumentCollection collection) {
    initialize(collection);
    }

    /**
    * Задание извне коллекции для обработки
    * @param collection
    */
    public void initialize(DocumentCollection collection) {
    this.ndc = collection;
    }

    /**
    * Метод содержит основную логику - перебор документов в цикле
    * подробности см. в классе-родителе
    */
    public boolean run(PDocumentBase oPDocument) {
    try {
    if (this.ndc == null)
    return false;

    if (this.ndc.getCount() == 0) return false;
    this.Count = this.ndc.getCount();

    Document ndTmp;
    Document nd = this.ndc.getFirstDocument();
    while (nd != null) {
    if (oPDocument.processDocument(nd)) {
    this.outputProgressInfo();
    }
    ndTmp = this.ndc.getNextDocument();
    nd.recycle();
    nd = ndTmp;
    }
    if (this.bOutputResult) {
    this.outputResultInfo();
    }
    return true;

    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    }


    protected void finalize() {
    recycle();
    super.finalize();
    }

    public void recycle() {
    try {
    if (ndc != null)
    ndc.recycle();
    }
    catch (NotesException e) {}
    }
    }[/CODE]<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">IteratorViewEntryCollection.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import lotus.domino.Document;
    import lotus.domino.ViewEntry;
    import lotus.domino.ViewEntryCollection;
    import lotus.domino.NotesException;

    /**
    * Базовый класс-итератор для перебора коллекции элементов вида
    */
    public class IteratorViewEntryCollection extends IteratorBase {
    protected ViewEntryCollection nvec;

    /**
    * Конструктор
    * Для случаев, когда коллекция известна с самого начала
    * @param collection
    */
    public IteratorViewEntryCollection(ViewEntryCollection collection) {
    initialize(collection);
    }

    /**
    * Задание извне коллекции для обработки
    * @param collection
    */
    public void initialize(ViewEntryCollection collection) {
    this.nvec = collection;
    }

    /**
    * Метод содержит основную логику - перебор документов в цикле
    * подробности см. в классе-родителе
    */
    public boolean run(PDocumentBase oPDocument) {
    try {
    if (this.nvec == null)
    return false;

    if (this.nvec.getCount() == 0) return false;
    this.Count = this.nvec.getCount();

    Document nd;
    ViewEntry nveTmp;
    ViewEntry nve = this.nvec.getFirstEntry();
    while (nve != null) {
    lCounter++;
    if (nve.isDocument()) {
    nd = nve.getDocument();
    if (oPDocument.processDocument(nd)) {
    this.outputProgressInfo();
    }
    }
    nveTmp = this.nvec.getNextEntry();
    nve.recycle();
    nve = nveTmp;
    }
    if (this.bOutputResult) {
    this.outputResultInfo();
    }
    return true;
    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    }


    protected void finalize() {
    recycle();
    super.finalize();
    }

    public void recycle() {
    try {
    nvec.recycle();
    }
    catch (NotesException e) {}
    }
    }[/CODE]<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">IteratorViewNavigator.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import lotus.domino.Document;
    import lotus.domino.ViewEntry;
    import lotus.domino.ViewNavigator;
    import lotus.domino.NotesException;

    /**
    * Базовый класс-итератор для перебора документов навигатора
    */
    public class IteratorViewNavigator extends IteratorBase {
    protected ViewNavigator navigator;

    /**
    * Конструктор
    * Для случаев, когда навигатор известен с самого начала
    * @param navigator
    */
    public IteratorViewNavigator(ViewNavigator navigator) {
    initialize(navigator);
    }

    /**
    * Задание извне навигатора для обработки
    * @param navigator
    */
    public void initialize(ViewNavigator navigator) {
    this.navigator = navigator;
    }

    /**
    * Метод содержит основную логику - перебор документов в цикле;
    * подробности см. в классе-родителе
    */
    public boolean run(PDocumentBase oPDocument) {
    try {
    if (this.navigator == null)
    return false;

    if (this.navigator.getCount() == 0) return false;
    this.Count = navigator.getCount();

    Document nd;
    ViewEntry nveTmp;
    ViewEntry nve = this.navigator.getFirstDocument();
    while (nve != null) {
    lCounter++;
    if (nve.isDocument()) {
    nd = nve.getDocument();
    if (oPDocument.processDocument(nd)) {
    this.outputProgressInfo();
    }
    }
    nveTmp = this.navigator.getNextDocument();
    nve.recycle();
    nve = nveTmp;
    }
    if (this.bOutputResult) {
    this.outputResultInfo();
    }
    return true;
    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    }


    protected void finalize() {
    recycle();
    super.finalize();
    }

    public void recycle() {
    try {
    navigator.recycle();
    }
    catch (NotesException e) {}
    }
    }[/CODE]<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">IteratorVectorUNID.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import java.util.Vector;

    import lotus.domino.Database;
    import lotus.domino.Document;
    import lotus.domino.NotesException;

    /**
    * Базовый класс-итератор для перебора документов с взятием их по UNID.
    * Будет почти всегда использоваться при работе в web.
    */
    public class IteratorVectorUNID extends IteratorBase {
    /**
    * item, в котором содержатся UNID'ы обрабатываемых документов;
    * часто используется при передаче из UI
    */
    public static final String ITEM_UNID = "DocumentsUNID";
    private Database ndbParent;
    protected Vector<String> vctUNID;

    /**
    * Конструктор
    * Для случаев, когда UNID'ы известны с самого начала
    * @param vct - объект Vector с UNID'ами обрабатываемых документов
    */
    public IteratorVectorUNID(Database ndb, Vector<String> vct) {
    initialize(ndb, vct);
    }

    /**
    * Задание извне UNID'ов документов для обработки
    * @param collection
    */
    public void initialize(Database ndb, Vector<String> vct) {
    this.ndbParent = ndb;
    this.vctUNID = vct;
    }

    /**
    * Метод содержит основную логику - перебор документов в цикле
    * подробности см. в классе-родителе
    */
    public boolean run(PDocumentBase oPDocument) {
    try {
    if (this.vctUNID.size() == 0) return false;
    this.Count = this.vctUNID.size();

    Document nd = null;
    for (lCounter = 0; lCounter < this.Count; lCounter++) {
    try {
    nd = ndbParent.getDocumentByUNID((String) this.vctUNID.get((int) lCounter));
    } catch (NotesException en) {/* игнорим Err=4091 */}
    if (nd != null) {
    if (oPDocument.processDocument(nd)) {
    this.outputProgressInfo();
    }
    nd.recycle();
    nd = null;
    }
    }
    if (this.bOutputResult) {
    this.outputResultInfo();
    }
    return true;
    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    }


    protected void finalize() {
    recycle();
    super.finalize();
    }

    public void recycle() {
    try {
    if (ndbParent != null)
    ndbParent.recycle();
    }
    catch (NotesException e) {}
    vctUNID = null;
    }
    }[/CODE]
    <div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">IteratorDocumentsBridge.java</div></div><div class="sp-body"><div class="sp-content"><!--shcode--><pre><code class='java'>import lotus.domino.Database;
    import lotus.domino.DocumentCollection;
    import lotus.domino.NotesException;
    import lotus.domino.ViewEntryCollection;
    import lotus.domino.ViewNavigator;

    /**
    * Базовый класс для реализации собственного функционала,
    * где необходим перебор документов, находящихся в Lotus-контейнерах
    */
    public abstract class IteratorDocumentsBridge extends PDocumentBase {
    protected int count;
    private Database parentDatabase;

    /**
    * @return объект БД, в которой был сформирован контейнер документов,
    * переданный в run
    */
    public void setParentDatabase(Database ndb) {
    parentDatabase = ndb;
    }

    /**
    * @return объект БД, в которой был сформирован контейнер документов,
    * переданный в run
    */
    public Database getParentDatabase() {
    return parentDatabase;
    }

    /**
    * Запуск на выполнение;
    * передаём свою логику в класс-итератор для Vector
    * @param collection - коллекция документов
    * @return Результат в виде boolean
    */
    public boolean run(java.util.Vector<String> vct) {
    count = vct.size();
    if (runPrepare()) {
    IteratorVectorUNID iterator = new IteratorVectorUNID(parentDatabase, vct);
    return iterator.run(this);
    }
    else return false;
    }

    /**
    * Запуск на выполнение;
    * передаём свою логику в класс-итератор для DocumentCollection
    * @param collection - коллекция документов
    * @return Результат в виде boolean
    */
    public boolean run(DocumentCollection collection) {
    try {
    parentDatabase = collection.getParent();
    count = collection.getCount();
    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    if (runPrepare()) {
    IteratorDocumentCollection iterator = new IteratorDocumentCollection(collection);
    return iterator.run(this);
    }
    else return false;
    }

    /**
    * Запуск на выполнение;
    * передаём свою логику в класс-итератор для ViewEntryCollection
    * @param collection - коллекция элементов вида/папки
    * @return Результат в виде boolean
    */
    public boolean run(ViewEntryCollection collection) {
    try {
    parentDatabase = collection.getParent().getParent();
    count = collection.getCount();
    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    if (runPrepare()) {
    IteratorViewEntryCollection iterator = new IteratorViewEntryCollection(collection);
    return iterator.run(this);
    }
    else return false;
    }

    /**
    * Запуск на выполнение;
    * передаём свою логику в класс-итератор для ViewNavigator
    * @param navigator - заранее подготовленный навигатор, содержащий элементы вида
    * @return Результат в виде boolean
    */
    public boolean run(ViewNavigator navigator) {
    try {
    parentDatabase = navigator.getParentView().getParent();
    count = navigator.getCount();
    }
    catch (NotesException en) {
    System.out.println(en.getStackTrace());
    return false;
    }
    if (runPrepare()) {
    IteratorViewNavigator iterator = new IteratorViewNavigator(navigator);
    return iterator.run(this);
    }
    else return false;
    }

    /**
    * Расширяем метод run;
    * для ситуаций, когда необходимо переопределить метод run,
    * добавив код перед основным вызовом.
    * @return Результат в виде boolean;
    * [true] для классов не переопределяющих данный метод.
    */
    public boolean runPrepare() {
    return true;
    }


    protected void finalize() {
    recycle();
    }

    public void recycle() {
    if (parentDatabase != null) {
    try {
    parentDatabase.recycle();
    }
    catch (NotesException en) {}
    }
    }
    }[/CODE]
    С Reflection и generics не заморачивался, всё по простому.
     
  16. Кирилл Шваб

    Кирилл Шваб Well-Known Member

    Регистрация:
    30 июн 2006
    Сообщения:
    143
    Симпатии:
    4
    VladSh

    В IteratorVectorUNID.java в методе run нет освобождения памяти в цикле.

    Код (Text):
                for (lCounter = 0; lCounter < this.Count; lCounter++) {
    nd = ndbParent.getDocumentByUNID((String) this.vctUNID.get((int) lCounter));
    if (oPDocument.processDocument(nd)) {
    this.outputProgressInfo();
    }
    }
    Соответственно если цикл будет большим, то память может закончиться. ;-)
     
  17. VladSh

    VladSh начинающий
    Lotus team

    Регистрация:
    11 дек 2009
    Сообщения:
    1.251
    Симпатии:
    2
    Кирилл Шваб
    Спасибо за замечание! :(
    Исправил, дополнительно добавил игнорирование ошибки ненахождения дока по UNID'у.
     
Загрузка...

Поделиться этой страницей