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

Zeka

Well-known member
01.09.2009
219
0
#1
В базе есть 100 тысяч документов. Надо УНИДы всех документов положить в Set. Как это сделать максимально быстро?

Код:
		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 секунд.

Код:
		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 секунд.

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

oshmianski

Достойный программист
Lotus team
25.04.2012
555
8
#2
Для 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;
}
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#3
а вот интересно еще попробовать через спецвью, с юнидом как колонка и брать его
 

oshmianski

Достойный программист
Lotus team
25.04.2012
555
8
#4
еще можно попробовать на Notes C API (если не обязательно java).
смотри NIFReadEntries и READ_MASK_NOTEID - это самый быстый способ, который я знаю.

проверил:

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

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

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

Вложения

Zeka

Well-known member
01.09.2009
219
0
#5
Для 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;
}
Спасибо за идею. Если сделать view.setAutoUpdate(false), то с навигатором пройтись по всем докам можно за ~12 секунд. Всё же лучше, чем 50 :)

Добавлено:
а вот интересно еще попробовать через спецвью, с юнидом как колонка и брать его
Через @DbColumn? Не получится. Ограничение - 32кб....
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#6
Через @DbColumn? Не получится. Ограничение - 32кб....
нет - просто вьюшку созадать с юнидами, но получать юнид не через entry.GetUniversalID, а через entry.Columnvalues(0)


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

oshmianski

Достойный программист
Lotus team
25.04.2012
555
8
#7
Спасибо за идею. Если сделать view.setAutoUpdate(false), то с навигатором пройтись по всем докам можно за ~12 секунд. Всё же лучше, чем 50 :)
пожалуйста.

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

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

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


Добавлено:
нет - просто вьюшку созадать с юнидами, но получать юнид не через entry.GetUniversalID, а через entry.Columnvalues(0)


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

ставлю на GetNoteID.
 

Zeka

Well-known member
01.09.2009
219
0
#8
а мне свегда казалось, что наоборот, GetUniversalID работает быстрее, чем ColumnValues(i).
возможно стоит проверить работу GetUniversalID, GetNoteID и ColumnValues.

ставлю на GetNoteID.
entry.GetUniversalID и entry.Columnvalues(0) по времени одинаково получилось.

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

garrick

Lotus team
26.10.2009
897
61
#9
Если уже есть вьюшка "AllByUNID", пройтись по ней от первого документа до последнего, не получая коллекцию? Я думаю, значительное время тратится именно на формирование коллекции.
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#10
Если уже есть вьюшка "AllByUNID", пройтись по ней от первого документа до последнего, не получая коллекцию? Я думаю, значительное время тратится именно на формирование коллекции.
на getallentries врядли тратится много времени, но таки да - можно получить энтрис и сразу
здесь другой момент, если собираемся писать не просто тест/одноразовый код, то более универсальным будет использование коллекций
 

VladSh

начинающий
Lotus team
11.12.2009
1 262
6
#11
если собираемся писать не просто тест/одноразовый код, то более универсальным будет использование коллекций
По моему, универсально - написать классы-итераторы для каждого варианта (NotesDocumentCollection, NotesViewEntryCollection, NotesViewNavigator), а внутрь их вбрасывать объекты классов, в которых реализованы определённые алгоритмы обработки.
 

garrick

Lotus team
26.10.2009
897
61
#12
на getallentries врядли тратится много времени...
Сегодня довелось проверить, правда не Entries, а Documets. Первый столбец во вьюхе содержит "1" специально для выбора всех документов. Во вьюхе ~130 тыс. документов. View.getAllDocumentsByKey("1", true) выполняется ококло 10 минут.
 

VladSh

начинающий
Lotus team
11.12.2009
1 262
6
#13
garrick
А если ентрисы попробовать? Ведь для эксперимента надо заменить лишь название метода)
 

garrick

Lotus team
26.10.2009
897
61
#14
С энтрисами раз в десять быстрее :(

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

VladSh

начинающий
Lotus team
11.12.2009
1 262
6
#15
Тогда и я навалю кода по поводу:
По моему, универсально - написать классы-итераторы для каждого варианта (NotesDocumentCollection, NotesViewEntryCollection, NotesViewNavigator), а внутрь их вбрасывать объекты классов, в которых реализованы определённые алгоритмы обработки.
<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 не заморачивался, всё по простому.
 

Кирилл Шваб

Well-known member
30.06.2006
145
4
#16
VladSh

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

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

VladSh

начинающий
Lotus team
11.12.2009
1 262
6
#17
Кирилл Шваб
Спасибо за замечание! :(
Исправил, дополнительно добавил игнорирование ошибки ненахождения дока по UNID'у.